solve 3.1.1 → 4.0.0

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.
@@ -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