molinillo 0.6.4 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09d18c88997aae5457b1aa36368b2a3134bf539e3d2972f268c536e06b4ca17e'
4
- data.tar.gz: 60a6c4950f21c00f7bc4e88499cacbb94374c7eea9e07c2cf52c012a4ba4e164
3
+ metadata.gz: 1b23f59b3ca4c8581474afb4d7f39183c981fa7941820e6f16376ef3f11ac393
4
+ data.tar.gz: e3567469a3033fca6d5bd5829e45b9ff9026eb069930780b0ed6c4da873d83b6
5
5
  SHA512:
6
- metadata.gz: 2ab70f2f47d6d2d89b4fdac53f61148135b3ff610997aa139e84110a5967d7838ac6b08ed4a9c3fe584aab6374a6451a857335239a509353c05de95525254fc5
7
- data.tar.gz: b3212b2b8dd7859fa2ef0a52751b4c5bd18a7c911dcaf0aa3695988bca193a8c7a11b4f80cf8c8ef2b427e3f4b70d88a83703f575ba1e5ef8df6b89313531085
6
+ metadata.gz: 1bc11782b15142c0514765cbe8d83e60c127338dd59413454269d5b45d5e5c1a41258df931bd504edc3d03932c0e851ba0fa560d40b903c3ccf805a2c25a6acc
7
+ data.tar.gz: 440b4b4b42206cf2a9ed016aeb11e829c69ef6e9d73e3d488f6672fe2217108428c770372a636b8f34a32c713c5925c4cb8aea6a1e608600c412bcfce96e7d8b
data/ARCHITECTURE.md CHANGED
@@ -33,7 +33,7 @@ This stack-based approach is used because backtracking (also known as *unwinding
33
33
  13. If there is an existing, `activated` vertex for the dependency, `attempt_to_filter_existing_spec`
34
34
  - This filters the contents of the existing vertex's `PossibilitySet` by the current state's `requirement`
35
35
  - If any possibilities remain within the `PossibilitySet`, it updates the activated vertex's payload with the new, filtered state and pushes a new `DependencyState`
36
- - If no possibilities remain within the `PossibilitySet` after filtering, or if the current state's `PossibilitySet` had a different set of sub-dependecy requirements to the existing vertex's `PossibilitySet`, `create_conflict` and `unwind_for_conflict`, back to the last `DependencyState` that has a chance to not generate a conflict. Go to #6
36
+ - If no possibilities remain within the `PossibilitySet` after filtering, or if the current state's `PossibilitySet` had a different set of sub-dependency requirements to the existing vertex's `PossibilitySet`, `create_conflict` and `unwind_for_conflict`, back to the last `DependencyState` that has a chance to not generate a conflict. Go to #6
37
37
  15. Terminate with the topmost state's dependency graph when there are no more requirements left
38
38
  16. For each vertex with a payload of allowable versions for this resolution (i.e., a `PossibilitySet`), pick a single specific version.
39
39
 
@@ -52,7 +52,7 @@ the previous unwinds that have determined our current state.
52
52
  1. First, consider the current conflict as follows:
53
53
  - Find the earliest (lowest index) set of requirements which combine to cause
54
54
  the conflict. Any non-binding requirements can be ignored, as removing them
55
- would not resolve the current onflict
55
+ would not resolve the current conflict
56
56
  - For each binding requirement, find all the alternative possibilities that
57
57
  would relax the requirement:
58
58
  - the requirement's DependencyState might have alternative possibilities
@@ -79,7 +79,7 @@ different, smaller unwind was chosen instead):
79
79
  error as resolution is not possible.
80
80
  3b. Filter the state that we're unwinding to, in order to remove any
81
81
  possibilities we know will result in a conflict. Consider all possible unwinds
82
- to the chosen state (there may be several, amasssed from previous unused
82
+ to the chosen state (there may be several, amassed from previous unused
83
83
  unwinds for different conflicts) when doing this filtering - only
84
84
  possibilities that will certainly result in *all* of those conflicts can be
85
85
  filtered out as having no chance of resolution
data/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # Molinillo Changelog
2
2
 
3
+ ## 0.8.0 (2021-08-09)
4
+
5
+ ##### Breaking
6
+
7
+ * Support for Ruby 2.0, 2.1 and 2.2 has been dropped, the minimum supported
8
+ Ruby version is now 2.3.
9
+ [David Rodríguez](https://github.com/deivid-rodriguez)
10
+
11
+ ##### Enhancements
12
+
13
+ * Use `Array#-` in unwind logic, since it performs better than `Array#&`, so it
14
+ speeds up resolution.
15
+ [Lukas Oberhuber](https://github.com/lukaso)
16
+
17
+ * Allow specification provider to customize how dependencies are compared when
18
+ grouping specifications with the same dependencies.
19
+ [David Rodríguez](https://github.com/deivid-rodriguez)
20
+
21
+ ##### Bug Fixes
22
+
23
+ * None.
24
+
25
+
26
+ ## 0.7.0 (2020-10-21)
27
+
28
+ ##### Breaking
29
+
30
+ * Support for Ruby 1.8.7 and 1.9.3 has been dropped, the minimum supported
31
+ Ruby version is now 2.0.
32
+ [Samuel Giddins](https://github.com/segiddins)
33
+
34
+ ##### Enhancements
35
+
36
+ * Circular dependency errors include the full (shortest) path between the
37
+ circularly-dependent vertices.
38
+ [Samuel Giddins](https://github.com/segiddins)
39
+
40
+ ##### Bug Fixes
41
+
42
+ * None.
43
+
44
+
45
+ ## 0.6.6 (2018-08-07)
46
+
47
+ ##### Enhancements
48
+
49
+ * Improve performance of `Vertex#path_to?`.
50
+ [Samuel Giddins](https://github.com/segiddins)
51
+
52
+ * Allow customization of string used to say that a version conflict has occurred
53
+ for a particular name by passing in the `:incompatible_version_message_for_conflict`
54
+ key when constructing a version conflict message with trees.
55
+ [Samuel Giddins](https://github.com/segiddins)
56
+
57
+ ##### Bug Fixes
58
+
59
+ * None.
60
+
61
+
62
+ ## 0.6.5 (2018-03-22)
63
+
64
+ ##### Enhancements
65
+
66
+ * Improve performance of recursive vertex methods.
67
+ [Samuel Giddins](https://github.com/segiddins)
68
+
69
+ ##### Bug Fixes
70
+
71
+ * None.
72
+
73
+
3
74
  ## 0.6.4 (2017-10-29)
4
75
 
5
76
  ##### Enhancements
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Molinillo
2
2
 
3
- [![Build Status](https://img.shields.io/travis/CocoaPods/Molinillo/master.svg?style=flat)](https://travis-ci.org/CocoaPods/Molinillo)
3
+ [![Build Status](https://github.com/CocoaPods/Molinillo/workflows/test/badge.svg)](https://github.com/CocoaPods/Molinillo/actions?query=branch%3Amaster)
4
4
  [![Coverage](https://img.shields.io/codeclimate/coverage/github/CocoaPods/Molinillo.svg?style=flat)](https://codeclimate.com/github/CocoaPods/Molinillo)
5
5
  [![Code Climate](https://img.shields.io/codeclimate/github/CocoaPods/Molinillo.svg?style=flat)](https://codeclimate.com/github/CocoaPods/Molinillo)
6
6
 
@@ -28,8 +28,7 @@ $ gem install molinillo
28
28
 
29
29
  ## Usage
30
30
 
31
- See the [ARCHITECTURE](ARCHITECTURE.md) file for an overview and look at the test suite for example usage. Better documentation and examples are
32
- forthcoming.
31
+ See the [ARCHITECTURE](ARCHITECTURE.md) file for an overview and look at the test suite for example usage.
33
32
 
34
33
  ## Contributing
35
34
 
data/lib/molinillo.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/compatibility'
4
- require 'molinillo/gem_metadata'
5
- require 'molinillo/errors'
6
- require 'molinillo/resolver'
7
- require 'molinillo/modules/ui'
8
- require 'molinillo/modules/specification_provider'
3
+ require_relative 'molinillo/gem_metadata'
4
+ require_relative 'molinillo/errors'
5
+ require_relative 'molinillo/resolver'
6
+ require_relative 'molinillo/modules/ui'
7
+ require_relative 'molinillo/modules/specification_provider'
9
8
 
10
9
  # Molinillo is a generic dependency resolution algorithm.
11
10
  module Molinillo
@@ -26,6 +26,13 @@ module Molinillo
26
26
  end
27
27
  end
28
28
 
29
+ # (see Molinillo::SpecificationProvider#dependencies_equal?)
30
+ def dependencies_equal?(dependencies, other_dependencies)
31
+ with_no_such_dependency_error_handling do
32
+ specification_provider.dependencies_equal?(dependencies, other_dependencies)
33
+ end
34
+ end
35
+
29
36
  # (see Molinillo::SpecificationProvider#name_for)
30
37
  def name_for(dependency)
31
38
  with_no_such_dependency_error_handling do
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
3
  require 'tsort'
5
4
 
6
- require 'molinillo/dependency_graph/log'
7
- require 'molinillo/dependency_graph/vertex'
5
+ require_relative 'dependency_graph/log'
6
+ require_relative 'dependency_graph/vertex'
8
7
 
9
8
  module Molinillo
10
9
  # A directed acyclic graph that is tuned to hold named dependencies
@@ -124,6 +123,7 @@ module Molinillo
124
123
  dot.join("\n")
125
124
  end
126
125
 
126
+ # @param [DependencyGraph] other
127
127
  # @return [Boolean] whether the two dependency graphs are equal, determined
128
128
  # by a recursive traversal of each {#root_vertices} and its
129
129
  # {Vertex#successors}
@@ -190,7 +190,7 @@ module Molinillo
190
190
  # @return [Edge] the added edge
191
191
  def add_edge(origin, destination, requirement)
192
192
  if destination.path_to?(origin)
193
- raise CircularDependencyError.new([origin, destination])
193
+ raise CircularDependencyError.new(path(destination, origin))
194
194
  end
195
195
  add_edge_no_circular(origin, destination, requirement)
196
196
  end
@@ -219,5 +219,37 @@ module Molinillo
219
219
  def add_edge_no_circular(origin, destination, requirement)
220
220
  log.add_edge_no_circular(self, origin.name, destination.name, requirement)
221
221
  end
222
+
223
+ # Returns the path between two vertices
224
+ # @raise [ArgumentError] if there is no path between the vertices
225
+ # @param [Vertex] from
226
+ # @param [Vertex] to
227
+ # @return [Array<Vertex>] the shortest path from `from` to `to`
228
+ def path(from, to)
229
+ distances = Hash.new(vertices.size + 1)
230
+ distances[from.name] = 0
231
+ predecessors = {}
232
+ each do |vertex|
233
+ vertex.successors.each do |successor|
234
+ if distances[successor.name] > distances[vertex.name] + 1
235
+ distances[successor.name] = distances[vertex.name] + 1
236
+ predecessors[successor] = vertex
237
+ end
238
+ end
239
+ end
240
+
241
+ path = [to]
242
+ while before = predecessors[to]
243
+ path << before
244
+ to = before
245
+ break if to == from
246
+ end
247
+
248
+ unless path.last.equal?(from)
249
+ raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
250
+ end
251
+
252
+ path.reverse
253
+ end
222
254
  end
223
255
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/action'
3
+ require_relative 'action'
4
4
  module Molinillo
5
5
  class DependencyGraph
6
6
  # @!visibility private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/action'
3
+ require_relative 'action'
4
4
  module Molinillo
5
5
  class DependencyGraph
6
6
  # @!visibility private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/action'
3
+ require_relative 'action'
4
4
  module Molinillo
5
5
  class DependencyGraph
6
6
  # @!visibility private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/action'
3
+ require_relative 'action'
4
4
  module Molinillo
5
5
  class DependencyGraph
6
6
  # @!visibility private
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/add_edge_no_circular'
4
- require 'molinillo/dependency_graph/add_vertex'
5
- require 'molinillo/dependency_graph/delete_edge'
6
- require 'molinillo/dependency_graph/detach_vertex_named'
7
- require 'molinillo/dependency_graph/set_payload'
8
- require 'molinillo/dependency_graph/tag'
3
+ require_relative 'add_edge_no_circular'
4
+ require_relative 'add_vertex'
5
+ require_relative 'delete_edge'
6
+ require_relative 'detach_vertex_named'
7
+ require_relative 'set_payload'
8
+ require_relative 'tag'
9
9
 
10
10
  module Molinillo
11
11
  class DependencyGraph
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/action'
3
+ require_relative 'action'
4
4
  module Molinillo
5
5
  class DependencyGraph
6
6
  # @!visibility private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph/action'
3
+ require_relative 'action'
4
4
  module Molinillo
5
5
  class DependencyGraph
6
6
  # @!visibility private
@@ -14,11 +14,11 @@ module Molinillo
14
14
  end
15
15
 
16
16
  # (see Action#up)
17
- def up(_graph)
17
+ def up(graph)
18
18
  end
19
19
 
20
20
  # (see Action#down)
21
- def down(_graph)
21
+ def down(graph)
22
22
  end
23
23
 
24
24
  # @!group Tag
@@ -50,14 +50,25 @@ module Molinillo
50
50
  incoming_edges.map(&:origin)
51
51
  end
52
52
 
53
- # @return [Array<Vertex>] the vertices of {#graph} where `self` is a
53
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
54
54
  # {#descendent?}
55
55
  def recursive_predecessors
56
- vertices = predecessors
57
- vertices += Compatibility.flat_map(vertices, &:recursive_predecessors)
58
- vertices.uniq!
56
+ _recursive_predecessors
57
+ end
58
+
59
+ # @param [Set<Vertex>] vertices the set to add the predecessors to
60
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
61
+ # {#descendent?}
62
+ def _recursive_predecessors(vertices = new_vertex_set)
63
+ incoming_edges.each do |edge|
64
+ vertex = edge.origin
65
+ next unless vertices.add?(vertex)
66
+ vertex._recursive_predecessors(vertices)
67
+ end
68
+
59
69
  vertices
60
70
  end
71
+ protected :_recursive_predecessors
61
72
 
62
73
  # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
63
74
  # `self` as their {Edge#origin}
@@ -65,14 +76,25 @@ module Molinillo
65
76
  outgoing_edges.map(&:destination)
66
77
  end
67
78
 
68
- # @return [Array<Vertex>] the vertices of {#graph} where `self` is an
79
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
69
80
  # {#ancestor?}
70
81
  def recursive_successors
71
- vertices = successors
72
- vertices += Compatibility.flat_map(vertices, &:recursive_successors)
73
- vertices.uniq!
82
+ _recursive_successors
83
+ end
84
+
85
+ # @param [Set<Vertex>] vertices the set to add the successors to
86
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
87
+ # {#ancestor?}
88
+ def _recursive_successors(vertices = new_vertex_set)
89
+ outgoing_edges.each do |edge|
90
+ vertex = edge.destination
91
+ next unless vertices.add?(vertex)
92
+ vertex._recursive_successors(vertices)
93
+ end
94
+
74
95
  vertices
75
96
  end
97
+ protected :_recursive_successors
76
98
 
77
99
  # @return [String] a string suitable for debugging
78
100
  def inspect
@@ -106,21 +128,37 @@ module Molinillo
106
128
 
107
129
  # Is there a path from `self` to `other` following edges in the
108
130
  # dependency graph?
109
- # @return true iff there is a path following edges within this {#graph}
131
+ # @return whether there is a path following edges within this {#graph}
110
132
  def path_to?(other)
111
- equal?(other) || successors.any? { |v| v.path_to?(other) }
133
+ _path_to?(other)
112
134
  end
113
135
 
114
136
  alias descendent? path_to?
115
137
 
138
+ # @param [Vertex] other the vertex to check if there's a path to
139
+ # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
140
+ # @return [Boolean] whether there is a path to `other` from `self`
141
+ def _path_to?(other, visited = new_vertex_set)
142
+ return false unless visited.add?(self)
143
+ return true if equal?(other)
144
+ successors.any? { |v| v._path_to?(other, visited) }
145
+ end
146
+ protected :_path_to?
147
+
116
148
  # Is there a path from `other` to `self` following edges in the
117
149
  # dependency graph?
118
- # @return true iff there is a path following edges within this {#graph}
150
+ # @return whether there is a path following edges within this {#graph}
119
151
  def ancestor?(other)
120
152
  other.path_to?(self)
121
153
  end
122
154
 
123
155
  alias is_reachable_from? ancestor?
156
+
157
+ def new_vertex_set
158
+ require 'set'
159
+ Set.new
160
+ end
161
+ private :new_vertex_set
124
162
  end
125
163
  end
126
164
  end
@@ -18,7 +18,7 @@ module Molinillo
18
18
  # @param [Array<Object>] required_by @see {#required_by}
19
19
  def initialize(dependency, required_by = [])
20
20
  @dependency = dependency
21
- @required_by = required_by
21
+ @required_by = required_by.uniq
22
22
  super()
23
23
  end
24
24
 
@@ -34,7 +34,7 @@ module Molinillo
34
34
 
35
35
  # An error caused by attempting to fulfil a dependency that was circular
36
36
  #
37
- # @note This exception will be thrown iff a {Vertex} is added to a
37
+ # @note This exception will be thrown if and only if a {Vertex} is added to a
38
38
  # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
39
39
  # existing {DependencyGraph::Vertex}
40
40
  class CircularDependencyError < ResolverError
@@ -65,7 +65,7 @@ module Molinillo
65
65
  # @param [SpecificationProvider] specification_provider see {#specification_provider}
66
66
  def initialize(conflicts, specification_provider)
67
67
  pairs = []
68
- Compatibility.flat_map(conflicts.values.flatten, &:requirements).each do |conflicting|
68
+ conflicts.values.flat_map(&:requirements).each do |conflicting|
69
69
  conflicting.each do |source, conflict_requirements|
70
70
  conflict_requirements.each do |c|
71
71
  pairs << [c, source]
@@ -80,7 +80,7 @@ module Molinillo
80
80
  @specification_provider = specification_provider
81
81
  end
82
82
 
83
- require 'molinillo/delegates/specification_provider'
83
+ require_relative 'delegates/specification_provider'
84
84
  include Delegates::SpecificationProvider
85
85
 
86
86
  # @return [String] An error message that includes requirement trees,
@@ -101,9 +101,14 @@ module Molinillo
101
101
  printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
102
102
  additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
103
103
  version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
104
+ incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
105
+ proc do |name, _conflict|
106
+ %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
107
+ end
108
+ end
104
109
 
105
110
  conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
106
- o << %(\n#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":\n)
111
+ o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
107
112
  if conflict.locked_requirement
108
113
  o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
109
114
  o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
@@ -116,7 +121,7 @@ module Molinillo
116
121
  t = ''.dup
117
122
  depth = 2
118
123
  tree.each do |req|
119
- t << ' ' * depth << req.to_s
124
+ t << ' ' * depth << printable_requirement.call(req)
120
125
  unless tree.last == req
121
126
  if spec = conflict.activated_by_name[name_for(req)]
122
127
  t << %( was resolved to #{version_for_spec.call(spec)}, which)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Molinillo
4
4
  # The version of Molinillo.
5
- VERSION = '0.6.4'.freeze
5
+ VERSION = '0.8.0'.freeze
6
6
  end
@@ -45,6 +45,17 @@ module Molinillo
45
45
  true
46
46
  end
47
47
 
48
+ # Determines whether two arrays of dependencies are equal, and thus can be
49
+ # grouped.
50
+ #
51
+ # @param [Array<Object>] dependencies
52
+ # @param [Array<Object>] other_dependencies
53
+ # @return [Boolean] whether `dependencies` and `other_dependencies` should
54
+ # be considered equal.
55
+ def dependencies_equal?(dependencies, other_dependencies)
56
+ dependencies == other_dependencies
57
+ end
58
+
48
59
  # Returns the name for the given `dependency`.
49
60
  # @note This method should be 'pure', i.e. the return value should depend
50
61
  # only on the `dependency` parameter.
@@ -207,7 +207,7 @@ module Molinillo
207
207
  def start_resolution
208
208
  @started_at = Time.now
209
209
 
210
- handle_missing_or_push_dependency_state(initial_state)
210
+ push_initial_state
211
211
 
212
212
  debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
213
213
  resolver_ui.before_resolution
@@ -238,11 +238,11 @@ module Molinillo
238
238
  debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
239
239
  end
240
240
 
241
- require 'molinillo/state'
242
- require 'molinillo/modules/specification_provider'
241
+ require_relative 'state'
242
+ require_relative 'modules/specification_provider'
243
243
 
244
- require 'molinillo/delegates/resolution_state'
245
- require 'molinillo/delegates/specification_provider'
244
+ require_relative 'delegates/resolution_state'
245
+ require_relative 'delegates/specification_provider'
246
246
 
247
247
  include Molinillo::Delegates::ResolutionState
248
248
  include Molinillo::Delegates::SpecificationProvider
@@ -273,10 +273,10 @@ module Molinillo
273
273
  states.last
274
274
  end
275
275
 
276
- # Creates the initial state for the resolution, based upon the
276
+ # Creates and pushes the initial state for the resolution, based upon the
277
277
  # {#requested} dependencies
278
- # @return [DependencyState] the initial state for the resolution
279
- def initial_state
278
+ # @return [void]
279
+ def push_initial_state
280
280
  graph = DependencyGraph.new.tap do |dg|
281
281
  original_requested.each do |requested|
282
282
  vertex = dg.add_vertex(name_for(requested), nil, true)
@@ -285,18 +285,7 @@ module Molinillo
285
285
  dg.tag(:initial_state)
286
286
  end
287
287
 
288
- requirements = sort_dependencies(original_requested, graph, {})
289
- initial_requirement = requirements.shift
290
- DependencyState.new(
291
- initial_requirement && name_for(initial_requirement),
292
- requirements,
293
- graph,
294
- initial_requirement,
295
- possibilities_for_requirement(initial_requirement, graph),
296
- 0,
297
- {},
298
- []
299
- )
288
+ push_state_for_requirements(original_requested, true, graph)
300
289
  end
301
290
 
302
291
  # Unwinds the states stack because a conflict has been encountered
@@ -340,11 +329,11 @@ module Molinillo
340
329
 
341
330
  # Look for past conflicts that could be unwound to affect the
342
331
  # requirement tree for the current conflict
332
+ all_reqs = last_detail_for_current_unwind.all_requirements
333
+ all_reqs_size = all_reqs.size
343
334
  relevant_unused_unwinds = unused_unwind_options.select do |alternative|
344
- intersecting_requirements =
345
- last_detail_for_current_unwind.all_requirements &
346
- alternative.requirements_unwound_to_instead
347
- next if intersecting_requirements.empty?
335
+ diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
336
+ next if diff_reqs.size == all_reqs_size
348
337
  # Find the highest index unwind whilst looping through
349
338
  current_detail = alternative if alternative > current_detail
350
339
  alternative
@@ -355,13 +344,17 @@ module Molinillo
355
344
  state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
356
345
 
357
346
  # Update the requirements_unwound_to_instead on any relevant unused unwinds
358
- relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
359
- unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
347
+ relevant_unused_unwinds.each do |d|
348
+ (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
349
+ end
350
+ unwind_details.each do |d|
351
+ (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
352
+ end
360
353
 
361
354
  current_detail
362
355
  end
363
356
 
364
- # @param [Array<Object>] array of requirements that combine to create a conflict
357
+ # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
365
358
  # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
366
359
  # of resolving the passed requirements
367
360
  def unwind_options_for_requirements(binding_requirements)
@@ -429,7 +422,7 @@ module Molinillo
429
422
  end
430
423
 
431
424
  # @param [DependencyState] state
432
- # @param [Array] array of requirements
425
+ # @param [Array] binding_requirements array of requirements
433
426
  # @return [Boolean] whether or not the given state has any possibilities
434
427
  # that could satisfy the given requirements
435
428
  def conflict_fixing_possibilities?(state, binding_requirements)
@@ -444,7 +437,8 @@ module Molinillo
444
437
 
445
438
  # Filter's a state's possibilities to remove any that would not fix the
446
439
  # conflict we've just rewound from
447
- # @param [UnwindDetails] details of the conflict just unwound from
440
+ # @param [UnwindDetails] unwind_details details of the conflict just
441
+ # unwound from
448
442
  # @return [void]
449
443
  def filter_possibilities_after_unwind(unwind_details)
450
444
  return unless state && !state.possibilities.empty?
@@ -458,7 +452,7 @@ module Molinillo
458
452
 
459
453
  # Filter's a state's possibilities to remove any that would not satisfy
460
454
  # the requirements in the conflict we've just rewound from
461
- # @param [UnwindDetails] details of the conflict just unwound from
455
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
462
456
  # @return [void]
463
457
  def filter_possibilities_for_primary_unwind(unwind_details)
464
458
  unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
@@ -491,7 +485,7 @@ module Molinillo
491
485
 
492
486
  # Filter's a state's possibilities to remove any that would (eventually)
493
487
  # create a requirement in the conflict we've just rewound from
494
- # @param [UnwindDetails] details of the conflict just unwound from
488
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
495
489
  # @return [void]
496
490
  def filter_possibilities_for_parent_unwind(unwind_details)
497
491
  unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
@@ -500,7 +494,7 @@ module Molinillo
500
494
  primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
501
495
  parent_unwinds = unwinds_to_state.uniq - primary_unwinds
502
496
 
503
- allowed_possibility_sets = Compatibility.flat_map(primary_unwinds) do |unwind|
497
+ allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
504
498
  states[unwind.state_index].possibilities.select do |possibility_set|
505
499
  possibility_set.possibilities.any? do |poss|
506
500
  possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
@@ -508,7 +502,7 @@ module Molinillo
508
502
  end
509
503
  end
510
504
 
511
- requirements_to_avoid = Compatibility.flat_map(parent_unwinds, &:sub_dependencies_to_avoid)
505
+ requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
512
506
 
513
507
  state.possibilities.reject! do |possibility_set|
514
508
  !allowed_possibility_sets.include?(possibility_set) &&
@@ -524,12 +518,12 @@ module Molinillo
524
518
 
525
519
  possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
526
520
 
527
- # When theres a `CircularDependency` error the conflicting requirement
528
- # (the one causing the circular) wont be `conflict.requirement`
529
- # (which wont be for the right state, because we wont have created it,
530
- # because its circular).
531
- # We need to make sure we have that requirement in the conflicts list,
532
- # otherwise we wont be able to unwind properly, so we just return all
521
+ # When there's a `CircularDependency` error the conflicting requirement
522
+ # (the one causing the circular) won't be `conflict.requirement`
523
+ # (which won't be for the right state, because we won't have created it,
524
+ # because it's circular).
525
+ # We need to make sure we have that requirement in the conflict's list,
526
+ # otherwise we won't be able to unwind properly, so we just return all
533
527
  # the requirements for the conflict.
534
528
  return possible_binding_requirements if conflict.underlying_error
535
529
 
@@ -558,8 +552,8 @@ module Molinillo
558
552
  end
559
553
 
560
554
  # @param [Object] requirement we wish to check
561
- # @param [Array] array of requirements
562
- # @param [Array] array of possibilities the requirements will be used to filter
555
+ # @param [Array] possible_binding_requirements array of requirements
556
+ # @param [Array] possibilities array of possibilities the requirements will be used to filter
563
557
  # @return [Boolean] whether or not the given requirement is required to filter
564
558
  # out all elements of the array of possibilities.
565
559
  def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
@@ -568,6 +562,7 @@ module Molinillo
568
562
  end
569
563
  end
570
564
 
565
+ # @param [Object] requirement
571
566
  # @return [Object] the requirement that led to `requirement` being added
572
567
  # to the list of requirements.
573
568
  def parent_of(requirement)
@@ -577,6 +572,7 @@ module Molinillo
577
572
  parent_state.requirement
578
573
  end
579
574
 
575
+ # @param [String] name
580
576
  # @return [Object] the requirement that led to a version of a possibility
581
577
  # with the given name being activated.
582
578
  def requirement_for_existing_name(name)
@@ -585,6 +581,7 @@ module Molinillo
585
581
  states.find { |s| s.name == name }.requirement
586
582
  end
587
583
 
584
+ # @param [Object] requirement
588
585
  # @return [ResolutionState] the state whose `requirement` is the given
589
586
  # `requirement`.
590
587
  def find_state_for(requirement)
@@ -592,6 +589,7 @@ module Molinillo
592
589
  states.find { |i| requirement == i.requirement }
593
590
  end
594
591
 
592
+ # @param [Object] underlying_error
595
593
  # @return [Conflict] a {Conflict} that reflects the failure to activate
596
594
  # the {#possibility} in conjunction with the current {#state}
597
595
  def create_conflict(underlying_error = nil)
@@ -628,6 +626,7 @@ module Molinillo
628
626
  vertex.requirements.map { |r| requirement_tree_for(r) }
629
627
  end
630
628
 
629
+ # @param [Object] requirement
631
630
  # @return [Array<Object>] the list of requirements that led to
632
631
  # `requirement` being required.
633
632
  def requirement_tree_for(requirement)
@@ -673,9 +672,8 @@ module Molinillo
673
672
  attempt_to_filter_existing_spec(existing_vertex)
674
673
  else
675
674
  latest = possibility.latest_version
676
- # use reject!(!satisfied) for 1.8.7 compatibility
677
- possibility.possibilities.reject! do |possibility|
678
- !requirement_satisfied_by?(requirement, activated, possibility)
675
+ possibility.possibilities.select! do |possibility|
676
+ requirement_satisfied_by?(requirement, activated, possibility)
679
677
  end
680
678
  if possibility.latest_version.nil?
681
679
  # ensure there's a possibility for better error messages
@@ -705,7 +703,7 @@ module Molinillo
705
703
 
706
704
  # Generates a filtered version of the existing vertex's `PossibilitySet` using the
707
705
  # current state's `requirement`
708
- # @param [Object] existing vertex
706
+ # @param [Object] vertex existing vertex
709
707
  # @return [PossibilitySet] filtered possibility set
710
708
  def filtered_possibility_set(vertex)
711
709
  PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
@@ -730,7 +728,7 @@ module Molinillo
730
728
  end
731
729
 
732
730
  # Requires the dependencies that the recently activated spec has
733
- # @param [Object] activated_possibility the PossibilitySet that has just been
731
+ # @param [Object] possibility_set the PossibilitySet that has just been
734
732
  # activated
735
733
  # @return [void]
736
734
  def require_nested_dependencies_for(possibility_set)
@@ -749,6 +747,8 @@ module Molinillo
749
747
  # Pushes a new {DependencyState} that encapsulates both existing and new
750
748
  # requirements
751
749
  # @param [Array] new_requirements
750
+ # @param [Boolean] requires_sort
751
+ # @param [Object] new_activated
752
752
  # @return [void]
753
753
  def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
754
754
  new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
@@ -767,7 +767,8 @@ module Molinillo
767
767
 
768
768
  # Checks a proposed requirement with any existing locked requirement
769
769
  # before generating an array of possibilities for it.
770
- # @param [Object] the proposed requirement
770
+ # @param [Object] requirement the proposed requirement
771
+ # @param [Object] activated
771
772
  # @return [Array] possibilities
772
773
  def possibilities_for_requirement(requirement, activated = self.activated)
773
774
  return [] unless requirement
@@ -778,7 +779,8 @@ module Molinillo
778
779
  group_possibilities(search_for(requirement))
779
780
  end
780
781
 
781
- # @param [Object] the proposed requirement
782
+ # @param [Object] requirement the proposed requirement
783
+ # @param [Object] activated
782
784
  # @return [Array] possibility set containing only the locked requirement, if any
783
785
  def locked_requirement_possibility_set(requirement, activated = self.activated)
784
786
  all_possibilities = search_for(requirement)
@@ -797,15 +799,15 @@ module Molinillo
797
799
  # Build an array of PossibilitySets, with each element representing a group of
798
800
  # dependency versions that all have the same sub-dependency version constraints
799
801
  # and are contiguous.
800
- # @param [Array] an array of possibilities
801
- # @return [Array] an array of possibility sets
802
+ # @param [Array] possibilities an array of possibilities
803
+ # @return [Array<PossibilitySet>] an array of possibility sets
802
804
  def group_possibilities(possibilities)
803
805
  possibility_sets = []
804
806
  current_possibility_set = nil
805
807
 
806
808
  possibilities.reverse_each do |possibility|
807
809
  dependencies = dependencies_for(possibility)
808
- if current_possibility_set && current_possibility_set.dependencies == dependencies
810
+ if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
809
811
  current_possibility_set.possibilities.unshift(possibility)
810
812
  else
811
813
  possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'molinillo/dependency_graph'
3
+ require_relative 'dependency_graph'
4
4
 
5
5
  module Molinillo
6
6
  # This class encapsulates a dependency resolver.
@@ -9,7 +9,7 @@ module Molinillo
9
9
  #
10
10
  #
11
11
  class Resolver
12
- require 'molinillo/resolution'
12
+ require_relative 'resolution'
13
13
 
14
14
  # @return [SpecificationProvider] the specification provider used
15
15
  # in the resolution process
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: molinillo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel E. Giddins
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-29 00:00:00.000000000 Z
11
+ date: 2021-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.5'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.5'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +24,7 @@ dependencies:
38
24
  - - ">="
39
25
  - !ruby/object:Gem::Version
40
26
  version: '0'
41
- description:
27
+ description:
42
28
  email:
43
29
  - segiddins@segiddins.me
44
30
  executables: []
@@ -50,7 +36,6 @@ files:
50
36
  - LICENSE
51
37
  - README.md
52
38
  - lib/molinillo.rb
53
- - lib/molinillo/compatibility.rb
54
39
  - lib/molinillo/delegates/resolution_state.rb
55
40
  - lib/molinillo/delegates/specification_provider.rb
56
41
  - lib/molinillo/dependency_graph.rb
@@ -74,7 +59,7 @@ homepage: https://github.com/CocoaPods/Molinillo
74
59
  licenses:
75
60
  - MIT
76
61
  metadata: {}
77
- post_install_message:
62
+ post_install_message:
78
63
  rdoc_options: []
79
64
  require_paths:
80
65
  - lib
@@ -82,16 +67,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
67
  requirements:
83
68
  - - ">="
84
69
  - !ruby/object:Gem::Version
85
- version: '0'
70
+ version: 2.3.0
86
71
  required_rubygems_version: !ruby/object:Gem::Requirement
87
72
  requirements:
88
73
  - - ">="
89
74
  - !ruby/object:Gem::Version
90
75
  version: '0'
91
76
  requirements: []
92
- rubyforge_project:
93
- rubygems_version: 2.6.13
94
- signing_key:
77
+ rubygems_version: 3.1.3
78
+ signing_key:
95
79
  specification_version: 4
96
80
  summary: Provides support for dependency resolution
97
81
  test_files: []
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Molinillo
4
- # Hacks needed for old Ruby versions.
5
- module Compatibility
6
- module_function
7
-
8
- if [].respond_to?(:flat_map)
9
- # Flat map
10
- # @param [Enumerable] enum an enumerable object
11
- # @block the block to flat-map with
12
- # @return The enum, flat-mapped
13
- def flat_map(enum, &blk)
14
- enum.flat_map(&blk)
15
- end
16
- else
17
- # Flat map
18
- # @param [Enumerable] enum an enumerable object
19
- # @block the block to flat-map with
20
- # @return The enum, flat-mapped
21
- def flat_map(enum, &blk)
22
- enum.map(&blk).flatten(1)
23
- end
24
- end
25
- end
26
- end