From: Russ Cox Date: Wed, 31 May 2023 14:24:23 +0000 (-0400) Subject: cmd/go: make toolchain less special in MVS X-Git-Tag: go1.21rc1~148 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=abdaa0f9aab853ea4e7a4fc1d9b8160309ae6579;p=gostls13.git cmd/go: make toolchain less special in MVS We were using the omission of toolchain from the MVS graph as a signal that toolchain was not mentioned on the go get line, but not including it in the graph causes various problems, and it may be reintroduced to the graph during operations like pruning conversion, after which its presence is not a good signal about whether it was mentioned on the go get command line. Fix all this irregularity by explicitly telling WriteGoMod whether the command line mentioned toolchain instead. For #57001. Change-Id: I74084637c177c30918fdb114a0d9030cdee7324e Reviewed-on: https://go-review.googlesource.com/c/go/+/499575 TryBot-Result: Gopher Robot Auto-Submit: Russ Cox Run-TryBot: Russ Cox Reviewed-by: Bryan Mills --- diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index ca5f0dc763..e1c0e5b4f6 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -304,6 +304,14 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } dropToolchain, queries := parseArgs(ctx, args) + opts := modload.WriteOpts{ + DropToolchain: dropToolchain, + } + for _, q := range queries { + if q.pattern == "toolchain" { + opts.ExplicitToolchain = true + } + } r := newResolver(ctx, queries) r.performLocalQueries(ctx) @@ -372,14 +380,10 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } r.checkPackageProblems(ctx, pkgPatterns) - if dropToolchain { - modload.OverrideRoots(ctx, []module.Version{{Path: "toolchain", Version: "none"}}) - } - // Everything succeeded. Update go.mod. oldReqs := reqsFromGoMod(modload.ModFile()) - if err := modload.WriteGoMod(ctx, modload.WriteOpts{}); err != nil { + if err := modload.WriteGoMod(ctx, opts); err != nil { if tooNew, ok := err.(*gover.TooNewError); ok { // This can happen for 'go get go@newversion' // when all the required modules are old enough diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go index 517ecfcf66..70092da92f 100644 --- a/src/cmd/go/internal/modload/buildlist.go +++ b/src/cmd/go/internal/modload/buildlist.go @@ -5,12 +5,6 @@ package modload import ( - "cmd/go/internal/base" - "cmd/go/internal/cfg" - "cmd/go/internal/gover" - "cmd/go/internal/mvs" - "cmd/go/internal/par" - "cmd/go/internal/slices" "context" "errors" "fmt" @@ -18,10 +12,17 @@ import ( "reflect" "runtime" "runtime/debug" + "slices" "strings" "sync" "sync/atomic" + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/gover" + "cmd/go/internal/mvs" + "cmd/go/internal/par" + "golang.org/x/mod/module" ) @@ -91,6 +92,15 @@ type cachedGraph struct { // accept and/or return an explicit parameter. var requirements *Requirements +func mustHaveGoRoot(roots []module.Version) { + for _, m := range roots { + if m.Path == "go" { + return + } + } + panic("go: internal error: missing go root module") +} + // newRequirements returns a new requirement set with the given root modules. // The dependencies of the roots will be loaded lazily at the first call to the // Graph method. @@ -102,6 +112,8 @@ var requirements *Requirements // If vendoring is in effect, the caller must invoke initVendor on the returned // *Requirements before any other method. func newRequirements(pruning modPruning, rootModules []module.Version, direct map[string]bool) *Requirements { + mustHaveGoRoot(rootModules) + if pruning == workspace { return &Requirements{ pruning: pruning, @@ -114,7 +126,6 @@ func newRequirements(pruning modPruning, rootModules []module.Version, direct ma if workFilePath != "" && pruning != workspace { panic("in workspace mode, but pruning is not workspace in newRequirements") } - for i, m := range rootModules { if m.Version == "" && MainModules.Contains(m.Path) { panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is a main module", i)) @@ -122,17 +133,21 @@ func newRequirements(pruning modPruning, rootModules []module.Version, direct ma if m.Path == "" || m.Version == "" { panic(fmt.Sprintf("bad requirement: rootModules[%v] = %v", i, m)) } - if i > 0 { - prev := rootModules[i-1] - if prev.Path > m.Path || (prev.Path == m.Path && gover.ModCompare(m.Path, prev.Version, m.Version) > 0) { - panic(fmt.Sprintf("newRequirements called with unsorted roots: %v", rootModules)) - } - } + } + + // Allow unsorted root modules, because go and toolchain + // are treated as the final graph roots but not trimmed from the build list, + // so they always appear at the beginning of the list. + r := slices.Clip(slices.Clone(rootModules)) + gover.ModSort(r) + if !reflect.DeepEqual(r, rootModules) { + fmt.Fprintln(os.Stderr, "RM", rootModules) + panic("unsorted") } rs := &Requirements{ pruning: pruning, - rootModules: slices.Clip(rootModules), + rootModules: rootModules, maxRootVersion: make(map[string]string, len(rootModules)), direct: direct, } @@ -157,7 +172,7 @@ func (rs *Requirements) String() string { func (rs *Requirements) initVendor(vendorList []module.Version) { rs.graphOnce.Do(func() { mg := &ModuleGraph{ - g: mvs.NewGraph(cmpVersion, MainModules.GraphRoots()), + g: mvs.NewGraph(cmpVersion, MainModules.Versions()), } if MainModules.Len() != 1 { @@ -278,6 +293,7 @@ var readModGraphDebugOnce sync.Once // Unlike LoadModGraph, readModGraph does not attempt to diagnose or update // inconsistent roots. func readModGraph(ctx context.Context, pruning modPruning, roots []module.Version, unprune map[module.Version]bool) (*ModuleGraph, error) { + mustHaveGoRoot(roots) if pruning == pruned { // Enable diagnostics for lazy module loading // (https://golang.org/ref/mod#lazy-loading) only if the module graph is @@ -301,13 +317,20 @@ func readModGraph(ctx context.Context, pruning modPruning, roots []module.Versio }) } + var graphRoots []module.Version + if inWorkspaceMode() { + graphRoots = roots + } else { + graphRoots = MainModules.Versions() + } var ( mu sync.Mutex // guards mg.g and hasError during loading hasError bool mg = &ModuleGraph{ - g: mvs.NewGraph(cmpVersion, MainModules.GraphRoots()), + g: mvs.NewGraph(cmpVersion, graphRoots), } ) + if pruning != workspace { if inWorkspaceMode() { panic("pruning is not workspace in workspace mode") @@ -380,6 +403,7 @@ func readModGraph(ctx context.Context, pruning modPruning, roots []module.Versio }) } + mustHaveGoRoot(roots) for _, m := range roots { enqueue(m, pruning) } @@ -789,6 +813,10 @@ func tidyPrunedRoots(ctx context.Context, mainModule module.Version, old *Requir roots = append(roots, module.Version{Path: "go", Version: v}) pathIsRoot["go"] = true } + if v, ok := old.rootSelected("toolchain"); ok { + roots = append(roots, module.Version{Path: "toolchain", Version: v}) + pathIsRoot["toolchain"] = true + } // We start by adding roots for every package in "all". // // Once that is done, we may still need to add more roots to cover upgraded or @@ -1254,6 +1282,11 @@ func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, old *Requ ) if v, ok := old.rootSelected("go"); ok { keep = append(keep, module.Version{Path: "go", Version: v}) + keptPath["go"] = true + } + if v, ok := old.rootSelected("toolchain"); ok { + keep = append(keep, module.Version{Path: "toolchain", Version: v}) + keptPath["toolchain"] = true } for _, pkg := range pkgs { if !pkg.fromExternalModule() { diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index efdd339998..6377e19856 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -90,6 +90,7 @@ type MainModuleSet struct { // versions are the module.Version values of each of the main modules. // For each of them, the Path fields are ordinary module paths and the Version // fields are empty strings. + // versions is clipped (len=cap). versions []module.Version // modRoot maps each module in versions to its absolute filesystem path. @@ -108,7 +109,7 @@ type MainModuleSet struct { modContainingCWD module.Version - workFileGoVersion string + workFile *modfile.WorkFile workFileReplaceMap map[module.Version]module.Version // highest replaced version of each module path; empty string for wildcard-only replacements @@ -133,18 +134,6 @@ func (mms *MainModuleSet) Versions() []module.Version { return mms.versions } -// GraphRoots returns the graph roots for the main module set. -// Callers should not modify the returned slice. -// This function is the same as Versions except that in workspace -// mode it adds a "go" version from the go.work file. -func (mms *MainModuleSet) GraphRoots() []module.Version { - versions := mms.Versions() - if inWorkspaceMode() { - versions = append(slices.Clip(versions), module.Version{Path: "go", Version: mms.GoVersion()}) - } - return versions -} - func (mms *MainModuleSet) Contains(path string) bool { if mms == nil { return false @@ -232,19 +221,49 @@ func (mms *MainModuleSet) HighestReplaced() map[string]string { // GoVersion returns the go version set on the single module, in module mode, // or the go.work file in workspace mode. func (mms *MainModuleSet) GoVersion() string { - switch { - case inWorkspaceMode(): - v := mms.workFileGoVersion - if v == "" { - // Fall back to 1.18 for go.work files. - v = "1.18" - } - return v - case mms == nil || len(mms.versions) == 0: - return "1.18" - default: - return modFileGoVersion(mms.ModFile(mms.mustGetSingleMainModule())) + if inWorkspaceMode() { + if mms.workFile != nil && mms.workFile.Go != nil { + return mms.workFile.Go.Version + } + return defaultGoWorkVersion + } + if mms != nil && len(mms.versions) == 1 { + f := mms.ModFile(mms.mustGetSingleMainModule()) + if f == nil { + // Special case: we are outside a module, like 'go run x.go'. + // Assume the local Go version. + // TODO(#49228): Clean this up; see loadModFile. + return gover.Local() + } + if f.Go != nil { + return f.Go.Version + } } + return defaultGoModVersion +} + +// Toolchain returns the toolchain set on the single module, in module mode, +// or the go.work file in workspace mode. +func (mms *MainModuleSet) Toolchain() string { + if inWorkspaceMode() { + if mms.workFile != nil && mms.workFile.Toolchain != nil { + return mms.workFile.Toolchain.Name + } + return "go" + mms.GoVersion() + } + if mms != nil && len(mms.versions) == 1 { + f := mms.ModFile(mms.mustGetSingleMainModule()) + if f == nil { + // Special case: we are outside a module, like 'go run x.go'. + // Assume the local Go version. + // TODO(#49228): Clean this up; see loadModFile. + return gover.LocalToolchain() + } + if f.Toolchain != nil { + return f.Toolchain.Name + } + } + return "go" + mms.GoVersion() } func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version { @@ -759,14 +778,18 @@ func loadModFile(ctx context.Context, opts *PackageOpts) *Requirements { // make MainModules.Len() == 0 mean that we're in module mode but not inside // any module. mainModule := module.Version{Path: "command-line-arguments"} - MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "", nil) + MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, nil) goVersion := gover.Local() rawGoVersion.Store(mainModule, goVersion) pruning := pruningForGoVersion(goVersion) if inWorkspaceMode() { pruning = workspace } - requirements = newRequirements(pruning, nil, nil) + roots := []module.Version{ + {Path: "go", Version: gover.Local()}, + {Path: "toolchain", Version: gover.LocalToolchain()}, + } + requirements = newRequirements(pruning, roots, nil) if cfg.BuildMod == "vendor" { // For issue 56536: Some users may have GOFLAGS=-mod=vendor set. // Make sure it behaves as though the fake module is vendored @@ -804,15 +827,7 @@ func loadModFile(ctx context.Context, opts *PackageOpts) *Requirements { } } - var wfGoVersion string - var wfReplace []*modfile.Replace - if workFile != nil && workFile.Go != nil { - wfGoVersion = workFile.Go.Version - } - if workFile != nil { - wfReplace = workFile.Replace - } - MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, wfGoVersion, wfReplace) + MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFile) setDefaultBuildMod() // possibly enable automatic vendoring rs := requirementsFromModFiles(ctx, workFile, modFiles, opts) @@ -869,7 +884,7 @@ func loadModFile(ctx context.Context, opts *PackageOpts) *Requirements { } } } else { - rawGoVersion.Store(mainModule, modFileGoVersion(MainModules.ModFile(mainModule))) + rawGoVersion.Store(mainModule, defaultGoModVersion) } } @@ -923,7 +938,7 @@ func CreateModFile(ctx context.Context, modPath string) { fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath) modFile := new(modfile.File) modFile.AddModuleStmt(modPath) - MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, "", nil) + MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, nil) addGoStmt(modFile, modFile.Module.Mod, gover.Local()) // Add the go directive before converted module requirements. convertedFrom, err := convertLegacyConfig(modFile, modRoot) @@ -1058,7 +1073,7 @@ func AllowMissingModuleImports() { // makeMainModules creates a MainModuleSet and associated variables according to // the given main modules. -func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFileGoVersion string, workFileReplaces []*modfile.Replace) *MainModuleSet { +func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFile *modfile.WorkFile) *MainModuleSet { for _, m := range ms { if m.Version != "" { panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m)) @@ -1066,15 +1081,19 @@ func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile } modRootContainingCWD := findModuleRoot(base.Cwd()) mainModules := &MainModuleSet{ - versions: slices.Clip(ms), - inGorootSrc: map[module.Version]bool{}, - pathPrefix: map[module.Version]string{}, - modRoot: map[module.Version]string{}, - modFiles: map[module.Version]*modfile.File{}, - indices: map[module.Version]*modFileIndex{}, - workFileGoVersion: workFileGoVersion, - workFileReplaceMap: toReplaceMap(workFileReplaces), - highestReplaced: map[string]string{}, + versions: slices.Clip(ms), + inGorootSrc: map[module.Version]bool{}, + pathPrefix: map[module.Version]string{}, + modRoot: map[module.Version]string{}, + modFiles: map[module.Version]*modfile.File{}, + indices: map[module.Version]*modFileIndex{}, + highestReplaced: map[string]string{}, + workFile: workFile, + } + var workFileReplaces []*modfile.Replace + if workFile != nil { + workFileReplaces = workFile.Replace + mainModules.workFileReplaceMap = toReplaceMap(workFile.Replace) } mainModulePaths := make(map[string]bool) for _, m := range ms { @@ -1162,17 +1181,17 @@ func requirementsFromModFiles(ctx context.Context, workFile *modfile.WorkFile, m var roots []module.Version direct := map[string]bool{} var pruning modPruning + var goVersion, toolchain string if inWorkspaceMode() { pruning = workspace roots = make([]module.Version, len(MainModules.Versions()), 2+len(MainModules.Versions())) copy(roots, MainModules.Versions()) - // Note: Ignoring the 'go' line in the main modules during mod tidy. See note below. - if workFile.Go != nil && (opts == nil || !opts.TidyGo) { - roots = append(roots, module.Version{Path: "go", Version: workFile.Go.Version}) - direct["go"] = true + if workFile.Go != nil { + goVersion = workFile.Go.Version + } + if workFile.Toolchain != nil { + toolchain = workFile.Toolchain.Name } - // Do not add toolchain to roots. - // We only want to see it in roots if it is on the command line. } else { pruning = pruningForGoVersion(MainModules.GoVersion()) if len(modFiles) != 1 { @@ -1196,16 +1215,30 @@ func requirementsFromModFiles(ctx context.Context, workFile *modfile.WorkFile, m direct[r.Mod.Path] = true } } - // Note: Ignoring the 'go' line in the main modules during mod tidy -go= - // so that we can find out the implied minimum go line from the - // dependencies instead. If it is higher than the -go= flag, we report an error in LoadPackages. - if modFile.Go != nil && (opts == nil || !opts.TidyGo) { - roots = append(roots, module.Version{Path: "go", Version: modFile.Go.Version}) - direct["go"] = true + if modFile.Go != nil { + goVersion = modFile.Go.Version + } + if modFile.Toolchain != nil { + toolchain = modFile.Toolchain.Name } - // Do not add "toolchain" to roots. - // We only want to see it in roots if it is on the command line. } + + // Add explicit go and toolchain versions, inferring as needed. + if opts != nil && opts.TidyGo { + goVersion = opts.GoVersion + } + if goVersion == "" { + goVersion = defaultGoModVersion + } + roots = append(roots, module.Version{Path: "go", Version: goVersion}) + direct["go"] = true + + if toolchain == "" { + toolchain = "go" + goVersion + } + roots = append(roots, module.Version{Path: "toolchain", Version: toolchain}) + direct["toolchain"] = true + gover.ModSort(roots) rs := newRequirements(pruning, roots, direct) return rs @@ -1517,6 +1550,8 @@ func findImportComment(file string) string { // WriteOpts control the behavior of WriteGoMod. type WriteOpts struct { + DropToolchain bool // go get toolchain@none + ExplicitToolchain bool // go get has set explicit toolchain version } // WriteGoMod writes the current build list back to go.mod. @@ -1554,11 +1589,10 @@ func commitRequirements(ctx context.Context, opts WriteOpts) (err error) { var list []*modfile.Require toolchain := "" - wroteGo := false + goVersion := "" for _, m := range requirements.rootModules { if m.Path == "go" { - wroteGo = true - forceGoStmt(modFile, mainModule, m.Version) + goVersion = m.Version continue } if m.Path == "toolchain" { @@ -1571,56 +1605,50 @@ func commitRequirements(ctx context.Context, opts WriteOpts) (err error) { }) } - var oldToolchain string - if modFile.Toolchain != nil { - oldToolchain = modFile.Toolchain.Name - } - oldToolVers := gover.FromToolchain(oldToolchain) - - // Update go and toolchain lines. - toolVers := gover.FromToolchain(toolchain) - - // Set go version if missing. - if modFile.Go == nil || modFile.Go.Version == "" { - wroteGo = true - v := modFileGoVersion(modFile) - if toolVers != "" && gover.Compare(v, toolVers) > 0 { - v = toolVers - } - modFile.AddGoStmt(v) + // Update go line. + // Every MVS graph we consider should have go as a root, + // and toolchain is either implied by the go line or explicitly a root. + if goVersion == "" { + base.Fatalf("go: internal error: missing go root module in WriteGoMod") } - if gover.Compare(modFile.Go.Version, gover.Local()) > 0 { + if gover.Compare(goVersion, gover.Local()) > 0 { // We cannot assume that we know how to update a go.mod to a newer version. - return &gover.TooNewError{What: "updating go.mod", GoVersion: modFile.Go.Version} + return &gover.TooNewError{What: "updating go.mod", GoVersion: goVersion} } - - // If we update the go line and don't have an explicit instruction - // for what to write in toolchain, make sure toolchain is at least our local version, - // for reproducibility. - if wroteGo && toolchain == "" && gover.Compare(oldToolVers, gover.Local()) < 0 && gover.Compare(modFile.Go.Version, GoStrictVersion) >= 0 { - toolVers = gover.Local() - toolchain = "go" + toolVers + wroteGo := false + if modFile.Go == nil || modFile.Go.Version != goVersion { + alwaysUpdate := cfg.BuildMod == "mod" || cfg.CmdName == "mod tidy" || cfg.CmdName == "get" + if modFile.Go == nil && goVersion == defaultGoModVersion && !alwaysUpdate { + // The go.mod has no go line, the implied default Go version matches + // what we've computed for the graph, and we're not in one of the + // traditional go.mod-updating programs, so leave it alone. + } else { + wroteGo = true + forceGoStmt(modFile, mainModule, goVersion) + } } - - // Default to old toolchain. if toolchain == "" { - toolchain = oldToolchain - toolVers = oldToolVers + toolchain = "go" + goVersion } - if toolchain == "none" { - toolchain = "" + + // For reproducibility, if we are writing a new go line, + // and we're not explicitly modifying the toolchain line with 'go get toolchain@something', + // and the toolchain running right now is newer than the current toolchain line, + // then update the toolchain line to record the newer toolchain. + toolVers := gover.FromToolchain(toolchain) + if wroteGo && !opts.DropToolchain && !opts.ExplicitToolchain && gover.Compare(gover.Local(), toolVers) > 0 { + toolchain = "go" + gover.Local() } - // Remove or add toolchain as needed. - // If toolchain is older than go version, drop it. - if toolchain == "" || gover.Compare(modFile.Go.Version, toolVers) >= 0 { + if opts.DropToolchain || toolchain == "go"+goVersion { + // go get toolchain@none or toolchain matches go line; drop it. modFile.DropToolchainStmt() } else { modFile.AddToolchainStmt(toolchain) } // Update require blocks. - if gover.Compare(modFileGoVersion(modFile), separateIndirectVersion) < 0 { + if gover.Compare(goVersion, separateIndirectVersion) < 0 { modFile.SetRequire(list) } else { modFile.SetRequireSeparateIndirect(list) diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index e0261d2c1f..b2bae6255b 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -33,6 +33,22 @@ const ( // tests outside of the main module. narrowAllVersion = "1.16" + // defaultGoModVersion is the Go version to assume for go.mod files + // that do not declare a Go version. The go command has been + // writing go versions to modules since Go 1.12, so a go.mod + // without a version is either very old or recently hand-written. + // Since we can't tell which, we have to assume it's very old. + // The semantics of the go.mod changed at Go 1.17 to support + // graph pruning. If see a go.mod without a go line, we have to + // assume Go 1.16 so that we interpret the requirements correctly. + // Note that this default must stay at Go 1.16; it cannot be moved forward. + defaultGoModVersion = "1.16" + + // defaultGoWorkVersion is the Go version to assume for go.work files + // that do not declare a Go version. Workspaces were added in Go 1.18, + // so use that. + defaultGoWorkVersion = "1.18" + // ExplicitIndirectVersion is the Go version at which a // module's go.mod file is expected to list explicit requirements on every // module that provides any package transitively imported by that module. @@ -91,27 +107,6 @@ func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfil return data, f, err } -// modFileGoVersion returns the (non-empty) Go version at which the requirements -// in modFile are interpreted, or the latest Go version if modFile is nil. -func modFileGoVersion(modFile *modfile.File) string { - if modFile == nil { - return gover.Local() - } - if modFile.Go == nil || modFile.Go.Version == "" { - // The main module necessarily has a go.mod file, and that file lacks a - // 'go' directive. The 'go' command has been adding that directive - // automatically since Go 1.12, so this module either dates to Go 1.11 or - // has been erroneously hand-edited. - // - // The semantics of the go.mod file are more-or-less the same from Go 1.11 - // through Go 1.16, changing at 1.17 to support module graph pruning. - // So even though a go.mod file without a 'go' directive is theoretically a - // Go 1.11 file, scripts may assume that it ends up as a Go 1.16 module. - return "1.16" - } - return modFile.Go.Version -} - // A modFileIndex is an index of data corresponding to a modFile // at a specific point in time. type modFileIndex struct { @@ -510,19 +505,6 @@ func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool { toolchain = modFile.Toolchain.Name } - // go.mod files did not always require a 'go' version, so do not error out - // if one is missing — we may be inside an older module - // and want to bias toward providing useful behavior. - // go lines are required if we need to declare version 1.17 or later. - // Note that as of CL 303229, a missing go directive implies 1.16, - // not “the latest Go version”. - if goV != i.goVersion && i.goVersion == "" && cfg.BuildMod != "mod" && gover.Compare(goV, "1.17") < 0 { - goV = "" - if toolchain != i.toolchain && i.toolchain == "" { - toolchain = "" - } - } - if goV != i.goVersion || toolchain != i.toolchain || len(modFile.Require) != len(i.require) || @@ -703,7 +685,8 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { if gover.IsToolchain(m.Path) { if m.Path == "go" { // Declare that go 1.2.3 requires toolchain 1.2.3, - // so that go get knows that downgrading toolchain implies downgrading go. + // so that go get knows that downgrading toolchain implies downgrading go + // and similarly upgrading go requires upgrading the toolchain. return &modFileSummary{module: m, require: []module.Version{{Path: "toolchain", Version: "go" + m.Version}}}, nil } return &modFileSummary{module: m}, nil diff --git a/src/cmd/go/testdata/script/mod_indirect_main.txt b/src/cmd/go/testdata/script/mod_indirect_main.txt index 43aaa39064..22b344f866 100644 --- a/src/cmd/go/testdata/script/mod_indirect_main.txt +++ b/src/cmd/go/testdata/script/mod_indirect_main.txt @@ -62,6 +62,7 @@ golang.org/issue/pkg v0.1.0 => ./pkg-v0.1.0 -- graph.txt -- golang.org/issue/root go@1.12 golang.org/issue/root golang.org/issue/mirror@v0.1.0 +golang.org/issue/root toolchain@go1.12 go@1.12 toolchain@go1.12 golang.org/issue/mirror@v0.1.0 golang.org/issue/root@v0.1.0 golang.org/issue/root@v0.1.0 golang.org/issue/pkg@v0.1.0 diff --git a/src/cmd/go/testdata/script/mod_skip_write.txt b/src/cmd/go/testdata/script/mod_skip_write.txt index 14b1c3728e..1850cdf5fd 100644 --- a/src/cmd/go/testdata/script/mod_skip_write.txt +++ b/src/cmd/go/testdata/script/mod_skip_write.txt @@ -84,6 +84,7 @@ m golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c m rsc.io/quote@v1.5.2 m rsc.io/sampler@v1.3.0 m rsc.io/testonly@v1.0.0 +m toolchain@go1.18 go@1.18 toolchain@go1.18 rsc.io/quote@v1.5.2 rsc.io/sampler@v1.3.0 rsc.io/sampler@v1.3.0 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c