1
0
mirror of https://github.com/golang/go synced 2024-11-18 19:54:44 -07:00

go/packages: add support for 'contains:' words

If a 'word' provided to go/packages' Load function starts with contains,
go/packages will interpret that word as the package containing the given
file.

For example:
   packages.Load(config, "contains:/usr/local/go/src/fmt/format.go")
would load the fmt package from the Go installation at /usr/local/go.

This implementation uses "go list ." in the directory the file is
contained in to find the package, but this won't work in the module
cache. We plan to add support to go list directly to help find the
containing package. Then, because we won't need to change directory,
go list will have knowledge of the correct vgo root module, and will
be able to surface correct results.

Change-Id: I6bff62447c12f13dae5e4c0c65f729d9f271c388
Reviewed-on: https://go-review.googlesource.com/126177
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Michael Matloob 2018-07-25 16:40:23 -04:00
parent bfb5194568
commit 9dbe528bbb
2 changed files with 93 additions and 2 deletions

View File

@ -18,6 +18,8 @@ import (
"sync"
"golang.org/x/tools/go/gcexportdata"
"path/filepath"
"strings"
)
// A LoadMode specifies the amount of detail to return when loading packages.
@ -231,6 +233,12 @@ func newLoader(cfg *Config) *loader {
if ld.Context == nil {
ld.Context = context.Background()
}
// Determine directory to be used for relative contains: paths.
if ld.Dir == "" {
if cwd, err := os.Getwd(); err == nil {
ld.Dir = cwd
}
}
if ld.Mode >= LoadSyntax {
if ld.Fset == nil {
ld.Fset = token.NewFileSet()
@ -257,21 +265,79 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
return nil, fmt.Errorf("no packages to load")
}
if ld.Dir == "" {
return nil, fmt.Errorf("failed to get working directory")
}
// Determine files requested in contains patterns
var containFiles []string
{
restPatterns := patterns[:0]
for _, pattern := range patterns {
if containFile := strings.TrimPrefix(pattern, "contains:"); containFile != pattern {
containFiles = append(containFiles, containFile)
} else {
restPatterns = append(restPatterns, pattern)
}
}
containFiles = absJoin(ld.Dir, containFiles)
patterns = restPatterns
}
// Do the metadata query and partial build.
// TODO(adonovan): support alternative build systems at this seam.
rawCfg := newRawConfig(&ld.Config)
list, err := golistPackages(rawCfg, patterns...)
listfunc := golistPackages
// TODO(matloob): Patterns may now be empty, if it was solely comprised of contains: patterns.
// See if the extra process invocation can be avoided.
list, err := listfunc(rawCfg, patterns...)
if _, ok := err.(GoTooOldError); ok {
if ld.Config.Mode >= LoadTypes {
// Upgrade to LoadAllSyntax because we can't depend on the existance
// of export data. We can remove this once iancottrell's cl is in.
ld.Config.Mode = LoadAllSyntax
}
list, err = golistPackagesFallback(rawCfg, patterns...)
listfunc = golistPackagesFallback
list, err = listfunc(rawCfg, patterns...)
}
if err != nil {
return nil, err
}
// Run go list for contains: patterns.
seenPkgs := make(map[string]bool) // for deduplication. different containing queries could produce same packages
if len(containFiles) > 0 {
for _, pkg := range list {
seenPkgs[pkg.ID] = true
}
}
for _, f := range containFiles {
// TODO(matloob): Do only one query per directory.
fdir := filepath.Dir(f)
rawCfg.Dir = fdir
cList, err := listfunc(rawCfg, ".")
if err != nil {
return nil, err
}
// Deduplicate and set deplist to set of packages requested files.
dedupedList := cList[:0] // invariant: only packages that haven't been seen before
for _, pkg := range cList {
if seenPkgs[pkg.ID] {
continue
}
seenPkgs[pkg.ID] = true
dedupedList = append(dedupedList, pkg)
pkg.DepOnly = true
for _, pkgFile := range pkg.GoFiles {
if filepath.Base(f) == filepath.Base(pkgFile) {
pkg.DepOnly = false
break
}
}
}
list = append(list, dedupedList...)
}
return ld.loadFrom(list...)
}

View File

@ -732,6 +732,31 @@ func TestAbsoluteFilenames(t *testing.T) {
}
}
func TestContains(t *testing.T) {
tmp, cleanup := makeTree(t, map[string]string{
"src/a/a.go": `package a; import "b"`,
"src/b/b.go": `package b; import "c"`,
"src/c/c.go": `package c`,
})
defer cleanup()
opts := &packages.Config{Env: append(os.Environ(), "GOPATH="+tmp), Dir: tmp, Mode: packages.LoadImports}
initial, err := packages.Load(opts, "contains:src/b/b.go")
if err != nil {
t.Fatal(err)
}
graph, _ := importGraph(initial)
wantGraph := `
* b
c
b -> c
`[1:]
if graph != wantGraph {
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
}
}
func errorMessages(errors []error) []string {
var msgs []string
for _, err := range errors {