Skip to content

Commit c5a2fd3

Browse files
committed
go/packages: handle case when go list returns absolute ImportPath
go list -e, when given an absolute path, will find the package contained at that directory. But when no package exists there, it will return a fake package with an error and the ImportPath set to the absolute path provided to go list. This change modifies the go list driver to convert that absolute path to what its package path would be if it's contained in a known module or GOPATH entry. This will allow the package to be properly "reclaimed" when overlays are processed. Fixes golang/go#33157 Change-Id: I5ac032879884f52edbe45e00ed3949d84a71715e Reviewed-on: https://go-review.googlesource.com/c/tools/+/188764 Run-TryBot: Michael Matloob <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]>
1 parent e1fc249 commit c5a2fd3

File tree

3 files changed

+63
-23
lines changed

3 files changed

+63
-23
lines changed

go/packages/golist.go

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"log"
1414
"os"
1515
"os/exec"
16+
"path"
1617
"path/filepath"
1718
"reflect"
1819
"regexp"
@@ -86,6 +87,23 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
8687
}()
8788
}
8889

90+
// start fetching rootDirs
91+
var rootDirs map[string]string
92+
var rootDirsReady = make(chan struct{})
93+
go func() {
94+
rootDirs = determineRootDirs(cfg)
95+
close(rootDirsReady)
96+
}()
97+
getRootDirs := func() map[string]string {
98+
<-rootDirsReady
99+
return rootDirs
100+
}
101+
102+
// always pass getRootDirs to golistDriver
103+
golistDriver := func(cfg *Config, patterns ...string) (*driverResponse, error) {
104+
return golistDriver(cfg, getRootDirs, patterns...)
105+
}
106+
89107
// Determine files requested in contains patterns
90108
var containFiles []string
91109
var packagesNamed []string
@@ -147,7 +165,7 @@ extractQueries:
147165
var containsCandidates []string
148166

149167
if len(containFiles) != 0 {
150-
if err := runContainsQueries(cfg, golistDriver, response, containFiles); err != nil {
168+
if err := runContainsQueries(cfg, golistDriver, response, containFiles, getRootDirs); err != nil {
151169
return nil, err
152170
}
153171
}
@@ -158,15 +176,15 @@ extractQueries:
158176
}
159177
}
160178

161-
modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response)
179+
modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getRootDirs)
162180
if err != nil {
163181
return nil, err
164182
}
165183
if len(containFiles) > 0 {
166184
containsCandidates = append(containsCandidates, modifiedPkgs...)
167185
containsCandidates = append(containsCandidates, needPkgs...)
168186
}
169-
if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs); err != nil {
187+
if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs, getRootDirs); err != nil {
170188
return nil, err
171189
}
172190
// Check candidate packages for containFiles.
@@ -198,7 +216,7 @@ extractQueries:
198216
return response.dr, nil
199217
}
200218

201-
func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error {
219+
func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string, getRootDirs func() map[string]string) error {
202220
if len(pkgs) == 0 {
203221
return nil
204222
}
@@ -209,17 +227,17 @@ func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDedu
209227
for _, pkg := range dr.Packages {
210228
response.addPackage(pkg)
211229
}
212-
_, needPkgs, err := processGolistOverlay(cfg, response)
230+
_, needPkgs, err := processGolistOverlay(cfg, response, getRootDirs)
213231
if err != nil {
214232
return err
215233
}
216-
if err := addNeededOverlayPackages(cfg, driver, response, needPkgs); err != nil {
234+
if err := addNeededOverlayPackages(cfg, driver, response, needPkgs, getRootDirs); err != nil {
217235
return err
218236
}
219237
return nil
220238
}
221239

222-
func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
240+
func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, rootDirs func() map[string]string) error {
223241
for _, query := range queries {
224242
// TODO(matloob): Do only one query per directory.
225243
fdir := filepath.Dir(query)
@@ -567,7 +585,7 @@ func otherFiles(p *jsonPackage) [][]string {
567585
// golistDriver uses the "go list" command to expand the pattern
568586
// words and return metadata for the specified packages. dir may be
569587
// "" and env may be nil, as per os/exec.Command.
570-
func golistDriver(cfg *Config, words ...string) (*driverResponse, error) {
588+
func golistDriver(cfg *Config, rootsDirs func() map[string]string, words ...string) (*driverResponse, error) {
571589
// go list uses the following identifiers in ImportPath and Imports:
572590
//
573591
// "p" -- importable package or main (command)
@@ -608,6 +626,20 @@ func golistDriver(cfg *Config, words ...string) (*driverResponse, error) {
608626
return nil, fmt.Errorf("package missing import path: %+v", p)
609627
}
610628

629+
// Work around https://golang.org/issue/33157:
630+
// go list -e, when given an absolute path, will find the package contained at
631+
// that directory. But when no package exists there, it will return a fake package
632+
// with an error and the ImportPath set to the absolute path provided to go list.
633+
// Try toto convert that absolute path to what its package path would be if it's
634+
// contained in a known module or GOPATH entry. This will allow the package to be
635+
// properly "reclaimed" when overlays are processed.
636+
if filepath.IsAbs(p.ImportPath) && p.Error != nil {
637+
pkgPath, ok := getPkgPath(p.ImportPath, rootsDirs)
638+
if ok {
639+
p.ImportPath = pkgPath
640+
}
641+
}
642+
611643
if old, found := seen[p.ImportPath]; found {
612644
if !reflect.DeepEqual(p, old) {
613645
return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
@@ -711,6 +743,27 @@ func golistDriver(cfg *Config, words ...string) (*driverResponse, error) {
711743
return &response, nil
712744
}
713745

746+
// getPkgPath finds the package path of a directory if it's relative to a root directory.
747+
func getPkgPath(dir string, rootDirs func() map[string]string) (string, bool) {
748+
for rdir, rpath := range rootDirs() {
749+
// TODO(matloob): This doesn't properly handle symlinks.
750+
r, err := filepath.Rel(rdir, dir)
751+
if err != nil {
752+
continue
753+
}
754+
if rpath != "" {
755+
// We choose only ore root even though the directory even it can belong in multiple modules
756+
// or GOPATH entries. This is okay because we only need to work with absolute dirs when a
757+
// file is missing from disk, for instance when gopls calls go/packages in an overlay.
758+
// Once the file is saved, gopls, or the next invocation of the tool will get the correct
759+
// result straight from golist.
760+
// TODO(matloob): Implement module tiebreaking?
761+
return path.Join(rpath, filepath.ToSlash(r)), true
762+
}
763+
}
764+
return "", false
765+
}
766+
714767
// absJoin absolutizes and flattens the lists of files.
715768
func absJoin(dir string, fileses ...[]string) (res []string) {
716769
for _, files := range fileses {

go/packages/golist_overlay.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@ import (
1010
"path/filepath"
1111
"strconv"
1212
"strings"
13-
"sync"
1413
)
1514

1615
// processGolistOverlay provides rudimentary support for adding
1716
// files that don't exist on disk to an overlay. The results can be
1817
// sometimes incorrect.
1918
// TODO(matloob): Handle unsupported cases, including the following:
2019
// - determining the correct package to add given a new import path
21-
func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
20+
func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func() map[string]string) (modifiedPkgs, needPkgs []string, err error) {
2221
havePkgs := make(map[string]string) // importPath -> non-test package ID
2322
needPkgsSet := make(map[string]bool)
2423
modifiedPkgsSet := make(map[string]bool)
@@ -29,9 +28,6 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs,
2928
havePkgs[pkg.PkgPath] = pkg.ID
3029
}
3130

32-
var rootDirs map[string]string
33-
var onceGetRootDirs sync.Once
34-
3531
// If no new imports are added, it is safe to avoid loading any needPkgs.
3632
// Otherwise, it's hard to tell which package is actually being loaded
3733
// (due to vendoring) and whether any modified package will show up
@@ -76,13 +72,10 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs,
7672
}
7773
// The overlay could have included an entirely new package.
7874
if pkg == nil {
79-
onceGetRootDirs.Do(func() {
80-
rootDirs = determineRootDirs(cfg)
81-
})
8275
// Try to find the module or gopath dir the file is contained in.
8376
// Then for modules, add the module opath to the beginning.
8477
var pkgPath string
85-
for rdir, rpath := range rootDirs {
78+
for rdir, rpath := range rootDirs() {
8679
// TODO(matloob): This doesn't properly handle symlinks.
8780
r, err := filepath.Rel(rdir, dir)
8881
if err != nil {

go/packages/packages_test.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -930,12 +930,6 @@ func testNewPackagesInOverlay(t *testing.T, exporter packagestest.Exporter) {
930930
"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
931931
"c/c.go": `package c; const C = "c"`,
932932
"d/d.go": `package d; const D = "d"`,
933-
934-
// TODO: Remove these temporary files when golang.org/issue/33157 is resolved.
935-
filepath.Join("e/e_temp.go"): ``,
936-
filepath.Join("f/f_temp.go"): ``,
937-
filepath.Join("g/g_temp.go"): ``,
938-
filepath.Join("h/h_temp.go"): ``,
939933
}}})
940934
defer exported.Cleanup()
941935

0 commit comments

Comments
 (0)