pkgPatterns = append(pkgPatterns, q.pattern)
}
}
- r.checkPackagesAndRetractions(ctx, pkgPatterns)
+ if len(pkgPatterns) > 0 {
+ // We skipped over missing-package errors earlier: we want to resolve
+ // pathSets ourselves, but at that point we don't have enough context
+ // to log the package-import chains leading to the error. Reload the package
+ // import graph one last time to report any remaining unresolved
+ // dependencies.
+ pkgOpts := modload.PackageOpts{
+ LoadTests: *getT,
+ ResolveMissingImports: false,
+ AllowErrors: false,
+ }
+ modload.LoadPackages(ctx, pkgOpts, pkgPatterns...)
+ base.ExitIfErrors()
+ }
// We've already downloaded modules (and identified direct and indirect
// dependencies) by loading packages in findAndUpgradeImports.
modload.AllowWriteGoMod()
modload.WriteGoMod()
modload.DisallowWriteGoMod()
+
+ // Report warnings if any retracted versions are in the build list.
+ // This must be done after writing go.mod to avoid spurious '// indirect'
+ // comments. These functions read and write global state.
+ //
+ // TODO(golang.org/issue/40775): ListModules (called from reportRetractions)
+ // resets modload.loader, which contains information about direct dependencies
+ // that WriteGoMod uses. Refactor to avoid these kinds of global side effects.
+ reportRetractions(ctx)
}
// parseArgs parses command-line arguments and reports errors.
return queries
}
+// reportRetractions prints warnings if any modules in the build list are
+// retracted.
+func reportRetractions(ctx context.Context) {
+ // Query for retractions of modules in the build list.
+ // Use modload.ListModules, since that provides information in the same format
+ // as 'go list -m'. Don't query for "all", since that's not allowed outside a
+ // module.
+ buildList := modload.LoadedModules()
+ args := make([]string, 0, len(buildList))
+ for _, m := range buildList {
+ if m.Version == "" {
+ // main module or dummy target module
+ continue
+ }
+ args = append(args, m.Path+"@"+m.Version)
+ }
+ listU := false
+ listVersions := false
+ listRetractions := true
+ mods := modload.ListModules(ctx, args, listU, listVersions, listRetractions)
+ retractPath := ""
+ for _, mod := range mods {
+ if len(mod.Retracted) > 0 {
+ if retractPath == "" {
+ retractPath = mod.Path
+ } else {
+ retractPath = "<module>"
+ }
+ rationale := modload.ShortRetractionRationale(mod.Retracted[0])
+ fmt.Fprintf(os.Stderr, "go: warning: %s@%s is retracted: %s\n", mod.Path, mod.Version, rationale)
+ }
+ }
+ if modload.HasModRoot() && retractPath != "" {
+ fmt.Fprintf(os.Stderr, "go: run 'go get %s@latest' to switch to the latest unretracted version\n", retractPath)
+ }
+}
+
type resolver struct {
localQueries []*query // queries for absolute or relative paths
pathQueries []*query // package path literal queries in original order
work *par.Queue
+ queryModuleCache par.Cache
+ queryPackagesCache par.Cache
+ queryPatternCache par.Cache
matchInModuleCache par.Cache
}
return nil, false
}
-// queryModule wraps modload.Query, substituting r.checkAllowedOr to decide
+// queryModule wraps modload.Query, substituting r.checkAllowedor to decide
// allowed versions.
func (r *resolver) queryModule(ctx context.Context, mPath, query string, selected func(string) string) (module.Version, error) {
current := r.initialSelected(mPath)
}
mu.Lock()
- upgrades = append(upgrades, pathSet{path: path, pkgMods: pkgMods, err: err})
+ upgrades = append(upgrades, pathSet{pkgMods: pkgMods, err: err})
mu.Unlock()
return false
}
return false, cs.mod
}
-// checkPackagesAndRetractions reloads packages for the given patterns and
-// reports missing and ambiguous package errors. It also reports loads and
-// reports retractions for resolved modules and modules needed to build
-// named packages.
-//
-// We skip missing-package errors earlier in the process, since we want to
-// resolve pathSets ourselves, but at that point, we don't have enough context
-// to log the package-import chains leading to each error.
-func (r *resolver) checkPackagesAndRetractions(ctx context.Context, pkgPatterns []string) {
- defer base.ExitIfErrors()
-
- // Build a list of modules to load retractions for. Start with versions
- // selected based on command line queries.
- //
- // This is a subset of the build list. If the main module has a lot of
- // dependencies, loading retractions for the entire build list would be slow.
- relevantMods := make(map[module.Version]struct{})
- for path, reason := range r.resolvedVersion {
- relevantMods[module.Version{Path: path, Version: reason.version}] = struct{}{}
- }
-
- // Reload packages, reporting errors for missing and ambiguous imports.
- if len(pkgPatterns) > 0 {
- // LoadPackages will print errors (since it has more context) but will not
- // exit, since we need to load retractions later.
- pkgOpts := modload.PackageOpts{
- LoadTests: *getT,
- ResolveMissingImports: false,
- AllowErrors: true,
- }
- matches, pkgs := modload.LoadPackages(ctx, pkgOpts, pkgPatterns...)
- for _, m := range matches {
- if len(m.Errs) > 0 {
- base.SetExitStatus(1)
- break
- }
- }
- for _, pkg := range pkgs {
- if _, _, err := modload.Lookup("", false, pkg); err != nil {
- base.SetExitStatus(1)
- if ambiguousErr := (*modload.AmbiguousImportError)(nil); errors.As(err, &ambiguousErr) {
- for _, m := range ambiguousErr.Modules {
- relevantMods[m] = struct{}{}
- }
- }
- }
- if m := modload.PackageModule(pkg); m.Path != "" {
- relevantMods[m] = struct{}{}
- }
- }
- }
-
- // Load and report retractions.
- type retraction struct {
- m module.Version
- err error
- }
- retractions := make([]retraction, 0, len(relevantMods))
- for m := range relevantMods {
- retractions = append(retractions, retraction{m: m})
- }
- sort.Slice(retractions, func(i, j int) bool {
- return retractions[i].m.Path < retractions[j].m.Path
- })
- for i := 0; i < len(retractions); i++ {
- i := i
- r.work.Add(func() {
- err := modload.CheckRetractions(ctx, retractions[i].m)
- if retractErr := (*modload.ModuleRetractedError)(nil); errors.As(err, &retractErr) {
- retractions[i].err = err
- }
- })
- }
- <-r.work.Idle()
- for _, r := range retractions {
- if r.err != nil {
- fmt.Fprintf(os.Stderr, "go: warning: %v\n", r.err)
- }
- }
-}
-
// reportChanges logs resolved version changes to os.Stderr.
func (r *resolver) reportChanges(queries []*query) {
for _, q := range queries {
if err := CheckExclusions(ctx, m); err != nil {
return err
}
- if err := CheckRetractions(ctx, m); err != nil {
+ if err := checkRetractions(ctx, m); err != nil {
return err
}
return nil
func (e *excludedError) Error() string { return "excluded by go.mod" }
func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
-// CheckRetractions returns an error if module m has been retracted by
+// checkRetractions returns an error if module m has been retracted by
// its author.
-func CheckRetractions(ctx context.Context, m module.Version) error {
+func checkRetractions(ctx context.Context, m module.Version) error {
if m.Version == "" {
// Main module, standard library, or file replacement module.
// Cannot be retracted.
}
}
if isRetracted {
- return module.VersionError(m, &ModuleRetractedError{Rationale: rationale})
+ return module.VersionError(m, &retractedError{rationale: rationale})
}
return nil
}
var retractCache par.Cache
-type ModuleRetractedError struct {
- Rationale []string
+type retractedError struct {
+ rationale []string
}
-func (e *ModuleRetractedError) Error() string {
+func (e *retractedError) Error() string {
msg := "retracted by module author"
- if len(e.Rationale) > 0 {
+ if len(e.rationale) > 0 {
// This is meant to be a short error printed on a terminal, so just
// print the first rationale.
- msg += ": " + ShortRetractionRationale(e.Rationale[0])
+ msg += ": " + ShortRetractionRationale(e.rationale[0])
}
return msg
}
-func (e *ModuleRetractedError) Is(err error) bool {
+func (e *retractedError) Is(err error) bool {
return err == ErrDisallowed
}
# When there is no rationale, 'go get' should print a hard-coded message.
go get -d example.com/retract/rationale@v1.0.0-empty
-stderr '^go: warning: example.com/retract/rationale@v1.0.0-empty: retracted by module author$'
+stderr '^go: warning: example.com/retract/rationale@v1.0.0-empty is retracted: retracted by module author$'
# 'go list' should print the same hard-coded message.
go list -m -retracted -f '{{.Retracted}}' example.com/retract/rationale
# When there is a multi-line message, 'go get' should print the first line.
go get -d example.com/retract/rationale@v1.0.0-multiline1
-stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline1: retracted by module author: short description$'
+stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline1 is retracted: short description$'
! stderr 'detail'
# 'go list' should show the full message.
# 'go get' output should be the same whether the retraction appears at top-level
# or in a block.
go get -d example.com/retract/rationale@v1.0.0-multiline2
-stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline2: retracted by module author: short description$'
+stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline2 is retracted: short description$'
! stderr 'detail'
# Same for 'go list'.
# 'go get' should omit long messages.
go get -d example.com/retract/rationale@v1.0.0-long
-stderr '^go: warning: example.com/retract/rationale@v1.0.0-long: retracted by module author: \(rationale omitted: too long\)'
+stderr '^go: warning: example.com/retract/rationale@v1.0.0-long is retracted: \(rationale omitted: too long\)'
# 'go list' should show the full message.
go list -m -retracted -f '{{.Retracted}}' example.com/retract/rationale
# 'go get' should omit messages with unprintable characters.
go get -d example.com/retract/rationale@v1.0.0-unprintable
-stderr '^go: warning: example.com/retract/rationale@v1.0.0-unprintable: retracted by module author: \(rationale omitted: contains non-printable characters\)'
+stderr '^go: warning: example.com/retract/rationale@v1.0.0-unprintable is retracted: \(rationale omitted: contains non-printable characters\)'
# 'go list' should show the full message.
go list -m -retracted -f '{{.Retracted}}' example.com/retract/rationale
# 'go get' will only report the first retraction to avoid being too verbose.
go get -d example.com/retract/rationale@v1.0.0-order
-stderr '^go: warning: example.com/retract/rationale@v1.0.0-order: retracted by module author: degenerate range$'
+stderr '^go: warning: example.com/retract/rationale@v1.0.0-order is retracted: degenerate range$'
go get -d example.com/retract/rationale@v1.0.1-order
-stderr '^go: warning: example.com/retract/rationale@v1.0.1-order: retracted by module author: single version$'
+stderr '^go: warning: example.com/retract/rationale@v1.0.1-order is retracted: single version$'
-- go.mod --
module m