Goroutine without waitgroup or channel; risks resource leaks or race conditions
UsageLine: "go get [-t] [-u] [-tool] [build flags] [packages]",
1// Copyright 2018 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// Package modget implements the module-aware “go get” command.6package modget78// The arguments to 'go get' are patterns with optional version queries, with9// the version queries defaulting to "upgrade".10//11// The patterns are normally interpreted as package patterns. However, if a12// pattern cannot match a package, it is instead interpreted as a *module*13// pattern. For version queries such as "upgrade" and "patch" that depend on the14// selected version of a module (or of the module containing a package),15// whether a pattern denotes a package or module may change as updates are16// applied (see the example in mod_get_patchmod.txt).17//18// There are a few other ambiguous cases to resolve, too. A package can exist in19// two different modules at the same version: for example, the package20// example.com/foo might be found in module example.com and also in module21// example.com/foo, and those modules may have independent v0.1.0 tags — so the22// input 'example.com/foo@v0.1.0' could syntactically refer to the variant of23// the package loaded from either module! (See mod_get_ambiguous_pkg.txt.)24// If the argument is ambiguous, the user can often disambiguate by specifying25// explicit versions for *all* of the potential module paths involved.2627import (28 "context"29 "errors"30 "fmt"31 "os"32 "path/filepath"33 "runtime"34 "sort"35 "strconv"36 "strings"37 "sync"3839 "cmd/go/internal/base"40 "cmd/go/internal/cfg"41 "cmd/go/internal/gover"42 "cmd/go/internal/imports"43 "cmd/go/internal/modfetch"44 "cmd/go/internal/modload"45 "cmd/go/internal/search"46 "cmd/go/internal/toolchain"47 "cmd/go/internal/work"48 "cmd/internal/par"4950 "golang.org/x/mod/modfile"51 "golang.org/x/mod/module"52)5354var CmdGet = &base.Command{55 // Note: flags below are listed explicitly because they're the most common.56 // Do not send CLs removing them because they're covered by [get flags].57 UsageLine: "go get [-t] [-u] [-tool] [build flags] [packages]",58 Short: "add dependencies to current module and install them",59 Long: `60Get resolves its command-line arguments to packages at specific module versions,61updates go.mod to require those versions, and downloads source code into the62module cache.6364To add a dependency for a package or upgrade it to its latest version:6566 go get example.com/pkg6768To upgrade or downgrade a package to a specific version:6970 go get example.com/pkg@v1.2.37172To remove a dependency on a module and downgrade modules that require it:7374 go get example.com/mod@none7576To upgrade the minimum required Go version to the latest released Go version:7778 go get go@latest7980To upgrade the Go toolchain to the latest patch release of the current Go toolchain:8182 go get toolchain@patch8384See https://go.dev/ref/mod#go-get for details.8586In earlier versions of Go, 'go get' was used to build and install packages.87Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install'88may be used to build and install commands instead. When a version is specified,89'go install' runs in module-aware mode and ignores the go.mod file in the90current directory. For example:9192 go install example.com/pkg@v1.2.393 go install example.com/pkg@latest9495See 'go help install' or https://go.dev/ref/mod#go-install for details.9697'go get' accepts the following flags.9899The -t flag instructs get to consider modules needed to build tests of100packages specified on the command line.101102The -u flag instructs get to update modules providing dependencies103of packages named on the command line to use newer minor or patch104releases when available.105106The -u=patch flag (not -u patch) also instructs get to update dependencies,107but changes the default to select patch releases.108109When the -t and -u flags are used together, get will update110test dependencies as well.111112The -tool flag instructs go to add a matching tool line to go.mod for each113listed package. If -tool is used with @none, the line will be removed.114See 'go help tool' for more information.115116The -x flag prints commands as they are executed. This is useful for117debugging version control commands when a module is downloaded directly118from a repository.119120For more about build flags, see 'go help build'.121122For more about modules, see https://go.dev/ref/mod.123124For more about using 'go get' to update the minimum Go version and125suggested Go toolchain, see https://go.dev/doc/toolchain.126127For more about specifying packages, see 'go help packages'.128129See also: go build, go install, go clean, go mod.130 `,131}132133var HelpVCS = &base.Command{134 UsageLine: "vcs",135 Short: "controlling version control with GOVCS",136 Long: `137The go command can run version control commands like git138to download imported code. This functionality is critical to the decentralized139Go package ecosystem, in which code can be imported from any server,140but it is also a potential security problem, if a malicious server finds a141way to cause the invoked version control command to run unintended code.142143To balance the functionality and security concerns, the go command144by default will only use git and hg to download code from public servers.145But it will use any known version control system (fossil, git, hg, svn)146to download code from private servers, defined as those hosting packages147matching the GOPRIVATE variable (see 'go help private'). The rationale behind148allowing only Git and Mercurial is that these two systems have had the most149attention to issues of being run as clients of untrusted servers. In contrast,150Bazaar, Fossil, and Subversion have primarily been used in trusted,151authenticated environments and are not as well scrutinized as attack surfaces.152153The version control command restrictions only apply when using direct version154control access to download code. When downloading modules from a proxy,155the go command uses the proxy protocol instead, which is always permitted.156By default, the go command uses the Go module mirror (proxy.golang.org)157for public packages and only falls back to version control for private158packages or when the mirror refuses to serve a public package (typically for159legal reasons). Therefore, clients can still access public code served from160Bazaar, Fossil, or Subversion repositories by default, because those downloads161use the Go module mirror, which takes on the security risk of running the162version control commands using a custom sandbox.163164The GOVCS variable can be used to change the allowed version control systems165for specific packages (identified by a module or import path).166The GOVCS variable applies when building package in both module-aware mode167and GOPATH mode. When using modules, the patterns match against the module path.168When using GOPATH, the patterns match against the import path corresponding to169the root of the version control repository.170171The general form of the GOVCS setting is a comma-separated list of172pattern:vcslist rules. The pattern is a glob pattern that must match173one or more leading elements of the module or import path. The vcslist174is a pipe-separated list of allowed version control commands, or "all"175to allow use of any known command, or "off" to disallow all commands.176Note that if a module matches a pattern with vcslist "off", it may still be177downloaded if the origin server uses the "mod" scheme, which instructs the178go command to download the module using the GOPROXY protocol.179The earliest matching pattern in the list applies, even if later patterns180might also match.181182For example, consider:183184 GOVCS=github.com:git,evil.com:off,*:git|hg185186With this setting, code with a module or import path beginning with187github.com/ can only use git; paths on evil.com cannot use any version188control command, and all other paths (* matches everything) can use189only git or hg.190191The special patterns "public" and "private" match public and private192module or import paths. A path is private if it matches the GOPRIVATE193variable; otherwise it is public.194195If no rules in the GOVCS variable match a particular module or import path,196the 'go get' command applies its default rule, which can now be summarized197in GOVCS notation as 'public:git|hg,private:all'.198199To allow unfettered use of any version control system for any package, use:200201 GOVCS=*:all202203To disable all use of version control, use:204205 GOVCS=*:off206207The 'go env -w' command (see 'go help env') can be used to set the GOVCS208variable for future go command invocations.209`,210}211212var (213 getD dFlag214 getF = CmdGet.Flag.Bool("f", false, "")215 getFix = CmdGet.Flag.Bool("fix", false, "")216 getM = CmdGet.Flag.Bool("m", false, "")217 getT = CmdGet.Flag.Bool("t", false, "")218 getU upgradeFlag219 getTool = CmdGet.Flag.Bool("tool", false, "")220 getInsecure = CmdGet.Flag.Bool("insecure", false, "")221)222223// upgradeFlag is a custom flag.Value for -u.224type upgradeFlag struct {225 rawVersion string226 version string227}228229func (*upgradeFlag) IsBoolFlag() bool { return true } // allow -u230231func (v *upgradeFlag) Set(s string) error {232 if s == "false" {233 v.version = ""234 v.rawVersion = ""235 } else if s == "true" {236 v.version = "upgrade"237 v.rawVersion = ""238 } else {239 v.version = s240 v.rawVersion = s241 }242 return nil243}244245func (v *upgradeFlag) String() string { return "" }246247// dFlag is a custom flag.Value for the deprecated -d flag248// which will be used to provide warnings or errors if -d249// is provided.250type dFlag struct {251 value bool252 set bool253}254255func (v *dFlag) IsBoolFlag() bool { return true }256257func (v *dFlag) Set(s string) error {258 v.set = true259 value, err := strconv.ParseBool(s)260 if err != nil {261 err = errors.New("parse error")262 }263 v.value = value264 return err265}266267func (b *dFlag) String() string { return "" }268269func init() {270 work.AddBuildFlags(CmdGet, work.OmitModFlag)271 CmdGet.Run = runGet // break init loop272 CmdGet.Flag.Var(&getD, "d", "")273 CmdGet.Flag.Var(&getU, "u", "")274}275276func runGet(ctx context.Context, cmd *base.Command, args []string) {277 moduleLoader := modload.NewLoader()278 switch getU.version {279 case "", "upgrade", "patch":280 // ok281 default:282 base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion)283 }284 if getD.set {285 if !getD.value {286 base.Fatalf("go: -d flag may not be set to false")287 }288 fmt.Fprintf(os.Stderr, "go: -d flag is deprecated. -d=true is a no-op\n")289 }290 if *getF {291 fmt.Fprintf(os.Stderr, "go: -f flag is a no-op\n")292 }293 if *getFix {294 fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op\n")295 }296 if *getM {297 base.Fatalf("go: -m flag is no longer supported")298 }299 if *getInsecure {300 base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead")301 }302303 moduleLoader.ForceUseModules = true304305 // Do not allow any updating of go.mod until we've applied306 // all the requested changes and checked that the result matches307 // what was requested.308 modload.ExplicitWriteGoMod = true309310 // Allow looking up modules for import paths when outside of a module.311 // 'go get' is expected to do this, unlike other commands.312 moduleLoader.AllowMissingModuleImports()313314 // 'go get' no longer builds or installs packages, so there's nothing to do315 // if there's no go.mod file.316 // TODO(#40775): make modload.Init return ErrNoModRoot instead of exiting.317 // We could handle that here by printing a different message.318 modload.Init(moduleLoader)319 if !moduleLoader.HasModRoot() {320 base.Fatalf("go: go.mod file not found in current directory or any parent directory.\n" +321 "\t'go get' is no longer supported outside a module.\n" +322 "\tTo build and install a command, use 'go install' with a version,\n" +323 "\tlike 'go install example.com/cmd@latest'\n" +324 "\tFor more information, see https://go.dev/doc/go-get-install-deprecation\n" +325 "\tor run 'go help get' or 'go help install'.")326 }327328 dropToolchain, queries := parseArgs(moduleLoader, ctx, args)329 opts := modload.WriteOpts{330 DropToolchain: dropToolchain,331 }332 for _, q := range queries {333 if q.pattern == "toolchain" {334 opts.ExplicitToolchain = true335 }336 }337338 r := newResolver(moduleLoader, ctx, queries)339 r.performLocalQueries(moduleLoader, ctx)340 r.performPathQueries(moduleLoader, ctx)341 r.performToolQueries(moduleLoader, ctx)342 r.performWorkQueries(moduleLoader, ctx)343344 for {345 r.performWildcardQueries(moduleLoader, ctx)346 r.performPatternAllQueries(moduleLoader, ctx)347348 if changed := r.resolveQueries(moduleLoader, ctx, queries); changed {349 // 'go get' arguments can be (and often are) package patterns rather than350 // (just) modules. A package can be provided by any module with a prefix351 // of its import path, and a wildcard can even match packages in modules352 // with totally different paths. Because of these effects, and because any353 // change to the selected version of a module can bring in entirely new354 // module paths as dependencies, we need to reissue queries whenever we355 // change the build list.356 //357 // The result of any version query for a given module — even "upgrade" or358 // "patch" — is always relative to the build list at the start of359 // the 'go get' command, not an intermediate state, and is therefore360 // deterministic and therefore cacheable, and the constraints on the361 // selected version of each module can only narrow as we iterate.362 //363 // "all" is functionally very similar to a wildcard pattern. The set of364 // packages imported by the main module does not change, and the query365 // result for the module containing each such package also does not change366 // (it is always relative to the initial build list, before applying367 // queries). So the only way that the result of an "all" query can change368 // is if some matching package moves from one module in the build list369 // to another, which should not happen very often.370 continue371 }372373 // When we load imports, we detect the following conditions:374 //375 // - missing transitive dependencies that need to be resolved from outside the376 // current build list (note that these may add new matches for existing377 // pattern queries!)378 //379 // - transitive dependencies that didn't match any other query,380 // but need to be upgraded due to the -u flag381 //382 // - ambiguous import errors.383 // TODO(#27899): Try to resolve ambiguous import errors automatically.384 upgrades := r.findAndUpgradeImports(moduleLoader, ctx, queries)385 if changed := r.applyUpgrades(moduleLoader, ctx, upgrades); changed {386 continue387 }388389 r.findMissingWildcards(moduleLoader, ctx)390 if changed := r.resolveQueries(moduleLoader, ctx, r.wildcardQueries); changed {391 continue392 }393394 break395 }396397 r.checkWildcardVersions(moduleLoader, ctx)398399 var pkgPatterns []string400 for _, q := range queries {401 if q.matchesPackages {402 pkgPatterns = append(pkgPatterns, q.pattern)403 }404 }405406 if *getTool {407 updateTools(moduleLoader, ctx, r, queries, &opts)408 }409410 // If a workspace applies, checkPackageProblems will switch to the workspace411 // using modload.EnterWorkspace when doing the final load, and then switch back.412 r.checkPackageProblems(moduleLoader, ctx, pkgPatterns)413414 // Everything succeeded. Update go.mod.415 oldReqs := reqsFromGoMod(modload.ModFile(moduleLoader))416417 if err := modload.WriteGoMod(moduleLoader, ctx, opts); err != nil {418 // A TooNewError can happen for 'go get go@newversion'419 // when all the required modules are old enough420 // but the command line is not.421 // TODO(bcmills): modload.EditBuildList should catch this instead,422 // and then this can be changed to base.Fatal(err).423 toolchain.SwitchOrFatal(moduleLoader, ctx, err)424 }425426 newReqs := reqsFromGoMod(modload.ModFile(moduleLoader))427 r.reportChanges(oldReqs, newReqs)428429 if gowork := moduleLoader.FindGoWork(base.Cwd()); gowork != "" {430 wf, err := modload.ReadWorkFile(gowork)431 if err == nil && modload.UpdateWorkGoVersion(wf, moduleLoader.MainModules.GoVersion(moduleLoader)) {432 modload.WriteWorkFile(gowork, wf)433 }434 }435}436437func updateTools(ld *modload.Loader, ctx context.Context, r *resolver, queries []*query, opts *modload.WriteOpts) {438 pkgOpts := modload.PackageOpts{439 VendorModulesInGOROOTSrc: true,440 LoadTests: *getT,441 ResolveMissingImports: false,442 AllowErrors: true,443 SilenceNoGoErrors: true,444 }445 patterns := []string{}446 for _, q := range queries {447 if search.IsMetaPackage(q.pattern) || q.pattern == "toolchain" {448 base.Fatalf("go: go get -tool does not work with \"%s\".", q.pattern)449 }450 patterns = append(patterns, q.pattern)451 }452453 matches, _ := modload.LoadPackages(ld, ctx, pkgOpts, patterns...)454 for i, m := range matches {455 if queries[i].version == "none" {456 opts.DropTools = append(opts.DropTools, m.Pkgs...)457 } else {458 opts.AddTools = append(opts.AddTools, m.Pkgs...)459 }460 }461462 mg, err := modload.LoadModGraph(ld, ctx, "")463 if err != nil {464 toolchain.SwitchOrFatal(ld, ctx, err)465 }466 r.buildList = mg.BuildList()467 r.buildListVersion = make(map[string]string, len(r.buildList))468 for _, m := range r.buildList {469 r.buildListVersion[m.Path] = m.Version470 }471}472473// parseArgs parses command-line arguments and reports errors.474//475// The command-line arguments are of the form path@version or simply path, with476// implicit @upgrade. path@none is "downgrade away".477func parseArgs(ld *modload.Loader, ctx context.Context, rawArgs []string) (dropToolchain bool, queries []*query) {478 defer base.ExitIfErrors()479480 for _, arg := range search.CleanPatterns(rawArgs) {481 q, err := newQuery(ld, arg)482 if err != nil {483 base.Error(err)484 continue485 }486487 if q.version == "none" {488 switch q.pattern {489 case "go":490 base.Errorf("go: cannot use go@none")491 continue492 case "toolchain":493 dropToolchain = true494 continue495 }496 }497498 // If there were no arguments, CleanPatterns returns ".". Set the raw499 // string back to "" for better errors.500 if len(rawArgs) == 0 {501 q.raw = ""502 }503504 // Guard against 'go get x.go', a common mistake.505 // Note that package and module paths may end with '.go', so only print an error506 // if the argument has no version and either has no slash or refers to an existing file.507 if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" {508 if !strings.Contains(q.raw, "/") {509 base.Errorf("go: %s: arguments must be package or module paths", q.raw)510 continue511 }512 if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() {513 base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw)514 continue515 }516 }517518 queries = append(queries, q)519 }520521 return dropToolchain, queries522}523524type resolver struct {525 localQueries []*query // queries for absolute or relative paths526 pathQueries []*query // package path literal queries in original order527 wildcardQueries []*query // path wildcard queries in original order528 patternAllQueries []*query // queries with the pattern "all"529 workQueries []*query // queries with the pattern "work"530 toolQueries []*query // queries with the pattern "tool"531532 // Indexed "none" queries. These are also included in the slices above;533 // they are indexed here to speed up noneForPath.534 nonesByPath map[string]*query // path-literal "@none" queries indexed by path535 wildcardNones []*query // wildcard "@none" queries536537 // resolvedVersion maps each module path to the version of that module that538 // must be selected in the final build list, along with the first query539 // that resolved the module to that version (the “reason”).540 resolvedVersion map[string]versionReason541542 buildList []module.Version543 buildListVersion map[string]string // index of buildList (module path → version)544545 initialVersion map[string]string // index of the initial build list at the start of 'go get'546547 missing []pathSet // candidates for missing transitive dependencies548549 work *par.Queue550551 matchInModuleCache par.ErrCache[matchInModuleKey, []string]552553 // workspace is used to check whether, in workspace mode, any of the workspace554 // modules would contain a package.555 workspace *workspace556}557558type versionReason struct {559 version string560 reason *query561}562563type matchInModuleKey struct {564 pattern string565 m module.Version566}567568func newResolver(ld *modload.Loader, ctx context.Context, queries []*query) *resolver {569 // LoadModGraph also sets modload.Target, which is needed by various resolver570 // methods.571 mg, err := modload.LoadModGraph(ld, ctx, "")572 if err != nil {573 toolchain.SwitchOrFatal(ld, ctx, err)574 }575576 buildList := mg.BuildList()577 initialVersion := make(map[string]string, len(buildList))578 for _, m := range buildList {579 initialVersion[m.Path] = m.Version580 }581582 r := &resolver{583 work: par.NewQueue(runtime.GOMAXPROCS(0)),584 resolvedVersion: map[string]versionReason{},585 buildList: buildList,586 buildListVersion: initialVersion,587 initialVersion: initialVersion,588 nonesByPath: map[string]*query{},589 workspace: loadWorkspace(ld.FindGoWork(base.Cwd())),590 }591592 for _, q := range queries {593 if q.pattern == "all" {594 r.patternAllQueries = append(r.patternAllQueries, q)595 } else if q.pattern == "work" {596 r.workQueries = append(r.workQueries, q)597 } else if q.pattern == "tool" {598 r.toolQueries = append(r.toolQueries, q)599 } else if q.patternIsLocal {600 r.localQueries = append(r.localQueries, q)601 } else if q.isWildcard() {602 r.wildcardQueries = append(r.wildcardQueries, q)603 } else {604 r.pathQueries = append(r.pathQueries, q)605 }606607 if q.version == "none" {608 // Index "none" queries to make noneForPath more efficient.609 if q.isWildcard() {610 r.wildcardNones = append(r.wildcardNones, q)611 } else {612 // All "<path>@none" queries for the same path are identical; we only613 // need to index one copy.614 r.nonesByPath[q.pattern] = q615 }616 }617 }618619 return r620}621622// initialSelected returns the version of the module with the given path that623// was selected at the start of this 'go get' invocation.624func (r *resolver) initialSelected(mPath string) (version string) {625 v, ok := r.initialVersion[mPath]626 if !ok {627 return "none"628 }629 return v630}631632// selected returns the version of the module with the given path that is633// selected in the resolver's current build list.634func (r *resolver) selected(mPath string) (version string) {635 v, ok := r.buildListVersion[mPath]636 if !ok {637 return "none"638 }639 return v640}641642// noneForPath returns a "none" query matching the given module path,643// or found == false if no such query exists.644func (r *resolver) noneForPath(mPath string) (nq *query, found bool) {645 if nq = r.nonesByPath[mPath]; nq != nil {646 return nq, true647 }648 for _, nq := range r.wildcardNones {649 if nq.matchesPath(mPath) {650 return nq, true651 }652 }653 return nil, false654}655656// queryModule wraps modload.Query, substituting r.checkAllowedOr to decide657// allowed versions.658func (r *resolver) queryModule(ld *modload.Loader, ctx context.Context, mPath, query string, selected func(string) string) (module.Version, error) {659 current := r.initialSelected(mPath)660 rev, err := modload.Query(ld, ctx, mPath, query, current, r.checkAllowedOr(ld, query, selected))661 if err != nil {662 return module.Version{}, err663 }664 return module.Version{Path: mPath, Version: rev.Version}, nil665}666667// queryPackages wraps modload.QueryPackage, substituting r.checkAllowedOr to668// decide allowed versions.669func (r *resolver) queryPackages(ld *modload.Loader, ctx context.Context, pattern, query string, selected func(string) string) (pkgMods []module.Version, err error) {670 results, err := modload.QueryPackages(ld, ctx, pattern, query, selected, r.checkAllowedOr(ld, query, selected))671 if len(results) > 0 {672 pkgMods = make([]module.Version, 0, len(results))673 for _, qr := range results {674 pkgMods = append(pkgMods, qr.Mod)675 }676 }677 return pkgMods, err678}679680// queryPattern wraps modload.QueryPattern, substituting r.checkAllowedOr to681// decide allowed versions.682func (r *resolver) queryPattern(ld *modload.Loader, ctx context.Context, pattern, query string, selected func(string) string) (pkgMods []module.Version, mod module.Version, err error) {683 results, modOnly, err := modload.QueryPattern(ld, ctx, pattern, query, selected, r.checkAllowedOr(ld, query, selected))684 if len(results) > 0 {685 pkgMods = make([]module.Version, 0, len(results))686 for _, qr := range results {687 pkgMods = append(pkgMods, qr.Mod)688 }689 }690 if modOnly != nil {691 mod = modOnly.Mod692 }693 return pkgMods, mod, err694}695696// checkAllowedOr is like modload.CheckAllowed, but it always allows the requested697// and current versions (even if they are retracted or otherwise excluded).698func (r *resolver) checkAllowedOr(s *modload.Loader, requested string, selected func(string) string) modload.AllowedFunc {699 return func(ctx context.Context, m module.Version) error {700 if m.Version == requested {701 return s.CheckExclusions(ctx, m)702 }703 if (requested == "upgrade" || requested == "patch") && m.Version == selected(m.Path) {704 return nil705 }706 return s.CheckAllowed(ctx, m)707 }708}709710// matchInModule is a caching wrapper around modload.MatchInModule.711func (r *resolver) matchInModule(ld *modload.Loader, ctx context.Context, pattern string, m module.Version) (packages []string, err error) {712 return r.matchInModuleCache.Do(matchInModuleKey{pattern, m}, func() ([]string, error) {713 match := modload.MatchInModule(ld, ctx, pattern, m, imports.AnyTags())714 if len(match.Errs) > 0 {715 return match.Pkgs, match.Errs[0]716 }717 return match.Pkgs, nil718 })719}720721// queryNone adds a candidate set to q for each module matching q.pattern.722// Each candidate set has only one possible module version: the matched723// module at version "none".724//725// We interpret arguments to 'go get' as packages first, and fall back to726// modules second. However, no module exists at version "none", and therefore no727// package exists at that version either: we know that the argument cannot match728// any packages, and thus it must match modules instead.729func (r *resolver) queryNone(ld *modload.Loader, ctx context.Context, q *query) {730 if search.IsMetaPackage(q.pattern) {731 panic(fmt.Sprintf("internal error: queryNone called with pattern %q", q.pattern))732 }733734 if !q.isWildcard() {735 q.pathOnce(q.pattern, func() pathSet {736 hasModRoot := ld.HasModRoot()737 if hasModRoot && ld.MainModules.Contains(q.pattern) {738 v := module.Version{Path: q.pattern}739 // The user has explicitly requested to downgrade their own module to740 // version "none". This is not an entirely unreasonable request: it741 // could plausibly mean “downgrade away everything that depends on any742 // explicit version of the main module”, or “downgrade away the743 // package with the same path as the main module, found in a module744 // with a prefix of the main module's path”.745 //746 // However, neither of those behaviors would be consistent with the747 // plain meaning of the query. To try to reduce confusion, reject the748 // query explicitly.749 return errSet(&modload.QueryMatchesMainModulesError{750 MainModules: []module.Version{v},751 Pattern: q.pattern,752 Query: q.version,753 PatternIsModule: ld.MainModules.Contains(q.pattern),754 })755 }756757 return pathSet{mod: module.Version{Path: q.pattern, Version: "none"}}758 })759 }760761 for _, curM := range r.buildList {762 if !q.matchesPath(curM.Path) {763 continue764 }765 q.pathOnce(curM.Path, func() pathSet {766 if ld.HasModRoot() && curM.Version == "" && ld.MainModules.Contains(curM.Path) {767 return errSet(&modload.QueryMatchesMainModulesError{768 MainModules: []module.Version{curM},769 Pattern: q.pattern,770 Query: q.version,771 PatternIsModule: ld.MainModules.Contains(q.pattern),772 })773 }774 return pathSet{mod: module.Version{Path: curM.Path, Version: "none"}}775 })776 }777}778779func (r *resolver) performLocalQueries(ld *modload.Loader, ctx context.Context) {780 for _, q := range r.localQueries {781 q.pathOnce(q.pattern, func() pathSet {782 absDetail := ""783 if !filepath.IsAbs(q.pattern) {784 if absPath, err := filepath.Abs(q.pattern); err == nil {785 absDetail = fmt.Sprintf(" (%s)", absPath)786 }787 }788789 // Absolute paths like C:\foo and relative paths like ../foo... are790 // restricted to matching packages in the main module.791 pkgPattern, mainModule := ld.MainModules.DirImportPath(ld, ctx, q.pattern)792 if pkgPattern == "." {793 ld.MustHaveModRoot()794 versions := ld.MainModules.Versions()795 modRoots := make([]string, 0, len(versions))796 for _, m := range versions {797 modRoots = append(modRoots, ld.MainModules.ModRoot(m))798 }799 var plural string800 if len(modRoots) != 1 {801 plural = "s"802 }803 return errSet(fmt.Errorf("%s%s is not within module%s rooted at %s", q.pattern, absDetail, plural, strings.Join(modRoots, ", ")))804 }805806 match := modload.MatchInModule(ld, ctx, pkgPattern, mainModule, imports.AnyTags())807 if len(match.Errs) > 0 {808 return pathSet{err: match.Errs[0]}809 }810811 if len(match.Pkgs) == 0 {812 if q.raw == "" || q.raw == "." {813 return errSet(fmt.Errorf("no package to get in current directory"))814 }815 if !q.isWildcard() {816 ld.MustHaveModRoot()817 return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, ld.MainModules.ModRoot(mainModule)))818 }819 search.WarnUnmatched([]*search.Match{match})820 return pathSet{}821 }822823 return pathSet{pkgMods: []module.Version{mainModule}}824 })825 }826}827828// performWildcardQueries populates the candidates for each query whose pattern829// is a wildcard.830//831// The candidates for a given module path matching (or containing a package832// matching) a wildcard query depend only on the initial build list, but the set833// of modules may be expanded by other queries, so wildcard queries need to be834// re-evaluated whenever a potentially-matching module path is added to the835// build list.836func (r *resolver) performWildcardQueries(ld *modload.Loader, ctx context.Context) {837 for _, q := range r.wildcardQueries {838 q := q839 r.work.Add(func() {840 if q.version == "none" {841 r.queryNone(ld, ctx, q)842 } else {843 r.queryWildcard(ld, ctx, q)844 }845 })846 }847 <-r.work.Idle()848}849850// queryWildcard adds a candidate set to q for each module for which:851// - some version of the module is already in the build list, and852// - that module exists at some version matching q.version, and853// - either the module path itself matches q.pattern, or some package within854// the module at q.version matches q.pattern.855func (r *resolver) queryWildcard(ld *modload.Loader, ctx context.Context, q *query) {856 // For wildcard patterns, modload.QueryPattern only identifies modules857 // matching the prefix of the path before the wildcard. However, the build858 // list may already contain other modules with matching packages, and we859 // should consider those modules to satisfy the query too.860 // We want to match any packages in existing dependencies, but we only want to861 // resolve new dependencies if nothing else turns up.862 for _, curM := range r.buildList {863 if !q.canMatchInModule(curM.Path) {864 continue865 }866 q.pathOnce(curM.Path, func() pathSet {867 if _, hit := r.noneForPath(curM.Path); hit {868 // This module is being removed, so it will no longer be in the build list869 // (and thus will no longer match the pattern).870 return pathSet{}871 }872873 if ld.MainModules.Contains(curM.Path) && !versionOkForMainModule(q.version) {874 if q.matchesPath(curM.Path) {875 return errSet(&modload.QueryMatchesMainModulesError{876 MainModules: []module.Version{curM},877 Pattern: q.pattern,878 Query: q.version,879 PatternIsModule: ld.MainModules.Contains(q.pattern),880 })881 }882883 packages, err := r.matchInModule(ld, ctx, q.pattern, curM)884 if err != nil {885 return errSet(err)886 }887 if len(packages) > 0 {888 return errSet(&modload.QueryMatchesPackagesInMainModuleError{889 Pattern: q.pattern,890 Query: q.version,891 Packages: packages,892 })893 }894895 return r.tryWildcard(ld, ctx, q, curM)896 }897898 m, err := r.queryModule(ld, ctx, curM.Path, q.version, r.initialSelected)899 if err != nil {900 if !isNoSuchModuleVersion(err) {901 // We can't tell whether a matching version exists.902 return errSet(err)903 }904 // There is no version of curM.Path matching the query.905906 // We haven't checked whether curM contains any matching packages at its907 // currently-selected version, or whether curM.Path itself matches q. If908 // either of those conditions holds, *and* no other query changes the909 // selected version of curM, then we will fail in checkWildcardVersions.910 // (This could be an error, but it's too soon to tell.)911 //912 // However, even then the transitive requirements of some other query913 // may downgrade this module out of the build list entirely, in which914 // case the pattern will no longer include it and it won't be an error.915 //916 // Either way, punt on the query rather than erroring out just yet.917 return pathSet{}918 }919920 return r.tryWildcard(ld, ctx, q, m)921 })922 }923924 // Even if no modules matched, we shouldn't query for a new module to provide925 // the pattern yet: some other query may yet induce a new requirement that926 // will match the wildcard. Instead, we'll check in findMissingWildcards.927}928929// tryWildcard returns a pathSet for module m matching query q.930// If m does not actually match q, tryWildcard returns an empty pathSet.931func (r *resolver) tryWildcard(ld *modload.Loader, ctx context.Context, q *query, m module.Version) pathSet {932 mMatches := q.matchesPath(m.Path)933 packages, err := r.matchInModule(ld, ctx, q.pattern, m)934 if err != nil {935 return errSet(err)936 }937 if len(packages) > 0 {938 return pathSet{pkgMods: []module.Version{m}}939 }940 if mMatches {941 return pathSet{mod: m}942 }943 return pathSet{}944}945946// findMissingWildcards adds a candidate set for each query in r.wildcardQueries947// that has not yet resolved to any version containing packages.948func (r *resolver) findMissingWildcards(ld *modload.Loader, ctx context.Context) {949 for _, q := range r.wildcardQueries {950 if q.version == "none" || q.matchesPackages {951 continue // q is not “missing”952 }953 r.work.Add(func() {954 q.pathOnce(q.pattern, func() pathSet {955 pkgMods, mod, err := r.queryPattern(ld, ctx, q.pattern, q.version, r.initialSelected)956 if err != nil {957 if isNoSuchPackageVersion(err) && len(q.resolved) > 0 {958 // q already resolved one or more modules but matches no packages.959 // That's ok: this pattern is just a module pattern, and we don't960 // need to add any more modules to satisfy it.961 return pathSet{}962 }963 return errSet(err)964 }965966 return pathSet{pkgMods: pkgMods, mod: mod}967 })968 })969 }970 <-r.work.Idle()971}972973// checkWildcardVersions reports an error if any module in the build list has a974// path (or contains a package) matching a query with a wildcard pattern, but975// has a selected version that does *not* match the query.976func (r *resolver) checkWildcardVersions(ld *modload.Loader, ctx context.Context) {977 defer base.ExitIfErrors()978979 for _, q := range r.wildcardQueries {980 for _, curM := range r.buildList {981 if !q.canMatchInModule(curM.Path) {982 continue983 }984 if !q.matchesPath(curM.Path) {985 packages, err := r.matchInModule(ld, ctx, q.pattern, curM)986 if len(packages) == 0 {987 if err != nil {988 reportError(q, err)989 }990 continue // curM is not relevant to q.991 }992 }993994 rev, err := r.queryModule(ld, ctx, curM.Path, q.version, r.initialSelected)995 if err != nil {996 reportError(q, err)997 continue998 }999 if rev.Version == curM.Version {1000 continue // curM already matches q.1001 }10021003 if !q.matchesPath(curM.Path) {1004 m := module.Version{Path: curM.Path, Version: rev.Version}1005 packages, err := r.matchInModule(ld, ctx, q.pattern, m)1006 if err != nil {1007 reportError(q, err)1008 continue1009 }1010 if len(packages) == 0 {1011 // curM at its original version contains a path matching q.pattern,1012 // but at rev.Version it does not, so (somewhat paradoxically) if1013 // we changed the version of curM it would no longer match the query.1014 var version any = m1015 if rev.Version != q.version {1016 version = fmt.Sprintf("%s@%s (%s)", m.Path, q.version, m.Version)1017 }1018 reportError(q, fmt.Errorf("%v matches packages in %v but not %v: specify a different version for module %s", q, curM, version, m.Path))1019 continue1020 }1021 }10221023 // Since queryModule succeeded and either curM or one of the packages it1024 // contains matches q.pattern, we should have either selected the version1025 // of curM matching q, or reported a conflict error (and exited).1026 // If we're still here and the version doesn't match,1027 // something has gone very wrong.1028 reportError(q, fmt.Errorf("internal error: selected %v instead of %v", curM, rev.Version))1029 }1030 }1031}10321033// performPathQueries populates the candidates for each query whose pattern is1034// a path literal.1035//1036// The candidate packages and modules for path literals depend only on the1037// initial build list, not the current build list, so we only need to query path1038// literals once.1039func (r *resolver) performPathQueries(ld *modload.Loader, ctx context.Context) {1040 for _, q := range r.pathQueries {1041 q := q1042 r.work.Add(func() {1043 if q.version == "none" {1044 r.queryNone(ld, ctx, q)1045 } else {1046 r.queryPath(ld, ctx, q)1047 }1048 })1049 }1050 <-r.work.Idle()1051}10521053// queryPath adds a candidate set to q for the package with path q.pattern.1054// The candidate set consists of all modules that could provide q.pattern1055// and have a version matching q, plus (if it exists) the module whose path1056// is itself q.pattern (at a matching version).1057func (r *resolver) queryPath(ld *modload.Loader, ctx context.Context, q *query) {1058 q.pathOnce(q.pattern, func() pathSet {1059 if search.IsMetaPackage(q.pattern) || q.isWildcard() {1060 panic(fmt.Sprintf("internal error: queryPath called with pattern %q", q.pattern))1061 }1062 if q.version == "none" {1063 panic(`internal error: queryPath called with version "none"`)1064 }10651066 if search.IsStandardImportPath(q.pattern) {1067 stdOnly := module.Version{}1068 packages, _ := r.matchInModule(ld, ctx, q.pattern, stdOnly)1069 if len(packages) > 0 {1070 if q.rawVersion != "" {1071 return errSet(fmt.Errorf("can't request explicit version %q of standard library package %s", q.version, q.pattern))1072 }10731074 q.matchesPackages = true1075 return pathSet{} // No module needed for standard library.1076 }1077 }10781079 pkgMods, mod, err := r.queryPattern(ld, ctx, q.pattern, q.version, r.initialSelected)1080 if err != nil {1081 return errSet(err)1082 }1083 return pathSet{pkgMods: pkgMods, mod: mod}1084 })1085}10861087// performToolQueries populates the candidates for each query whose1088// pattern is "tool".1089func (r *resolver) performToolQueries(ld *modload.Loader, ctx context.Context) {1090 for _, q := range r.toolQueries {1091 for tool := range ld.MainModules.Tools() {1092 q.pathOnce(tool, func() pathSet {1093 pkgMods, err := r.queryPackages(ld, ctx, tool, q.version, r.initialSelected)1094 return pathSet{pkgMods: pkgMods, err: err}1095 })1096 }1097 }1098}10991100// performWorkQueries populates the candidates for each query whose pattern is "work".1101// The candidate module to resolve the work pattern is exactly the single main module.1102func (r *resolver) performWorkQueries(ld *modload.Loader, ctx context.Context) {1103 for _, q := range r.workQueries {1104 q.pathOnce(q.pattern, func() pathSet {1105 // TODO(matloob): Maybe export MainModules.mustGetSingleMainModule and call that.1106 // There are a few other places outside the modload package where we expect1107 // a single main module.1108 if len(ld.MainModules.Versions()) != 1 {1109 panic("internal error: number of main modules is not exactly one in resolution phase of go get")1110 }1111 mainModule := ld.MainModules.Versions()[0]11121113 // We know what the result is going to be, assuming the main module is not1114 // empty, (it's the main module itself) but first check to see that there1115 // are packages in the main module, so that if there aren't any, we can1116 // return the expected warning that the pattern matched no packages.1117 match := modload.MatchInModule(ld, ctx, q.pattern, mainModule, imports.AnyTags())1118 if len(match.Errs) > 0 {1119 return pathSet{err: match.Errs[0]}1120 }1121 if len(match.Pkgs) == 0 {1122 search.WarnUnmatched([]*search.Match{match})1123 return pathSet{} // There are no packages in the main module, so the main module isn't needed to resolve them.1124 }11251126 return pathSet{pkgMods: []module.Version{mainModule}}1127 })1128 }1129}11301131// performPatternAllQueries populates the candidates for each query whose1132// pattern is "all".1133//1134// The candidate modules for a given package in "all" depend only on the initial1135// build list, but we cannot follow the dependencies of a given package until we1136// know which candidate is selected — and that selection may depend on the1137// results of other queries. We need to re-evaluate the "all" queries whenever1138// the module for one or more packages in "all" are resolved.1139func (r *resolver) performPatternAllQueries(ld *modload.Loader, ctx context.Context) {1140 if len(r.patternAllQueries) == 0 {1141 return1142 }11431144 findPackage := func(ctx context.Context, path string, m module.Version) (versionOk bool) {1145 versionOk = true1146 for _, q := range r.patternAllQueries {1147 q.pathOnce(path, func() pathSet {1148 pkgMods, err := r.queryPackages(ld, ctx, path, q.version, r.initialSelected)1149 if len(pkgMods) != 1 || pkgMods[0] != m {1150 // There are candidates other than m for the given path, so we can't1151 // be certain that m will actually be the module selected to provide1152 // the package. Don't load its dependencies just yet, because they1153 // might no longer be dependencies after we resolve the correct1154 // version.1155 versionOk = false1156 }1157 return pathSet{pkgMods: pkgMods, err: err}1158 })1159 }1160 return versionOk1161 }11621163 r.loadPackages(ld, ctx, []string{"all"}, findPackage)11641165 // Since we built up the candidate lists concurrently, they may be in a1166 // nondeterministic order. We want 'go get' to be fully deterministic,1167 // including in which errors it chooses to report, so sort the candidates1168 // into a deterministic-but-arbitrary order.1169 for _, q := range r.patternAllQueries {1170 sort.Slice(q.candidates, func(i, j int) bool {1171 return q.candidates[i].path < q.candidates[j].path1172 })1173 }1174}11751176// findAndUpgradeImports returns a pathSet for each package that is not yet1177// in the build list but is transitively imported by the packages matching the1178// given queries (which must already have been resolved).1179//1180// If the getU flag ("-u") is set, findAndUpgradeImports also returns a1181// pathSet for each module that is not constrained by any other1182// command-line argument and has an available matching upgrade.1183func (r *resolver) findAndUpgradeImports(ld *modload.Loader, ctx context.Context, queries []*query) (upgrades []pathSet) {1184 patterns := make([]string, 0, len(queries))1185 for _, q := range queries {1186 if q.matchesPackages {1187 patterns = append(patterns, q.pattern)1188 }1189 }1190 if len(patterns) == 0 {1191 return nil1192 }11931194 // mu guards concurrent writes to upgrades, which will be sorted1195 // (to restore determinism) after loading.1196 var mu sync.Mutex11971198 findPackage := func(ctx context.Context, path string, m module.Version) (versionOk bool) {1199 version := "latest"1200 if m.Path != "" {1201 if getU.version == "" {1202 // The user did not request that we upgrade transitive dependencies.1203 return true1204 }1205 if _, ok := r.resolvedVersion[m.Path]; ok {1206 // We cannot upgrade m implicitly because its version is determined by1207 // an explicit pattern argument.1208 return true1209 }1210 version = getU.version1211 }12121213 // Unlike other queries, the "-u" flag upgrades relative to the build list1214 // after applying changes so far, not the initial build list.1215 // This is for two reasons:1216 //1217 // - The "-u" flag intentionally applies to transitive dependencies,1218 // which may not be known or even resolved in advance of applying1219 // other version changes.1220 //1221 // - The "-u" flag, unlike other arguments, does not cause version1222 // conflicts with other queries. (The other query always wins.)12231224 pkgMods, err := r.queryPackages(ld, ctx, path, version, r.selected)1225 for _, u := range pkgMods {1226 if u == m {1227 // The selected package version is already upgraded appropriately; there1228 // is no need to change it.1229 return true1230 }1231 }12321233 if err != nil {1234 if isNoSuchPackageVersion(err) || (m.Path == "" && module.CheckPath(path) != nil) {1235 // We can't find the package because it doesn't — or can't — even exist1236 // in any module at the latest version. (Note that invalid module paths1237 // could in general exist due to replacements, so we at least need to1238 // run the query to check those.)1239 //1240 // There is no version change we can make to fix the package, so leave1241 // it unresolved. Either some other query (perhaps a wildcard matching a1242 // newly-added dependency for some other missing package) will fill in1243 // the gaps, or we will report an error (with a better import stack) in1244 // the final LoadPackages call.1245 return true1246 }1247 }12481249 mu.Lock()1250 upgrades = append(upgrades, pathSet{path: path, pkgMods: pkgMods, err: err})1251 mu.Unlock()1252 return false1253 }12541255 r.loadPackages(ld, ctx, patterns, findPackage)12561257 // Since we built up the candidate lists concurrently, they may be in a1258 // nondeterministic order. We want 'go get' to be fully deterministic,1259 // including in which errors it chooses to report, so sort the candidates1260 // into a deterministic-but-arbitrary order.1261 sort.Slice(upgrades, func(i, j int) bool {1262 return upgrades[i].path < upgrades[j].path1263 })1264 return upgrades1265}12661267// loadPackages loads the packages matching the given patterns, invoking the1268// findPackage function for each package that may require a change to the1269// build list.1270//1271// loadPackages invokes the findPackage function for each package loaded from a1272// module outside the main module. If the module or version that supplies that1273// package needs to be changed due to a query, findPackage may return false1274// and the imports of that package will not be loaded.1275//1276// loadPackages also invokes the findPackage function for each imported package1277// that is neither present in the standard library nor in any module in the1278// build list.1279func (r *resolver) loadPackages(ld *modload.Loader, ctx context.Context, patterns []string, findPackage func(ctx context.Context, path string, m module.Version) (versionOk bool)) {1280 opts := modload.PackageOpts{1281 Tags: imports.AnyTags(),1282 VendorModulesInGOROOTSrc: true,1283 LoadTests: *getT,1284 AssumeRootsImported: true, // After 'go get foo', imports of foo should build.1285 SilencePackageErrors: true, // May be fixed by subsequent upgrades or downgrades.1286 Switcher: toolchain.NewSwitcher(ld),1287 }12881289 opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error {1290 if m.Path == "" || m.Version == "" {1291 // Packages in the standard library and main modules are already at their1292 // latest (and only) available versions.1293 return nil1294 }1295 if ok := findPackage(ctx, path, m); !ok {1296 return errVersionChange1297 }1298 return nil1299 }13001301 _, pkgs := modload.LoadPackages(ld, ctx, opts, patterns...)1302 for _, pkgPath := range pkgs {1303 const (1304 parentPath = ""1305 parentIsStd = false1306 )1307 _, _, err := modload.Lookup(ld, parentPath, parentIsStd, pkgPath)1308 if err == nil {1309 continue1310 }1311 if errors.Is(err, errVersionChange) {1312 // We already added candidates during loading.1313 continue1314 }1315 if r.workspace != nil && r.workspace.hasPackage(pkgPath) {1316 // Don't try to resolve imports that are in the resolver's associated workspace. (#73654)1317 continue1318 }13191320 if _, ok := errors.AsType[*modload.ImportMissingError](err); !ok {1321 if _, ok := errors.AsType[*modload.AmbiguousImportError](err); !ok {1322 // The package, which is a dependency of something we care about, has some1323 // problem that we can't resolve with a version change.1324 // Leave the error for the final LoadPackages call.1325 continue1326 }1327 }13281329 path := pkgPath1330 r.work.Add(func() {1331 findPackage(ctx, path, module.Version{})1332 })1333 }1334 <-r.work.Idle()1335}13361337// errVersionChange is a sentinel error indicating that a module's version needs1338// to be updated before its dependencies can be loaded.1339var errVersionChange = errors.New("version change needed")13401341// resolveQueries resolves candidate sets that are attached to the given1342// queries and/or needed to provide the given missing-package dependencies.1343//1344// resolveQueries starts by resolving one module version from each1345// unambiguous pathSet attached to the given queries.1346//1347// If no unambiguous query results in a change to the build list,1348// resolveQueries revisits the ambiguous query candidates and resolves them1349// arbitrarily in order to guarantee forward progress.1350//1351// If all pathSets are resolved without any changes to the build list,1352// resolveQueries returns with changed=false.1353func (r *resolver) resolveQueries(ld *modload.Loader, ctx context.Context, queries []*query) (changed bool) {1354 defer base.ExitIfErrors()13551356 // Note: this is O(N²) with the number of pathSets in the worst case.1357 //1358 // We could perhaps get it down to O(N) if we were to index the pathSets1359 // by module path, so that we only revisit a given pathSet when the1360 // version of some module in its containingPackage list has been determined.1361 //1362 // However, N tends to be small, and most candidate sets will include only one1363 // candidate module (so they will be resolved in the first iteration), so for1364 // now we'll stick to the simple O(N²) approach.13651366 resolved := 01367 for {1368 prevResolved := resolved13691370 // If we found modules that were too new, find the max of the required versions1371 // and then try to switch to a newer toolchain.1372 sw := toolchain.NewSwitcher(ld)1373 for _, q := range queries {1374 for _, cs := range q.candidates {1375 sw.Error(cs.err)1376 }1377 }1378 // Only switch if we need a newer toolchain.1379 // Otherwise leave the cs.err for reporting later.1380 if sw.NeedSwitch() {1381 sw.Switch(ctx)1382 // If NeedSwitch is true and Switch returns, Switch has failed to locate a newer toolchain.1383 // It printed the errors along with one more about not finding a good toolchain.1384 base.Exit()1385 }13861387 for _, q := range queries {1388 unresolved := q.candidates[:0]13891390 for _, cs := range q.candidates {1391 if cs.err != nil {1392 reportError(q, cs.err)1393 resolved++1394 continue1395 }13961397 filtered, isPackage, m, unique := r.disambiguate(ld, cs)1398 if !unique {1399 unresolved = append(unresolved, filtered)1400 continue1401 }14021403 if m.Path == "" {1404 // The query is not viable. Choose an arbitrary candidate from1405 // before filtering and “resolve” it to report a conflict.1406 isPackage, m = r.chooseArbitrarily(cs)1407 }1408 if isPackage {1409 q.matchesPackages = true1410 }1411 r.resolve(ld, q, m)1412 resolved++1413 }14141415 q.candidates = unresolved1416 }14171418 base.ExitIfErrors()1419 if resolved == prevResolved {1420 break // No unambiguous candidate remains.1421 }1422 }14231424 if resolved > 0 {1425 if changed = r.updateBuildList(ld, ctx, nil); changed {1426 // The build list has changed, so disregard any remaining ambiguous queries:1427 // they might now be determined by requirements in the build list, which we1428 // would prefer to use instead of arbitrary versions.1429 return true1430 }1431 }14321433 // The build list will be the same on the next iteration as it was on this1434 // iteration, so any ambiguous queries will remain so. In order to make1435 // progress, resolve them arbitrarily but deterministically.1436 //1437 // If that results in conflicting versions, the user can re-run 'go get'1438 // with additional explicit versions for the conflicting packages or1439 // modules.1440 resolvedArbitrarily := 01441 for _, q := range queries {1442 for _, cs := range q.candidates {1443 isPackage, m := r.chooseArbitrarily(cs)1444 if isPackage {1445 q.matchesPackages = true1446 }1447 r.resolve(ld, q, m)1448 resolvedArbitrarily++1449 }1450 }1451 if resolvedArbitrarily > 0 {1452 changed = r.updateBuildList(ld, ctx, nil)1453 }1454 return changed1455}14561457// applyUpgrades disambiguates candidate sets that are needed to upgrade (or1458// provide) transitive dependencies imported by previously-resolved packages.1459//1460// applyUpgrades modifies the build list by adding one module version from each1461// pathSet in upgrades, then downgrading (or further upgrading) those modules as1462// needed to maintain any already-resolved versions of other modules.1463// applyUpgrades does not mark the new versions as resolved, so they can still1464// be further modified by other queries (such as wildcards).1465//1466// If all pathSets are resolved without any changes to the build list,1467// applyUpgrades returns with changed=false.1468func (r *resolver) applyUpgrades(ld *modload.Loader, ctx context.Context, upgrades []pathSet) (changed bool) {1469 defer base.ExitIfErrors()14701471 // Arbitrarily add a "latest" version that provides each missing package, but1472 // do not mark the version as resolved: we still want to allow the explicit1473 // queries to modify the resulting versions.1474 var tentative []module.Version1475 for _, cs := range upgrades {1476 if cs.err != nil {1477 base.Error(cs.err)1478 continue1479 }14801481 filtered, _, m, unique := r.disambiguate(ld, cs)1482 if !unique {1483 _, m = r.chooseArbitrarily(filtered)1484 }1485 if m.Path == "" {1486 // There is no viable candidate for the missing package.1487 // Leave it unresolved.1488 continue1489 }1490 tentative = append(tentative, m)1491 }1492 base.ExitIfErrors()14931494 changed = r.updateBuildList(ld, ctx, tentative)1495 return changed1496}14971498// disambiguate eliminates candidates from cs that conflict with other module1499// versions that have already been resolved. If there is only one (unique)1500// remaining candidate, disambiguate returns that candidate, along with1501// an indication of whether that result interprets cs.path as a package1502//1503// Note: we're only doing very simple disambiguation here. The goal is to1504// reproduce the user's intent, not to find a solution that a human couldn't.1505// In the vast majority of cases, we expect only one module per pathSet,1506// but we want to give some minimal additional tools so that users can add an1507// extra argument or two on the command line to resolve simple ambiguities.1508func (r *resolver) disambiguate(s *modload.Loader, cs pathSet) (filtered pathSet, isPackage bool, m module.Version, unique bool) {1509 if len(cs.pkgMods) == 0 && cs.mod.Path == "" {1510 panic("internal error: resolveIfUnambiguous called with empty pathSet")1511 }15121513 for _, m := range cs.pkgMods {1514 if _, ok := r.noneForPath(m.Path); ok {1515 // A query with version "none" forces the candidate module to version1516 // "none", so we cannot use any other version for that module.1517 continue1518 }15191520 if s.MainModules.Contains(m.Path) {1521 if m.Version == "" {1522 return pathSet{}, true, m, true1523 }1524 // A main module can only be set to its own version.1525 continue1526 }15271528 vr, ok := r.resolvedVersion[m.Path]1529 if !ok {1530 // m is a viable answer to the query, but other answers may also1531 // still be viable.1532 filtered.pkgMods = append(filtered.pkgMods, m)1533 continue1534 }15351536 if vr.version != m.Version {1537 // Some query forces the candidate module to a version other than this1538 // one.1539 //1540 // The command could be something like1541 //1542 // go get example.com/foo/bar@none example.com/foo/bar/baz@latest1543 //1544 // in which case we *cannot* resolve the package from1545 // example.com/foo/bar (because it is constrained to version1546 // "none") and must fall through to module example.com/foo@latest.1547 continue1548 }15491550 // Some query forces the candidate module *to* the candidate version.1551 // As a result, this candidate is the only viable choice to provide1552 // its package(s): any other choice would result in an ambiguous import1553 // for this path.1554 //1555 // For example, consider the command1556 //1557 // go get example.com/foo@latest example.com/foo/bar/baz@latest1558 //1559 // If modules example.com/foo and example.com/foo/bar both provide1560 // package example.com/foo/bar/baz, then we *must* resolve the package1561 // from example.com/foo: if we instead resolved it from1562 // example.com/foo/bar, we would have two copies of the package.1563 return pathSet{}, true, m, true1564 }15651566 if cs.mod.Path != "" {1567 vr, ok := r.resolvedVersion[cs.mod.Path]1568 if !ok || vr.version == cs.mod.Version {1569 filtered.mod = cs.mod1570 }1571 }15721573 if len(filtered.pkgMods) == 1 &&1574 (filtered.mod.Path == "" || filtered.mod == filtered.pkgMods[0]) {1575 // Exactly one viable module contains the package with the given path1576 // (by far the common case), so we can resolve it unambiguously.1577 return pathSet{}, true, filtered.pkgMods[0], true1578 }15791580 if len(filtered.pkgMods) == 0 {1581 // All modules that could provide the path as a package conflict with other1582 // resolved arguments. If it can refer to a module instead, return that;1583 // otherwise, this pathSet cannot be resolved (and we will return the1584 // zero module.Version).1585 return pathSet{}, false, filtered.mod, true1586 }15871588 // The query remains ambiguous: there are at least two different modules1589 // to which cs.path could refer.1590 return filtered, false, module.Version{}, false1591}15921593// chooseArbitrarily returns an arbitrary (but deterministic) module version1594// from among those in the given set.1595//1596// chooseArbitrarily prefers module paths that were already in the build list at1597// the start of 'go get', prefers modules that provide packages over those that1598// do not, and chooses the first module meeting those criteria (so biases toward1599// longer paths).1600func (r *resolver) chooseArbitrarily(cs pathSet) (isPackage bool, m module.Version) {1601 // Prefer to upgrade some module that was already in the build list.1602 for _, m := range cs.pkgMods {1603 if r.initialSelected(m.Path) != "none" {1604 return true, m1605 }1606 }16071608 // Otherwise, arbitrarily choose the first module that provides the package.1609 if len(cs.pkgMods) > 0 {1610 return true, cs.pkgMods[0]1611 }16121613 return false, cs.mod1614}16151616// checkPackageProblems reloads packages for the given patterns and reports1617// missing and ambiguous package errors. It also reports retractions and1618// deprecations for resolved modules and modules needed to build named packages.1619// It also adds a sum for each updated module in the build list if we had one1620// before and didn't get one while loading packages.1621//1622// We skip missing-package errors earlier in the process, since we want to1623// resolve pathSets ourselves, but at that point, we don't have enough context1624// to log the package-import chains leading to each error.1625func (r *resolver) checkPackageProblems(ld *modload.Loader, ctx context.Context, pkgPatterns []string) {1626 defer base.ExitIfErrors()16271628 // Enter workspace mode, if the current main module would belong to it, when1629 // doing the workspace load. We want to check that the workspace loads properly1630 // and doesn't have missing or ambiguous imports (rather than checking the module1631 // by itself) because the module may have unreleased dependencies in the workspace.1632 // We'll also report issues for retracted and deprecated modules using the workspace1633 // info, but switch back to single module mode when fetching sums so that we update1634 // the single module's go.sum file.1635 var exitWorkspace func()1636 if r.workspace != nil && r.workspace.hasModule(ld.MainModules.Versions()[0].Path) {1637 var err error1638 exitWorkspace, err = modload.EnterWorkspace(ld, ctx)1639 if err != nil {1640 // A TooNewError can happen for1641 // go get go@newversion when all the required modules1642 // are old enough but the go command itself is not new1643 // enough. See the related comment on the SwitchOrFatal1644 // in runGet when WriteGoMod returns an error.1645 toolchain.SwitchOrFatal(ld, ctx, err)1646 }1647 }16481649 // Gather information about modules we might want to load retractions and1650 // deprecations for. Loading this metadata requires at least one version1651 // lookup per module, and we don't want to load information that's neither1652 // relevant nor actionable.1653 type modFlags int1654 const (1655 resolved modFlags = 1 << iota // version resolved by 'go get'1656 named // explicitly named on command line or provides a named package1657 hasPkg // needed to build named packages1658 direct // provides a direct dependency of the main module or workspace modules1659 )1660 relevantMods := make(map[module.Version]modFlags)1661 for path, reason := range r.resolvedVersion {1662 m := module.Version{Path: path, Version: reason.version}1663 relevantMods[m] |= resolved1664 }16651666 // Reload packages, reporting errors for missing and ambiguous imports.1667 if len(pkgPatterns) > 0 {1668 // LoadPackages will print errors (since it has more context) but will not1669 // exit, since we need to load retractions later.1670 pkgOpts := modload.PackageOpts{1671 VendorModulesInGOROOTSrc: true,1672 LoadTests: *getT,1673 ResolveMissingImports: false,1674 AllowErrors: true,1675 SilenceNoGoErrors: true,1676 }1677 matches, pkgs := modload.LoadPackages(ld, ctx, pkgOpts, pkgPatterns...)1678 for _, m := range matches {1679 if len(m.Errs) > 0 {1680 base.SetExitStatus(1)1681 break1682 }1683 }1684 for _, pkg := range pkgs {1685 if dir, _, err := modload.Lookup(ld, "", false, pkg); err != nil {1686 if dir != "" && errors.Is(err, imports.ErrNoGo) {1687 // Since dir is non-empty, we must have located source files1688 // associated with either the package or its test — ErrNoGo must1689 // indicate that none of those source files happen to apply in this1690 // configuration. If we are actually building the package (no -d1691 // flag), we will report the problem then; otherwise, assume that the1692 // user is going to build or test this package in some other1693 // configuration and suppress the error.1694 continue1695 }16961697 base.SetExitStatus(1)1698 if ambiguousErr, ok := errors.AsType[*modload.AmbiguousImportError](err); ok {1699 for _, m := range ambiguousErr.Modules {1700 relevantMods[m] |= hasPkg1701 }1702 }1703 }1704 if m := ld.PackageModule(pkg); m.Path != "" {1705 relevantMods[m] |= hasPkg1706 }1707 }1708 for _, match := range matches {1709 for _, pkg := range match.Pkgs {1710 m := ld.PackageModule(pkg)1711 relevantMods[m] |= named1712 }1713 }1714 }17151716 reqs := modload.LoadModFile(ld, ctx)1717 for m := range relevantMods {1718 if reqs.IsDirect(m.Path) {1719 relevantMods[m] |= direct1720 }1721 }17221723 // Load retractions for modules mentioned on the command line and modules1724 // needed to build named packages. We care about retractions of indirect1725 // dependencies, since we might be able to upgrade away from them.1726 type modMessage struct {1727 m module.Version1728 message string1729 }1730 retractions := make([]modMessage, 0, len(relevantMods))1731 for m, flags := range relevantMods {1732 if flags&(resolved|named|hasPkg) != 0 {1733 retractions = append(retractions, modMessage{m: m})1734 }1735 }1736 sort.Slice(retractions, func(i, j int) bool { return retractions[i].m.Path < retractions[j].m.Path })1737 for i := range retractions {1738 i := i1739 r.work.Add(func() {1740 err := ld.CheckRetractions(ctx, retractions[i].m)1741 if _, ok := errors.AsType[*modload.ModuleRetractedError](err); ok {1742 retractions[i].message = err.Error()1743 }1744 })1745 }17461747 // Load deprecations for modules mentioned on the command line. Only load1748 // deprecations for indirect dependencies if they're also direct dependencies1749 // of the main module or workspace modules. Deprecations of purely indirect1750 // dependencies are not actionable.1751 deprecations := make([]modMessage, 0, len(relevantMods))1752 for m, flags := range relevantMods {1753 if flags&(resolved|named) != 0 || flags&(hasPkg|direct) == hasPkg|direct {1754 deprecations = append(deprecations, modMessage{m: m})1755 }1756 }1757 sort.Slice(deprecations, func(i, j int) bool { return deprecations[i].m.Path < deprecations[j].m.Path })1758 for i := range deprecations {1759 i := i1760 r.work.Add(func() {1761 deprecation, err := modload.CheckDeprecation(ld, ctx, deprecations[i].m)1762 if err != nil || deprecation == "" {1763 return1764 }1765 deprecations[i].message = modload.ShortMessage(deprecation, "")1766 })1767 }17681769 // exit the workspace if we had entered it earlier. We want to add the sums1770 // to the go.sum file for the module we're running go get from.1771 if exitWorkspace != nil {1772 // Wait for retraction and deprecation checks (that depend on the global1773 // modload state containing the workspace) to finish before we reset the1774 // state back to single module mode.1775 <-r.work.Idle()1776 exitWorkspace()1777 }17781779 // Load sums for updated modules that had sums before. When we update a1780 // module, we may update another module in the build list that provides a1781 // package in 'all' that wasn't loaded as part of this 'go get' command.1782 // If we don't add a sum for that module, builds may fail later.1783 // Note that an incidentally updated package could still import packages1784 // from unknown modules or from modules in the build list that we didn't1785 // need previously. We can't handle that case without loading 'all'.1786 sumErrs := make([]error, len(r.buildList))1787 for i := range r.buildList {1788 i := i1789 m := r.buildList[i]1790 mActual := m1791 if mRepl := modload.Replacement(ld, m); mRepl.Path != "" {1792 mActual = mRepl1793 }1794 old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}1795 if old.Version == "" {1796 continue1797 }1798 oldActual := old1799 if oldRepl := modload.Replacement(ld, old); oldRepl.Path != "" {1800 oldActual = oldRepl1801 }1802 if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(ld.Fetcher(), oldActual) {1803 continue1804 }1805 r.work.Add(func() {1806 if _, err := ld.Fetcher().DownloadZip(ctx, mActual); err != nil {1807 verb := "upgraded"1808 if gover.ModCompare(m.Path, m.Version, old.Version) < 0 {1809 verb = "downgraded"1810 }1811 replaced := ""1812 if mActual != m {1813 replaced = fmt.Sprintf(" (replaced by %s)", mActual)1814 }1815 err = fmt.Errorf("%s %s %s => %s%s: error finding sum for %s: %v", verb, m.Path, old.Version, m.Version, replaced, mActual, err)1816 sumErrs[i] = err1817 }1818 })1819 }18201821 <-r.work.Idle()18221823 // Report deprecations, then retractions, then errors fetching sums.1824 // Only errors fetching sums are hard errors.1825 for _, mm := range deprecations {1826 if mm.message != "" {1827 fmt.Fprintf(os.Stderr, "go: module %s is deprecated: %s\n", mm.m.Path, mm.message)1828 }1829 }1830 var retractPath string1831 for _, mm := range retractions {1832 if mm.message != "" {1833 fmt.Fprintf(os.Stderr, "go: warning: %v\n", mm.message)1834 if retractPath == "" {1835 retractPath = mm.m.Path1836 } else {1837 retractPath = "<module>"1838 }1839 }1840 }1841 if retractPath != "" {1842 fmt.Fprintf(os.Stderr, "go: to switch to the latest unretracted version, run:\n\tgo get %s@latest\n", retractPath)1843 }1844 for _, err := range sumErrs {1845 if err != nil {1846 base.Error(err)1847 }1848 }1849}18501851// reportChanges logs version changes to os.Stderr.1852//1853// reportChanges only logs changes to modules named on the command line and to1854// explicitly required modules in go.mod. Most changes to indirect requirements1855// are not relevant to the user and are not logged.1856//1857// reportChanges should be called after WriteGoMod.1858func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) {1859 type change struct {1860 path, old, new string1861 }1862 changes := make(map[string]change)18631864 // Collect changes in modules matched by command line arguments.1865 for path, reason := range r.resolvedVersion {1866 if gover.IsToolchain(path) {1867 continue1868 }1869 old := r.initialVersion[path]1870 new := reason.version1871 if old != new && (old != "" || new != "none") {1872 changes[path] = change{path, old, new}1873 }1874 }18751876 // Collect changes to explicit requirements in go.mod.1877 for _, req := range oldReqs {1878 if gover.IsToolchain(req.Path) {1879 continue1880 }1881 path := req.Path1882 old := req.Version1883 new := r.buildListVersion[path]1884 if old != new {1885 changes[path] = change{path, old, new}1886 }1887 }1888 for _, req := range newReqs {1889 if gover.IsToolchain(req.Path) {1890 continue1891 }1892 path := req.Path1893 old := r.initialVersion[path]1894 new := req.Version1895 if old != new {1896 changes[path] = change{path, old, new}1897 }1898 }18991900 // Toolchain diffs are easier than requirements: diff old and new directly.1901 toolchainVersions := func(reqs []module.Version) (goV, toolchain string) {1902 for _, req := range reqs {1903 if req.Path == "go" {1904 goV = req.Version1905 }1906 if req.Path == "toolchain" {1907 toolchain = req.Version1908 }1909 }1910 return1911 }1912 oldGo, oldToolchain := toolchainVersions(oldReqs)1913 newGo, newToolchain := toolchainVersions(newReqs)1914 if oldGo != newGo {1915 changes["go"] = change{"go", oldGo, newGo}1916 }1917 if oldToolchain != newToolchain {1918 changes["toolchain"] = change{"toolchain", oldToolchain, newToolchain}1919 }19201921 sortedChanges := make([]change, 0, len(changes))1922 for _, c := range changes {1923 sortedChanges = append(sortedChanges, c)1924 }1925 sort.Slice(sortedChanges, func(i, j int) bool {1926 pi := sortedChanges[i].path1927 pj := sortedChanges[j].path1928 if pi == pj {1929 return false1930 }1931 // go first; toolchain second1932 switch {1933 case pi == "go":1934 return true1935 case pj == "go":1936 return false1937 case pi == "toolchain":1938 return true1939 case pj == "toolchain":1940 return false1941 }1942 return pi < pj1943 })19441945 for _, c := range sortedChanges {1946 if c.old == "" {1947 fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new)1948 } else if c.new == "none" || c.new == "" {1949 fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old)1950 } else if gover.ModCompare(c.path, c.new, c.old) > 0 {1951 fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new)1952 if c.path == "go" && gover.Compare(c.old, gover.ExplicitIndirectVersion) < 0 && gover.Compare(c.new, gover.ExplicitIndirectVersion) >= 0 {1953 fmt.Fprintf(os.Stderr, "\tnote: expanded dependencies to upgrade to go %s or higher; run 'go mod tidy' to clean up\n", gover.ExplicitIndirectVersion)1954 }19551956 } else {1957 fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new)1958 }1959 }19601961 // TODO(golang.org/issue/33284): attribute changes to command line arguments.1962 // For modules matched by command line arguments, this probably isn't1963 // necessary, but it would be useful for unmatched direct dependencies of1964 // the main module.1965}19661967// resolve records that module m must be at its indicated version (which may be1968// "none") due to query q. If some other query forces module m to be at a1969// different version, resolve reports a conflict error.1970func (r *resolver) resolve(s *modload.Loader, q *query, m module.Version) {1971 if m.Path == "" {1972 panic("internal error: resolving a module.Version with an empty path")1973 }19741975 if s.MainModules.Contains(m.Path) && m.Version != "" {1976 reportError(q, &modload.QueryMatchesMainModulesError{1977 MainModules: []module.Version{{Path: m.Path}},1978 Pattern: q.pattern,1979 Query: q.version,1980 PatternIsModule: s.MainModules.Contains(q.pattern),1981 })1982 return1983 }19841985 vr, ok := r.resolvedVersion[m.Path]1986 if ok && vr.version != m.Version {1987 reportConflict(q, m, vr)1988 return1989 }1990 r.resolvedVersion[m.Path] = versionReason{m.Version, q}1991 q.resolved = append(q.resolved, m)1992}19931994// updateBuildList updates the module loader's global build list to be1995// consistent with r.resolvedVersion, and to include additional modules1996// provided that they do not conflict with the resolved versions.1997//1998// If the additional modules conflict with the resolved versions, they will be1999// downgraded to a non-conflicting version (possibly "none").2000//
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.