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