test/run: process build tags like go/build

R=bradfitz, dave, rsc, r
CC=golang-dev
https://golang.org/cl/10001045
This commit is contained in:
Anthony Martin 2013-08-13 12:25:41 -04:00 committed by Russ Cox
parent 71c6da39ce
commit a538558003
2 changed files with 105 additions and 33 deletions

View file

@ -27,6 +27,7 @@ import (
"sort"
"strconv"
"strings"
"unicode"
)
var (
@ -299,14 +300,17 @@ func goDirPackages(longdir string) ([][]string, error) {
return pkgs, nil
}
type context struct {
GOOS string
GOARCH string
}
// shouldTest looks for build tags in a source file and returns
// whether the file should be used according to the tags.
func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
if idx := strings.Index(src, "\npackage"); idx >= 0 {
src = src[:idx]
}
notgoos := "!" + goos
notgoarch := "!" + goarch
for _, line := range strings.Split(src, "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "//") {
@ -318,29 +322,59 @@ func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
if len(line) == 0 || line[0] != '+' {
continue
}
ctxt := &context{
GOOS: goos,
GOARCH: goarch,
}
words := strings.Fields(line)
if words[0] == "+build" {
for _, word := range words {
switch word {
case goos, goarch:
return true, ""
case notgoos, notgoarch:
continue
default:
if word[0] == '!' {
// NOT something-else
return true, ""
}
ok := false
for _, word := range words[1:] {
if ctxt.match(word) {
ok = true
break
}
}
// no matching tag found.
return false, line
if !ok {
// no matching tag found.
return false, line
}
}
}
// no build tags.
// no build tags
return true, ""
}
func (ctxt *context) match(name string) bool {
if name == "" {
return false
}
if i := strings.Index(name, ","); i >= 0 {
// comma-separated list
return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
}
if strings.HasPrefix(name, "!!") { // bad syntax, reject always
return false
}
if strings.HasPrefix(name, "!") { // negation
return len(name) > 1 && !ctxt.match(name[1:])
}
// Tags must be letters, digits, underscores or dots.
// Unlike in Go identifiers, all digits are fine (e.g., "386").
for _, c := range name {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
return false
}
}
if name == ctxt.GOOS || name == ctxt.GOARCH {
return true
}
return false
}
func init() { checkShouldTest() }
// run runs a test.
@ -815,7 +849,7 @@ func defaultRunOutputLimit() int {
return cpu
}
// checkShouldTest runs canity checks on the shouldTest function.
// checkShouldTest runs sanity checks on the shouldTest function.
func checkShouldTest() {
assert := func(ok bool, _ string) {
if !ok {
@ -823,11 +857,28 @@ func checkShouldTest() {
}
}
assertNot := func(ok bool, _ string) { assert(!ok, "") }
// Simple tests.
assert(shouldTest("// +build linux", "linux", "arm"))
assert(shouldTest("// +build !windows", "linux", "arm"))
assertNot(shouldTest("// +build !windows", "windows", "amd64"))
assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
// A file with no build tags will always be tested.
assert(shouldTest("// This is a test.", "os", "arch"))
// Build tags separated by a space are OR-ed together.
assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
// Build tags seperated by a comma are AND-ed together.
assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64"))
assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386"))
// Build tags on multiple lines are AND-ed together.
assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64"))
assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64"))
// Test that (!a OR !b) matches anything.
assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
}
// envForDir returns a copy of the environment

View file

@ -16,29 +16,50 @@ pkgs() {
done | sort
}
_match() {
case $1 in
*,*)
#echo >&2 "match comma separated $1"
first=$(echo $1 | sed 's/,.*//')
rest=$(echo $1 | sed 's/[^,]*,//')
if _match $first && _match $rest; then
return 0
fi
return 1
;;
'!'*)
#echo >&2 "match negation $1"
neg=$(echo $1 | sed 's/^!//')
if _match $neg; then
return 1
fi
return 0
;;
$GOARCH|$GOOS)
#echo >&2 "match GOARCH or GOOS $1"
return 0
;;
esac
return 1
}
# +build aborts execution if the supplied tags don't match,
# i.e. none of the tags (x or !x) matches GOARCH or GOOS.
+build() {
if (( $# == 0 )); then
return
fi
m=0
for tag; do
case $tag in
$GOARCH|$GOOS)
#echo >&2 "match $tag in $1"
return # don't exclude.
;;
'!'$GOARCH|'!'$GOOS)
;;
'!'*)
# not x where x is neither GOOS nor GOARCH.
#echo >&2 "match $tag in $1"
return # don't exclude
;;
esac
if _match $tag; then
m=1
fi
done
# no match.
exit 0
if [ $m = 0 ]; then
#echo >&2 no match
exit 0
fi
unset m
}
compile() {