class Gem::Resolver
Given a set of Gem::Dependency
objects as needed
and a way to query the set of available specs via set
, calculates a set of ActivationRequest
objects which indicate all the specs that should be activated to meet the all the requirements.
Constants
- DEBUG_RESOLVER
If the
DEBUG_RESOLVER
environment variable is set then debugging mode is enabled for the resolver. This will display information about the state of the resolver while a set of dependencies is being resolved.- SINGLE_POSSIBILITY_CONSTRAINT_PENALTY
Attributes
Set
to true if all development dependencies should be considered.
Set
to true if immediate development dependencies should be considered.
When true, no dependencies are looked up for requested gems.
List of dependencies that could not be found in the configured sources.
Hash
of gems to skip resolution. Keyed by gem name, with arrays of gem specifications as values.
When a missing dependency, don't stop. Just go on and record what was missing.
Public Class Methods
Combines sets
into a ComposedSet
that allows specification lookup in a uniform manner. If one of the sets
is itself a ComposedSet
its sets are flattened into the result ComposedSet
.
# File lib/rubygems/resolver.rb, line 61 def self.compose_sets(*sets) sets.compact! sets = sets.map do |set| case set when Gem::Resolver::BestSet then set when Gem::Resolver::ComposedSet then set.sets else set end end.flatten case sets.length when 0 then raise ArgumentError, 'one set in the composition must be non-nil' when 1 then sets.first else Gem::Resolver::ComposedSet.new(*sets) end end
Creates a Resolver
that queries only against the already installed gems for the needed
dependencies.
# File lib/rubygems/resolver.rb, line 89 def self.for_current_gems(needed) new needed, Gem::Resolver::CurrentSet.new end
Create Resolver
object which will resolve the tree starting with needed
Dependency objects.
set
is an object that provides where to look for specifications to satisfy the Dependencies. This defaults to IndexSet
, which will query rubygems.org.
# File lib/rubygems/resolver.rb, line 101 def initialize(needed, set = nil) @set = set || Gem::Resolver::IndexSet.new @needed = needed @development = false @development_shallow = false @ignore_dependencies = false @missing = [] @skip_gems = {} @soft_missing = false @stats = Gem::Resolver::Stats.new end
Public Instance Methods
# File lib/rubygems/resolver.rb, line 276 def allow_missing?(dependency) @missing << dependency @soft_missing end
# File lib/rubygems/resolver.rb, line 179 def debug? DEBUG_RESOLVER end
# File lib/rubygems/resolver.rb, line 257 def dependencies_for(specification) return [] if @ignore_dependencies spec = specification.spec requests(spec, specification) end
# File lib/rubygems/resolver.rb, line 272 def name_for(dependency) dependency.name end
# File lib/rubygems/resolver.rb, line 175 def output @output ||= debug? ? $stdout : File.open(IO::NULL, 'w') end
# File lib/rubygems/resolver.rb, line 263 def requirement_satisfied_by?(requirement, activated, spec) matches_spec = requirement.matches_spec? spec return matches_spec if @soft_missing matches_spec && spec.spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && spec.spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) end
Proceed with resolution! Returns an array of ActivationRequest
objects.
# File lib/rubygems/resolver.rb, line 188 def resolve locking_dg = Molinillo::DependencyGraph.new Molinillo::Resolver.new(self, self).resolve(@needed.map {|d| DependencyRequest.new d, nil }, locking_dg).tsort.map(&:payload).compact rescue Molinillo::VersionConflict => e conflict = e.conflicts.values.first raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement) ensure @output.close if defined?(@output) and !debug? end
# File lib/rubygems/resolver.rb, line 227 def search_for(dependency) possibles, all = find_possible(dependency) if !@soft_missing && possibles.empty? @missing << dependency exc = Gem::UnsatisfiableDependencyError.new dependency, all exc.errors = @set.errors raise exc end groups = Hash.new {|hash, key| hash[key] = [] } # create groups & sources in the same loop sources = possibles.map do |spec| source = spec.source groups[source] << spec source end.uniq.reverse activation_requests = [] sources.each do |source| groups[source]. sort_by {|spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }. map {|spec| ActivationRequest.new spec, dependency }. each {|activation_request| activation_requests << activation_request } end activation_requests end
# File lib/rubygems/resolver.rb, line 281 def sort_dependencies(dependencies, activated, conflicts) dependencies.sort_by.with_index do |dependency, i| name = name_for(dependency) [ activated.vertex_named(name).payload ? 0 : 1, amount_constrained(dependency), conflicts[name] ? 0 : 1, activated.vertex_named(name).payload ? 0 : search_for(dependency).count, i, # for stable sort ] end end
Private Instance Methods
returns an integer in (-infty, 0] a number closer to 0 means the dependency is less constraining
dependencies w/ 0 or 1 possibilities (ignoring version requirements) are given very negative values, so they always sort first, before dependencies that are unconstrained
# File lib/rubygems/resolver.rb, line 303 def amount_constrained(dependency) @amount_constrained ||= {} @amount_constrained[dependency.name] ||= begin name_dependency = Gem::Dependency.new(dependency.name) dependency_request_for_name = Gem::Resolver::DependencyRequest.new(name_dependency, dependency.requester) all = @set.find_all(dependency_request_for_name).size if all <= 1 all - SINGLE_POSSIBILITY_CONSTRAINT_PENALTY else search = search_for(dependency).size search - all end end end