| // 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/modload" |
| "cmd/go/internal/work" |
| |
| "golang.org/x/mod/module" |
| ) |
| |
| var cmdDownload = &base.Command{ |
| UsageLine: "go mod download [-x] [-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 all dependencies of the main module |
| (equivalent to 'go mod download all'). |
| |
| 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 |
| 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) |
| } |
| |
| The -x flag causes download to print the commands download executes. |
| |
| See 'go help modules' for more about module queries. |
| `, |
| } |
| |
| var downloadJSON = cmdDownload.Flag.Bool("json", false, "") |
| |
| 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, "") |
| work.AddModCommonFlags(cmdDownload) |
| } |
| |
| type moduleJSON struct { |
| Path string `json:",omitempty"` |
| Version 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"` |
| } |
| |
| func runDownload(ctx context.Context, cmd *base.Command, args []string) { |
| // Check whether modules are enabled and whether we're in a module. |
| if cfg.Getenv("GO111MODULE") == "off" { |
| base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") |
| } |
| if !modload.HasModRoot() && len(args) == 0 { |
| base.Fatalf("go mod download: no modules specified (see 'go help mod download')") |
| } |
| if len(args) == 0 { |
| args = []string{"all"} |
| } else if modload.HasModRoot() { |
| modload.InitMod(ctx) // to fill Target |
| targetAtLatest := modload.Target.Path + "@latest" |
| targetAtUpgrade := modload.Target.Path + "@upgrade" |
| targetAtPatch := modload.Target.Path + "@patch" |
| for _, arg := range args { |
| switch arg { |
| case modload.Target.Path, targetAtLatest, targetAtUpgrade, targetAtPatch: |
| os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n") |
| } |
| } |
| } |
| |
| downloadModule := func(m *moduleJSON) { |
| var err error |
| m.Info, err = modfetch.InfoFile(m.Path, m.Version) |
| if err != nil { |
| m.Error = err.Error() |
| return |
| } |
| 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 |
| listU := false |
| listVersions := false |
| listRetractions := false |
| type token struct{} |
| sem := make(chan token, runtime.GOMAXPROCS(0)) |
| for _, info := range modload.ListModules(ctx, args, listU, listVersions, listRetractions) { |
| 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, |
| } |
| mods = append(mods, m) |
| if info.Error != nil { |
| m.Error = info.Error.Err |
| 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("%v", err) |
| } |
| os.Stdout.Write(append(b, '\n')) |
| if m.Error != "" { |
| base.SetExitStatus(1) |
| } |
| } |
| } else { |
| for _, m := range mods { |
| if m.Error != "" { |
| base.Errorf("%s", m.Error) |
| } |
| } |
| base.ExitIfErrors() |
| } |
| } |