mirror of
https://github.com/golang/go
synced 2024-11-11 19:51:37 -07:00
cmd/go: with -mod=vendor, don't panic if there are duplicate requirements
In loadModFile with -mod=vendor, load the vendor list and use it to initialize the module graph before calling updateRoots. In updateLazyRoots with any mode other than "mod", return the original *Requirements if no roots needed to be upgraded, even if there are inconsistencies. This means 'go list -m -mod=readonly' and -mod=vendor may succeed if there are duplicate requirements or requirements on versions of the main module. Fixes #47565 Change-Id: I4640dffc4a7359367cc910da0d29e3538bfd1ca4 Reviewed-on: https://go-review.googlesource.com/c/go/+/340252 Trust: Jay Conrod <jayconrod@google.com> Trust: Bryan C. Mills <bcmills@google.com> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
7aeaad5c86
commit
f1dce319ff
@ -191,6 +191,19 @@ func (rs *Requirements) rootSelected(path string) (version string, ok bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasRedundantRoot returns true if the root list contains multiple requirements
|
||||||
|
// of the same module or a requirement on any version of the main module.
|
||||||
|
// Redundant requirements should be pruned, but they may influence version
|
||||||
|
// selection.
|
||||||
|
func (rs *Requirements) hasRedundantRoot() bool {
|
||||||
|
for i, m := range rs.rootModules {
|
||||||
|
if m.Path == Target.Path || (i > 0 && m.Path == rs.rootModules[i-1].Path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Graph returns the graph of module requirements loaded from the current
|
// Graph returns the graph of module requirements loaded from the current
|
||||||
// root modules (as reported by RootModules).
|
// root modules (as reported by RootModules).
|
||||||
//
|
//
|
||||||
@ -882,6 +895,12 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
|
|||||||
// and (trivially) version.
|
// and (trivially) version.
|
||||||
|
|
||||||
if !rootsUpgraded {
|
if !rootsUpgraded {
|
||||||
|
if cfg.BuildMod != "mod" {
|
||||||
|
// The only changes to the root set (if any) were to remove duplicates.
|
||||||
|
// The requirements are consistent (if perhaps redundant), so keep the
|
||||||
|
// original rs to preserve its ModuleGraph.
|
||||||
|
return rs, nil
|
||||||
|
}
|
||||||
// The root set has converged: every root going into this iteration was
|
// The root set has converged: every root going into this iteration was
|
||||||
// already at its selected version, although we have have removed other
|
// already at its selected version, although we have have removed other
|
||||||
// (redundant) roots for the same path.
|
// (redundant) roots for the same path.
|
||||||
|
@ -449,13 +449,22 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDefaultBuildMod() // possibly enable automatic vendoring
|
setDefaultBuildMod() // possibly enable automatic vendoring
|
||||||
rs = requirementsFromModFile(ctx)
|
rs = requirementsFromModFile()
|
||||||
|
|
||||||
if cfg.BuildMod == "vendor" {
|
if cfg.BuildMod == "vendor" {
|
||||||
readVendorList()
|
readVendorList()
|
||||||
checkVendorConsistency()
|
checkVendorConsistency()
|
||||||
rs.initVendor(vendorList)
|
rs.initVendor(vendorList)
|
||||||
}
|
}
|
||||||
|
if rs.hasRedundantRoot() {
|
||||||
|
// 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
|
||||||
|
// transitive dependencies.
|
||||||
|
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
base.Fatalf("go: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if index.goVersionV == "" {
|
if index.goVersionV == "" {
|
||||||
// TODO(#45551): Do something more principled instead of checking
|
// TODO(#45551): Do something more principled instead of checking
|
||||||
// cfg.CmdName directly here.
|
// cfg.CmdName directly here.
|
||||||
@ -530,7 +539,12 @@ func CreateModFile(ctx context.Context, modPath string) {
|
|||||||
base.Fatalf("go: %v", err)
|
base.Fatalf("go: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commitRequirements(ctx, modFileGoVersion(), requirementsFromModFile(ctx))
|
rs := requirementsFromModFile()
|
||||||
|
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
base.Fatalf("go: %v", err)
|
||||||
|
}
|
||||||
|
commitRequirements(ctx, modFileGoVersion(), rs)
|
||||||
|
|
||||||
// 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
|
||||||
@ -641,9 +655,8 @@ func initTarget(m module.Version) {
|
|||||||
|
|
||||||
// requirementsFromModFile returns the set of non-excluded requirements from
|
// requirementsFromModFile returns the set of non-excluded requirements from
|
||||||
// the global modFile.
|
// the global modFile.
|
||||||
func requirementsFromModFile(ctx context.Context) *Requirements {
|
func requirementsFromModFile() *Requirements {
|
||||||
roots := make([]module.Version, 0, len(modFile.Require))
|
roots := make([]module.Version, 0, len(modFile.Require))
|
||||||
mPathCount := map[string]int{Target.Path: 1}
|
|
||||||
direct := map[string]bool{}
|
direct := map[string]bool{}
|
||||||
for _, r := range modFile.Require {
|
for _, r := range modFile.Require {
|
||||||
if index != nil && index.exclude[r.Mod] {
|
if index != nil && index.exclude[r.Mod] {
|
||||||
@ -656,28 +669,12 @@ func requirementsFromModFile(ctx context.Context) *Requirements {
|
|||||||
}
|
}
|
||||||
|
|
||||||
roots = append(roots, r.Mod)
|
roots = append(roots, r.Mod)
|
||||||
mPathCount[r.Mod.Path]++
|
|
||||||
if !r.Indirect {
|
if !r.Indirect {
|
||||||
direct[r.Mod.Path] = true
|
direct[r.Mod.Path] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.Sort(roots)
|
module.Sort(roots)
|
||||||
rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct)
|
rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct)
|
||||||
|
|
||||||
// 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
|
|
||||||
// transitive dependencies.
|
|
||||||
for _, n := range mPathCount {
|
|
||||||
if n > 1 {
|
|
||||||
var err error
|
|
||||||
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
|
||||||
if err != nil {
|
|
||||||
base.Fatalf("go: %v", err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,18 +2,13 @@
|
|||||||
# 'go mod tidy' should not panic if the main module initially
|
# 'go mod tidy' should not panic if the main module initially
|
||||||
# requires an older version of itself.
|
# requires an older version of itself.
|
||||||
|
|
||||||
|
# A module may require an older version of itself without error. This is
|
||||||
|
# inconsistent (the required version is never selected), but we still get
|
||||||
|
# a reproducible build list.
|
||||||
|
go list -m all
|
||||||
|
stdout '^golang.org/issue/46078$'
|
||||||
|
|
||||||
# A module that explicitly requires an older version of itself should be
|
# 'go mod tidy' should fix this (and not crash).
|
||||||
# rejected as inconsistent: we enforce that every explicit requirement is the
|
|
||||||
# selected version of its module path, but the selected version of the main
|
|
||||||
# module is always itself — not some explicit version.
|
|
||||||
|
|
||||||
! go list -m all
|
|
||||||
stderr '^go: updates to go\.mod needed; to update it:\n\tgo mod tidy$'
|
|
||||||
|
|
||||||
|
|
||||||
# The suggested 'go mod tidy' command should succeed (not crash).
|
|
||||||
|
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
|
|
||||||
|
29
src/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt
vendored
Normal file
29
src/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# 'go list -mod=vendor' should succeed even when go.mod contains redundant
|
||||||
|
# requirements. Verifies #47565.
|
||||||
|
go list -mod=vendor
|
||||||
|
|
||||||
|
-- go.mod --
|
||||||
|
module m
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require example.com/m v0.0.0
|
||||||
|
require example.com/m v0.0.0
|
||||||
|
|
||||||
|
replace example.com/m v0.0.0 => ./m
|
||||||
|
-- m/go.mod --
|
||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
-- m/m.go --
|
||||||
|
package m
|
||||||
|
-- use.go --
|
||||||
|
package use
|
||||||
|
|
||||||
|
import _ "example.com/m"
|
||||||
|
-- vendor/example.com/m/m.go --
|
||||||
|
package m
|
||||||
|
-- vendor/modules.txt --
|
||||||
|
# example.com/m v0.0.0 => ./m
|
||||||
|
## explicit; go 1.17
|
||||||
|
example.com/m
|
Loading…
Reference in New Issue
Block a user