mirror of
https://github.com/golang/go
synced 2024-11-11 19:21:37 -07:00
cmd/go: smooth out upgrade paths for lazy loading
This change adds two possible upgrade paths for lazy loading: 1. Run 'go mod tidy -go=1.17'. 2. Starting in a module with no existing 'go' directive, run any 'go' command that updates the go.mod file. In the latter case, commands other than 'go mod tidy' may leave the go.mod file *very* untidy if it had non-trivial dependencies. (The 'go' invocation will promote all implicit eager dependencies to explicit lazy ones, which preserves the original module graph — most of which is not actually relevant.) 'go mod tidy -go=1.17' can be used to enable lazy loading without accidentally downgrading existing transitive dependencies. 'go mod tidy -go=1.16' can be used to disable lazy loading and clear away redundant roots in a single step (if reducing the go version), or to prune away dependencies of tests-of-external-tests (if increasing the go version). 'go mod tidy -go=1.15' can be used to add dependencies of tests-of-external-tests, although there isn't much point to that. DO NOT MERGE This change still needs an explicit test and a release note. Fixes #45094 For #36460 Change-Id: I68f057e39489dfd6a667cd11dc1e320c1ee1aec1 Reviewed-on: https://go-review.googlesource.com/c/go/+/315210 Trust: Bryan C. Mills <bcmills@google.com> Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
0e315ad79a
commit
7dedc237c5
@ -58,6 +58,17 @@ Do not send CLs removing the interior tags from such phrases.
|
|||||||
<!-- TODO(bcmills): replace the design-doc link with proper documentation. -->
|
<!-- TODO(bcmills): replace the design-doc link with proper documentation. -->
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><!-- golang.org/issue/45094 --> To facilitate the upgrade to lazy loading,
|
||||||
|
the <code>go</code> <code>mod</code> <code>tidy</code> subcommand now supports
|
||||||
|
a <code>-go</code> flag to set or change the <code>go</code> version in
|
||||||
|
the <code>go.mod</code> file. To enable lazy loading for an existing module
|
||||||
|
without changing the selected versions of its dependencies, run:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
go mod tidy -go=1.17
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h4 id="module-deprecation-comments">Module deprecation comments</h4>
|
<h4 id="module-deprecation-comments">Module deprecation comments</h4>
|
||||||
|
|
||||||
<p><!-- golang.org/issue/40357 -->
|
<p><!-- golang.org/issue/40357 -->
|
||||||
|
@ -1221,7 +1221,7 @@
|
|||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
//
|
//
|
||||||
// go mod tidy [-e] [-v]
|
// go mod tidy [-e] [-v] [-go=version]
|
||||||
//
|
//
|
||||||
// Tidy makes sure go.mod matches the source code in the module.
|
// Tidy makes sure go.mod matches the source code in the module.
|
||||||
// It adds any missing modules necessary to build the current module's
|
// It adds any missing modules necessary to build the current module's
|
||||||
@ -1235,6 +1235,12 @@
|
|||||||
// The -e flag causes tidy to attempt to proceed despite errors
|
// The -e flag causes tidy to attempt to proceed despite errors
|
||||||
// encountered while loading packages.
|
// encountered while loading packages.
|
||||||
//
|
//
|
||||||
|
// The -go flag causes tidy to update the 'go' directive in the go.mod
|
||||||
|
// file to the given version, which may change which module dependencies
|
||||||
|
// are retained as explicit requirements in the go.mod file.
|
||||||
|
// (Go versions 1.17 and higher retain more requirements in order to
|
||||||
|
// support lazy module loading.)
|
||||||
|
//
|
||||||
// See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
|
// See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -12,10 +12,12 @@ import (
|
|||||||
"cmd/go/internal/imports"
|
"cmd/go/internal/imports"
|
||||||
"cmd/go/internal/modload"
|
"cmd/go/internal/modload"
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdTidy = &base.Command{
|
var cmdTidy = &base.Command{
|
||||||
UsageLine: "go mod tidy [-e] [-v]",
|
UsageLine: "go mod tidy [-e] [-v] [-go=version]",
|
||||||
Short: "add missing and remove unused modules",
|
Short: "add missing and remove unused modules",
|
||||||
Long: `
|
Long: `
|
||||||
Tidy makes sure go.mod matches the source code in the module.
|
Tidy makes sure go.mod matches the source code in the module.
|
||||||
@ -30,16 +32,26 @@ to standard error.
|
|||||||
The -e flag causes tidy to attempt to proceed despite errors
|
The -e flag causes tidy to attempt to proceed despite errors
|
||||||
encountered while loading packages.
|
encountered while loading packages.
|
||||||
|
|
||||||
|
The -go flag causes tidy to update the 'go' directive in the go.mod
|
||||||
|
file to the given version, which may change which module dependencies
|
||||||
|
are retained as explicit requirements in the go.mod file.
|
||||||
|
(Go versions 1.17 and higher retain more requirements in order to
|
||||||
|
support lazy module loading.)
|
||||||
|
|
||||||
See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
|
See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
|
||||||
`,
|
`,
|
||||||
Run: runTidy,
|
Run: runTidy,
|
||||||
}
|
}
|
||||||
|
|
||||||
var tidyE bool // if true, report errors but proceed anyway.
|
var (
|
||||||
|
tidyE bool // if true, report errors but proceed anyway.
|
||||||
|
tidyGo string // go version to write to the tidied go.mod file (toggles lazy loading)
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
||||||
cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
|
cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
|
||||||
|
cmdTidy.Flag.StringVar(&tidyGo, "go", "", "")
|
||||||
base.AddModCommonFlags(&cmdTidy.Flag)
|
base.AddModCommonFlags(&cmdTidy.Flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +60,12 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
|
|||||||
base.Fatalf("go mod tidy: no arguments allowed")
|
base.Fatalf("go mod tidy: no arguments allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tidyGo != "" {
|
||||||
|
if !modfile.GoVersionRE.MatchString(tidyGo) {
|
||||||
|
base.Fatalf(`go mod: invalid -go option %q; expecting something like "-go 1.17"`, tidyGo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tidy aims to make 'go test' reproducible for any package in 'all', so we
|
// Tidy aims to make 'go test' reproducible for any package in 'all', so we
|
||||||
// need to include test dependencies. For modules that specify go 1.15 or
|
// need to include test dependencies. For modules that specify go 1.15 or
|
||||||
// earlier this is a no-op (because 'all' saturates transitive test
|
// earlier this is a no-op (because 'all' saturates transitive test
|
||||||
@ -62,6 +80,7 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
|
|||||||
modload.RootMode = modload.NeedRoot
|
modload.RootMode = modload.NeedRoot
|
||||||
|
|
||||||
modload.LoadPackages(ctx, modload.PackageOpts{
|
modload.LoadPackages(ctx, modload.PackageOpts{
|
||||||
|
GoVersion: tidyGo,
|
||||||
Tags: imports.AnyTags(),
|
Tags: imports.AnyTags(),
|
||||||
Tidy: true,
|
Tidy: true,
|
||||||
VendorModulesInGOROOTSrc: true,
|
VendorModulesInGOROOTSrc: true,
|
||||||
|
@ -259,8 +259,8 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
|
|||||||
if m.GoVersion == "" && checksumOk("/go.mod") {
|
if m.GoVersion == "" && checksumOk("/go.mod") {
|
||||||
// Load the go.mod file to determine the Go version, since it hasn't
|
// Load the go.mod file to determine the Go version, since it hasn't
|
||||||
// already been populated from rawGoVersion.
|
// already been populated from rawGoVersion.
|
||||||
if summary, err := rawGoModSummary(mod); err == nil && summary.goVersionV != "" {
|
if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" {
|
||||||
m.GoVersion = summary.goVersionV[1:]
|
m.GoVersion = summary.goVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (
|
|||||||
// sufficient to build the packages it contains. We must load its full
|
// sufficient to build the packages it contains. We must load its full
|
||||||
// transitive dependency graph to be sure that we see all relevant
|
// transitive dependency graph to be sure that we see all relevant
|
||||||
// dependencies.
|
// dependencies.
|
||||||
if depth == eager || summary.depth() == eager {
|
if depth == eager || summary.depth == eager {
|
||||||
for _, r := range summary.require {
|
for _, r := range summary.require {
|
||||||
enqueue(r, eager)
|
enqueue(r, eager)
|
||||||
}
|
}
|
||||||
@ -393,7 +393,7 @@ func LoadModGraph(ctx context.Context) *ModuleGraph {
|
|||||||
base.Fatalf("go: %v", err)
|
base.Fatalf("go: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commitRequirements(ctx, rs)
|
commitRequirements(ctx, modFileGoVersion(), rs)
|
||||||
return mg
|
return mg
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,7 +459,7 @@ func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (chang
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
commitRequirements(ctx, rs)
|
commitRequirements(ctx, modFileGoVersion(), rs)
|
||||||
return changed, err
|
return changed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -943,10 +943,40 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return rs, err
|
return rs, err
|
||||||
}
|
}
|
||||||
if reflect.DeepEqual(min, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
|
if rs.depth == eager && reflect.DeepEqual(min, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
|
||||||
// The root set is unchanged, so keep rs to preserve its cached ModuleGraph
|
// The root set is unchanged and rs was already eager, so keep rs to
|
||||||
// (if any).
|
// preserve its cached ModuleGraph (if any).
|
||||||
return rs, nil
|
return rs, nil
|
||||||
}
|
}
|
||||||
return newRequirements(rs.depth, min, direct), nil
|
return newRequirements(eager, min, direct), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertDepth returns a version of rs with the given depth.
|
||||||
|
// If rs already has the given depth, convertDepth returns rs unmodified.
|
||||||
|
func convertDepth(ctx context.Context, rs *Requirements, depth modDepth) (*Requirements, error) {
|
||||||
|
if rs.depth == depth {
|
||||||
|
return rs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if depth == eager {
|
||||||
|
// We are converting a lazy module to an eager one. The roots of an eager
|
||||||
|
// module graph are a superset of the roots of a lazy graph, so we don't
|
||||||
|
// need to add any new roots — we just need to prune away the ones that are
|
||||||
|
// redundant given eager loading, which is exactly what updateEagerRoots
|
||||||
|
// does.
|
||||||
|
return updateEagerRoots(ctx, rs.direct, rs, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are converting an eager module to a lazy one. The module graph of an
|
||||||
|
// eager module includes the transitive dependencies of every module in the
|
||||||
|
// build list.
|
||||||
|
//
|
||||||
|
// Hey, we can express that as a lazy root set! “Include the transitive
|
||||||
|
// dependencies of every module in the build list” is exactly what happens in
|
||||||
|
// a lazy module if we promote every module in the build list to a root!
|
||||||
|
mg, err := rs.Graph(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return rs, err
|
||||||
|
}
|
||||||
|
return newRequirements(lazy, mg.BuildList()[1:], rs.direct), nil
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if summary.depth() == eager {
|
if summary.depth == eager {
|
||||||
// For efficiency, we'll load all of the eager upgrades as one big
|
// For efficiency, we'll load all of the eager upgrades as one big
|
||||||
// graph, rather than loading the (potentially-overlapping) subgraph for
|
// graph, rather than loading the (potentially-overlapping) subgraph for
|
||||||
// each upgrade individually.
|
// each upgrade individually.
|
||||||
@ -522,7 +522,7 @@ func (l *versionLimiter) check(m module.Version, depth modDepth) dqState {
|
|||||||
return l.disqualify(m, dqState{err: err})
|
return l.disqualify(m, dqState{err: err})
|
||||||
}
|
}
|
||||||
|
|
||||||
if summary.depth() == eager {
|
if summary.depth == eager {
|
||||||
depth = eager
|
depth = eager
|
||||||
}
|
}
|
||||||
for _, r := range summary.require {
|
for _, r := range summary.require {
|
||||||
|
@ -381,7 +381,7 @@ var errGoModDirty error = goModDirtyError{}
|
|||||||
func LoadModFile(ctx context.Context) *Requirements {
|
func LoadModFile(ctx context.Context) *Requirements {
|
||||||
rs, needCommit := loadModFile(ctx)
|
rs, needCommit := loadModFile(ctx)
|
||||||
if needCommit {
|
if needCommit {
|
||||||
commitRequirements(ctx, rs)
|
commitRequirements(ctx, modFileGoVersion(), rs)
|
||||||
}
|
}
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
@ -401,8 +401,9 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
|||||||
if modRoot == "" {
|
if modRoot == "" {
|
||||||
Target = module.Version{Path: "command-line-arguments"}
|
Target = module.Version{Path: "command-line-arguments"}
|
||||||
targetPrefix = "command-line-arguments"
|
targetPrefix = "command-line-arguments"
|
||||||
rawGoVersion.Store(Target, latestGoVersion())
|
goVersion := latestGoVersion()
|
||||||
requirements = newRequirements(index.depth(), nil, nil)
|
rawGoVersion.Store(Target, goVersion)
|
||||||
|
requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
|
||||||
return requirements, false
|
return requirements, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +433,7 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDefaultBuildMod() // possibly enable automatic vendoring
|
setDefaultBuildMod() // possibly enable automatic vendoring
|
||||||
rs = requirementsFromModFile(ctx, f)
|
rs = requirementsFromModFile(ctx)
|
||||||
|
|
||||||
if cfg.BuildMod == "vendor" {
|
if cfg.BuildMod == "vendor" {
|
||||||
readVendorList()
|
readVendorList()
|
||||||
@ -440,27 +441,23 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
|||||||
rs.initVendor(vendorList)
|
rs.initVendor(vendorList)
|
||||||
}
|
}
|
||||||
if index.goVersionV == "" {
|
if index.goVersionV == "" {
|
||||||
// The main module necessarily has a go.mod file, and that file lacks a
|
// TODO(#45551): Do something more principled instead of checking
|
||||||
// 'go' directive. The 'go' command has been adding that directive
|
// cfg.CmdName directly here.
|
||||||
// automatically since Go 1.12, so this module either dates to Go 1.11 or
|
if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
|
||||||
// has been erroneously hand-edited.
|
addGoStmt(latestGoVersion())
|
||||||
//
|
if go117EnableLazyLoading {
|
||||||
// The semantics of the go.mod file are more-or-less the same from Go 1.11
|
// We need to add a 'go' version to the go.mod file, but we must assume
|
||||||
// through Go 1.16, changing at 1.17 for lazy loading. So even though a
|
// that its existing contents match something between Go 1.11 and 1.16.
|
||||||
// go.mod file without a 'go' directive is theoretically a Go 1.11 file,
|
// Go 1.11 through 1.16 have eager requirements, but the latest Go
|
||||||
// scripts may assume that it ends up as a Go 1.16 module. We can't go
|
// version uses lazy requirements instead — so we need to cnvert the
|
||||||
// higher than that, because we don't know which semantics the user intends.
|
// requirements to be lazy.
|
||||||
//
|
rs, err = convertDepth(ctx, rs, lazy)
|
||||||
// (Note that 'go mod init' always adds the latest version, so scripts that
|
if err != nil {
|
||||||
// use 'go mod init' will result in current-version modules instead of Go
|
base.Fatalf("go: %v", err)
|
||||||
// 1.16 modules.)
|
}
|
||||||
//
|
}
|
||||||
// If we are able to modify the go.mod file, we will add a 'go' directive
|
|
||||||
// to at least make the situation explicit going forward.
|
|
||||||
if cfg.BuildMod == "mod" {
|
|
||||||
addGoStmt("1.16")
|
|
||||||
} else {
|
} else {
|
||||||
rawGoVersion.Store(Target, "1.16")
|
rawGoVersion.Store(Target, modFileGoVersion())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +506,7 @@ func CreateModFile(ctx context.Context, modPath string) {
|
|||||||
base.Fatalf("go: %v", err)
|
base.Fatalf("go: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commitRequirements(ctx, requirementsFromModFile(ctx, modFile))
|
commitRequirements(ctx, modFileGoVersion(), requirementsFromModFile(ctx))
|
||||||
|
|
||||||
// Suggest running 'go mod tidy' unless the project is empty. Even if we
|
// Suggest running 'go mod tidy' unless the project is empty. Even if we
|
||||||
// imported all the correct requirements above, we're probably missing
|
// imported all the correct requirements above, we're probably missing
|
||||||
@ -661,12 +658,13 @@ func initTarget(m module.Version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requirementsFromModFile returns the set of non-excluded requirements from f.
|
// requirementsFromModFile returns the set of non-excluded requirements from
|
||||||
func requirementsFromModFile(ctx context.Context, f *modfile.File) *Requirements {
|
// the global modFile.
|
||||||
roots := make([]module.Version, 0, len(f.Require))
|
func requirementsFromModFile(ctx context.Context) *Requirements {
|
||||||
|
roots := make([]module.Version, 0, len(modFile.Require))
|
||||||
mPathCount := map[string]int{Target.Path: 1}
|
mPathCount := map[string]int{Target.Path: 1}
|
||||||
direct := map[string]bool{}
|
direct := map[string]bool{}
|
||||||
for _, r := range f.Require {
|
for _, r := range modFile.Require {
|
||||||
if index != nil && index.exclude[r.Mod] {
|
if index != nil && index.exclude[r.Mod] {
|
||||||
if cfg.BuildMod == "mod" {
|
if cfg.BuildMod == "mod" {
|
||||||
fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
|
fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
|
||||||
@ -683,7 +681,7 @@ func requirementsFromModFile(ctx context.Context, f *modfile.File) *Requirements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.Sort(roots)
|
module.Sort(roots)
|
||||||
rs := newRequirements(index.depth(), roots, direct)
|
rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct)
|
||||||
|
|
||||||
// If any module path appears more than once in the roots, we know that the
|
// If any module path appears more than once in the roots, we know that the
|
||||||
// go.mod file needs to be updated even though we have not yet loaded any
|
// go.mod file needs to be updated even though we have not yet loaded any
|
||||||
@ -988,12 +986,12 @@ func WriteGoMod(ctx context.Context) {
|
|||||||
if !allowWriteGoMod {
|
if !allowWriteGoMod {
|
||||||
panic("WriteGoMod called while disallowed")
|
panic("WriteGoMod called while disallowed")
|
||||||
}
|
}
|
||||||
commitRequirements(ctx, LoadModFile(ctx))
|
commitRequirements(ctx, modFileGoVersion(), LoadModFile(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// commitRequirements writes sets the global requirements variable to rs and
|
// commitRequirements writes sets the global requirements variable to rs and
|
||||||
// writes its contents back to the go.mod file on disk.
|
// writes its contents back to the go.mod file on disk.
|
||||||
func commitRequirements(ctx context.Context, rs *Requirements) {
|
func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) {
|
||||||
requirements = rs
|
requirements = rs
|
||||||
|
|
||||||
if !allowWriteGoMod {
|
if !allowWriteGoMod {
|
||||||
@ -1014,6 +1012,9 @@ func commitRequirements(ctx context.Context, rs *Requirements) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
modFile.SetRequire(list)
|
modFile.SetRequire(list)
|
||||||
|
if goVersion != "" {
|
||||||
|
modFile.AddGoStmt(goVersion)
|
||||||
|
}
|
||||||
modFile.Cleanup()
|
modFile.Cleanup()
|
||||||
|
|
||||||
dirty := index.modFileIsDirty(modFile)
|
dirty := index.modFileIsDirty(modFile)
|
||||||
|
@ -72,7 +72,7 @@ func ListModules(ctx context.Context, args []string, mode ListMode) ([]*modinfo.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
commitRequirements(ctx, rs)
|
commitRequirements(ctx, modFileGoVersion(), rs)
|
||||||
}
|
}
|
||||||
return mods, err
|
return mods, err
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ package modload
|
|||||||
// Because "go mod vendor" prunes out the tests of vendored packages, the
|
// Because "go mod vendor" prunes out the tests of vendored packages, the
|
||||||
// behavior of the "all" pattern with -mod=vendor in Go 1.11–1.15 is the same
|
// behavior of the "all" pattern with -mod=vendor in Go 1.11–1.15 is the same
|
||||||
// as the "all" pattern (regardless of the -mod flag) in 1.16+.
|
// as the "all" pattern (regardless of the -mod flag) in 1.16+.
|
||||||
// The allClosesOverTests parameter to the loader indicates whether the "all"
|
// The loader uses the GoVersion parameter to determine whether the "all"
|
||||||
// pattern should close over tests (as in Go 1.11–1.15) or stop at only those
|
// pattern should close over tests (as in Go 1.11–1.15) or stop at only those
|
||||||
// packages transitively imported by the packages and tests in the main module
|
// packages transitively imported by the packages and tests in the main module
|
||||||
// ("all" in Go 1.16+ and "go mod vendor" in Go 1.11+).
|
// ("all" in Go 1.16+ and "go mod vendor" in Go 1.11+).
|
||||||
@ -121,6 +121,7 @@ import (
|
|||||||
"cmd/go/internal/str"
|
"cmd/go/internal/str"
|
||||||
|
|
||||||
"golang.org/x/mod/module"
|
"golang.org/x/mod/module"
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// loaded is the most recently-used package loader.
|
// loaded is the most recently-used package loader.
|
||||||
@ -133,6 +134,14 @@ var loaded *loader
|
|||||||
|
|
||||||
// PackageOpts control the behavior of the LoadPackages function.
|
// PackageOpts control the behavior of the LoadPackages function.
|
||||||
type PackageOpts struct {
|
type PackageOpts struct {
|
||||||
|
// GoVersion is the Go version to which the go.mod file should be updated
|
||||||
|
// after packages have been loaded.
|
||||||
|
//
|
||||||
|
// An empty GoVersion means to use the Go version already specified in the
|
||||||
|
// main module's go.mod file, or the latest Go version if there is no main
|
||||||
|
// module.
|
||||||
|
GoVersion string
|
||||||
|
|
||||||
// Tags are the build tags in effect (as interpreted by the
|
// Tags are the build tags in effect (as interpreted by the
|
||||||
// cmd/go/internal/imports package).
|
// cmd/go/internal/imports package).
|
||||||
// If nil, treated as equivalent to imports.Tags().
|
// If nil, treated as equivalent to imports.Tags().
|
||||||
@ -305,12 +314,15 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
|
|||||||
|
|
||||||
initialRS, _ := loadModFile(ctx) // Ignore needCommit — we're going to commit at the end regardless.
|
initialRS, _ := loadModFile(ctx) // Ignore needCommit — we're going to commit at the end regardless.
|
||||||
|
|
||||||
|
if opts.GoVersion == "" {
|
||||||
|
opts.GoVersion = modFileGoVersion()
|
||||||
|
}
|
||||||
|
|
||||||
ld := loadFromRoots(ctx, loaderParams{
|
ld := loadFromRoots(ctx, loaderParams{
|
||||||
PackageOpts: opts,
|
PackageOpts: opts,
|
||||||
requirements: initialRS,
|
requirements: initialRS,
|
||||||
|
|
||||||
allClosesOverTests: index.allPatternClosesOverTests() && !opts.UseVendorAll,
|
allPatternIsRoot: allPatternIsRoot,
|
||||||
allPatternIsRoot: allPatternIsRoot,
|
|
||||||
|
|
||||||
listRoots: func(rs *Requirements) (roots []string) {
|
listRoots: func(rs *Requirements) (roots []string) {
|
||||||
updateMatches(rs, nil)
|
updateMatches(rs, nil)
|
||||||
@ -368,7 +380,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
|
|||||||
|
|
||||||
// Success! Update go.mod and go.sum (if needed) and return the results.
|
// Success! Update go.mod and go.sum (if needed) and return the results.
|
||||||
loaded = ld
|
loaded = ld
|
||||||
commitRequirements(ctx, loaded.requirements)
|
commitRequirements(ctx, opts.GoVersion, loaded.requirements)
|
||||||
|
|
||||||
for _, pkg := range ld.pkgs {
|
for _, pkg := range ld.pkgs {
|
||||||
if !pkg.isTest() {
|
if !pkg.isTest() {
|
||||||
@ -593,21 +605,22 @@ func ImportFromFiles(ctx context.Context, gofiles []string) {
|
|||||||
base.Fatalf("go: %v", err)
|
base.Fatalf("go: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goVersion := modFileGoVersion()
|
||||||
loaded = loadFromRoots(ctx, loaderParams{
|
loaded = loadFromRoots(ctx, loaderParams{
|
||||||
PackageOpts: PackageOpts{
|
PackageOpts: PackageOpts{
|
||||||
|
GoVersion: goVersion,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
ResolveMissingImports: true,
|
ResolveMissingImports: true,
|
||||||
SilencePackageErrors: true,
|
SilencePackageErrors: true,
|
||||||
},
|
},
|
||||||
requirements: rs,
|
requirements: rs,
|
||||||
allClosesOverTests: index.allPatternClosesOverTests(),
|
|
||||||
listRoots: func(*Requirements) (roots []string) {
|
listRoots: func(*Requirements) (roots []string) {
|
||||||
roots = append(roots, imports...)
|
roots = append(roots, imports...)
|
||||||
roots = append(roots, testImports...)
|
roots = append(roots, testImports...)
|
||||||
return roots
|
return roots
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
commitRequirements(ctx, loaded.requirements)
|
commitRequirements(ctx, goVersion, loaded.requirements)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DirImportPath returns the effective import path for dir,
|
// DirImportPath returns the effective import path for dir,
|
||||||
@ -743,6 +756,12 @@ func Lookup(parentPath string, parentIsStd bool, path string) (dir, realPath str
|
|||||||
type loader struct {
|
type loader struct {
|
||||||
loaderParams
|
loaderParams
|
||||||
|
|
||||||
|
// allClosesOverTests indicates whether the "all" pattern includes
|
||||||
|
// dependencies of tests outside the main module (as in Go 1.11–1.15).
|
||||||
|
// (Otherwise — as in Go 1.16+ — the "all" pattern includes only the packages
|
||||||
|
// transitively *imported by* the packages and tests in the main module.)
|
||||||
|
allClosesOverTests bool
|
||||||
|
|
||||||
work *par.Queue
|
work *par.Queue
|
||||||
|
|
||||||
// reset on each iteration
|
// reset on each iteration
|
||||||
@ -757,8 +776,7 @@ type loaderParams struct {
|
|||||||
PackageOpts
|
PackageOpts
|
||||||
requirements *Requirements
|
requirements *Requirements
|
||||||
|
|
||||||
allClosesOverTests bool // Does the "all" pattern include the transitive closure of tests of packages in "all"?
|
allPatternIsRoot bool // Is the "all" pattern an additional root?
|
||||||
allPatternIsRoot bool // Is the "all" pattern an additional root?
|
|
||||||
|
|
||||||
listRoots func(rs *Requirements) []string
|
listRoots func(rs *Requirements) []string
|
||||||
}
|
}
|
||||||
@ -903,6 +921,22 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
|
|||||||
work: par.NewQueue(runtime.GOMAXPROCS(0)),
|
work: par.NewQueue(runtime.GOMAXPROCS(0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.GoVersion != "" {
|
||||||
|
if semver.Compare("v"+params.GoVersion, narrowAllVersionV) < 0 && !ld.UseVendorAll {
|
||||||
|
// The module's go version explicitly predates the change in "all" for lazy
|
||||||
|
// loading, so continue to use the older interpretation.
|
||||||
|
// (If params.GoVersion is empty, we are probably not in any module at all
|
||||||
|
// and should use the latest semantics.)
|
||||||
|
ld.allClosesOverTests = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ld.requirements, err = convertDepth(ctx, ld.requirements, modDepthFromGoVersion(params.GoVersion))
|
||||||
|
if err != nil {
|
||||||
|
ld.errorf("go: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ld.requirements.depth == eager {
|
if ld.requirements.depth == eager {
|
||||||
var err error
|
var err error
|
||||||
ld.requirements, _, err = expandGraph(ctx, ld.requirements)
|
ld.requirements, _, err = expandGraph(ctx, ld.requirements)
|
||||||
|
@ -51,6 +51,27 @@ const (
|
|||||||
|
|
||||||
var modFile *modfile.File
|
var modFile *modfile.File
|
||||||
|
|
||||||
|
// modFileGoVersion returns the (non-empty) Go version at which the requirements
|
||||||
|
// in modFile are intepreted, or the latest Go version if modFile is nil.
|
||||||
|
func modFileGoVersion() string {
|
||||||
|
if modFile == nil {
|
||||||
|
return latestGoVersion()
|
||||||
|
}
|
||||||
|
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 for lazy loading. 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
|
// A modFileIndex is an index of data corresponding to a modFile
|
||||||
// at a specific point in time.
|
// at a specific point in time.
|
||||||
type modFileIndex struct {
|
type modFileIndex struct {
|
||||||
@ -79,6 +100,16 @@ const (
|
|||||||
eager // load all transitive dependencies eagerly
|
eager // load all transitive dependencies eagerly
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func modDepthFromGoVersion(goVersion string) modDepth {
|
||||||
|
if !go117EnableLazyLoading {
|
||||||
|
return eager
|
||||||
|
}
|
||||||
|
if semver.Compare("v"+goVersion, lazyLoadingVersionV) < 0 {
|
||||||
|
return eager
|
||||||
|
}
|
||||||
|
return lazy
|
||||||
|
}
|
||||||
|
|
||||||
// CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by
|
// CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by
|
||||||
// the main module's go.mod or retracted by its author. Most version queries use
|
// the main module's go.mod or retracted by its author. Most version queries use
|
||||||
// this to filter out versions that should not be used.
|
// this to filter out versions that should not be used.
|
||||||
@ -344,32 +375,6 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// allPatternClosesOverTests reports whether the "all" pattern includes
|
|
||||||
// dependencies of tests outside the main module (as in Go 1.11–1.15).
|
|
||||||
// (Otherwise — as in Go 1.16+ — the "all" pattern includes only the packages
|
|
||||||
// transitively *imported by* the packages and tests in the main module.)
|
|
||||||
func (i *modFileIndex) allPatternClosesOverTests() bool {
|
|
||||||
if i != nil && i.goVersionV != "" && semver.Compare(i.goVersionV, narrowAllVersionV) < 0 {
|
|
||||||
// The module explicitly predates the change in "all" for lazy loading, so
|
|
||||||
// continue to use the older interpretation. (If i == nil, we not in any
|
|
||||||
// module at all and should use the latest semantics.)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// depth reports the modDepth indicated by the indexed go.mod file,
|
|
||||||
// or lazy if the go.mod file has not been indexed.
|
|
||||||
func (i *modFileIndex) depth() modDepth {
|
|
||||||
if !go117EnableLazyLoading {
|
|
||||||
return eager
|
|
||||||
}
|
|
||||||
if i != nil && semver.Compare(i.goVersionV, lazyLoadingVersionV) < 0 {
|
|
||||||
return eager
|
|
||||||
}
|
|
||||||
return lazy
|
|
||||||
}
|
|
||||||
|
|
||||||
// modFileIsDirty reports whether the go.mod file differs meaningfully
|
// modFileIsDirty reports whether the go.mod file differs meaningfully
|
||||||
// from what was indexed.
|
// from what was indexed.
|
||||||
// If modFile has been changed (even cosmetically) since it was first read,
|
// If modFile has been changed (even cosmetically) since it was first read,
|
||||||
@ -396,7 +401,7 @@ func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if "v"+modFile.Go.Version != i.goVersionV {
|
} else if "v"+modFile.Go.Version != i.goVersionV {
|
||||||
if i.goVersionV == "" && cfg.BuildMod == "readonly" {
|
if i.goVersionV == "" && cfg.BuildMod != "mod" {
|
||||||
// go.mod files did not always require a 'go' version, so do not error out
|
// 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 in the module
|
// if one is missing — we may be inside an older module in the module
|
||||||
// cache, and should bias toward providing useful behavior.
|
// cache, and should bias toward providing useful behavior.
|
||||||
@ -452,7 +457,8 @@ var rawGoVersion sync.Map // map[module.Version]string
|
|||||||
// module.
|
// module.
|
||||||
type modFileSummary struct {
|
type modFileSummary struct {
|
||||||
module module.Version
|
module module.Version
|
||||||
goVersionV string // GoVersion with "v" prefix
|
goVersion string
|
||||||
|
depth modDepth
|
||||||
require []module.Version
|
require []module.Version
|
||||||
retract []retraction
|
retract []retraction
|
||||||
deprecated string
|
deprecated string
|
||||||
@ -465,20 +471,6 @@ type retraction struct {
|
|||||||
Rationale string
|
Rationale string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *modFileSummary) depth() modDepth {
|
|
||||||
if !go117EnableLazyLoading {
|
|
||||||
return eager
|
|
||||||
}
|
|
||||||
// The 'go' command fills in the 'go' directive automatically, so an empty
|
|
||||||
// goVersionV in a dependency implies either Go 1.11 (eager loading) or no
|
|
||||||
// explicit go.mod file at all (no difference between eager and lazy because
|
|
||||||
// the module doesn't specify any requirements at all).
|
|
||||||
if s.goVersionV == "" || semver.Compare(s.goVersionV, lazyLoadingVersionV) < 0 {
|
|
||||||
return eager
|
|
||||||
}
|
|
||||||
return lazy
|
|
||||||
}
|
|
||||||
|
|
||||||
// goModSummary returns a summary of the go.mod file for module m,
|
// goModSummary returns a summary of the go.mod file for module m,
|
||||||
// taking into account any replacements for m, exclusions of its dependencies,
|
// taking into account any replacements for m, exclusions of its dependencies,
|
||||||
// and/or vendoring.
|
// and/or vendoring.
|
||||||
@ -638,7 +630,10 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
|
|||||||
}
|
}
|
||||||
if f.Go != nil && f.Go.Version != "" {
|
if f.Go != nil && f.Go.Version != "" {
|
||||||
rawGoVersion.LoadOrStore(m, f.Go.Version)
|
rawGoVersion.LoadOrStore(m, f.Go.Version)
|
||||||
summary.goVersionV = "v" + f.Go.Version
|
summary.goVersion = f.Go.Version
|
||||||
|
summary.depth = modDepthFromGoVersion(f.Go.Version)
|
||||||
|
} else {
|
||||||
|
summary.depth = eager
|
||||||
}
|
}
|
||||||
if len(f.Require) > 0 {
|
if len(f.Require) > 0 {
|
||||||
summary.require = make([]module.Version, 0, len(f.Require))
|
summary.require = make([]module.Version, 0, len(f.Require))
|
||||||
|
@ -30,12 +30,26 @@ cmp go.mod go.mod.orig
|
|||||||
stderr 'cannot find package "\." in:\n\t.*[/\\]vendor[/\\]example.com[/\\]badedit$'
|
stderr 'cannot find package "\." in:\n\t.*[/\\]vendor[/\\]example.com[/\\]badedit$'
|
||||||
|
|
||||||
# When we set -mod=mod, the go version should be updated immediately,
|
# When we set -mod=mod, the go version should be updated immediately,
|
||||||
# to Go 1.16 (not the current version).
|
# to the current version, converting the requirements from eager to lazy.
|
||||||
|
#
|
||||||
|
# Since we don't know which requirements are actually relevant to the main
|
||||||
|
# module, all requirements are added as roots, making the requirements untidy.
|
||||||
|
|
||||||
go list -mod=mod all
|
go list -mod=mod all
|
||||||
! stdout '^example.com/testdep$'
|
! stdout '^example.com/testdep$'
|
||||||
cmp stdout list-1.txt
|
cmp stdout list-1.txt
|
||||||
cmp go.mod go.mod.want
|
cmpenv go.mod go.mod.untidy
|
||||||
|
|
||||||
|
go mod tidy
|
||||||
|
cmpenv go.mod go.mod.tidy
|
||||||
|
|
||||||
|
# On the other hand, if we jump straight to 'go mod tidy',
|
||||||
|
# the requirements remain tidy from the start.
|
||||||
|
|
||||||
|
cp go.mod.orig go.mod
|
||||||
|
go mod tidy
|
||||||
|
cmpenv go.mod go.mod.tidy
|
||||||
|
|
||||||
|
|
||||||
# The updated version should have been written back to go.mod, so now the 'go'
|
# The updated version should have been written back to go.mod, so now the 'go'
|
||||||
# directive is explicit. -mod=vendor should trigger by default, and the stronger
|
# directive is explicit. -mod=vendor should trigger by default, and the stronger
|
||||||
@ -54,10 +68,24 @@ replace (
|
|||||||
example.com/dep v0.1.0 => ./dep
|
example.com/dep v0.1.0 => ./dep
|
||||||
example.com/testdep v0.1.0 => ./testdep
|
example.com/testdep v0.1.0 => ./testdep
|
||||||
)
|
)
|
||||||
-- go.mod.want --
|
-- go.mod.untidy --
|
||||||
module example.com/m
|
module example.com/m
|
||||||
|
|
||||||
go 1.16
|
go $goversion
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.com/dep v0.1.0
|
||||||
|
example.com/testdep v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.com/dep v0.1.0 => ./dep
|
||||||
|
example.com/testdep v0.1.0 => ./testdep
|
||||||
|
)
|
||||||
|
-- go.mod.tidy --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go $goversion
|
||||||
|
|
||||||
require example.com/dep v0.1.0
|
require example.com/dep v0.1.0
|
||||||
|
|
||||||
|
@ -18,6 +18,6 @@ package use
|
|||||||
import _ "rsc.io/quote"
|
import _ "rsc.io/quote"
|
||||||
|
|
||||||
-- want --
|
-- want --
|
||||||
go mod download: rsc.io/quote@v1.5.2 (replaced by example.com/quote@v1.5.2): parsing go.mod:
|
go: rsc.io/quote@v1.5.2 (replaced by example.com/quote@v1.5.2): parsing go.mod:
|
||||||
module declares its path as: rsc.io/Quote
|
module declares its path as: rsc.io/Quote
|
||||||
but was required as: rsc.io/quote
|
but was required as: rsc.io/quote
|
||||||
|
4
src/cmd/go/testdata/script/mod_retention.txt
vendored
4
src/cmd/go/testdata/script/mod_retention.txt
vendored
@ -62,6 +62,7 @@ cmp go.mod go.mod.tidy
|
|||||||
|
|
||||||
# A missing "go" version directive should be added.
|
# A missing "go" version directive should be added.
|
||||||
# However, that should not remove other redundant requirements.
|
# However, that should not remove other redundant requirements.
|
||||||
|
# In fact, it may *add* redundant requirements due to activating lazy loading.
|
||||||
cp go.mod.nogo go.mod
|
cp go.mod.nogo go.mod
|
||||||
go list -mod=mod all
|
go list -mod=mod all
|
||||||
cmpenv go.mod go.mod.addedgo
|
cmpenv go.mod go.mod.addedgo
|
||||||
@ -136,9 +137,10 @@ require (
|
|||||||
-- go.mod.addedgo --
|
-- go.mod.addedgo --
|
||||||
module m
|
module m
|
||||||
|
|
||||||
go 1.16
|
go $goversion
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
|
||||||
rsc.io/quote v1.5.2
|
rsc.io/quote v1.5.2
|
||||||
rsc.io/sampler v1.3.0 // indirect
|
rsc.io/sampler v1.3.0 // indirect
|
||||||
rsc.io/testonly v1.0.0 // indirect
|
rsc.io/testonly v1.0.0 // indirect
|
||||||
|
248
src/cmd/go/testdata/script/mod_tidy_version.txt
vendored
Normal file
248
src/cmd/go/testdata/script/mod_tidy_version.txt
vendored
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
# https://golang.org/issue/45094: 'go mod tidy' now accepts a '-go' flag
|
||||||
|
# to change the language version in use.
|
||||||
|
#
|
||||||
|
# The package import graph used in this test looks like:
|
||||||
|
#
|
||||||
|
# m --- a --- b
|
||||||
|
# |
|
||||||
|
# b_test --- c
|
||||||
|
# |
|
||||||
|
# c_test --- d
|
||||||
|
#
|
||||||
|
# The module diagram looks like:
|
||||||
|
#
|
||||||
|
# m --- a --- b
|
||||||
|
# |
|
||||||
|
# + --- c
|
||||||
|
# |
|
||||||
|
# + --- d
|
||||||
|
#
|
||||||
|
# Module b omits its dependency on c, and module c omits its dependency on d.
|
||||||
|
#
|
||||||
|
# In go 1.15, the tidy main module must require a (because it is direct),
|
||||||
|
# c (because it is a missing test dependency of an imported package),
|
||||||
|
# and d (because it is a missing transitive test dependency).
|
||||||
|
#
|
||||||
|
# In go 1.16, the tidy main module can omit d because it is no longer
|
||||||
|
# included in "all".
|
||||||
|
#
|
||||||
|
# In go 1.17, the main module must explicitly require b
|
||||||
|
# (because it is transitively imported by the main module).
|
||||||
|
|
||||||
|
|
||||||
|
cp go.mod go.mod.orig
|
||||||
|
|
||||||
|
# An invalid argument should be rejected.
|
||||||
|
|
||||||
|
! go mod tidy -go=bananas
|
||||||
|
stderr '^go mod: invalid -go option "bananas"; expecting something like "-go 1.17"$'
|
||||||
|
cmp go.mod go.mod.orig
|
||||||
|
|
||||||
|
|
||||||
|
go mod tidy -go=1.15
|
||||||
|
cmp go.mod go.mod.115
|
||||||
|
|
||||||
|
go mod tidy
|
||||||
|
cmp go.mod go.mod.115
|
||||||
|
|
||||||
|
|
||||||
|
go mod tidy -go=1.16
|
||||||
|
cmp go.mod go.mod.116
|
||||||
|
|
||||||
|
go mod tidy
|
||||||
|
cmp go.mod go.mod.116
|
||||||
|
|
||||||
|
|
||||||
|
go mod tidy -go=1.17
|
||||||
|
cmp go.mod go.mod.117
|
||||||
|
|
||||||
|
go mod tidy
|
||||||
|
cmp go.mod go.mod.117
|
||||||
|
|
||||||
|
|
||||||
|
# If we downgrade back to 1.15, we should re-resolve d to v0.2.0 instead
|
||||||
|
# of the original v0.1.0 (because the original requirement is lost).
|
||||||
|
|
||||||
|
go mod tidy -go=1.15
|
||||||
|
cmp go.mod go.mod.115-2
|
||||||
|
|
||||||
|
|
||||||
|
# -go= (with an empty argument) maintains the existing version or adds the
|
||||||
|
# default version (just like omitting the flag).
|
||||||
|
|
||||||
|
go mod tidy -go=''
|
||||||
|
cmp go.mod go.mod.115-2
|
||||||
|
|
||||||
|
cp go.mod.orig go.mod
|
||||||
|
go mod tidy -go=''
|
||||||
|
cmpenv go.mod go.mod.latest
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- go.mod --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.net/a v0.1.0
|
||||||
|
example.net/c v0.1.0 // indirect
|
||||||
|
example.net/d v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.net/a v0.1.0 => ./a
|
||||||
|
example.net/a v0.2.0 => ./a
|
||||||
|
example.net/b v0.1.0 => ./b
|
||||||
|
example.net/b v0.2.0 => ./b
|
||||||
|
example.net/c v0.1.0 => ./c
|
||||||
|
example.net/c v0.2.0 => ./c
|
||||||
|
example.net/d v0.1.0 => ./d
|
||||||
|
example.net/d v0.2.0 => ./d
|
||||||
|
)
|
||||||
|
-- m.go --
|
||||||
|
package m
|
||||||
|
|
||||||
|
import _ "example.net/a"
|
||||||
|
|
||||||
|
-- go.mod.115 --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.net/a v0.1.0
|
||||||
|
example.net/c v0.1.0 // indirect
|
||||||
|
example.net/d v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.net/a v0.1.0 => ./a
|
||||||
|
example.net/a v0.2.0 => ./a
|
||||||
|
example.net/b v0.1.0 => ./b
|
||||||
|
example.net/b v0.2.0 => ./b
|
||||||
|
example.net/c v0.1.0 => ./c
|
||||||
|
example.net/c v0.2.0 => ./c
|
||||||
|
example.net/d v0.1.0 => ./d
|
||||||
|
example.net/d v0.2.0 => ./d
|
||||||
|
)
|
||||||
|
-- go.mod.115-2 --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.net/a v0.1.0
|
||||||
|
example.net/c v0.1.0 // indirect
|
||||||
|
example.net/d v0.2.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.net/a v0.1.0 => ./a
|
||||||
|
example.net/a v0.2.0 => ./a
|
||||||
|
example.net/b v0.1.0 => ./b
|
||||||
|
example.net/b v0.2.0 => ./b
|
||||||
|
example.net/c v0.1.0 => ./c
|
||||||
|
example.net/c v0.2.0 => ./c
|
||||||
|
example.net/d v0.1.0 => ./d
|
||||||
|
example.net/d v0.2.0 => ./d
|
||||||
|
)
|
||||||
|
-- go.mod.116 --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.net/a v0.1.0
|
||||||
|
example.net/c v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.net/a v0.1.0 => ./a
|
||||||
|
example.net/a v0.2.0 => ./a
|
||||||
|
example.net/b v0.1.0 => ./b
|
||||||
|
example.net/b v0.2.0 => ./b
|
||||||
|
example.net/c v0.1.0 => ./c
|
||||||
|
example.net/c v0.2.0 => ./c
|
||||||
|
example.net/d v0.1.0 => ./d
|
||||||
|
example.net/d v0.2.0 => ./d
|
||||||
|
)
|
||||||
|
-- go.mod.117 --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.net/a v0.1.0
|
||||||
|
example.net/b v0.1.0 // indirect
|
||||||
|
example.net/c v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.net/a v0.1.0 => ./a
|
||||||
|
example.net/a v0.2.0 => ./a
|
||||||
|
example.net/b v0.1.0 => ./b
|
||||||
|
example.net/b v0.2.0 => ./b
|
||||||
|
example.net/c v0.1.0 => ./c
|
||||||
|
example.net/c v0.2.0 => ./c
|
||||||
|
example.net/d v0.1.0 => ./d
|
||||||
|
example.net/d v0.2.0 => ./d
|
||||||
|
)
|
||||||
|
-- go.mod.latest --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go $goversion
|
||||||
|
|
||||||
|
require (
|
||||||
|
example.net/a v0.1.0
|
||||||
|
example.net/b v0.1.0 // indirect
|
||||||
|
example.net/c v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
example.net/a v0.1.0 => ./a
|
||||||
|
example.net/a v0.2.0 => ./a
|
||||||
|
example.net/b v0.1.0 => ./b
|
||||||
|
example.net/b v0.2.0 => ./b
|
||||||
|
example.net/c v0.1.0 => ./c
|
||||||
|
example.net/c v0.2.0 => ./c
|
||||||
|
example.net/d v0.1.0 => ./d
|
||||||
|
example.net/d v0.2.0 => ./d
|
||||||
|
)
|
||||||
|
-- a/go.mod --
|
||||||
|
module example.net/a
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require example.net/b v0.1.0
|
||||||
|
-- a/a.go --
|
||||||
|
package a
|
||||||
|
|
||||||
|
import _ "example.net/b"
|
||||||
|
|
||||||
|
-- b/go.mod --
|
||||||
|
module example.net/b
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
-- b/b.go --
|
||||||
|
package b
|
||||||
|
-- b/b_test.go --
|
||||||
|
package b_test
|
||||||
|
|
||||||
|
import _ "example.net/c"
|
||||||
|
|
||||||
|
-- c/go.mod --
|
||||||
|
module example.net/c
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
-- c/c.go --
|
||||||
|
package c
|
||||||
|
-- c/c_test.go --
|
||||||
|
package c_test
|
||||||
|
|
||||||
|
import _ "example.net/d"
|
||||||
|
|
||||||
|
-- d/go.mod --
|
||||||
|
module example.net/d
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
-- d/d.go --
|
||||||
|
package d
|
Loading…
Reference in New Issue
Block a user