mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00: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:
parent
8d8abb3b8a
commit
4063605e0d
5 changed files with 139 additions and 90 deletions
|
|
@ -220,10 +220,13 @@ func (e *invalidImportError) Unwrap() error {
|
||||||
return e.err
|
return e.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// importFromModules finds the module and directory in the build list
|
// importFromModules finds the module and directory in the dependency graph of
|
||||||
// containing the package with the given import path. The answer must be unique:
|
// rs containing the package with the given import path. If mg is nil,
|
||||||
// importFromModules returns an error if multiple modules attempt to provide
|
// importFromModules attempts to locate the module using only the main module
|
||||||
// the same package.
|
// 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
|
// importFromModules can return a module with an empty m.Path, for packages in
|
||||||
// the standard library.
|
// 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
|
// If the package is not present in any module selected from the requirement
|
||||||
// graph, importFromModules returns an *ImportMissingError.
|
// 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, "@") {
|
if strings.Contains(path, "@") {
|
||||||
return module.Version{}, "", fmt.Errorf("import path should not have @version")
|
return module.Version{}, "", fmt.Errorf("import path should not have @version")
|
||||||
}
|
}
|
||||||
|
|
@ -295,12 +298,97 @@ 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
|
// 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
|
// will tend to remain smallish (if for no other reason than filesystem
|
||||||
// path limitations).
|
// path limitations).
|
||||||
var mg *ModuleGraph
|
//
|
||||||
if go117LazyTODO {
|
// We perform this iteration either one or two times. If mg is initially nil,
|
||||||
// Pull the prefix-matching loop below into another (new) loop.
|
// then we first attempt to load the package using only the main module and
|
||||||
// If the main module is lazy, try it once with mg == nil, and then load mg
|
// its root requirements. If that does not identify the package, or if mg is
|
||||||
// and try again.
|
// already non-nil, then we attempt to load the package using the full
|
||||||
} else {
|
// requirements in mg.
|
||||||
|
for {
|
||||||
|
var sumErrMods []module.Version
|
||||||
|
for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
|
||||||
|
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}
|
||||||
|
|
||||||
|
needSum := true
|
||||||
|
root, isLocal, err := fetch(ctx, m, needSum)
|
||||||
|
if err != nil {
|
||||||
|
if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) {
|
||||||
|
// We are missing a sum needed to fetch a module in the build list.
|
||||||
|
// We can't verify that the package is unique, and we may not find
|
||||||
|
// the package at all. Keep checking other modules to decide which
|
||||||
|
// error to report. Multiple sums may be missing if we need to look in
|
||||||
|
// multiple nested modules to resolve the import; we'll report them all.
|
||||||
|
sumErrMods = append(sumErrMods, m)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Report fetch error.
|
||||||
|
// Note that we don't know for sure this module is necessary,
|
||||||
|
// but it certainly _could_ provide the package, and even if we
|
||||||
|
// continue the loop and find the package in some other module,
|
||||||
|
// we need to look at this module to make sure the import is
|
||||||
|
// not ambiguous.
|
||||||
|
return module.Version{}, "", err
|
||||||
|
}
|
||||||
|
if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
|
||||||
|
return module.Version{}, "", err
|
||||||
|
} else if ok {
|
||||||
|
mods = append(mods, m)
|
||||||
|
dirs = append(dirs, dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mods) > 1 {
|
||||||
|
// We produce the list of directories from longest to shortest candidate
|
||||||
|
// module path, but the AmbiguousImportError should report them from
|
||||||
|
// shortest to longest. Reverse them now.
|
||||||
|
for i := 0; i < len(mods)/2; i++ {
|
||||||
|
j := len(mods) - 1 - i
|
||||||
|
mods[i], mods[j] = mods[j], mods[i]
|
||||||
|
dirs[i], dirs[j] = dirs[j], dirs[i]
|
||||||
|
}
|
||||||
|
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sumErrMods) > 0 {
|
||||||
|
for i := 0; i < len(sumErrMods)/2; i++ {
|
||||||
|
j := len(sumErrMods) - 1 - i
|
||||||
|
sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i]
|
||||||
|
}
|
||||||
|
return module.Version{}, "", &ImportMissingSumError{
|
||||||
|
importPath: path,
|
||||||
|
mods: sumErrMods,
|
||||||
|
found: len(mods) > 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mods) == 1 {
|
||||||
|
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
|
||||||
|
if !HasModRoot() {
|
||||||
|
queryErr = ErrNoModRoot
|
||||||
|
}
|
||||||
|
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)
|
mg, err = rs.Graph(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// We might be missing one or more transitive (implicit) dependencies from
|
// We might be missing one or more transitive (implicit) dependencies from
|
||||||
|
|
@ -310,78 +398,6 @@ func importFromModules(ctx context.Context, path string, rs *Requirements) (m mo
|
||||||
return module.Version{}, "", err
|
return module.Version{}, "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sumErrMods []module.Version
|
|
||||||
for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
|
|
||||||
v := mg.Selected(prefix)
|
|
||||||
if v == "none" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m := module.Version{Path: prefix, Version: v}
|
|
||||||
|
|
||||||
needSum := true
|
|
||||||
root, isLocal, err := fetch(ctx, m, needSum)
|
|
||||||
if err != nil {
|
|
||||||
if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) {
|
|
||||||
// We are missing a sum needed to fetch a module in the build list.
|
|
||||||
// We can't verify that the package is unique, and we may not find
|
|
||||||
// the package at all. Keep checking other modules to decide which
|
|
||||||
// error to report. Multiple sums may be missing if we need to look in
|
|
||||||
// multiple nested modules to resolve the import; we'll report them all.
|
|
||||||
sumErrMods = append(sumErrMods, m)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Report fetch error.
|
|
||||||
// Note that we don't know for sure this module is necessary,
|
|
||||||
// but it certainly _could_ provide the package, and even if we
|
|
||||||
// continue the loop and find the package in some other module,
|
|
||||||
// we need to look at this module to make sure the import is
|
|
||||||
// not ambiguous.
|
|
||||||
return module.Version{}, "", err
|
|
||||||
}
|
|
||||||
if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
|
|
||||||
return module.Version{}, "", err
|
|
||||||
} else if ok {
|
|
||||||
mods = append(mods, m)
|
|
||||||
dirs = append(dirs, dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(mods) > 1 {
|
|
||||||
// We produce the list of directories from longest to shortest candidate
|
|
||||||
// module path, but the AmbiguousImportError should report them from
|
|
||||||
// shortest to longest. Reverse them now.
|
|
||||||
for i := 0; i < len(mods)/2; i++ {
|
|
||||||
j := len(mods) - 1 - i
|
|
||||||
mods[i], mods[j] = mods[j], mods[i]
|
|
||||||
dirs[i], dirs[j] = dirs[j], dirs[i]
|
|
||||||
}
|
|
||||||
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sumErrMods) > 0 {
|
|
||||||
for i := 0; i < len(sumErrMods)/2; i++ {
|
|
||||||
j := len(sumErrMods) - 1 - i
|
|
||||||
sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i]
|
|
||||||
}
|
|
||||||
return module.Version{}, "", &ImportMissingSumError{
|
|
||||||
importPath: path,
|
|
||||||
mods: sumErrMods,
|
|
||||||
found: len(mods) > 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(mods) == 1 {
|
|
||||||
return mods[0], dirs[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We checked the full module graph and still didn't find the
|
|
||||||
// requested package.
|
|
||||||
var queryErr error
|
|
||||||
if !HasModRoot() {
|
|
||||||
queryErr = ErrNoModRoot
|
|
||||||
}
|
|
||||||
return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// queryImport attempts to locate a module that can be added to the current
|
// queryImport attempts to locate a module that can be added to the current
|
||||||
|
|
|
||||||
|
|
@ -1125,6 +1125,22 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
|
||||||
continue
|
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) {
|
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
|
||||||
if v := mg.Selected(prefix); v != "none" {
|
if v := mg.Selected(prefix); v != "none" {
|
||||||
m := module.Version{Path: prefix, Version: v}
|
m := module.Version{Path: prefix, Version: v}
|
||||||
|
|
|
||||||
|
|
@ -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
|
// In some sense, we can think of this as ‘upgraded the module providing
|
||||||
// pkg.path from "none" to a version higher than "none"’.
|
// 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
|
changed = true
|
||||||
break
|
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
|
// 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
|
// lucky — we can identify all of its imports without actually loading the
|
||||||
// full module graph.
|
// full module graph.
|
||||||
m, _, err := importFromModules(ctx, path, ld.requirements)
|
m, _, err := importFromModules(ctx, path, ld.requirements, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var missing *ImportMissingError
|
var missing *ImportMissingError
|
||||||
if errors.As(err, &missing) && ld.ResolveMissingImports {
|
if errors.As(err, &missing) && ld.ResolveMissingImports {
|
||||||
|
|
@ -1516,7 +1516,24 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
|
||||||
return
|
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 == "" {
|
if pkg.dir == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@ cd tmp
|
||||||
go mod init tmp
|
go mod init tmp
|
||||||
go mod edit -require=rsc.io/fortune@v1.0.0
|
go mod edit -require=rsc.io/fortune@v1.0.0
|
||||||
! go install -mod=readonly $GOPATH/pkg/mod/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
|
! 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 get -d rsc.io/fortune@v1.0.0
|
||||||
go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0
|
go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0
|
||||||
exists $GOPATH/bin/fortune$GOEXE
|
exists $GOPATH/bin/fortune$GOEXE
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,9 @@ cd tmp
|
||||||
go mod init tmp
|
go mod init tmp
|
||||||
go mod edit -require=rsc.io/fortune@v1.0.0
|
go mod edit -require=rsc.io/fortune@v1.0.0
|
||||||
! go run -mod=readonly $GOPATH/pkg/mod/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
|
! 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 ..
|
cd ..
|
||||||
rm tmp
|
rm tmp
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue