New Upstream Release - ruby-molinillo

Ready changes

Summary

Merged new upstream version: 0.8.0 (was: 0.6.4).

Resulting package

Built on 2022-03-10T19:13 (took 1m49s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases ruby-molinillo

Lintian Result

Diff

diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index b7c869c..528fa83 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -33,7 +33,7 @@ This stack-based approach is used because backtracking (also known as *unwinding
 13. If there is an existing, `activated` vertex for the dependency, `attempt_to_filter_existing_spec`
   - This filters the contents of the existing vertex's `PossibilitySet` by the current state's `requirement`
   - If any possibilities remain within the `PossibilitySet`, it updates the activated vertex's payload with the new, filtered state and pushes a new `DependencyState`
-  - 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
+  - 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
 15. Terminate with the topmost state's dependency graph when there are no more requirements left
 16. For each vertex with a payload of allowable versions for this resolution (i.e., a `PossibilitySet`), pick a single specific version.
 
@@ -52,7 +52,7 @@ the previous unwinds that have determined our current state.
 1. First, consider the current conflict as follows:
   - Find the earliest (lowest index) set of requirements which combine to cause
   the conflict. Any non-binding requirements can be ignored, as removing them
-  would not resolve the current onflict
+  would not resolve the current conflict
   - For each binding requirement, find all the alternative possibilities that
   would relax the requirement:
     - the requirement's DependencyState might have alternative possibilities
@@ -79,7 +79,7 @@ different, smaller unwind was chosen instead):
 error as resolution is not possible.
 3b. Filter the state that we're unwinding to, in order to remove any
 possibilities we know will result in a conflict. Consider all possible unwinds
-to the chosen state (there may be several, amasssed from previous unused
+to the chosen state (there may be several, amassed from previous unused
 unwinds for different conflicts) when doing this filtering - only
 possibilities that will certainly result in *all* of those conflicts can be
 filtered out as having no chance of resolution
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aeafb18..285c69a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,76 @@
 # Molinillo Changelog
 
+## 0.8.0 (2021-08-09)
+
+##### Breaking
+
+* Support for Ruby 2.0, 2.1 and 2.2 has been dropped, the minimum supported
+  Ruby version is now 2.3.  
+  [David Rodríguez](https://github.com/deivid-rodriguez)
+
+##### Enhancements
+
+* Use `Array#-` in unwind logic, since it performs better than `Array#&`, so it
+  speeds up resolution.  
+  [Lukas Oberhuber](https://github.com/lukaso)
+
+* Allow specification provider to customize how dependencies are compared when
+  grouping specifications with the same dependencies.  
+  [David Rodríguez](https://github.com/deivid-rodriguez)
+
+##### Bug Fixes
+
+* None.  
+
+
+## 0.7.0 (2020-10-21)
+
+##### Breaking
+
+* Support for Ruby 1.8.7 and 1.9.3 has been dropped, the minimum supported
+  Ruby version is now 2.0.  
+  [Samuel Giddins](https://github.com/segiddins)
+
+##### Enhancements
+
+* Circular dependency errors include the full (shortest) path between the
+  circularly-dependent vertices.  
+  [Samuel Giddins](https://github.com/segiddins)
+
+##### Bug Fixes
+
+* None.  
+
+
+## 0.6.6 (2018-08-07)
+
+##### Enhancements
+
+* Improve performance of `Vertex#path_to?`.  
+  [Samuel Giddins](https://github.com/segiddins)
+
+* Allow customization of string used to say that a version conflict has occurred
+  for a particular name by passing in the `:incompatible_version_message_for_conflict` 
+  key when constructing a version conflict message with trees.  
+  [Samuel Giddins](https://github.com/segiddins)
+
+##### Bug Fixes
+
+* None.  
+
+
+## 0.6.5 (2018-03-22)
+
+##### Enhancements
+
+* Improve performance of recursive vertex methods.  
+  [Samuel Giddins](https://github.com/segiddins)
+
+##### Bug Fixes
+
+* None.  
+
+
 ## 0.6.4 (2017-10-29)
 
 ##### Enhancements
diff --git a/README.md b/README.md
index 500caf3..b4f3dad 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Molinillo
 
-[![Build Status](https://img.shields.io/travis/CocoaPods/Molinillo/master.svg?style=flat)](https://travis-ci.org/CocoaPods/Molinillo)
+[![Build Status](https://github.com/CocoaPods/Molinillo/workflows/test/badge.svg)](https://github.com/CocoaPods/Molinillo/actions?query=branch%3Amaster)
 [![Coverage](https://img.shields.io/codeclimate/coverage/github/CocoaPods/Molinillo.svg?style=flat)](https://codeclimate.com/github/CocoaPods/Molinillo)
 [![Code Climate](https://img.shields.io/codeclimate/github/CocoaPods/Molinillo.svg?style=flat)](https://codeclimate.com/github/CocoaPods/Molinillo)
 
@@ -28,8 +28,7 @@ $ gem install molinillo
 
 ## Usage
 
-See the [ARCHITECTURE](ARCHITECTURE.md) file for an overview and look at the test suite for example usage. Better documentation and examples are
-forthcoming.
+See the [ARCHITECTURE](ARCHITECTURE.md) file for an overview and look at the test suite for example usage.
 
 ## Contributing
 
diff --git a/debian/changelog b/debian/changelog
index a412802..31f1d56 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-ruby-molinillo (0.6.4-2) UNRELEASED; urgency=medium
+ruby-molinillo (0.8.0-1) UNRELEASED; urgency=medium
 
   [ Utkarsh Gupta ]
   * Add salsa-ci.yml
@@ -13,8 +13,9 @@ ruby-molinillo (0.6.4-2) UNRELEASED; urgency=medium
   * Update watch file format version to 4.
   * Apply multi-arch hints.
     + ruby-molinillo: Add :any qualifier for ruby dependency.
+  * New upstream release.
 
- -- Utkarsh Gupta <guptautkarsh2102@gmail.com>  Tue, 13 Aug 2019 06:05:54 +0530
+ -- Utkarsh Gupta <guptautkarsh2102@gmail.com>  Thu, 10 Mar 2022 19:11:28 -0000
 
 ruby-molinillo (0.6.4-1) unstable; urgency=medium
 
diff --git a/lib/molinillo.rb b/lib/molinillo.rb
index 09d1641..9d38683 100644
--- a/lib/molinillo.rb
+++ b/lib/molinillo.rb
@@ -1,11 +1,10 @@
 # frozen_string_literal: true
 
-require 'molinillo/compatibility'
-require 'molinillo/gem_metadata'
-require 'molinillo/errors'
-require 'molinillo/resolver'
-require 'molinillo/modules/ui'
-require 'molinillo/modules/specification_provider'
+require_relative 'molinillo/gem_metadata'
+require_relative 'molinillo/errors'
+require_relative 'molinillo/resolver'
+require_relative 'molinillo/modules/ui'
+require_relative 'molinillo/modules/specification_provider'
 
 # Molinillo is a generic dependency resolution algorithm.
 module Molinillo
diff --git a/lib/molinillo/compatibility.rb b/lib/molinillo/compatibility.rb
deleted file mode 100644
index 32d5d01..0000000
--- a/lib/molinillo/compatibility.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-module Molinillo
-  # Hacks needed for old Ruby versions.
-  module Compatibility
-    module_function
-
-    if [].respond_to?(:flat_map)
-      # Flat map
-      # @param [Enumerable] enum an enumerable object
-      # @block the block to flat-map with
-      # @return The enum, flat-mapped
-      def flat_map(enum, &blk)
-        enum.flat_map(&blk)
-      end
-    else
-      # Flat map
-      # @param [Enumerable] enum an enumerable object
-      # @block the block to flat-map with
-      # @return The enum, flat-mapped
-      def flat_map(enum, &blk)
-        enum.map(&blk).flatten(1)
-      end
-    end
-  end
-end
diff --git a/lib/molinillo/delegates/specification_provider.rb b/lib/molinillo/delegates/specification_provider.rb
index 95b581f..20157aa 100644
--- a/lib/molinillo/delegates/specification_provider.rb
+++ b/lib/molinillo/delegates/specification_provider.rb
@@ -26,6 +26,13 @@ module Molinillo
         end
       end
 
+      # (see Molinillo::SpecificationProvider#dependencies_equal?)
+      def dependencies_equal?(dependencies, other_dependencies)
+        with_no_such_dependency_error_handling do
+          specification_provider.dependencies_equal?(dependencies, other_dependencies)
+        end
+      end
+
       # (see Molinillo::SpecificationProvider#name_for)
       def name_for(dependency)
         with_no_such_dependency_error_handling do
diff --git a/lib/molinillo/dependency_graph.rb b/lib/molinillo/dependency_graph.rb
index 746d80e..a80ecd6 100644
--- a/lib/molinillo/dependency_graph.rb
+++ b/lib/molinillo/dependency_graph.rb
@@ -1,10 +1,9 @@
 # frozen_string_literal: true
 
-require 'set'
 require 'tsort'
 
-require 'molinillo/dependency_graph/log'
-require 'molinillo/dependency_graph/vertex'
+require_relative 'dependency_graph/log'
+require_relative 'dependency_graph/vertex'
 
 module Molinillo
   # A directed acyclic graph that is tuned to hold named dependencies
@@ -124,6 +123,7 @@ module Molinillo
       dot.join("\n")
     end
 
+    # @param [DependencyGraph] other
     # @return [Boolean] whether the two dependency graphs are equal, determined
     #   by a recursive traversal of each {#root_vertices} and its
     #   {Vertex#successors}
@@ -190,7 +190,7 @@ module Molinillo
     # @return [Edge] the added edge
     def add_edge(origin, destination, requirement)
       if destination.path_to?(origin)
-        raise CircularDependencyError.new([origin, destination])
+        raise CircularDependencyError.new(path(destination, origin))
       end
       add_edge_no_circular(origin, destination, requirement)
     end
@@ -219,5 +219,37 @@ module Molinillo
     def add_edge_no_circular(origin, destination, requirement)
       log.add_edge_no_circular(self, origin.name, destination.name, requirement)
     end
+
+    # Returns the path between two vertices
+    # @raise [ArgumentError] if there is no path between the vertices
+    # @param [Vertex] from
+    # @param [Vertex] to
+    # @return [Array<Vertex>] the shortest path from `from` to `to`
+    def path(from, to)
+      distances = Hash.new(vertices.size + 1)
+      distances[from.name] = 0
+      predecessors = {}
+      each do |vertex|
+        vertex.successors.each do |successor|
+          if distances[successor.name] > distances[vertex.name] + 1
+            distances[successor.name] = distances[vertex.name] + 1
+            predecessors[successor] = vertex
+          end
+        end
+      end
+
+      path = [to]
+      while before = predecessors[to]
+        path << before
+        to = before
+        break if to == from
+      end
+
+      unless path.last.equal?(from)
+        raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
+      end
+
+      path.reverse
+    end
   end
 end
diff --git a/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/molinillo/dependency_graph/add_edge_no_circular.rb
index 0738b0a..6857f68 100644
--- a/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ b/lib/molinillo/dependency_graph/add_edge_no_circular.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/action'
+require_relative 'action'
 module Molinillo
   class DependencyGraph
     # @!visibility private
diff --git a/lib/molinillo/dependency_graph/add_vertex.rb b/lib/molinillo/dependency_graph/add_vertex.rb
index 63a551e..aad3b96 100644
--- a/lib/molinillo/dependency_graph/add_vertex.rb
+++ b/lib/molinillo/dependency_graph/add_vertex.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/action'
+require_relative 'action'
 module Molinillo
   class DependencyGraph
     # @!visibility private
diff --git a/lib/molinillo/dependency_graph/delete_edge.rb b/lib/molinillo/dependency_graph/delete_edge.rb
index 9a8d222..b46196e 100644
--- a/lib/molinillo/dependency_graph/delete_edge.rb
+++ b/lib/molinillo/dependency_graph/delete_edge.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/action'
+require_relative 'action'
 module Molinillo
   class DependencyGraph
     # @!visibility private
diff --git a/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/molinillo/dependency_graph/detach_vertex_named.rb
index a7e64fb..b6521df 100644
--- a/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ b/lib/molinillo/dependency_graph/detach_vertex_named.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/action'
+require_relative 'action'
 module Molinillo
   class DependencyGraph
     # @!visibility private
diff --git a/lib/molinillo/dependency_graph/log.rb b/lib/molinillo/dependency_graph/log.rb
index 6a71033..b5445c9 100644
--- a/lib/molinillo/dependency_graph/log.rb
+++ b/lib/molinillo/dependency_graph/log.rb
@@ -1,11 +1,11 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/add_edge_no_circular'
-require 'molinillo/dependency_graph/add_vertex'
-require 'molinillo/dependency_graph/delete_edge'
-require 'molinillo/dependency_graph/detach_vertex_named'
-require 'molinillo/dependency_graph/set_payload'
-require 'molinillo/dependency_graph/tag'
+require_relative 'add_edge_no_circular'
+require_relative 'add_vertex'
+require_relative 'delete_edge'
+require_relative 'detach_vertex_named'
+require_relative 'set_payload'
+require_relative 'tag'
 
 module Molinillo
   class DependencyGraph
diff --git a/lib/molinillo/dependency_graph/set_payload.rb b/lib/molinillo/dependency_graph/set_payload.rb
index ff039e4..14f7cf3 100644
--- a/lib/molinillo/dependency_graph/set_payload.rb
+++ b/lib/molinillo/dependency_graph/set_payload.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/action'
+require_relative 'action'
 module Molinillo
   class DependencyGraph
     # @!visibility private
diff --git a/lib/molinillo/dependency_graph/tag.rb b/lib/molinillo/dependency_graph/tag.rb
index b789513..d361eb8 100644
--- a/lib/molinillo/dependency_graph/tag.rb
+++ b/lib/molinillo/dependency_graph/tag.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph/action'
+require_relative 'action'
 module Molinillo
   class DependencyGraph
     # @!visibility private
@@ -14,11 +14,11 @@ module Molinillo
       end
 
       # (see Action#up)
-      def up(_graph)
+      def up(graph)
       end
 
       # (see Action#down)
-      def down(_graph)
+      def down(graph)
       end
 
       # @!group Tag
diff --git a/lib/molinillo/dependency_graph/vertex.rb b/lib/molinillo/dependency_graph/vertex.rb
index 1269527..5eafc90 100644
--- a/lib/molinillo/dependency_graph/vertex.rb
+++ b/lib/molinillo/dependency_graph/vertex.rb
@@ -50,14 +50,25 @@ module Molinillo
         incoming_edges.map(&:origin)
       end
 
-      # @return [Array<Vertex>] the vertices of {#graph} where `self` is a
+      # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
       #   {#descendent?}
       def recursive_predecessors
-        vertices = predecessors
-        vertices += Compatibility.flat_map(vertices, &:recursive_predecessors)
-        vertices.uniq!
+        _recursive_predecessors
+      end
+
+      # @param [Set<Vertex>] vertices the set to add the predecessors to
+      # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
+      #   {#descendent?}
+      def _recursive_predecessors(vertices = new_vertex_set)
+        incoming_edges.each do |edge|
+          vertex = edge.origin
+          next unless vertices.add?(vertex)
+          vertex._recursive_predecessors(vertices)
+        end
+
         vertices
       end
+      protected :_recursive_predecessors
 
       # @return [Array<Vertex>] the vertices of {#graph} that have an edge with
       #   `self` as their {Edge#origin}
@@ -65,14 +76,25 @@ module Molinillo
         outgoing_edges.map(&:destination)
       end
 
-      # @return [Array<Vertex>] the vertices of {#graph} where `self` is an
+      # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
       #   {#ancestor?}
       def recursive_successors
-        vertices = successors
-        vertices += Compatibility.flat_map(vertices, &:recursive_successors)
-        vertices.uniq!
+        _recursive_successors
+      end
+
+      # @param [Set<Vertex>] vertices the set to add the successors to
+      # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
+      #   {#ancestor?}
+      def _recursive_successors(vertices = new_vertex_set)
+        outgoing_edges.each do |edge|
+          vertex = edge.destination
+          next unless vertices.add?(vertex)
+          vertex._recursive_successors(vertices)
+        end
+
         vertices
       end
+      protected :_recursive_successors
 
       # @return [String] a string suitable for debugging
       def inspect
@@ -106,21 +128,37 @@ module Molinillo
 
       # Is there a path from `self` to `other` following edges in the
       # dependency graph?
-      # @return true iff there is a path following edges within this {#graph}
+      # @return whether there is a path following edges within this {#graph}
       def path_to?(other)
-        equal?(other) || successors.any? { |v| v.path_to?(other) }
+        _path_to?(other)
       end
 
       alias descendent? path_to?
 
+      # @param [Vertex] other the vertex to check if there's a path to
+      # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
+      # @return [Boolean] whether there is a path to `other` from `self`
+      def _path_to?(other, visited = new_vertex_set)
+        return false unless visited.add?(self)
+        return true if equal?(other)
+        successors.any? { |v| v._path_to?(other, visited) }
+      end
+      protected :_path_to?
+
       # Is there a path from `other` to `self` following edges in the
       # dependency graph?
-      # @return true iff there is a path following edges within this {#graph}
+      # @return whether there is a path following edges within this {#graph}
       def ancestor?(other)
         other.path_to?(self)
       end
 
       alias is_reachable_from? ancestor?
+
+      def new_vertex_set
+        require 'set'
+        Set.new
+      end
+      private :new_vertex_set
     end
   end
 end
diff --git a/lib/molinillo/errors.rb b/lib/molinillo/errors.rb
index 51e464d..c240ecd 100644
--- a/lib/molinillo/errors.rb
+++ b/lib/molinillo/errors.rb
@@ -18,7 +18,7 @@ module Molinillo
     # @param [Array<Object>] required_by @see {#required_by}
     def initialize(dependency, required_by = [])
       @dependency = dependency
-      @required_by = required_by
+      @required_by = required_by.uniq
       super()
     end
 
@@ -34,7 +34,7 @@ module Molinillo
 
   # An error caused by attempting to fulfil a dependency that was circular
   #
-  # @note This exception will be thrown iff a {Vertex} is added to a
+  # @note This exception will be thrown if and only if a {Vertex} is added to a
   #   {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
   #   existing {DependencyGraph::Vertex}
   class CircularDependencyError < ResolverError
@@ -65,7 +65,7 @@ module Molinillo
     # @param [SpecificationProvider] specification_provider see {#specification_provider}
     def initialize(conflicts, specification_provider)
       pairs = []
-      Compatibility.flat_map(conflicts.values.flatten, &:requirements).each do |conflicting|
+      conflicts.values.flat_map(&:requirements).each do |conflicting|
         conflicting.each do |source, conflict_requirements|
           conflict_requirements.each do |c|
             pairs << [c, source]
@@ -80,7 +80,7 @@ module Molinillo
       @specification_provider = specification_provider
     end
 
-    require 'molinillo/delegates/specification_provider'
+    require_relative 'delegates/specification_provider'
     include Delegates::SpecificationProvider
 
     # @return [String] An error message that includes requirement trees,
@@ -101,9 +101,14 @@ module Molinillo
       printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
       additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
       version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
+      incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
+        proc do |name, _conflict|
+          %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
+        end
+      end
 
       conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
-        o << %(\n#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":\n)
+        o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
         if conflict.locked_requirement
           o << %(  In snapshot (#{name_for_locking_dependency_source}):\n)
           o << %(    #{printable_requirement.call(conflict.locked_requirement)}\n)
@@ -116,7 +121,7 @@ module Molinillo
           t = ''.dup
           depth = 2
           tree.each do |req|
-            t << '  ' * depth << req.to_s
+            t << '  ' * depth << printable_requirement.call(req)
             unless tree.last == req
               if spec = conflict.activated_by_name[name_for(req)]
                 t << %( was resolved to #{version_for_spec.call(spec)}, which)
diff --git a/lib/molinillo/gem_metadata.rb b/lib/molinillo/gem_metadata.rb
index 7222ead..a22afd2 100644
--- a/lib/molinillo/gem_metadata.rb
+++ b/lib/molinillo/gem_metadata.rb
@@ -2,5 +2,5 @@
 
 module Molinillo
   # The version of Molinillo.
-  VERSION = '0.6.4'.freeze
+  VERSION = '0.8.0'.freeze
 end
diff --git a/lib/molinillo/modules/specification_provider.rb b/lib/molinillo/modules/specification_provider.rb
index 036009f..8012104 100644
--- a/lib/molinillo/modules/specification_provider.rb
+++ b/lib/molinillo/modules/specification_provider.rb
@@ -45,6 +45,17 @@ module Molinillo
       true
     end
 
+    # Determines whether two arrays of dependencies are equal, and thus can be
+    # grouped.
+    #
+    # @param [Array<Object>] dependencies
+    # @param [Array<Object>] other_dependencies
+    # @return [Boolean] whether `dependencies` and `other_dependencies` should
+    #   be considered equal.
+    def dependencies_equal?(dependencies, other_dependencies)
+      dependencies == other_dependencies
+    end
+
     # Returns the name for the given `dependency`.
     # @note This method should be 'pure', i.e. the return value should depend
     #   only on the `dependency` parameter.
diff --git a/lib/molinillo/resolution.rb b/lib/molinillo/resolution.rb
index 4854bb2..455476c 100644
--- a/lib/molinillo/resolution.rb
+++ b/lib/molinillo/resolution.rb
@@ -207,7 +207,7 @@ module Molinillo
       def start_resolution
         @started_at = Time.now
 
-        handle_missing_or_push_dependency_state(initial_state)
+        push_initial_state
 
         debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
         resolver_ui.before_resolution
@@ -238,11 +238,11 @@ module Molinillo
         debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
       end
 
-      require 'molinillo/state'
-      require 'molinillo/modules/specification_provider'
+      require_relative 'state'
+      require_relative 'modules/specification_provider'
 
-      require 'molinillo/delegates/resolution_state'
-      require 'molinillo/delegates/specification_provider'
+      require_relative 'delegates/resolution_state'
+      require_relative 'delegates/specification_provider'
 
       include Molinillo::Delegates::ResolutionState
       include Molinillo::Delegates::SpecificationProvider
@@ -273,10 +273,10 @@ module Molinillo
         states.last
       end
 
-      # Creates the initial state for the resolution, based upon the
+      # Creates and pushes the initial state for the resolution, based upon the
       # {#requested} dependencies
-      # @return [DependencyState] the initial state for the resolution
-      def initial_state
+      # @return [void]
+      def push_initial_state
         graph = DependencyGraph.new.tap do |dg|
           original_requested.each do |requested|
             vertex = dg.add_vertex(name_for(requested), nil, true)
@@ -285,18 +285,7 @@ module Molinillo
           dg.tag(:initial_state)
         end
 
-        requirements = sort_dependencies(original_requested, graph, {})
-        initial_requirement = requirements.shift
-        DependencyState.new(
-          initial_requirement && name_for(initial_requirement),
-          requirements,
-          graph,
-          initial_requirement,
-          possibilities_for_requirement(initial_requirement, graph),
-          0,
-          {},
-          []
-        )
+        push_state_for_requirements(original_requested, true, graph)
       end
 
       # Unwinds the states stack because a conflict has been encountered
@@ -340,11 +329,11 @@ module Molinillo
 
         # Look for past conflicts that could be unwound to affect the
         # requirement tree for the current conflict
+        all_reqs = last_detail_for_current_unwind.all_requirements
+        all_reqs_size = all_reqs.size
         relevant_unused_unwinds = unused_unwind_options.select do |alternative|
-          intersecting_requirements =
-            last_detail_for_current_unwind.all_requirements &
-            alternative.requirements_unwound_to_instead
-          next if intersecting_requirements.empty?
+          diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
+          next if diff_reqs.size == all_reqs_size
           # Find the highest index unwind whilst looping through
           current_detail = alternative if alternative > current_detail
           alternative
@@ -355,13 +344,17 @@ module Molinillo
         state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
 
         # Update the requirements_unwound_to_instead on any relevant unused unwinds
-        relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
-        unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
+        relevant_unused_unwinds.each do |d|
+          (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
+        end
+        unwind_details.each do |d|
+          (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
+        end
 
         current_detail
       end
 
-      # @param [Array<Object>] array of requirements that combine to create a conflict
+      # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
       # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
       #    of resolving the passed requirements
       def unwind_options_for_requirements(binding_requirements)
@@ -429,7 +422,7 @@ module Molinillo
       end
 
       # @param [DependencyState] state
-      # @param [Array] array of requirements
+      # @param [Array] binding_requirements array of requirements
       # @return [Boolean] whether or not the given state has any possibilities
       #    that could satisfy the given requirements
       def conflict_fixing_possibilities?(state, binding_requirements)
@@ -444,7 +437,8 @@ module Molinillo
 
       # Filter's a state's possibilities to remove any that would not fix the
       # conflict we've just rewound from
-      # @param [UnwindDetails] details of the conflict just unwound from
+      # @param [UnwindDetails] unwind_details details of the conflict just
+      #   unwound from
       # @return [void]
       def filter_possibilities_after_unwind(unwind_details)
         return unless state && !state.possibilities.empty?
@@ -458,7 +452,7 @@ module Molinillo
 
       # Filter's a state's possibilities to remove any that would not satisfy
       # the requirements in the conflict we've just rewound from
-      # @param [UnwindDetails] details of the conflict just unwound from
+      # @param [UnwindDetails] unwind_details details of the conflict just unwound from
       # @return [void]
       def filter_possibilities_for_primary_unwind(unwind_details)
         unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
@@ -491,7 +485,7 @@ module Molinillo
 
       # Filter's a state's possibilities to remove any that would (eventually)
       # create a requirement in the conflict we've just rewound from
-      # @param [UnwindDetails] details of the conflict just unwound from
+      # @param [UnwindDetails] unwind_details details of the conflict just unwound from
       # @return [void]
       def filter_possibilities_for_parent_unwind(unwind_details)
         unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
@@ -500,7 +494,7 @@ module Molinillo
         primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
         parent_unwinds = unwinds_to_state.uniq - primary_unwinds
 
-        allowed_possibility_sets = Compatibility.flat_map(primary_unwinds) do |unwind|
+        allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
           states[unwind.state_index].possibilities.select do |possibility_set|
             possibility_set.possibilities.any? do |poss|
               possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
@@ -508,7 +502,7 @@ module Molinillo
           end
         end
 
-        requirements_to_avoid = Compatibility.flat_map(parent_unwinds, &:sub_dependencies_to_avoid)
+        requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
 
         state.possibilities.reject! do |possibility_set|
           !allowed_possibility_sets.include?(possibility_set) &&
@@ -524,12 +518,12 @@ module Molinillo
 
         possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
 
-        # When there’s a `CircularDependency` error the conflicting requirement
-        # (the one causing the circular) won’t be `conflict.requirement`
-        # (which won’t be for the right state, because we won’t have created it,
-        # because it’s circular).
-        # We need to make sure we have that requirement in the conflict’s list,
-        # otherwise we won’t be able to unwind properly, so we just return all
+        # When there's a `CircularDependency` error the conflicting requirement
+        # (the one causing the circular) won't be `conflict.requirement`
+        # (which won't be for the right state, because we won't have created it,
+        # because it's circular).
+        # We need to make sure we have that requirement in the conflict's list,
+        # otherwise we won't be able to unwind properly, so we just return all
         # the requirements for the conflict.
         return possible_binding_requirements if conflict.underlying_error
 
@@ -558,8 +552,8 @@ module Molinillo
       end
 
       # @param [Object] requirement we wish to check
-      # @param [Array] array of requirements
-      # @param [Array] array of possibilities the requirements will be used to filter
+      # @param [Array] possible_binding_requirements array of requirements
+      # @param [Array] possibilities array of possibilities the requirements will be used to filter
       # @return [Boolean] whether or not the given requirement is required to filter
       #    out all elements of the array of possibilities.
       def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
@@ -568,6 +562,7 @@ module Molinillo
         end
       end
 
+      # @param [Object] requirement
       # @return [Object] the requirement that led to `requirement` being added
       #   to the list of requirements.
       def parent_of(requirement)
@@ -577,6 +572,7 @@ module Molinillo
         parent_state.requirement
       end
 
+      # @param [String] name
       # @return [Object] the requirement that led to a version of a possibility
       #   with the given name being activated.
       def requirement_for_existing_name(name)
@@ -585,6 +581,7 @@ module Molinillo
         states.find { |s| s.name == name }.requirement
       end
 
+      # @param [Object] requirement
       # @return [ResolutionState] the state whose `requirement` is the given
       #   `requirement`.
       def find_state_for(requirement)
@@ -592,6 +589,7 @@ module Molinillo
         states.find { |i| requirement == i.requirement }
       end
 
+      # @param [Object] underlying_error
       # @return [Conflict] a {Conflict} that reflects the failure to activate
       #   the {#possibility} in conjunction with the current {#state}
       def create_conflict(underlying_error = nil)
@@ -628,6 +626,7 @@ module Molinillo
         vertex.requirements.map { |r| requirement_tree_for(r) }
       end
 
+      # @param [Object] requirement
       # @return [Array<Object>] the list of requirements that led to
       #   `requirement` being required.
       def requirement_tree_for(requirement)
@@ -673,9 +672,8 @@ module Molinillo
           attempt_to_filter_existing_spec(existing_vertex)
         else
           latest = possibility.latest_version
-          # use reject!(!satisfied) for 1.8.7 compatibility
-          possibility.possibilities.reject! do |possibility|
-            !requirement_satisfied_by?(requirement, activated, possibility)
+          possibility.possibilities.select! do |possibility|
+            requirement_satisfied_by?(requirement, activated, possibility)
           end
           if possibility.latest_version.nil?
             # ensure there's a possibility for better error messages
@@ -705,7 +703,7 @@ module Molinillo
 
       # Generates a filtered version of the existing vertex's `PossibilitySet` using the
       # current state's `requirement`
-      # @param [Object] existing vertex
+      # @param [Object] vertex existing vertex
       # @return [PossibilitySet] filtered possibility set
       def filtered_possibility_set(vertex)
         PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
@@ -730,7 +728,7 @@ module Molinillo
       end
 
       # Requires the dependencies that the recently activated spec has
-      # @param [Object] activated_possibility the PossibilitySet that has just been
+      # @param [Object] possibility_set the PossibilitySet that has just been
       #   activated
       # @return [void]
       def require_nested_dependencies_for(possibility_set)
@@ -749,6 +747,8 @@ module Molinillo
       # Pushes a new {DependencyState} that encapsulates both existing and new
       # requirements
       # @param [Array] new_requirements
+      # @param [Boolean] requires_sort
+      # @param [Object] new_activated
       # @return [void]
       def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
         new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
@@ -767,7 +767,8 @@ module Molinillo
 
       # Checks a proposed requirement with any existing locked requirement
       # before generating an array of possibilities for it.
-      # @param [Object] the proposed requirement
+      # @param [Object] requirement the proposed requirement
+      # @param [Object] activated
       # @return [Array] possibilities
       def possibilities_for_requirement(requirement, activated = self.activated)
         return [] unless requirement
@@ -778,7 +779,8 @@ module Molinillo
         group_possibilities(search_for(requirement))
       end
 
-      # @param [Object] the proposed requirement
+      # @param [Object] requirement the proposed requirement
+      # @param [Object] activated
       # @return [Array] possibility set containing only the locked requirement, if any
       def locked_requirement_possibility_set(requirement, activated = self.activated)
         all_possibilities = search_for(requirement)
@@ -797,15 +799,15 @@ module Molinillo
       # Build an array of PossibilitySets, with each element representing a group of
       # dependency versions that all have the same sub-dependency version constraints
       # and are contiguous.
-      # @param [Array] an array of possibilities
-      # @return [Array] an array of possibility sets
+      # @param [Array] possibilities an array of possibilities
+      # @return [Array<PossibilitySet>] an array of possibility sets
       def group_possibilities(possibilities)
         possibility_sets = []
         current_possibility_set = nil
 
         possibilities.reverse_each do |possibility|
           dependencies = dependencies_for(possibility)
-          if current_possibility_set && current_possibility_set.dependencies == dependencies
+          if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
             current_possibility_set.possibilities.unshift(possibility)
           else
             possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
diff --git a/lib/molinillo/resolver.rb b/lib/molinillo/resolver.rb
index a2e09c4..a2ce6d5 100644
--- a/lib/molinillo/resolver.rb
+++ b/lib/molinillo/resolver.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'molinillo/dependency_graph'
+require_relative 'dependency_graph'
 
 module Molinillo
   # This class encapsulates a dependency resolver.
@@ -9,7 +9,7 @@ module Molinillo
   #
   #
   class Resolver
-    require 'molinillo/resolution'
+    require_relative 'resolution'
 
     # @return [SpecificationProvider] the specification provider used
     #   in the resolution process
diff --git a/molinillo.gemspec b/molinillo.gemspec
index 2e3f592..4849da2 100644
--- a/molinillo.gemspec
+++ b/molinillo.gemspec
@@ -2,34 +2,33 @@
 # This file has been automatically generated by gem2tgz #
 #########################################################
 # -*- encoding: utf-8 -*-
+# stub: molinillo 0.8.0 ruby lib
 
 Gem::Specification.new do |s|
-  s.name = "molinillo"
-  s.version = "0.6.4"
+  s.name = "molinillo".freeze
+  s.version = "0.8.0"
 
-  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
-  s.authors = ["Samuel E. Giddins"]
-  s.date = "2017-10-29"
-  s.email = ["segiddins@segiddins.me"]
-  s.files = ["ARCHITECTURE.md", "CHANGELOG.md", "LICENSE", "README.md", "lib/molinillo.rb", "lib/molinillo/compatibility.rb", "lib/molinillo/delegates/resolution_state.rb", "lib/molinillo/delegates/specification_provider.rb", "lib/molinillo/dependency_graph.rb", "lib/molinillo/dependency_graph/action.rb", "lib/molinillo/dependency_graph/add_edge_no_circular.rb", "lib/molinillo/dependency_graph/add_vertex.rb", "lib/molinillo/dependency_graph/delete_edge.rb", "lib/molinillo/dependency_graph/detach_vertex_named.rb", "lib/molinillo/dependency_graph/log.rb", "lib/molinillo/dependency_graph/set_payload.rb", "lib/molinillo/dependency_graph/tag.rb", "lib/molinillo/dependency_graph/vertex.rb", "lib/molinillo/errors.rb", "lib/molinillo/gem_metadata.rb", "lib/molinillo/modules/specification_provider.rb", "lib/molinillo/modules/ui.rb", "lib/molinillo/resolution.rb", "lib/molinillo/resolver.rb", "lib/molinillo/state.rb"]
-  s.homepage = "https://github.com/CocoaPods/Molinillo"
-  s.licenses = ["MIT"]
-  s.require_paths = ["lib"]
-  s.rubygems_version = "1.8.23"
-  s.summary = "Provides support for dependency resolution"
+  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+  s.require_paths = ["lib".freeze]
+  s.authors = ["Samuel E. Giddins".freeze]
+  s.date = "2021-08-09"
+  s.email = ["segiddins@segiddins.me".freeze]
+  s.files = ["ARCHITECTURE.md".freeze, "CHANGELOG.md".freeze, "LICENSE".freeze, "README.md".freeze, "lib/molinillo.rb".freeze, "lib/molinillo/delegates/resolution_state.rb".freeze, "lib/molinillo/delegates/specification_provider.rb".freeze, "lib/molinillo/dependency_graph.rb".freeze, "lib/molinillo/dependency_graph/action.rb".freeze, "lib/molinillo/dependency_graph/add_edge_no_circular.rb".freeze, "lib/molinillo/dependency_graph/add_vertex.rb".freeze, "lib/molinillo/dependency_graph/delete_edge.rb".freeze, "lib/molinillo/dependency_graph/detach_vertex_named.rb".freeze, "lib/molinillo/dependency_graph/log.rb".freeze, "lib/molinillo/dependency_graph/set_payload.rb".freeze, "lib/molinillo/dependency_graph/tag.rb".freeze, "lib/molinillo/dependency_graph/vertex.rb".freeze, "lib/molinillo/errors.rb".freeze, "lib/molinillo/gem_metadata.rb".freeze, "lib/molinillo/modules/specification_provider.rb".freeze, "lib/molinillo/modules/ui.rb".freeze, "lib/molinillo/resolution.rb".freeze, "lib/molinillo/resolver.rb".freeze, "lib/molinillo/state.rb".freeze]
+  s.homepage = "https://github.com/CocoaPods/Molinillo".freeze
+  s.licenses = ["MIT".freeze]
+  s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
+  s.rubygems_version = "2.7.6.2".freeze
+  s.summary = "Provides support for dependency resolution".freeze
 
   if s.respond_to? :specification_version then
     s.specification_version = 4
 
     if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_development_dependency(%q<bundler>, ["~> 1.5"])
-      s.add_development_dependency(%q<rake>, [">= 0"])
+      s.add_development_dependency(%q<rake>.freeze, [">= 0"])
     else
-      s.add_dependency(%q<bundler>, ["~> 1.5"])
-      s.add_dependency(%q<rake>, [">= 0"])
+      s.add_dependency(%q<rake>.freeze, [">= 0"])
     end
   else
-    s.add_dependency(%q<bundler>, ["~> 1.5"])
-    s.add_dependency(%q<rake>, [">= 0"])
+    s.add_dependency(%q<rake>.freeze, [">= 0"])
   end
 end

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/molinillo-0.8.0.gemspec

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/ruby/vendor_ruby/molinillo/compatibility.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/molinillo-0.6.4.gemspec

Control files: lines which differ (wdiff format)

  • Ruby-Versions: all

More details

Full run details