1
0
mirror of https://github.com/golang/go synced 2024-11-13 19:00:25 -07:00

go/build: add AllTags to Package

AllTags lists all the tags that can affect the decision
about which files to include. Tools scanning packages
can use this to decide how many variants there are
and what they are.

R=bradfitz
CC=golang-dev
https://golang.org/cl/12703044
This commit is contained in:
Russ Cox 2013-08-09 18:34:08 -04:00
parent 080e00d55d
commit d9f93b0e0b
3 changed files with 89 additions and 42 deletions

View File

@ -337,16 +337,17 @@ const (
// A Package describes the Go package found in a directory. // A Package describes the Go package found in a directory.
type Package struct { type Package struct {
Dir string // directory containing package sources Dir string // directory containing package sources
Name string // package name Name string // package name
Doc string // documentation synopsis Doc string // documentation synopsis
ImportPath string // import path of package ("" if unknown) ImportPath string // import path of package ("" if unknown)
Root string // root of Go tree where this package lives Root string // root of Go tree where this package lives
SrcRoot string // package source root directory ("" if unknown) SrcRoot string // package source root directory ("" if unknown)
PkgRoot string // package install root directory ("" if unknown) PkgRoot string // package install root directory ("" if unknown)
BinDir string // command install directory ("" if unknown) BinDir string // command install directory ("" if unknown)
Goroot bool // package found in Go root Goroot bool // package found in Go root
PkgObj string // installed .a file PkgObj string // installed .a file
AllTags []string // tags that can influence file selection in this directory
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@ -578,6 +579,7 @@ Found:
imported := make(map[string][]token.Position) imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position) testImported := make(map[string][]token.Position)
xTestImported := make(map[string][]token.Position) xTestImported := make(map[string][]token.Position)
allTags := make(map[string]bool)
fset := token.NewFileSet() fset := token.NewFileSet()
for _, d := range dirs { for _, d := range dirs {
if d.IsDir() { if d.IsDir() {
@ -595,7 +597,7 @@ Found:
} }
ext := name[i:] ext := name[i:]
if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) { if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
if ext == ".go" { if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
} }
@ -634,7 +636,7 @@ Found:
} }
// Look for +build comments to accept or reject the file. // Look for +build comments to accept or reject the file.
if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) { if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles {
if ext == ".go" { if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
} }
@ -736,6 +738,7 @@ Found:
} }
} }
if isCgo { if isCgo {
allTags["cgo"] = true
if ctxt.CgoEnabled { if ctxt.CgoEnabled {
p.CgoFiles = append(p.CgoFiles, name) p.CgoFiles = append(p.CgoFiles, name)
} }
@ -751,6 +754,11 @@ Found:
return p, &NoGoError{p.Dir} return p, &NoGoError{p.Dir}
} }
for tag := range allTags {
p.AllTags = append(p.AllTags, tag)
}
sort.Strings(p.AllTags)
p.Imports, p.ImportPos = cleanImports(imported) p.Imports, p.ImportPos = cleanImports(imported)
p.TestImports, p.TestImportPos = cleanImports(testImported) p.TestImports, p.TestImportPos = cleanImports(testImported)
p.XTestImports, p.XTestImportPos = cleanImports(xTestImported) p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
@ -800,7 +808,7 @@ var slashslash = []byte("//")
// //
// marks the file as applicable only on Windows and Linux. // marks the file as applicable only on Windows and Linux.
// //
func (ctxt *Context) shouldBuild(content []byte) bool { func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) bool {
// Pass 1. Identify leading run of // comments and blank lines, // Pass 1. Identify leading run of // comments and blank lines,
// which must be followed by a blank line. // which must be followed by a blank line.
end := 0 end := 0
@ -825,6 +833,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
// Pass 2. Process each line in the run. // Pass 2. Process each line in the run.
p = content p = content
allok := true
for len(p) > 0 { for len(p) > 0 {
line := p line := p
if i := bytes.IndexByte(line, '\n'); i >= 0 { if i := bytes.IndexByte(line, '\n'); i >= 0 {
@ -841,19 +850,19 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
if f[0] == "+build" { if f[0] == "+build" {
ok := false ok := false
for _, tok := range f[1:] { for _, tok := range f[1:] {
if ctxt.match(tok) { if ctxt.match(tok, allTags) {
ok = true ok = true
break
} }
} }
if !ok { if !ok {
return false // this one doesn't match allok = false
} }
} }
} }
} }
} }
return true // everything matches
return allok
} }
// saveCgo saves the information from the #cgo lines in the import "C" comment. // saveCgo saves the information from the #cgo lines in the import "C" comment.
@ -893,7 +902,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
if len(cond) > 0 { if len(cond) > 0 {
ok := false ok := false
for _, c := range cond { for _, c := range cond {
if ctxt.match(c) { if ctxt.match(c, nil) {
ok = true ok = true
break break
} }
@ -1018,19 +1027,28 @@ func splitQuoted(s string) (r []string, err error) {
// !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags) // !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags)
// a comma-separated list of any of these // a comma-separated list of any of these
// //
func (ctxt *Context) match(name string) bool { func (ctxt *Context) match(name string, allTags map[string]bool) bool {
if name == "" { if name == "" {
if allTags != nil {
allTags[name] = true
}
return false return false
} }
if i := strings.Index(name, ","); i >= 0 { if i := strings.Index(name, ","); i >= 0 {
// comma-separated list // comma-separated list
return ctxt.match(name[:i]) && ctxt.match(name[i+1:]) ok1 := ctxt.match(name[:i], allTags)
ok2 := ctxt.match(name[i+1:], allTags)
return ok1 && ok2
} }
if strings.HasPrefix(name, "!!") { // bad syntax, reject always if strings.HasPrefix(name, "!!") { // bad syntax, reject always
return false return false
} }
if strings.HasPrefix(name, "!") { // negation if strings.HasPrefix(name, "!") { // negation
return len(name) > 1 && !ctxt.match(name[1:]) return len(name) > 1 && !ctxt.match(name[1:], allTags)
}
if allTags != nil {
allTags[name] = true
} }
// Tags must be letters, digits, underscores or dots. // Tags must be letters, digits, underscores or dots.
@ -1075,7 +1093,7 @@ func (ctxt *Context) match(name string) bool {
// name_$(GOARCH)_test.* // name_$(GOARCH)_test.*
// name_$(GOOS)_$(GOARCH)_test.* // name_$(GOOS)_$(GOARCH)_test.*
// //
func (ctxt *Context) goodOSArchFile(name string) bool { func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
if dot := strings.Index(name, "."); dot != -1 { if dot := strings.Index(name, "."); dot != -1 {
name = name[:dot] name = name[:dot]
} }
@ -1085,12 +1103,16 @@ func (ctxt *Context) goodOSArchFile(name string) bool {
} }
n := len(l) n := len(l)
if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
allTags[l[n-2]] = true
allTags[l[n-1]] = true
return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
} }
if n >= 1 && knownOS[l[n-1]] { if n >= 1 && knownOS[l[n-1]] {
allTags[l[n-1]] = true
return l[n-1] == ctxt.GOOS return l[n-1] == ctxt.GOOS
} }
if n >= 1 && knownArch[l[n-1]] { if n >= 1 && knownArch[l[n-1]] {
allTags[l[n-1]] = true
return l[n-1] == ctxt.GOARCH return l[n-1] == ctxt.GOARCH
} }
return true return true

View File

@ -7,6 +7,7 @@ package build
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"runtime" "runtime"
"testing" "testing"
) )
@ -14,29 +15,37 @@ import (
func TestMatch(t *testing.T) { func TestMatch(t *testing.T) {
ctxt := Default ctxt := Default
what := "default" what := "default"
match := func(tag string) { match := func(tag string, want map[string]bool) {
if !ctxt.match(tag) { m := make(map[string]bool)
if !ctxt.match(tag, m) {
t.Errorf("%s context should match %s, does not", what, tag) t.Errorf("%s context should match %s, does not", what, tag)
} }
if !reflect.DeepEqual(m, want) {
t.Errorf("%s tags = %v, want %v", tag, m, want)
}
} }
nomatch := func(tag string) { nomatch := func(tag string, want map[string]bool) {
if ctxt.match(tag) { m := make(map[string]bool)
if ctxt.match(tag, m) {
t.Errorf("%s context should NOT match %s, does", what, tag) t.Errorf("%s context should NOT match %s, does", what, tag)
} }
if !reflect.DeepEqual(m, want) {
t.Errorf("%s tags = %v, want %v", tag, m, want)
}
} }
match(runtime.GOOS + "," + runtime.GOARCH) match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
match(runtime.GOOS + "," + runtime.GOARCH + ",!foo") match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
nomatch(runtime.GOOS + "," + runtime.GOARCH + ",foo") nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
what = "modified" what = "modified"
ctxt.BuildTags = []string{"foo"} ctxt.BuildTags = []string{"foo"}
match(runtime.GOOS + "," + runtime.GOARCH) match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
match(runtime.GOOS + "," + runtime.GOARCH + ",foo") match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
nomatch(runtime.GOOS + "," + runtime.GOARCH + ",!foo") nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
match(runtime.GOOS + "," + runtime.GOARCH + ",!bar") match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar") nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
nomatch("!") nomatch("!", map[string]bool{})
} }
func TestDotSlashImport(t *testing.T) { func TestDotSlashImport(t *testing.T) {
@ -92,28 +101,44 @@ func TestLocalDirectory(t *testing.T) {
func TestShouldBuild(t *testing.T) { func TestShouldBuild(t *testing.T) {
const file1 = "// +build tag1\n\n" + const file1 = "// +build tag1\n\n" +
"package main\n" "package main\n"
want1 := map[string]bool{"tag1": true}
const file2 = "// +build cgo\n\n" + const file2 = "// +build cgo\n\n" +
"// This package implements parsing of tags like\n" + "// This package implements parsing of tags like\n" +
"// +build tag1\n" + "// +build tag1\n" +
"package build" "package build"
want2 := map[string]bool{"cgo": true}
const file3 = "// Copyright The Go Authors.\n\n" + const file3 = "// Copyright The Go Authors.\n\n" +
"package build\n\n" + "package build\n\n" +
"// shouldBuild checks tags given by lines of the form\n" + "// shouldBuild checks tags given by lines of the form\n" +
"// +build tag\n" + "// +build tag\n" +
"func shouldBuild(content []byte)\n" "func shouldBuild(content []byte)\n"
want3 := map[string]bool{}
ctx := &Context{BuildTags: []string{"tag1"}} ctx := &Context{BuildTags: []string{"tag1"}}
if !ctx.shouldBuild([]byte(file1)) { m := map[string]bool{}
t.Errorf("should not build file1, expected the contrary") if !ctx.shouldBuild([]byte(file1), m) {
t.Errorf("shouldBuild(file1) = false, want true")
} }
if ctx.shouldBuild([]byte(file2)) { if !reflect.DeepEqual(m, want1) {
t.Errorf("should build file2, expected the contrary") t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1)
} }
m = map[string]bool{}
if ctx.shouldBuild([]byte(file2), m) {
t.Errorf("shouldBuild(file2) = true, want fakse")
}
if !reflect.DeepEqual(m, want2) {
t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2)
}
m = map[string]bool{}
ctx = &Context{BuildTags: nil} ctx = &Context{BuildTags: nil}
if !ctx.shouldBuild([]byte(file3)) { if !ctx.shouldBuild([]byte(file3), m) {
t.Errorf("should not build file3, expected the contrary") t.Errorf("shouldBuild(file3) = false, want true")
}
if !reflect.DeepEqual(m, want3) {
t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3)
} }
} }

View File

@ -55,7 +55,7 @@ var tests = []GoodFileTest{
func TestGoodOSArch(t *testing.T) { func TestGoodOSArch(t *testing.T) {
for _, test := range tests { for _, test := range tests {
if Default.goodOSArchFile(test.name) != test.result { if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result {
t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
} }
} }