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