mirror of
https://github.com/golang/go
synced 2024-11-18 21:05:02 -07:00
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 <matloob@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
e1fc249b66
commit
c5a2fd39b7
@ -13,6 +13,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -86,6 +87,23 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start fetching rootDirs
|
||||||
|
var rootDirs map[string]string
|
||||||
|
var rootDirsReady = make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
rootDirs = determineRootDirs(cfg)
|
||||||
|
close(rootDirsReady)
|
||||||
|
}()
|
||||||
|
getRootDirs := func() map[string]string {
|
||||||
|
<-rootDirsReady
|
||||||
|
return rootDirs
|
||||||
|
}
|
||||||
|
|
||||||
|
// always pass getRootDirs to golistDriver
|
||||||
|
golistDriver := func(cfg *Config, patterns ...string) (*driverResponse, error) {
|
||||||
|
return golistDriver(cfg, getRootDirs, patterns...)
|
||||||
|
}
|
||||||
|
|
||||||
// Determine files requested in contains patterns
|
// Determine files requested in contains patterns
|
||||||
var containFiles []string
|
var containFiles []string
|
||||||
var packagesNamed []string
|
var packagesNamed []string
|
||||||
@ -147,7 +165,7 @@ extractQueries:
|
|||||||
var containsCandidates []string
|
var containsCandidates []string
|
||||||
|
|
||||||
if len(containFiles) != 0 {
|
if len(containFiles) != 0 {
|
||||||
if err := runContainsQueries(cfg, golistDriver, response, containFiles); err != nil {
|
if err := runContainsQueries(cfg, golistDriver, response, containFiles, getRootDirs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +176,7 @@ extractQueries:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response)
|
modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getRootDirs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -166,7 +184,7 @@ extractQueries:
|
|||||||
containsCandidates = append(containsCandidates, modifiedPkgs...)
|
containsCandidates = append(containsCandidates, modifiedPkgs...)
|
||||||
containsCandidates = append(containsCandidates, needPkgs...)
|
containsCandidates = append(containsCandidates, needPkgs...)
|
||||||
}
|
}
|
||||||
if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs); err != nil {
|
if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs, getRootDirs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Check candidate packages for containFiles.
|
// Check candidate packages for containFiles.
|
||||||
@ -198,7 +216,7 @@ extractQueries:
|
|||||||
return response.dr, nil
|
return response.dr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error {
|
func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string, getRootDirs func() map[string]string) error {
|
||||||
if len(pkgs) == 0 {
|
if len(pkgs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -209,17 +227,17 @@ func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDedu
|
|||||||
for _, pkg := range dr.Packages {
|
for _, pkg := range dr.Packages {
|
||||||
response.addPackage(pkg)
|
response.addPackage(pkg)
|
||||||
}
|
}
|
||||||
_, needPkgs, err := processGolistOverlay(cfg, response)
|
_, needPkgs, err := processGolistOverlay(cfg, response, getRootDirs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := addNeededOverlayPackages(cfg, driver, response, needPkgs); err != nil {
|
if err := addNeededOverlayPackages(cfg, driver, response, needPkgs, getRootDirs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
|
func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, rootDirs func() map[string]string) error {
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
// TODO(matloob): Do only one query per directory.
|
// TODO(matloob): Do only one query per directory.
|
||||||
fdir := filepath.Dir(query)
|
fdir := filepath.Dir(query)
|
||||||
@ -567,7 +585,7 @@ func otherFiles(p *jsonPackage) [][]string {
|
|||||||
// golistDriver uses the "go list" command to expand the pattern
|
// golistDriver uses the "go list" command to expand the pattern
|
||||||
// words and return metadata for the specified packages. dir may be
|
// words and return metadata for the specified packages. dir may be
|
||||||
// "" and env may be nil, as per os/exec.Command.
|
// "" and env may be nil, as per os/exec.Command.
|
||||||
func golistDriver(cfg *Config, words ...string) (*driverResponse, error) {
|
func golistDriver(cfg *Config, rootsDirs func() map[string]string, words ...string) (*driverResponse, error) {
|
||||||
// go list uses the following identifiers in ImportPath and Imports:
|
// go list uses the following identifiers in ImportPath and Imports:
|
||||||
//
|
//
|
||||||
// "p" -- importable package or main (command)
|
// "p" -- importable package or main (command)
|
||||||
@ -608,6 +626,20 @@ func golistDriver(cfg *Config, words ...string) (*driverResponse, error) {
|
|||||||
return nil, fmt.Errorf("package missing import path: %+v", p)
|
return nil, fmt.Errorf("package missing import path: %+v", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Work around https://golang.org/issue/33157:
|
||||||
|
// 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.
|
||||||
|
// Try toto 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.
|
||||||
|
if filepath.IsAbs(p.ImportPath) && p.Error != nil {
|
||||||
|
pkgPath, ok := getPkgPath(p.ImportPath, rootsDirs)
|
||||||
|
if ok {
|
||||||
|
p.ImportPath = pkgPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if old, found := seen[p.ImportPath]; found {
|
if old, found := seen[p.ImportPath]; found {
|
||||||
if !reflect.DeepEqual(p, old) {
|
if !reflect.DeepEqual(p, old) {
|
||||||
return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
|
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) {
|
|||||||
return &response, nil
|
return &response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getPkgPath finds the package path of a directory if it's relative to a root directory.
|
||||||
|
func getPkgPath(dir string, rootDirs func() map[string]string) (string, bool) {
|
||||||
|
for rdir, rpath := range rootDirs() {
|
||||||
|
// TODO(matloob): This doesn't properly handle symlinks.
|
||||||
|
r, err := filepath.Rel(rdir, dir)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rpath != "" {
|
||||||
|
// We choose only ore root even though the directory even it can belong in multiple modules
|
||||||
|
// or GOPATH entries. This is okay because we only need to work with absolute dirs when a
|
||||||
|
// file is missing from disk, for instance when gopls calls go/packages in an overlay.
|
||||||
|
// Once the file is saved, gopls, or the next invocation of the tool will get the correct
|
||||||
|
// result straight from golist.
|
||||||
|
// TODO(matloob): Implement module tiebreaking?
|
||||||
|
return path.Join(rpath, filepath.ToSlash(r)), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
// absJoin absolutizes and flattens the lists of files.
|
// absJoin absolutizes and flattens the lists of files.
|
||||||
func absJoin(dir string, fileses ...[]string) (res []string) {
|
func absJoin(dir string, fileses ...[]string) (res []string) {
|
||||||
for _, files := range fileses {
|
for _, files := range fileses {
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// processGolistOverlay provides rudimentary support for adding
|
// processGolistOverlay provides rudimentary support for adding
|
||||||
@ -18,7 +17,7 @@ import (
|
|||||||
// sometimes incorrect.
|
// sometimes incorrect.
|
||||||
// TODO(matloob): Handle unsupported cases, including the following:
|
// TODO(matloob): Handle unsupported cases, including the following:
|
||||||
// - determining the correct package to add given a new import path
|
// - determining the correct package to add given a new import path
|
||||||
func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
|
func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func() map[string]string) (modifiedPkgs, needPkgs []string, err error) {
|
||||||
havePkgs := make(map[string]string) // importPath -> non-test package ID
|
havePkgs := make(map[string]string) // importPath -> non-test package ID
|
||||||
needPkgsSet := make(map[string]bool)
|
needPkgsSet := make(map[string]bool)
|
||||||
modifiedPkgsSet := make(map[string]bool)
|
modifiedPkgsSet := make(map[string]bool)
|
||||||
@ -29,9 +28,6 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs,
|
|||||||
havePkgs[pkg.PkgPath] = pkg.ID
|
havePkgs[pkg.PkgPath] = pkg.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootDirs map[string]string
|
|
||||||
var onceGetRootDirs sync.Once
|
|
||||||
|
|
||||||
// If no new imports are added, it is safe to avoid loading any needPkgs.
|
// If no new imports are added, it is safe to avoid loading any needPkgs.
|
||||||
// Otherwise, it's hard to tell which package is actually being loaded
|
// Otherwise, it's hard to tell which package is actually being loaded
|
||||||
// (due to vendoring) and whether any modified package will show up
|
// (due to vendoring) and whether any modified package will show up
|
||||||
@ -76,13 +72,10 @@ func processGolistOverlay(cfg *Config, response *responseDeduper) (modifiedPkgs,
|
|||||||
}
|
}
|
||||||
// The overlay could have included an entirely new package.
|
// The overlay could have included an entirely new package.
|
||||||
if pkg == nil {
|
if pkg == nil {
|
||||||
onceGetRootDirs.Do(func() {
|
|
||||||
rootDirs = determineRootDirs(cfg)
|
|
||||||
})
|
|
||||||
// Try to find the module or gopath dir the file is contained in.
|
// Try to find the module or gopath dir the file is contained in.
|
||||||
// Then for modules, add the module opath to the beginning.
|
// Then for modules, add the module opath to the beginning.
|
||||||
var pkgPath string
|
var pkgPath string
|
||||||
for rdir, rpath := range rootDirs {
|
for rdir, rpath := range rootDirs() {
|
||||||
// TODO(matloob): This doesn't properly handle symlinks.
|
// TODO(matloob): This doesn't properly handle symlinks.
|
||||||
r, err := filepath.Rel(rdir, dir)
|
r, err := filepath.Rel(rdir, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -930,12 +930,6 @@ func testNewPackagesInOverlay(t *testing.T, exporter packagestest.Exporter) {
|
|||||||
"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
|
"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
|
||||||
"c/c.go": `package c; const C = "c"`,
|
"c/c.go": `package c; const C = "c"`,
|
||||||
"d/d.go": `package d; const D = "d"`,
|
"d/d.go": `package d; const D = "d"`,
|
||||||
|
|
||||||
// TODO: Remove these temporary files when golang.org/issue/33157 is resolved.
|
|
||||||
filepath.Join("e/e_temp.go"): ``,
|
|
||||||
filepath.Join("f/f_temp.go"): ``,
|
|
||||||
filepath.Join("g/g_temp.go"): ``,
|
|
||||||
filepath.Join("h/h_temp.go"): ``,
|
|
||||||
}}})
|
}}})
|
||||||
defer exported.Cleanup()
|
defer exported.Cleanup()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user