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 if path, vers, found := strings.Cut(arg, "@"); found {
154 if vers == "upgrade" || vers == "patch" {
155 if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned {
156 needFullGraph = true
157 if !HasModRoot() {
158 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
159 }
160 }
161 }
162 continue
163 }
164 if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned {
165 needFullGraph = true
166 if mode&ListVersions == 0 && !HasModRoot() {
167 base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot)
168 }
169 }
170 }
171
172 var mg *ModuleGraph
173 if needFullGraph {
174 rs, mg, mgErr = expandGraph(ctx, rs)
175 }
176
177 matchedModule := map[module.Version]bool{}
178 for _, arg := range args {
179 if path, vers, found := strings.Cut(arg, "@"); found {
180 var current string
181 if mg == nil {
182 current, _ = rs.rootSelected(path)
183 } else {
184 current = mg.Selected(path)
185 }
186 if current == "none" && mgErr != nil {
187 if vers == "upgrade" || vers == "patch" {
188
189
190
191 continue
192 }
193 }
194
195 allowed := CheckAllowed
196 if IsRevisionQuery(path, vers) || mode&ListRetracted != 0 {
197
198
199 allowed = nil
200 }
201 info, err := queryReuse(ctx, path, vers, current, allowed, reuse)
202 if err != nil {
203 var origin *codehost.Origin
204 if info != nil {
205 origin = info.Origin
206 }
207 mods = append(mods, &modinfo.ModulePublic{
208 Path: path,
209 Version: vers,
210 Error: modinfoError(path, vers, err),
211 Origin: origin,
212 })
213 continue
214 }
215
216
217
218 var noRS *Requirements
219
220 mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode, reuse)
221 if vers != mod.Version {
222 mod.Query = vers
223 }
224 mod.Origin = info.Origin
225 mods = append(mods, mod)
226 continue
227 }
228
229
230 var match func(string) bool
231 if arg == "all" {
232 match = func(p string) bool { return !gover.IsToolchain(p) }
233 } else if strings.Contains(arg, "...") {
234 mp := pkgpattern.MatchPattern(arg)
235 match = func(p string) bool { return mp(p) && !gover.IsToolchain(p) }
236 } else {
237 var v string
238 if mg == nil {
239 var ok bool
240 v, ok = rs.rootSelected(arg)
241 if !ok {
242
243
244 panic(fmt.Sprintf("internal error: root requirement expected but not found for %v", arg))
245 }
246 } else {
247 v = mg.Selected(arg)
248 }
249 if v == "none" && mgErr != nil {
250
251 continue
252 }
253 if v != "none" {
254 mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode, reuse))
255 } else if cfg.BuildMod == "vendor" {
256
257
258
259 mods = append(mods, &modinfo.ModulePublic{
260 Path: arg,
261 Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")),
262 })
263 } else if mode&ListVersions != 0 {
264
265
266
267 mods = append(mods, &modinfo.ModulePublic{Path: arg})
268 } else {
269 mods = append(mods, &modinfo.ModulePublic{
270 Path: arg,
271 Error: modinfoError(arg, "", errors.New("not a known dependency")),
272 })
273 }
274 continue
275 }
276
277 var matches []module.Version
278 for _, m := range mg.BuildList() {
279 if match(m.Path) {
280 if !matchedModule[m] {
281 matchedModule[m] = true
282 matches = append(matches, m)
283 }
284 }
285 }
286
287 if len(matches) == 0 {
288 fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
289 }
290
291 q := par.NewQueue(runtime.GOMAXPROCS(0))
292 fetchedMods := make([]*modinfo.ModulePublic, len(matches))
293 for i, m := range matches {
294 q.Add(func() {
295 fetchedMods[i] = moduleInfo(ctx, rs, m, mode, reuse)
296 })
297 }
298 <-q.Idle()
299 mods = append(mods, fetchedMods...)
300 }
301
302 return rs, mods, mgErr
303 }
304
305
306
307 func modinfoError(path, vers string, err error) *modinfo.ModuleError {
308 var nerr *NoMatchingVersionError
309 var merr *module.ModuleError
310 if errors.As(err, &nerr) {
311
312
313 err = &module.ModuleError{Path: path, Err: err}
314 } else if !errors.As(err, &merr) {
315
316
317 err = &module.ModuleError{Path: path, Version: vers, Err: err}
318 }
319
320 return &modinfo.ModuleError{Err: err.Error()}
321 }
322
View as plain text