solve 3.1.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -67,7 +67,7 @@ module Solve
67
67
  return false unless other.is_a?(Graph)
68
68
  return false unless artifacts.size == other.artifacts.size
69
69
 
70
- self_artifacts = self.artifacts
70
+ self_artifacts = artifacts
71
71
  other_artifacts = other.artifacts
72
72
 
73
73
  self_dependencies = self_artifacts.inject([]) do |list, artifact|
@@ -79,8 +79,8 @@ module Solve
79
79
  end.flatten
80
80
 
81
81
  self_dependencies.size == other_dependencies.size &&
82
- self_artifacts.all? { |artifact| other_artifacts.include?(artifact) } &&
83
- self_dependencies.all? { |dependency| other_dependencies.include?(dependency) }
82
+ self_artifacts.all? { |artifact| other_artifacts.include?(artifact) } &&
83
+ self_dependencies.all? { |dependency| other_dependencies.include?(dependency) }
84
84
  end
85
85
  alias_method :eql?, :==
86
86
  end
@@ -1,6 +1,7 @@
1
- require 'set'
2
- require 'molinillo'
3
- require_relative 'solver/serializer'
1
+ require "set"
2
+ require "molinillo"
3
+ require "molinillo/modules/specification_provider"
4
+ require_relative "solver/serializer"
4
5
 
5
6
  module Solve
6
7
  class RubySolver
@@ -14,7 +15,7 @@ module Solve
14
15
  seconds.to_i * 1_000
15
16
  end
16
17
 
17
- # For optinal solver engines, this attempts to load depenencies. The
18
+ # For optional solver engines, this attempts to load depenencies. The
18
19
  # RubySolver is a non-optional component, so this is a no-op
19
20
  def activate
20
21
  true
@@ -45,7 +46,7 @@ module Solve
45
46
  @timeout_ms = self.class.timeout
46
47
 
47
48
  @ui = options[:ui] # could be nil, but that's okay
48
- @dependency_source = options[:dependency_source] || 'user-specified dependency'
49
+ @dependency_source = options[:dependency_source] || "user-specified dependency"
49
50
 
50
51
  @molinillo_graph = Molinillo::DependencyGraph.new
51
52
  @resolver = Molinillo::Resolver.new(self, self)
@@ -74,10 +75,10 @@ module Solve
74
75
 
75
76
  solved_graph = resolve_with_error_wrapping
76
77
 
77
- solution = solved_graph.map(&:payload)
78
+ solution = solved_graph.map(&:payload)
78
79
 
79
80
  unsorted_solution = solution.inject({}) do |stringified_soln, artifact|
80
- stringified_soln[artifact.name] = artifact.version.to_s if artifact
81
+ stringified_soln[artifact.name] = artifact.version.to_s
81
82
  stringified_soln
82
83
  end
83
84
 
@@ -106,13 +107,13 @@ module Solve
106
107
  # Callback required by Molinillo, called when the solve starts
107
108
  # @return nil
108
109
  def before_resolution
109
- @ui.say('Starting dependency resolution') if @ui
110
+ @ui.say("Starting dependency resolution") if @ui
110
111
  end
111
112
 
112
113
  # Callback required by Molinillo, called when the solve is complete.
113
114
  # @return nil
114
115
  def after_resolution
115
- @ui.say('Finished dependency resolution') if @ui
116
+ @ui.say("Finished dependency resolution") if @ui
116
117
  end
117
118
 
118
119
  # Callback required by Molinillo, called when resolving every progress_rate
@@ -123,32 +124,22 @@ module Solve
123
124
 
124
125
  # Callback required by Molinillo, gives debug information about the solution
125
126
  # @return nil
126
- def debug(current_resolver_depth)
127
+ def debug(current_resolver_depth = 0)
127
128
  # debug info will be returned if you call yield here, but it seems to be
128
129
  # broken in current Molinillo
129
130
  @ui.say(yield) if @ui
130
131
  end
131
132
 
132
- # Callback required by Molinillo
133
- # @return [String] the dependency's name
134
- def name_for(dependency)
135
- dependency.name
136
- end
137
-
138
- # Callback required by Molinillo
139
- # @return [Array<Solve::Dependency>] the dependencies sorted by preference.
140
- def sort_dependencies(dependencies, activated, conflicts)
141
- dependencies.sort_by do |dependency|
142
- name = name_for(dependency)
143
- [
144
- activated.vertex_named(name).payload ? 0 : 1,
145
- conflicts[name] ? 0 : 1,
146
- activated.vertex_named(name).payload ? 0 : graph.versions(dependency.name).count,
147
- ]
148
- end
149
- end
133
+ include Molinillo::SpecificationProvider
150
134
 
151
135
  # Callback required by Molinillo
136
+ # Search for the specifications that match the given dependency.
137
+ # The specifications in the returned array will be considered in reverse
138
+ # order, so the latest version ought to be last.
139
+ # @note This method should be 'pure', i.e. the return value should depend
140
+ # only on the `dependency` parameter.
141
+ #
142
+ # @param [Object] dependency
152
143
  # @return [Array<Solve::Artifact>] the artifacts that match the dependency.
153
144
  def search_for(dependency)
154
145
  # This array gets mutated by Molinillo; it's okay because sort returns a
@@ -157,29 +148,101 @@ module Solve
157
148
  end
158
149
 
159
150
  # Callback required by Molinillo
160
- # @return [Boolean]
151
+ # Returns the dependencies of `specification`.
152
+ # @note This method should be 'pure', i.e. the return value should depend
153
+ # only on the `specification` parameter.
154
+ #
155
+ # @param [Object] specification
156
+ # @return [Array<Solve::Dependency>] the dependencies of the given artifact
157
+ def dependencies_for(specification)
158
+ specification.dependencies
159
+ end
160
+
161
+ # Callback required by Molinillo
162
+ # Determines whether the given `requirement` is satisfied by the given
163
+ # `spec`, in the context of the current `activated` dependency graph.
164
+ #
165
+ # @param [Object] requirement
166
+ # @param [DependencyGraph] activated the current dependency graph in the
167
+ # resolution process.
168
+ # @param [Object] spec
169
+ # @return [Boolean] whether `requirement` is satisfied by `spec` in the
170
+ # context of the current `activated` dependency graph.
161
171
  def requirement_satisfied_by?(requirement, activated, spec)
162
- requirement.constraint.satisfies?(spec.version)
172
+ version = spec.version
173
+ return false unless requirement.constraint.satisfies?(version)
174
+ shared_possibility_versions = possibility_versions(requirement, activated)
175
+ return false if !shared_possibility_versions.empty? && !shared_possibility_versions.include?(version)
176
+ true
177
+ end
178
+
179
+ # Searches the current dependency graph to find previously activated
180
+ # requirements for the current artifact.
181
+ #
182
+ # @param [Object] requirement
183
+ # @param [DependencyGraph] activated the current dependency graph in the
184
+ # resolution process.
185
+ # @return [Array<Semverse::Version> the list of currently activated versions
186
+ # of this requirement
187
+ def possibility_versions(requirement, activated)
188
+ activated.vertices.values.flat_map do |vertex|
189
+
190
+ next unless vertex.payload
191
+
192
+ next unless vertex.name == requirement.name
193
+
194
+ if vertex.payload.respond_to?(:possibilities)
195
+ vertex.payload.possibilities.map(&:version)
196
+ else
197
+ vertex.payload.version
198
+ end
199
+ end.compact
163
200
  end
201
+ private :possibility_versions
164
202
 
165
203
  # Callback required by Molinillo
166
- # @return [Array<Solve::Dependency>] the dependencies of the given artifact
167
- def dependencies_for(specification)
168
- specification.dependencies
204
+ # Returns the name for the given `dependency`.
205
+ # @note This method should be 'pure', i.e. the return value should depend
206
+ # only on the `dependency` parameter.
207
+ #
208
+ # @param [Object] dependency
209
+ # @return [String] the name for the given `dependency`.
210
+ def name_for(dependency)
211
+ dependency.name
169
212
  end
170
213
 
214
+ # Callback required by Molinillo
171
215
  # @return [String] the name of the source of explicit dependencies, i.e.
172
216
  # those passed to {Resolver#resolve} directly.
173
217
  def name_for_explicit_dependency_source
174
218
  @dependency_source
175
219
  end
176
220
 
177
- # @return [String] the name of the source of 'locked' dependencies, i.e.
178
- # those passed to {Resolver#resolve} directly as the `base`
179
- def name_for_locking_dependency_source
180
- 'Lockfile'
221
+ # Callback required by Molinillo
222
+ # Sort dependencies so that the ones that are easiest to resolve are first.
223
+ # Easiest to resolve is (usually) defined by:
224
+ # 1) Is this dependency already activated?
225
+ # 2) How relaxed are the requirements?
226
+ # 3) Are there any conflicts for this dependency?
227
+ # 4) How many possibilities are there to satisfy this dependency?
228
+ #
229
+ # @param [Array<Object>] dependencies
230
+ # @param [DependencyGraph] activated the current dependency graph in the
231
+ # resolution process.
232
+ # @param [{String => Array<Conflict>}] conflicts
233
+ # @return [Array<Solve::Dependency>] the dependencies sorted by preference.
234
+ def sort_dependencies(dependencies, activated, conflicts)
235
+ dependencies.sort_by do |dependency|
236
+ name = name_for(dependency)
237
+ [
238
+ activated.vertex_named(name).payload ? 0 : 1,
239
+ conflicts[name] ? 0 : 1,
240
+ search_for(dependency).count,
241
+ ]
242
+ end
181
243
  end
182
244
 
245
+ # Callback required by Molinillo
183
246
  # Returns whether this dependency, which has no possible matching
184
247
  # specifications, can safely be ignored.
185
248
  #
@@ -191,35 +254,35 @@ module Solve
191
254
 
192
255
  private
193
256
 
194
- def resolve_with_error_wrapping
195
- @resolver.resolve(demands, @molinillo_graph)
196
- rescue Molinillo::VersionConflict, Molinillo::CircularDependencyError => e
197
- raise Solve::Errors::NoSolutionError.new(e.message)
198
- end
257
+ def resolve_with_error_wrapping
258
+ @resolver.resolve(demands, @molinillo_graph)
259
+ rescue Molinillo::VersionConflict, Molinillo::CircularDependencyError => e
260
+ raise Solve::Errors::NoSolutionError.new(e.message)
261
+ end
199
262
 
200
- def build_sorted_solution(unsorted_solution)
201
- nodes = Hash.new
202
- unsorted_solution.each do |name, version|
203
- nodes[name] = @graph.artifact(name, version).dependencies.map(&:name)
204
- end
263
+ def build_sorted_solution(unsorted_solution)
264
+ nodes = Hash.new
265
+ unsorted_solution.each do |name, version|
266
+ nodes[name] = @graph.artifact(name, version).dependencies.map(&:name)
267
+ end
205
268
 
206
- # Modified from http://ruby-doc.org/stdlib-1.9.3/libdoc/tsort/rdoc/TSort.html
207
- class << nodes
208
- include TSort
209
- alias tsort_each_node each_key
210
- def tsort_each_child(node, &block)
211
- fetch(node).each(&block)
212
- end
213
- end
214
- begin
215
- sorted_names = nodes.tsort
216
- rescue TSort::Cyclic => e
217
- raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
269
+ # Modified from http://ruby-doc.org/stdlib-1.9.3/libdoc/tsort/rdoc/TSort.html
270
+ class << nodes
271
+ include TSort
272
+ alias tsort_each_node each_key
273
+ def tsort_each_child(node, &block)
274
+ fetch(node).each(&block)
218
275
  end
276
+ end
277
+ begin
278
+ sorted_names = nodes.tsort
279
+ rescue TSort::Cyclic => e
280
+ raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
281
+ end
219
282
 
220
- sorted_names.map do |artifact|
221
- [artifact, unsorted_solution[artifact]]
222
- end
283
+ sorted_names.map do |artifact|
284
+ [artifact, unsorted_solution[artifact]]
223
285
  end
286
+ end
224
287
  end
225
288
  end
@@ -1,5 +1,5 @@
1
- require 'json'
2
- require 'solve/graph'
1
+ require "json"
2
+ require "solve/graph"
3
3
 
4
4
  module Solve
5
5
 
@@ -58,71 +58,71 @@ module Solve
58
58
 
59
59
  private
60
60
 
61
- def format_graph(graph)
62
- artifacts = graph.artifacts.inject([]) do |list, artifact|
63
- list << format_artifact(artifact)
64
- end
65
- { "graph" => artifacts }
61
+ def format_graph(graph)
62
+ artifacts = graph.artifacts.inject([]) do |list, artifact|
63
+ list << format_artifact(artifact)
66
64
  end
65
+ { "graph" => artifacts }
66
+ end
67
67
 
68
- def format_artifact(artifact)
69
- dependencies = artifact.dependencies.inject([]) do |list, dependency|
70
- list << format_dependency(dependency)
71
- end
72
-
73
- {
74
- "name" => artifact.name,
75
- "version" => artifact.version.to_s,
76
- "dependencies" => dependencies
77
- }
68
+ def format_artifact(artifact)
69
+ dependencies = artifact.dependencies.inject([]) do |list, dependency|
70
+ list << format_dependency(dependency)
78
71
  end
79
72
 
80
- def format_dependency(dependency)
81
- {
82
- "name" => dependency.name,
83
- "constraint" => dependency.constraint.to_s
84
- }
85
- end
73
+ {
74
+ "name" => artifact.name,
75
+ "version" => artifact.version.to_s,
76
+ "dependencies" => dependencies,
77
+ }
78
+ end
86
79
 
87
- def format_demands(demands)
88
- demands_list = demands.inject([]) do |list, demand|
89
- list << format_demand(demand)
90
- end
91
- { "demands" => demands_list }
92
- end
80
+ def format_dependency(dependency)
81
+ {
82
+ "name" => dependency.name,
83
+ "constraint" => dependency.constraint.to_s,
84
+ }
85
+ end
93
86
 
94
- def format_demand(demand)
95
- {
96
- "name" => demand[0],
97
- "constraint" => demand[1]
98
- }
87
+ def format_demands(demands)
88
+ demands_list = demands.inject([]) do |list, demand|
89
+ list << format_demand(demand)
99
90
  end
91
+ { "demands" => demands_list }
92
+ end
100
93
 
101
- def load_graph(artifacts_list)
102
- graph = Solve::Graph.new
103
- artifacts_list.each do |artifact_spec|
104
- load_artifact(graph, artifact_spec)
105
- end
106
- graph
107
- end
94
+ def format_demand(demand)
95
+ {
96
+ "name" => demand[0],
97
+ "constraint" => demand[1],
98
+ }
99
+ end
108
100
 
109
- def load_artifact(graph, artifact_spec)
110
- artifact = graph.artifact(artifact_spec["name"], artifact_spec["version"])
111
- artifact_spec["dependencies"].each do |dependency_spec|
112
- load_dependency(artifact, dependency_spec)
113
- end
114
- artifact
101
+ def load_graph(artifacts_list)
102
+ graph = Solve::Graph.new
103
+ artifacts_list.each do |artifact_spec|
104
+ load_artifact(graph, artifact_spec)
115
105
  end
106
+ graph
107
+ end
116
108
 
117
- def load_dependency(artifact, dependency_spec)
118
- artifact.depends(dependency_spec["name"], dependency_spec["constraint"])
109
+ def load_artifact(graph, artifact_spec)
110
+ artifact = graph.artifact(artifact_spec["name"], artifact_spec["version"])
111
+ artifact_spec["dependencies"].each do |dependency_spec|
112
+ load_dependency(artifact, dependency_spec)
119
113
  end
114
+ artifact
115
+ end
120
116
 
121
- def load_demands(demand_specs)
122
- demand_specs.inject([]) do |list, demand_spec|
123
- list << [demand_spec["name"], demand_spec["constraint"]]
124
- end
117
+ def load_dependency(artifact, dependency_spec)
118
+ artifact.depends(dependency_spec["name"], dependency_spec["constraint"])
119
+ end
120
+
121
+ def load_demands(demand_specs)
122
+ demand_specs.inject([]) do |list, demand_spec|
123
+ list << [demand_spec["name"], demand_spec["constraint"]]
125
124
  end
125
+ end
126
126
  end
127
127
  end
128
128
  end
@@ -1,3 +1,3 @@
1
1
  module Solve
2
- VERSION = "3.1.1"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/solve/version', __FILE__)
2
+ require File.expand_path("../lib/solve/version", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.authors = ["Jamie Winsor", "Andrew Garson", "Thibaud Guillaume-Gentil"]
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.homepage = "https://github.com/berkshelf/solve"
10
10
  s.license = "Apache 2.0"
11
11
  s.files = `git ls-files`.split($\)
12
- s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
+ s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
13
13
  s.test_files = s.files.grep(%r{^spec/})
14
14
  s.name = "solve"
15
15
  s.require_paths = ["lib"]
@@ -17,10 +17,10 @@ Gem::Specification.new do |s|
17
17
  s.required_ruby_version = ">= 2.1.0"
18
18
 
19
19
  s.add_dependency "semverse", ">= 1.1", "< 3.0"
20
- s.add_dependency "molinillo", ">= 0.5"
20
+ s.add_dependency "molinillo", "~> 0.6"
21
21
 
22
- s.add_development_dependency 'thor'
23
- s.add_development_dependency 'rake'
24
- s.add_development_dependency 'spork'
25
- s.add_development_dependency 'rspec'
22
+ s.add_development_dependency "thor"
23
+ s.add_development_dependency "rake"
24
+ s.add_development_dependency "spork"
25
+ s.add_development_dependency "rspec"
26
26
  end