aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/internal/modload/query.go
diff options
context:
space:
mode:
authorJay Conrod <jayconrod@google.com>2019-04-08 14:31:21 -0400
committerJay Conrod <jayconrod@google.com>2019-04-16 18:49:50 +0000
commit6997671d2e69cb3d5ac26b34564d117eed472260 (patch)
tree9f55f81927a88e4175549d08163cc87a263e778b /src/cmd/go/internal/modload/query.go
parent75308c98e3068691cdf21914bb4eca2c81ce1b15 (diff)
downloadgo-6997671d2e69cb3d5ac26b34564d117eed472260.tar.gz
go-6997671d2e69cb3d5ac26b34564d117eed472260.zip
cmd/go: handle wildcards for unknown modules in "go get"
For example, "go get golang.org/x/tools/cmd/..." will add a requirement for "golang.org/x/tools" to go.mod and will install executables from the "cmd" subdirectory. Fixes #29363 Change-Id: Id53f051710708d7760ffe831d4274fd54533d2b7 Reviewed-on: https://go-review.googlesource.com/c/go/+/171138 Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
Diffstat (limited to 'src/cmd/go/internal/modload/query.go')
-rw-r--r--src/cmd/go/internal/modload/query.go65
1 files changed, 65 insertions, 0 deletions
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
index 30bdc4dc7d..84950732be 100644
--- a/src/cmd/go/internal/modload/query.go
+++ b/src/cmd/go/internal/modload/query.go
@@ -10,6 +10,7 @@ import (
"cmd/go/internal/module"
"cmd/go/internal/semver"
"cmd/go/internal/str"
+ "errors"
"fmt"
pathpkg "path"
"strings"
@@ -254,3 +255,67 @@ func QueryPackage(path, query string, allowed func(module.Version) bool) (module
return module.Version{}, nil, finalErr
}
+
+// QueryPattern looks up a module with at least one package matching the
+// given pattern at the given version. It returns a list of matched packages
+// and information about the module.
+//
+// QueryPattern queries modules with package paths up to the first "..."
+// in the pattern. For the pattern "example.com/a/b.../c", QueryPattern would
+// consider prefixes of "example.com/a". If multiple modules have versions
+// that match the query and packages that match the pattern, QueryPattern
+// picks the one with the longest module path.
+func QueryPattern(pattern string, query string, allowed func(module.Version) bool) ([]string, module.Version, *modfetch.RevInfo, error) {
+ i := strings.Index(pattern, "...")
+ if i < 0 {
+ m, info, err := QueryPackage(pattern, query, allowed)
+ if err != nil {
+ return nil, module.Version{}, nil, err
+ } else {
+ return []string{pattern}, m, info, nil
+ }
+ }
+ base := pathpkg.Dir(pattern[:i+3])
+
+ // Return the most specific error for the longest module path.
+ const (
+ errNoModule = 0
+ errNoVersion = 1
+ errNoMatch = 2
+ )
+ errLevel := errNoModule
+ finalErr := errors.New("cannot find module matching pattern")
+
+ for p := base; p != "." && p != "/"; p = pathpkg.Dir(p) {
+ info, err := Query(p, query, allowed)
+ if err != nil {
+ if _, ok := err.(*codehost.VCSError); ok {
+ // A VCSError means we know where to find the code,
+ // we just can't. Abort search.
+ return nil, module.Version{}, nil, err
+ }
+ if errLevel < errNoVersion {
+ errLevel = errNoVersion
+ finalErr = err
+ }
+ continue
+ }
+ m := module.Version{Path: p, Version: info.Version}
+ // matchPackages also calls fetch but treats errors as fatal, so we
+ // fetch here first.
+ _, _, err = fetch(m)
+ if err != nil {
+ return nil, module.Version{}, nil, err
+ }
+ pkgs := matchPackages(pattern, anyTags, false, []module.Version{m})
+ if len(pkgs) > 0 {
+ return pkgs, m, info, nil
+ }
+ if errLevel < errNoMatch {
+ errLevel = errNoMatch
+ finalErr = fmt.Errorf("no matching packages in module %s@%s", m.Path, m.Version)
+ }
+ }
+
+ return nil, module.Version{}, nil, finalErr
+}