@@ -48,6 +48,11 @@ func (set Releases[R, D]) SortDescent() {
48
48
// dependency resolution
49
49
type Resolver [R Release [D ], D Dependency ] struct {
50
50
releases map [string ]Releases [R , D ]
51
+
52
+ // resolver state
53
+ solution map [string ]R
54
+ depsToProcess []D
55
+ problematicDeps map [dependencyHash ]int
51
56
}
52
57
53
58
// NewResolver creates a new archive
@@ -75,9 +80,9 @@ func (ar *Resolver[R, D]) AddReleases(rels ...R) {
75
80
// arguent using a backtracking algorithm. This function is NOT thread-safe.
76
81
func (ar * Resolver [R , D ]) Resolve (release R ) Releases [R , D ] {
77
82
// Initial empty state of the resolver
78
- solution : = map [string ]R {}
79
- depsToProcess : = []D {}
80
- problematicDeps : = map [dependencyHash ]int {}
83
+ ar . solution = map [string ]R {}
84
+ ar . depsToProcess = []D {}
85
+ ar . problematicDeps = map [dependencyHash ]int {}
81
86
82
87
// Check if the release is in the archive
83
88
if len (ar .releases [release .GetName ()].FilterBy (& Equals {Version : release .GetVersion ()})) == 0 {
@@ -86,9 +91,9 @@ func (ar *Resolver[R, D]) Resolve(release R) Releases[R, D] {
86
91
87
92
// Add the requested release to the solution and proceed
88
93
// with the dependencies resolution
89
- solution [release .GetName ()] = release
90
- depsToProcess = append (depsToProcess , release .GetDependencies ()... )
91
- return ar .resolve (solution , depsToProcess , problematicDeps )
94
+ ar . solution [release .GetName ()] = release
95
+ ar . depsToProcess = append (ar . depsToProcess , release .GetDependencies ()... )
96
+ return ar .resolve ()
92
97
}
93
98
94
99
type dependencyHash string
@@ -97,39 +102,45 @@ func hashDependency[D Dependency](dep D) dependencyHash {
97
102
return dependencyHash (dep .GetName () + "/" + dep .GetConstraint ().String ())
98
103
}
99
104
100
- func (ar * Resolver [R , D ]) resolve (solution map [ string ] R , depsToProcess [] D , problematicDeps map [ dependencyHash ] int ) Releases [R , D ] {
101
- debug ("deps to process: %s" , depsToProcess )
102
- if len (depsToProcess ) == 0 {
105
+ func (ar * Resolver [R , D ]) resolve () Releases [R , D ] {
106
+ debug ("deps to process: %s" , ar . depsToProcess )
107
+ if len (ar . depsToProcess ) == 0 {
103
108
debug ("All dependencies have been resolved." )
104
109
var res Releases [R , D ]
105
- for _ , v := range solution {
110
+ for _ , v := range ar . solution {
106
111
res = append (res , v )
107
112
}
108
113
return res
109
114
}
110
115
111
116
// Pick the first dependency in the deps to process
112
- dep := depsToProcess [0 ]
117
+ dep := ar . depsToProcess [0 ]
113
118
depName := dep .GetName ()
114
119
debug ("Considering next dep: %s" , depName )
115
120
116
121
// If a release is already picked in the solution check if it match the dep
117
- if existingRelease , has := solution [depName ]; has {
122
+ if existingRelease , has := ar . solution [depName ]; has {
118
123
if dep .GetConstraint ().Match (existingRelease .GetVersion ()) {
119
124
debug ("%s already in solution and matching" , existingRelease )
120
- return ar .resolve (solution , depsToProcess [1 :], problematicDeps )
125
+ oldDepsToProcess := ar .depsToProcess
126
+ ar .depsToProcess = ar .depsToProcess [1 :]
127
+ if res := ar .resolve (); res != nil {
128
+ return res
129
+ }
130
+ ar .depsToProcess = oldDepsToProcess
131
+ return nil
121
132
}
122
133
debug ("%s already in solution do not match... rollingback" , existingRelease )
123
134
return nil
124
135
}
125
136
126
137
// Otherwise start backtracking the dependency
127
- releases := ar .releases [dep . GetName () ].FilterBy (dep .GetConstraint ())
138
+ releases := ar .releases [depName ].FilterBy (dep .GetConstraint ())
128
139
129
140
// Consider the latest versions first
130
141
releases .SortDescent ()
131
-
132
142
debug ("releases matching criteria: %s" , releases )
143
+
133
144
backtracking_loop:
134
145
for _ , release := range releases {
135
146
releaseDeps := release .GetDependencies ()
@@ -142,21 +153,23 @@ backtracking_loop:
142
153
}
143
154
}
144
155
145
- solution [depName ] = release
146
- newDepsToProcess := append (depsToProcess [1 :], deps ... )
156
+ ar .solution [depName ] = release
157
+ oldDepsToProcess := ar .depsToProcess
158
+ ar .depsToProcess = append (ar .depsToProcess [1 :], releaseDeps ... )
147
159
// bubble up problematics deps so they are processed first
148
- sort .Slice (newDepsToProcess , func (i , j int ) bool {
149
- ci := hashDependency (newDepsToProcess [i ])
150
- cj := hashDependency (newDepsToProcess [j ])
151
- return problematicDeps [ci ] > problematicDeps [cj ]
160
+ sort .Slice (ar . depsToProcess , func (i , j int ) bool {
161
+ ci := hashDependency (ar . depsToProcess [i ])
162
+ cj := hashDependency (ar . depsToProcess [j ])
163
+ return ar . problematicDeps [ci ] > ar . problematicDeps [cj ]
152
164
})
153
- if res := ar .resolve (solution , newDepsToProcess , problematicDeps ); res != nil {
165
+ if res := ar .resolve (); res != nil {
154
166
return res
155
167
}
168
+ ar .depsToProcess = oldDepsToProcess
156
169
debug ("%s did not work..." , release )
157
- delete (solution , depName )
170
+ delete (ar . solution , depName )
158
171
}
159
172
160
- problematicDeps [hashDependency (dep )]++
173
+ ar . problematicDeps [hashDependency (dep )]++
161
174
return nil
162
175
}
0 commit comments