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