diff --git a/go/packages/doc.go b/go/packages/doc.go index d4984a58..9b067fb0 100644 --- a/go/packages/doc.go +++ b/go/packages/doc.go @@ -214,9 +214,6 @@ application, but not by the metadata query, so, for example: Questions & Tasks -- Add this pass-through option for the underlying query tool: - Flags []string - - Add GOARCH/GOOS? They are not portable concepts, but could be made portable. Our goal has been to allow users to express themselves using the conventions @@ -230,8 +227,6 @@ Questions & Tasks However, this approach is low-level, unwieldy, and non-portable. GOOS and GOARCH seem important enough to warrant a dedicated option. -- Build tags: where do they fit in? How does Bazel/Blaze handle them? - - How should we handle partial failures such as a mixture of good and malformed patterns, existing and non-existent packages, succesful and failed builds, import failures, import cycles, and so on, in a call to diff --git a/go/packages/golist_fallback.go b/go/packages/golist_fallback.go index df72e0d6..b84dda39 100644 --- a/go/packages/golist_fallback.go +++ b/go/packages/golist_fallback.go @@ -96,7 +96,7 @@ func golistPackagesFallback(cfg *rawConfig, words ...string) ([]*rawPackage, err return result, nil } - buf, err := golist(cfg, append([]string{"list", "-e", "-json", "--"}, deps...)) + buf, err := golist(cfg, golistargs_fallback(cfg, deps)) if err != nil { return nil, err } @@ -116,7 +116,7 @@ func golistPackagesFallback(cfg *rawConfig, words ...string) ([]*rawPackage, err // getDeps runs an initial go list to determine all the dependency packages. func getDeps(cfg *rawConfig, words ...string) (originalSet map[string]*jsonPackage, deps []string, err error) { - buf, err := golist(cfg, append([]string{"list", "-e", "-json", "--"}, words...)) + buf, err := golist(cfg, golistargs_fallback(cfg, words)) if err != nil { return nil, nil, err } @@ -146,3 +146,11 @@ func getDeps(cfg *rawConfig, words ...string) (originalSet map[string]*jsonPacka } return originalSet, deps, nil } + +func golistargs_fallback(cfg *rawConfig, words []string) []string { + fullargs := []string{"list", "-e", "-json"} + fullargs = append(fullargs, cfg.ExtraFlags...) + fullargs = append(fullargs, "--") + fullargs = append(fullargs, words...) + return fullargs +} diff --git a/go/packages/packages.go b/go/packages/packages.go index c124f7b6..9c786490 100644 --- a/go/packages/packages.go +++ b/go/packages/packages.go @@ -78,6 +78,10 @@ type Config struct { // Env []string + // Flags is a list of command-line flags to be passed through to + // the underlying query tool. + Flags []string + // Error is called for each error encountered during package loading. // It must be safe to call Error simultaneously from multiple goroutines. // In addition to calling Error, the loader will record each error diff --git a/go/packages/packages_test.go b/go/packages/packages_test.go index 3a63908d..1ffa1c4e 100644 --- a/go/packages/packages_test.go +++ b/go/packages/packages_test.go @@ -37,6 +37,7 @@ var usesOldGolist = false // import error) will result in a JSON blob with no name and a // nonexistent testmain file in GoFiles. Test that we handle this // gracefully. +// - test more Flags. // // TypeCheck & WholeProgram modes: // - Fset may be user-supplied or not. @@ -269,7 +270,7 @@ func imports(p *packages.Package) []string { return keys } -func TestOptionsDir(t *testing.T) { +func TestConfigDir(t *testing.T) { tmp, cleanup := makeTree(t, map[string]string{ "src/a/a.go": `package a; const Name = "a" `, "src/a/b/b.go": `package b; const Name = "a/b"`, @@ -316,6 +317,64 @@ func TestOptionsDir(t *testing.T) { } +func TestConfigFlags(t *testing.T) { + // Test satisfying +build line tags, with -tags flag. + tmp, cleanup := makeTree(t, map[string]string{ + // package a + "src/a/a.go": `package a; import _ "a/b"`, + "src/a/b.go": `// +build tag + +package a`, + "src/a/c.go": `// +build tag tag2 + +package a`, + "src/a/d.go": `// +build tag,tag2 + +package a`, + // package a/b + "src/a/b/a.go": `package b`, + "src/a/b/b.go": `// +build tag + +package b`, + }) + defer cleanup() + + for _, test := range []struct { + pattern string + tags []string + wantSrcs string + wantImportSrcs map[string]string + }{ + {`a`, []string{}, "a.go", map[string]string{"a/b": "a.go"}}, + {`a`, []string{`-tags=tag`}, "a.go b.go c.go", map[string]string{"a/b": "a.go b.go"}}, + {`a`, []string{`-tags=tag2`}, "a.go c.go", map[string]string{"a/b": "a.go"}}, + {`a`, []string{`-tags=tag tag2`}, "a.go b.go c.go d.go", map[string]string{"a/b": "a.go b.go"}}, + } { + cfg := &packages.Config{ + Mode: packages.LoadFiles, + Flags: test.tags, + Env: append(os.Environ(), "GOPATH="+tmp), + } + + initial, err := packages.Load(cfg, test.pattern) + if err != nil { + t.Error(err) + } + if len(initial) != 1 { + t.Errorf("test tags %v: pattern %s, expected 1 package, got %d packages.", test.tags, test.pattern, len(initial)) + } + pkg := initial[0] + if srcs := strings.Join(srcs(pkg), " "); srcs != test.wantSrcs { + t.Errorf("test tags %v: srcs of package %s = [%s], want [%s]", test.tags, test.pattern, srcs, test.wantSrcs) + } + for path, ipkg := range pkg.Imports { + if srcs := strings.Join(srcs(ipkg), " "); srcs != test.wantImportSrcs[path] { + t.Errorf("build tags %v: srcs of imported package %s = [%s], want [%s]", test.tags, path, srcs, test.wantImportSrcs[path]) + } + } + } +} + type errCollector struct { mu sync.Mutex errors []error diff --git a/go/packages/raw.go b/go/packages/raw.go index 0affe3ff..58a66eb1 100644 --- a/go/packages/raw.go +++ b/go/packages/raw.go @@ -51,22 +51,24 @@ type rawPackage struct { // rawConfig specifies details about what raw package information is needed // and how the underlying build tool should load package data. type rawConfig struct { - Context context.Context - Dir string - Env []string - Export bool - Tests bool - Deps bool + Context context.Context + Dir string + Env []string + ExtraFlags []string + Export bool + Tests bool + Deps bool } func newRawConfig(cfg *Config) *rawConfig { rawCfg := &rawConfig{ - Context: cfg.Context, - Dir: cfg.Dir, - Env: cfg.Env, - Export: cfg.Mode > LoadImports && cfg.Mode < LoadAllSyntax, - Tests: cfg.Tests, - Deps: cfg.Mode >= LoadImports, + Context: cfg.Context, + Dir: cfg.Dir, + Env: cfg.Env, + ExtraFlags: cfg.Flags, + Export: cfg.Mode > LoadImports && cfg.Mode < LoadAllSyntax, + Tests: cfg.Tests, + Deps: cfg.Mode >= LoadImports, } if rawCfg.Env == nil { rawCfg.Env = os.Environ() @@ -75,9 +77,11 @@ func newRawConfig(cfg *Config) *rawConfig { } func (cfg *rawConfig) Flags() []string { - return []string{ + return append([]string{ fmt.Sprintf("-test=%t", cfg.Tests), fmt.Sprintf("-export=%t", cfg.Export), fmt.Sprintf("-deps=%t", cfg.Deps), - } + }, + cfg.ExtraFlags..., + ) }