1
0
mirror of https://github.com/golang/go synced 2024-11-26 14:26:51 -07:00

cmd/go/internal/modload: avoid loading the full module graph for imports satisfied by lazy roots

For #36460

Change-Id: Ibdbaa893ded772617e22f12db7a0463604db5195
Reviewed-on: https://go-review.googlesource.com/c/go/+/308516
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:
Bryan C. Mills 2021-04-28 12:57:55 -04:00
parent 8d8abb3b8a
commit 4063605e0d
5 changed files with 139 additions and 90 deletions

View File

@ -220,10 +220,13 @@ func (e *invalidImportError) Unwrap() error {
return e.err
}
// importFromModules finds the module and directory in the build list
// containing the package with the given import path. The answer must be unique:
// importFromModules returns an error if multiple modules attempt to provide
// the same package.
// importFromModules finds the module and directory in the dependency graph of
// rs containing the package with the given import path. If mg is nil,
// importFromModules attempts to locate the module using only the main module
// and the roots of rs before it loads the full graph.
//
// The answer must be unique: importFromModules returns an error if multiple
// modules are observed to provide the same package.
//
// importFromModules can return a module with an empty m.Path, for packages in
// the standard library.
@ -233,7 +236,7 @@ func (e *invalidImportError) Unwrap() error {
//
// If the package is not present in any module selected from the requirement
// graph, importFromModules returns an *ImportMissingError.
func importFromModules(ctx context.Context, path string, rs *Requirements) (m module.Version, dir string, err error) {
func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) {
if strings.Contains(path, "@") {
return module.Version{}, "", fmt.Errorf("import path should not have @version")
}
@ -295,26 +298,25 @@ func importFromModules(ctx context.Context, path string, rs *Requirements) (m mo
// large projects both M and P may be very large (note that M ≤ P), but k
// will tend to remain smallish (if for no other reason than filesystem
// path limitations).
var mg *ModuleGraph
if go117LazyTODO {
// Pull the prefix-matching loop below into another (new) loop.
// If the main module is lazy, try it once with mg == nil, and then load mg
// and try again.
} else {
mg, err = rs.Graph(ctx)
if err != nil {
// We might be missing one or more transitive (implicit) dependencies from
// the module graph, so we can't return an ImportMissingError here — one
// of the missing modules might actually contain the package in question,
// in which case we shouldn't go looking for it in some new dependency.
return module.Version{}, "", err
}
}
//
// We perform this iteration either one or two times. If mg is initially nil,
// then we first attempt to load the package using only the main module and
// its root requirements. If that does not identify the package, or if mg is
// already non-nil, then we attempt to load the package using the full
// requirements in mg.
for {
var sumErrMods []module.Version
for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
v := mg.Selected(prefix)
if v == "none" {
var (
v string
ok bool
)
if mg == nil {
v, ok = rs.rootSelected(prefix)
} else {
v, ok = mg.Selected(prefix), true
}
if !ok || v == "none" {
continue
}
m := module.Version{Path: prefix, Version: v}
@ -375,6 +377,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements) (m mo
return mods[0], dirs[0], nil
}
if mg != nil {
// We checked the full module graph and still didn't find the
// requested package.
var queryErr error
@ -384,6 +387,19 @@ func importFromModules(ctx context.Context, path string, rs *Requirements) (m mo
return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
}
// So far we've checked the root dependencies.
// Load the full module graph and try again.
mg, err = rs.Graph(ctx)
if err != nil {
// We might be missing one or more transitive (implicit) dependencies from
// the module graph, so we can't return an ImportMissingError here — one
// of the missing modules might actually contain the package in question,
// in which case we shouldn't go looking for it in some new dependency.
return module.Version{}, "", err
}
}
}
// queryImport attempts to locate a module that can be added to the current
// build list to provide the package with the given import path.
//

View File

@ -1125,6 +1125,22 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
continue
}
if rs.depth == lazy && pkg.mod.Path != "" {
if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
// pkg was loaded from a root module, and because the main module is
// lazy we do not check non-root modules for conflicts for packages
// that can be found in roots. So we only need the checksums for the
// root modules that may contain pkg, not all possible modules.
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
if v, ok := rs.rootSelected(prefix); ok && v != "none" {
m := module.Version{Path: prefix, Version: v}
keep[resolveReplacement(m)] = true
}
}
continue
}
}
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
if v := mg.Selected(prefix); v != "none" {
m := module.Version{Path: prefix, Version: v}

View File

@ -1236,7 +1236,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
//
// In some sense, we can think of this as upgraded the module providing
// pkg.path from "none" to a version higher than "none".
if _, _, err = importFromModules(ctx, pkg.path, rs); err == nil {
if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
changed = true
break
}
@ -1429,7 +1429,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch
// If the main module is tidy and the package is in "all" — or if we're
// lucky — we can identify all of its imports without actually loading the
// full module graph.
m, _, err := importFromModules(ctx, path, ld.requirements)
m, _, err := importFromModules(ctx, path, ld.requirements, nil)
if err != nil {
var missing *ImportMissingError
if errors.As(err, &missing) && ld.ResolveMissingImports {
@ -1516,7 +1516,24 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
return
}
pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements)
var mg *ModuleGraph
if ld.requirements.depth == eager {
var err error
mg, err = ld.requirements.Graph(ctx)
if err != nil {
// We already checked the error from Graph in loadFromRoots and/or
// updateRequirements, so we ignored the error on purpose and we should
// keep trying to push past it.
//
// However, because mg may be incomplete (and thus may select inaccurate
// versions), we shouldn't use it to load packages. Instead, we pass a nil
// *ModuleGraph, which will cause mg to first try loading from only the
// main module and root dependencies.
mg = nil
}
}
pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
if pkg.dir == "" {
return
}

View File

@ -67,9 +67,9 @@ cd tmp
go mod init tmp
go mod edit -require=rsc.io/fortune@v1.0.0
! go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0
stderr '^rsc.io/fortune@v1.0.0: missing go.sum entry; to add it:\n\tgo mod download rsc.io/fortune$'
stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$'
! go install -mod=readonly ../../pkg/mod/rsc.io/fortune@v1.0.0
stderr '^rsc.io/fortune@v1.0.0: missing go.sum entry; to add it:\n\tgo mod download rsc.io/fortune$'
stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$'
go get -d rsc.io/fortune@v1.0.0
go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0
exists $GOPATH/bin/fortune$GOEXE

View File

@ -64,9 +64,9 @@ cd tmp
go mod init tmp
go mod edit -require=rsc.io/fortune@v1.0.0
! go run -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0
stderr '^rsc.io/fortune@v1.0.0: missing go.sum entry; to add it:\n\tgo mod download rsc.io/fortune$'
stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$'
! go run -mod=readonly ../../pkg/mod/rsc.io/fortune@v1.0.0
stderr '^rsc.io/fortune@v1.0.0: missing go.sum entry; to add it:\n\tgo mod download rsc.io/fortune$'
stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$'
cd ..
rm tmp