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