Skip to content

Commit ea26ce7

Browse files
committed
cmd/go: examine dependencies of main modules in workspace mode
To make sure that we properly pull in everything in all, because different main modules can interfere with each others' pruning. Fixes #49763 Change-Id: I0756993d8ae9919ccb27ec460d579d348c38ec3b Reviewed-on: https://go-review.googlesource.com/c/go/+/370663 Trust: Michael Matloob <[email protected]> Run-TryBot: Michael Matloob <[email protected]> Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 1d3a5b4 commit ea26ce7

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

src/cmd/go/internal/modload/buildlist.go

+46
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,52 @@ func readModGraph(ctx context.Context, pruning modPruning, roots []module.Versio
386386
}
387387
<-loadQueue.Idle()
388388

389+
// Reload any dependencies of the main modules which are not
390+
// at their selected versions at workspace mode, because the
391+
// requirements don't accurately reflect the transitive imports.
392+
if pruning == workspace {
393+
// hasDepsInAll contains the set of modules that need to be loaded
394+
// at workspace pruning because any of their dependencies may
395+
// provide packages in all.
396+
hasDepsInAll := make(map[string]bool)
397+
seen := map[module.Version]bool{}
398+
for _, m := range roots {
399+
hasDepsInAll[m.Path] = true
400+
seen[m] = true
401+
}
402+
// This loop will terminate because it will call enqueue on each version of
403+
// each dependency of the modules in hasDepsInAll at most once (and only
404+
// calls enqueue on successively increasing versions of each dependency).
405+
for {
406+
needsEnqueueing := map[module.Version]bool{}
407+
for p := range hasDepsInAll {
408+
m := module.Version{Path: p, Version: mg.g.Selected(p)}
409+
reqs, ok := mg.g.RequiredBy(m)
410+
if !ok {
411+
needsEnqueueing[m] = true
412+
continue
413+
}
414+
for _, r := range reqs {
415+
s := module.Version{Path: r.Path, Version: mg.g.Selected(r.Path)}
416+
if cmpVersion(s.Version, r.Version) > 0 && !seen[s] {
417+
needsEnqueueing[s] = true
418+
}
419+
}
420+
}
421+
// add all needs enqueueing to paths we care about
422+
if len(needsEnqueueing) == 0 {
423+
break
424+
}
425+
426+
for p := range needsEnqueueing {
427+
enqueue(p, workspace)
428+
seen[p] = true
429+
hasDepsInAll[p.Path] = true
430+
}
431+
<-loadQueue.Idle()
432+
}
433+
}
434+
389435
if hasError {
390436
return mg, mg.findError()
391437
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# This test makes sure workspace mode's handling of the module graph
2+
# is compatible with module pruning. The graph we load from either of
3+
# the workspace modules should be the same, even if their graphs
4+
# don't overlap.
5+
#
6+
# This is the module graph in the test:
7+
#
8+
# example.com/p -> example.com/q v1.0.0
9+
# example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0 -> example.com/w v1.0.0 -> example.com/x v1.0.0 -> example.com/y v1.0.0
10+
# |-> example.com/z v1.0.0 |-> example.com/z v1.1.0
11+
# |-> example.com/q v1.0.5 -> example.com/r v1.0.0
12+
# If we didn't load the whole graph and didn't load the dependencies of b
13+
# when loading p, we would end up loading q v1.0.0, rather than v1.1.0,
14+
# which is selected by MVS.
15+
16+
go list -m all
17+
stdout 'example.com/w v1.0.0'
18+
stdout 'example.com/q v1.1.0'
19+
stdout 'example.com/z v1.1.0'
20+
stdout 'example.com/x v1.0.0'
21+
! stdout 'example.com/r'
22+
! stdout 'example.com/y'
23+
24+
-- go.work --
25+
go 1.18
26+
27+
use (
28+
./a
29+
./p
30+
)
31+
32+
replace example.com/b v1.0.0 => ./b
33+
replace example.com/q v1.0.0 => ./q1_0_0
34+
replace example.com/q v1.0.5 => ./q1_0_5
35+
replace example.com/q v1.1.0 => ./q1_1_0
36+
replace example.com/r v1.0.0 => ./r
37+
replace example.com/w v1.0.0 => ./w
38+
replace example.com/x v1.0.0 => ./x
39+
replace example.com/y v1.0.0 => ./y
40+
replace example.com/z v1.0.0 => ./z1_0_0
41+
replace example.com/z v1.1.0 => ./z1_1_0
42+
43+
-- a/go.mod --
44+
module example.com/a
45+
46+
go 1.18
47+
48+
require example.com/b v1.0.0
49+
require example.com/z v1.0.0
50+
-- a/foo.go --
51+
package main
52+
53+
import "example.com/b"
54+
55+
func main() {
56+
b.B()
57+
}
58+
-- b/go.mod --
59+
module example.com/b
60+
61+
go 1.18
62+
63+
require example.com/q v1.1.0
64+
-- b/b.go --
65+
package b
66+
67+
func B() {
68+
}
69+
-- p/go.mod --
70+
module example.com/p
71+
72+
go 1.18
73+
74+
require example.com/q v1.0.0
75+
76+
replace example.com/q v1.0.0 => ../q1_0_0
77+
replace example.com/q v1.1.0 => ../q1_1_0
78+
-- p/main.go --
79+
package main
80+
81+
import "example.com/q"
82+
83+
func main() {
84+
q.PrintVersion()
85+
}
86+
-- q1_0_0/go.mod --
87+
module example.com/q
88+
89+
go 1.18
90+
-- q1_0_0/q.go --
91+
package q
92+
93+
import "fmt"
94+
95+
func PrintVersion() {
96+
fmt.Println("version 1.0.0")
97+
}
98+
-- q1_0_5/go.mod --
99+
module example.com/q
100+
101+
go 1.18
102+
103+
require example.com/r v1.0.0
104+
-- q1_0_5/q.go --
105+
package q
106+
107+
import _ "example.com/r"
108+
-- q1_1_0/go.mod --
109+
module example.com/q
110+
111+
require example.com/w v1.0.0
112+
require example.com/z v1.1.0
113+
114+
go 1.18
115+
-- q1_1_0/q.go --
116+
package q
117+
118+
import _ "example.com/w"
119+
import _ "example.com/z"
120+
121+
import "fmt"
122+
123+
func PrintVersion() {
124+
fmt.Println("version 1.1.0")
125+
}
126+
-- r/go.mod --
127+
module example.com/r
128+
129+
go 1.18
130+
131+
require example.com/r v1.0.0
132+
-- r/r.go --
133+
package r
134+
-- w/go.mod --
135+
module example.com/w
136+
137+
go 1.18
138+
139+
require example.com/x v1.0.0
140+
-- w/w.go --
141+
package w
142+
-- w/w_test.go --
143+
package w
144+
145+
import _ "example.com/x"
146+
-- x/go.mod --
147+
module example.com/x
148+
149+
go 1.18
150+
-- x/x.go --
151+
package x
152+
-- x/x_test.go --
153+
package x
154+
import _ "example.com/y"
155+
-- y/go.mod --
156+
module example.com/y
157+
158+
go 1.18
159+
-- y/y.go --
160+
package y
161+
-- z1_0_0/go.mod --
162+
module example.com/z
163+
164+
go 1.18
165+
166+
require example.com/q v1.0.5
167+
-- z1_0_0/z.go --
168+
package z
169+
170+
import _ "example.com/q"
171+
-- z1_1_0/go.mod --
172+
module example.com/z
173+
174+
go 1.18
175+
-- z1_1_0/z.go --
176+
package z

0 commit comments

Comments
 (0)