aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/internal/modload/buildlist.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/modload/buildlist.go')
-rw-r--r--src/cmd/go/internal/modload/buildlist.go66
1 files changed, 60 insertions, 6 deletions
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index cde4953afa..9b42644660 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -10,6 +10,7 @@ import (
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"context"
+ "errors"
"fmt"
"os"
"reflect"
@@ -689,8 +690,8 @@ func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Ve
// roots) until the set of roots has converged.
func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
var (
- roots []module.Version
- pathIncluded = map[string]bool{mainModule.Path: true}
+ roots []module.Version
+ pathIsRoot = map[string]bool{mainModule.Path: true}
)
// We start by adding roots for every package in "all".
//
@@ -710,9 +711,9 @@ func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[
if !pkg.flags.has(pkgInAll) {
continue
}
- if pkg.fromExternalModule() && !pathIncluded[pkg.mod.Path] {
+ if pkg.fromExternalModule() && !pathIsRoot[pkg.mod.Path] {
roots = append(roots, pkg.mod)
- pathIncluded[pkg.mod.Path] = true
+ pathIsRoot[pkg.mod.Path] = true
}
queue = append(queue, pkg)
queued[pkg] = true
@@ -744,11 +745,12 @@ func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[
queue = append(queue, pkg.test)
queued[pkg.test] = true
}
- if !pathIncluded[m.Path] {
+
+ if !pathIsRoot[m.Path] {
if s := mg.Selected(m.Path); cmpVersion(s, m.Version) < 0 {
roots = append(roots, m)
+ pathIsRoot[m.Path] = true
}
- pathIncluded[m.Path] = true
}
}
@@ -758,10 +760,62 @@ func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[
}
}
+ roots = tidy.rootModules
_, err := tidy.Graph(ctx)
if err != nil {
return nil, err
}
+
+ // We try to avoid adding explicit requirements for test-only dependencies of
+ // packages in external modules. However, if we drop the explicit
+ // requirements, that may change an import from unambiguous (due to lazy
+ // module loading) to ambiguous (because lazy module loading no longer
+ // disambiguates it). For any package that has become ambiguous, we try
+ // to fix it by promoting its module to an explicit root.
+ // (See https://go.dev/issue/60313.)
+ q := par.NewQueue(runtime.GOMAXPROCS(0))
+ for {
+ var disambiguateRoot sync.Map
+ for _, pkg := range pkgs {
+ if pkg.mod.Path == "" || pathIsRoot[pkg.mod.Path] {
+ // Lazy module loading will cause pkg.mod to be checked before any other modules
+ // that are only indirectly required. It is as unambiguous as possible.
+ continue
+ }
+ pkg := pkg
+ q.Add(func() {
+ skipModFile := true
+ _, _, _, _, err := importFromModules(ctx, pkg.path, tidy, nil, skipModFile)
+ if aie := (*AmbiguousImportError)(nil); errors.As(err, &aie) {
+ disambiguateRoot.Store(pkg.mod, true)
+ }
+ })
+ }
+ <-q.Idle()
+
+ disambiguateRoot.Range(func(k, _ any) bool {
+ m := k.(module.Version)
+ roots = append(roots, m)
+ pathIsRoot[m.Path] = true
+ return true
+ })
+
+ if len(roots) > len(tidy.rootModules) {
+ module.Sort(roots)
+ tidy = newRequirements(pruned, roots, tidy.direct)
+ _, err = tidy.Graph(ctx)
+ if err != nil {
+ return nil, err
+ }
+ // Adding these roots may have pulled additional modules into the module
+ // graph, causing additional packages to become ambiguous. Keep iterating
+ // until we reach a fixed point.
+ continue
+ }
+
+ break
+ }
+
return tidy, nil
}