solve 3.1.1 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/solve.rb +10 -11
- data/lib/solve/artifact.rb +10 -10
- data/lib/solve/constraint.rb +47 -47
- data/lib/solve/demand.rb +2 -2
- data/lib/solve/dependency.rb +4 -2
- data/lib/solve/errors.rb +3 -3
- data/lib/solve/gecode_solver.rb +98 -95
- data/lib/solve/graph.rb +3 -3
- data/lib/solve/ruby_solver.rb +128 -63
- data/lib/solve/solver/serializer.rb +53 -53
- data/lib/solve/version.rb +1 -1
- metadata +14 -52
- data/.gitignore +0 -20
- data/.travis.yml +0 -22
- data/Gemfile +0 -33
- data/Guardfile +0 -17
- data/NoGecode.gemfile +0 -4
- data/README.md +0 -85
- data/Rakefile +0 -1
- data/Thorfile +0 -36
- data/solve.gemspec +0 -26
- data/spec/acceptance/benchmark.rb +0 -59
- data/spec/acceptance/large_graph_no_solution.rb +0 -18730
- data/spec/acceptance/opscode_ci_graph.rb +0 -18600
- data/spec/acceptance/ruby_solver_solutions_spec.rb +0 -307
- data/spec/acceptance/solutions_spec.rb +0 -317
- data/spec/spec_helper.rb +0 -25
- data/spec/unit/solve/artifact_spec.rb +0 -137
- data/spec/unit/solve/demand_spec.rb +0 -53
- data/spec/unit/solve/dependency_spec.rb +0 -25
- data/spec/unit/solve/gecode_solver_spec.rb +0 -206
- data/spec/unit/solve/graph_spec.rb +0 -130
- data/spec/unit/solve/ruby_solver_spec.rb +0 -166
- data/spec/unit/solve/solver/serializer_spec.rb +0 -33
- data/spec/unit/solve_spec.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5d0ae05eff693dfa5424fa6d2c92f96839d997da2d585505992e70f91e6ab863
|
4
|
+
data.tar.gz: 4ddae0850f4f234d98208ed8e42f6a2073b43e6f1d6696935c09127cd30c0bf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c4b234445b2b64082b1cdfecad5f4b49b3c13b6757237033c4b23d359a28d9899f24eb2545cf686f238a037504f1151c6924f0c376e7819b142335e6d5255a3
|
7
|
+
data.tar.gz: 67dc423bc183e3c7b680ba9078340179556561837ef0eb14b0f3f650bbc3e4bd0430137d20a1e1998dca4e7c17f1890f7379147bf0e258a1fa1fad0bbbf3278e
|
data/lib/solve.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require "semverse"
|
2
2
|
|
3
3
|
module Solve
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
9
|
-
require_relative
|
10
|
-
require_relative
|
11
|
-
require_relative
|
4
|
+
require_relative "solve/artifact"
|
5
|
+
require_relative "solve/demand"
|
6
|
+
require_relative "solve/dependency"
|
7
|
+
require_relative "solve/version"
|
8
|
+
require_relative "solve/errors"
|
9
|
+
require_relative "solve/graph"
|
10
|
+
require_relative "solve/ruby_solver"
|
11
|
+
require_relative "solve/gecode_solver"
|
12
12
|
|
13
13
|
# We have to set the default engine here, it gets set on the wrong object if
|
14
14
|
# we put this in the metaclass context below.
|
@@ -23,7 +23,6 @@ module Solve
|
|
23
23
|
# @return [Symbol]
|
24
24
|
attr_reader :engine
|
25
25
|
|
26
|
-
|
27
26
|
# Sets the solving backend engine. Solve supports 2 engines:
|
28
27
|
# * `:ruby` - Molinillo, a pure ruby solver
|
29
28
|
# * `:gecode` - dep-selector, a wrapper around the Gecode CSP solver library
|
@@ -42,6 +41,7 @@ module Solve
|
|
42
41
|
else
|
43
42
|
engine_class.activate
|
44
43
|
end
|
44
|
+
|
45
45
|
@engine = selected_engine
|
46
46
|
end
|
47
47
|
|
@@ -77,4 +77,3 @@ module Solve
|
|
77
77
|
end
|
78
78
|
|
79
79
|
end
|
80
|
-
|
data/lib/solve/artifact.rb
CHANGED
@@ -74,7 +74,7 @@ module Solve
|
|
74
74
|
# .depends('ntp', '~> 1.3')
|
75
75
|
#
|
76
76
|
# @return [Solve::Artifact]
|
77
|
-
def depends(name, constraint =
|
77
|
+
def depends(name, constraint = ">= 0.0.0")
|
78
78
|
unless dependency?(name, constraint)
|
79
79
|
set_dependency(name, constraint)
|
80
80
|
end
|
@@ -91,8 +91,8 @@ module Solve
|
|
91
91
|
# @return [Boolean]
|
92
92
|
def ==(other)
|
93
93
|
other.is_a?(self.class) &&
|
94
|
-
|
95
|
-
|
94
|
+
name == other.name &&
|
95
|
+
version == other.version
|
96
96
|
end
|
97
97
|
alias_method :eql?, :==
|
98
98
|
|
@@ -100,17 +100,17 @@ module Solve
|
|
100
100
|
#
|
101
101
|
# @return [Integer]
|
102
102
|
def <=>(other)
|
103
|
-
|
103
|
+
version <=> other.version
|
104
104
|
end
|
105
105
|
|
106
106
|
private
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
def get_dependency(name, constraint)
|
109
|
+
@dependencies["#{name}-#{constraint}"]
|
110
|
+
end
|
111
111
|
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
def set_dependency(name, constraint)
|
113
|
+
@dependencies["#{name}-#{constraint}"] = Dependency.new(self, name, constraint)
|
114
|
+
end
|
115
115
|
end
|
116
116
|
end
|
data/lib/solve/constraint.rb
CHANGED
@@ -48,17 +48,17 @@ module Solve
|
|
48
48
|
end
|
49
49
|
|
50
50
|
split_version = case version.to_s
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
51
|
+
when /^(\d+)\.(\d+)\.(\d+)(-([0-9a-z\-\.]+))?(\+([0-9a-z\-\.]+))?$/i
|
52
|
+
[ $1.to_i, $2.to_i, $3.to_i, $5, $7 ]
|
53
|
+
when /^(\d+)\.(\d+)\.(\d+)?$/
|
54
|
+
[ $1.to_i, $2.to_i, $3.to_i, nil, nil ]
|
55
|
+
when /^(\d+)\.(\d+)?$/
|
56
|
+
[ $1.to_i, $2.to_i, nil, nil, nil ]
|
57
|
+
when /^(\d+)$/
|
58
|
+
[ $1.to_i, nil, nil, nil, nil ]
|
59
|
+
else
|
60
|
+
raise Errors::InvalidConstraintFormat.new(constraint)
|
61
|
+
end
|
62
62
|
|
63
63
|
[ operator, split_version ].flatten
|
64
64
|
end
|
@@ -110,30 +110,30 @@ module Solve
|
|
110
110
|
def compare_approx(constraint, target_version)
|
111
111
|
min = constraint.version
|
112
112
|
max = if constraint.patch.nil?
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
113
|
+
Semverse::Version.new([min.major + 1, 0, 0, 0])
|
114
|
+
elsif constraint.build
|
115
|
+
identifiers = constraint.version.identifiers(:build)
|
116
|
+
replace = identifiers.last.to_i.to_s == identifiers.last.to_s ? "-" : nil
|
117
|
+
Semverse::Version.new([min.major, min.minor, min.patch, min.pre_release, identifiers.fill(replace, -1).join(".")])
|
118
|
+
elsif constraint.pre_release
|
119
|
+
identifiers = constraint.version.identifiers(:pre_release)
|
120
|
+
replace = identifiers.last.to_i.to_s == identifiers.last.to_s ? "-" : nil
|
121
|
+
Semverse::Version.new([min.major, min.minor, min.patch, identifiers.fill(replace, -1).join(".")])
|
122
|
+
else
|
123
|
+
Semverse::Version.new([min.major, min.minor + 1, 0, 0])
|
124
|
+
end
|
125
125
|
min <= target_version && target_version < max
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
129
|
OPERATOR_TYPES = {
|
130
130
|
"~>" => :approx,
|
131
|
-
"~"
|
131
|
+
"~" => :approx,
|
132
132
|
">=" => :greater_than_equal,
|
133
133
|
"<=" => :less_than_equal,
|
134
|
-
"="
|
135
|
-
">"
|
136
|
-
"<"
|
134
|
+
"=" => :equal,
|
135
|
+
">" => :greater_than,
|
136
|
+
"<" => :less_than,
|
137
137
|
}.freeze
|
138
138
|
|
139
139
|
COMPARE_FUNS = {
|
@@ -142,10 +142,10 @@ module Solve
|
|
142
142
|
greater_than: method(:compare_gt),
|
143
143
|
less_than_equal: method(:compare_lte),
|
144
144
|
less_than: method(:compare_lt),
|
145
|
-
equal: method(:compare_equal)
|
145
|
+
equal: method(:compare_equal),
|
146
146
|
}.freeze
|
147
147
|
|
148
|
-
REGEXP = /^(#{OPERATOR_TYPES.keys.join('|')})\s?(.+)
|
148
|
+
REGEXP = /^(#{OPERATOR_TYPES.keys.join('|')})\s?(.+)$/.freeze
|
149
149
|
|
150
150
|
attr_reader :operator
|
151
151
|
attr_reader :major
|
@@ -164,7 +164,7 @@ module Solve
|
|
164
164
|
def initialize(constraint = nil)
|
165
165
|
constraint = constraint.to_s
|
166
166
|
if constraint.nil? || constraint.empty?
|
167
|
-
constraint =
|
167
|
+
constraint = ">= 0.0.0"
|
168
168
|
end
|
169
169
|
|
170
170
|
@operator, @major, @minor, @patch, @pre_release, @build = self.class.split(constraint)
|
@@ -175,18 +175,18 @@ module Solve
|
|
175
175
|
end
|
176
176
|
|
177
177
|
@version = Semverse::Version.new([
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
178
|
+
major,
|
179
|
+
minor,
|
180
|
+
patch,
|
181
|
+
pre_release,
|
182
|
+
build,
|
183
183
|
])
|
184
184
|
end
|
185
185
|
|
186
186
|
# @return [Symbol]
|
187
187
|
def operator_type
|
188
|
-
unless type = OPERATOR_TYPES.fetch(operator)
|
189
|
-
raise
|
188
|
+
unless ( type = OPERATOR_TYPES.fetch(operator) )
|
189
|
+
raise "unknown operator type: #{operator}"
|
190
190
|
end
|
191
191
|
|
192
192
|
type
|
@@ -201,7 +201,7 @@ module Solve
|
|
201
201
|
def satisfies?(target)
|
202
202
|
target = Semverse::Version.coerce(target)
|
203
203
|
|
204
|
-
return false if !version
|
204
|
+
return false if !(version == 0) && greedy_match?(target)
|
205
205
|
|
206
206
|
compare(target)
|
207
207
|
end
|
@@ -215,13 +215,13 @@ module Solve
|
|
215
215
|
# @return [Boolean]
|
216
216
|
def ==(other)
|
217
217
|
other.is_a?(self.class) &&
|
218
|
-
|
219
|
-
|
218
|
+
operator == other.operator &&
|
219
|
+
version == other.version
|
220
220
|
end
|
221
221
|
alias_method :eql?, :==
|
222
222
|
|
223
223
|
def inspect
|
224
|
-
"#<#{self.class
|
224
|
+
"#<#{self.class} #{self}>"
|
225
225
|
end
|
226
226
|
|
227
227
|
def to_s
|
@@ -241,15 +241,15 @@ module Solve
|
|
241
241
|
#
|
242
242
|
# @param [Semverse::Version] target_version
|
243
243
|
#
|
244
|
-
|
245
|
-
|
246
|
-
|
244
|
+
def greedy_match?(target_version)
|
245
|
+
operator_type !~ /less/ && target_version.pre_release? && !version.pre_release?
|
246
|
+
end
|
247
247
|
|
248
248
|
# @param [Semverse::Version] target
|
249
249
|
#
|
250
250
|
# @return [Boolean]
|
251
|
-
|
252
|
-
|
253
|
-
|
251
|
+
def compare(target)
|
252
|
+
COMPARE_FUNS.fetch(operator_type).call(self, target)
|
253
|
+
end
|
254
254
|
end
|
255
255
|
end
|
data/lib/solve/demand.rb
CHANGED
data/lib/solve/dependency.rb
CHANGED
@@ -27,14 +27,16 @@ module Solve
|
|
27
27
|
def to_s
|
28
28
|
"#{name} (#{constraint})"
|
29
29
|
end
|
30
|
+
alias :inspect :to_s
|
30
31
|
|
31
32
|
# @param [Object] other
|
32
33
|
#
|
33
34
|
# @return [Boolean]
|
34
35
|
def ==(other)
|
35
36
|
other.is_a?(self.class) &&
|
36
|
-
|
37
|
-
|
37
|
+
name == other.name &&
|
38
|
+
artifact == other.artifact &&
|
39
|
+
constraint == other.constraint
|
38
40
|
end
|
39
41
|
alias_method :eql?, :==
|
40
42
|
end
|
data/lib/solve/errors.rb
CHANGED
@@ -47,14 +47,14 @@ module Solve
|
|
47
47
|
def to_s
|
48
48
|
s = ""
|
49
49
|
s << "#{@message}\n"
|
50
|
-
s << "Missing artifacts: #{missing_artifacts.join(
|
50
|
+
s << "Missing artifacts: #{missing_artifacts.join(",")}\n" unless missing_artifacts.empty?
|
51
51
|
unless constraints_excluding_all_artifacts.empty?
|
52
|
-
|
52
|
+
pretty = constraints_excluding_all_artifacts.map { |constraint| "(#{constraint[0]} #{constraint[1]})" }.join(",")
|
53
53
|
s << "Constraints that match no available version: #{pretty}\n"
|
54
54
|
end
|
55
55
|
s << "Demand that cannot be met: #{unsatisfiable_demand}\n" if unsatisfiable_demand
|
56
56
|
unless artifacts_with_no_satisfactory_version.empty?
|
57
|
-
s << "Artifacts for which there are conflicting dependencies: #{artifacts_with_no_satisfactory_version.join(
|
57
|
+
s << "Artifacts for which there are conflicting dependencies: #{artifacts_with_no_satisfactory_version.join(",")}"
|
58
58
|
end
|
59
59
|
s
|
60
60
|
end
|
data/lib/solve/gecode_solver.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require_relative
|
1
|
+
require "set" unless defined?(Set)
|
2
|
+
require_relative "errors"
|
3
|
+
require_relative "solver/serializer"
|
4
4
|
|
5
5
|
module Solve
|
6
6
|
class GecodeSolver
|
@@ -10,13 +10,13 @@ module Solve
|
|
10
10
|
#
|
11
11
|
# @return [Integer]
|
12
12
|
def timeout
|
13
|
-
seconds = 30 unless seconds = ENV["SOLVE_TIMEOUT"]
|
13
|
+
seconds = 30 unless ( seconds = ENV["SOLVE_TIMEOUT"] )
|
14
14
|
seconds.to_i * 1_000
|
15
15
|
end
|
16
16
|
|
17
17
|
# Attemp to load the dep_selector gem which this solver engine requires.
|
18
18
|
def activate
|
19
|
-
require
|
19
|
+
require "dep_selector"
|
20
20
|
rescue LoadError => e
|
21
21
|
raise Errors::EngineNotAvailable, "dep_selector is not installed, GecodeSolver cannot be used (#{e})"
|
22
22
|
end
|
@@ -84,126 +84,129 @@ module Solve
|
|
84
84
|
private
|
85
85
|
|
86
86
|
# DepSelector::DependencyGraph object representing the problem.
|
87
|
-
|
87
|
+
attr_reader :ds_graph
|
88
88
|
|
89
89
|
# Timeout in milliseconds. Hardcoded to 1s for now.
|
90
|
-
|
90
|
+
attr_reader :timeout_ms
|
91
91
|
|
92
92
|
# Runs the solver with the set of demands given. If any DepSelector
|
93
93
|
# exceptions are raised, they are rescued and re-raised
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
94
|
+
def solve_demands(demands_as_constraints)
|
95
|
+
selector = DepSelector::Selector.new(ds_graph, (timeout_ms / 1000.0))
|
96
|
+
selector.find_solution(demands_as_constraints, all_artifacts)
|
97
|
+
rescue DepSelector::Exceptions::InvalidSolutionConstraints => e
|
98
|
+
report_invalid_constraints_error(e)
|
99
|
+
rescue DepSelector::Exceptions::NoSolutionExists => e
|
100
|
+
report_no_solution_error(e)
|
101
|
+
rescue DepSelector::Exceptions::TimeBoundExceeded
|
102
|
+
# DepSelector timed out trying to find the solution. There may or may
|
103
|
+
# not be a solution.
|
104
|
+
raise Solve::Errors::NoSolutionError.new(
|
105
|
+
"The dependency constraints could not be solved in the time allotted."
|
106
|
+
)
|
107
|
+
rescue DepSelector::Exceptions::TimeBoundExceededNoSolution
|
108
|
+
# DepSelector determined there wasn't a solution to the problem, then
|
109
|
+
# timed out trying to determine which constraints cause the conflict.
|
110
|
+
raise Solve::Errors::NoSolutionCauseUnknown.new(
|
111
|
+
"There is a dependency conflict, but the solver could not determine the precise cause in the time allotted."
|
112
|
+
)
|
113
|
+
end
|
112
114
|
|
113
115
|
# Maps demands to corresponding DepSelector::SolutionConstraint objects.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
116
|
+
def demands_as_constraints
|
117
|
+
@demands_as_constraints ||= demands_array.map do |demands_item|
|
118
|
+
item_name, constraint_with_operator = demands_item
|
119
|
+
version_constraint = Semverse::Constraint.new(constraint_with_operator)
|
120
|
+
DepSelector::SolutionConstraint.new(ds_graph.package(item_name), version_constraint)
|
120
121
|
end
|
122
|
+
end
|
121
123
|
|
122
124
|
# Maps all artifacts in the graph to DepSelector::Package objects. If not
|
123
125
|
# already done, artifacts are added to the ds_graph as a necessary side effect.
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
def all_artifacts
|
127
|
+
return @all_artifacts if @all_artifacts
|
128
|
+
|
129
|
+
populate_ds_graph!
|
130
|
+
@all_artifacts
|
131
|
+
end
|
129
132
|
|
130
133
|
# Converts artifacts to DepSelector::Package objects and adds them to the
|
131
134
|
# DepSelector graph. This should only be called once; use #all_artifacts
|
132
135
|
# to safely get the set of all artifacts.
|
133
|
-
|
134
|
-
|
136
|
+
def populate_ds_graph!
|
137
|
+
@all_artifacts = Set.new
|
135
138
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
139
|
+
graph.artifacts.each do |artifact|
|
140
|
+
add_artifact_to_ds_graph(artifact)
|
141
|
+
@all_artifacts << ds_graph.package(artifact.name)
|
140
142
|
end
|
143
|
+
end
|
141
144
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end
|
148
|
-
package_version
|
145
|
+
def add_artifact_to_ds_graph(artifact)
|
146
|
+
package_version = ds_graph.package(artifact.name).add_version(artifact.version)
|
147
|
+
artifact.dependencies.each do |dependency|
|
148
|
+
dependency = DepSelector::Dependency.new(ds_graph.package(dependency.name), dependency.constraint)
|
149
|
+
package_version.dependencies << dependency
|
149
150
|
end
|
151
|
+
package_version
|
152
|
+
end
|
150
153
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
constrained_to_no_versions = e.constrained_to_no_versions.inject([]) do |list, constraint|
|
157
|
-
list << [constraint.package.name, constraint.constraint.to_s]
|
158
|
-
end
|
154
|
+
def report_invalid_constraints_error(e)
|
155
|
+
non_existent_cookbooks = e.non_existent_packages.inject([]) do |list, constraint|
|
156
|
+
list << constraint.package.name
|
157
|
+
end
|
159
158
|
|
160
|
-
|
161
|
-
|
162
|
-
missing_artifacts: non_existent_cookbooks,
|
163
|
-
constraints_excluding_all_artifacts: constrained_to_no_versions
|
164
|
-
)
|
159
|
+
constrained_to_no_versions = e.constrained_to_no_versions.inject([]) do |list, constraint|
|
160
|
+
list << [constraint.package.name, constraint.constraint.to_s]
|
165
161
|
end
|
166
162
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
163
|
+
raise Solve::Errors::NoSolutionError.new(
|
164
|
+
"Required artifacts do not exist at the desired version",
|
165
|
+
missing_artifacts: non_existent_cookbooks,
|
166
|
+
constraints_excluding_all_artifacts: constrained_to_no_versions
|
167
|
+
)
|
168
|
+
end
|
171
169
|
|
172
|
-
|
173
|
-
|
174
|
-
|
170
|
+
def report_no_solution_error(e)
|
171
|
+
most_constrained_cookbooks = e.disabled_most_constrained_packages.inject([]) do |list, package|
|
172
|
+
list << "#{package.name} = #{package.versions.first}"
|
173
|
+
end
|
175
174
|
|
176
|
-
|
177
|
-
|
178
|
-
unsatisfiable_demand: e.unsatisfiable_solution_constraint.to_s,
|
179
|
-
missing_artifacts: non_existent_cookbooks,
|
180
|
-
artifacts_with_no_satisfactory_version: most_constrained_cookbooks
|
181
|
-
)
|
175
|
+
non_existent_cookbooks = e.disabled_non_existent_packages.inject([]) do |list, package|
|
176
|
+
list << package.name
|
182
177
|
end
|
183
178
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
179
|
+
raise Solve::Errors::NoSolutionError.new(
|
180
|
+
e.message,
|
181
|
+
unsatisfiable_demand: e.unsatisfiable_solution_constraint.to_s,
|
182
|
+
missing_artifacts: non_existent_cookbooks,
|
183
|
+
artifacts_with_no_satisfactory_version: most_constrained_cookbooks
|
184
|
+
)
|
185
|
+
end
|
189
186
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
fetch(node).each(&block)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
begin
|
199
|
-
sorted_names = nodes.tsort
|
200
|
-
rescue TSort::Cyclic => e
|
201
|
-
raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
|
202
|
-
end
|
187
|
+
def build_sorted_solution(unsorted_solution)
|
188
|
+
nodes = {}
|
189
|
+
unsorted_solution.each do |name, version|
|
190
|
+
nodes[name] = @graph.artifact(name, version).dependencies.map(&:name)
|
191
|
+
end
|
203
192
|
|
204
|
-
|
205
|
-
|
193
|
+
# Modified from http://ruby-doc.org/stdlib-1.9.3/libdoc/tsort/rdoc/TSort.html
|
194
|
+
class << nodes
|
195
|
+
include TSort
|
196
|
+
alias tsort_each_node each_key
|
197
|
+
def tsort_each_child(node, &block)
|
198
|
+
fetch(node).each(&block)
|
206
199
|
end
|
207
200
|
end
|
201
|
+
begin
|
202
|
+
sorted_names = nodes.tsort
|
203
|
+
rescue TSort::Cyclic => e
|
204
|
+
raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
|
205
|
+
end
|
206
|
+
|
207
|
+
sorted_names.map do |artifact|
|
208
|
+
[artifact, unsorted_solution[artifact]]
|
209
|
+
end
|
210
|
+
end
|
208
211
|
end
|
209
212
|
end
|