Skip to content

Commit 0e1b14b

Browse files
committed
cmd/go: fix get with the new 'work' pattern
Before this change, go get didn't have support for the work pattern. The work pattern is new in Go 1.25 and evaluates to the packages in the work (also called main) modules. 'go get work' would cause a panic because 'work' would be incorrectly considered a path pattern and then queryPath would would try to query a metapackage pattern (resulting in the internal error panic). This change properly supports the work pattern in go get. It's pretty simple: First, we need to seprate the work pattern from the other patterns. Then in performWorkQueries, which maps queries to the modules that satisfy them, we return the single main module because by definition the work pattern is the set of packages in the work modules, and go get always runs in single module mode. (The exception is when the work module contains no packages, in which case we report a warning, and return no candidates because nothing is needed to resolve nothing). The rest of the work is already done by loading the packages matching the query and finding missing imports in the call to findAndUpgradeImports in runGet. Change-Id: I3c4610878b3d930a1d106cc59d9a0be194d966cd Reviewed-on: https://go-review.googlesource.com/c/go/+/675895 Reviewed-by: Michael Matloob <matloob@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
1 parent 09f1546 commit 0e1b14b

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

src/cmd/go/internal/modget/get.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
337337
r.performLocalQueries(ctx)
338338
r.performPathQueries(ctx)
339339
r.performToolQueries(ctx)
340+
r.performWorkQueries(ctx)
340341

341342
for {
342343
r.performWildcardQueries(ctx)
@@ -513,6 +514,7 @@ type resolver struct {
513514
pathQueries []*query // package path literal queries in original order
514515
wildcardQueries []*query // path wildcard queries in original order
515516
patternAllQueries []*query // queries with the pattern "all"
517+
workQueries []*query // queries with the pattern "work"
516518
toolQueries []*query // queries with the pattern "tool"
517519

518520
// Indexed "none" queries. These are also included in the slices above;
@@ -578,6 +580,8 @@ func newResolver(ctx context.Context, queries []*query) *resolver {
578580
for _, q := range queries {
579581
if q.pattern == "all" {
580582
r.patternAllQueries = append(r.patternAllQueries, q)
583+
} else if q.pattern == "work" {
584+
r.workQueries = append(r.workQueries, q)
581585
} else if q.pattern == "tool" {
582586
r.toolQueries = append(r.toolQueries, q)
583587
} else if q.patternIsLocal {
@@ -1070,6 +1074,37 @@ func (r *resolver) performToolQueries(ctx context.Context) {
10701074
}
10711075
}
10721076

1077+
// performWorkQueries populates the candidates for each query whose pattern is "work".
1078+
// The candidate module to resolve the work pattern is exactly the single main module.
1079+
func (r *resolver) performWorkQueries(ctx context.Context) {
1080+
for _, q := range r.workQueries {
1081+
q.pathOnce(q.pattern, func() pathSet {
1082+
// TODO(matloob): Maybe export MainModules.mustGetSingleMainModule and call that.
1083+
// There are a few other places outside the modload package where we expect
1084+
// a single main module.
1085+
if len(modload.MainModules.Versions()) != 1 {
1086+
panic("internal error: number of main modules is not exactly one in resolution phase of go get")
1087+
}
1088+
mainModule := modload.MainModules.Versions()[0]
1089+
1090+
// We know what the result is going to be, assuming the main module is not
1091+
// empty, (it's the main module itself) but first check to see that there
1092+
// are packages in the main module, so that if there aren't any, we can
1093+
// return the expected warning that the pattern matched no packages.
1094+
match := modload.MatchInModule(ctx, q.pattern, mainModule, imports.AnyTags())
1095+
if len(match.Errs) > 0 {
1096+
return pathSet{err: match.Errs[0]}
1097+
}
1098+
if len(match.Pkgs) == 0 {
1099+
search.WarnUnmatched([]*search.Match{match})
1100+
return pathSet{} // There are no packages in the main module, so the main module isn't needed to resolve them.
1101+
}
1102+
1103+
return pathSet{pkgMods: []module.Version{mainModule}}
1104+
})
1105+
}
1106+
}
1107+
10731108
// performPatternAllQueries populates the candidates for each query whose
10741109
// pattern is "all".
10751110
//

src/cmd/go/testdata/script/mod_get_nopkgs.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysu
2929
! go get builtin/... # in GOROOT/src, but contains no packages
3030
stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$'
3131

32+
cd ../subdirmod
33+
go get work
34+
stderr -count=1 'matched no packages'
35+
3236
-- go.mod --
3337
module example.net/emptysubdir
3438

@@ -38,3 +42,7 @@ go 1.16
3842
package emptysubdir
3943
-- subdir/README.txt --
4044
This module intentionally does not contain any p
45+
-- subdirmod/go.mod --
46+
module example.net/emptysubdir/subdirmod
47+
48+
go 1.16
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Test go get with the work pattern.
2+
3+
# go get work gets dependencies to satisfy missing imports in the
4+
# main modules' package graph. Before the 'work' pattern existed, users
5+
# would have to run './...' in the root of the work (main) module.
6+
cp go.mod go.mod.orig
7+
go get work
8+
cmp go.mod go.mod.want
9+
10+
# 'go get work' and 'go get all' behave very differently. Because
11+
# 'all' evaluates to work packages but also to their dependencies,
12+
# 'go get all' will run the 'get' logic on all the dependency module
13+
# packages, bumping all their modules to the latest versions.
14+
cp go.mod.orig go.mod
15+
go get all
16+
cmp go.mod go.mod.all.want
17+
-- go.mod --
18+
module example.com/a
19+
20+
go 1.25
21+
-- go.mod.want --
22+
module example.com/a
23+
24+
go 1.25
25+
26+
require rsc.io/quote v1.5.2
27+
28+
require (
29+
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
30+
rsc.io/sampler v1.3.0 // indirect
31+
)
32+
-- go.mod.all.want --
33+
module example.com/a
34+
35+
go 1.25
36+
37+
require rsc.io/quote v1.5.2
38+
39+
require (
40+
golang.org/x/text v0.3.0 // indirect
41+
rsc.io/sampler v1.99.99 // indirect
42+
)
43+
-- a.go --
44+
package a
45+
46+
import _ "rsc.io/quote"

src/cmd/go/testdata/script/mod_get_work_incomplete.txt renamed to src/cmd/go/testdata/script/mod_get_workspace_incomplete.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ go get ./...
2020
cmp go.mod go.mod.want
2121
cmp go.sum go.sum.want
2222

23+
# Test go get with an incomplete module using a "work" query.
24+
cp go.mod.orig go.mod
25+
rm go.sum
26+
go get work
27+
cmp go.mod go.mod.want
28+
cmp go.sum go.sum.want
29+
2330
# Test go get with an incomplete module using a path query that can be resolved.
2431
cp go.mod.orig go.mod
2532
rm go.sum

0 commit comments

Comments
 (0)