1
2
3
4
5 package modload
6
7 import (
8 "context"
9 "encoding/hex"
10 "errors"
11 "fmt"
12 "io/fs"
13 "os"
14 "path/filepath"
15 "strings"
16
17 "cmd/go/internal/base"
18 "cmd/go/internal/cfg"
19 "cmd/go/internal/gover"
20 "cmd/go/internal/modfetch"
21 "cmd/go/internal/modfetch/codehost"
22 "cmd/go/internal/modindex"
23 "cmd/go/internal/modinfo"
24 "cmd/go/internal/search"
25
26 "golang.org/x/mod/module"
27 )
28
29 var (
30 infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
31 infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2")
32 )
33
34 func isStandardImportPath(path string) bool {
35 return findStandardImportPath(path) != ""
36 }
37
38 func findStandardImportPath(path string) string {
39 if path == "" {
40 panic("findStandardImportPath called with empty path")
41 }
42 if search.IsStandardImportPath(path) {
43 if modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
44 return filepath.Join(cfg.GOROOT, "src", path)
45 }
46 }
47 return ""
48 }
49
50
51
52
53
54 func PackageModuleInfo(ctx context.Context, pkgpath string) *modinfo.ModulePublic {
55 if isStandardImportPath(pkgpath) || !Enabled() {
56 return nil
57 }
58 m, ok := findModule(loaded, pkgpath)
59 if !ok {
60 return nil
61 }
62
63 rs := LoadModFile(ctx)
64 return moduleInfo(ctx, rs, m, 0, nil)
65 }
66
67
68
69
70
71 func PackageModRoot(ctx context.Context, pkgpath string) string {
72 if isStandardImportPath(pkgpath) || !Enabled() || cfg.BuildMod == "vendor" {
73 return ""
74 }
75 m, ok := findModule(loaded, pkgpath)
76 if !ok {
77 return ""
78 }
79 root, _, err := fetch(ctx, m)
80 if err != nil {
81 return ""
82 }
83 return root
84 }
85
86 func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
87 if !Enabled() {
88 return nil
89 }
90
91 if path, vers, found := strings.Cut(path, "@"); found {
92 m := module.Version{Path: path, Version: vers}
93 return moduleInfo(ctx, nil, m, 0, nil)
94 }
95
96 rs := LoadModFile(ctx)
97
98 var (
99 v string
100 ok bool
101 )
102 if rs.pruning == pruned {
103 v, ok = rs.rootSelected(path)
104 }
105 if !ok {
106 mg, err := rs.Graph(ctx)
107 if err != nil {
108 base.Fatal(err)
109 }
110 v = mg.Selected(path)
111 }
112
113 if v == "none" {
114 return &modinfo.ModulePublic{
115 Path: path,
116 Error: &modinfo.ModuleError{
117 Err: "module not in current build",
118 },
119 }
120 }
121
122 return moduleInfo(ctx, rs, module.Version{Path: path, Version: v}, 0, nil)
123 }
124
125
126 func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
127 if m.Version == "" {
128 return
129 }
130
131 info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed)
132 var noVersionErr *NoMatchingVersionError
133 if errors.Is(err, ErrDisallowed) ||
134 errors.Is(err, fs.ErrNotExist) ||
135 errors.As(err, &noVersionErr) {
136
137
138
139
140
141
142
143
144
145
146
147 return
148 } else if err != nil {
149 if m.Error == nil {
150 m.Error = &modinfo.ModuleError{Err: err.Error()}
151 }
152 return
153 }
154
155 if gover.ModCompare(m.Path, info.Version, m.Version) > 0 {
156 m.Update = &modinfo.ModulePublic{
157 Path: m.Path,
158 Version: info.Version,
159 Time: &info.Time,
160 }
161 }
162 }
163
164
165
166
167
168 func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin {
169 if m1 == nil || m2 == nil {
170 return nil
171 }
172
173 if m2.VCS != m1.VCS ||
174 m2.URL != m1.URL ||
175 m2.Subdir != m1.Subdir {
176 return nil
177 }
178
179 merged := *m1
180 if m2.Hash != "" {
181 if m1.Hash != "" && m1.Hash != m2.Hash {
182 return nil
183 }
184 merged.Hash = m2.Hash
185 }
186 if m2.TagSum != "" {
187 if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) {
188 return nil
189 }
190 merged.TagSum = m2.TagSum
191 merged.TagPrefix = m2.TagPrefix
192 }
193 if m2.Ref != "" {
194 if m1.Ref != "" && m1.Ref != m2.Ref {
195 return nil
196 }
197 merged.Ref = m2.Ref
198 }
199
200 switch {
201 case merged == *m1:
202 return m1
203 case merged == *m2:
204 return m2
205 default:
206
207
208 clone := merged
209 return &clone
210 }
211 }
212
213
214
215
216 func addVersions(ctx context.Context, m *modinfo.ModulePublic, listRetracted bool) {
217
218
219
220
221 allowed := CheckAllowed
222 if listRetracted {
223 allowed = CheckExclusions
224 }
225 v, origin, err := versions(ctx, m.Path, allowed)
226 if err != nil && m.Error == nil {
227 m.Error = &modinfo.ModuleError{Err: err.Error()}
228 }
229 m.Versions = v
230 m.Origin = mergeOrigin(m.Origin, origin)
231 }
232
233
234
235 func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
236 if m.Version == "" {
237 return
238 }
239
240 err := CheckRetractions(ctx, module.Version{Path: m.Path, Version: m.Version})
241 var noVersionErr *NoMatchingVersionError
242 var retractErr *ModuleRetractedError
243 if err == nil || errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
244
245
246
247
248
249
250
251
252 return
253 } else if errors.As(err, &retractErr) {
254 if len(retractErr.Rationale) == 0 {
255 m.Retracted = []string{"retracted by module author"}
256 } else {
257 m.Retracted = retractErr.Rationale
258 }
259 } else if m.Error == nil {
260 m.Error = &modinfo.ModuleError{Err: err.Error()}
261 }
262 }
263
264
265
266 func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) {
267 deprecation, err := CheckDeprecation(ctx, module.Version{Path: m.Path, Version: m.Version})
268 var noVersionErr *NoMatchingVersionError
269 if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
270
271
272
273
274
275
276
277
278 return
279 }
280 if err != nil {
281 if m.Error == nil {
282 m.Error = &modinfo.ModuleError{Err: err.Error()}
283 }
284 return
285 }
286 m.Deprecated = deprecation
287 }
288
289
290
291
292 func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) *modinfo.ModulePublic {
293 if m.Version == "" && MainModules.Contains(m.Path) {
294 info := &modinfo.ModulePublic{
295 Path: m.Path,
296 Version: m.Version,
297 Main: true,
298 }
299 if v, ok := rawGoVersion.Load(m); ok {
300 info.GoVersion = v.(string)
301 } else {
302 panic("internal error: GoVersion not set for main module")
303 }
304 if modRoot := MainModules.ModRoot(m); modRoot != "" {
305 info.Dir = modRoot
306 info.GoMod = modFilePath(modRoot)
307 }
308 return info
309 }
310
311 info := &modinfo.ModulePublic{
312 Path: m.Path,
313 Version: m.Version,
314 Indirect: rs != nil && !rs.direct[m.Path],
315 }
316 if v, ok := rawGoVersion.Load(m); ok {
317 info.GoVersion = v.(string)
318 }
319
320
321 completeFromModCache := func(m *modinfo.ModulePublic) {
322 if gover.IsToolchain(m.Path) {
323 return
324 }
325
326 checksumOk := func(suffix string) bool {
327 return rs == nil || m.Version == "" || !mustHaveSums() ||
328 modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix})
329 }
330
331 mod := module.Version{Path: m.Path, Version: m.Version}
332
333 if m.Version != "" {
334 if old := reuse[mod]; old != nil {
335 if err := checkReuse(ctx, mod, old.Origin); err == nil {
336 *m = *old
337 m.Query = ""
338 m.Dir = ""
339 return
340 }
341 }
342
343 if q, err := Query(ctx, m.Path, m.Version, "", nil); err != nil {
344 m.Error = &modinfo.ModuleError{Err: err.Error()}
345 } else {
346 m.Version = q.Version
347 m.Time = &q.Time
348 }
349 }
350
351 if m.GoVersion == "" && checksumOk("/go.mod") {
352
353
354 if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" {
355 m.GoVersion = summary.goVersion
356 }
357 }
358
359 if m.Version != "" {
360 if checksumOk("/go.mod") {
361 gomod, err := modfetch.CachePath(ctx, mod, "mod")
362 if err == nil {
363 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() {
364 m.GoMod = gomod
365 }
366 }
367 if gomodsum, ok := modfetch.RecordedSum(modkey(mod)); ok {
368 m.GoModSum = gomodsum
369 }
370 }
371 if checksumOk("") {
372 dir, err := modfetch.DownloadDir(ctx, mod)
373 if err == nil {
374 m.Dir = dir
375 }
376 if sum, ok := modfetch.RecordedSum(mod); ok {
377 m.Sum = sum
378 }
379 }
380
381 if mode&ListRetracted != 0 {
382 addRetraction(ctx, m)
383 }
384 }
385 }
386
387 if rs == nil {
388
389
390 completeFromModCache(info)
391 return info
392 }
393
394 r := Replacement(m)
395 if r.Path == "" {
396 if cfg.BuildMod == "vendor" {
397
398
399
400
401
402 } else {
403 completeFromModCache(info)
404 }
405 return info
406 }
407
408
409
410
411
412 info.Replace = &modinfo.ModulePublic{
413 Path: r.Path,
414 Version: r.Version,
415 }
416 if v, ok := rawGoVersion.Load(m); ok {
417 info.Replace.GoVersion = v.(string)
418 }
419 if r.Version == "" {
420 if filepath.IsAbs(r.Path) {
421 info.Replace.Dir = r.Path
422 } else {
423 info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path)
424 }
425 info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
426 }
427 if cfg.BuildMod != "vendor" {
428 completeFromModCache(info.Replace)
429 info.Dir = info.Replace.Dir
430 info.GoMod = info.Replace.GoMod
431 info.Retracted = info.Replace.Retracted
432 }
433 info.GoVersion = info.Replace.GoVersion
434 return info
435 }
436
437
438
439
440 func findModule(ld *loader, path string) (module.Version, bool) {
441 if pkg, ok := ld.pkgCache.Get(path); ok {
442 return pkg.mod, pkg.mod != module.Version{}
443 }
444 return module.Version{}, false
445 }
446
447 func ModInfoProg(info string, isgccgo bool) []byte {
448
449
450
451
452
453 if isgccgo {
454 return fmt.Appendf(nil, `package main
455 import _ "unsafe"
456 //go:linkname __set_debug_modinfo__ runtime.setmodinfo
457 func __set_debug_modinfo__(string)
458 func init() { __set_debug_modinfo__(%q) }
459 `, ModInfoData(info))
460 }
461 return nil
462 }
463
464 func ModInfoData(info string) []byte {
465 return []byte(string(infoStart) + info + string(infoEnd))
466 }
467
View as plain text