123456789_123456789_123456789_123456789_123456789_

Class: Gem::Molinillo::Resolver::Resolution

Relationships & Source Files
Namespace Children
Classes:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb

Overview

A specific resolution from a given ::Gem::Molinillo::Resolver

Class Method Summary

Instance Attribute Summary

Instance Method Summary

::Gem::Molinillo::Delegates::SpecificationProvider - Included

#allow_missing?

Returns whether this dependency, which has no possible matching specifications, can safely be ignored.

#dependencies_equal?

Determines whether two arrays of dependencies are equal, and thus can be grouped.

#dependencies_for

Returns the dependencies of specification.

#name_for

Returns the name for the given dependency.

#name_for_explicit_dependency_source, #name_for_locking_dependency_source,
#requirement_satisfied_by?

Determines whether the given requirement is satisfied by the given spec, in the context of the current activated dependency graph.

#search_for

Search for the specifications that match the given dependency.

#sort_dependencies

Sort dependencies so that the ones that are easiest to resolve are first.

#with_no_such_dependency_error_handling

::Gem::Molinillo::Delegates::ResolutionState - Included

#activated

A state that a Resolution can be in.

#conflicts

A state that a Resolution can be in.

#depth

A state that a Resolution can be in.

#name

A state that a Resolution can be in.

#possibilities

A state that a Resolution can be in.

#requirement

A state that a Resolution can be in.

#requirements

A state that a Resolution can be in.

#unused_unwind_options

A state that a Resolution can be in.

Constructor Details

.new(specification_provider, resolver_ui, requested, base) ⇒ Resolution

Initializes a new resolution.

Parameters:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 152

def initialize(specification_provider, resolver_ui, requested, base)
  @specification_provider = specification_provider
  @resolver_ui = resolver_ui
  @original_requested = requested
  @base = base
  @states = []
  @iteration_counter = 0
  @parents_of = Hash.new { |h, k| h[k] = [] }
end

Instance Attribute Details

#baseDependencyGraph (readonly)

Returns:

  • (DependencyGraph)

    the base dependency graph to which dependencies should be ‘locked’

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 141

attr_reader :base

#iteration_rateInteger (rw, private)

Returns:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 192

attr_accessor :iteration_rate

#original_requestedArray (readonly)

Returns:

  • (Array)

    the dependencies that were explicitly required

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 144

attr_reader :original_requested

#resolver_uiUI (readonly)

Returns:

  • (UI)

    the ::Gem::Molinillo::UI that knows how to communicate feedback about the resolution process back to the user

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 137

attr_reader :resolver_ui

#specification_providerSpecificationProvider (readonly)

Returns:

  • (SpecificationProvider)

    the provider that knows about dependencies, requirements, specifications, versions, etc.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 133

attr_reader :specification_provider

#started_atTime (rw, private)

Returns:

  • (Time)

    the time at which resolution began

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 196

attr_accessor :started_at

#statesArray<ResolutionState> (rw, private)

Returns:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 200

attr_accessor :states

Instance Method Details

#activate_new_specvoid (private)

This method returns an undefined value.

Add the current #possibility to the dependency graph of the current #state

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 723

def activate_new_spec
  conflicts.delete(name)
  debug(depth) { "Activated #{name} at #{possibility}" }
  activated.set_payload(name, possibility)
  require_nested_dependencies_for(possibility)
end

#attempt_to_activatevoid (private)

This method returns an undefined value.

Attempts to activate the current #possibility

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 667

def attempt_to_activate
  debug(depth) { 'Attempting to activate ' + possibility.to_s }
  existing_vertex = activated.vertex_named(name)
  if existing_vertex.payload
    debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
    attempt_to_filter_existing_spec(existing_vertex)
  else
    latest = possibility.latest_version
    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
      possibility.possibilities << latest if latest
      create_conflict
      unwind_for_conflict
    else
      activate_new_spec
    end
  end
end

#attempt_to_filter_existing_spec(vertex) ⇒ void (private)

This method returns an undefined value.

Attempts to update the existing vertex’s Resolution::PossibilitySet with a filtered version

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 691

def attempt_to_filter_existing_spec(vertex)
  filtered_set = filtered_possibility_set(vertex)
  if !filtered_set.possibilities.empty?
    activated.set_payload(name, filtered_set)
    new_requirements = requirements.dup
    push_state_for_requirements(new_requirements, false)
  else
    create_conflict
    debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
    unwind_for_conflict
  end
end

#binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities) ⇒ Boolean (private)

Parameters:

  • requirement (Object)

    we wish to check

  • possible_binding_requirements (Array)

    array of requirements

  • possibilities (Array)

    array of possibilities the requirements will be used to filter

Returns:

  • (Boolean)

    whether or not the given requirement is required to filter out all elements of the array of possibilities.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 559

def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
  possibilities.any? do |poss|
    possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
  end
end

#binding_requirements_for_conflict(conflict) ⇒ Array (private)

Parameters:

Returns:

  • (Array)

    minimal array of requirements that would cause the passed conflict to occur.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 516

def binding_requirements_for_conflict(conflict)
  return [conflict.requirement] if conflict.possibility.nil?

  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
  # the requirements for the conflict.
  return possible_binding_requirements if conflict.underlying_error

  possibilities = search_for(conflict.requirement)

  # If all the requirements together don't filter out all possibilities,
  # then the only two requirements we need to consider are the initial one
  # (where the dependency's version was first chosen) and the last
  if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
    return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
  end

  # Loop through the possible binding requirements, removing each one
  # that doesn't bind. Use a `reverse_each` as we want the earliest set of
  # binding requirements, and don't use `reject!` as we wish to refine the
  # array *on each iteration*.
  binding_requirements = possible_binding_requirements.dup
  possible_binding_requirements.reverse_each do |req|
    next if req == conflict.requirement
    unless binding_requirement_in_set?(req, binding_requirements, possibilities)
      binding_requirements -= [req]
    end
  end

  binding_requirements
end

#build_details_for_unwindUnwindDetails (private)

Returns:

  • (UnwindDetails)

    Details of the nearest index to which we could unwind

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 321

def build_details_for_unwind
  # Get the possible unwinds for the current conflict
  current_conflict = conflicts[name]
  binding_requirements = binding_requirements_for_conflict(current_conflict)
  unwind_details = unwind_options_for_requirements(binding_requirements)

  last_detail_for_current_unwind = unwind_details.sort.last
  current_detail = last_detail_for_current_unwind

  # 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|
    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
  end

  # Add the current unwind options to the `unused_unwind_options` array.
  # The "used" option will be filtered out during `unwind_for_conflict`.
  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 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

#conflict_fixing_possibilities?(state, binding_requirements) ⇒ Boolean (private)

Parameters:

  • state (DependencyState)
  • binding_requirements (Array)

    array of requirements

Returns:

  • (Boolean)

    whether or not the given state has any possibilities that could satisfy the given requirements

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 428

def conflict_fixing_possibilities?(state, binding_requirements)
  return false unless state

  state.possibilities.any? do |possibility_set|
    possibility_set.possibilities.any? do |poss|
      possibility_satisfies_requirements?(poss, binding_requirements)
    end
  end
end

#create_conflict(underlying_error = nil) ⇒ Conflict (private)

Parameters:

  • underlying_error (Object) (defaults to: nil)

Returns:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 595

def create_conflict(underlying_error = nil)
  vertex = activated.vertex_named(name)
  locked_requirement = locked_requirement_named(name)

  requirements = {}
  unless vertex.explicit_requirements.empty?
    requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
  end
  requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
  vertex.incoming_edges.each do |edge|
    (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
  end

  activated_by_name = {}
  activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
  conflicts[name] = Conflict.new(
    requirement,
    requirements,
    vertex.payload && vertex.payload.latest_version,
    possibility,
    locked_requirement,
    requirement_trees,
    activated_by_name,
    underlying_error
  )
end

#debug(depth = 0, &block) ⇒ void (private)

This method returns an undefined value.

Calls the #resolver_ui‘s UI#debug method

Parameters:

  • depth (Integer) (defaults to: 0)

    the depth of the #states stack

  • block (Proc)

    a block that yields a #to_s

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 661

def debug(depth = 0, &block)
  resolver_ui.debug(depth, &block)
end

#end_resolutionvoid (private)

This method returns an undefined value.

Ends the resolution process

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 231

def end_resolution
  resolver_ui.after_resolution
  debug do
    "Finished resolution (#{@iteration_counter} steps) " \
    "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
  end
  debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
  debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
end

#filter_possibilities_after_unwind(unwind_details) ⇒ void (private)

This method returns an undefined value.

Filter’s a state’s possibilities to remove any that would not fix the conflict we’ve just rewound from

Parameters:

  • unwind_details (UnwindDetails)

    details of the conflict just unwound from

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 443

def filter_possibilities_after_unwind(unwind_details)
  return unless state && !state.possibilities.empty?

  if unwind_details.unwinding_to_primary_requirement?
    filter_possibilities_for_primary_unwind(unwind_details)
  else
    filter_possibilities_for_parent_unwind(unwind_details)
  end
end

#filter_possibilities_for_parent_unwind(unwind_details) ⇒ void (private)

This method returns an undefined value.

Filter’s a state’s possibilities to remove any that would (eventually) create a requirement in the conflict we’ve just rewound from

Parameters:

  • unwind_details (UnwindDetails)

    details of the conflict just unwound from

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 490

def filter_possibilities_for_parent_unwind(unwind_details)
  unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
  unwinds_to_state << unwind_details

  primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
  parent_unwinds = unwinds_to_state.uniq - primary_unwinds

  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)
      end
    end
  end

  requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)

  state.possibilities.reject! do |possibility_set|
    !allowed_possibility_sets.include?(possibility_set) &&
      (requirements_to_avoid - possibility_set.dependencies).empty?
  end
end

#filter_possibilities_for_primary_unwind(unwind_details) ⇒ void (private)

This method returns an undefined value.

Filter’s a state’s possibilities to remove any that would not satisfy the requirements in the conflict we’ve just rewound from

Parameters:

  • unwind_details (UnwindDetails)

    details of the conflict just unwound from

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 457

def filter_possibilities_for_primary_unwind(unwind_details)
  unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
  unwinds_to_state << unwind_details
  unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)

  state.possibilities.reject! do |possibility_set|
    possibility_set.possibilities.none? do |poss|
      unwind_requirement_sets.any? do |requirements|
        possibility_satisfies_requirements?(poss, requirements)
      end
    end
  end
end

#filtered_possibility_set(vertex) ⇒ PossibilitySet (private)

Generates a filtered version of the existing vertex’s Resolution::PossibilitySet using the current state’s requirement

Parameters:

  • vertex (Object)

    existing vertex

Returns:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 708

def filtered_possibility_set(vertex)
  PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
end

#find_state_for(requirement) ⇒ ResolutionState (private)

Parameters:

  • requirement (Object)

Returns:

  • (ResolutionState)

    the state whose requirement is the given ‘requirement`.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 587

def find_state_for(requirement)
  return nil unless requirement
  states.find { |i| requirement == i.requirement }
end

#group_possibilities(possibilities) ⇒ Array<PossibilitySet> (private)

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.

Parameters:

  • possibilities (Array)

    an array of possibilities

Returns:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 804

def group_possibilities(possibilities)
  possibility_sets = []
  current_possibility_set = nil

  possibilities.reverse_each do |possibility|
    dependencies = dependencies_for(possibility)
    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]))
      current_possibility_set = possibility_sets.first
    end
  end

  possibility_sets
end

#handle_missing_or_push_dependency_state(state) ⇒ void (private)

This method returns an undefined value.

Pushes a new ::Gem::Molinillo::DependencyState. If the #specification_provider says to SpecificationProvider#allow_missing? that particular requirement, and there are no possibilities for that requirement, then #state is not pushed, and the vertex in #activated is removed, and we continue resolving the remaining requirements.

Parameters:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 829

def handle_missing_or_push_dependency_state(state)
  if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
    state.activated.detach_vertex_named(state.name)
    push_state_for_requirements(state.requirements.dup, false, state.activated)
  else
    states.push(state).tap { activated.tag(state) }
  end
end

#indicate_progressvoid (private)

This method returns an undefined value.

Indicates progress roughly once every second

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 643

def indicate_progress
  @iteration_counter += 1
  @progress_rate ||= resolver_ui.progress_rate
  if iteration_rate.nil?
    if Time.now - started_at >= @progress_rate
      self.iteration_rate = @iteration_counter
    end
  end

  if iteration_rate && (@iteration_counter % iteration_rate) == 0
    resolver_ui.indicate_progress
  end
end

#locked_requirement_named(requirement_name) ⇒ Object (private)

Parameters:

  • requirement_name (String)

    the spec name to search for

Returns:

  • (Object)

    the locked spec named requirement_name, if one is found on #base

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 715

def locked_requirement_named(requirement_name)
  vertex = base.vertex_named(requirement_name)
  vertex && vertex.payload
end

#locked_requirement_possibility_set(requirement, activated = self.activated) ⇒ Array (private)

Parameters:

  • requirement (Object)

    the proposed requirement

  • activated (Object) (defaults to: self.activated)

Returns:

  • (Array)

    possibility set containing only the locked requirement, if any

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 785

def locked_requirement_possibility_set(requirement, activated = self.activated)
  all_possibilities = search_for(requirement)
  locked_requirement = locked_requirement_named(name_for(requirement))

  # Longwinded way to build a possibilities array with either the locked
  # requirement or nothing in it. Required, since the API for
  # locked_requirement isn't guaranteed.
  locked_possibilities = all_possibilities.select do |possibility|
    requirement_satisfied_by?(locked_requirement, activated, possibility)
  end

  group_possibilities(locked_possibilities)
end

#parent_of(requirement) ⇒ Object (private)

Parameters:

  • requirement (Object)

Returns:

  • (Object)

    the requirement that led to requirement being added to the list of requirements.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 568

def parent_of(requirement)
  return unless requirement
  return unless index = @parents_of[requirement].last
  return unless parent_state = @states[index]
  parent_state.requirement
end

#possibilities_for_requirement(requirement, activated = self.activated) ⇒ Array (private)

Checks a proposed requirement with any existing locked requirement before generating an array of possibilities for it.

Parameters:

  • requirement (Object)

    the proposed requirement

  • activated (Object) (defaults to: self.activated)

Returns:

  • (Array)

    possibilities

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 773

def possibilities_for_requirement(requirement, activated = self.activated)
  return [] unless requirement
  if locked_requirement_named(name_for(requirement))
    return locked_requirement_possibility_set(requirement, activated)
  end

  group_possibilities(search_for(requirement))
end

#possibilityObject (private)

Returns:

  • (Object)

    the current possibility that the resolution is trying to activate

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 266

def possibility
  possibilities.last
end

#possibility_satisfies_requirements?(possibility, requirements) ⇒ Boolean (private)

Parameters:

  • possibility (Object)

    a single possibility

  • requirements (Array)

    an array of requirements

Returns:

  • (Boolean)

    whether the possibility satisfies all of the given requirements

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 475

def possibility_satisfies_requirements?(possibility, requirements)
  name = name_for(possibility)

  activated.tag(:swap)
  activated.set_payload(name, possibility) if activated.vertex_named(name)
  satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
  activated.rewind_to(:swap)

  satisfied
end

#process_topmost_statevoid (private)

This method returns an undefined value.

Processes the topmost available RequirementState on the stack

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 252

def process_topmost_state
  if possibility
    attempt_to_activate
  else
    create_conflict
    unwind_for_conflict
  end
rescue CircularDependencyError => underlying_error
  create_conflict(underlying_error)
  unwind_for_conflict
end

#push_initial_statevoid (private)

This method returns an undefined value.

Creates and pushes the initial state for the resolution, based upon the #requested dependencies

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 279

def push_initial_state
  graph = DependencyGraph.new.tap do |dg|
    original_requested.each do |requested|
      vertex = dg.add_vertex(name_for(requested), nil, true)
      vertex.explicit_requirements << requested
    end
    dg.tag(:initial_state)
  end

  push_state_for_requirements(original_requested, true, graph)
end

#push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated) ⇒ void (private)

This method returns an undefined value.

Pushes a new ::Gem::Molinillo::DependencyState that encapsulates both existing and new requirements

Parameters:

  • new_requirements (Array)
  • requires_sort (Boolean) (defaults to: true)
  • new_activated (Object) (defaults to: activated)
[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 753

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
  new_requirement = nil
  loop do
    new_requirement = new_requirements.shift
    break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
  end
  new_name = new_requirement ? name_for(new_requirement) : ''.freeze
  possibilities = possibilities_for_requirement(new_requirement)
  handle_missing_or_push_dependency_state DependencyState.new(
    new_name, new_requirements, new_activated,
    new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
  )
end

#raise_error_unless_state(conflicts) ⇒ void (private)

This method returns an undefined value.

Raises a ::Gem::Molinillo::VersionConflict error, or any underlying error, if there is no current state

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 313

def raise_error_unless_state(conflicts)
  return if state

  error = conflicts.values.map(&:underlying_error).compact.first
  raise error || VersionConflict.new(conflicts, specification_provider)
end

#require_nested_dependencies_for(possibility_set) ⇒ void (private)

This method returns an undefined value.

Requires the dependencies that the recently activated spec has

Parameters:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 734

def require_nested_dependencies_for(possibility_set)
  nested_dependencies = dependencies_for(possibility_set.latest_version)
  debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
  nested_dependencies.each do |d|
    activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
    parent_index = states.size - 1
    parents = @parents_of[d]
    parents << parent_index if parents.empty?
  end

  push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
end

#requirement_for_existing_name(name) ⇒ Object (private)

Parameters:

  • name (String)

Returns:

  • (Object)

    the requirement that led to a version of a possibility with the given name being activated.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 578

def requirement_for_existing_name(name)
  return nil unless vertex = activated.vertex_named(name)
  return nil unless vertex.payload
  states.find { |s| s.name == name }.requirement
end

#requirement_tree_for(requirement) ⇒ Array<Object> (private)

Parameters:

  • requirement (Object)

Returns:

  • (Array<Object>)

    the list of requirements that led to ‘requirement` being required.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 632

def requirement_tree_for(requirement)
  tree = []
  while requirement
    tree.unshift(requirement)
    requirement = parent_of(requirement)
  end
  tree
end

#requirement_treesArray<Array<Object>> (private)

Returns:

  • (Array<Array<Object>>)

    The different requirement trees that led to every requirement for the current spec.

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 624

def requirement_trees
  vertex = activated.vertex_named(name)
  vertex.requirements.map { |r| requirement_tree_for(r) }
end

#resolveDependencyGraph

Resolves the #original_requested dependencies into a full dependency

graph

Returns:

  • (DependencyGraph)

    the dependency graph of successfully resolved dependencies

Raises:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 167

def resolve
  start_resolution

  while state
    break if !state.requirement && state.requirements.empty?
    indicate_progress
    if state.respond_to?(:pop_possibility_state) # DependencyState
      debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
      state.pop_possibility_state.tap do |s|
        if s
          states.push(s)
          activated.tag(s)
        end
      end
    end
    process_topmost_state
  end

  resolve_activated_specs
ensure
  end_resolution
end

#resolve_activated_specs (private)

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 216

def resolve_activated_specs
  activated.vertices.each do |_, vertex|
    next unless vertex.payload

    latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
      vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
    end

    activated.set_payload(vertex.name, latest_version)
  end
  activated.freeze
end

#start_resolutionvoid (private)

This method returns an undefined value.

Sets up the resolution process

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 207

def start_resolution
  @started_at = Time.now

  push_initial_state

  debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
  resolver_ui.before_resolution
end

#stateRequirementState (private)

Returns:

  • (RequirementState)

    the current state the resolution is operating upon

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 272

def state
  states.last
end

#unwind_for_conflictvoid (private)

This method returns an undefined value.

Unwinds the states stack because a conflict has been encountered

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 293

def unwind_for_conflict
  details_for_unwind = build_details_for_unwind
  unwind_options = unused_unwind_options
  debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
  conflicts.tap do |c|
    sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
    raise_error_unless_state(c)
    activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
    state.conflicts = c
    state.unused_unwind_options = unwind_options
    filter_possibilities_after_unwind(details_for_unwind)
    index = states.size - 1
    @parents_of.each { |_, a| a.reject! { |i| i >= index } }
    state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
  end
end

#unwind_options_for_requirements(binding_requirements) ⇒ Array<UnwindDetails> (private)

Parameters:

  • binding_requirements (Array<Object>)

    array of requirements that combine to create a conflict

Returns:

[ GitHub ]

  
# File 'lib/rubygems/vendor/molinillo/lib/molinillo/resolution.rb', line 360

def unwind_options_for_requirements(binding_requirements)
  unwind_details = []

  trees = []
  binding_requirements.reverse_each do |r|
    partial_tree = [r]
    trees << partial_tree
    unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])

    # If this requirement has alternative possibilities, check if any would
    # satisfy the other requirements that created this conflict
    requirement_state = find_state_for(r)
    if conflict_fixing_possibilities?(requirement_state, binding_requirements)
      unwind_details << UnwindDetails.new(
        states.index(requirement_state),
        r,
        partial_tree,
        binding_requirements,
        trees,
        []
      )
    end

    # Next, look at the parent of this requirement, and check if the requirement
    # could have been avoided if an alternative PossibilitySet had been chosen
    parent_r = parent_of(r)
    next if parent_r.nil?
    partial_tree.unshift(parent_r)
    requirement_state = find_state_for(parent_r)
    if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
      unwind_details << UnwindDetails.new(
        states.index(requirement_state),
        parent_r,
        partial_tree,
        binding_requirements,
        trees,
        []
      )
    end

    # Finally, look at the grandparent and up of this requirement, looking
    # for any possibilities that wouldn't create their parent requirement
    grandparent_r = parent_of(parent_r)
    until grandparent_r.nil?
      partial_tree.unshift(grandparent_r)
      requirement_state = find_state_for(grandparent_r)
      if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
        unwind_details << UnwindDetails.new(
          states.index(requirement_state),
          grandparent_r,
          partial_tree,
          binding_requirements,
          trees,
          []
        )
      end
      parent_r = grandparent_r
      grandparent_r = parent_of(parent_r)
    end
  end

  unwind_details
end