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 "path/filepath" 12 "strings" 13 "sync" 14 "unicode" 15 16 "cmd/go/internal/base" 17 "cmd/go/internal/cfg" 18 "cmd/go/internal/lockedfile" 19 "cmd/go/internal/modfetch" 20 "cmd/go/internal/par" 21 "cmd/go/internal/trace" 22 23 "golang.org/x/mod/modfile" 24 "golang.org/x/mod/module" 25 "golang.org/x/mod/semver" 26) 27 28// narrowAllVersionV is the Go version (plus leading "v") at which the 29// module-module "all" pattern no longer closes over the dependencies of 30// tests outside of the main module. 31const narrowAllVersionV = "v1.16" 32const go116EnableNarrowAll = true 33 34var modFile *modfile.File 35 36// A modFileIndex is an index of data corresponding to a modFile 37// at a specific point in time. 38type modFileIndex struct { 39 data []byte 40 dataNeedsFix bool // true if fixVersion applied a change while parsing data 41 module module.Version 42 goVersionV string // GoVersion with "v" prefix 43 require map[module.Version]requireMeta 44 replace map[module.Version]module.Version 45 highestReplaced map[string]string // highest replaced version of each module path; empty string for wildcard-only replacements 46 exclude map[module.Version]bool 47} 48 49// index is the index of the go.mod file as of when it was last read or written. 50var index *modFileIndex 51 52type requireMeta struct { 53 indirect bool 54} 55 56// CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by 57// the main module's go.mod or retracted by its author. Most version queries use 58// this to filter out versions that should not be used. 59func CheckAllowed(ctx context.Context, m module.Version) error { 60 if err := CheckExclusions(ctx, m); err != nil { 61 return err 62 } 63 if err := CheckRetractions(ctx, m); err != nil { 64 return err 65 } 66 return nil 67} 68 69// ErrDisallowed is returned by version predicates passed to Query and similar 70// functions to indicate that a version should not be considered. 71var ErrDisallowed = errors.New("disallowed module version") 72 73// CheckExclusions returns an error equivalent to ErrDisallowed if module m is 74// excluded by the main module's go.mod file. 75func CheckExclusions(ctx context.Context, m module.Version) error { 76 if index != nil && index.exclude[m] { 77 return module.VersionError(m, errExcluded) 78 } 79 return nil 80} 81 82var errExcluded = &excludedError{} 83 84type excludedError struct{} 85 86func (e *excludedError) Error() string { return "excluded by go.mod" } 87func (e *excludedError) Is(err error) bool { return err == ErrDisallowed } 88 89// CheckRetractions returns an error if module m has been retracted by 90// its author. 91func CheckRetractions(ctx context.Context, m module.Version) error { 92 if m.Version == "" { 93 // Main module, standard library, or file replacement module. 94 // Cannot be retracted. 95 return nil 96 } 97 98 // Look up retraction information from the latest available version of 99 // the module. Cache retraction information so we don't parse the go.mod 100 // file repeatedly. 101 type entry struct { 102 retract []retraction 103 err error 104 } 105 path := m.Path 106 e := retractCache.Do(path, func() (v interface{}) { 107 ctx, span := trace.StartSpan(ctx, "checkRetractions "+path) 108 defer span.Done() 109 110 if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" { 111 // All versions of the module were replaced with a local directory. 112 // Don't load retractions. 113 return &entry{nil, nil} 114 } 115 116 // Find the latest version of the module. 117 // Ignore exclusions from the main module's go.mod. 118 const ignoreSelected = "" 119 var allowAll AllowedFunc 120 rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll) 121 if err != nil { 122 return &entry{nil, err} 123 } 124 125 // Load go.mod for that version. 126 // If the version is replaced, we'll load retractions from the replacement. 127 // 128 // If there's an error loading the go.mod, we'll return it here. 129 // These errors should generally be ignored by callers of checkRetractions, 130 // since they happen frequently when we're offline. These errors are not 131 // equivalent to ErrDisallowed, so they may be distinguished from 132 // retraction errors. 133 // 134 // We load the raw file here: the go.mod file may have a different module 135 // path that we expect if the module or its repository was renamed. 136 // We still want to apply retractions to other aliases of the module. 137 rm := module.Version{Path: path, Version: rev.Version} 138 if repl := Replacement(rm); repl.Path != "" { 139 rm = repl 140 } 141 summary, err := rawGoModSummary(rm) 142 if err != nil { 143 return &entry{nil, err} 144 } 145 return &entry{summary.retract, nil} 146 }).(*entry) 147 148 if err := e.err; err != nil { 149 // Attribute the error to the version being checked, not the version from 150 // which the retractions were to be loaded. 151 var mErr *module.ModuleError 152 if errors.As(err, &mErr) { 153 err = mErr.Err 154 } 155 return &retractionLoadingError{m: m, err: err} 156 } 157 158 var rationale []string 159 isRetracted := false 160 for _, r := range e.retract { 161 if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 { 162 isRetracted = true 163 if r.Rationale != "" { 164 rationale = append(rationale, r.Rationale) 165 } 166 } 167 } 168 if isRetracted { 169 return module.VersionError(m, &ModuleRetractedError{Rationale: rationale}) 170 } 171 return nil 172} 173 174var retractCache par.Cache 175 176type ModuleRetractedError struct { 177 Rationale []string 178} 179 180func (e *ModuleRetractedError) Error() string { 181 msg := "retracted by module author" 182 if len(e.Rationale) > 0 { 183 // This is meant to be a short error printed on a terminal, so just 184 // print the first rationale. 185 msg += ": " + ShortRetractionRationale(e.Rationale[0]) 186 } 187 return msg 188} 189 190func (e *ModuleRetractedError) Is(err error) bool { 191 return err == ErrDisallowed 192} 193 194type retractionLoadingError struct { 195 m module.Version 196 err error 197} 198 199func (e *retractionLoadingError) Error() string { 200 return fmt.Sprintf("loading module retractions for %v: %v", e.m, e.err) 201} 202 203func (e *retractionLoadingError) Unwrap() error { 204 return e.err 205} 206 207// ShortRetractionRationale returns a retraction rationale string that is safe 208// to print in a terminal. It returns hard-coded strings if the rationale 209// is empty, too long, or contains non-printable characters. 210func ShortRetractionRationale(rationale string) string { 211 const maxRationaleBytes = 500 212 if i := strings.Index(rationale, "\n"); i >= 0 { 213 rationale = rationale[:i] 214 } 215 rationale = strings.TrimSpace(rationale) 216 if rationale == "" { 217 return "retracted by module author" 218 } 219 if len(rationale) > maxRationaleBytes { 220 return "(rationale omitted: too long)" 221 } 222 for _, r := range rationale { 223 if !unicode.IsGraphic(r) && !unicode.IsSpace(r) { 224 return "(rationale omitted: contains non-printable characters)" 225 } 226 } 227 // NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here. 228 return rationale 229} 230 231// Replacement returns the replacement for mod, if any, from go.mod. 232// If there is no replacement for mod, Replacement returns 233// a module.Version with Path == "". 234func Replacement(mod module.Version) module.Version { 235 if index != nil { 236 if r, ok := index.replace[mod]; ok { 237 return r 238 } 239 if r, ok := index.replace[module.Version{Path: mod.Path}]; ok { 240 return r 241 } 242 } 243 return module.Version{} 244} 245 246// indexModFile rebuilds the index of modFile. 247// If modFile has been changed since it was first read, 248// modFile.Cleanup must be called before indexModFile. 249func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex { 250 i := new(modFileIndex) 251 i.data = data 252 i.dataNeedsFix = needsFix 253 254 i.module = module.Version{} 255 if modFile.Module != nil { 256 i.module = modFile.Module.Mod 257 } 258 259 i.goVersionV = "" 260 if modFile.Go != nil { 261 // We're going to use the semver package to compare Go versions, so go ahead 262 // and add the "v" prefix it expects once instead of every time. 263 i.goVersionV = "v" + modFile.Go.Version 264 } 265 266 i.require = make(map[module.Version]requireMeta, len(modFile.Require)) 267 for _, r := range modFile.Require { 268 i.require[r.Mod] = requireMeta{indirect: r.Indirect} 269 } 270 271 i.replace = make(map[module.Version]module.Version, len(modFile.Replace)) 272 for _, r := range modFile.Replace { 273 if prev, dup := i.replace[r.Old]; dup && prev != r.New { 274 base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New) 275 } 276 i.replace[r.Old] = r.New 277 } 278 279 i.highestReplaced = make(map[string]string) 280 for _, r := range modFile.Replace { 281 v, ok := i.highestReplaced[r.Old.Path] 282 if !ok || semver.Compare(r.Old.Version, v) > 0 { 283 i.highestReplaced[r.Old.Path] = r.Old.Version 284 } 285 } 286 287 i.exclude = make(map[module.Version]bool, len(modFile.Exclude)) 288 for _, x := range modFile.Exclude { 289 i.exclude[x.Mod] = true 290 } 291 292 return i 293} 294 295// allPatternClosesOverTests reports whether the "all" pattern includes 296// dependencies of tests outside the main module (as in Go 1.11–1.15). 297// (Otherwise — as in Go 1.16+ — the "all" pattern includes only the packages 298// transitively *imported by* the packages and tests in the main module.) 299func (i *modFileIndex) allPatternClosesOverTests() bool { 300 if !go116EnableNarrowAll { 301 return true 302 } 303 if i != nil && semver.Compare(i.goVersionV, narrowAllVersionV) < 0 { 304 // The module explicitly predates the change in "all" for lazy loading, so 305 // continue to use the older interpretation. (If i == nil, we not in any 306 // module at all and should use the latest semantics.) 307 return true 308 } 309 return false 310} 311 312// modFileIsDirty reports whether the go.mod file differs meaningfully 313// from what was indexed. 314// If modFile has been changed (even cosmetically) since it was first read, 315// modFile.Cleanup must be called before modFileIsDirty. 316func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool { 317 if i == nil { 318 return modFile != nil 319 } 320 321 if i.dataNeedsFix { 322 return true 323 } 324 325 if modFile.Module == nil { 326 if i.module != (module.Version{}) { 327 return true 328 } 329 } else if modFile.Module.Mod != i.module { 330 return true 331 } 332 333 if modFile.Go == nil { 334 if i.goVersionV != "" { 335 return true 336 } 337 } else if "v"+modFile.Go.Version != i.goVersionV { 338 if i.goVersionV == "" && cfg.BuildMod == "readonly" { 339 // go.mod files did not always require a 'go' version, so do not error out 340 // if one is missing — we may be inside an older module in the module 341 // cache, and should bias toward providing useful behavior. 342 } else { 343 return true 344 } 345 } 346 347 if len(modFile.Require) != len(i.require) || 348 len(modFile.Replace) != len(i.replace) || 349 len(modFile.Exclude) != len(i.exclude) { 350 return true 351 } 352 353 for _, r := range modFile.Require { 354 if meta, ok := i.require[r.Mod]; !ok { 355 return true 356 } else if r.Indirect != meta.indirect { 357 if cfg.BuildMod == "readonly" { 358 // The module's requirements are consistent; only the "// indirect" 359 // comments that are wrong. But those are only guaranteed to be accurate 360 // after a "go mod tidy" — it's a good idea to run those before 361 // committing a change, but it's certainly not mandatory. 362 } else { 363 return true 364 } 365 } 366 } 367 368 for _, r := range modFile.Replace { 369 if r.New != i.replace[r.Old] { 370 return true 371 } 372 } 373 374 for _, x := range modFile.Exclude { 375 if !i.exclude[x.Mod] { 376 return true 377 } 378 } 379 380 return false 381} 382 383// rawGoVersion records the Go version parsed from each module's go.mod file. 384// 385// If a module is replaced, the version of the replacement is keyed by the 386// replacement module.Version, not the version being replaced. 387var rawGoVersion sync.Map // map[module.Version]string 388 389// A modFileSummary is a summary of a go.mod file for which we do not need to 390// retain complete information — for example, the go.mod file of a dependency 391// module. 392type modFileSummary struct { 393 module module.Version 394 goVersionV string // GoVersion with "v" prefix 395 require []module.Version 396 retract []retraction 397} 398 399// A retraction consists of a retracted version interval and rationale. 400// retraction is like modfile.Retract, but it doesn't point to the syntax tree. 401type retraction struct { 402 modfile.VersionInterval 403 Rationale string 404} 405 406// goModSummary returns a summary of the go.mod file for module m, 407// taking into account any replacements for m, exclusions of its dependencies, 408// and/or vendoring. 409// 410// m must be a version in the module graph, reachable from the Target module. 411// In readonly mode, the go.sum file must contain an entry for m's go.mod file 412// (or its replacement). goModSummary must not be called for the Target module 413// itself, as its requirements may change. Use rawGoModSummary for other 414// module versions. 415// 416// The caller must not modify the returned summary. 417func goModSummary(m module.Version) (*modFileSummary, error) { 418 if m == Target { 419 panic("internal error: goModSummary called on the Target module") 420 } 421 422 if cfg.BuildMod == "vendor" { 423 summary := &modFileSummary{ 424 module: module.Version{Path: m.Path}, 425 } 426 if vendorVersion[m.Path] != m.Version { 427 // This module is not vendored, so packages cannot be loaded from it and 428 // it cannot be relevant to the build. 429 return summary, nil 430 } 431 432 // For every module other than the target, 433 // return the full list of modules from modules.txt. 434 readVendorList() 435 436 // TODO(#36876): Load the "go" version from vendor/modules.txt and store it 437 // in rawGoVersion with the appropriate key. 438 439 // We don't know what versions the vendored module actually relies on, 440 // so assume that it requires everything. 441 summary.require = vendorList 442 return summary, nil 443 } 444 445 actual := Replacement(m) 446 if actual.Path == "" { 447 actual = m 448 } 449 if HasModRoot() && cfg.BuildMod == "readonly" && actual.Version != "" { 450 key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"} 451 if !modfetch.HaveSum(key) { 452 suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path) 453 return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion}) 454 } 455 } 456 summary, err := rawGoModSummary(actual) 457 if err != nil { 458 return nil, err 459 } 460 461 if actual.Version == "" { 462 // The actual module is a filesystem-local replacement, for which we have 463 // unfortunately not enforced any sort of invariants about module lines or 464 // matching module paths. Anything goes. 465 // 466 // TODO(bcmills): Remove this special-case, update tests, and add a 467 // release note. 468 } else { 469 if summary.module.Path == "" { 470 return nil, module.VersionError(actual, errors.New("parsing go.mod: missing module line")) 471 } 472 473 // In theory we should only allow mpath to be unequal to m.Path here if the 474 // version that we fetched lacks an explicit go.mod file: if the go.mod file 475 // is explicit, then it should match exactly (to ensure that imports of other 476 // packages within the module are interpreted correctly). Unfortunately, we 477 // can't determine that information from the module proxy protocol: we'll have 478 // to leave that validation for when we load actual packages from within the 479 // module. 480 if mpath := summary.module.Path; mpath != m.Path && mpath != actual.Path { 481 return nil, module.VersionError(actual, fmt.Errorf(`parsing go.mod: 482 module declares its path as: %s 483 but was required as: %s`, mpath, m.Path)) 484 } 485 } 486 487 if index != nil && len(index.exclude) > 0 { 488 // Drop any requirements on excluded versions. 489 // Don't modify the cached summary though, since we might need the raw 490 // summary separately. 491 haveExcludedReqs := false 492 for _, r := range summary.require { 493 if index.exclude[r] { 494 haveExcludedReqs = true 495 break 496 } 497 } 498 if haveExcludedReqs { 499 s := new(modFileSummary) 500 *s = *summary 501 s.require = make([]module.Version, 0, len(summary.require)) 502 for _, r := range summary.require { 503 if !index.exclude[r] { 504 s.require = append(s.require, r) 505 } 506 } 507 summary = s 508 } 509 } 510 return summary, nil 511} 512 513// rawGoModSummary returns a new summary of the go.mod file for module m, 514// ignoring all replacements that may apply to m and excludes that may apply to 515// its dependencies. 516// 517// rawGoModSummary cannot be used on the Target module. 518func rawGoModSummary(m module.Version) (*modFileSummary, error) { 519 if m == Target { 520 panic("internal error: rawGoModSummary called on the Target module") 521 } 522 523 type cached struct { 524 summary *modFileSummary 525 err error 526 } 527 c := rawGoModSummaryCache.Do(m, func() interface{} { 528 summary := new(modFileSummary) 529 var f *modfile.File 530 if m.Version == "" { 531 // m is a replacement module with only a file path. 532 dir := m.Path 533 if !filepath.IsAbs(dir) { 534 dir = filepath.Join(ModRoot(), dir) 535 } 536 gomod := filepath.Join(dir, "go.mod") 537 538 data, err := lockedfile.Read(gomod) 539 if err != nil { 540 return cached{nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(gomod), err))} 541 } 542 f, err = modfile.ParseLax(gomod, data, nil) 543 if err != nil { 544 return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err))} 545 } 546 } else { 547 if !semver.IsValid(m.Version) { 548 // Disallow the broader queries supported by fetch.Lookup. 549 base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version) 550 } 551 552 data, err := modfetch.GoMod(m.Path, m.Version) 553 if err != nil { 554 return cached{nil, err} 555 } 556 f, err = modfile.ParseLax("go.mod", data, nil) 557 if err != nil { 558 return cached{nil, module.VersionError(m, fmt.Errorf("parsing go.mod: %v", err))} 559 } 560 } 561 562 if f.Module != nil { 563 summary.module = f.Module.Mod 564 } 565 if f.Go != nil && f.Go.Version != "" { 566 rawGoVersion.LoadOrStore(m, f.Go.Version) 567 summary.goVersionV = "v" + f.Go.Version 568 } 569 if len(f.Require) > 0 { 570 summary.require = make([]module.Version, 0, len(f.Require)) 571 for _, req := range f.Require { 572 summary.require = append(summary.require, req.Mod) 573 } 574 } 575 if len(f.Retract) > 0 { 576 summary.retract = make([]retraction, 0, len(f.Retract)) 577 for _, ret := range f.Retract { 578 summary.retract = append(summary.retract, retraction{ 579 VersionInterval: ret.VersionInterval, 580 Rationale: ret.Rationale, 581 }) 582 } 583 } 584 585 return cached{summary, nil} 586 }).(cached) 587 588 return c.summary, c.err 589} 590 591var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result 592