1
2
3
4
5 package mvs
6
7 import (
8 "fmt"
9 "slices"
10
11 "cmd/go/internal/gover"
12
13 "golang.org/x/mod/module"
14 )
15
16
17
18 type Graph struct {
19 cmp func(p, v1, v2 string) int
20 roots []module.Version
21
22 required map[module.Version][]module.Version
23
24 isRoot map[module.Version]bool
25 selected map[string]string
26 }
27
28
29
30
31
32
33 func NewGraph(cmp func(p, v1, v2 string) int, roots []module.Version) *Graph {
34 g := &Graph{
35 cmp: cmp,
36 roots: slices.Clip(roots),
37 required: make(map[module.Version][]module.Version),
38 isRoot: make(map[module.Version]bool),
39 selected: make(map[string]string),
40 }
41
42 for _, m := range roots {
43 g.isRoot[m] = true
44 if g.cmp(m.Path, g.Selected(m.Path), m.Version) < 0 {
45 g.selected[m.Path] = m.Version
46 }
47 }
48
49 return g
50 }
51
52
53
54
55
56
57
58
59
60 func (g *Graph) Require(m module.Version, reqs []module.Version) {
61
62
63
64 if _, reachable := g.isRoot[m]; !reachable {
65 panic(fmt.Sprintf("%v is not reachable from any root", m))
66 }
67
68
69
70 reqs = slices.Clip(reqs)
71
72 if _, dup := g.required[m]; dup {
73 panic(fmt.Sprintf("requirements of %v have already been set", m))
74 }
75 g.required[m] = reqs
76
77 for _, dep := range reqs {
78
79 if _, ok := g.isRoot[dep]; !ok {
80 g.isRoot[dep] = false
81 }
82
83 if g.cmp(dep.Path, g.Selected(dep.Path), dep.Version) < 0 {
84 g.selected[dep.Path] = dep.Version
85 }
86 }
87 }
88
89
90
91
92
93
94
95 func (g *Graph) RequiredBy(m module.Version) (reqs []module.Version, ok bool) {
96 reqs, ok = g.required[m]
97 return reqs, ok
98 }
99
100
101
102
103 func (g *Graph) Selected(path string) (version string) {
104 v, ok := g.selected[path]
105 if !ok {
106 return "none"
107 }
108 return v
109 }
110
111
112
113
114
115
116 func (g *Graph) BuildList() []module.Version {
117 seenRoot := make(map[string]bool, len(g.roots))
118
119 var list []module.Version
120 for _, r := range g.roots {
121 if seenRoot[r.Path] {
122
123
124
125
126
127
128 continue
129 }
130
131 if v := g.Selected(r.Path); v != "none" {
132 list = append(list, module.Version{Path: r.Path, Version: v})
133 }
134 seenRoot[r.Path] = true
135 }
136 uniqueRoots := list
137
138 for path, version := range g.selected {
139 if !seenRoot[path] {
140 list = append(list, module.Version{Path: path, Version: version})
141 }
142 }
143 gover.ModSort(list[len(uniqueRoots):])
144
145 return list
146 }
147
148
149
150
151 func (g *Graph) WalkBreadthFirst(f func(m module.Version)) {
152 var queue []module.Version
153 enqueued := make(map[module.Version]bool)
154 for _, m := range g.roots {
155 if m.Version != "none" {
156 queue = append(queue, m)
157 enqueued[m] = true
158 }
159 }
160
161 for len(queue) > 0 {
162 m := queue[0]
163 queue = queue[1:]
164
165 f(m)
166
167 reqs, _ := g.RequiredBy(m)
168 for _, r := range reqs {
169 if !enqueued[r] && r.Version != "none" {
170 queue = append(queue, r)
171 enqueued[r] = true
172 }
173 }
174 }
175 }
176
177
178
179
180 func (g *Graph) FindPath(f func(module.Version) bool) []module.Version {
181
182
183 firstRequires := make(map[module.Version]module.Version)
184
185 queue := g.roots
186 for _, m := range g.roots {
187 firstRequires[m] = module.Version{}
188 }
189
190 for len(queue) > 0 {
191 m := queue[0]
192 queue = queue[1:]
193
194 if f(m) {
195
196
197 path := []module.Version{m}
198 for {
199 m = firstRequires[m]
200 if m.Path == "" {
201 break
202 }
203 path = append(path, m)
204 }
205
206 i, j := 0, len(path)-1
207 for i < j {
208 path[i], path[j] = path[j], path[i]
209 i++
210 j--
211 }
212
213 return path
214 }
215
216 reqs, _ := g.RequiredBy(m)
217 for _, r := range reqs {
218 if _, seen := firstRequires[r]; !seen {
219 queue = append(queue, r)
220 firstRequires[r] = m
221 }
222 }
223 }
224
225 return nil
226 }
227
View as plain text