1
2
3
4
5 package modload
6
7 import (
8 "context"
9 "encoding/hex"
10 "errors"
11 "fmt"
12 "internal/goroot"
13 "io/fs"
14 "os"
15 "path/filepath"
16 "strings"
17
18 "cmd/go/internal/base"
19 "cmd/go/internal/cfg"
20 "cmd/go/internal/modfetch"
21 "cmd/go/internal/modinfo"
22 "cmd/go/internal/search"
23
24 "golang.org/x/mod/module"
25 "golang.org/x/mod/semver"
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 goroot.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(ctx context.Context, pkgpath string) *modinfo.ModulePublic {
54 if isStandardImportPath(pkgpath) || !Enabled() {
55 return nil
56 }
57 m, ok := findModule(loaded, pkgpath)
58 if !ok {
59 return nil
60 }
61
62 rs := LoadModFile(ctx)
63 return moduleInfo(ctx, rs, m, 0)
64 }
65
66 func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
67 if !Enabled() {
68 return nil
69 }
70
71 if i := strings.Index(path, "@"); i >= 0 {
72 m := module.Version{Path: path[:i], Version: path[i+1:]}
73 return moduleInfo(ctx, nil, m, 0)
74 }
75
76 rs := LoadModFile(ctx)
77
78 var (
79 v string
80 ok bool
81 )
82 if rs.pruning == pruned {
83 v, ok = rs.rootSelected(path)
84 }
85 if !ok {
86 mg, err := rs.Graph(ctx)
87 if err != nil {
88 base.Fatalf("go: %v", err)
89 }
90 v = mg.Selected(path)
91 }
92
93 if v == "none" {
94 return &modinfo.ModulePublic{
95 Path: path,
96 Error: &modinfo.ModuleError{
97 Err: "module not in current build",
98 },
99 }
100 }
101
102 return moduleInfo(ctx, rs, module.Version{Path: path, Version: v}, 0)
103 }
104
105
106 func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
107 if m.Version == "" {
108 return
109 }
110
111 info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed)
112 var noVersionErr *NoMatchingVersionError
113 if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
114
115
116
117
118
119
120
121
122 return
123 } else if err != nil {
124 if m.Error == nil {
125 m.Error = &modinfo.ModuleError{Err: err.Error()}
126 }
127 return
128 }
129
130 if semver.Compare(info.Version, m.Version) > 0 {
131 m.Update = &modinfo.ModulePublic{
132 Path: m.Path,
133 Version: info.Version,
134 Time: &info.Time,
135 }
136 }
137 }
138
139
140
141
142 func addVersions(ctx context.Context, m *modinfo.ModulePublic, listRetracted bool) {
143 allowed := CheckAllowed
144 if listRetracted {
145 allowed = CheckExclusions
146 }
147 var err error
148 m.Versions, err = versions(ctx, m.Path, allowed)
149 if err != nil && m.Error == nil {
150 m.Error = &modinfo.ModuleError{Err: err.Error()}
151 }
152 }
153
154
155
156 func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
157 if m.Version == "" {
158 return
159 }
160
161 err := CheckRetractions(ctx, module.Version{Path: m.Path, Version: m.Version})
162 var noVersionErr *NoMatchingVersionError
163 var retractErr *ModuleRetractedError
164 if err == nil || errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
165
166
167
168
169
170
171
172
173 return
174 } else if errors.As(err, &retractErr) {
175 if len(retractErr.Rationale) == 0 {
176 m.Retracted = []string{"retracted by module author"}
177 } else {
178 m.Retracted = retractErr.Rationale
179 }
180 } else if m.Error == nil {
181 m.Error = &modinfo.ModuleError{Err: err.Error()}
182 }
183 }
184
185
186
187 func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) {
188 deprecation, err := CheckDeprecation(ctx, module.Version{Path: m.Path, Version: m.Version})
189 var noVersionErr *NoMatchingVersionError
190 if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
191
192
193
194
195
196
197
198
199 return
200 }
201 if err != nil {
202 if m.Error == nil {
203 m.Error = &modinfo.ModuleError{Err: err.Error()}
204 }
205 return
206 }
207 m.Deprecated = deprecation
208 }
209
210
211
212
213 func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode) *modinfo.ModulePublic {
214 if m.Version == "" && MainModules.Contains(m.Path) {
215 info := &modinfo.ModulePublic{
216 Path: m.Path,
217 Version: m.Version,
218 Main: true,
219 }
220 if v, ok := rawGoVersion.Load(m); ok {
221 info.GoVersion = v.(string)
222 } else {
223 panic("internal error: GoVersion not set for main module")
224 }
225 if modRoot := MainModules.ModRoot(m); modRoot != "" {
226 info.Dir = modRoot
227 info.GoMod = modFilePath(modRoot)
228 }
229 return info
230 }
231
232 info := &modinfo.ModulePublic{
233 Path: m.Path,
234 Version: m.Version,
235 Indirect: rs != nil && !rs.direct[m.Path],
236 }
237 if v, ok := rawGoVersion.Load(m); ok {
238 info.GoVersion = v.(string)
239 }
240
241
242 completeFromModCache := func(m *modinfo.ModulePublic) {
243 checksumOk := func(suffix string) bool {
244 return rs == nil || m.Version == "" || cfg.BuildMod == "mod" ||
245 modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix})
246 }
247
248 if m.Version != "" {
249 if q, err := Query(ctx, m.Path, m.Version, "", nil); err != nil {
250 m.Error = &modinfo.ModuleError{Err: err.Error()}
251 } else {
252 m.Version = q.Version
253 m.Time = &q.Time
254 }
255 }
256 mod := module.Version{Path: m.Path, Version: m.Version}
257
258 if m.GoVersion == "" && checksumOk("/go.mod") {
259
260
261 if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" {
262 m.GoVersion = summary.goVersion
263 }
264 }
265
266 if m.Version != "" {
267 if checksumOk("/go.mod") {
268 gomod, err := modfetch.CachePath(mod, "mod")
269 if err == nil {
270 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() {
271 m.GoMod = gomod
272 }
273 }
274 }
275 if checksumOk("") {
276 dir, err := modfetch.DownloadDir(mod)
277 if err == nil {
278 m.Dir = dir
279 }
280 }
281
282 if mode&ListRetracted != 0 {
283 addRetraction(ctx, m)
284 }
285 }
286 }
287
288 if rs == nil {
289
290
291 completeFromModCache(info)
292 return info
293 }
294
295 r := Replacement(m)
296 if r.Path == "" {
297 if cfg.BuildMod == "vendor" {
298
299
300
301
302
303 } else {
304 completeFromModCache(info)
305 }
306 return info
307 }
308
309
310
311
312
313 info.Replace = &modinfo.ModulePublic{
314 Path: r.Path,
315 Version: r.Version,
316 }
317 if v, ok := rawGoVersion.Load(m); ok {
318 info.Replace.GoVersion = v.(string)
319 }
320 if r.Version == "" {
321 if filepath.IsAbs(r.Path) {
322 info.Replace.Dir = r.Path
323 } else {
324 info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path)
325 }
326 info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
327 }
328 if cfg.BuildMod != "vendor" {
329 completeFromModCache(info.Replace)
330 info.Dir = info.Replace.Dir
331 info.GoMod = info.Replace.GoMod
332 info.Retracted = info.Replace.Retracted
333 }
334 info.GoVersion = info.Replace.GoVersion
335 return info
336 }
337
338
339
340
341 func findModule(ld *loader, path string) (module.Version, bool) {
342 if pkg, ok := ld.pkgCache.Get(path).(*loadPkg); ok {
343 return pkg.mod, pkg.mod != module.Version{}
344 }
345 return module.Version{}, false
346 }
347
348 func ModInfoProg(info string, isgccgo bool) []byte {
349
350
351
352
353
354 if isgccgo {
355 return []byte(fmt.Sprintf(`package main
356 import _ "unsafe"
357 //go:linkname __set_debug_modinfo__ runtime.setmodinfo
358 func __set_debug_modinfo__(string)
359 func init() { __set_debug_modinfo__(%q) }
360 `, ModInfoData(info)))
361 }
362 return nil
363 }
364
365 func ModInfoData(info string) []byte {
366 return []byte(string(infoStart) + info + string(infoEnd))
367 }
368
View as plain text