1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "encoding/hex"
11 "errors"
12 "fmt"
13 "internal/goroot"
14 "io/fs"
15 "os"
16 "path/filepath"
17 "strings"
18
19 "cmd/go/internal/base"
20 "cmd/go/internal/cfg"
21 "cmd/go/internal/modfetch"
22 "cmd/go/internal/modinfo"
23 "cmd/go/internal/search"
24
25 "golang.org/x/mod/module"
26 "golang.org/x/mod/semver"
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 goroot.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)
65 }
66
67 func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
68 if !Enabled() {
69 return nil
70 }
71
72 if i := strings.Index(path, "@"); i >= 0 {
73 m := module.Version{Path: path[:i], Version: path[i+1:]}
74 return moduleInfo(ctx, nil, m, 0)
75 }
76
77 rs := LoadModFile(ctx)
78
79 var (
80 v string
81 ok bool
82 )
83 if rs.depth == lazy {
84 v, ok = rs.rootSelected(path)
85 }
86 if !ok {
87 mg, err := rs.Graph(ctx)
88 if err != nil {
89 base.Fatalf("go: %v", err)
90 }
91 v = mg.Selected(path)
92 }
93
94 if v == "none" {
95 return &modinfo.ModulePublic{
96 Path: path,
97 Error: &modinfo.ModuleError{
98 Err: "module not in current build",
99 },
100 }
101 }
102
103 return moduleInfo(ctx, rs, module.Version{Path: path, Version: v}, 0)
104 }
105
106
107 func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
108 if m.Version == "" {
109 return
110 }
111
112 info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed)
113 var noVersionErr *NoMatchingVersionError
114 if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
115
116
117
118
119
120
121
122
123 return
124 } else if err != nil {
125 if m.Error == nil {
126 m.Error = &modinfo.ModuleError{Err: err.Error()}
127 }
128 return
129 }
130
131 if semver.Compare(info.Version, m.Version) > 0 {
132 m.Update = &modinfo.ModulePublic{
133 Path: m.Path,
134 Version: info.Version,
135 Time: &info.Time,
136 }
137 }
138 }
139
140
141
142
143 func addVersions(ctx context.Context, m *modinfo.ModulePublic, listRetracted bool) {
144 allowed := CheckAllowed
145 if listRetracted {
146 allowed = CheckExclusions
147 }
148 var err error
149 m.Versions, err = versions(ctx, m.Path, allowed)
150 if err != nil && m.Error == nil {
151 m.Error = &modinfo.ModuleError{Err: err.Error()}
152 }
153 }
154
155
156
157 func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
158 if m.Version == "" {
159 return
160 }
161
162 err := CheckRetractions(ctx, module.Version{Path: m.Path, Version: m.Version})
163 var noVersionErr *NoMatchingVersionError
164 var retractErr *ModuleRetractedError
165 if err == nil || errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
166
167
168
169
170
171
172
173
174 return
175 } else if errors.As(err, &retractErr) {
176 if len(retractErr.Rationale) == 0 {
177 m.Retracted = []string{"retracted by module author"}
178 } else {
179 m.Retracted = retractErr.Rationale
180 }
181 } else if m.Error == nil {
182 m.Error = &modinfo.ModuleError{Err: err.Error()}
183 }
184 }
185
186
187
188 func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) {
189 deprecation, err := CheckDeprecation(ctx, module.Version{Path: m.Path, Version: m.Version})
190 var noVersionErr *NoMatchingVersionError
191 if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
192
193
194
195
196
197
198
199
200 return
201 }
202 if err != nil {
203 if m.Error == nil {
204 m.Error = &modinfo.ModuleError{Err: err.Error()}
205 }
206 return
207 }
208 m.Deprecated = deprecation
209 }
210
211
212
213
214 func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode) *modinfo.ModulePublic {
215 if m == Target {
216 info := &modinfo.ModulePublic{
217 Path: m.Path,
218 Version: m.Version,
219 Main: true,
220 }
221 if v, ok := rawGoVersion.Load(Target); ok {
222 info.GoVersion = v.(string)
223 } else {
224 panic("internal error: GoVersion not set for main module")
225 }
226 if HasModRoot() {
227 info.Dir = ModRoot()
228 info.GoMod = ModFilePath()
229 }
230 return info
231 }
232
233 info := &modinfo.ModulePublic{
234 Path: m.Path,
235 Version: m.Version,
236 Indirect: rs != nil && !rs.direct[m.Path],
237 }
238 if v, ok := rawGoVersion.Load(m); ok {
239 info.GoVersion = v.(string)
240 }
241
242
243 completeFromModCache := func(m *modinfo.ModulePublic) {
244 checksumOk := func(suffix string) bool {
245 return rs == nil || m.Version == "" || cfg.BuildMod == "mod" ||
246 modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix})
247 }
248
249 if m.Version != "" {
250 if q, err := Query(ctx, m.Path, m.Version, "", nil); err != nil {
251 m.Error = &modinfo.ModuleError{Err: err.Error()}
252 } else {
253 m.Version = q.Version
254 m.Time = &q.Time
255 }
256 }
257 mod := module.Version{Path: m.Path, Version: m.Version}
258
259 if m.GoVersion == "" && checksumOk("/go.mod") {
260
261
262 if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" {
263 m.GoVersion = summary.goVersion
264 }
265 }
266
267 if m.Version != "" {
268 if checksumOk("/go.mod") {
269 gomod, err := modfetch.CachePath(mod, "mod")
270 if err == nil {
271 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() {
272 m.GoMod = gomod
273 }
274 }
275 }
276 if checksumOk("") {
277 dir, err := modfetch.DownloadDir(mod)
278 if err == nil {
279 m.Dir = dir
280 }
281 }
282
283 if mode&ListRetracted != 0 {
284 addRetraction(ctx, m)
285 }
286 }
287 }
288
289 if rs == nil {
290
291
292 completeFromModCache(info)
293 return info
294 }
295
296 r := Replacement(m)
297 if r.Path == "" {
298 if cfg.BuildMod == "vendor" {
299
300
301
302
303
304 } else {
305 completeFromModCache(info)
306 }
307 return info
308 }
309
310
311
312
313
314 info.Replace = &modinfo.ModulePublic{
315 Path: r.Path,
316 Version: r.Version,
317 }
318 if v, ok := rawGoVersion.Load(m); ok {
319 info.Replace.GoVersion = v.(string)
320 }
321 if r.Version == "" {
322 if filepath.IsAbs(r.Path) {
323 info.Replace.Dir = r.Path
324 } else {
325 info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
326 }
327 info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
328 }
329 if cfg.BuildMod != "vendor" {
330 completeFromModCache(info.Replace)
331 info.Dir = info.Replace.Dir
332 info.GoMod = info.Replace.GoMod
333 info.Retracted = info.Replace.Retracted
334 }
335 info.GoVersion = info.Replace.GoVersion
336 return info
337 }
338
339
340
341
342 func PackageBuildInfo(path string, deps []string) string {
343 if isStandardImportPath(path) || !Enabled() {
344 return ""
345 }
346
347 target := mustFindModule(loaded, path, path)
348 mdeps := make(map[module.Version]bool)
349 for _, dep := range deps {
350 if !isStandardImportPath(dep) {
351 mdeps[mustFindModule(loaded, path, dep)] = true
352 }
353 }
354 var mods []module.Version
355 delete(mdeps, target)
356 for mod := range mdeps {
357 mods = append(mods, mod)
358 }
359 module.Sort(mods)
360
361 var buf bytes.Buffer
362 fmt.Fprintf(&buf, "path\t%s\n", path)
363
364 writeEntry := func(token string, m module.Version) {
365 mv := m.Version
366 if mv == "" {
367 mv = "(devel)"
368 }
369 fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv)
370 if r := Replacement(m); r.Path == "" {
371 fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m))
372 } else {
373 fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
374 }
375 }
376
377 writeEntry("mod", target)
378 for _, mod := range mods {
379 writeEntry("dep", mod)
380 }
381
382 return buf.String()
383 }
384
385
386
387
388
389
390 func mustFindModule(ld *loader, target, path string) module.Version {
391 pkg, ok := ld.pkgCache.Get(path).(*loadPkg)
392 if ok {
393 if pkg.err != nil {
394 base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err)
395 }
396 return pkg.mod
397 }
398
399 if path == "command-line-arguments" {
400 return Target
401 }
402
403 base.Fatalf("build %v: cannot find module for path %v", target, path)
404 panic("unreachable")
405 }
406
407
408
409
410 func findModule(ld *loader, path string) (module.Version, bool) {
411 if pkg, ok := ld.pkgCache.Get(path).(*loadPkg); ok {
412 return pkg.mod, pkg.mod != module.Version{}
413 }
414 if path == "command-line-arguments" {
415 return Target, true
416 }
417 return module.Version{}, false
418 }
419
420 func ModInfoProg(info string, isgccgo bool) []byte {
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436 if !isgccgo {
437 return []byte(fmt.Sprintf(`package main
438 import _ "unsafe"
439 //go:linkname __debug_modinfo__ runtime.modinfo
440 var __debug_modinfo__ = %q
441 `, string(infoStart)+info+string(infoEnd)))
442 } else {
443 return []byte(fmt.Sprintf(`package main
444 import _ "unsafe"
445 //go:linkname __set_debug_modinfo__ runtime.setmodinfo
446 func __set_debug_modinfo__(string)
447 func init() { __set_debug_modinfo__(%q) }
448 `, string(infoStart)+info+string(infoEnd)))
449 }
450 }
451
View as plain text