Commit b5777571 authored by Russ Cox's avatar Russ Cox

go/build: add BuildTags to Context, allow !tag

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
parent bf0c1903
......@@ -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
}
......
......@@ -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
......
This diff is collapsed.
......@@ -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")
}
......@@ -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
......
......@@ -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.
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment