1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "io"
14 "os"
15 "runtime"
16 "strings"
17
18 "cmd/go/internal/base"
19 "cmd/go/internal/cfg"
20 "cmd/go/internal/gover"
21 "cmd/go/internal/modfetch/codehost"
22 "cmd/go/internal/modinfo"
23 "cmd/go/internal/search"
24 "cmd/internal/par"
25 "cmd/internal/pkgpattern"
26
27 "golang.org/x/mod/module"
28 )
29
30 type ListMode int
31
32 const (
33 ListU ListMode = 1 << iota
34 ListRetracted
35 ListDeprecated
36 ListVersions
37 ListRetractedVersions
38 )
39
40
41
42
43
44 func ListModules(ctx context.Context, args []string, mode ListMode, reuseFile string) ([]*modinfo.ModulePublic, error) {
45 var reuse map[module.Version]*modinfo.ModulePublic
46 if reuseFile != "" {
47 data, err := os.ReadFile(reuseFile)
48 if err != nil {
49 return nil, err
50 }
51 dec := json.NewDecoder(bytes.NewReader(data))
52 reuse = make(map[module.Version]*modinfo.ModulePublic)
53 for {
54 var m modinfo.ModulePublic
55 if err := dec.Decode(&m); err != nil {
56 if err == io.EOF {
57 break
58 }
59 return nil, fmt.Errorf("parsing %s: %v", reuseFile, err)
60 }
61 if m.Origin == nil {
62 continue
63 }
64 m.Reuse = true
65 reuse[module.Version{Path: m.Path, Version: m.Version}] = &m
66 if m.Query != "" {
67 reuse[module.Version{Path: m.Path, Version: m.Query}] = &m
68 }
69 }
70 }
71
72 rs, mods, err := listModules(ctx, LoadModFile(ctx), args, mode, reuse)
73
74 type token struct{}
75 sem := make(chan token, runtime.GOMAXPROCS(0))
76 if mode != 0 {
77 for _, m := range mods {
78 if m.Reuse {
79 continue
80 }
81 add := func(m *modinfo.ModulePublic) {
82 sem <- token{}
83 go func() {
84 if mode&ListU != 0 {
85 addUpdate(ctx, m)
86 }
87 if mode&ListVersions != 0 {
88 addVersions(ctx, m, mode&ListRetractedVersions != 0)
89 }
90 if mode&ListRetracted != 0 {
91 addRetraction(ctx, m)
92 }
93 if mode&ListDeprecated != 0 {
94 addDeprecation(ctx, m)
95 }
96 <-sem
97 }()
98 }
99
100 add(m)
101 if m.Replace != nil {
102 add(m.Replace)
103 }
104 }
105 }
106
107 for n := cap(sem); n > 0; n-- {
108 sem <- token{}
109 }
110
111 if err == nil {
112 requirements = rs
113
114
115
116
117
118
119 if !ExplicitWriteGoMod && mode&ListU == 0 {
120 err = commitRequirements(ctx, WriteOpts{})
121 }
122 }
123 return mods, err
124 }
125
126 func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) {
127 if len(args) == 0 {
128 var ms []*modinfo.ModulePublic
129 for _, m := range MainModules.Versions() {
130 if gover.IsToolchain(m.Path) {
131 continue
132 }
133 ms = append(ms, moduleInfo(ctx, rs, m, mode, reuse))
134 }
135 return rs, ms, nil
136 }
137
138 needFullGraph := false
139 for _, arg := range args {
140 if strings.Contains(arg, `\`) {
141 base.Fatalf("go: module paths never use backslash")
142 }
143 if search.IsRelativePath(arg) {
144 base.Fatalf("go: cannot use relative path %s to specify module", arg)
145 }
146 if arg == "all" || strings.Contains(arg, "...") {
147 needFullGraph = true
148 if !HasModRoot() {
149 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
150 }
151 continue
152 }
153 path, vers, found, err := ParsePathVersion(arg)
154 if err != nil {
155 base.Fatalf("go: %v", err)
156 }
157 if found {
158 if vers == "upgrade" || vers == "patch" {
159 if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned {
160 needFullGraph = true
161 if !HasModRoot() {
162 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
163 }
164 }
165 }
166 continue
167 }
168 if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned {
169 needFullGraph = true
170 if mode&ListVersions == 0 && !HasModRoot() {
171 base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot)
172 }
173 }
174 }
175
176 var mg *ModuleGraph
177 if needFullGraph {
178 rs, mg, mgErr = expandGraph(ctx, rs)
179 }
180
181 matchedModule := map[module.Version]bool{}
182 for _, arg := range args {
183 path, vers, found, err := ParsePathVersion(arg)
184 if err != nil {
185 base.Fatalf("go: %v", err)
186 }
187 if found {
188 var current string
189 if mg == nil {
190 current, _ = rs.rootSelected(path)
191 } else {
192 current = mg.Selected(path)
193 }
194 if current == "none" && mgErr != nil {
195 if vers == "upgrade" || vers == "patch" {
196
197
198
199 continue
200 }
201 }
202
203 allowed := CheckAllowed
204 if IsRevisionQuery(path, vers) || mode&ListRetracted != 0 {
205
206
207 allowed = nil
208 }
209 info, err := queryReuse(ctx, path, vers, current, allowed, reuse)
210 if err != nil {
211 var origin *codehost.Origin
212 if info != nil {
213 origin = info.Origin
214 }
215 mods = append(mods, &modinfo.ModulePublic{
216 Path: path,
217 Version: vers,
218 Error: modinfoError(path, vers, err),
219 Origin: origin,
220 })
221 continue
222 }
223
224
225
226 var noRS *Requirements
227
228 mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode, reuse)
229 if vers != mod.Version {
230 mod.Query = vers
231 }
232 mod.Origin = info.Origin
233 mods = append(mods, mod)
234 continue
235 }
236
237
238 var match func(string) bool
239 if arg == "all" {
240 match = func(p string) bool { return !gover.IsToolchain(p) }
241 } else if strings.Contains(arg, "...") {
242 mp := pkgpattern.MatchPattern(arg)
243 match = func(p string) bool { return mp(p) && !gover.IsToolchain(p) }
244 } else {
245 var v string
246 if mg == nil {
247 var ok bool
248 v, ok = rs.rootSelected(arg)
249 if !ok {
250
251
252 panic(fmt.Sprintf("internal error: root requirement expected but not found for %v", arg))
253 }
254 } else {
255 v = mg.Selected(arg)
256 }
257 if v == "none" && mgErr != nil {
258
259 continue
260 }
261 if v != "none" {
262 mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode, reuse))
263 } else if cfg.BuildMod == "vendor" {
264
265
266
267 mods = append(mods, &modinfo.ModulePublic{
268 Path: arg,
269 Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")),
270 })
271 } else if mode&ListVersions != 0 {
272
273
274
275 mods = append(mods, &modinfo.ModulePublic{Path: arg})
276 } else {
277 mods = append(mods, &modinfo.ModulePublic{
278 Path: arg,
279 Error: modinfoError(arg, "", errors.New("not a known dependency")),
280 })
281 }
282 continue
283 }
284
285 var matches []module.Version
286 for _, m := range mg.BuildList() {
287 if match(m.Path) {
288 if !matchedModule[m] {
289 matchedModule[m] = true
290 matches = append(matches, m)
291 }
292 }
293 }
294
295 if len(matches) == 0 {
296 fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
297 }
298
299 q := par.NewQueue(runtime.GOMAXPROCS(0))
300 fetchedMods := make([]*modinfo.ModulePublic, len(matches))
301 for i, m := range matches {
302 q.Add(func() {
303 fetchedMods[i] = moduleInfo(ctx, rs, m, mode, reuse)
304 })
305 }
306 <-q.Idle()
307 mods = append(mods, fetchedMods...)
308 }
309
310 return rs, mods, mgErr
311 }
312
313
314
315 func modinfoError(path, vers string, err error) *modinfo.ModuleError {
316 var nerr *NoMatchingVersionError
317 var merr *module.ModuleError
318 if errors.As(err, &nerr) {
319
320
321 err = &module.ModuleError{Path: path, Err: err}
322 } else if !errors.As(err, &merr) {
323
324
325 err = &module.ModuleError{Path: path, Version: vers, Err: err}
326 }
327
328 return &modinfo.ModuleError{Err: err.Error()}
329 }
330
331
332
333
334
335
336
337
338 func ParsePathVersion(arg string) (path, vers string, found bool, err error) {
339 path, vers, found = strings.Cut(arg, "@")
340 if !found {
341 return arg, "", false, nil
342 }
343 if len(vers) > 0 && (vers[0] == '-' || vers[0] == '/') {
344 return "", "", false, fmt.Errorf("invalid module version %q", vers)
345 }
346 return path, vers, true, nil
347 }
348
View as plain text