From 16720d5f2d266a9398a17cd3fac6f7b5267b1751 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Mon, 24 Sep 2018 17:54:34 -0400 Subject: [PATCH] go/packages: allow absolute paths when using the fallback Another change to bring the go1.10 fallback's functionality in line with the go1.11 go list implementation. The fallback now uses go env to determine GOPATH and GOROOT and searches them to see if any match an absolute path, and if so trims the GOPATH/GOROOT entry off the start of the path. Fixes golang/go#27734 Change-Id: Ibd2313fc4301d42fd8c0cd98f1f3e7a313d65eb7 Reviewed-on: https://go-review.googlesource.com/137096 Run-TryBot: Michael Matloob TryBot-Result: Gobot Gobot Reviewed-by: Heschi Kreinick --- go/packages/golist_fallback.go | 48 ++++++++++++++++++++++++++++++++++ go/packages/packages_test.go | 27 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/go/packages/golist_fallback.go b/go/packages/golist_fallback.go index ad5e003c..02816342 100644 --- a/go/packages/golist_fallback.go +++ b/go/packages/golist_fallback.go @@ -10,6 +10,7 @@ import ( "go/build" "io/ioutil" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -27,6 +28,12 @@ import ( // in Q1 2019. func golistDriverFallback(cfg *Config, words ...string) (*driverResponse, error) { + // Turn absolute paths into GOROOT and GOPATH-relative paths to provide to go list. + // This will have surprising behavior if GOROOT or GOPATH contain multiple packages with the same + // path and a user provides an absolute path to a directory that's shadowed by an earlier + // directory in GOROOT or GOPATH with the same package path. + words = cleanAbsPaths(cfg, words) + original, deps, err := getDeps(cfg, words...) if err != nil { return nil, err @@ -283,6 +290,47 @@ func createTestVariants(response *driverResponse, pkgUnderTest, xtestPkg *Packag } } +// cleanAbsPaths replaces all absolute paths with GOPATH- and GOROOT-relative +// paths. If an absolute path is not GOPATH- or GOROOT- relative, it is left as an +// absolute path so an error can be returned later. +func cleanAbsPaths(cfg *Config, words []string) []string { + var searchpaths []string + var cleaned = make([]string, len(words)) + for i := range cleaned { + cleaned[i] = words[i] + if !filepath.IsAbs(cleaned[i]) { + continue + } + // otherwise, it's an absolute path. Search GOPATH and GOROOT to find it. + if searchpaths == nil { + cmd := exec.Command("go", "env", "GOPATH", "GOROOT") + cmd.Env = cfg.Env + out, err := cmd.Output() + if err != nil { + searchpaths = []string{} + continue // suppress the error, it will show up again when running go list + } + lines := strings.Split(string(out), "\n") + if len(lines) != 3 || lines[0] == "" || lines[1] == "" || lines[2] != "" { + continue // suppress error + } + // first line is GOPATH + for _, path := range filepath.SplitList(lines[0]) { + searchpaths = append(searchpaths, filepath.Join(path, "src")) + } + // second line is GOROOT + searchpaths = append(searchpaths, filepath.Join(lines[1], "src")) + } + for _, sp := range searchpaths { + if strings.HasPrefix(cleaned[i], sp) { + cleaned[i] = strings.TrimPrefix(cleaned[i], sp) + cleaned[i] = strings.TrimLeft(cleaned[i], string(filepath.Separator)) + } + } + } + return cleaned +} + // vendorlessPath returns the devendorized version of the import path ipath. // For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b". // Copied from golang.org/x/tools/imports/fix.go. diff --git a/go/packages/packages_test.go b/go/packages/packages_test.go index f23de18c..a91633d4 100644 --- a/go/packages/packages_test.go +++ b/go/packages/packages_test.go @@ -331,6 +331,33 @@ func TestLoadImportsC(t *testing.T) { } } +func TestLoadAbsolutePath(t *testing.T) { + tmp, cleanup := makeTree(t, map[string]string{ + "gopatha/src/a/a.go": `package a`, + "gopathb/src/b/b.go": `package b`, + }) + defer cleanup() + + cfg := &packages.Config{ + Mode: packages.LoadImports, + Env: append(os.Environ(), + "GOPATH="+filepath.Join(tmp, "gopatha")+string(filepath.ListSeparator)+filepath.Join(tmp, "gopathb"), + "GO111MODULE=off"), + } + initial, err := packages.Load(cfg, filepath.Join(tmp, "gopatha", "src", "a"), filepath.Join(tmp, "gopathb", "src", "b")) + if err != nil { + t.Fatalf("failed to load imports: %v", err) + } + + got := []string{} + for _, p := range initial { + got = append(got, p.ID) + } + if !reflect.DeepEqual(got, []string{"a", "b"}) { + t.Fatalf("initial packages loaded: got [%s], want [a b]", got) + } +} + func TestVendorImports(t *testing.T) { tmp, cleanup := makeTree(t, map[string]string{ "src/a/a.go": `package a; import _ "b"; import _ "c";`,