From 0e315ad79ae388eedb03dce27101c40b29292e2a Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Fri, 9 Apr 2021 23:40:22 -0400 Subject: [PATCH] cmd/go/internal/modload: avoid loading the full module graph when listing specific modules For #36460 For #41297 Updates #29666 Change-Id: I5f324c0ef9a164f8043d2188101d141bb5fa7454 Reviewed-on: https://go-review.googlesource.com/c/go/+/309191 Trust: Bryan C. Mills Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Jay Conrod Reviewed-by: Michael Matloob --- src/cmd/go/internal/modload/list.go | 67 ++++++++++++++------ src/cmd/go/testdata/script/mod_list_sums.txt | 19 +++--- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/cmd/go/internal/modload/list.go b/src/cmd/go/internal/modload/list.go index 66927a8288..344b2aa2c7 100644 --- a/src/cmd/go/internal/modload/list.go +++ b/src/cmd/go/internal/modload/list.go @@ -82,19 +82,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List return rs, []*modinfo.ModulePublic{moduleInfo(ctx, rs, Target, mode)}, nil } - var mg *ModuleGraph - if go117LazyTODO { - // Pull the args-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 { - // TODO(#41297): Don't bother loading or expanding the graph if all - // arguments are explicit version queries (including if no arguments are - // present at all). - rs, mg, mgErr = expandGraph(ctx, rs) - } - - matchedModule := map[module.Version]bool{} + needFullGraph := false for _, arg := range args { if strings.Contains(arg, `\`) { base.Fatalf("go: module paths never use backslash") @@ -102,19 +90,51 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List if search.IsRelativePath(arg) { base.Fatalf("go: cannot use relative path %s to specify module", arg) } - if !HasModRoot() { - if arg == "all" || strings.Contains(arg, "...") { + if arg == "all" || strings.Contains(arg, "...") { + needFullGraph = true + if !HasModRoot() { base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) } - if mode&ListVersions == 0 && !strings.Contains(arg, "@") { + continue + } + if i := strings.Index(arg, "@"); i >= 0 { + path := arg[:i] + vers := arg[i+1:] + if vers == "upgrade" || vers == "patch" { + if _, ok := rs.rootSelected(path); !ok || rs.depth == eager { + needFullGraph = true + if !HasModRoot() { + base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) + } + } + } + continue + } + if _, ok := rs.rootSelected(arg); !ok || rs.depth == eager { + needFullGraph = true + if mode&ListVersions == 0 && !HasModRoot() { base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot) } } + } + + var mg *ModuleGraph + if needFullGraph { + rs, mg, mgErr = expandGraph(ctx, rs) + } + + matchedModule := map[module.Version]bool{} + for _, arg := range args { if i := strings.Index(arg, "@"); i >= 0 { path := arg[:i] vers := arg[i+1:] - current := mg.Selected(path) + var current string + if mg == nil { + current, _ = rs.rootSelected(path) + } else { + current = mg.Selected(path) + } if current == "none" && mgErr != nil { if vers == "upgrade" || vers == "patch" { // The module graph is incomplete, so we don't know what version we're @@ -156,7 +176,18 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List } else if strings.Contains(arg, "...") { match = search.MatchPattern(arg) } else { - v := mg.Selected(arg) + var v string + if mg == nil { + var ok bool + v, ok = rs.rootSelected(arg) + if !ok { + // We checked rootSelected(arg) in the earlier args loop, so if there + // is no such root we should have loaded a non-nil mg. + panic(fmt.Sprintf("internal error: root requirement expected but not found for %v", arg)) + } + } else { + v = mg.Selected(arg) + } if v == "none" && mgErr != nil { // mgErr is already set, so just skip this module. continue diff --git a/src/cmd/go/testdata/script/mod_list_sums.txt b/src/cmd/go/testdata/script/mod_list_sums.txt index e5f80d7fb6..86c528f829 100644 --- a/src/cmd/go/testdata/script/mod_list_sums.txt +++ b/src/cmd/go/testdata/script/mod_list_sums.txt @@ -4,16 +4,13 @@ go mod init m go mod edit -require=rsc.io/quote@v1.5.1 -# 'go list' currently loads the whole build list, even when listing only -# non-dependencies. -# -# TODO(#41297): Thes should not be errors. +go list -m -mod=readonly rsc.io/quote@latest +stdout '^rsc\.io/quote v1\.5\.2$' +! stderr . -! go list -m -mod=readonly rsc.io/quote@latest -stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' - -! go list -m -mod=readonly -versions rsc.io/quote -stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' +go list -m -mod=readonly -versions rsc.io/quote +stdout 'rsc\.io/quote v1\.0\.0 .* v1\.5\.3-pre1$' +! stderr . # Incidentally fetching the required version of a module records its checksum, # just because it happens to be in the build list, and recording the checksum @@ -21,8 +18,8 @@ stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n # # TODO(#41297): This should not be an error. ! go list -m -mod=readonly rsc.io/quote@