github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/modload/build.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 18 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 19 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch" 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch/codehost" 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modindex" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modinfo" 23 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/search" 24 25 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 26 "github.com/bir3/gocompiler/src/xvendor/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 modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { 44 return filepath.Join(cfg.GOROOT, "src", path) 45 } 46 } 47 return "" 48 } 49 50 // PackageModuleInfo returns information about the module that provides 51 // a given package. If modules are not enabled or if the package is in the 52 // standard library or if the package was not successfully loaded with 53 // LoadPackages or ImportFromFiles, nil is returned. 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 // PackageModRoot returns the module root directory for the module that provides 68 // a given package. If modules are not enabled or if the package is in the 69 // standard library or if the package was not successfully loaded with 70 // LoadPackages or ImportFromFiles, the empty string is returned. 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.Fatalf("go: %v", 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 // addUpdate fills in m.Update if an updated version is available. 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 // Ignore "not found" and "no matching version" errors. 137 // This means the proxy has no matching version or no versions at all. 138 // 139 // Ignore "disallowed" errors. This means the current version is 140 // excluded or retracted and there are no higher allowed versions. 141 // 142 // We should report other errors though. An attacker that controls the 143 // network shouldn't be able to hide versions by interfering with 144 // the HTTPS connection. An attacker that controls the proxy may still 145 // hide versions, since the "list" and "latest" endpoints are not 146 // authenticated. 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 semver.Compare(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 // mergeOrigin merges two origins, 165 // returning and possibly modifying one of its arguments. 166 // If the two origins conflict, mergeOrigin returns a non-specific one 167 // that will not pass CheckReuse. 168 // If m1 or m2 is nil, the other is returned unmodified. 169 // But if m1 or m2 is non-nil and uncheckable, the result is also uncheckable, 170 // to preserve uncheckability. 171 func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin { 172 if m1 == nil { 173 return m2 174 } 175 if m2 == nil { 176 return m1 177 } 178 if !m1.Checkable() { 179 return m1 180 } 181 if !m2.Checkable() { 182 return m2 183 } 184 185 merged := new(codehost.Origin) 186 *merged = *m1 // Clone to avoid overwriting fields in cached results. 187 188 if m2.TagSum != "" { 189 if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) { 190 merged.ClearCheckable() 191 return merged 192 } 193 merged.TagSum = m2.TagSum 194 merged.TagPrefix = m2.TagPrefix 195 } 196 if m2.Hash != "" { 197 if m1.Hash != "" && (m1.Hash != m2.Hash || m1.Ref != m2.Ref) { 198 merged.ClearCheckable() 199 return merged 200 } 201 merged.Hash = m2.Hash 202 merged.Ref = m2.Ref 203 } 204 return merged 205 } 206 207 // addVersions fills in m.Versions with the list of known versions. 208 // Excluded versions will be omitted. If listRetracted is false, retracted 209 // versions will also be omitted. 210 func addVersions(ctx context.Context, m *modinfo.ModulePublic, listRetracted bool) { 211 allowed := CheckAllowed 212 if listRetracted { 213 allowed = CheckExclusions 214 } 215 v, origin, err := versions(ctx, m.Path, allowed) 216 if err != nil && m.Error == nil { 217 m.Error = &modinfo.ModuleError{Err: err.Error()} 218 } 219 m.Versions = v 220 m.Origin = mergeOrigin(m.Origin, origin) 221 } 222 223 // addRetraction fills in m.Retracted if the module was retracted by its author. 224 // m.Error is set if there's an error loading retraction information. 225 func addRetraction(ctx context.Context, m *modinfo.ModulePublic) { 226 if m.Version == "" { 227 return 228 } 229 230 err := CheckRetractions(ctx, module.Version{Path: m.Path, Version: m.Version}) 231 var noVersionErr *NoMatchingVersionError 232 var retractErr *ModuleRetractedError 233 if err == nil || errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) { 234 // Ignore "not found" and "no matching version" errors. 235 // This means the proxy has no matching version or no versions at all. 236 // 237 // We should report other errors though. An attacker that controls the 238 // network shouldn't be able to hide versions by interfering with 239 // the HTTPS connection. An attacker that controls the proxy may still 240 // hide versions, since the "list" and "latest" endpoints are not 241 // authenticated. 242 return 243 } else if errors.As(err, &retractErr) { 244 if len(retractErr.Rationale) == 0 { 245 m.Retracted = []string{"retracted by module author"} 246 } else { 247 m.Retracted = retractErr.Rationale 248 } 249 } else if m.Error == nil { 250 m.Error = &modinfo.ModuleError{Err: err.Error()} 251 } 252 } 253 254 // addDeprecation fills in m.Deprecated if the module was deprecated by its 255 // author. m.Error is set if there's an error loading deprecation information. 256 func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) { 257 deprecation, err := CheckDeprecation(ctx, module.Version{Path: m.Path, Version: m.Version}) 258 var noVersionErr *NoMatchingVersionError 259 if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) { 260 // Ignore "not found" and "no matching version" errors. 261 // This means the proxy has no matching version or no versions at all. 262 // 263 // We should report other errors though. An attacker that controls the 264 // network shouldn't be able to hide versions by interfering with 265 // the HTTPS connection. An attacker that controls the proxy may still 266 // hide versions, since the "list" and "latest" endpoints are not 267 // authenticated. 268 return 269 } 270 if err != nil { 271 if m.Error == nil { 272 m.Error = &modinfo.ModuleError{Err: err.Error()} 273 } 274 return 275 } 276 m.Deprecated = deprecation 277 } 278 279 // moduleInfo returns information about module m, loaded from the requirements 280 // in rs (which may be nil to indicate that m was not loaded from a requirement 281 // graph). 282 func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) *modinfo.ModulePublic { 283 if m.Version == "" && MainModules.Contains(m.Path) { 284 info := &modinfo.ModulePublic{ 285 Path: m.Path, 286 Version: m.Version, 287 Main: true, 288 } 289 if v, ok := rawGoVersion.Load(m); ok { 290 info.GoVersion = v.(string) 291 } else { 292 panic("internal error: GoVersion not set for main module") 293 } 294 if modRoot := MainModules.ModRoot(m); modRoot != "" { 295 info.Dir = modRoot 296 info.GoMod = modFilePath(modRoot) 297 } 298 return info 299 } 300 301 info := &modinfo.ModulePublic{ 302 Path: m.Path, 303 Version: m.Version, 304 Indirect: rs != nil && !rs.direct[m.Path], 305 } 306 if v, ok := rawGoVersion.Load(m); ok { 307 info.GoVersion = v.(string) 308 } 309 310 // completeFromModCache fills in the extra fields in m using the module cache. 311 completeFromModCache := func(m *modinfo.ModulePublic) { 312 if old := reuse[module.Version{Path: m.Path, Version: m.Version}]; old != nil { 313 if err := checkReuse(ctx, m.Path, old.Origin); err == nil { 314 *m = *old 315 m.Query = "" 316 m.Dir = "" 317 return 318 } 319 } 320 321 checksumOk := func(suffix string) bool { 322 return rs == nil || m.Version == "" || !mustHaveSums() || 323 modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix}) 324 } 325 326 if m.Version != "" { 327 if q, err := Query(ctx, m.Path, m.Version, "", nil); err != nil { 328 m.Error = &modinfo.ModuleError{Err: err.Error()} 329 } else { 330 m.Version = q.Version 331 m.Time = &q.Time 332 } 333 } 334 mod := module.Version{Path: m.Path, Version: m.Version} 335 336 if m.GoVersion == "" && checksumOk("/go.mod") { 337 // Load the go.mod file to determine the Go version, since it hasn't 338 // already been populated from rawGoVersion. 339 if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" { 340 m.GoVersion = summary.goVersion 341 } 342 } 343 344 if m.Version != "" { 345 if checksumOk("/go.mod") { 346 gomod, err := modfetch.CachePath(mod, "mod") 347 if err == nil { 348 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() { 349 m.GoMod = gomod 350 } 351 } 352 } 353 if checksumOk("") { 354 dir, err := modfetch.DownloadDir(mod) 355 if err == nil { 356 m.Dir = dir 357 } 358 } 359 360 if mode&ListRetracted != 0 { 361 addRetraction(ctx, m) 362 } 363 } 364 } 365 366 if rs == nil { 367 // If this was an explicitly-versioned argument to 'go mod download' or 368 // 'go list -m', report the actual requested version, not its replacement. 369 completeFromModCache(info) // Will set m.Error in vendor mode. 370 return info 371 } 372 373 r := Replacement(m) 374 if r.Path == "" { 375 if cfg.BuildMod == "vendor" { 376 // It's tempting to fill in the "Dir" field to point within the vendor 377 // directory, but that would be misleading: the vendor directory contains 378 // a flattened package tree, not complete modules, and it can even 379 // interleave packages from different modules if one module path is a 380 // prefix of the other. 381 } else { 382 completeFromModCache(info) 383 } 384 return info 385 } 386 387 // Don't hit the network to fill in extra data for replaced modules. 388 // The original resolved Version and Time don't matter enough to be 389 // worth the cost, and we're going to overwrite the GoMod and Dir from the 390 // replacement anyway. See https://golang.org/issue/27859. 391 info.Replace = &modinfo.ModulePublic{ 392 Path: r.Path, 393 Version: r.Version, 394 } 395 if v, ok := rawGoVersion.Load(m); ok { 396 info.Replace.GoVersion = v.(string) 397 } 398 if r.Version == "" { 399 if filepath.IsAbs(r.Path) { 400 info.Replace.Dir = r.Path 401 } else { 402 info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path) 403 } 404 info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod") 405 } 406 if cfg.BuildMod != "vendor" { 407 completeFromModCache(info.Replace) 408 info.Dir = info.Replace.Dir 409 info.GoMod = info.Replace.GoMod 410 info.Retracted = info.Replace.Retracted 411 } 412 info.GoVersion = info.Replace.GoVersion 413 return info 414 } 415 416 // findModule searches for the module that contains the package at path. 417 // If the package was loaded, its containing module and true are returned. 418 // Otherwise, module.Version{} and false are returned. 419 func findModule(ld *loader, path string) (module.Version, bool) { 420 if pkg, ok := ld.pkgCache.Get(path).(*loadPkg); ok { 421 return pkg.mod, pkg.mod != module.Version{} 422 } 423 return module.Version{}, false 424 } 425 426 func ModInfoProg(info string, isgccgo bool) []byte { 427 // Inject an init function to set runtime.modinfo. 428 // This is only used for gccgo - with gc we hand the info directly to the linker. 429 // The init function has the drawback that packages may want to 430 // look at the module info in their init functions (see issue 29628), 431 // which won't work. See also issue 30344. 432 if isgccgo { 433 return fmt.Appendf(nil, `package main 434 import _ "unsafe" 435 //go:linkname __set_debug_modinfo__ runtime.setmodinfo 436 func __set_debug_modinfo__(string) 437 func init() { __set_debug_modinfo__(%q) } 438 `, ModInfoData(info)) 439 } 440 return nil 441 } 442 443 func ModInfoData(info string) []byte { 444 return []byte(string(infoStart) + info + string(infoEnd)) 445 }