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 "golang.org/x/mod/semver"
20 )
21
22 var cmdDownload = &base.Command{
23 UsageLine: "go mod download [-x] [-json] [modules]",
24 Short: "download modules to local cache",
25 Long: `
26 Download downloads the named modules, which can be module patterns selecting
27 dependencies of the main module or module queries of the form path@version.
28
29 With no arguments, download applies to the modules needed to build and test
30 the packages in the main module: the modules explicitly required by the main
31 module if it is at 'go 1.17' or higher, or all transitively-required modules
32 if at 'go 1.16' or lower.
33
34 The go command will automatically download modules as needed during ordinary
35 execution. The "go mod download" command is useful mainly for pre-filling
36 the local cache or to compute the answers for a Go module proxy.
37
38 By default, download writes nothing to standard output. It may print progress
39 messages and errors to standard error.
40
41 The -json flag causes download to print a sequence of JSON objects
42 to standard output, describing each downloaded module (or failure),
43 corresponding to this Go struct:
44
45 type Module struct {
46 Path string // module path
47 Version string // module version
48 Error string // error loading module
49 Info string // absolute path to cached .info file
50 GoMod string // absolute path to cached .mod file
51 Zip string // absolute path to cached .zip file
52 Dir string // absolute path to cached source root directory
53 Sum string // checksum for path, version (as in go.sum)
54 GoModSum string // checksum for go.mod (as in go.sum)
55 }
56
57 The -x flag causes download to print the commands download executes.
58
59 See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'.
60
61 See https://golang.org/ref/mod#version-queries for more about version queries.
62 `,
63 }
64
65 var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
66
67 func init() {
68 cmdDownload.Run = runDownload
69
70
71 cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
72 base.AddModCommonFlags(&cmdDownload.Flag)
73 }
74
75 type moduleJSON struct {
76 Path string `json:",omitempty"`
77 Version string `json:",omitempty"`
78 Error string `json:",omitempty"`
79 Info string `json:",omitempty"`
80 GoMod string `json:",omitempty"`
81 Zip string `json:",omitempty"`
82 Dir string `json:",omitempty"`
83 Sum string `json:",omitempty"`
84 GoModSum string `json:",omitempty"`
85 }
86
87 func runDownload(ctx context.Context, cmd *base.Command, args []string) {
88 modload.InitWorkfile()
89
90
91 modload.ForceUseModules = true
92 modload.ExplicitWriteGoMod = true
93 haveExplicitArgs := len(args) > 0
94
95 if modload.HasModRoot() || modload.WorkFilePath() != "" {
96 modload.LoadModFile(ctx)
97
98 if haveExplicitArgs {
99 for _, mainModule := range modload.MainModules.Versions() {
100 targetAtUpgrade := mainModule.Path + "@upgrade"
101 targetAtPatch := mainModule.Path + "@patch"
102 for _, arg := range args {
103 switch arg {
104 case mainModule.Path, targetAtUpgrade, targetAtPatch:
105 os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n")
106 }
107 }
108 }
109 } else if modload.WorkFilePath() != "" {
110
111
112
113 args = []string{"all"}
114 } else {
115 mainModule := modload.MainModules.Versions()[0]
116 modFile := modload.MainModules.ModFile(mainModule)
117 if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, modload.ExplicitIndirectVersionV) < 0 {
118 if len(modFile.Require) > 0 {
119 args = []string{"all"}
120 }
121 } else {
122
123
124
125
126
127
128
129
130
131
132 _ = modload.LoadModGraph(ctx, "")
133
134 for _, m := range modFile.Require {
135 args = append(args, m.Mod.Path)
136 }
137 }
138 }
139 }
140
141 if len(args) == 0 {
142 if modload.HasModRoot() {
143 os.Stderr.WriteString("go: no module dependencies to download\n")
144 } else {
145 base.Errorf("go: no modules specified (see 'go help mod download')")
146 }
147 base.Exit()
148 }
149
150 downloadModule := func(m *moduleJSON) {
151 var err error
152 m.Info, err = modfetch.InfoFile(m.Path, m.Version)
153 if err != nil {
154 m.Error = err.Error()
155 return
156 }
157 m.GoMod, err = modfetch.GoModFile(m.Path, m.Version)
158 if err != nil {
159 m.Error = err.Error()
160 return
161 }
162 m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version)
163 if err != nil {
164 m.Error = err.Error()
165 return
166 }
167 mod := module.Version{Path: m.Path, Version: m.Version}
168 m.Zip, err = modfetch.DownloadZip(ctx, mod)
169 if err != nil {
170 m.Error = err.Error()
171 return
172 }
173 m.Sum = modfetch.Sum(mod)
174 m.Dir, err = modfetch.Download(ctx, mod)
175 if err != nil {
176 m.Error = err.Error()
177 return
178 }
179 }
180
181 var mods []*moduleJSON
182 type token struct{}
183 sem := make(chan token, runtime.GOMAXPROCS(0))
184 infos, infosErr := modload.ListModules(ctx, args, 0)
185 if !haveExplicitArgs {
186
187
188
189
190
191
192
193
194
195 if err := modload.WriteGoMod(ctx); err != nil {
196 base.Fatalf("go: %v", err)
197 }
198 }
199
200 for _, info := range infos {
201 if info.Replace != nil {
202 info = info.Replace
203 }
204 if info.Version == "" && info.Error == nil {
205
206
207 continue
208 }
209 m := &moduleJSON{
210 Path: info.Path,
211 Version: info.Version,
212 }
213 mods = append(mods, m)
214 if info.Error != nil {
215 m.Error = info.Error.Err
216 continue
217 }
218 sem <- token{}
219 go func() {
220 downloadModule(m)
221 <-sem
222 }()
223 }
224
225
226 for n := cap(sem); n > 0; n-- {
227 sem <- token{}
228 }
229
230 if *downloadJSON {
231 for _, m := range mods {
232 b, err := json.MarshalIndent(m, "", "\t")
233 if err != nil {
234 base.Fatalf("go: %v", err)
235 }
236 os.Stdout.Write(append(b, '\n'))
237 if m.Error != "" {
238 base.SetExitStatus(1)
239 }
240 }
241 } else {
242 for _, m := range mods {
243 if m.Error != "" {
244 base.Errorf("go: %v", m.Error)
245 }
246 }
247 base.ExitIfErrors()
248 }
249
250
251
252
253
254
255
256 if haveExplicitArgs {
257 if err := modload.WriteGoMod(ctx); err != nil {
258 base.Errorf("go: %v", err)
259 }
260 }
261
262
263
264
265 if infosErr != nil {
266 base.Errorf("go: %v", infosErr)
267 }
268 }
269
View as plain text