mirror of https://github.com/golang/go.git
go/packages: remove named query
I removed the usage of this from goimports. Change-Id: If10a5d6265e412e77f7b0a2671ee2106ee4abbf6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/215937 Run-TryBot: Heschi Kreinick <heschi@google.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
0a06c69952
commit
7b0c362a2f
|
|
@ -9,14 +9,12 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -24,8 +22,6 @@ import (
|
|||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/internal/packagesdriver"
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
"golang.org/x/tools/internal/semver"
|
||||
)
|
||||
|
||||
// debug controls verbose logging.
|
||||
|
|
@ -139,7 +135,6 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
|||
|
||||
// Determine files requested in contains patterns
|
||||
var containFiles []string
|
||||
var packagesNamed []string
|
||||
restPatterns := make([]string, 0, len(patterns))
|
||||
// Extract file= and other [querytype]= patterns. Report an error if querytype
|
||||
// doesn't exist.
|
||||
|
|
@ -155,8 +150,6 @@ extractQueries:
|
|||
containFiles = append(containFiles, value)
|
||||
case "pattern":
|
||||
restPatterns = append(restPatterns, value)
|
||||
case "iamashamedtousethedisabledqueryname":
|
||||
packagesNamed = append(packagesNamed, value)
|
||||
case "": // not a reserved query
|
||||
restPatterns = append(restPatterns, pattern)
|
||||
default:
|
||||
|
|
@ -203,12 +196,6 @@ extractQueries:
|
|||
}
|
||||
}
|
||||
|
||||
if len(packagesNamed) != 0 {
|
||||
if err := runNamedQueries(cfg, golistDriver, response, packagesNamed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -342,278 +329,10 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q
|
|||
return nil
|
||||
}
|
||||
|
||||
// modCacheRegexp splits a path in a module cache into module, module version, and package.
|
||||
var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
|
||||
|
||||
func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
|
||||
// calling `go env` isn't free; bail out if there's nothing to do.
|
||||
if len(queries) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Determine which directories are relevant to scan.
|
||||
roots, modRoot, err := roots(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Scan the selected directories. Simple matches, from GOPATH/GOROOT
|
||||
// or the local module, can simply be "go list"ed. Matches from the
|
||||
// module cache need special treatment.
|
||||
var matchesMu sync.Mutex
|
||||
var simpleMatches, modCacheMatches []string
|
||||
add := func(root gopathwalk.Root, dir string) {
|
||||
// Walk calls this concurrently; protect the result slices.
|
||||
matchesMu.Lock()
|
||||
defer matchesMu.Unlock()
|
||||
|
||||
path := dir
|
||||
if dir != root.Path {
|
||||
path = dir[len(root.Path)+1:]
|
||||
}
|
||||
if pathMatchesQueries(path, queries) {
|
||||
switch root.Type {
|
||||
case gopathwalk.RootModuleCache:
|
||||
modCacheMatches = append(modCacheMatches, path)
|
||||
case gopathwalk.RootCurrentModule:
|
||||
// We'd need to read go.mod to find the full
|
||||
// import path. Relative's easier.
|
||||
rel, err := filepath.Rel(cfg.Dir, dir)
|
||||
if err != nil {
|
||||
// This ought to be impossible, since
|
||||
// we found dir in the current module.
|
||||
panic(err)
|
||||
}
|
||||
simpleMatches = append(simpleMatches, "./"+rel)
|
||||
case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT:
|
||||
simpleMatches = append(simpleMatches, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startWalk := time.Now()
|
||||
gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug})
|
||||
cfg.Logf("%v for walk", time.Since(startWalk))
|
||||
|
||||
// Weird special case: the top-level package in a module will be in
|
||||
// whatever directory the user checked the repository out into. It's
|
||||
// more reasonable for that to not match the package name. So, if there
|
||||
// are any Go files in the mod root, query it just to be safe.
|
||||
if modRoot != "" {
|
||||
rel, err := filepath.Rel(cfg.Dir, modRoot)
|
||||
if err != nil {
|
||||
panic(err) // See above.
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(modRoot)
|
||||
if err != nil {
|
||||
panic(err) // See above.
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if strings.HasSuffix(f.Name(), ".go") {
|
||||
simpleMatches = append(simpleMatches, rel)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addResponse := func(r *driverResponse) {
|
||||
for _, pkg := range r.Packages {
|
||||
response.addPackage(pkg)
|
||||
for _, name := range queries {
|
||||
if pkg.Name == name {
|
||||
response.addRoot(pkg.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(simpleMatches) != 0 {
|
||||
resp, err := driver(cfg, simpleMatches...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addResponse(resp)
|
||||
}
|
||||
|
||||
// Module cache matches are tricky. We want to avoid downloading new
|
||||
// versions of things, so we need to use the ones present in the cache.
|
||||
// go list doesn't accept version specifiers, so we have to write out a
|
||||
// temporary module, and do the list in that module.
|
||||
if len(modCacheMatches) != 0 {
|
||||
// Collect all the matches, deduplicating by major version
|
||||
// and preferring the newest.
|
||||
type modInfo struct {
|
||||
mod string
|
||||
major string
|
||||
}
|
||||
mods := make(map[modInfo]string)
|
||||
var imports []string
|
||||
for _, modPath := range modCacheMatches {
|
||||
matches := modCacheRegexp.FindStringSubmatch(modPath)
|
||||
mod, ver := filepath.ToSlash(matches[1]), matches[2]
|
||||
importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3]))
|
||||
|
||||
major := semver.Major(ver)
|
||||
if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 {
|
||||
mods[modInfo{mod, major}] = ver
|
||||
}
|
||||
|
||||
imports = append(imports, importPath)
|
||||
}
|
||||
|
||||
// Build the temporary module.
|
||||
var gomod bytes.Buffer
|
||||
gomod.WriteString("module modquery\nrequire (\n")
|
||||
for mod, version := range mods {
|
||||
gomod.WriteString("\t" + mod.mod + " " + version + "\n")
|
||||
}
|
||||
gomod.WriteString(")\n")
|
||||
|
||||
tmpCfg := *cfg
|
||||
|
||||
// We're only trying to look at stuff in the module cache, so
|
||||
// disable the network. This should speed things up, and has
|
||||
// prevented errors in at least one case, #28518.
|
||||
tmpCfg.Env = append([]string{"GOPROXY=off"}, cfg.Env...)
|
||||
|
||||
var err error
|
||||
tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpCfg.Dir)
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil {
|
||||
return fmt.Errorf("writing go.mod for module cache query: %v", err)
|
||||
}
|
||||
|
||||
// Run the query, using the import paths calculated from the matches above.
|
||||
resp, err := driver(&tmpCfg, imports...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("querying module cache matches: %v", err)
|
||||
}
|
||||
addResponse(resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSizes(cfg *Config) (types.Sizes, error) {
|
||||
return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
|
||||
}
|
||||
|
||||
// roots selects the appropriate paths to walk based on the passed-in configuration,
|
||||
// particularly the environment and the presence of a go.mod in cfg.Dir's parents.
|
||||
func roots(cfg *Config) ([]gopathwalk.Root, string, error) {
|
||||
stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
fields := strings.Split(stdout.String(), "\n")
|
||||
if len(fields) != 4 || len(fields[3]) != 0 {
|
||||
return nil, "", fmt.Errorf("go env returned unexpected output: %q", stdout.String())
|
||||
}
|
||||
goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2]
|
||||
var modDir string
|
||||
if gomod != "" {
|
||||
modDir = filepath.Dir(gomod)
|
||||
}
|
||||
|
||||
var roots []gopathwalk.Root
|
||||
// Always add GOROOT.
|
||||
roots = append(roots, gopathwalk.Root{
|
||||
Path: filepath.Join(goroot, "/src"),
|
||||
Type: gopathwalk.RootGOROOT,
|
||||
})
|
||||
// If modules are enabled, scan the module dir.
|
||||
if modDir != "" {
|
||||
roots = append(roots, gopathwalk.Root{
|
||||
Path: modDir,
|
||||
Type: gopathwalk.RootCurrentModule,
|
||||
})
|
||||
}
|
||||
// Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode.
|
||||
for _, p := range gopath {
|
||||
if modDir != "" {
|
||||
roots = append(roots, gopathwalk.Root{
|
||||
Path: filepath.Join(p, "/pkg/mod"),
|
||||
Type: gopathwalk.RootModuleCache,
|
||||
})
|
||||
} else {
|
||||
roots = append(roots, gopathwalk.Root{
|
||||
Path: filepath.Join(p, "/src"),
|
||||
Type: gopathwalk.RootGOPATH,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return roots, modDir, nil
|
||||
}
|
||||
|
||||
// These functions were copied from goimports. See further documentation there.
|
||||
|
||||
// pathMatchesQueries is adapted from pkgIsCandidate.
|
||||
// TODO: is it reasonable to do Contains here, rather than an exact match on a path component?
|
||||
func pathMatchesQueries(path string, queries []string) bool {
|
||||
lastTwo := lastTwoComponents(path)
|
||||
for _, query := range queries {
|
||||
if strings.Contains(lastTwo, query) {
|
||||
return true
|
||||
}
|
||||
if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) {
|
||||
lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
|
||||
if strings.Contains(lastTwo, query) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lastTwoComponents returns at most the last two path components
|
||||
// of v, using either / or \ as the path separator.
|
||||
func lastTwoComponents(v string) string {
|
||||
nslash := 0
|
||||
for i := len(v) - 1; i >= 0; i-- {
|
||||
if v[i] == '/' || v[i] == '\\' {
|
||||
nslash++
|
||||
if nslash == 2 {
|
||||
return v[i:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func hasHyphenOrUpperASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
if b == '-' || ('A' <= b && b <= 'Z') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func lowerASCIIAndRemoveHyphen(s string) (ret string) {
|
||||
buf := make([]byte, 0, len(s))
|
||||
for i := 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case b == '-':
|
||||
continue
|
||||
case 'A' <= b && b <= 'Z':
|
||||
buf = append(buf, b+('a'-'A'))
|
||||
default:
|
||||
buf = append(buf, b)
|
||||
}
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// Fields must match go list;
|
||||
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
|
||||
type jsonPackage struct {
|
||||
|
|
|
|||
|
|
@ -1595,138 +1595,6 @@ func testContainsFallbackSticks(t *testing.T, exporter packagestest.Exporter) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) { packagestest.TestAll(t, testName) }
|
||||
func testName(t *testing.T, exporter packagestest.Exporter) {
|
||||
exported := packagestest.Export(t, exporter, []packagestest.Module{{
|
||||
Name: "golang.org/fake",
|
||||
Files: map[string]interface{}{
|
||||
"a/needle/needle.go": `package needle; import "golang.org/fake/c"`,
|
||||
"b/needle/needle.go": `package needle;`,
|
||||
"c/c.go": `package c;`,
|
||||
"irrelevant/irrelevant.go": `package irrelevant;`,
|
||||
}}})
|
||||
defer exported.Cleanup()
|
||||
|
||||
exported.Config.Mode = packages.LoadImports
|
||||
initial, err := packages.Load(exported.Config, "iamashamedtousethedisabledqueryname=needle")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
graph, _ := importGraph(initial)
|
||||
wantGraph := `
|
||||
* golang.org/fake/a/needle
|
||||
* golang.org/fake/b/needle
|
||||
golang.org/fake/c
|
||||
golang.org/fake/a/needle -> golang.org/fake/c
|
||||
`[1:]
|
||||
if graph != wantGraph {
|
||||
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
|
||||
}
|
||||
}
|
||||
|
||||
func TestName_Modules(t *testing.T) {
|
||||
// Test the top-level package case described in runNamedQueries.
|
||||
exported := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
|
||||
{
|
||||
Name: "golang.org/pkg",
|
||||
Files: map[string]interface{}{
|
||||
"pkg.go": `package pkg`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "example.com/tools-testrepo",
|
||||
Files: map[string]interface{}{
|
||||
"pkg/pkg.go": `package pkg`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "example.com/tools-testrepo/v2",
|
||||
Files: map[string]interface{}{
|
||||
"pkg/pkg.go": `package pkg`,
|
||||
},
|
||||
},
|
||||
})
|
||||
defer exported.Cleanup()
|
||||
|
||||
exported.Config.Mode = packages.LoadImports
|
||||
initial, err := packages.Load(exported.Config, "iamashamedtousethedisabledqueryname=pkg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
graph, _ := importGraph(initial)
|
||||
wantGraph := `
|
||||
* example.com/tools-testrepo/pkg
|
||||
* example.com/tools-testrepo/v2/pkg
|
||||
* golang.org/pkg
|
||||
`[1:]
|
||||
if graph != wantGraph {
|
||||
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
|
||||
}
|
||||
}
|
||||
|
||||
func TestName_ModulesDedup(t *testing.T) {
|
||||
exported := packagestest.Export(t, packagestest.Modules, []packagestest.Module{{
|
||||
Name: "golang.org/fake",
|
||||
Files: map[string]interface{}{
|
||||
"fake.go": `package fake`,
|
||||
}}})
|
||||
defer exported.Cleanup()
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gopath, err := ioutil.TempDir("", "TestName_ModulesDedup")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(gopath)
|
||||
if err := copyAll(filepath.Join(wd, "testdata", "TestName_ModulesDedup"), gopath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// testdata/TestNamed_ModulesDedup contains:
|
||||
// - pkg/mod/github.com/heschik/tools-testrepo/v2@v2.0.2/pkg/pkg.go
|
||||
// - pkg/mod/github.com/heschik/tools-testrepo/v2@v2.0.1/pkg/pkg.go
|
||||
// - pkg/mod/github.com/heschik/tools-testrepo@v1.0.0/pkg/pkg.go
|
||||
// but, inexplicably, not v2.0.0. Nobody knows why.
|
||||
exported.Config.Mode = packages.LoadImports
|
||||
exported.Config.Env = append(exported.Config.Env, "GOPATH="+gopath)
|
||||
initial, err := packages.Load(exported.Config, "iamashamedtousethedisabledqueryname=pkg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, pkg := range initial {
|
||||
if strings.Contains(pkg.PkgPath, "v2") {
|
||||
if strings.Contains(pkg.GoFiles[0], "v2.0.2") {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Errorf("didn't find v2.0.2 of pkg in Load results: %v", initial)
|
||||
}
|
||||
|
||||
// Test that Load doesn't get confused when two different patterns match the same package. See #29297.
|
||||
func TestRedundantQueries(t *testing.T) { packagestest.TestAll(t, testRedundantQueries) }
|
||||
func testRedundantQueries(t *testing.T, exporter packagestest.Exporter) {
|
||||
exported := packagestest.Export(t, exporter, []packagestest.Module{{
|
||||
Name: "golang.org/fake",
|
||||
Files: map[string]interface{}{
|
||||
"a/a.go": `package a;`,
|
||||
}}})
|
||||
defer exported.Cleanup()
|
||||
|
||||
cfg := *exported.Config
|
||||
cfg.Tests = false
|
||||
|
||||
initial, err := packages.Load(&cfg, "errors", "iamashamedtousethedisabledqueryname=errors")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(initial) != 1 || initial[0].Name != "errors" {
|
||||
t.Fatalf(`Load("errors", "iamashamedtousethedisabledqueryname=errors") = %v, wanted just the errors package`, initial)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that Load with no patterns is equivalent to loading "." via the golist
|
||||
// driver.
|
||||
func TestNoPatterns(t *testing.T) { packagestest.TestAll(t, testNoPatterns) }
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
Test data directories here were created by running go commands with GOPATH set as such:
|
||||
GOPATH=......./testdata/TestNamed_ModulesDedup go get github.com/heschik/tools-testrepo/v2@v2.0.1
|
||||
and then removing the vcs cache directories, which appear to be unnecessary.
|
||||
|
|
@ -1 +0,0 @@
|
|||
v1.0.0
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"Version":"v1.0.0","Time":"2018-09-28T22:09:08Z"}
|
||||
|
|
@ -1 +0,0 @@
|
|||
module github.com/heschik/tools-testrepo
|
||||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
h1:D2qc+R2eCTCyoT8WAYoExXhPBThJWmlYSfB4coWbfBE=
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
v2.0.1
|
||||
v2.0.2
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"Version":"v2.0.1","Time":"2018-09-28T22:12:08Z"}
|
||||
|
|
@ -1 +0,0 @@
|
|||
module github.com/heschik/tools-testrepo/v2
|
||||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
h1:efPBVdJ45IMcA/KXBOWyOZLo1TETKCXvzrZgfY+gqZk=
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"Version":"v2.0.2","Time":"2018-09-28T22:12:08Z"}
|
||||
|
|
@ -1 +0,0 @@
|
|||
module github.com/heschik/tools-testrepo/v2
|
||||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
h1:vUnR/JOkfEQt/wvMqbT9G2gODHVgVD1saTJ8x2ngAck=
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
module github.com/heschik/tools-testrepo/v2
|
||||
|
||||
go 1.12
|
||||
|
|
@ -1 +0,0 @@
|
|||
package pkg
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
module github.com/heschik/tools-testrepo/v2
|
||||
|
||||
go 1.12
|
||||
|
|
@ -1 +0,0 @@
|
|||
package pkg
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
module github.com/heschik/tools-testrepo
|
||||
|
||||
go 1.12
|
||||
|
|
@ -1 +0,0 @@
|
|||
package pkg
|
||||
Loading…
Reference in New Issue