Class: Bundler::Resolver
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Inherits: | Object |
| Defined in: | lib/bundler/resolver.rb, lib/bundler/resolver/base.rb, lib/bundler/resolver/candidate.rb, lib/bundler/resolver/incompatibility.rb, lib/bundler/resolver/package.rb, lib/bundler/resolver/root.rb, lib/bundler/resolver/spec_group.rb, lib/bundler/resolver/strategy.rb |
Overview
This class implements the interface needed by PubGrub for resolution. It is
equivalent to the PubGrub::BasicPackageSource class provided by PubGrub by
default and used by the most simple PubGrub consumers.
Constant Summary
-
METADATA_DEP_FIELD =
# File 'lib/bundler/resolver.rb', line 676{ "Ruby\0" => :required_ruby_version, "RubyGems\0" => :required_rubygems_version, }.freeze
Class Method Summary
Instance Attribute Summary
- #bundler_pinned_to_current_version? ⇒ Boolean readonly
- #cooldown_skipped readonly
- #debug? ⇒ Boolean readonly
Instance Method Summary
- #all_versions_for(package)
- #default_bundler_source
- #find_names_to_relax(incompatibility)
- #incompatibilities_for(package, version)
- #name_for_explicit_dependency_source
- #no_versions_incompatibility_for(package, unsatisfied_term)
- #override_diagnostic_summary
- #parse_dependency(package, dependency)
- #raise_incomplete!(incomplete_specs)
- #setup_solver
- #solve_versions(root:, logger:)
- #sort_versions_by_preferred(package, versions)
- #source_for(name)
- #start
- #versions_for(package, range = VersionRange.any)
- #apply_metadata_overrides(dependencies, name) private
- #apply_overrides(dependencies) private
- #base_requirements private
- #bundler_not_found_message(conflict_dependencies) private
- #cooldown_excluded?(spec) ⇒ Boolean private
- #cooldown_excluded_versions(specs) private
- #cooldown_hint(specs) private
- #cooldown_now private
-
#cooldown_skipped_summary(spec_set)
private
Reports, per gem, the newest version that cooldown kept out of a successful resolution.
- #filter_cooldown(specs) private
-
#filter_invalid_self_dependencies(specs, name)
private
Ignore versions that depend on themselves incorrectly.
- #filter_matching_specs(specs, requirements) private
- #filter_prereleases(specs, package) private
- #filter_remote_specs(specs, package) private
- #filter_specs(specs, package) private
- #filtered_versions_for(package) private
-
#locked_by_lockfile?(spec) ⇒ Boolean
private
A version already written to the lockfile has been adopted, and cooldown only governs the adoption of new versions, so it must never retract one the lockfile already pins.
- #other_specs_matching_message(specs, requirement) private
- #platform_mismatch_hint private
- #prepare_dependencies(requirements, packages) private
- #raise_all_versions_filtered_out!(package) private
- #raise_not_found!(package) private
- #remaining_cooldown_days(spec) private
- #repository_for(package) private
- #requirement_satisfied_by?(requirement, spec) ⇒ Boolean private
- #requirement_to_range(requirement) private
- #select_all_versions(package, range) private
- #to_dependency_hash(dependencies, packages) private
Constructor Details
.new(base, gem_version_promoter, most_specific_locked_platform = nil) ⇒ Resolver
# File 'lib/bundler/resolver.rb', line 19
def initialize(base, gem_version_promoter, most_specific_locked_platform = nil) @source_requirements = base.source_requirements @base = base @gem_version_promoter = gem_version_promoter @most_specific_locked_platform = most_specific_locked_platform @cooldown_skipped = [] @cooldown_skipped_specs = {} end
Instance Attribute Details
#bundler_pinned_to_current_version? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/bundler/resolver.rb', line 337
def bundler_pinned_to_current_version? !default_bundler_source.nil? end
#cooldown_skipped (readonly)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 17
attr_reader :cooldown_skipped
#debug? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/bundler/resolver.rb', line 225
def debug? ENV["BUNDLER_DEBUG_RESOLVER"] || ENV["BUNDLER_DEBUG_RESOLVER_TREE"] || ENV["DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER_TREE"] || false end
Instance Method Details
#all_versions_for(package)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 276
def all_versions_for(package) name = package.name results = (@base[name] + filter_specs(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] } if name == "bundler" && !bundler_pinned_to_current_version? bundler_spec = Gem.loaded_specs["bundler"] results << bundler_spec if bundler_spec end locked_requirement = base_requirements[name] results = filter_matching_specs(results, locked_requirement) if locked_requirement results.group_by(&:version).reduce([]) do |groups, (version, specs)| platform_specs = package.platform_specs(specs) # If package is a top-level dependency, # candidate is only valid if there are matching versions for all resolution platforms. # # If package is not a top-level deependency, # then it's not necessary that it has matching versions for all platforms, since it may have been introduced only as # a dependency for a platform specific variant, so it will only need to have a valid version for that platform. # if package.top_level? next groups if platform_specs.any?(&:empty?) else next groups if platform_specs.all?(&:empty?) end ruby_specs = MatchPlatform.select_best_platform_match(specs, Gem::Platform::RUBY) ruby_group = Resolver::SpecGroup.new(ruby_specs) unless ruby_group.empty? platform_specs.each do |s| ruby_group.merge(Resolver::SpecGroup.new(s)) end groups << Resolver::Candidate.new(version, group: ruby_group, priority: -1) next groups if package.force_ruby_platform? end platform_specs = platform_specs.flatten.uniq platform_group = Resolver::SpecGroup.new((platform_specs + ruby_specs).uniq) next groups if platform_group == ruby_group groups << Resolver::Candidate.new(version, group: platform_group, priority: 1) platform_only_group = Resolver::SpecGroup.new(platform_specs) groups << Resolver::Candidate.new(version, group: platform_only_group, priority: 0) unless platform_only_group == platform_group groups end end
#apply_metadata_overrides(dependencies, name) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 681
def (dependencies, name) return dependencies if @base.overrides.empty? dependencies.map do |dep| field = METADATA_DEP_FIELD[dep.name] next dep unless field override = Override.find_for(@base.overrides, name, field) next dep unless override Gem::Dependency.new(dep.name, override.apply_to(dep.requirement)) end end
#apply_overrides(dependencies) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 666
def apply_overrides(dependencies) return dependencies if @base.overrides.empty? dependencies.map do |dep| override = Override.find_for(@base.overrides, dep.name, :version) next dep unless override Gem::Dependency.new(dep.name, override.apply_to(dep.requirement)) end end
#base_requirements (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 575
def base_requirements @base.base_requirements end
#bundler_not_found_message(conflict_dependencies) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 693
def (conflict_dependencies) candidate_specs = filter_matching_specs(default_bundler_source.specs.search("bundler"), conflict_dependencies) if candidate_specs.any? target_version = candidate_specs.last.version new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ") "Your bundle requires a different version of Bundler than the one you're running.\n" \ "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n" else "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n" end end
#cooldown_excluded?(spec) ⇒ Boolean (private)
# File 'lib/bundler/resolver.rb', line 469
def cooldown_excluded?(spec) return false unless spec.respond_to?(:created_at) && spec.created_at return false unless spec.respond_to?(:remote) && spec.remote return false if locked_by_lockfile?(spec) days = spec.remote.effective_cooldown return false if days.nil? || days <= 0 (cooldown_now - spec.created_at) < (days * 86_400) end
#cooldown_excluded_versions(specs) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 453
def cooldown_excluded_versions(specs) excluded = {} specs.each do |spec| next unless cooldown_excluded?(spec) excluded[[spec.name, spec.version]] = true (@cooldown_skipped_specs ||= {})[[spec.name, spec.version]] ||= spec end excluded end
#cooldown_hint(specs) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 463
def cooldown_hint(specs) excluded_versions = cooldown_excluded_versions(specs) return nil if excluded_versions.empty? "#{excluded_versions.size} version#{"s" if excluded_versions.size > 1} excluded by the cooldown setting; pass `--cooldown 0` to bypass" end
#cooldown_now (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 497
def cooldown_now @cooldown_now ||= Time.now end
#cooldown_skipped_summary(spec_set) (private)
Reports, per gem, the newest version that cooldown kept out of a successful resolution. A skipped version is only worth reporting when it is newer than the resolved version and satisfies every requirement the final resolution places on that gem, so we don't claim a version the resolver could never have picked anyway.
# File 'lib/bundler/resolver.rb', line 506
def cooldown_skipped_summary(spec_set) return [] if @cooldown_skipped_specs.empty? requirements = Hash.new {|h, name| h[name] = [] } @requirements.each {|dep| requirements[dep.name] << dep.requirement } spec_set.each do |spec| spec.dependencies.each {|dep| requirements[dep.name] << dep.requirement } end resolved_versions = {} spec_set.each do |spec| version = resolved_versions[spec.name] resolved_versions[spec.name] = spec.version if version.nil? || spec.version > version end newest_skipped = {} @cooldown_skipped_specs.each do |(name, version), spec| resolved = resolved_versions[name] next unless resolved && version > resolved next unless requirements[name].all? {|req| req.satisfied_by?(version) } newest = newest_skipped[name] newest_skipped[name] = spec if newest.nil? || version > newest.version end newest_skipped.values.sort_by(&:name).map do |spec| { name: spec.name, version: spec.version, resolved: resolved_versions[spec.name], available_in_days: remaining_cooldown_days(spec), } end end
#default_bundler_source
[ GitHub ]# File 'lib/bundler/resolver.rb', line 333
def default_bundler_source @source_requirements[:default_bundler] end
#filter_cooldown(specs) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 446
def filter_cooldown(specs) return specs if specs.empty? excluded_versions = cooldown_excluded_versions(specs) return specs if excluded_versions.empty? specs.reject {|s| excluded_versions.include?([s.name, s.version]) } end
#filter_invalid_self_dependencies(specs, name) (private)
Ignore versions that depend on themselves incorrectly
# File 'lib/bundler/resolver.rb', line 561
def filter_invalid_self_dependencies(specs, name) specs.reject do |s| s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } end end
#filter_matching_specs(specs, requirements) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 430
def filter_matching_specs(specs, requirements) Array(requirements).flat_map do |requirement| specs.select {| spec| requirement_satisfied_by?(requirement, spec) } end end
#filter_prereleases(specs, package) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 440
def filter_prereleases(specs, package) return specs unless package.ignores_prereleases? && specs.size > 1 specs.reject {|s| s.version.prerelease? } end
#filter_remote_specs(specs, package) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 545
def filter_remote_specs(specs, package) if package.prefer_local? local_specs = specs.select {|s| s.is_a?(StubSpecification) } if local_specs.empty? package.consider_remote_versions! specs else local_specs end else specs end end
#filter_specs(specs, package) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 436
def filter_specs(specs, package) filter_remote_specs(filter_cooldown(filter_prereleases(specs, package)), package) end
#filtered_versions_for(package) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 415
def filtered_versions_for(package) @gem_version_promoter.filter_versions(package, @all_versions[package]) end
#find_names_to_relax(incompatibility)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 153
def find_names_to_relax(incompatibility) names_to_unlock = [] names_to_allow_prereleases_for = [] names_to_allow_remote_specs_for = [] extended_explanation = nil while incompatibility.conflict? cause = incompatibility.cause incompatibility = cause.incompatibility incompatibility.terms.each do |term| package = term.package name = package.name if base_requirements[name] names_to_unlock << name elsif package.ignores_prereleases? && @all_specs[name].any? {|s| s.version.prerelease? } names_to_allow_prereleases_for << name elsif package.prefer_local? && @all_specs[name].any? {|s| !s.is_a?(StubSpecification) } names_to_allow_remote_specs_for << name end no_versions_incompat = [cause.incompatibility, cause.satisfier].find {|incompat| incompat.cause.is_a?(Gem::PubGrub::Incompatibility::NoVersions) } next unless no_versions_incompat extended_explanation = no_versions_incompat.extended_explanation end end [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, names_to_allow_remote_specs_for.uniq, extended_explanation] end
#incompatibilities_for(package, version)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 233
def incompatibilities_for(package, version) package_deps = @cached_dependencies[package] sorted_versions = @sorted_versions[package] package_deps[version].map do |dep_package, dep_constraint| low = high = sorted_versions.index(version) # find version low such that all >= low share the same dep while low > 0 && package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint low -= 1 end low = if low == 0 nil else sorted_versions[low] end # find version high such that all < high share the same dep while high < sorted_versions.length && package_deps[sorted_versions[high]][dep_package] == dep_constraint high += 1 end high = if high == sorted_versions.length nil else sorted_versions[high] end range = Gem::PubGrub::VersionRange.new(min: low, max: high, include_min: !low.nil?) self_constraint = Gem::PubGrub::VersionConstraint.new(package, range: range) dep_term = Gem::PubGrub::Term.new(dep_constraint, false) self_term = Gem::PubGrub::Term.new(self_constraint, true) custom_explanation = if dep_package. && package.root? "current #{dep_package} version is #{dep_constraint.constraint_string}" end Gem::PubGrub::Incompatibility.new([self_term, dep_term], cause: :dependency, custom_explanation: custom_explanation) end end
#locked_by_lockfile?(spec) ⇒ Boolean (private)
A version already written to the lockfile has been adopted, and cooldown
only governs the adoption of new versions, so it must never retract one
the lockfile already pins. Keying this off the locked specs rather than the
prevent-downgrade floor matters because that floor is absent on resolutions
that re-pick a gem from scratch: the auxiliary full update run to compute
--update targets, and the from-scratch retries after a conflict unlocks a
gem. In those passes the locked version is the only candidate, so filtering
it out makes an unrelated operation impossible whenever every published
version matching the requirement sits inside the cooldown window.
Gems named on a bundle update GEM command are the exception: the user
asked to move them, so they stay subject to cooldown and a locked-but-fresh
release is pushed back to an older one (or fails loudly when none exists).
# File 'lib/bundler/resolver.rb', line 491
def locked_by_lockfile?(spec) return false unless defined?(@base) && @base return false if @base.explicitly_unlocked?(spec.name) @base.locked_specs[spec.name].any? {|locked| locked.version == spec.version } end
#name_for_explicit_dependency_source
[ GitHub ]# File 'lib/bundler/resolver.rb', line 341
def name_for_explicit_dependency_source Bundler.default_gemfile.basename.to_s rescue StandardError "Gemfile" end
#no_versions_incompatibility_for(package, unsatisfied_term)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 199
def no_versions_incompatibility_for(package, unsatisfied_term) cause = Gem::PubGrub::Incompatibility::NoVersions.new(unsatisfied_term) name = package.name constraint = unsatisfied_term.constraint constraint_string = constraint.constraint_string requirements = constraint_string.split(" OR ").map {|req| Gem::Requirement.new(req.split(",")) } if name == "bundler" && bundler_pinned_to_current_version? custom_explanation = "the current Bundler version (#{Bundler::VERSION}) does not satisfy #{constraint}" extended_explanation = (requirements) else specs_matching_other_platforms = filter_matching_specs(@all_specs[name], requirements) platforms_explanation = specs_matching_other_platforms.any? ? " for any resolution platforms (#{package.platforms.join(", ")})" : "" custom_explanation = "#{constraint} could not be found in #{repository_for(package)}#{platforms_explanation}" if hint = cooldown_hint(specs_matching_other_platforms) custom_explanation += " (#{hint})" end label = "#{name} (#{constraint_string})" extended_explanation = (specs_matching_other_platforms, label) if specs_matching_other_platforms.any? end Incompatibility.new([unsatisfied_term], cause: cause, custom_explanation: custom_explanation, extended_explanation: extended_explanation) end
#other_specs_matching_message(specs, requirement) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 613
def (specs, requirement) = String.new("The source contains the following gems matching '#{requirement}':\n") << specs.map {|s| " * #{s.full_name}" }.join("\n") end
#override_diagnostic_summary
[ GitHub ]# File 'lib/bundler/resolver.rb', line 140
def override_diagnostic_summary return nil if @base.overrides.empty? lines = ["Bundler applied the following overrides while resolving:"] @base.overrides.each do |override| target = override.target == :all ? ":all" : override.target.inspect location = override.source_location_label lines << " override #{target}, #{override.field}: #{override.operation.inspect}" \ "#{location ? " (declared at #{location})" : ""}" end "\n\n#{lines.join("\n")}" end
#parse_dependency(package, dependency)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 185
def parse_dependency(package, dependency) range = if repository_for(package).is_a?(Source::Gemspec) Gem::PubGrub::VersionRange.any else requirement_to_range(dependency) end Gem::PubGrub::VersionConstraint.new(package, range: range) end
#platform_mismatch_hint (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 401
def platform_mismatch_hint locked_platforms = Bundler.locked_gems&.platforms return unless locked_platforms local_platform = Bundler.local_platform return if locked_platforms.include?(local_platform) return if locked_platforms.any? {|p| p == Gem::Platform::RUBY } "Your current platform (#{local_platform}) is not included in the lockfile's platforms (#{locked_platforms.join(", ")}). " \ "Add the current platform to the lockfile with\n`bundle lock --add-platform #{local_platform}` and try again." rescue GemfileNotFound nil end
#prepare_dependencies(requirements, packages) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 579
def prepare_dependencies(requirements, packages) to_dependency_hash(requirements, packages).filter_map do |dep_package, dep_constraint| name = dep_package.name next [dep_package, dep_constraint] if name == "bundler" dep_range = dep_constraint.range versions = versions_for(dep_package, dep_range) if versions.empty? if dep_package.ignores_prereleases? || dep_package.prefer_local? @all_versions.delete(dep_package) @sorted_versions.delete(dep_package) end dep_package.consider_prereleases! if dep_package.ignores_prereleases? dep_package.consider_remote_versions! if dep_package.prefer_local? versions = versions_for(dep_package, dep_range) end if versions.empty? && select_all_versions(dep_package, dep_range).any? raise_all_versions_filtered_out!(dep_package) end next [dep_package, dep_constraint] unless versions.empty? next unless dep_package.current_platform? raise_not_found!(dep_package) end.to_h end
#raise_all_versions_filtered_out!(package) (private)
# File 'lib/bundler/resolver.rb', line 419
def raise_all_versions_filtered_out!(package) level = @gem_version_promoter.level name = package.name locked_version = package.locked_version requirement = package.dependency raise GemNotFound, "#{name} is locked to #{locked_version}, while Gemfile is requesting #{requirement}. " \ "--strict --#{level} was specified, but there are no #{level} level upgrades from #{locked_version} satisfying #{requirement}, so version solving has failed" end
#raise_incomplete!(incomplete_specs)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 347
def raise_incomplete!(incomplete_specs) raise_not_found!(@base.get_package(incomplete_specs.first.name)) end
#raise_not_found!(package) (private)
# File 'lib/bundler/resolver.rb', line 357
def raise_not_found!(package) name = package.name source = source_for(name) specs = @all_specs[name] matching_part = name requirement_label = SharedHelpers.pretty_dependency(package.dependency) = begin " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist? rescue GemfileNotFound nil end specs_matching_requirement = filter_matching_specs(specs, package.dependency.requirement) = if specs_matching_requirement.any? specs = specs_matching_requirement matching_part = requirement_label platforms = package.platforms if platforms.size == 1 "Could not find gem '#{requirement_label}' with platform '#{platforms.first}'" else "Could not find gems matching '#{requirement_label}' valid for all resolution platforms (#{platforms.join(", ")})" end else "Could not find gem '#{requirement_label}'" end = String.new("#{} in #{source}#{}.\n") if specs.any? << "\n#{(specs, matching_part)}" end if hint = cooldown_hint(specs_matching_requirement) << "\n\n#{hint}." end if specs_matching_requirement.any? && (hint = platform_mismatch_hint) << "\n\n#{hint}" end raise GemNotFound, end
#remaining_cooldown_days(spec) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 540
def remaining_cooldown_days(spec) remaining = (spec.remote.effective_cooldown * 86_400) - (cooldown_now - spec.created_at) [(remaining / 86_400.0).ceil, 1].max end
#repository_for(package) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 571
def repository_for(package) source_for(package.name) end
#requirement_satisfied_by?(requirement, spec) ⇒ Boolean (private)
#requirement_to_range(requirement) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 619
def requirement_to_range(requirement) ranges = requirement.requirements.map do |(op, version)| ver = Resolver::Candidate.new(version, priority: -1) platform_ver = Resolver::Candidate.new(version, priority: 1) case op when "~>" name = "~> #{ver}" bump = Resolver::Candidate.new(version.bump.to_s + ".A") Gem::PubGrub::VersionRange.new(name: name, min: ver, max: bump, include_min: true) when ">" Gem::PubGrub::VersionRange.new(min: platform_ver) when ">=" Gem::PubGrub::VersionRange.new(min: ver, include_min: true) when "<" Gem::PubGrub::VersionRange.new(max: ver) when "<=" Gem::PubGrub::VersionRange.new(max: platform_ver, include_max: true) when "=" Gem::PubGrub::VersionRange.new(min: ver, max: platform_ver, include_min: true, include_max: true) when "!=" Gem::PubGrub::VersionRange.new(min: ver, max: platform_ver, include_min: true, include_max: true).invert else raise "bad version specifier: #{op}" end end ranges.inject(&:intersect) end
#select_all_versions(package, range) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 609
def select_all_versions(package, range) range.select_versions(@all_versions[package]) end
#setup_solver
[ GitHub ]# File 'lib/bundler/resolver.rb', line 39
def setup_solver root = Resolver::Root.new(name_for_explicit_dependency_source) root_version = Resolver::Candidate.new(0) @cooldown_skipped_specs = {} @all_specs = Hash.new do |specs, name| source = source_for(name) matches = source.specs.search(name) # Don't bother to check for circular deps when no dependency API are # available, since it's too slow to be usable. That edge case won't work # but resolution other than that should work fine and reasonably fast. if source.respond_to?(:dependency_api_available?) && source.dependency_api_available? matches = filter_invalid_self_dependencies(matches, name) end specs[name] = matches.sort_by {|s| [s.version, s.platform.to_s] } end @all_versions = Hash.new do |candidates, package| candidates[package] = all_versions_for(package) end @sorted_versions = Hash.new do |candidates, package| candidates[package] = filtered_versions_for(package).sort end @sorted_versions[root] = [root_version] root_dependencies = prepare_dependencies(@requirements, @packages) @cached_dependencies = Hash.new do |dependencies, package| dependencies[package] = Hash.new do |versions, version| deps = version.dependencies.reject {|d| d.name == package.name } deps = (deps, package.name) versions[version] = to_dependency_hash(deps, @packages) end end @cached_dependencies[root] = { root_version => root_dependencies } logger = Bundler::UI::Shell.new logger.level = debug? ? "debug" : "warn" [root, logger] end
#solve_versions(root:, logger:)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 87
def solve_versions(root:, logger:) solver = Gem::PubGrub::VersionSolver.new(source: self, root: root, strategy: Strategy.new(self), logger: logger) result = solver.solve resolved_specs = result.flat_map {|package, version| version.to_specs(package, @most_specific_locked_platform) } Override.attach(resolved_specs, @base.overrides) spec_set = SpecSet.new(resolved_specs).specs_with_additional_variants_from(@base.locked_specs) @cooldown_skipped = cooldown_skipped_summary(spec_set) spec_set rescue Gem::PubGrub::SolveFailure => e incompatibility = e.incompatibility names_to_unlock, names_to_allow_prereleases_for, names_to_allow_remote_specs_for, extended_explanation = find_names_to_relax(incompatibility) names_to_relax = names_to_unlock + names_to_allow_prereleases_for + names_to_allow_remote_specs_for if names_to_relax.any? if names_to_unlock.any? Bundler.ui.debug "Found conflicts with locked dependencies. Will retry with #{names_to_unlock.join(", ")} unlocked...", true @base.unlock_names(names_to_unlock) end if names_to_allow_prereleases_for.any? Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retry considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true @base.include_prereleases(names_to_allow_prereleases_for) end if names_to_allow_remote_specs_for.any? Bundler.ui.debug "Found conflicts with local versions of #{names_to_allow_remote_specs_for.join(", ")}. Will retry considering remote versions...", true @base.include_remote_specs(names_to_allow_remote_specs_for) end root, logger = setup_solver Bundler.ui.debug "Retrying resolution...", true retry end explanation = e. if extended_explanation explanation << "\n\n" explanation << extended_explanation end override_summary = override_diagnostic_summary explanation << override_summary if override_summary raise SolveFailure.new(explanation) end
#sort_versions_by_preferred(package, versions)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 351
def sort_versions_by_preferred(package, versions) @gem_version_promoter.sort_versions(package, versions) end
#source_for(name)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 329
def source_for(name) @source_requirements[name] || @source_requirements[:default] end
#start
[ GitHub ]# File 'lib/bundler/resolver.rb', line 28
def start @requirements = @base.requirements @packages = @base.packages root, logger = setup_solver Bundler.ui.info "Resolving dependencies...", true solve_versions(root: root, logger: logger) end
#to_dependency_hash(dependencies, packages) (private)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 649
def to_dependency_hash(dependencies, packages) apply_overrides(dependencies).inject({}) do |deps, dep| package = packages[dep.name] current_req = deps[package] new_req = parse_dependency(package, dep.requirement) deps[package] = if current_req current_req.intersect(new_req) else new_req end deps end end
#versions_for(package, range = VersionRange.any)
[ GitHub ]# File 'lib/bundler/resolver.rb', line 195
def versions_for(package, range = VersionRange.any) range.select_versions(@sorted_versions[package]) end