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.
17 "cmd/go/internal/base"
19 "cmd/go/internal/fsys"
20 "cmd/go/internal/lockedfile"
21 "cmd/go/internal/modfetch"
23 "cmd/go/internal/trace"
25 "golang.org/x/mod/modfile"
26 "golang.org/x/mod/module"
27 "golang.org/x/mod/semver"
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"
36 // lazyLoadingVersionV 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 lazyLoadingVersionV = "v1.17"
41 // separateIndirectVersionV is the Go version (plus leading "v") at which
42 // "// indirect" dependencies are added in a block separate from the direct
43 // ones. See https://golang.org/issue/45965.
44 separateIndirectVersionV = "v1.17"
48 // go117EnableLazyLoading toggles whether lazy-loading code paths should be
49 // active. It will be removed once the lazy loading implementation is stable
51 go117EnableLazyLoading = true
53 // go1117LazyTODO is a constant that exists only until lazy loading is
54 // implemented. Its use indicates a condition that will need to change if the
55 // main module is lazy.
59 // modFileGoVersion returns the (non-empty) Go version at which the requirements
60 // in modFile are intepreted, or the latest Go version if modFile is nil.
61 func modFileGoVersion() string {
62 _ = TODOWorkspaces("this is obviously wrong.")
63 // Yes we're picking arbitrarily, we'll have to pass through the version
65 modFile := MainModules.ModFile(MainModules.Versions()[0])
67 return LatestGoVersion()
69 if modFile.Go == nil || modFile.Go.Version == "" {
70 // The main module necessarily has a go.mod file, and that file lacks a
71 // 'go' directive. The 'go' command has been adding that directive
72 // automatically since Go 1.12, so this module either dates to Go 1.11 or
73 // has been erroneously hand-edited.
75 // The semantics of the go.mod file are more-or-less the same from Go 1.11
76 // through Go 1.16, changing at 1.17 for lazy loading. So even though a
77 // go.mod file without a 'go' directive is theoretically a Go 1.11 file,
78 // scripts may assume that it ends up as a Go 1.16 module.
81 return modFile.Go.Version
84 // A modFileIndex is an index of data corresponding to a modFile
85 // at a specific point in time.
86 type modFileIndex struct {
88 dataNeedsFix bool // true if fixVersion applied a change while parsing data
90 goVersionV string // GoVersion with "v" prefix
91 require map[module.Version]requireMeta
92 replace map[module.Version]module.Version
93 highestReplaced map[string]string // highest replaced version of each module path; empty string for wildcard-only replacements
94 exclude map[module.Version]bool
97 type requireMeta struct {
101 // A modDepth indicates which dependencies should be loaded for a go.mod file.
105 lazy modDepth = iota // load dependencies only as needed
106 eager // load all transitive dependencies eagerly
109 func modDepthFromGoVersion(goVersion string) modDepth {
110 if !go117EnableLazyLoading {
113 if semver.Compare("v"+goVersion, lazyLoadingVersionV) < 0 {
119 // CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by
120 // the main module's go.mod or retracted by its author. Most version queries use
121 // this to filter out versions that should not be used.
122 func CheckAllowed(ctx context.Context, m module.Version) error {
123 if err := CheckExclusions(ctx, m); err != nil {
126 if err := CheckRetractions(ctx, m); err != nil {
132 // ErrDisallowed is returned by version predicates passed to Query and similar
133 // functions to indicate that a version should not be considered.
134 var ErrDisallowed = errors.New("disallowed module version")
136 // CheckExclusions returns an error equivalent to ErrDisallowed if module m is
137 // excluded by the main module's go.mod file.
138 func CheckExclusions(ctx context.Context, m module.Version) error {
139 for _, mainModule := range MainModules.Versions() {
140 if index := MainModules.Index(mainModule); index != nil && index.exclude[m] {
141 return module.VersionError(m, errExcluded)
147 var errExcluded = &excludedError{}
149 type excludedError struct{}
151 func (e *excludedError) Error() string { return "excluded by go.mod" }
152 func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
154 // CheckRetractions returns an error if module m has been retracted by
156 func CheckRetractions(ctx context.Context, m module.Version) (err error) {
158 if retractErr := (*ModuleRetractedError)(nil); err == nil || errors.As(err, &retractErr) {
161 // Attribute the error to the version being checked, not the version from
162 // which the retractions were to be loaded.
163 if mErr := (*module.ModuleError)(nil); errors.As(err, &mErr) {
166 err = &retractionLoadingError{m: m, err: err}
170 // Main module, standard library, or file replacement module.
171 // Cannot be retracted.
174 if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
175 // All versions of the module were replaced.
176 // Don't load retractions, since we'd just load the replacement.
180 // Find the latest available version of the module, and load its go.mod. If
181 // the latest version is replaced, we'll load the replacement.
183 // If there's an error loading the go.mod, we'll return it here. These errors
184 // should generally be ignored by callers since they happen frequently when
185 // we're offline. These errors are not equivalent to ErrDisallowed, so they
186 // may be distinguished from retraction errors.
188 // We load the raw file here: the go.mod file may have a different module
189 // path that we expect if the module or its repository was renamed.
190 // We still want to apply retractions to other aliases of the module.
191 rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
195 summary, err := rawGoModSummary(rm)
200 var rationale []string
202 for _, r := range summary.retract {
203 if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 {
205 if r.Rationale != "" {
206 rationale = append(rationale, r.Rationale)
211 return module.VersionError(m, &ModuleRetractedError{Rationale: rationale})
216 type ModuleRetractedError struct {
220 func (e *ModuleRetractedError) Error() string {
221 msg := "retracted by module author"
222 if len(e.Rationale) > 0 {
223 // This is meant to be a short error printed on a terminal, so just
224 // print the first rationale.
225 msg += ": " + ShortMessage(e.Rationale[0], "retracted by module author")
230 func (e *ModuleRetractedError) Is(err error) bool {
231 return err == ErrDisallowed
234 type retractionLoadingError struct {
239 func (e *retractionLoadingError) Error() string {
240 return fmt.Sprintf("loading module retractions for %v: %v", e.m, e.err)
243 func (e *retractionLoadingError) Unwrap() error {
247 // ShortMessage returns a string from go.mod (for example, a retraction
248 // rationale or deprecation message) that is safe to print in a terminal.
250 // If the given string is empty, ShortMessage returns the given default. If the
251 // given string is too long or contains non-printable characters, ShortMessage
252 // returns a hard-coded string.
253 func ShortMessage(message, emptyDefault string) string {
255 if i := strings.Index(message, "\n"); i >= 0 {
256 message = message[:i]
258 message = strings.TrimSpace(message)
262 if len(message) > maxLen {
263 return "(message omitted: too long)"
265 for _, r := range message {
266 if !unicode.IsGraphic(r) && !unicode.IsSpace(r) {
267 return "(message omitted: contains non-printable characters)"
270 // NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
274 // CheckDeprecation returns a deprecation message from the go.mod file of the
275 // latest version of the given module. Deprecation messages are comments
276 // before or on the same line as the module directives that start with
277 // "Deprecated:" and run until the end of the paragraph.
279 // CheckDeprecation returns an error if the message can't be loaded.
280 // CheckDeprecation returns "", nil if there is no deprecation message.
281 func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string, err error) {
284 err = fmt.Errorf("loading deprecation for %s: %w", m.Path, err)
289 // Main module, standard library, or file replacement module.
290 // Don't look up deprecation.
293 if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
294 // All versions of the module were replaced.
295 // We'll look up deprecation separately for the replacement.
299 latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
303 summary, err := rawGoModSummary(latest)
307 return summary.deprecated, nil
310 func replacement(mod module.Version, index *modFileIndex) (fromVersion string, to module.Version, ok bool) {
311 if r, ok := index.replace[mod]; ok {
312 return mod.Version, r, true
314 if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
317 return "", module.Version{}, false
320 // Replacement returns the replacement for mod, if any, from go.mod.
321 // If there is no replacement for mod, Replacement returns
322 // a module.Version with Path == "".
323 func Replacement(mod module.Version) module.Version {
324 _ = TODOWorkspaces("support replaces in the go.work file")
325 foundFrom, found, foundModRoot := "", module.Version{}, ""
326 for _, v := range MainModules.Versions() {
327 if index := MainModules.Index(v); index != nil {
328 if from, r, ok := replacement(mod, index); ok {
329 modRoot := MainModules.ModRoot(v)
330 if foundModRoot != "" && foundFrom != from && found != r {
331 _ = TODOWorkspaces("once the go.work file supports replaces, recommend them as a way to override conflicts")
332 base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v",
333 mod, modFilePath(foundModRoot), modFilePath(modRoot))
336 found, foundModRoot = r, modRoot
343 // resolveReplacement returns the module actually used to load the source code
344 // for m: either m itself, or the replacement for m (iff m is replaced).
345 func resolveReplacement(m module.Version) module.Version {
346 if r := Replacement(m); r.Path != "" {
352 // indexModFile rebuilds the index of modFile.
353 // If modFile has been changed since it was first read,
354 // modFile.Cleanup must be called before indexModFile.
355 func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex {
356 i := new(modFileIndex)
358 i.dataNeedsFix = needsFix
360 i.module = module.Version{}
361 if modFile.Module != nil {
362 i.module = modFile.Module.Mod
366 if modFile.Go == nil {
367 rawGoVersion.Store(mod, "")
369 // We're going to use the semver package to compare Go versions, so go ahead
370 // and add the "v" prefix it expects once instead of every time.
371 i.goVersionV = "v" + modFile.Go.Version
372 rawGoVersion.Store(mod, modFile.Go.Version)
375 i.require = make(map[module.Version]requireMeta, len(modFile.Require))
376 for _, r := range modFile.Require {
377 i.require[r.Mod] = requireMeta{indirect: r.Indirect}
380 i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
381 for _, r := range modFile.Replace {
382 if prev, dup := i.replace[r.Old]; dup && prev != r.New {
383 base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
385 i.replace[r.Old] = r.New
388 i.highestReplaced = make(map[string]string)
389 for _, r := range modFile.Replace {
390 v, ok := i.highestReplaced[r.Old.Path]
391 if !ok || semver.Compare(r.Old.Version, v) > 0 {
392 i.highestReplaced[r.Old.Path] = r.Old.Version
396 i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
397 for _, x := range modFile.Exclude {
398 i.exclude[x.Mod] = true
404 // modFileIsDirty reports whether the go.mod file differs meaningfully
405 // from what was indexed.
406 // If modFile has been changed (even cosmetically) since it was first read,
407 // modFile.Cleanup must be called before modFileIsDirty.
408 func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
410 return modFile != nil
417 if modFile.Module == nil {
418 if i.module != (module.Version{}) {
421 } else if modFile.Module.Mod != i.module {
425 if modFile.Go == nil {
426 if i.goVersionV != "" {
429 } else if "v"+modFile.Go.Version != i.goVersionV {
430 if i.goVersionV == "" && cfg.BuildMod != "mod" {
431 // go.mod files did not always require a 'go' version, so do not error out
432 // if one is missing — we may be inside an older module in the module
433 // cache, and should bias toward providing useful behavior.
439 if len(modFile.Require) != len(i.require) ||
440 len(modFile.Replace) != len(i.replace) ||
441 len(modFile.Exclude) != len(i.exclude) {
445 for _, r := range modFile.Require {
446 if meta, ok := i.require[r.Mod]; !ok {
448 } else if r.Indirect != meta.indirect {
449 if cfg.BuildMod == "readonly" {
450 // The module's requirements are consistent; only the "// indirect"
451 // comments that are wrong. But those are only guaranteed to be accurate
452 // after a "go mod tidy" — it's a good idea to run those before
453 // committing a change, but it's certainly not mandatory.
460 for _, r := range modFile.Replace {
461 if r.New != i.replace[r.Old] {
466 for _, x := range modFile.Exclude {
467 if !i.exclude[x.Mod] {
475 // rawGoVersion records the Go version parsed from each module's go.mod file.
477 // If a module is replaced, the version of the replacement is keyed by the
478 // replacement module.Version, not the version being replaced.
479 var rawGoVersion sync.Map // map[module.Version]string
481 // A modFileSummary is a summary of a go.mod file for which we do not need to
482 // retain complete information — for example, the go.mod file of a dependency
484 type modFileSummary struct {
485 module module.Version
488 require []module.Version
493 // A retraction consists of a retracted version interval and rationale.
494 // retraction is like modfile.Retract, but it doesn't point to the syntax tree.
495 type retraction struct {
496 modfile.VersionInterval
500 // goModSummary returns a summary of the go.mod file for module m,
501 // taking into account any replacements for m, exclusions of its dependencies,
504 // m must be a version in the module graph, reachable from the Target module.
505 // In readonly mode, the go.sum file must contain an entry for m's go.mod file
506 // (or its replacement). goModSummary must not be called for the Target module
507 // itself, as its requirements may change. Use rawGoModSummary for other
510 // The caller must not modify the returned summary.
511 func goModSummary(m module.Version) (*modFileSummary, error) {
512 if m.Version == "" && MainModules.Contains(m.Path) {
513 panic("internal error: goModSummary called on a main module")
516 if cfg.BuildMod == "vendor" {
517 summary := &modFileSummary{
518 module: module.Version{Path: m.Path},
520 if vendorVersion[m.Path] != m.Version {
521 // This module is not vendored, so packages cannot be loaded from it and
522 // it cannot be relevant to the build.
526 // For every module other than the target,
527 // return the full list of modules from modules.txt.
530 // We don't know what versions the vendored module actually relies on,
531 // so assume that it requires everything.
532 summary.require = vendorList
536 actual := resolveReplacement(m)
537 if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" {
538 key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"}
539 if !modfetch.HaveSum(key) {
540 suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path)
541 return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion})
544 summary, err := rawGoModSummary(actual)
549 if actual.Version == "" {
550 // The actual module is a filesystem-local replacement, for which we have
551 // unfortunately not enforced any sort of invariants about module lines or
552 // matching module paths. Anything goes.
554 // TODO(bcmills): Remove this special-case, update tests, and add a
557 if summary.module.Path == "" {
558 return nil, module.VersionError(actual, errors.New("parsing go.mod: missing module line"))
561 // In theory we should only allow mpath to be unequal to m.Path here if the
562 // version that we fetched lacks an explicit go.mod file: if the go.mod file
563 // is explicit, then it should match exactly (to ensure that imports of other
564 // packages within the module are interpreted correctly). Unfortunately, we
565 // can't determine that information from the module proxy protocol: we'll have
566 // to leave that validation for when we load actual packages from within the
568 if mpath := summary.module.Path; mpath != m.Path && mpath != actual.Path {
569 return nil, module.VersionError(actual, fmt.Errorf(`parsing go.mod:
570 module declares its path as: %s
571 but was required as: %s`, mpath, m.Path))
575 for _, mainModule := range MainModules.Versions() {
576 if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 {
577 // Drop any requirements on excluded versions.
578 // Don't modify the cached summary though, since we might need the raw
579 // summary separately.
580 haveExcludedReqs := false
581 for _, r := range summary.require {
582 if index.exclude[r] {
583 haveExcludedReqs = true
587 if haveExcludedReqs {
588 s := new(modFileSummary)
590 s.require = make([]module.Version, 0, len(summary.require))
591 for _, r := range summary.require {
592 if !index.exclude[r] {
593 s.require = append(s.require, r)
603 // rawGoModSummary returns a new summary of the go.mod file for module m,
604 // ignoring all replacements that may apply to m and excludes that may apply to
607 // rawGoModSummary cannot be used on the Target module.
608 func rawGoModSummary(m module.Version) (*modFileSummary, error) {
609 if m.Path == "" && MainModules.Contains(m.Path) {
610 panic("internal error: rawGoModSummary called on the Target module")
614 summary *modFileSummary
617 c := rawGoModSummaryCache.Do(m, func() interface{} {
618 summary := new(modFileSummary)
619 name, data, err := rawGoModData(m)
621 return cached{nil, err}
623 f, err := modfile.ParseLax(name, data, nil)
625 return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(name), err))}
628 summary.module = f.Module.Mod
629 summary.deprecated = f.Module.Deprecated
631 if f.Go != nil && f.Go.Version != "" {
632 rawGoVersion.LoadOrStore(m, f.Go.Version)
633 summary.goVersion = f.Go.Version
634 summary.depth = modDepthFromGoVersion(f.Go.Version)
636 summary.depth = eager
638 if len(f.Require) > 0 {
639 summary.require = make([]module.Version, 0, len(f.Require))
640 for _, req := range f.Require {
641 summary.require = append(summary.require, req.Mod)
644 if len(f.Retract) > 0 {
645 summary.retract = make([]retraction, 0, len(f.Retract))
646 for _, ret := range f.Retract {
647 summary.retract = append(summary.retract, retraction{
648 VersionInterval: ret.VersionInterval,
649 Rationale: ret.Rationale,
654 return cached{summary, nil}
657 return c.summary, c.err
660 var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
662 // rawGoModData returns the content of the go.mod file for module m, ignoring
663 // all replacements that may apply to m.
665 // rawGoModData cannot be used on the Target module.
667 // Unlike rawGoModSummary, rawGoModData does not cache its results in memory.
668 // Use rawGoModSummary instead unless you specifically need these bytes.
669 func rawGoModData(m module.Version) (name string, data []byte, err error) {
671 // m is a replacement module with only a file path.
673 if !filepath.IsAbs(dir) {
674 dir = filepath.Join(ModRoot(), dir)
676 name = filepath.Join(dir, "go.mod")
677 if gomodActual, ok := fsys.OverlayPath(name); ok {
678 // Don't lock go.mod if it's part of the overlay.
679 // On Plan 9, locking requires chmod, and we don't want to modify any file
680 // in the overlay. See #44700.
681 data, err = os.ReadFile(gomodActual)
683 data, err = lockedfile.Read(gomodActual)
686 return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err))
689 if !semver.IsValid(m.Version) {
690 // Disallow the broader queries supported by fetch.Lookup.
691 base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version)
694 data, err = modfetch.GoMod(m.Path, m.Version)
696 return name, data, err
699 // queryLatestVersionIgnoringRetractions looks up the latest version of the
700 // module with the given path without considering retracted or excluded
703 // If all versions of the module are replaced,
704 // queryLatestVersionIgnoringRetractions returns the replacement without making
707 // If the queried latest version is replaced,
708 // queryLatestVersionIgnoringRetractions returns the replacement.
709 func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) {
711 latest module.Version
714 e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} {
715 ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
718 if repl := Replacement(module.Version{Path: path}); repl.Path != "" {
719 // All versions of the module were replaced.
721 return &entry{latest: repl}
724 // Find the latest version of the module.
725 // Ignore exclusions from the main module's go.mod.
726 const ignoreSelected = ""
727 var allowAll AllowedFunc
728 rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll)
730 return &entry{err: err}
732 latest := module.Version{Path: path, Version: rev.Version}
733 if repl := resolveReplacement(latest); repl.Path != "" {
736 return &entry{latest: latest}
738 return e.latest, e.err
741 var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result