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:
parent
080e00d55d
commit
d9f93b0e0b
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user