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