diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/go/internal/modcmd/download.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/internal/modcmd/download.go')
-rw-r--r-- | src/cmd/go/internal/modcmd/download.go | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go new file mode 100644 index 0000000..f0b62e8 --- /dev/null +++ b/src/cmd/go/internal/modcmd/download.go @@ -0,0 +1,311 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modcmd + +import ( + "context" + "encoding/json" + "os" + "runtime" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/modload" + + "golang.org/x/mod/module" + "golang.org/x/mod/semver" +) + +var cmdDownload = &base.Command{ + UsageLine: "go mod download [-x] [-json] [-reuse=old.json] [modules]", + Short: "download modules to local cache", + Long: ` +Download downloads the named modules, which can be module patterns selecting +dependencies of the main module or module queries of the form path@version. + +With no arguments, download applies to the modules needed to build and test +the packages in the main module: the modules explicitly required by the main +module if it is at 'go 1.17' or higher, or all transitively-required modules +if at 'go 1.16' or lower. + +The go command will automatically download modules as needed during ordinary +execution. The "go mod download" command is useful mainly for pre-filling +the local cache or to compute the answers for a Go module proxy. + +By default, download writes nothing to standard output. It may print progress +messages and errors to standard error. + +The -json flag causes download to print a sequence of JSON objects +to standard output, describing each downloaded module (or failure), +corresponding to this Go struct: + + type Module struct { + Path string // module path + Query string // version query corresponding to this version + Version string // module version + Error string // error loading module + Info string // absolute path to cached .info file + GoMod string // absolute path to cached .mod file + Zip string // absolute path to cached .zip file + Dir string // absolute path to cached source root directory + Sum string // checksum for path, version (as in go.sum) + GoModSum string // checksum for go.mod (as in go.sum) + Origin any // provenance of module + Reuse bool // reuse of old module info is safe + } + +The -reuse flag accepts the name of file containing the JSON output of a +previous 'go mod download -json' invocation. The go command may use this +file to determine that a module is unchanged since the previous invocation +and avoid redownloading it. Modules that are not redownloaded will be marked +in the new output by setting the Reuse field to true. Normally the module +cache provides this kind of reuse automatically; the -reuse flag can be +useful on systems that do not preserve the module cache. + +The -x flag causes download to print the commands download executes. + +See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'. + +See https://golang.org/ref/mod#version-queries for more about version queries. + `, +} + +var ( + downloadJSON = cmdDownload.Flag.Bool("json", false, "") + downloadReuse = cmdDownload.Flag.String("reuse", "", "") +) + +func init() { + cmdDownload.Run = runDownload // break init cycle + + // TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands. + cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "") + base.AddChdirFlag(&cmdDownload.Flag) + base.AddModCommonFlags(&cmdDownload.Flag) +} + +type moduleJSON struct { + Path string `json:",omitempty"` + Version string `json:",omitempty"` + Query string `json:",omitempty"` + Error string `json:",omitempty"` + Info string `json:",omitempty"` + GoMod string `json:",omitempty"` + Zip string `json:",omitempty"` + Dir string `json:",omitempty"` + Sum string `json:",omitempty"` + GoModSum string `json:",omitempty"` + + Origin *codehost.Origin `json:",omitempty"` + Reuse bool `json:",omitempty"` +} + +func runDownload(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + + // Check whether modules are enabled and whether we're in a module. + modload.ForceUseModules = true + modload.ExplicitWriteGoMod = true + haveExplicitArgs := len(args) > 0 + + if modload.HasModRoot() || modload.WorkFilePath() != "" { + modload.LoadModFile(ctx) // to fill MainModules + + if haveExplicitArgs { + for _, mainModule := range modload.MainModules.Versions() { + targetAtUpgrade := mainModule.Path + "@upgrade" + targetAtPatch := mainModule.Path + "@patch" + for _, arg := range args { + switch arg { + case mainModule.Path, targetAtUpgrade, targetAtPatch: + os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n") + } + } + } + } else if modload.WorkFilePath() != "" { + // TODO(#44435): Think about what the correct query is to download the + // right set of modules. Also see code review comment at + // https://go-review.googlesource.com/c/go/+/359794/comments/ce946a80_6cf53992. + args = []string{"all"} + } else { + mainModule := modload.MainModules.Versions()[0] + modFile := modload.MainModules.ModFile(mainModule) + if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, modload.ExplicitIndirectVersionV) < 0 { + if len(modFile.Require) > 0 { + args = []string{"all"} + } + } else { + // As of Go 1.17, the go.mod file explicitly requires every module + // that provides any package imported by the main module. + // 'go mod download' is typically run before testing packages in the + // main module, so by default we shouldn't download the others + // (which are presumed irrelevant to the packages in the main module). + // See https://golang.org/issue/44435. + // + // However, we also need to load the full module graph, to ensure that + // we have downloaded enough of the module graph to run 'go list all', + // 'go mod graph', and similar commands. + _ = modload.LoadModGraph(ctx, "") + + for _, m := range modFile.Require { + args = append(args, m.Mod.Path) + } + } + } + } + + if len(args) == 0 { + if modload.HasModRoot() { + os.Stderr.WriteString("go: no module dependencies to download\n") + } else { + base.Errorf("go: no modules specified (see 'go help mod download')") + } + base.Exit() + } + + downloadModule := func(m *moduleJSON) { + _, file, err := modfetch.InfoFile(m.Path, m.Version) + if err != nil { + m.Error = err.Error() + return + } + m.Info = file + m.GoMod, err = modfetch.GoModFile(m.Path, m.Version) + if err != nil { + m.Error = err.Error() + return + } + m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version) + if err != nil { + m.Error = err.Error() + return + } + mod := module.Version{Path: m.Path, Version: m.Version} + m.Zip, err = modfetch.DownloadZip(ctx, mod) + if err != nil { + m.Error = err.Error() + return + } + m.Sum = modfetch.Sum(mod) + m.Dir, err = modfetch.Download(ctx, mod) + if err != nil { + m.Error = err.Error() + return + } + } + + var mods []*moduleJSON + + if *downloadReuse != "" && modload.HasModRoot() { + base.Fatalf("go mod download -reuse cannot be used inside a module") + } + + type token struct{} + sem := make(chan token, runtime.GOMAXPROCS(0)) + infos, infosErr := modload.ListModules(ctx, args, 0, *downloadReuse) + if !haveExplicitArgs && modload.WorkFilePath() == "" { + // 'go mod download' is sometimes run without arguments to pre-populate the + // module cache. In modules that aren't at go 1.17 or higher, it may fetch + // modules that aren't needed to build packages in the main module. This is + // usually not intended, so don't save sums for downloaded modules + // (golang.org/issue/45332). We do still fix inconsistencies in go.mod + // though. + // + // TODO(#45551): In the future, report an error if go.mod or go.sum need to + // be updated after loading the build list. This may require setting + // the mode to "mod" or "readonly" depending on haveExplicitArgs. + if err := modload.WriteGoMod(ctx); err != nil { + base.Fatalf("go: %v", err) + } + } + + for _, info := range infos { + if info.Replace != nil { + info = info.Replace + } + if info.Version == "" && info.Error == nil { + // main module or module replaced with file path. + // Nothing to download. + continue + } + m := &moduleJSON{ + Path: info.Path, + Version: info.Version, + Query: info.Query, + Reuse: info.Reuse, + Origin: info.Origin, + } + mods = append(mods, m) + if info.Error != nil { + m.Error = info.Error.Err + continue + } + if m.Reuse { + continue + } + sem <- token{} + go func() { + downloadModule(m) + <-sem + }() + } + + // Fill semaphore channel to wait for goroutines to finish. + for n := cap(sem); n > 0; n-- { + sem <- token{} + } + + if *downloadJSON { + for _, m := range mods { + b, err := json.MarshalIndent(m, "", "\t") + if err != nil { + base.Fatalf("go: %v", err) + } + os.Stdout.Write(append(b, '\n')) + if m.Error != "" { + base.SetExitStatus(1) + } + } + } else { + for _, m := range mods { + if m.Error != "" { + base.Errorf("go: %v", m.Error) + } + } + base.ExitIfErrors() + } + + // If there were explicit arguments, update go.mod and especially go.sum. + // 'go mod download mod@version' is a useful way to add a sum without using + // 'go get mod@version', which may have other side effects. We print this in + // some error message hints. + // + // If we're in workspace mode, update go.work.sum with checksums for all of + // the modules we downloaded that aren't already recorded. Since a requirement + // in one module may upgrade a dependency of another, we can't be sure that + // the import graph matches the import graph of any given module in isolation, + // so we may end up needing to load packages from modules that wouldn't + // otherwise be relevant. + // + // TODO(#44435): If we adjust the set of modules downloaded in workspace mode, + // we may also need to adjust the logic for saving checksums here. + // + // Don't save sums for 'go mod download' without arguments unless we're in + // workspace mode; see comment above. + if haveExplicitArgs || modload.WorkFilePath() != "" { + if err := modload.WriteGoMod(ctx); err != nil { + base.Errorf("go: %v", err) + } + } + + // If there was an error matching some of the requested packages, emit it now + // (after we've written the checksums for the modules that were downloaded + // successfully). + if infosErr != nil { + base.Errorf("go: %v", infosErr) + } +} |