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 moduleLoaderState := modload.NewState()
113 moduleLoaderState.InitWorkfile()
114
115
116 moduleLoaderState.ForceUseModules = true
117 modload.ExplicitWriteGoMod = true
118 haveExplicitArgs := len(args) > 0
119
120 if moduleLoaderState.HasModRoot() || modload.WorkFilePath(moduleLoaderState) != "" {
121 modload.LoadModFile(moduleLoaderState, ctx)
122
123 if haveExplicitArgs {
124 for _, mainModule := range moduleLoaderState.MainModules.Versions() {
125 targetAtUpgrade := mainModule.Path + "@upgrade"
126 targetAtPatch := mainModule.Path + "@patch"
127 for _, arg := range args {
128 switch arg {
129 case mainModule.Path, targetAtUpgrade, targetAtPatch:
130 os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n")
131 }
132 }
133 }
134 } else if modload.WorkFilePath(moduleLoaderState) != "" {
135
136
137
138 args = []string{"all"}
139 } else {
140 mainModule := moduleLoaderState.MainModules.Versions()[0]
141 modFile := moduleLoaderState.MainModules.ModFile(mainModule)
142 if modFile.Go == nil || gover.Compare(modFile.Go.Version, gover.ExplicitIndirectVersion) < 0 {
143 if len(modFile.Require) > 0 {
144 args = []string{"all"}
145 }
146 } else {
147
148
149
150
151
152
153
154
155
156
157 _, err := modload.LoadModGraph(moduleLoaderState, ctx, "")
158 if err != nil {
159
160
161
162 toolchain.SwitchOrFatal(moduleLoaderState, ctx, err)
163 }
164
165 for _, m := range modFile.Require {
166 args = append(args, m.Mod.Path)
167 }
168 }
169 }
170 }
171
172 if len(args) == 0 {
173 if moduleLoaderState.HasModRoot() {
174 os.Stderr.WriteString("go: no module dependencies to download\n")
175 } else {
176 base.Errorf("go: no modules specified (see 'go help mod download')")
177 }
178 base.Exit()
179 }
180
181 if *downloadReuse != "" && moduleLoaderState.HasModRoot() {
182 base.Fatalf("go mod download -reuse cannot be used inside a module")
183 }
184
185 var mods []*ModuleJSON
186 type token struct{}
187 sem := make(chan token, runtime.GOMAXPROCS(0))
188 infos, infosErr := modload.ListModules(moduleLoaderState, ctx, args, 0, *downloadReuse)
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
214 if infosErr != nil {
215 sw := toolchain.NewSwitcher(moduleLoaderState)
216 sw.Error(infosErr)
217 if sw.NeedSwitch() {
218 sw.Switch(ctx)
219 }
220
221
222 }
223
224 if !haveExplicitArgs && modload.WorkFilePath(moduleLoaderState) == "" {
225
226
227
228
229
230
231
232
233
234
235 if err := modload.WriteGoMod(moduleLoaderState, ctx, modload.WriteOpts{}); err != nil {
236 base.Fatal(err)
237 }
238 }
239
240 var downloadErrs sync.Map
241 for _, info := range infos {
242 if info.Replace != nil {
243 info = info.Replace
244 }
245 if info.Version == "" && info.Error == nil {
246
247
248 continue
249 }
250 m := &ModuleJSON{
251 Path: info.Path,
252 Version: info.Version,
253 Query: info.Query,
254 Reuse: info.Reuse,
255 Origin: info.Origin,
256 }
257 mods = append(mods, m)
258 if info.Error != nil {
259 m.Error = info.Error.Err
260 continue
261 }
262 if m.Reuse {
263 continue
264 }
265 sem <- token{}
266 go func() {
267 err := DownloadModule(ctx, moduleLoaderState.Fetcher(), m)
268 if err != nil {
269 downloadErrs.Store(m, err)
270 m.Error = err.Error()
271 }
272 <-sem
273 }()
274 }
275
276
277 for n := cap(sem); n > 0; n-- {
278 sem <- token{}
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295 if haveExplicitArgs || modload.WorkFilePath(moduleLoaderState) != "" {
296 sw := toolchain.NewSwitcher(moduleLoaderState)
297
298
299 for _, m := range mods {
300 if erri, ok := downloadErrs.Load(m); ok {
301 sw.Error(erri.(error))
302 }
303 }
304
305
306
307
308
309 if sw.NeedSwitch() {
310 sw.Switch(ctx)
311 }
312 }
313
314 if *downloadJSON {
315 for _, m := range mods {
316 b, err := json.MarshalIndent(m, "", "\t")
317 if err != nil {
318 base.Fatal(err)
319 }
320 os.Stdout.Write(append(b, '\n'))
321 if m.Error != "" {
322 base.SetExitStatus(1)
323 }
324 }
325 } else {
326 for _, m := range mods {
327 if m.Error != "" {
328 base.Error(errors.New(m.Error))
329 }
330 }
331 base.ExitIfErrors()
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 if haveExplicitArgs || modload.WorkFilePath(moduleLoaderState) != "" {
352 if err := modload.WriteGoMod(moduleLoaderState, ctx, modload.WriteOpts{}); err != nil {
353 base.Error(err)
354 }
355 }
356
357
358
359
360 if infosErr != nil {
361 base.Error(infosErr)
362 }
363 }
364
365
366
367 func DownloadModule(ctx context.Context, f *modfetch.Fetcher, m *ModuleJSON) error {
368 var err error
369 _, file, err := f.InfoFile(ctx, m.Path, m.Version)
370 if err != nil {
371 return err
372 }
373 m.Info = file
374 m.GoMod, err = f.GoModFile(ctx, m.Path, m.Version)
375 if err != nil {
376 return err
377 }
378 m.GoModSum, err = f.GoModSum(ctx, m.Path, m.Version)
379 if err != nil {
380 return err
381 }
382 mod := module.Version{Path: m.Path, Version: m.Version}
383 m.Zip, err = f.DownloadZip(ctx, mod)
384 if err != nil {
385 return err
386 }
387 m.Sum = modfetch.Sum(ctx, mod)
388 m.Dir, err = f.Download(ctx, mod)
389 return err
390 }
391
View as plain text