mirror of
https://github.com/golang/go
synced 2024-11-12 09:50:21 -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:
parent
080e00d55d
commit
d9f93b0e0b
@ -337,16 +337,17 @@ const (
|
||||
|
||||
// A Package describes the Go package found in a directory.
|
||||
type Package struct {
|
||||
Dir string // directory containing package sources
|
||||
Name string // package name
|
||||
Doc string // documentation synopsis
|
||||
ImportPath string // import path of package ("" if unknown)
|
||||
Root string // root of Go tree where this package lives
|
||||
SrcRoot string // package source root directory ("" if unknown)
|
||||
PkgRoot string // package install root directory ("" if unknown)
|
||||
BinDir string // command install directory ("" if unknown)
|
||||
Goroot bool // package found in Go root
|
||||
PkgObj string // installed .a file
|
||||
Dir string // directory containing package sources
|
||||
Name string // package name
|
||||
Doc string // documentation synopsis
|
||||
ImportPath string // import path of package ("" if unknown)
|
||||
Root string // root of Go tree where this package lives
|
||||
SrcRoot string // package source root directory ("" if unknown)
|
||||
PkgRoot string // package install root directory ("" if unknown)
|
||||
BinDir string // command install directory ("" if unknown)
|
||||
Goroot bool // package found in Go root
|
||||
PkgObj string // installed .a file
|
||||
AllTags []string // tags that can influence file selection in this directory
|
||||
|
||||
// Source files
|
||||
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||
@ -578,6 +579,7 @@ Found:
|
||||
imported := make(map[string][]token.Position)
|
||||
testImported := make(map[string][]token.Position)
|
||||
xTestImported := make(map[string][]token.Position)
|
||||
allTags := make(map[string]bool)
|
||||
fset := token.NewFileSet()
|
||||
for _, d := range dirs {
|
||||
if d.IsDir() {
|
||||
@ -595,7 +597,7 @@ Found:
|
||||
}
|
||||
ext := name[i:]
|
||||
|
||||
if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
|
||||
if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
|
||||
if ext == ".go" {
|
||||
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
|
||||
}
|
||||
@ -634,7 +636,7 @@ Found:
|
||||
}
|
||||
|
||||
// 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" {
|
||||
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
|
||||
}
|
||||
@ -736,6 +738,7 @@ Found:
|
||||
}
|
||||
}
|
||||
if isCgo {
|
||||
allTags["cgo"] = true
|
||||
if ctxt.CgoEnabled {
|
||||
p.CgoFiles = append(p.CgoFiles, name)
|
||||
}
|
||||
@ -751,6 +754,11 @@ Found:
|
||||
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.TestImports, p.TestImportPos = cleanImports(testImported)
|
||||
p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
|
||||
@ -800,7 +808,7 @@ var slashslash = []byte("//")
|
||||
//
|
||||
// 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,
|
||||
// which must be followed by a blank line.
|
||||
end := 0
|
||||
@ -825,6 +833,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
|
||||
|
||||
// Pass 2. Process each line in the run.
|
||||
p = content
|
||||
allok := true
|
||||
for len(p) > 0 {
|
||||
line := p
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
@ -841,19 +850,19 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
|
||||
if f[0] == "+build" {
|
||||
ok := false
|
||||
for _, tok := range f[1:] {
|
||||
if ctxt.match(tok) {
|
||||
if ctxt.match(tok, allTags) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
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.
|
||||
@ -893,7 +902,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
|
||||
if len(cond) > 0 {
|
||||
ok := false
|
||||
for _, c := range cond {
|
||||
if ctxt.match(c) {
|
||||
if ctxt.match(c, nil) {
|
||||
ok = true
|
||||
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)
|
||||
// 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 allTags != nil {
|
||||
allTags[name] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if i := strings.Index(name, ","); i >= 0 {
|
||||
// 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
|
||||
return false
|
||||
}
|
||||
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.
|
||||
@ -1075,7 +1093,7 @@ func (ctxt *Context) match(name string) bool {
|
||||
// name_$(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 {
|
||||
name = name[:dot]
|
||||
}
|
||||
@ -1085,12 +1103,16 @@ func (ctxt *Context) goodOSArchFile(name string) bool {
|
||||
}
|
||||
n := len(l)
|
||||
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
|
||||
}
|
||||
if n >= 1 && knownOS[l[n-1]] {
|
||||
allTags[l[n-1]] = true
|
||||
return l[n-1] == ctxt.GOOS
|
||||
}
|
||||
if n >= 1 && knownArch[l[n-1]] {
|
||||
allTags[l[n-1]] = true
|
||||
return l[n-1] == ctxt.GOARCH
|
||||
}
|
||||
return true
|
||||
|
@ -7,6 +7,7 @@ package build
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
@ -14,29 +15,37 @@ import (
|
||||
func TestMatch(t *testing.T) {
|
||||
ctxt := Default
|
||||
what := "default"
|
||||
match := func(tag string) {
|
||||
if !ctxt.match(tag) {
|
||||
match := func(tag string, want map[string]bool) {
|
||||
m := make(map[string]bool)
|
||||
if !ctxt.match(tag, m) {
|
||||
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) {
|
||||
if ctxt.match(tag) {
|
||||
nomatch := func(tag string, want map[string]bool) {
|
||||
m := make(map[string]bool)
|
||||
if ctxt.match(tag, m) {
|
||||
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 + ",!foo")
|
||||
nomatch(runtime.GOOS + "," + runtime.GOARCH + ",foo")
|
||||
match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
|
||||
match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
|
||||
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")
|
||||
nomatch("!")
|
||||
match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
|
||||
match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
|
||||
match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
|
||||
nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
|
||||
nomatch("!", map[string]bool{})
|
||||
}
|
||||
|
||||
func TestDotSlashImport(t *testing.T) {
|
||||
@ -92,28 +101,44 @@ func TestLocalDirectory(t *testing.T) {
|
||||
func TestShouldBuild(t *testing.T) {
|
||||
const file1 = "// +build tag1\n\n" +
|
||||
"package main\n"
|
||||
want1 := map[string]bool{"tag1": true}
|
||||
|
||||
const file2 = "// +build cgo\n\n" +
|
||||
"// This package implements parsing of tags like\n" +
|
||||
"// +build tag1\n" +
|
||||
"package build"
|
||||
want2 := map[string]bool{"cgo": true}
|
||||
|
||||
const file3 = "// Copyright The Go Authors.\n\n" +
|
||||
"package build\n\n" +
|
||||
"// shouldBuild checks tags given by lines of the form\n" +
|
||||
"// +build tag\n" +
|
||||
"func shouldBuild(content []byte)\n"
|
||||
want3 := map[string]bool{}
|
||||
|
||||
ctx := &Context{BuildTags: []string{"tag1"}}
|
||||
if !ctx.shouldBuild([]byte(file1)) {
|
||||
t.Errorf("should not build file1, expected the contrary")
|
||||
m := map[string]bool{}
|
||||
if !ctx.shouldBuild([]byte(file1), m) {
|
||||
t.Errorf("shouldBuild(file1) = false, want true")
|
||||
}
|
||||
if ctx.shouldBuild([]byte(file2)) {
|
||||
t.Errorf("should build file2, expected the contrary")
|
||||
if !reflect.DeepEqual(m, want1) {
|
||||
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}
|
||||
if !ctx.shouldBuild([]byte(file3)) {
|
||||
t.Errorf("should not build file3, expected the contrary")
|
||||
if !ctx.shouldBuild([]byte(file3), m) {
|
||||
t.Errorf("shouldBuild(file3) = false, want true")
|
||||
}
|
||||
if !reflect.DeepEqual(m, want3) {
|
||||
t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3)
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ var tests = []GoodFileTest{
|
||||
|
||||
func TestGoodOSArch(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user