diff options
author | Jay Conrod <jayconrod@google.com> | 2019-04-08 14:31:21 -0400 |
---|---|---|
committer | Jay Conrod <jayconrod@google.com> | 2019-04-16 18:49:50 +0000 |
commit | 6997671d2e69cb3d5ac26b34564d117eed472260 (patch) | |
tree | 9f55f81927a88e4175549d08163cc87a263e778b /src/cmd/go/internal/modload/query.go | |
parent | 75308c98e3068691cdf21914bb4eca2c81ce1b15 (diff) | |
download | go-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.go | 65 |
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 +} |