]> Cypherpunks repositories - gostls13.git/commitdiff
test/run: process build tags like go/build
authorAnthony Martin <ality@pbrane.org>
Tue, 13 Aug 2013 16:25:41 +0000 (12:25 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 13 Aug 2013 16:25:41 +0000 (12:25 -0400)
R=bradfitz, dave, rsc, r
CC=golang-dev
https://golang.org/cl/10001045

test/run.go
test/testlib

index 5e167d6b0ccc324c7072da8efb11427687089704..3535532406f6a94b5fa554ebb56a25a39de7ad36 100644 (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
index de138b1d19d2d60fadf1c00a2d5841716b079f2e..4a17f4feb9c901b5bbe60d4cef780ff0079a7f02 100644 (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() {