1
2
3
4
5 package modcmd
6
7 import (
8 "context"
9 "encoding/json"
10 "os"
11 "runtime"
12
13 "cmd/go/internal/base"
14 "cmd/go/internal/cfg"
15 "cmd/go/internal/modfetch"
16 "cmd/go/internal/modload"
17
18 "golang.org/x/mod/module"
19 )
20
21 var cmdDownload = &base.Command{
22 UsageLine: "go mod download [-x] [-json] [modules]",
23 Short: "download modules to local cache",
24 Long: `
25 Download downloads the named modules, which can be module patterns selecting
26 dependencies of the main module or module queries of the form path@version.
27 With no arguments, download applies to all dependencies of the main module
28 (equivalent to 'go mod download all').
29
30 The go command will automatically download modules as needed during ordinary
31 execution. The "go mod download" command is useful mainly for pre-filling
32 the local cache or to compute the answers for a Go module proxy.
33
34 By default, download writes nothing to standard output. It may print progress
35 messages and errors to standard error.
36
37 The -json flag causes download to print a sequence of JSON objects
38 to standard output, describing each downloaded module (or failure),
39 corresponding to this Go struct:
40
41 type Module struct {
42 Path string // module path
43 Version string // module version
44 Error string // error loading module
45 Info string // absolute path to cached .info file
46 GoMod string // absolute path to cached .mod file
47 Zip string // absolute path to cached .zip file
48 Dir string // absolute path to cached source root directory
49 Sum string // checksum for path, version (as in go.sum)
50 GoModSum string // checksum for go.mod (as in go.sum)
51 }
52
53 The -x flag causes download to print the commands download executes.
54
55 See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'.
56
57 See https://golang.org/ref/mod#version-queries for more about version queries.
58 `,
59 }
60
61 var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
62
63 func init() {
64 cmdDownload.Run = runDownload
65
66
67 cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
68 base.AddModCommonFlags(&cmdDownload.Flag)
69 }
70
71 type moduleJSON struct {
72 Path string `json:",omitempty"`
73 Version string `json:",omitempty"`
74 Error string `json:",omitempty"`
75 Info string `json:",omitempty"`
76 GoMod string `json:",omitempty"`
77 Zip string `json:",omitempty"`
78 Dir string `json:",omitempty"`
79 Sum string `json:",omitempty"`
80 GoModSum string `json:",omitempty"`
81 }
82
83 func runDownload(ctx context.Context, cmd *base.Command, args []string) {
84
85 modload.ForceUseModules = true
86 if !modload.HasModRoot() && len(args) == 0 {
87 base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
88 }
89 if len(args) == 0 {
90 args = []string{"all"}
91 } else if modload.HasModRoot() {
92 modload.LoadModFile(ctx)
93 targetAtUpgrade := modload.Target.Path + "@upgrade"
94 targetAtPatch := modload.Target.Path + "@patch"
95 for _, arg := range args {
96 switch arg {
97 case modload.Target.Path, targetAtUpgrade, targetAtPatch:
98 os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n")
99 }
100 }
101 }
102
103 downloadModule := func(m *moduleJSON) {
104 var err error
105 m.Info, err = modfetch.InfoFile(m.Path, m.Version)
106 if err != nil {
107 m.Error = err.Error()
108 return
109 }
110 m.GoMod, err = modfetch.GoModFile(m.Path, m.Version)
111 if err != nil {
112 m.Error = err.Error()
113 return
114 }
115 m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version)
116 if err != nil {
117 m.Error = err.Error()
118 return
119 }
120 mod := module.Version{Path: m.Path, Version: m.Version}
121 m.Zip, err = modfetch.DownloadZip(ctx, mod)
122 if err != nil {
123 m.Error = err.Error()
124 return
125 }
126 m.Sum = modfetch.Sum(mod)
127 m.Dir, err = modfetch.Download(ctx, mod)
128 if err != nil {
129 m.Error = err.Error()
130 return
131 }
132 }
133
134 var mods []*moduleJSON
135 listU := false
136 listVersions := false
137 listRetractions := false
138 type token struct{}
139 sem := make(chan token, runtime.GOMAXPROCS(0))
140 for _, info := range modload.ListModules(ctx, args, listU, listVersions, listRetractions) {
141 if info.Replace != nil {
142 info = info.Replace
143 }
144 if info.Version == "" && info.Error == nil {
145
146
147 continue
148 }
149 m := &moduleJSON{
150 Path: info.Path,
151 Version: info.Version,
152 }
153 mods = append(mods, m)
154 if info.Error != nil {
155 m.Error = info.Error.Err
156 continue
157 }
158 sem <- token{}
159 go func() {
160 downloadModule(m)
161 <-sem
162 }()
163 }
164
165
166 for n := cap(sem); n > 0; n-- {
167 sem <- token{}
168 }
169
170 if *downloadJSON {
171 for _, m := range mods {
172 b, err := json.MarshalIndent(m, "", "\t")
173 if err != nil {
174 base.Fatalf("go mod download: %v", err)
175 }
176 os.Stdout.Write(append(b, '\n'))
177 if m.Error != "" {
178 base.SetExitStatus(1)
179 }
180 }
181 } else {
182 for _, m := range mods {
183 if m.Error != "" {
184 base.Errorf("go mod download: %v", m.Error)
185 }
186 }
187 base.ExitIfErrors()
188 }
189
190
191 modload.WriteGoMod()
192 }
193
View as plain text