github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/modload/modfile.go (about) 1 // Copyright 2020 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 "errors" 10 "fmt" 11 "os" 12 "path/filepath" 13 "strings" 14 "sync" 15 "unicode" 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/fsys" 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/lockedfile" 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/par" 23 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/trace" 24 25 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/modfile" 26 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 27 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/semver" 28 ) 29 30 const ( 31 // narrowAllVersionV is the Go version (plus leading "v") at which the 32 // module-module "all" pattern no longer closes over the dependencies of 33 // tests outside of the main module. 34 narrowAllVersionV = "v1.16" 35 36 // ExplicitIndirectVersionV is the Go version (plus leading "v") at which a 37 // module's go.mod file is expected to list explicit requirements on every 38 // module that provides any package transitively imported by that module. 39 // 40 // Other indirect dependencies of such a module can be safely pruned out of 41 // the module graph; see https://golang.org/ref/mod#graph-pruning. 42 ExplicitIndirectVersionV = "v1.17" 43 44 // separateIndirectVersionV is the Go version (plus leading "v") at which 45 // "// indirect" dependencies are added in a block separate from the direct 46 // ones. See https://golang.org/issue/45965. 47 separateIndirectVersionV = "v1.17" 48 49 // tidyGoModSumVersionV is the Go version (plus leading "v") at which 50 // 'go mod tidy' preserves go.mod checksums needed to build test dependencies 51 // of packages in "all", so that 'go test all' can be run without checksum 52 // errors. 53 // See https://go.dev/issue/56222. 54 tidyGoModSumVersionV = "v1.21" 55 ) 56 57 // ReadModFile reads and parses the mod file at gomod. ReadModFile properly applies the 58 // overlay, locks the file while reading, and applies fix, if applicable. 59 func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) { 60 if gomodActual, ok := fsys.OverlayPath(gomod); ok { 61 // Don't lock go.mod if it's part of the overlay. 62 // On Plan 9, locking requires chmod, and we don't want to modify any file 63 // in the overlay. See #44700. 64 data, err = os.ReadFile(gomodActual) 65 } else { 66 data, err = lockedfile.Read(gomodActual) 67 } 68 if err != nil { 69 return nil, nil, err 70 } 71 72 f, err = modfile.Parse(gomod, data, fix) 73 if err != nil { 74 // Errors returned by modfile.Parse begin with file:line. 75 return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err) 76 } 77 if f.Module == nil { 78 // No module declaration. Must add module path. 79 return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod") 80 } 81 82 return data, f, err 83 } 84 85 // modFileGoVersion returns the (non-empty) Go version at which the requirements 86 // in modFile are interpreted, or the latest Go version if modFile is nil. 87 func modFileGoVersion(modFile *modfile.File) string { 88 if modFile == nil { 89 return LatestGoVersion() 90 } 91 if modFile.Go == nil || modFile.Go.Version == "" { 92 // The main module necessarily has a go.mod file, and that file lacks a 93 // 'go' directive. The 'go' command has been adding that directive 94 // automatically since Go 1.12, so this module either dates to Go 1.11 or 95 // has been erroneously hand-edited. 96 // 97 // The semantics of the go.mod file are more-or-less the same from Go 1.11 98 // through Go 1.16, changing at 1.17 to support module graph pruning. 99 // So even though a go.mod file without a 'go' directive is theoretically a 100 // Go 1.11 file, scripts may assume that it ends up as a Go 1.16 module. 101 return "1.16" 102 } 103 return modFile.Go.Version 104 } 105 106 // A modFileIndex is an index of data corresponding to a modFile 107 // at a specific point in time. 108 type modFileIndex struct { 109 data []byte 110 dataNeedsFix bool // true if fixVersion applied a change while parsing data 111 module module.Version 112 goVersionV string // GoVersion with "v" prefix 113 require map[module.Version]requireMeta 114 replace map[module.Version]module.Version 115 exclude map[module.Version]bool 116 } 117 118 type requireMeta struct { 119 indirect bool 120 } 121 122 // A modPruning indicates whether transitive dependencies of Go 1.17 dependencies 123 // are pruned out of the module subgraph rooted at a given module. 124 // (See https://golang.org/ref/mod#graph-pruning.) 125 type modPruning uint8 126 127 const ( 128 pruned modPruning = iota // transitive dependencies of modules at go 1.17 and higher are pruned out 129 unpruned // no transitive dependencies are pruned out 130 workspace // pruned to the union of modules in the workspace 131 ) 132 133 func pruningForGoVersion(goVersion string) modPruning { 134 if semver.Compare("v"+goVersion, ExplicitIndirectVersionV) < 0 { 135 // The go.mod file does not duplicate relevant information about transitive 136 // dependencies, so they cannot be pruned out. 137 return unpruned 138 } 139 return pruned 140 } 141 142 // CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by 143 // the main module's go.mod or retracted by its author. Most version queries use 144 // this to filter out versions that should not be used. 145 func CheckAllowed(ctx context.Context, m module.Version) error { 146 if err := CheckExclusions(ctx, m); err != nil { 147 return err 148 } 149 if err := CheckRetractions(ctx, m); err != nil { 150 return err 151 } 152 return nil 153 } 154 155 // ErrDisallowed is returned by version predicates passed to Query and similar 156 // functions to indicate that a version should not be considered. 157 var ErrDisallowed = errors.New("disallowed module version") 158 159 // CheckExclusions returns an error equivalent to ErrDisallowed if module m is 160 // excluded by the main module's go.mod file. 161 func CheckExclusions(ctx context.Context, m module.Version) error { 162 for _, mainModule := range MainModules.Versions() { 163 if index := MainModules.Index(mainModule); index != nil && index.exclude[m] { 164 return module.VersionError(m, errExcluded) 165 } 166 } 167 return nil 168 } 169 170 var errExcluded = &excludedError{} 171 172 type excludedError struct{} 173 174 func (e *excludedError) Error() string { return "excluded by go.mod" } 175 func (e *excludedError) Is(err error) bool { return err == ErrDisallowed } 176 177 // CheckRetractions returns an error if module m has been retracted by 178 // its author. 179 func CheckRetractions(ctx context.Context, m module.Version) (err error) { 180 defer func() { 181 if retractErr := (*ModuleRetractedError)(nil); err == nil || errors.As(err, &retractErr) { 182 return 183 } 184 // Attribute the error to the version being checked, not the version from 185 // which the retractions were to be loaded. 186 if mErr := (*module.ModuleError)(nil); errors.As(err, &mErr) { 187 err = mErr.Err 188 } 189 err = &retractionLoadingError{m: m, err: err} 190 }() 191 192 if m.Version == "" { 193 // Main module, standard library, or file replacement module. 194 // Cannot be retracted. 195 return nil 196 } 197 if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" { 198 // All versions of the module were replaced. 199 // Don't load retractions, since we'd just load the replacement. 200 return nil 201 } 202 203 // Find the latest available version of the module, and load its go.mod. If 204 // the latest version is replaced, we'll load the replacement. 205 // 206 // If there's an error loading the go.mod, we'll return it here. These errors 207 // should generally be ignored by callers since they happen frequently when 208 // we're offline. These errors are not equivalent to ErrDisallowed, so they 209 // may be distinguished from retraction errors. 210 // 211 // We load the raw file here: the go.mod file may have a different module 212 // path that we expect if the module or its repository was renamed. 213 // We still want to apply retractions to other aliases of the module. 214 rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path) 215 if err != nil { 216 return err 217 } 218 summary, err := rawGoModSummary(rm) 219 if err != nil { 220 return err 221 } 222 223 var rationale []string 224 isRetracted := false 225 for _, r := range summary.retract { 226 if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 { 227 isRetracted = true 228 if r.Rationale != "" { 229 rationale = append(rationale, r.Rationale) 230 } 231 } 232 } 233 if isRetracted { 234 return module.VersionError(m, &ModuleRetractedError{Rationale: rationale}) 235 } 236 return nil 237 } 238 239 type ModuleRetractedError struct { 240 Rationale []string 241 } 242 243 func (e *ModuleRetractedError) Error() string { 244 msg := "retracted by module author" 245 if len(e.Rationale) > 0 { 246 // This is meant to be a short error printed on a terminal, so just 247 // print the first rationale. 248 msg += ": " + ShortMessage(e.Rationale[0], "retracted by module author") 249 } 250 return msg 251 } 252 253 func (e *ModuleRetractedError) Is(err error) bool { 254 return err == ErrDisallowed 255 } 256 257 type retractionLoadingError struct { 258 m module.Version 259 err error 260 } 261 262 func (e *retractionLoadingError) Error() string { 263 return fmt.Sprintf("loading module retractions for %v: %v", e.m, e.err) 264 } 265 266 func (e *retractionLoadingError) Unwrap() error { 267 return e.err 268 } 269 270 // ShortMessage returns a string from go.mod (for example, a retraction 271 // rationale or deprecation message) that is safe to print in a terminal. 272 // 273 // If the given string is empty, ShortMessage returns the given default. If the 274 // given string is too long or contains non-printable characters, ShortMessage 275 // returns a hard-coded string. 276 func ShortMessage(message, emptyDefault string) string { 277 const maxLen = 500 278 if i := strings.Index(message, "\n"); i >= 0 { 279 message = message[:i] 280 } 281 message = strings.TrimSpace(message) 282 if message == "" { 283 return emptyDefault 284 } 285 if len(message) > maxLen { 286 return "(message omitted: too long)" 287 } 288 for _, r := range message { 289 if !unicode.IsGraphic(r) && !unicode.IsSpace(r) { 290 return "(message omitted: contains non-printable characters)" 291 } 292 } 293 // NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here. 294 return message 295 } 296 297 // CheckDeprecation returns a deprecation message from the go.mod file of the 298 // latest version of the given module. Deprecation messages are comments 299 // before or on the same line as the module directives that start with 300 // "Deprecated:" and run until the end of the paragraph. 301 // 302 // CheckDeprecation returns an error if the message can't be loaded. 303 // CheckDeprecation returns "", nil if there is no deprecation message. 304 func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string, err error) { 305 defer func() { 306 if err != nil { 307 err = fmt.Errorf("loading deprecation for %s: %w", m.Path, err) 308 } 309 }() 310 311 if m.Version == "" { 312 // Main module, standard library, or file replacement module. 313 // Don't look up deprecation. 314 return "", nil 315 } 316 if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" { 317 // All versions of the module were replaced. 318 // We'll look up deprecation separately for the replacement. 319 return "", nil 320 } 321 322 latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path) 323 if err != nil { 324 return "", err 325 } 326 summary, err := rawGoModSummary(latest) 327 if err != nil { 328 return "", err 329 } 330 return summary.deprecated, nil 331 } 332 333 func replacement(mod module.Version, replace map[module.Version]module.Version) (fromVersion string, to module.Version, ok bool) { 334 if r, ok := replace[mod]; ok { 335 return mod.Version, r, true 336 } 337 if r, ok := replace[module.Version{Path: mod.Path}]; ok { 338 return "", r, true 339 } 340 return "", module.Version{}, false 341 } 342 343 // Replacement returns the replacement for mod, if any. If the path in the 344 // module.Version is relative it's relative to the single main module outside 345 // workspace mode, or the workspace's directory in workspace mode. 346 func Replacement(mod module.Version) module.Version { 347 foundFrom, found, foundModRoot := "", module.Version{}, "" 348 if MainModules == nil { 349 return module.Version{} 350 } else if MainModules.Contains(mod.Path) && mod.Version == "" { 351 // Don't replace the workspace version of the main module. 352 return module.Version{} 353 } 354 if _, r, ok := replacement(mod, MainModules.WorkFileReplaceMap()); ok { 355 return r 356 } 357 for _, v := range MainModules.Versions() { 358 if index := MainModules.Index(v); index != nil { 359 if from, r, ok := replacement(mod, index.replace); ok { 360 modRoot := MainModules.ModRoot(v) 361 if foundModRoot != "" && foundFrom != from && found != r { 362 base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v", 363 mod, modFilePath(foundModRoot), modFilePath(modRoot)) 364 return canonicalizeReplacePath(found, foundModRoot) 365 } 366 found, foundModRoot = r, modRoot 367 } 368 } 369 } 370 return canonicalizeReplacePath(found, foundModRoot) 371 } 372 373 func replaceRelativeTo() string { 374 if workFilePath := WorkFilePath(); workFilePath != "" { 375 return filepath.Dir(workFilePath) 376 } 377 return MainModules.ModRoot(MainModules.mustGetSingleMainModule()) 378 } 379 380 // canonicalizeReplacePath ensures that relative, on-disk, replaced module paths 381 // are relative to the workspace directory (in workspace mode) or to the module's 382 // directory (in module mode, as they already are). 383 func canonicalizeReplacePath(r module.Version, modRoot string) module.Version { 384 if filepath.IsAbs(r.Path) || r.Version != "" { 385 return r 386 } 387 workFilePath := WorkFilePath() 388 if workFilePath == "" { 389 return r 390 } 391 abs := filepath.Join(modRoot, r.Path) 392 if rel, err := filepath.Rel(filepath.Dir(workFilePath), abs); err == nil { 393 return module.Version{Path: rel, Version: r.Version} 394 } 395 // We couldn't make the version's path relative to the workspace's path, 396 // so just return the absolute path. It's the best we can do. 397 return module.Version{Path: abs, Version: r.Version} 398 } 399 400 // resolveReplacement returns the module actually used to load the source code 401 // for m: either m itself, or the replacement for m (iff m is replaced). 402 // It also returns the modroot of the module providing the replacement if 403 // one was found. 404 func resolveReplacement(m module.Version) module.Version { 405 if r := Replacement(m); r.Path != "" { 406 return r 407 } 408 return m 409 } 410 411 func toReplaceMap(replacements []*modfile.Replace) map[module.Version]module.Version { 412 replaceMap := make(map[module.Version]module.Version, len(replacements)) 413 for _, r := range replacements { 414 if prev, dup := replaceMap[r.Old]; dup && prev != r.New { 415 base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New) 416 } 417 replaceMap[r.Old] = r.New 418 } 419 return replaceMap 420 } 421 422 // indexModFile rebuilds the index of modFile. 423 // If modFile has been changed since it was first read, 424 // modFile.Cleanup must be called before indexModFile. 425 func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex { 426 i := new(modFileIndex) 427 i.data = data 428 i.dataNeedsFix = needsFix 429 430 i.module = module.Version{} 431 if modFile.Module != nil { 432 i.module = modFile.Module.Mod 433 } 434 435 i.goVersionV = "" 436 if modFile.Go == nil { 437 rawGoVersion.Store(mod, "") 438 } else { 439 // We're going to use the semver package to compare Go versions, so go ahead 440 // and add the "v" prefix it expects once instead of every time. 441 i.goVersionV = "v" + modFile.Go.Version 442 rawGoVersion.Store(mod, modFile.Go.Version) 443 } 444 445 i.require = make(map[module.Version]requireMeta, len(modFile.Require)) 446 for _, r := range modFile.Require { 447 i.require[r.Mod] = requireMeta{indirect: r.Indirect} 448 } 449 450 i.replace = toReplaceMap(modFile.Replace) 451 452 i.exclude = make(map[module.Version]bool, len(modFile.Exclude)) 453 for _, x := range modFile.Exclude { 454 i.exclude[x.Mod] = true 455 } 456 457 return i 458 } 459 460 // modFileIsDirty reports whether the go.mod file differs meaningfully 461 // from what was indexed. 462 // If modFile has been changed (even cosmetically) since it was first read, 463 // modFile.Cleanup must be called before modFileIsDirty. 464 func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool { 465 if i == nil { 466 return modFile != nil 467 } 468 469 if i.dataNeedsFix { 470 return true 471 } 472 473 if modFile.Module == nil { 474 if i.module != (module.Version{}) { 475 return true 476 } 477 } else if modFile.Module.Mod != i.module { 478 return true 479 } 480 481 if modFile.Go == nil { 482 if i.goVersionV != "" { 483 return true 484 } 485 } else if "v"+modFile.Go.Version != i.goVersionV { 486 if i.goVersionV == "" && cfg.BuildMod != "mod" { 487 // go.mod files did not always require a 'go' version, so do not error out 488 // if one is missing — we may be inside an older module in the module 489 // cache, and should bias toward providing useful behavior. 490 } else { 491 return true 492 } 493 } 494 495 if len(modFile.Require) != len(i.require) || 496 len(modFile.Replace) != len(i.replace) || 497 len(modFile.Exclude) != len(i.exclude) { 498 return true 499 } 500 501 for _, r := range modFile.Require { 502 if meta, ok := i.require[r.Mod]; !ok { 503 return true 504 } else if r.Indirect != meta.indirect { 505 if cfg.BuildMod == "readonly" { 506 // The module's requirements are consistent; only the "// indirect" 507 // comments that are wrong. But those are only guaranteed to be accurate 508 // after a "go mod tidy" — it's a good idea to run those before 509 // committing a change, but it's certainly not mandatory. 510 } else { 511 return true 512 } 513 } 514 } 515 516 for _, r := range modFile.Replace { 517 if r.New != i.replace[r.Old] { 518 return true 519 } 520 } 521 522 for _, x := range modFile.Exclude { 523 if !i.exclude[x.Mod] { 524 return true 525 } 526 } 527 528 return false 529 } 530 531 // rawGoVersion records the Go version parsed from each module's go.mod file. 532 // 533 // If a module is replaced, the version of the replacement is keyed by the 534 // replacement module.Version, not the version being replaced. 535 var rawGoVersion sync.Map // map[module.Version]string 536 537 // A modFileSummary is a summary of a go.mod file for which we do not need to 538 // retain complete information — for example, the go.mod file of a dependency 539 // module. 540 type modFileSummary struct { 541 module module.Version 542 goVersion string 543 pruning modPruning 544 require []module.Version 545 retract []retraction 546 deprecated string 547 } 548 549 // A retraction consists of a retracted version interval and rationale. 550 // retraction is like modfile.Retract, but it doesn't point to the syntax tree. 551 type retraction struct { 552 modfile.VersionInterval 553 Rationale string 554 } 555 556 // goModSummary returns a summary of the go.mod file for module m, 557 // taking into account any replacements for m, exclusions of its dependencies, 558 // and/or vendoring. 559 // 560 // m must be a version in the module graph, reachable from the Target module. 561 // In readonly mode, the go.sum file must contain an entry for m's go.mod file 562 // (or its replacement). goModSummary must not be called for the Target module 563 // itself, as its requirements may change. Use rawGoModSummary for other 564 // module versions. 565 // 566 // The caller must not modify the returned summary. 567 func goModSummary(m module.Version) (*modFileSummary, error) { 568 if m.Version == "" && !inWorkspaceMode() && MainModules.Contains(m.Path) { 569 panic("internal error: goModSummary called on a main module") 570 } 571 572 if cfg.BuildMod == "vendor" { 573 summary := &modFileSummary{ 574 module: module.Version{Path: m.Path}, 575 } 576 577 readVendorList(MainModules.mustGetSingleMainModule()) 578 if vendorVersion[m.Path] != m.Version { 579 // This module is not vendored, so packages cannot be loaded from it and 580 // it cannot be relevant to the build. 581 return summary, nil 582 } 583 584 // For every module other than the target, 585 // return the full list of modules from modules.txt. 586 // We don't know what versions the vendored module actually relies on, 587 // so assume that it requires everything. 588 summary.require = vendorList 589 return summary, nil 590 } 591 592 actual := resolveReplacement(m) 593 if mustHaveSums() && actual.Version != "" { 594 key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"} 595 if !modfetch.HaveSum(key) { 596 suggestion := fmt.Sprintf(" for go.mod file; to add it:\n\tgo mod download %s", m.Path) 597 return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion}) 598 } 599 } 600 summary, err := rawGoModSummary(actual) 601 if err != nil { 602 return nil, err 603 } 604 605 if actual.Version == "" { 606 // The actual module is a filesystem-local replacement, for which we have 607 // unfortunately not enforced any sort of invariants about module lines or 608 // matching module paths. Anything goes. 609 // 610 // TODO(bcmills): Remove this special-case, update tests, and add a 611 // release note. 612 } else { 613 if summary.module.Path == "" { 614 return nil, module.VersionError(actual, errors.New("parsing go.mod: missing module line")) 615 } 616 617 // In theory we should only allow mpath to be unequal to m.Path here if the 618 // version that we fetched lacks an explicit go.mod file: if the go.mod file 619 // is explicit, then it should match exactly (to ensure that imports of other 620 // packages within the module are interpreted correctly). Unfortunately, we 621 // can't determine that information from the module proxy protocol: we'll have 622 // to leave that validation for when we load actual packages from within the 623 // module. 624 if mpath := summary.module.Path; mpath != m.Path && mpath != actual.Path { 625 return nil, module.VersionError(actual, fmt.Errorf(`parsing go.mod: 626 module declares its path as: %s 627 but was required as: %s`, mpath, m.Path)) 628 } 629 } 630 631 for _, mainModule := range MainModules.Versions() { 632 if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 { 633 // Drop any requirements on excluded versions. 634 // Don't modify the cached summary though, since we might need the raw 635 // summary separately. 636 haveExcludedReqs := false 637 for _, r := range summary.require { 638 if index.exclude[r] { 639 haveExcludedReqs = true 640 break 641 } 642 } 643 if haveExcludedReqs { 644 s := new(modFileSummary) 645 *s = *summary 646 s.require = make([]module.Version, 0, len(summary.require)) 647 for _, r := range summary.require { 648 if !index.exclude[r] { 649 s.require = append(s.require, r) 650 } 651 } 652 summary = s 653 } 654 } 655 } 656 return summary, nil 657 } 658 659 // rawGoModSummary returns a new summary of the go.mod file for module m, 660 // ignoring all replacements that may apply to m and excludes that may apply to 661 // its dependencies. 662 // 663 // rawGoModSummary cannot be used on the Target module. 664 665 func rawGoModSummary(m module.Version) (*modFileSummary, error) { 666 if m.Path == "" && MainModules.Contains(m.Path) { 667 panic("internal error: rawGoModSummary called on the Target module") 668 } 669 670 type key struct { 671 m module.Version 672 } 673 type cached struct { 674 summary *modFileSummary 675 err error 676 } 677 c := rawGoModSummaryCache.Do(key{m}, func() any { 678 summary := new(modFileSummary) 679 name, data, err := rawGoModData(m) 680 if err != nil { 681 return cached{nil, err} 682 } 683 f, err := modfile.ParseLax(name, data, nil) 684 if err != nil { 685 return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(name), err))} 686 } 687 if f.Module != nil { 688 summary.module = f.Module.Mod 689 summary.deprecated = f.Module.Deprecated 690 } 691 if f.Go != nil && f.Go.Version != "" { 692 rawGoVersion.LoadOrStore(m, f.Go.Version) 693 summary.goVersion = f.Go.Version 694 summary.pruning = pruningForGoVersion(f.Go.Version) 695 } else { 696 summary.pruning = unpruned 697 } 698 if len(f.Require) > 0 { 699 summary.require = make([]module.Version, 0, len(f.Require)) 700 for _, req := range f.Require { 701 summary.require = append(summary.require, req.Mod) 702 } 703 } 704 if len(f.Retract) > 0 { 705 summary.retract = make([]retraction, 0, len(f.Retract)) 706 for _, ret := range f.Retract { 707 summary.retract = append(summary.retract, retraction{ 708 VersionInterval: ret.VersionInterval, 709 Rationale: ret.Rationale, 710 }) 711 } 712 } 713 714 return cached{summary, nil} 715 }).(cached) 716 717 return c.summary, c.err 718 } 719 720 var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result 721 722 // rawGoModData returns the content of the go.mod file for module m, ignoring 723 // all replacements that may apply to m. 724 // 725 // rawGoModData cannot be used on the Target module. 726 // 727 // Unlike rawGoModSummary, rawGoModData does not cache its results in memory. 728 // Use rawGoModSummary instead unless you specifically need these bytes. 729 func rawGoModData(m module.Version) (name string, data []byte, err error) { 730 if m.Version == "" { 731 // m is a replacement module with only a file path. 732 733 dir := m.Path 734 if !filepath.IsAbs(dir) { 735 if inWorkspaceMode() && MainModules.Contains(m.Path) { 736 dir = MainModules.ModRoot(m) 737 } else { 738 dir = filepath.Join(replaceRelativeTo(), dir) 739 } 740 } 741 name = filepath.Join(dir, "go.mod") 742 if gomodActual, ok := fsys.OverlayPath(name); ok { 743 // Don't lock go.mod if it's part of the overlay. 744 // On Plan 9, locking requires chmod, and we don't want to modify any file 745 // in the overlay. See #44700. 746 data, err = os.ReadFile(gomodActual) 747 } else { 748 data, err = lockedfile.Read(gomodActual) 749 } 750 if err != nil { 751 return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err)) 752 } 753 } else { 754 if !semver.IsValid(m.Version) { 755 // Disallow the broader queries supported by fetch.Lookup. 756 base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version) 757 } 758 name = "go.mod" 759 data, err = modfetch.GoMod(m.Path, m.Version) 760 } 761 return name, data, err 762 } 763 764 // queryLatestVersionIgnoringRetractions looks up the latest version of the 765 // module with the given path without considering retracted or excluded 766 // versions. 767 // 768 // If all versions of the module are replaced, 769 // queryLatestVersionIgnoringRetractions returns the replacement without making 770 // a query. 771 // 772 // If the queried latest version is replaced, 773 // queryLatestVersionIgnoringRetractions returns the replacement. 774 func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) { 775 type entry struct { 776 latest module.Version 777 err error 778 } 779 e := latestVersionIgnoringRetractionsCache.Do(path, func() any { 780 ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path) 781 defer span.Done() 782 783 if repl := Replacement(module.Version{Path: path}); repl.Path != "" { 784 // All versions of the module were replaced. 785 // No need to query. 786 return &entry{latest: repl} 787 } 788 789 // Find the latest version of the module. 790 // Ignore exclusions from the main module's go.mod. 791 const ignoreSelected = "" 792 var allowAll AllowedFunc 793 rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll) 794 if err != nil { 795 return &entry{err: err} 796 } 797 latest := module.Version{Path: path, Version: rev.Version} 798 if repl := resolveReplacement(latest); repl.Path != "" { 799 latest = repl 800 } 801 return &entry{latest: latest} 802 }).(*entry) 803 return e.latest, e.err 804 } 805 806 var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result 807 808 // ToDirectoryPath adds a prefix if necessary so that path in unambiguously 809 // an absolute path or a relative path starting with a '.' or '..' 810 // path component. 811 func ToDirectoryPath(path string) string { 812 if path == "." || modfile.IsDirectoryPath(path) { 813 return path 814 } 815 // The path is not a relative path or an absolute path, so make it relative 816 // to the current directory. 817 return "./" + filepath.ToSlash(filepath.Clean(path)) 818 }