diff options
author | Jay Conrod <jayconrod@google.com> | 2019-04-21 15:21:58 -0400 |
---|---|---|
committer | Jay Conrod <jayconrod@google.com> | 2019-04-30 22:20:57 +0000 |
commit | 65b89c3542fb3d36632e404f672b41f111b8b60a (patch) | |
tree | 803a9d73b058db0bc166c8b2d9bf1084ca9ed8ab /src/cmd/go/internal/modload/load.go | |
parent | 4d9dd3580624df413d65d83e467fcd6ad4a0168b (diff) | |
download | go-65b89c3542fb3d36632e404f672b41f111b8b60a.tar.gz go-65b89c3542fb3d36632e404f672b41f111b8b60a.zip |
cmd/go: make get -u upgrade only modules providing packages
Currently, 'go get -u' upgrades modules matching command line
arguments and any modules they transitively require. 'go get -u' with
no positional arguments upgrades all modules transitively required by
the main module. This usually adds a large number of indirect
requirements, which is surprising to users.
With this change, 'go get' will load packages specified by
its arguments using a similar process to other commands
('go build', etc). Only modules providing packages will be upgraded.
'go get -u' now upgrades modules providing packages transitively
imported by the command-line arguments. 'go get -u' without arguments
will only upgrade modules needed by the package in the current
directory.
'go get -m' will load all packages within a module. 'go get -m -u'
without arguments will upgrade modules needed by the main module. It
is equivalent to 'go get -u all'. Neither command will upgrade modules
that are required but not used.
Note that 'go get -m' and 'go get -d' both download modules in order
to load packages.
Fixes #26902
Change-Id: I2bad686b3ca8c9de985a81fb42b16a36bb4cc3ea
Reviewed-on: https://go-review.googlesource.com/c/go/+/174099
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/cmd/go/internal/modload/load.go')
-rw-r--r-- | src/cmd/go/internal/modload/load.go | 145 |
1 files changed, 122 insertions, 23 deletions
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 388837e205..579ef50382 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -33,7 +33,8 @@ import ( // buildList is the list of modules to use for building packages. // It is initialized by calling ImportPaths, ImportFromFiles, -// LoadALL, or LoadBuildList, each of which uses loaded.load. +// ModulePackages, LoadALL, or LoadBuildList, each of which uses +// loaded.load. // // Ideally, exactly ONE of those functions would be called, // and exactly once. Most of the time, that's true. @@ -53,27 +54,22 @@ var loaded *loader // ImportPaths returns the set of packages matching the args (patterns), // adding modules to the build list as needed to satisfy new imports. func ImportPaths(patterns []string) []*search.Match { - InitMod() - - var matches []*search.Match - for _, pattern := range search.CleanPatterns(patterns) { - m := &search.Match{ - Pattern: pattern, - Literal: !strings.Contains(pattern, "...") && !search.IsMetaPackage(pattern), - } - if m.Literal { - m.Pkgs = []string{pattern} - } - matches = append(matches, m) - } + matches := ImportPathsQuiet(patterns) + search.WarnUnmatched(matches) + return matches +} - fsDirs := make([][]string, len(matches)) - loaded = newLoader() - updateMatches := func(iterating bool) { +// ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches. +func ImportPathsQuiet(patterns []string) []*search.Match { + var fsDirs [][]string + updateMatches := func(matches []*search.Match, iterating bool) { for i, m := range matches { switch { case build.IsLocalImport(m.Pattern) || filepath.IsAbs(m.Pattern): // Evaluate list of file system directories on first iteration. + if fsDirs == nil { + fsDirs = make([][]string, len(matches)) + } if fsDirs[i] == nil { var dirs []string if m.Literal { @@ -167,23 +163,113 @@ func ImportPaths(patterns []string) []*search.Match { if len(m.Pkgs) == 0 { m.Pkgs = search.MatchPackages(m.Pattern).Pkgs } + + default: + m.Pkgs = []string{m.Pattern} } } } + return loadPatterns(patterns, true, updateMatches) +} + +// ModulePackages returns packages provided by each module in patterns. +// patterns may contain module paths, patterns matching module paths, +// "all" (interpreted as package pattern "all"), and "." (interpreted +// as the main module). Additional modules (including modules providing +// dependencies) may be added to the build list or upgraded. +func ModulePackages(patterns []string) []*search.Match { + updateMatches := func(matches []*search.Match, iterating bool) { + for _, m := range matches { + switch { + case search.IsRelativePath(m.Pattern) || filepath.IsAbs(m.Pattern): + if m.Pattern != "." { + base.Errorf("go: path %s is not a module", m.Pattern) + continue + } + m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target}) + + case strings.Contains(m.Pattern, "..."): + match := search.MatchPattern(m.Pattern) + var matched []module.Version + for _, mod := range buildList { + if match(mod.Path) || str.HasPathPrefix(m.Pattern, mod.Path) { + matched = append(matched, mod) + } + } + m.Pkgs = matchPackages(m.Pattern, loaded.tags, false, matched) + + case m.Pattern == "all": + loaded.testAll = true + if iterating { + // Enumerate the packages in the main module. + // We'll load the dependencies as we find them. + m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target}) + } else { + // Starting with the packages in the main module, + // enumerate the full list of "all". + m.Pkgs = loaded.computePatternAll(m.Pkgs) + } + + default: + found := false + for _, mod := range buildList { + if mod.Path == m.Pattern { + found = true + m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{mod}) + break + } + } + if !found { + base.Errorf("go %s: module not in build list", m.Pattern) + } + } + } + } + return loadPatterns(patterns, false, updateMatches) +} + +// loadPatterns returns a set of packages matching the args (patterns), +// adding modules to the build list as needed to satisfy new imports. +// +// useTags indicates whether to use the default build constraints to +// filter source files. If useTags is false, only "ignore" and malformed +// build tag requirements are considered false. +// +// The interpretation of patterns is determined by updateMatches, which will be +// called repeatedly until the build list is finalized. updateMatches should +// should store a list of matching packages in each search.Match when it is +// called. The iterating parameter is true if the build list has not been +// finalized yet. +// +// If errors are encountered, loadPatterns will print them and exit. +// On success, loadPatterns will update the build list and write go.mod. +func loadPatterns(patterns []string, useTags bool, updateMatches func(matches []*search.Match, iterating bool)) []*search.Match { + InitMod() + + var matches []*search.Match + for _, pattern := range search.CleanPatterns(patterns) { + matches = append(matches, &search.Match{ + Pattern: pattern, + Literal: !strings.Contains(pattern, "...") && !search.IsMetaPackage(pattern), + }) + } + + loaded = newLoader() + if !useTags { + loaded.tags = anyTags + } loaded.load(func() []string { var roots []string - updateMatches(true) + updateMatches(matches, true) for _, m := range matches { - for _, pkg := range m.Pkgs { - roots = append(roots, pkg) - } + roots = append(roots, m.Pkgs...) } return roots }) // One last pass to finalize wildcards. - updateMatches(false) + updateMatches(matches, false) // A given module path may be used as itself or as a replacement for another // module, but not both at the same time. Otherwise, the aliasing behavior is @@ -204,7 +290,6 @@ func ImportPaths(patterns []string) []*search.Match { base.ExitIfErrors() WriteGoMod() - search.WarnUnmatched(matches) return matches } @@ -410,6 +495,20 @@ func PackageModule(path string) module.Version { return pkg.mod } +// PackageImports returns the imports for the package named by the import path. +// It does not include test imports. It returns nil for unknown packages. +func PackageImports(path string) []string { + pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) + if !ok { + return nil + } + imports := make([]string, len(pkg.imports)) + for i, p := range pkg.imports { + imports[i] = p.path + } + return imports +} + // ModuleUsedDirectly reports whether the main module directly imports // some package in the module with the given path. func ModuleUsedDirectly(path string) bool { |