1
2
3
4
5 package modcmd
6
7 import (
8 "context"
9 "encoding/json"
10 "errors"
11 "os"
12 "runtime"
13 "sync"
14
15 "cmd/go/internal/base"
16 "cmd/go/internal/cfg"
17 "cmd/go/internal/gover"
18 "cmd/go/internal/modfetch"
19 "cmd/go/internal/modfetch/codehost"
20 "cmd/go/internal/modload"
21 "cmd/go/internal/toolchain"
22
23 "golang.org/x/mod/module"
24 )
25
26 var cmdDownload = &base.Command{
27 UsageLine: "go mod download [-x] [-json] [-reuse=old.json] [modules]",
28 Short: "download modules to local cache",
29 Long: `
30 Download downloads the named modules, which can be module patterns selecting
31 dependencies of the main module or module queries of the form path@version.
32
33 With no arguments, download applies to the modules needed to build and test
34 the packages in the main module: the modules explicitly required by the main
35 module if it is at 'go 1.17' or higher, or all transitively-required modules
36 if at 'go 1.16' or lower.
37
38 The go command will automatically download modules as needed during ordinary
39 execution. The "go mod download" command is useful mainly for pre-filling
40 the local cache or to compute the answers for a Go module proxy.
41
42 By default, download writes nothing to standard output. It may print progress
43 messages and errors to standard error.
44
45 The -json flag causes download to print a sequence of JSON objects
46 to standard output, describing each downloaded module (or failure),
47 corresponding to this Go struct:
48
49 type Module struct {
50 Path string // module path
51 Query string // version query corresponding to this version
52 Version string // module version
53 Error string // error loading module
54 Info string // absolute path to cached .info file
55 GoMod string // absolute path to cached .mod file
56 Zip string // absolute path to cached .zip file
57 Dir string // absolute path to cached source root directory
58 Sum string // checksum for path, version (as in go.sum)
59 GoModSum string // checksum for go.mod (as in go.sum)
60 Origin any // provenance of module
61 Reuse bool // reuse of old module info is safe
62 }
63
64 The -reuse flag accepts the name of file containing the JSON output of a
65 previous 'go mod download -json' invocation. The go command may use this
66 file to determine that a module is unchanged since the previous invocation
67 and avoid redownloading it. Modules that are not redownloaded will be marked
68 in the new output by setting the Reuse field to true. Normally the module
69 cache provides this kind of reuse automatically; the -reuse flag can be
70 useful on systems that do not preserve the module cache.
71
72 The -x flag causes download to print the commands download executes.
73
74 See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'.
75
76 See https://golang.org/ref/mod#version-queries for more about version queries.
77 `,
78 }
79
80 var (
81 downloadJSON = cmdDownload.Flag.Bool("json", false, "")
82 downloadReuse = cmdDownload.Flag.String("reuse", "", "")
83 )
84
85 func init() {
86 cmdDownload.Run = runDownload
87
88
89 cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
90 base.AddChdirFlag(&cmdDownload.Flag)
91 base.AddModCommonFlags(&cmdDownload.Flag)
92 }
93
94
95 type ModuleJSON struct {
96 Path string `json:",omitempty"`
97 Version string `json:",omitempty"`
98 Query string `json:",omitempty"`
99 Error string `json:",omitempty"`
100 Info string `json:",omitempty"`
101 GoMod string `json:",omitempty"`
102 Zip string `json:",omitempty"`
103 Dir string `json:",omitempty"`
104 Sum string `json:",omitempty"`
105 GoModSum string `json:",omitempty"`
106
107 Origin *codehost.Origin `json:",omitempty"`
108 Reuse bool `json:",omitempty"`
109 }
110
111 func runDownload(ctx context.Context, cmd *base.Command, args []string) {
112 modload.InitWorkfile()
113
114
115 modload.ForceUseModules = true
116 modload.ExplicitWriteGoMod = true
117 haveExplicitArgs := len(args) > 0
118
119 if modload.HasModRoot() || modload.WorkFilePath() != "" {
120 modload.LoadModFile(ctx)
121
122 if haveExplicitArgs {
123 for _, mainModule := range modload.MainModules.Versions() {
124 targetAtUpgrade := mainModule.Path + "@upgrade"
125 targetAtPatch := mainModule.Path + "@patch"
126 for _, arg := range args {
127 switch arg {
128 case mainModule.Path, targetAtUpgrade, targetAtPatch:
129 os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n")
130 }
131 }
132 }
133 } else if modload.WorkFilePath() != "" {
134
135
136
137 args = []string{"all"}
138 } else {
139 mainModule := modload.MainModules.Versions()[0]
140 modFile := modload.MainModules.ModFile(mainModule)
141 if modFile.Go == nil || gover.Compare(modFile.Go.Version, gover.ExplicitIndirectVersion) < 0 {
142 if len(modFile.Require) > 0 {
143 args = []string{"all"}
144 }
145 } else {
146
147
148
149
150
151
152
153
154
155
156 _, err := modload.LoadModGraph(ctx, "")
157 if err != nil {
158
159
160
161 toolchain.SwitchOrFatal(ctx, err)
162 }
163
164 for _, m := range modFile.Require {
165 args = append(args, m.Mod.Path)
166 }
167 }
168 }
169 }
170
171 if len(args) == 0 {
172 if modload.HasModRoot() {
173 os.Stderr.WriteString("go: no module dependencies to download\n")
174 } else {
175 base.Errorf("go: no modules specified (see 'go help mod download')")
176 }
177 base.Exit()
178 }
179
180 if *downloadReuse != "" && modload.HasModRoot() {
181 base.Fatalf("go mod download -reuse cannot be used inside a module")
182 }
183
184 var mods []*ModuleJSON
185 type token struct{}
186 sem := make(chan token, runtime.GOMAXPROCS(0))
187 infos, infosErr := modload.ListModules(ctx, args, 0, *downloadReuse)
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 if infosErr != nil {
214 var sw toolchain.Switcher
215 sw.Error(infosErr)
216 if sw.NeedSwitch() {
217 sw.Switch(ctx)
218 }
219
220
221 }
222
223 if !haveExplicitArgs && modload.WorkFilePath() == "" {
224
225
226
227
228
229
230
231
232
233
234 if err := modload.WriteGoMod(ctx, modload.WriteOpts{}); err != nil {
235 base.Fatal(err)
236 }
237 }
238
239 var downloadErrs sync.Map
240 for _, info := range infos {
241 if info.Replace != nil {
242 info = info.Replace
243 }
244 if info.Version == "" && info.Error == nil {
245
246
247 continue
248 }
249 m := &ModuleJSON{
250 Path: info.Path,
251 Version: info.Version,
252 Query: info.Query,
253 Reuse: info.Reuse,
254 Origin: info.Origin,
255 }
256 mods = append(mods, m)
257 if info.Error != nil {
258 m.Error = info.Error.Err
259 continue
260 }
261 if m.Reuse {
262 continue
263 }
264 sem <- token{}
265 go func() {
266 err := DownloadModule(ctx, m)
267 if err != nil {
268 downloadErrs.Store(m, err)
269 m.Error = err.Error()
270 }
271 <-sem
272 }()
273 }
274
275
276 for n := cap(sem); n > 0; n-- {
277 sem <- token{}
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294 if haveExplicitArgs || modload.WorkFilePath() != "" {
295 var sw toolchain.Switcher
296
297
298 for _, m := range mods {
299 if erri, ok := downloadErrs.Load(m); ok {
300 sw.Error(erri.(error))
301 }
302 }
303
304
305
306
307
308 if sw.NeedSwitch() {
309 sw.Switch(ctx)
310 }
311 }
312
313 if *downloadJSON {
314 for _, m := range mods {
315 b, err := json.MarshalIndent(m, "", "\t")
316 if err != nil {
317 base.Fatal(err)
318 }
319 os.Stdout.Write(append(b, '\n'))
320 if m.Error != "" {
321 base.SetExitStatus(1)
322 }
323 }
324 } else {
325 for _, m := range mods {
326 if m.Error != "" {
327 base.Error(errors.New(m.Error))
328 }
329 }
330 base.ExitIfErrors()
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350 if haveExplicitArgs || modload.WorkFilePath() != "" {
351 if err := modload.WriteGoMod(ctx, modload.WriteOpts{}); err != nil {
352 base.Error(err)
353 }
354 }
355
356
357
358
359 if infosErr != nil {
360 base.Error(infosErr)
361 }
362 }
363
364
365
366 func DownloadModule(ctx context.Context, m *ModuleJSON) error {
367 var err error
368 _, file, err := modfetch.InfoFile(ctx, m.Path, m.Version)
369 if err != nil {
370 return err
371 }
372 m.Info = file
373 m.GoMod, err = modfetch.GoModFile(ctx, m.Path, m.Version)
374 if err != nil {
375 return err
376 }
377 m.GoModSum, err = modfetch.GoModSum(ctx, m.Path, m.Version)
378 if err != nil {
379 return err
380 }
381 mod := module.Version{Path: m.Path, Version: m.Version}
382 m.Zip, err = modfetch.DownloadZip(ctx, mod)
383 if err != nil {
384 return err
385 }
386 m.Sum = modfetch.Sum(ctx, mod)
387 m.Dir, err = modfetch.Download(ctx, mod)
388 return err
389 }
390
View as plain text