]> Cypherpunks repositories - gostls13.git/commitdiff
go/build: add BuildTags to Context, allow !tag
authorRuss Cox <rsc@golang.org>
Mon, 23 Jan 2012 20:16:38 +0000 (15:16 -0500)
committerRuss Cox <rsc@golang.org>
Mon, 23 Jan 2012 20:16:38 +0000 (15:16 -0500)
This lets the client of go/build specify additional tags that
can be recognized in a // +build directive.  For example,
a build for a custom environment like App Engine might
include "appengine" in the BuildTags list, so that packages
can be written with some files saying

        // +build appengine   (build only on app engine)

or

        // +build !appengine  (build only when NOT on app engine)

App Engine here is just a hypothetical context.  I plan to use
this in the cmd/go sources to distinguish the bootstrap version
of cmd/go (which will not use networking) from the full version
using a custom tag.  It might also be useful in App Engine.

Also, delete Build and Script, which we did not end up using for
cmd/go and which never got turned on for real in goinstall.

R=r, adg
CC=golang-dev
https://golang.org/cl/5554079

src/cmd/goinstall/main.go
src/pkg/crypto/tls/root_stub.go
src/pkg/go/build/build.go
src/pkg/go/build/build_test.go
src/pkg/go/build/dir.go
src/pkg/net/cgo_stub.go
src/pkg/os/user/lookup_stubs.go

index ba8592b54a524c333a3aa1c8c5bc4b40eb9173c4..bbc4b6b765bb7111de06b9e82a721b20e95c6f80 100644 (file)
@@ -44,7 +44,7 @@ var (
        doInstall         = flag.Bool("install", true, "build and install")
        clean             = flag.Bool("clean", false, "clean the package directory before installing")
        nuke              = flag.Bool("nuke", false, "clean the package directory and target before installing")
-       useMake           = flag.Bool("make", true, "use make to build and install")
+       useMake           = flag.Bool("make", true, "use make to build and install (obsolete, always true)")
        verbose           = flag.Bool("v", false, "verbose")
 )
 
@@ -336,35 +336,10 @@ func installPackage(pkg, parent string, tree *build.Tree, retry bool) (installEr
        }
 
        // Install this package.
-       if *useMake {
-               err := domake(dir, pkg, tree, dirInfo.IsCommand())
-               if err != nil {
-                       return &BuildError{pkg, err}
-               }
-               return nil
-       }
-       script, err := build.Build(tree, pkg, dirInfo)
+       err = domake(dir, pkg, tree, dirInfo.IsCommand())
        if err != nil {
                return &BuildError{pkg, err}
        }
-       if *nuke {
-               printf("%s: nuke\n", pkg)
-               script.Nuke()
-       } else if *clean {
-               printf("%s: clean\n", pkg)
-               script.Clean()
-       }
-       if *doInstall {
-               if script.Stale() {
-                       printf("%s: install\n", pkg)
-                       if err := script.Run(); err != nil {
-                               return &BuildError{pkg, err}
-                       }
-               } else {
-                       printf("%s: up-to-date\n", pkg)
-               }
-       }
-
        return nil
 }
 
index d00493a5736d466291e3fc9684e5308b94db5571..ee2c3e01795677cd81c79cb9dd860002cd2d5ed7 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build plan9 darwin/nocgo
+// +build plan9 darwin,!cgo
 
 package tls
 
index 9515a7e645224f41d032ad227e0ff4e4cf89ad1f..68e8d34200530753b76c4880a447cd7e9e66dcd8 100644 (file)
@@ -5,245 +5,7 @@
 // Package build provides tools for building Go packages.
 package build
 
-import (
-       "bytes"
-       "errors"
-       "fmt"
-       "os"
-       "os/exec"
-       "path/filepath"
-       "regexp"
-       "runtime"
-       "strings"
-       "time"
-)
-
-// Build produces a build Script for the given package.
-func Build(tree *Tree, pkg string, info *DirInfo) (*Script, error) {
-       s := &Script{}
-       b := &build{
-               script: s,
-               path:   filepath.Join(tree.SrcDir(), pkg),
-       }
-       b.obj = b.abs("_obj") + string(filepath.Separator)
-
-       b.goarch = runtime.GOARCH
-       if g := os.Getenv("GOARCH"); g != "" {
-               b.goarch = g
-       }
-       var err error
-       b.arch, err = ArchChar(b.goarch)
-       if err != nil {
-               return nil, err
-       }
-
-       // add import object files to list of Inputs
-       for _, pkg := range info.Imports {
-               t, p, err := FindTree(pkg)
-               if err != nil && err != ErrNotFound {
-                       // FindTree should always be able to suggest an import
-                       // path and tree. The path must be malformed
-                       // (for example, an absolute or relative path).
-                       return nil, errors.New("build: invalid import: " + pkg)
-               }
-               s.addInput(filepath.Join(t.PkgDir(), p+".a"))
-       }
-
-       // .go files to be built with gc
-       gofiles := b.abss(info.GoFiles...)
-       s.addInput(gofiles...)
-
-       var ofiles []string // object files to be linked or packed
-
-       // make build directory
-       b.mkdir(b.obj)
-       s.addIntermediate(b.obj)
-
-       // cgo
-       if len(info.CgoFiles) > 0 {
-               cgoFiles := b.abss(info.CgoFiles...)
-               s.addInput(cgoFiles...)
-               cgoCFiles := b.abss(info.CFiles...)
-               s.addInput(cgoCFiles...)
-               outGo, outObj := b.cgo(cgoFiles, cgoCFiles)
-               gofiles = append(gofiles, outGo...)
-               ofiles = append(ofiles, outObj...)
-               s.addIntermediate(outGo...)
-               s.addIntermediate(outObj...)
-       }
-
-       // compile
-       if len(gofiles) > 0 {
-               ofile := b.obj + "_go_." + b.arch
-               b.gc(ofile, gofiles...)
-               ofiles = append(ofiles, ofile)
-               s.addIntermediate(ofile)
-       }
-
-       // assemble
-       for _, sfile := range info.SFiles {
-               ofile := b.obj + sfile[:len(sfile)-1] + b.arch
-               sfile = b.abs(sfile)
-               s.addInput(sfile)
-               b.asm(ofile, sfile)
-               ofiles = append(ofiles, ofile)
-               s.addIntermediate(ofile)
-       }
-
-       if len(ofiles) == 0 {
-               return nil, errors.New("make: no object files to build")
-       }
-
-       // choose target file
-       var targ string
-       if info.IsCommand() {
-               // use the last part of the import path as binary name
-               _, bin := filepath.Split(pkg)
-               if runtime.GOOS == "windows" {
-                       bin += ".exe"
-               }
-               targ = filepath.Join(tree.BinDir(), bin)
-       } else {
-               targ = filepath.Join(tree.PkgDir(), pkg+".a")
-       }
-
-       // make target directory
-       targDir, _ := filepath.Split(targ)
-       b.mkdir(targDir)
-
-       // link binary or pack object
-       if info.IsCommand() {
-               b.ld(targ, ofiles...)
-       } else {
-               b.gopack(targ, ofiles...)
-       }
-       s.Output = append(s.Output, targ)
-
-       return b.script, nil
-}
-
-// A Script describes the build process for a Go package.
-// The Input, Intermediate, and Output fields are lists of absolute paths.
-type Script struct {
-       Cmd          []*Cmd
-       Input        []string
-       Intermediate []string
-       Output       []string
-}
-
-func (s *Script) addInput(file ...string) {
-       s.Input = append(s.Input, file...)
-}
-
-func (s *Script) addIntermediate(file ...string) {
-       s.Intermediate = append(s.Intermediate, file...)
-}
-
-// Run runs the Script's Cmds in order.
-func (s *Script) Run() error {
-       for _, c := range s.Cmd {
-               if err := c.Run(); err != nil {
-                       return err
-               }
-       }
-       return nil
-}
-
-// Stale returns true if the build's inputs are newer than its outputs.
-func (s *Script) Stale() bool {
-       var latest time.Time
-       // get latest mtime of outputs
-       for _, file := range s.Output {
-               fi, err := os.Stat(file)
-               if err != nil {
-                       // any error reading output files means stale
-                       return true
-               }
-               if mtime := fi.ModTime(); mtime.After(latest) {
-                       latest = mtime
-               }
-       }
-       for _, file := range s.Input {
-               fi, err := os.Stat(file)
-               if err != nil || fi.ModTime().After(latest) {
-                       // any error reading input files means stale
-                       // (attempt to rebuild to figure out why)
-                       return true
-               }
-       }
-       return false
-}
-
-// Clean removes the Script's Intermediate files.
-// It tries to remove every file and returns the first error it encounters.
-func (s *Script) Clean() (err error) {
-       // Reverse order so that directories get removed after the files they contain.
-       for i := len(s.Intermediate) - 1; i >= 0; i-- {
-               if e := os.Remove(s.Intermediate[i]); err == nil {
-                       err = e
-               }
-       }
-       return
-}
-
-// Nuke removes the Script's Intermediate and Output files.
-// It tries to remove every file and returns the first error it encounters.
-func (s *Script) Nuke() (err error) {
-       // Reverse order so that directories get removed after the files they contain.
-       for i := len(s.Output) - 1; i >= 0; i-- {
-               if e := os.Remove(s.Output[i]); err == nil {
-                       err = e
-               }
-       }
-       if e := s.Clean(); err == nil {
-               err = e
-       }
-       return
-}
-
-// A Cmd describes an individual build command.
-type Cmd struct {
-       Args   []string // command-line
-       Stdout string   // write standard output to this file, "" is passthrough
-       Dir    string   // working directory
-       Env    []string // environment
-       Input  []string // file paths (dependencies)
-       Output []string // file paths
-}
-
-func (c *Cmd) String() string {
-       return strings.Join(c.Args, " ")
-}
-
-// Run executes the Cmd.
-func (c *Cmd) Run() error {
-       if c.Args[0] == "mkdir" {
-               for _, p := range c.Output {
-                       if err := os.MkdirAll(p, 0777); err != nil {
-                               return fmt.Errorf("command %q: %v", c, err)
-                       }
-               }
-               return nil
-       }
-       out := new(bytes.Buffer)
-       cmd := exec.Command(c.Args[0], c.Args[1:]...)
-       cmd.Dir = c.Dir
-       cmd.Env = c.Env
-       cmd.Stdout = out
-       cmd.Stderr = out
-       if c.Stdout != "" {
-               f, err := os.Create(c.Stdout)
-               if err != nil {
-                       return err
-               }
-               defer f.Close()
-               cmd.Stdout = f
-       }
-       if err := cmd.Run(); err != nil {
-               return fmt.Errorf("command %q: %v\n%v", c, err, out)
-       }
-       return nil
-}
+import "errors"
 
 // ArchChar returns the architecture character for the given goarch.
 // For example, ArchChar("amd64") returns "6".
@@ -258,188 +20,3 @@ func ArchChar(goarch string) (string, error) {
        }
        return "", errors.New("unsupported GOARCH " + goarch)
 }
-
-type build struct {
-       script *Script
-       path   string
-       obj    string
-       goarch string
-       arch   string
-}
-
-func (b *build) abs(file string) string {
-       if filepath.IsAbs(file) {
-               return file
-       }
-       return filepath.Join(b.path, file)
-}
-
-func (b *build) abss(file ...string) []string {
-       s := make([]string, len(file))
-       for i, f := range file {
-               s[i] = b.abs(f)
-       }
-       return s
-}
-
-func (b *build) add(c Cmd) {
-       b.script.Cmd = append(b.script.Cmd, &c)
-}
-
-func (b *build) mkdir(name string) {
-       b.add(Cmd{
-               Args:   []string{"mkdir", "-p", name},
-               Output: []string{name},
-       })
-}
-
-func (b *build) gc(ofile string, gofiles ...string) {
-       gc := b.arch + "g"
-       args := append([]string{gc, "-o", ofile}, gcImportArgs...)
-       args = append(args, gofiles...)
-       b.add(Cmd{
-               Args:   args,
-               Input:  gofiles,
-               Output: []string{ofile},
-       })
-}
-
-func (b *build) asm(ofile string, sfile string) {
-       asm := b.arch + "a"
-       b.add(Cmd{
-               Args:   []string{asm, "-o", ofile, sfile},
-               Input:  []string{sfile},
-               Output: []string{ofile},
-       })
-}
-
-func (b *build) ld(targ string, ofiles ...string) {
-       ld := b.arch + "l"
-       args := append([]string{ld, "-o", targ}, ldImportArgs...)
-       args = append(args, ofiles...)
-       b.add(Cmd{
-               Args:   args,
-               Input:  ofiles,
-               Output: []string{targ},
-       })
-}
-
-func (b *build) gopack(targ string, ofiles ...string) {
-       b.add(Cmd{
-               Args:   append([]string{"gopack", "grc", targ}, ofiles...),
-               Input:  ofiles,
-               Output: []string{targ},
-       })
-}
-
-func (b *build) cc(ofile string, cfiles ...string) {
-       cc := b.arch + "c"
-       dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
-       inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
-       args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
-       b.add(Cmd{
-               Args:   append(args, cfiles...),
-               Input:  cfiles,
-               Output: []string{ofile},
-       })
-}
-
-func (b *build) gccCompile(ofile, cfile string) {
-       b.add(Cmd{
-               Args:   b.gccArgs("-o", ofile, "-c", cfile),
-               Input:  []string{cfile},
-               Output: []string{ofile},
-       })
-}
-
-func (b *build) gccLink(ofile string, ofiles ...string) {
-       b.add(Cmd{
-               Args:   append(b.gccArgs("-o", ofile), ofiles...),
-               Input:  ofiles,
-               Output: []string{ofile},
-       })
-}
-
-func (b *build) gccArgs(args ...string) []string {
-       // TODO(adg): HOST_CC
-       a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
-       switch b.arch {
-       case "8":
-               a = append(a, "-m32")
-       case "6":
-               a = append(a, "-m64")
-       }
-       return append(a, args...)
-}
-
-var cgoRe = regexp.MustCompile(`[/\\:]`)
-
-func (b *build) cgo(cgofiles, cgocfiles []string) (outGo, outObj []string) {
-       // cgo
-       // TODO(adg): CGOPKGPATH
-       // TODO(adg): CGO_FLAGS
-       gofiles := []string{b.obj + "_cgo_gotypes.go"}
-       cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
-       for _, fn := range cgofiles {
-               f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
-               gofiles = append(gofiles, f+"cgo1.go")
-               cfiles = append(cfiles, f+"cgo2.c")
-       }
-       defunC := b.obj + "_cgo_defun.c"
-       output := append([]string{defunC}, cfiles...)
-       output = append(output, gofiles...)
-       b.add(Cmd{
-               Args:   append([]string{"cgo", "--"}, cgofiles...),
-               Dir:    b.path,
-               Env:    append(os.Environ(), "GOARCH="+b.goarch),
-               Input:  cgofiles,
-               Output: output,
-       })
-       outGo = append(outGo, gofiles...)
-       b.script.addIntermediate(defunC, b.obj+"_cgo_export.h", b.obj+"_cgo_flags")
-       b.script.addIntermediate(cfiles...)
-
-       // cc _cgo_defun.c
-       defunObj := b.obj + "_cgo_defun." + b.arch
-       b.cc(defunObj, defunC)
-       outObj = append(outObj, defunObj)
-
-       // gcc
-       linkobj := make([]string, 0, len(cfiles))
-       for _, cfile := range cfiles {
-               ofile := cfile[:len(cfile)-1] + "o"
-               b.gccCompile(ofile, cfile)
-               linkobj = append(linkobj, ofile)
-               if !strings.HasSuffix(ofile, "_cgo_main.o") {
-                       outObj = append(outObj, ofile)
-               } else {
-                       b.script.addIntermediate(ofile)
-               }
-       }
-       for _, cfile := range cgocfiles {
-               ofile := b.obj + cgoRe.ReplaceAllString(cfile[:len(cfile)-1], "_") + "o"
-               b.gccCompile(ofile, cfile)
-               linkobj = append(linkobj, ofile)
-               outObj = append(outObj, ofile)
-       }
-       dynObj := b.obj + "_cgo_.o"
-       b.gccLink(dynObj, linkobj...)
-       b.script.addIntermediate(dynObj)
-
-       // cgo -dynimport
-       importC := b.obj + "_cgo_import.c"
-       b.add(Cmd{
-               Args:   []string{"cgo", "-dynimport", dynObj},
-               Stdout: importC,
-               Input:  []string{dynObj},
-               Output: []string{importC},
-       })
-       b.script.addIntermediate(importC)
-
-       // cc _cgo_import.ARCH
-       importObj := b.obj + "_cgo_import." + b.arch
-       b.cc(importObj, importC)
-       outObj = append(outObj, importObj)
-
-       return
-}
index fd4030632a9d8dc77cc9e898fdc6d3ef1f756501..381e2b2d96a2ab433ab30244e660606613911075 100644 (file)
@@ -5,7 +5,6 @@
 package build
 
 import (
-       "os/exec"
        "path/filepath"
        "reflect"
        "runtime"
@@ -63,8 +62,6 @@ func ifCgo(x []string) []string {
        return nil
 }
 
-const cmdtestOutput = "3"
-
 func TestBuild(t *testing.T) {
        for _, tt := range buildPkgs {
                tree := Path[0] // Goroot
@@ -78,39 +75,32 @@ func TestBuild(t *testing.T) {
                        t.Errorf("ScanDir(%#q) = %#v, want %#v\n", tt.dir, info, tt.info)
                        continue
                }
+       }
+}
 
-               if tt.dir == "go/build/cgotest" && len(info.CgoFiles) == 0 {
-                       continue
-               }
-
-               s, err := Build(tree, tt.dir, info)
-               if err != nil {
-                       t.Errorf("Build(%#q): %v", tt.dir, err)
-                       continue
+func TestMatch(t *testing.T) {
+       ctxt := DefaultContext
+       what := "default"
+       match := func(tag string) {
+               if !ctxt.match(tag) {
+                       t.Errorf("%s context should match %s, does not", what, tag)
                }
-
-               if err := s.Run(); err != nil {
-                       t.Errorf("Run(%#q): %v", tt.dir, err)
-                       continue
+       }
+       nomatch := func(tag string) {
+               if ctxt.match(tag) {
+                       t.Errorf("%s context should NOT match %s, does", what, tag)
                }
+       }
 
-               if tt.dir == "go/build/cmdtest" {
-                       bin := s.Output[0]
-                       b, err := exec.Command(bin).CombinedOutput()
-                       if err != nil {
-                               t.Errorf("exec %s: %v", bin, err)
-                               continue
-                       }
-                       if string(b) != cmdtestOutput {
-                               t.Errorf("cmdtest output: %s want: %s", b, cmdtestOutput)
-                       }
-               }
+       match(runtime.GOOS + "," + runtime.GOARCH)
+       match(runtime.GOOS + "," + runtime.GOARCH + ",!foo")
+       nomatch(runtime.GOOS + "," + runtime.GOARCH + ",foo")
 
-               // Deferred because cmdtest depends on pkgtest.
-               defer func(s *Script) {
-                       if err := s.Nuke(); err != nil {
-                               t.Errorf("nuking: %v", err)
-                       }
-               }(s)
-       }
+       what = "modified"
+       ctxt.BuildTags = []string{"foo"}
+       match(runtime.GOOS + "," + runtime.GOARCH)
+       match(runtime.GOOS + "," + runtime.GOARCH + ",foo")
+       nomatch(runtime.GOOS + "," + runtime.GOARCH + ",!foo")
+       match(runtime.GOOS + "," + runtime.GOARCH + ",!bar")
+       nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar")
 }
index 5ce75fda7e08d38e5f9afdd2359484bef8cfea7d..66005455dfc895430ea6eb82cd892ac890bfc4f8 100644 (file)
@@ -25,9 +25,10 @@ import (
 
 // A Context specifies the supporting context for a build.
 type Context struct {
-       GOARCH     string // target architecture
-       GOOS       string // target operating system
-       CgoEnabled bool   // whether cgo can be used
+       GOARCH     string   // target architecture
+       GOOS       string   // target operating system
+       CgoEnabled bool     // whether cgo can be used
+       BuildTags  []string // additional tags to recognize in +build lines
 
        // By default, ScanDir uses the operating system's
        // file system calls to read directories and files.
@@ -74,7 +75,7 @@ func (ctxt *Context) readFile(dir, file string) (string, []byte, error) {
 // The DefaultContext is the default Context for builds.
 // It uses the GOARCH and GOOS environment variables
 // if set, or else the compiled code's GOARCH and GOOS.
-var DefaultContext = defaultContext()
+var DefaultContext Context = defaultContext()
 
 var cgoEnabled = map[string]bool{
        "darwin/386":    true,
@@ -121,7 +122,7 @@ type DirInfo struct {
        Imports        []string          // All packages imported by GoFiles
 
        // Source files
-       GoFiles  []string // .go files in dir (excluding CgoFiles)
+       GoFiles  []string // .go files in dir (excluding CgoFiles, TestGoFiles, XTestGoFiles)
        HFiles   []string // .h files in dir
        CFiles   []string // .c files in dir
        SFiles   []string // .s (and, when using cgo, .S files in dir)
@@ -148,13 +149,71 @@ func ScanDir(dir string) (info *DirInfo, err error) {
        return DefaultContext.ScanDir(dir)
 }
 
-// ScanDir returns a structure with details about the Go content found
-// in the given directory. The file lists exclude:
+// TODO(rsc): Move this comment to a more appropriate place.
+
+// ScanDir returns a structure with details about the Go package
+// found in the given directory.
+//
+// Most .go, .c, .h, and .s files in the directory are considered part
+// of the package.  The exceptions are:
 //
-//     - files in package main (unless no other package is found)
-//     - files in package documentation
-//     - files ending in _test.go
+//     - .go files in package main (unless no other package is found)
+//     - .go files in package documentation
 //     - files starting with _ or .
+//     - files with build constraints not satisfied by the context
+//
+// Build Constraints
+//
+// A build constraint is a line comment beginning with the directive +build
+// that lists the conditions under which a file should be included in the package.
+// Constraints may appear in any kind of source file (not just Go), but
+// they must be appear near the top of the file, preceded
+// only by blank lines and other line comments.
+//
+// A build constraint is evaluated as the OR of space-separated options;
+// each option evaluates as the AND of ots comma-separated terms;
+// and each term is an alphanumeric word or, preceded by !, its negation.
+// That is, the build constraint:
+//
+//     // +build linux,386 darwin,!cgo
+//
+// corresponds to the boolean formula:
+//
+//     (linux AND 386) OR (darwin AND (NOT cgo))
+//
+// During a particular build, the following words are satisfied:
+//
+//     - the target operating system, as spelled by runtime.GOOS
+//     - the target architecture, as spelled by runtime.GOARCH
+//     - "cgo", if ctxt.CgoEnabled is true
+//     - any additional words listed in ctxt.BuildTags
+//
+// If a file's name, after stripping the extension and a possible _test suffix,
+// matches *_GOOS, *_GOARCH, or *_GOOS_GOARCH for any known operating
+// system and architecture values, then the file is considered to have an implicit
+// build constraint requiring those terms.
+//
+// Examples
+//
+// To keep a file from being considered for the build:
+//
+//     // +build ignore
+//
+// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
+//
+// To build a file only when using cgo, and only on Linux and OS X:
+//
+//     // +build linux,cgo darwin,cgo
+// 
+// Such a file is usually paired with another file implementing the
+// default functionality for other systems, which in this case would
+// carry the constraint:
+//
+//     // +build !linux !darwin !cgo
+//
+// Naming a file dns_windows.go will cause it to be included only when
+// building the package for Windows; similarly, math_386.s will be included
+// only when building the package for 32-bit x86.
 //
 func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
        dirs, err := ctxt.readDir(dir)
@@ -389,7 +448,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
                                if f[0] == "+build" {
                                        ok := false
                                        for _, tok := range f[1:] {
-                                               if ctxt.matchOSArch(tok) {
+                                               if ctxt.match(tok) {
                                                        ok = true
                                                        break
                                                }
@@ -441,7 +500,7 @@ func (ctxt *Context) saveCgo(filename string, di *DirInfo, cg *ast.CommentGroup)
                if len(cond) > 0 {
                        ok := false
                        for _, c := range cond {
-                               if ctxt.matchOSArch(c) {
+                               if ctxt.match(c) {
                                        ok = true
                                        break
                                }
@@ -550,26 +609,55 @@ func splitQuoted(s string) (r []string, err error) {
        return args, err
 }
 
-// matchOSArch returns true if the name is one of:
+// match returns true if the name is one of:
 //
 //     $GOOS
 //     $GOARCH
 //     cgo (if cgo is enabled)
-//     nocgo (if cgo is disabled)
+//     !cgo (if cgo is disabled)
+//     tag (if tag is listed in ctxt.BuildTags)
+//     !tag (if tag is not listed in ctxt.BuildTags)
 //     a slash-separated list of any of these
 //
-func (ctxt *Context) matchOSArch(name string) bool {
-       if ctxt.CgoEnabled && name == "cgo" {
-               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 !ctxt.match(name[1:])
+       }
+
+       // Tags must be letters, digits, underscores.
+       // Unlike in Go identifiers, all digits is fine (e.g., "386").
+       for _, c := range name {
+               if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
+                       return false
+               }
        }
-       if !ctxt.CgoEnabled && name == "nocgo" {
+
+       // special tags
+       if ctxt.CgoEnabled && name == "cgo" {
                return true
        }
        if name == ctxt.GOOS || name == ctxt.GOARCH {
                return true
        }
-       i := strings.Index(name, "/")
-       return i >= 0 && ctxt.matchOSArch(name[:i]) && ctxt.matchOSArch(name[i+1:])
+
+       // other tags
+       for _, tag := range ctxt.BuildTags {
+               if tag == name {
+                       return true
+               }
+       }
+
+       return false
 }
 
 // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
index 66aff837d0a412fc2529b874152600fd46e6b5d2..52e57d7400e075ef3f6ee968bed1616ee93ed792 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build nocgo
+// +build !cgo
 
 // Stub cgo routines for systems that do not use cgo to do network lookups.
 
index 42fa557445f2c21476995473d75b8006352de080..10f5170a9c92666141340fcd50627373ee509871 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build nocgo windows
+// +build !cgo windows
 
 package user