From aa53b37fa6aeb4ea378b5fc833f0aea5d6f9711e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 15 Sep 2013 11:29:47 -0400 Subject: [PATCH] go/build: add ctxt.MatchFile Fixes #6369. R=dsymonds, r CC=golang-dev https://golang.org/cl/13708043 --- src/pkg/go/build/build.go | 152 ++++++++++++++++++++++----------- src/pkg/go/build/build_test.go | 42 +++++++++ 2 files changed, 143 insertions(+), 51 deletions(-) diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go index be48df9d38..d608f0410e 100644 --- a/src/pkg/go/build/build.go +++ b/src/pkg/go/build/build.go @@ -408,6 +408,14 @@ func (e *NoGoError) Error() string { return "no buildable Go source files in " + e.Dir } +func nameExt(name string) string { + i := strings.LastIndex(name, ".") + if i < 0 { + return "" + } + return name[i:] +} + // Import returns details about the Go package named by the import path, // interpreting local import paths relative to the srcDir directory. // If the path is a local import path naming a package that can be imported @@ -591,58 +599,15 @@ Found: if d.IsDir() { continue } + name := d.Name() - if strings.HasPrefix(name, "_") || - strings.HasPrefix(name, ".") { - continue - } + ext := nameExt(name) - i := strings.LastIndex(name, ".") - if i < 0 { - i = len(name) - } - ext := name[i:] - - if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { - if ext == ".go" { - p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) - } - continue - } - - switch ext { - case ".go", ".c", ".cc", ".cxx", ".cpp", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx": - // tentatively okay - read to make sure - case ".syso": - // binary objects to add to package archive - // Likely of the form foo_windows.syso, but - // the name was vetted above with goodOSArchFile. - p.SysoFiles = append(p.SysoFiles, name) - continue - default: - // skip - continue - } - - filename := ctxt.joinPath(p.Dir, name) - f, err := ctxt.openFile(filename) + match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags) if err != nil { return p, err } - - var data []byte - if strings.HasSuffix(filename, ".go") { - data, err = readImports(f, false) - } else { - data, err = readComments(f) - } - f.Close() - if err != nil { - return p, fmt.Errorf("read %s: %v", filename, err) - } - - // Look for +build comments to accept or reject the file. - if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles { + if !match { if ext == ".go" { p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) } @@ -672,6 +637,12 @@ Found: case ".swigcxx": p.SwigCXXFiles = append(p.SwigCXXFiles, name) continue + case ".syso": + // binary objects to add to package archive + // Likely of the form foo_windows.syso, but + // the name was vetted above with goodOSArchFile. + p.SysoFiles = append(p.SysoFiles, name) + continue } pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) @@ -782,6 +753,79 @@ Found: return p, pkgerr } +// MatchFile reports whether the file with the given name in the given directory +// matches the context and would be included in a Package created by ImportDir +// of that directory. +// +// MatchFile considers the name of the file and may use ctxt.OpenFile to +// read some or all of the file's content. +func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { + match, _, _, err = ctxt.matchFile(dir, name, false, nil) + return +} + +// matchFile determines whether the file with the given name in the given directory +// should be included in the package being constructed. +// It returns the data read from the file. +// If returnImports is true and name denotes a Go program, matchFile reads +// until the end of the imports (and returns that data) even though it only +// considers text until the first non-comment. +// If allTags is non-nil, matchFile records any encountered build tag +// by setting allTags[tag] = true. +func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool) (match bool, data []byte, filename string, err error) { + if strings.HasPrefix(name, "_") || + strings.HasPrefix(name, ".") { + return + } + + i := strings.LastIndex(name, ".") + if i < 0 { + i = len(name) + } + ext := name[i:] + + if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { + return + } + + switch ext { + case ".go", ".c", ".cc", ".cxx", ".cpp", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx": + // tentatively okay - read to make sure + case ".syso": + // binary, no reading + match = true + return + default: + // skip + return + } + + filename = ctxt.joinPath(dir, name) + f, err := ctxt.openFile(filename) + if err != nil { + return + } + + if strings.HasSuffix(filename, ".go") { + data, err = readImports(f, false) + } else { + data, err = readComments(f) + } + f.Close() + if err != nil { + err = fmt.Errorf("read %s: %v", filename, err) + return + } + + // Look for +build comments to accept or reject the file. + if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles { + return + } + + match = true + return +} + func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) { all := make([]string, 0, len(m)) for path := range m { @@ -1114,16 +1158,22 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) 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 + if allTags != nil { + 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 + if allTags != nil { + allTags[l[n-1]] = true + } return l[n-1] == ctxt.GOOS } if n >= 1 && knownArch[l[n-1]] { - allTags[l[n-1]] = true + if allTags != nil { + allTags[l[n-1]] = true + } return l[n-1] == ctxt.GOARCH } return true diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go index e36048edec..fca8d4bdb2 100644 --- a/src/pkg/go/build/build_test.go +++ b/src/pkg/go/build/build_test.go @@ -5,10 +5,12 @@ package build import ( + "io" "os" "path/filepath" "reflect" "runtime" + "strings" "testing" ) @@ -142,3 +144,43 @@ func TestShouldBuild(t *testing.T) { t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3) } } + +type readNopCloser struct { + io.Reader +} + +func (r readNopCloser) Close() error { + return nil +} + +var matchFileTests = []struct { + name string + data string + match bool +}{ + {"foo_arm.go", "", true}, + {"foo1_arm.go", "// +build linux\n\npackage main\n", false}, + {"foo_darwin.go", "", false}, + {"foo.go", "", true}, + {"foo1.go", "// +build linux\n\npackage main\n", false}, + {"foo.badsuffix", "", false}, +} + +func TestMatchFile(t *testing.T) { + for _, tt := range matchFileTests { + ctxt := Context{GOARCH: "arm", GOOS: "plan9"} + ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) { + if path != "x+"+tt.name { + t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name) + } + return &readNopCloser{strings.NewReader(tt.data)}, nil + } + ctxt.JoinPath = func(elem ...string) string { + return strings.Join(elem, "+") + } + match, err := ctxt.MatchFile("x", tt.name) + if match != tt.match || err != nil { + t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match) + } + } +}