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.
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.
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.
# File tmp/rubies/ruby-3.2.0/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
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 tmp/rubies/ruby-3.2.0/lib/rubygems/resolver.rb, line 89
def self.for_current_gems(needed)
new needed, Gem::Resolver::CurrentSet.new
end
Creates a Resolver
that queries only against the already installed gems for the needed
dependencies.
# File tmp/rubies/ruby-3.2.0/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
# File tmp/rubies/ruby-3.2.0/lib/rubygems/resolver.rb, line 276
def allow_missing?(dependency)
@missing << dependency
@soft_missing
end
# File tmp/rubies/ruby-3.2.0/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
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 tmp/rubies/ruby-3.2.0/lib/rubygems/resolver.rb, line 179
def debug?
DEBUG_RESOLVER
end
# File tmp/rubies/ruby-3.2.0/lib/rubygems/resolver.rb, line 257
def dependencies_for(specification)
return [] if @ignore_dependencies
spec = specification.spec
requests(spec, specification)
end
# File tmp/rubies/ruby-3.2.0/lib/rubygems/resolver.rb, line 272
def name_for(dependency)
dependency.name
end
# File tmp/rubies/ruby-3.2.0/lib/rubygems/resolver.rb, line 175
def output
@output ||= debug? ? $stdout : File.open(IO::NULL, "w")
end
# File tmp/rubies/ruby-3.2.0/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
# File tmp/rubies/ruby-3.2.0/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) && !debug?
end
Proceed with resolution! Returns an array of ActivationRequest
objects.
# File tmp/rubies/ruby-3.2.0/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, spec.platform =~ Gem::Platform.local ? 1 : 0] }.
map {|spec| ActivationRequest.new spec, dependency }.
each {|activation_request| activation_requests << activation_request }
end
activation_requests
end
# File tmp/rubies/ruby-3.2.0/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