1
2
3
4
5
6
7 package mvs
8
9 import (
10 "fmt"
11 "reflect"
12 "sort"
13 "sync"
14
15 "cmd/go/internal/par"
16
17 "golang.org/x/mod/module"
18 )
19
20
21
22
23
24
25
26
27
28
29
30 type Reqs interface {
31
32
33 Required(m module.Version) ([]module.Version, error)
34
35
36
37
38
39
40
41
42
43 Max(v1, v2 string) string
44 }
45
46
47 type UpgradeReqs interface {
48 Reqs
49
50
51
52
53
54
55
56
57
58
59
60
61 Upgrade(m module.Version) (module.Version, error)
62 }
63
64
65 type DowngradeReqs interface {
66 Reqs
67
68
69
70 Previous(m module.Version) (module.Version, error)
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 func BuildList(targets []module.Version, reqs Reqs) ([]module.Version, error) {
90 return buildList(targets, reqs, nil)
91 }
92
93 func buildList(targets []module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) {
94 cmp := func(v1, v2 string) int {
95 if reqs.Max(v1, v2) != v1 {
96 return -1
97 }
98 if reqs.Max(v2, v1) != v2 {
99 return 1
100 }
101 return 0
102 }
103
104 var (
105 mu sync.Mutex
106 g = NewGraph(cmp, targets)
107 upgrades = map[module.Version]module.Version{}
108 errs = map[module.Version]error{}
109 )
110
111
112
113 var work par.Work
114 for _, target := range targets {
115 work.Add(target)
116 }
117 work.Do(10, func(item any) {
118 m := item.(module.Version)
119
120 var required []module.Version
121 var err error
122 if m.Version != "none" {
123 required, err = reqs.Required(m)
124 }
125
126 u := m
127 if upgrade != nil {
128 upgradeTo, upErr := upgrade(m)
129 if upErr == nil {
130 u = upgradeTo
131 } else if err == nil {
132 err = upErr
133 }
134 }
135
136 mu.Lock()
137 if err != nil {
138 errs[m] = err
139 }
140 if u != m {
141 upgrades[m] = u
142 required = append([]module.Version{u}, required...)
143 }
144 g.Require(m, required)
145 mu.Unlock()
146
147 for _, r := range required {
148 work.Add(r)
149 }
150 })
151
152
153
154 if len(errs) > 0 {
155 errPath := g.FindPath(func(m module.Version) bool {
156 return errs[m] != nil
157 })
158 if len(errPath) == 0 {
159 panic("internal error: could not reconstruct path to module with error")
160 }
161
162 err := errs[errPath[len(errPath)-1]]
163 isUpgrade := func(from, to module.Version) bool {
164 if u, ok := upgrades[from]; ok {
165 return u == to
166 }
167 return false
168 }
169 return nil, NewBuildListError(err.(error), errPath, isUpgrade)
170 }
171
172
173 list := g.BuildList()
174 if vs := list[:len(targets)]; !reflect.DeepEqual(vs, targets) {
175
176
177
178
179 panic(fmt.Sprintf("mistake: chose versions %+v instead of targets %+v", vs, targets))
180 }
181 return list, nil
182 }
183
184
185
186
187 func Req(mainModule module.Version, base []string, reqs Reqs) ([]module.Version, error) {
188 list, err := BuildList([]module.Version{mainModule}, reqs)
189 if err != nil {
190 return nil, err
191 }
192
193
194
195
196
197
198 var postorder []module.Version
199 reqCache := map[module.Version][]module.Version{}
200 reqCache[mainModule] = nil
201
202 var walk func(module.Version) error
203 walk = func(m module.Version) error {
204 _, ok := reqCache[m]
205 if ok {
206 return nil
207 }
208 required, err := reqs.Required(m)
209 if err != nil {
210 return err
211 }
212 reqCache[m] = required
213 for _, m1 := range required {
214 if err := walk(m1); err != nil {
215 return err
216 }
217 }
218 postorder = append(postorder, m)
219 return nil
220 }
221 for _, m := range list {
222 if err := walk(m); err != nil {
223 return nil, err
224 }
225 }
226
227
228 have := map[module.Version]bool{}
229 walk = func(m module.Version) error {
230 if have[m] {
231 return nil
232 }
233 have[m] = true
234 for _, m1 := range reqCache[m] {
235 walk(m1)
236 }
237 return nil
238 }
239 max := map[string]string{}
240 for _, m := range list {
241 if v, ok := max[m.Path]; ok {
242 max[m.Path] = reqs.Max(m.Version, v)
243 } else {
244 max[m.Path] = m.Version
245 }
246 }
247
248 var min []module.Version
249 haveBase := map[string]bool{}
250 for _, path := range base {
251 if haveBase[path] {
252 continue
253 }
254 m := module.Version{Path: path, Version: max[path]}
255 min = append(min, m)
256 walk(m)
257 haveBase[path] = true
258 }
259
260 for i := len(postorder) - 1; i >= 0; i-- {
261 m := postorder[i]
262 if max[m.Path] != m.Version {
263
264 continue
265 }
266 if !have[m] {
267 min = append(min, m)
268 walk(m)
269 }
270 }
271 sort.Slice(min, func(i, j int) bool {
272 return min[i].Path < min[j].Path
273 })
274 return min, nil
275 }
276
277
278
279 func UpgradeAll(target module.Version, reqs UpgradeReqs) ([]module.Version, error) {
280 return buildList([]module.Version{target}, reqs, func(m module.Version) (module.Version, error) {
281 if m.Path == target.Path {
282 return target, nil
283 }
284
285 return reqs.Upgrade(m)
286 })
287 }
288
289
290
291 func Upgrade(target module.Version, reqs UpgradeReqs, upgrade ...module.Version) ([]module.Version, error) {
292 list, err := reqs.Required(target)
293 if err != nil {
294 return nil, err
295 }
296
297 pathInList := make(map[string]bool, len(list))
298 for _, m := range list {
299 pathInList[m.Path] = true
300 }
301 list = append([]module.Version(nil), list...)
302
303 upgradeTo := make(map[string]string, len(upgrade))
304 for _, u := range upgrade {
305 if !pathInList[u.Path] {
306 list = append(list, module.Version{Path: u.Path, Version: "none"})
307 }
308 if prev, dup := upgradeTo[u.Path]; dup {
309 upgradeTo[u.Path] = reqs.Max(prev, u.Version)
310 } else {
311 upgradeTo[u.Path] = u.Version
312 }
313 }
314
315 return buildList([]module.Version{target}, &override{target, list, reqs}, func(m module.Version) (module.Version, error) {
316 if v, ok := upgradeTo[m.Path]; ok {
317 return module.Version{Path: m.Path, Version: v}, nil
318 }
319 return m, nil
320 })
321 }
322
323
324
325
326
327
328
329
330 func Downgrade(target module.Version, reqs DowngradeReqs, downgrade ...module.Version) ([]module.Version, error) {
331
332
333
334
335
336
337
338 list, err := BuildList([]module.Version{target}, reqs)
339 if err != nil {
340 return nil, err
341 }
342 list = list[1:]
343
344 max := make(map[string]string)
345 for _, r := range list {
346 max[r.Path] = r.Version
347 }
348 for _, d := range downgrade {
349 if v, ok := max[d.Path]; !ok || reqs.Max(v, d.Version) != d.Version {
350 max[d.Path] = d.Version
351 }
352 }
353
354 var (
355 added = make(map[module.Version]bool)
356 rdeps = make(map[module.Version][]module.Version)
357 excluded = make(map[module.Version]bool)
358 )
359 var exclude func(module.Version)
360 exclude = func(m module.Version) {
361 if excluded[m] {
362 return
363 }
364 excluded[m] = true
365 for _, p := range rdeps[m] {
366 exclude(p)
367 }
368 }
369 var add func(module.Version)
370 add = func(m module.Version) {
371 if added[m] {
372 return
373 }
374 added[m] = true
375 if v, ok := max[m.Path]; ok && reqs.Max(m.Version, v) != v {
376
377
378
379 exclude(m)
380 return
381 }
382 list, err := reqs.Required(m)
383 if err != nil {
384
385
386
387
388
389
390
391
392
393
394 exclude(m)
395 return
396 }
397 for _, r := range list {
398 add(r)
399 if excluded[r] {
400 exclude(m)
401 return
402 }
403 rdeps[r] = append(rdeps[r], m)
404 }
405 }
406
407 downgraded := make([]module.Version, 0, len(list)+1)
408 downgraded = append(downgraded, target)
409 List:
410 for _, r := range list {
411 add(r)
412 for excluded[r] {
413 p, err := reqs.Previous(r)
414 if err != nil {
415
416
417
418
419
420 return nil, err
421 }
422
423
424
425
426 if v := max[r.Path]; reqs.Max(v, r.Version) != v && reqs.Max(p.Version, v) != p.Version {
427 p.Version = v
428 }
429 if p.Version == "none" {
430 continue List
431 }
432 add(p)
433 r = p
434 }
435 downgraded = append(downgraded, r)
436 }
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 actual, err := BuildList([]module.Version{target}, &override{
454 target: target,
455 list: downgraded,
456 Reqs: reqs,
457 })
458 if err != nil {
459 return nil, err
460 }
461 actualVersion := make(map[string]string, len(actual))
462 for _, m := range actual {
463 actualVersion[m.Path] = m.Version
464 }
465
466 downgraded = downgraded[:0]
467 for _, m := range list {
468 if v, ok := actualVersion[m.Path]; ok {
469 downgraded = append(downgraded, module.Version{Path: m.Path, Version: v})
470 }
471 }
472
473 return BuildList([]module.Version{target}, &override{
474 target: target,
475 list: downgraded,
476 Reqs: reqs,
477 })
478 }
479
480 type override struct {
481 target module.Version
482 list []module.Version
483 Reqs
484 }
485
486 func (r *override) Required(m module.Version) ([]module.Version, error) {
487 if m == r.target {
488 return r.list, nil
489 }
490 return r.Reqs.Required(m)
491 }
492
View as plain text