123456789_123456789_123456789_123456789_123456789_

Class: Bundler::Definition

Relationships & Source Files
Inherits: Object
Defined in: lib/bundler/definition.rb

Class Attribute Summary

  • .no_lock rw

    Do not create or modify a lockfile (Makes #lock a noop).

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [], overrides = []) ⇒ Definition

How does the new system work?

  • Load information from Gemfile and Lockfile
  • Invalidate stale locked specs
  • All specs from stale source are stale
  • All specs that are reachable only through a stale dependency are stale.
  • If all fresh dependencies are satisfied by the locked specs, then we can try to resolve locally.

Parameters:

  • lockfile (Pathname)

    Path to Gemfile.lock

  • dependencies (Array(Bundler::Dependency))

    array of dependencies from Gemfile

  • sources (Bundler::SourceList)
  • unlock (Hash, Boolean, nil)

    Gems that have been requested to be updated or true if all gems should be updated

  • ruby_version (Bundler::RubyVersion, nil) (defaults to: nil)

    Requested Ruby Version

  • optional_groups (Array(String)) (defaults to: [])

    A list of optional groups

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 65

def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [], overrides = [])
  unlock ||= {}

  if unlock == true
    @unlocking_all = true
    strict = false
    @unlocking_bundler = false
    @unlocking = unlock
    @sources_to_unlock = []
    @unlocking_ruby = false
    @explicit_unlocks = []
    conservative = false
  else
    @unlocking_all = false
    strict = unlock.delete(:strict)
    @unlocking_bundler = unlock.delete(:bundler)
    @unlocking = unlock.any? {|_k, v| !Array(v).empty? }
    @sources_to_unlock = unlock.delete(:sources) || []
    @unlocking_ruby = unlock.delete(:ruby)
    @explicit_unlocks = unlock.delete(:gems) || []
    conservative = unlock.delete(:conservative)
  end

  @dependencies    = dependencies
  @sources         = sources
  @optional_groups = optional_groups
  @prefer_local    = false
  @specs           = nil
  @ruby_version    = ruby_version
  @gemfiles        = gemfiles
  @overrides       = overrides

  @lockfile               = lockfile
  @lockfile_contents      = String.new

  @locked_bundler_version = nil
  @resolved_bundler_version = nil

  @locked_ruby_version = nil
  @new_platforms = []
  @removed_platforms = []
  @originally_invalid_platforms = []

  if lockfile_exists?
    @lockfile_contents = Bundler.read_file(lockfile)
    @locked_gems = LockfileParser.new(@lockfile_contents, strict: strict)
    @locked_platforms = @locked_gems.platforms
    @most_specific_locked_platform = @locked_gems.most_specific_locked_platform
    @platforms = @locked_platforms.dup
    @locked_bundler_version = @locked_gems.bundler_version
    @locked_ruby_version = @locked_gems.ruby_version
    @locked_deps = @locked_gems.dependencies
    Override.attach(@locked_gems.specs, @overrides)
    @originally_locked_specs = SpecSet.new(@locked_gems.specs)
    @originally_locked_sources = @locked_gems.sources
    @locked_checksums = @locked_gems.checksums

    if @unlocking_all
      @locked_specs   = SpecSet.new([])
      @locked_sources = []
    else
      @locked_specs   = @originally_locked_specs
      @locked_sources = @originally_locked_sources
    end

    locked_gem_sources = @originally_locked_sources.select {|s| s.is_a?(Source::Rubygems) }
    multisource_lockfile = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes?

    if multisource_lockfile
      msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."

      Bundler::SharedHelpers.feature_removed! msg
    end
  else
    @locked_gems = nil
    @locked_platforms = []
    @most_specific_locked_platform = nil
    @platforms      = []
    @locked_deps    = {}
    @locked_specs   = SpecSet.new([])
    @locked_sources = []
    @originally_locked_specs = @locked_specs
    @originally_locked_sources = @locked_sources
    @locked_checksums = Bundler.settings[:lockfile_checksums]
  end

  @unlocking_ruby ||= if @ruby_version && locked_ruby_version_object
    @ruby_version.diff(locked_ruby_version_object)
  end
  @unlocking ||= @unlocking_ruby ||= (!@locked_ruby_version ^ !@ruby_version)

  @current_platform_missing = add_current_platform unless Bundler.frozen_bundle?

  @source_changes = converge_sources
  @path_changes = converge_paths

  if conservative
    @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
  else
    eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") }
    @gems_to_unlock = @locked_specs.for(eager_unlock, platforms).map(&:name).uniq
  end

  @dependency_changes = converge_dependencies
  @local_changes = converge_locals

  check_lockfile
end

Class Attribute Details

.no_lock (rw)

Do not create or modify a lockfile (Makes #lock a noop)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 10

attr_accessor :no_lock

Class Method Details

.build(gemfile, lockfile, unlock) ⇒ Definition

Given a gemfile and lockfile creates a ::Bundler definition

Parameters:

  • gemfile (Pathname)

    Path to Gemfile

  • lockfile (Pathname, nil)

    Path to Gemfile.lock

  • unlock (Hash, Boolean, nil)

    Gems that have been requested to be updated or true if all gems should be updated

Raises:

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 35

def self.build(gemfile, lockfile, unlock)
  unlock ||= {}
  gemfile = Pathname.new(gemfile).expand_path

  raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file?

  Plugin.hook(Plugin::Events::GEM_BEFORE_EVAL, gemfile, lockfile)
  Dsl.evaluate(gemfile, lockfile, unlock).tap do |definition|
    Plugin.hook(Plugin::Events::GEM_AFTER_EVAL, definition)
  end
end

Instance Attribute Details

#current_platform_locked?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 831

def current_platform_locked?
  @platforms.any? do |bundle_platform|
    Bundler.generic_local_platform == bundle_platform || Bundler.local_platform === bundle_platform
  end
end

#dependencies (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#gemfiles (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#install_needed?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 614

def install_needed?
  resolve_needed? || missing_specs?
end

#locked_checksums (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#locked_deps (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#locked_gems (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#lockfile (rw)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#lockfile=(value) (rw)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 13

attr_writer :lockfile, :overrides

#lockfile_exists?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 643

def lockfile_exists?
  lockfile && File.exist?(lockfile)
end

#missing_specs (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 267

def missing_specs
  preload_git_sources
  resolve.missing_specs_for(requested_dependencies)
end

#missing_specs?Boolean (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 272

def missing_specs?
  missing = missing_specs
  return false if missing.empty?
  Bundler.ui.debug "The definition is missing #{missing.map(&:full_name)}"
  true
rescue BundlerError => e
  @resolve = nil
  @resolver = nil
  @resolution_base = nil
  @source_requirements = nil
  @specs = nil

  Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})"
  true
end

#no_resolve_needed?Boolean (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 544

def no_resolve_needed?
  !resolve_needed?
end

#nothing_changed?Boolean (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 540

def nothing_changed?
  !something_changed?
end

#overrides (rw)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#overrides=(value) (rw)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 13

attr_writer :lockfile, :overrides

#platforms (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#precompute_source_requirements_for_indirect_dependencies?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 809

def precompute_source_requirements_for_indirect_dependencies?
  if sources.non_global_rubygems_sources.all?(&:dependency_api_available?)
    true
  else
    non_dependency_api_warning
    false
  end
end

#resolve_needed?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 635

def resolve_needed?
  unlocking? || something_changed?
end

#ruby_version (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#should_add_extra_platforms?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 639

def should_add_extra_platforms?
  !lockfile_exists? && Bundler::MatchPlatform.generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
end

#something_changed?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 618

def something_changed?
  return true unless lockfile_exists?

  @source_changes ||
    @dependency_changes ||
    @current_platform_missing ||
    @new_platforms.any? ||
    @path_changes ||
    @local_changes ||
    @missing_lockfile_dep ||
    @unlocking_bundler ||
    @locked_spec_with_missing_checksums ||
    @locked_spec_with_empty_checksums ||
    @locked_spec_with_missing_deps ||
    @locked_spec_with_invalid_deps
end

#sources (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 15

attr_reader(
  :dependencies,
  :locked_checksums,
  :locked_deps,
  :locked_gems,
  :overrides,
  :platforms,
  :ruby_version,
  :lockfile,
  :gemfiles,
  :sources
)

#unlocking?Boolean (readonly)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 548

def unlocking?
  @unlocking
end

Instance Method Details

#add_checksums

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 552

def add_checksums
  require "rubygems/package"

  @locked_checksums = true

  setup_domain!(add_checksums: true)

  # force materialization to real specifications, so that checksums are fetched
  specs.each do |spec|
    next unless spec.source.is_a?(Bundler::Source::Rubygems)
    # Checksum was fetched from the compact index API.
    next if !spec.source.checksum_store.missing?(spec) && !spec.source.checksum_store.empty?(spec)
    # The gem isn't installed, can't compute the checksum.
    next unless spec.loaded_from

    package = Gem::Package.new(spec.source.cached_built_in_gem(spec))
    checksum = Checksum.from_gem_package(package)
    spec.source.checksum_store.register(spec, checksum)
  end
end

#add_current_platform (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 837

def add_current_platform
  return if @platforms.include?(Bundler.local_platform)

  @most_specific_non_local_locked_platform = find_most_specific_locked_platform
  return if @most_specific_non_local_locked_platform

  @platforms << Bundler.local_platform
  true
end

#add_platform(platform)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 526

def add_platform(platform)
  return if @platforms.include?(platform)

  @new_platforms << platform
  @platforms << platform
end

#additional_base_requirements_to_force_updates(resolution_base) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1294

def additional_base_requirements_to_force_updates(resolution_base)
  return resolution_base if @explicit_unlocks.empty?
  full_update = SpecSet.new(new_resolver_for_full_update.start)
  @explicit_unlocks.each do |name|
    version = full_update.version_for(name)
    resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
  end
  resolution_base
end

#additional_base_requirements_to_prevent_downgrades(resolution_base) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1281

def additional_base_requirements_to_prevent_downgrades(resolution_base)
  return resolution_base unless @locked_gems
  @originally_locked_specs.each do |locked_spec|
    next if locked_spec.source.is_a?(Source::Path) || locked_spec.source_changed?

    name = locked_spec.name
    next if @changed_dependencies.include?(name)

    resolution_base.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
  end
  resolution_base
end

#apply_override_to(dep) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 660

def apply_override_to(dep)
  override = Override.find_for(@overrides, dep.name, :version)
  return dep unless override
  new_dep = dep.dup
  new_dep.instance_variable_set(:@requirement, override.apply_to(dep.requirement))
  new_dep
end

#apply_overrides_to(deps) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 655

def apply_overrides_to(deps)
  return deps if @overrides.empty?
  deps.map {|dep| apply_override_to(dep) }
end

#bundler_version_to_lock

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 460

def bundler_version_to_lock
  @resolved_bundler_version || Bundler.gem_version
end

#change_reason (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 865

def change_reason
  if resolve_needed?
    if unlocking?
      unlocking_reason
    else
      lockfile_changed_reason
    end
  else
    "some dependencies were deleted from your gemfile"
  end
end

#check!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 178

def check!
  # If dependencies have changed, we need to resolve remotely. Otherwise,
  # since we'll be resolving with a single local source, we may end up
  # locking gems under the wrong source in the lockfile, and missing lockfile
  # checksums
  resolve_remotely! if @dependency_changes

  # Now do a local only resolve, to verify if any gems are missing locally
  sources.local_only!
  resolve
end

#check_lockfile (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 960

def check_lockfile
  @locked_spec_with_invalid_deps = nil
  @locked_spec_with_missing_deps = nil
  @locked_spec_with_missing_checksums = nil
  @locked_spec_with_empty_checksums = nil

  missing_deps = []
  missing_checksums = []
  empty_checksums = []
  invalid = []

  @locked_specs.each do |s|
    if @locked_checksums
      checksum_store = s.source.checksum_store

      if checksum_store.missing?(s)
        missing_checksums << s
      elsif checksum_store.empty?(s)
        empty_checksums << s
      end
    end

    validation = @locked_specs.validate_deps(s)

    missing_deps << s if validation == :missing
    invalid << s if validation == :invalid
  end

  @locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any?
  @locked_spec_with_empty_checksums = empty_checksums.first.name if empty_checksums.any?

  if missing_deps.any?
    @locked_specs.delete(missing_deps)

    @locked_spec_with_missing_deps = missing_deps.first.name
  end

  if invalid.any?
    @locked_specs.delete(invalid)

    @locked_spec_with_invalid_deps = invalid.first.name
  end
end

#converge_dependencies (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1038

def converge_dependencies
  @missing_lockfile_dep = nil
  @changed_dependencies = []

  @dependencies.each do |dep|
    if dep.source
      dep.source = sources.get(dep.source)
    end
    next unless relevant_deps?(dep)

    name = dep.name

    dep_changed = @locked_deps[name].nil?

    unless name == "bundler"
      locked_specs = @originally_locked_specs[name]

      if locked_specs.empty?
        @missing_lockfile_dep = name if dep_changed == false
      else
        if locked_specs.map(&:source).uniq.size > 1
          @locked_specs.delete(locked_specs.select {|s| s.source != dep.source })
        end

        unless apply_override_to(dep).matches_spec?(locked_specs.first)
          @gems_to_unlock << name
          dep_changed = true
        end
      end
    end

    @changed_dependencies << name if dep_changed
  end

  converge_overrides_outside_dependencies

  @changed_dependencies.any?
end

#converge_locals (private)

Get all locals and override their matching sources. Return true if any of the locals changed (for example, they point to a new revision) or depend on new specs.

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 942

def converge_locals
  locals = []

  Bundler.settings.local_overrides.map do |k, v|
    spec   = @dependencies.find {|s| s.name == k }
    source = spec&.source
    if source&.respond_to?(:local_override!)
      source.unlock! if @gems_to_unlock.include?(spec.name)
      locals << [source, source.local_override!(v)]
    end
  end

  sources_with_changes = locals.select do |source, changed|
    changed || specs_changed?(source)
  end.map(&:first)
  !sources_with_changes.each {|source| @sources_to_unlock << source.name }.empty?
end

#converge_locked_specs (private)

Remove elements from the locked specs that are expired. This will most commonly happen if the Gemfile has changed since the lockfile was last generated

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1101

def converge_locked_specs
  converged = converge_specs(@locked_specs)

  resolve = SpecSet.new(converged)

  diff = nil

  # Now, we unlock any sources that do not have anymore gems pinned to it
  sources.all_sources.each do |source|
    next unless source.respond_to?(:unlock!)

    unless resolve.any? {|s| s.source == source }
      diff ||= @locked_specs.to_a - resolve.to_a
      source.unlock! if diff.any? {|s| s.source == source }
    end
  end

  resolve
end

#converge_overrides_outside_dependencies (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1077

def converge_overrides_outside_dependencies
  @overrides.each do |override|
    # :all overrides are intentionally not pre-unlocked. They take effect on
    # fresh resolution (no lockfile) or when the user runs `bundle update`.
    # Forcing a full re-resolve from a single :all directive would surprise
    # users with unrelated dependency churn.
    next unless override.target.is_a?(String)

    name = override.target
    next if @changed_dependencies.include?(name)
    next if @originally_locked_specs[name].empty?
    # version: overrides on direct deps are detected in the per-dep
    # converge_dependencies loop via apply_override_to + matches_spec?.
    # Other fields are not visible there, so they always reach here.
    next if override.field == :version && @dependencies.any? {|d| d.name == name }

    @gems_to_unlock << name
    @changed_dependencies << name
  end
end

#converge_paths (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1004

def converge_paths
  sources.path_sources.any? do |source|
    specs_changed?(source)
  end
end

#converge_sources (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1010

def converge_sources
  # Replace the sources from the Gemfile with the sources from the Gemfile.lock,
  # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
  # source in the Gemfile.lock, use the one from the Gemfile.
  changes = sources.replace_sources!(@locked_sources)

  sources.all_sources.each do |source|
    # has to be done separately, because we want to keep the locked checksum
    # store for a source, even when doing a full update
    if @locked_checksums && @locked_gems && locked_source = @originally_locked_sources.find {|s| s == source && !s.equal?(source) }
      source.checksum_store.merge!(locked_source.checksum_store)
    end
    # If the source is unlockable and the current command allows an unlock of
    # the source (for example, you are doing a `bundle update <foo>` of a git-pinned
    # gem), unlock it. For git sources, this means to unlock the revision, which
    # will cause the `ref` used to be the most recent for the branch (or master) if
    # an explicit `ref` is not used.
    if source.respond_to?(:unlock!) && @sources_to_unlock.include?(source.name)
      source.unlock!
      changes = true
    end
  end

  sources..checksum_store.merge!(@locked_gems..checksum_store) if @locked_gems

  changes
end

#converge_specs(specs) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1121

def converge_specs(specs)
  converged = []
  deps = []

  specs.each do |s|
    name = s.name
    next if @gems_to_unlock.include?(name)

    dep = @dependencies.find {|d| s.satisfies?(d) }
    lockfile_source = s.source

    if dep
      replacement_source = dep.source

      deps << dep if !replacement_source || lockfile_source.include?(replacement_source) || new_deps.include?(dep)
    else
      parent_dep = @dependencies.find do |d|
        next unless d.source && d.source != lockfile_source
        next if d.source.is_a?(Source::Gemspec)

        parent_locked_specs = @originally_locked_specs[d.name]

        parent_locked_specs.any? do |parent_spec|
          parent_spec.runtime_dependencies.any? {|rd| rd.name == s.name }
        end
      end

      if parent_dep && parent_dep.source.is_a?(Source::Path) && parent_dep.source.specs[s]&.any?
        replacement_source = parent_dep.source
      else
        replacement_source = sources.get(lockfile_source)
      end
    end

    # Replace the locked dependency's source with the equivalent source from the Gemfile
    s.source = replacement_source || default_source
    next if s.source_changed?

    source = s.source
    next if @sources_to_unlock.include?(source.name)

    # Path sources have special logic
    if source.is_a?(Source::Path)
      new_spec = source.specs[s].first
      if new_spec
        s.runtime_dependencies.replace(new_spec.runtime_dependencies)
      else
        # If the spec is no longer in the path source, unlock it. This
        # commonly happens if the version changed in the gemspec
        @gems_to_unlock << name
      end
    end

    converged << s
  end

  filter_specs(converged, deps, skips: @gems_to_unlock)
end

#current_dependencies

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 296

def current_dependencies
  filter_relevant(dependencies)
end

#current_locked_dependencies

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 300

def current_locked_dependencies
  filter_relevant(locked_dependencies)
end

#default_source (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1257

def default_source
  sources.default_source
end

#deleted_deps

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 324

def deleted_deps
  @deleted_deps ||= locked_dependencies - @dependencies
end

#dependencies_for(groups)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 334

def dependencies_for(groups)
  groups.map!(&:to_sym)
  deps = current_dependencies # always returns a new array
  deps.select! do |d|
    d.groups.intersect?(groups)
  end
  deps
end

#dependencies_for_source_changed?(source, locked_source) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 922

def dependencies_for_source_changed?(source, locked_source)
  deps_for_source = @dependencies.select {|dep| dep.source == source }
  locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }

  deps_for_source.uniq.sort != locked_deps_for_source.sort
end

#dependencies_with_bundler (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 668

def dependencies_with_bundler
  return dependencies unless @unlocking_bundler
  return dependencies if dependencies.any? {|d| d.name == "bundler" }

  [Dependency.new("bundler", @unlocking_bundler)] + dependencies
end

#ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)

Raises:

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 469

def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
  return unless Bundler.frozen_bundle?

  raise ProductionError, "Frozen mode is set, but there's no lockfile" unless lockfile_exists?

  msg = lockfile_changes_summary("frozen mode is set")
  return unless msg

  unless explicit_flag
    suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
      "bundle config set frozen false"
    end
    msg << "\n\nIf this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \
           "freeze by running `#{suggested_command}`." if suggested_command
  end

  raise ProductionError, msg
end

#excluded_git_sources (private)

Git sources that should be excluded (only used by --without groups)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1220

def excluded_git_sources
  sources.git_sources - needed_git_sources
end

#expanded_dependencies (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 651

def expanded_dependencies
  apply_overrides_to(dependencies_with_bundler) + 
end

#filter_relevant(dependencies)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 304

def filter_relevant(dependencies)
  dependencies.select do |d|
    relevant_deps?(d)
  end
end

#filter_specs(specs, deps, skips: []) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 686

def filter_specs(specs, deps, skips: [])
  SpecSet.new(specs).for(deps, platforms, skips: skips)
end

#find_most_specific_locked_platform (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 847

def find_most_specific_locked_platform
  return unless current_platform_locked?

  @most_specific_locked_platform
end

#find_source_requirements (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1224

def find_source_requirements
  preload_git_sources

  # Only safe to exclude when locked_requirements (merged below) backfills the gap.
  nothing_changed = nothing_changed?
  excluded = nothing_changed ? excluded_git_sources : []

  # Record the specs available in each gem's source, so that those
  # specs will be available later when the resolver knows where to
  # look for that gemspec (or its dependencies)
  source_requirements = if precompute_source_requirements_for_indirect_dependencies?
    all_requirements = source_map.all_requirements(excluded)
    { default: default_source }.merge(all_requirements)
  else
    { default: Source::RubygemsAggregate.new(sources, source_map, excluded) }.merge(source_map.direct_requirements)
  end
  source_requirements.merge!(source_map.locked_requirements) if nothing_changed
  .each do |dep|
    source_requirements[dep.name] = sources.
  end

  default_bundler_source = source_requirements["bundler"] || default_source

  if @unlocking_bundler
    default_bundler_source.add_dependency_names("bundler")
  else
    source_requirements[:default_bundler] = default_bundler_source
    source_requirements["bundler"] = sources. # needs to come last to override
  end

  source_requirements
end

#gem_version_promoter

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 174

def gem_version_promoter
  @gem_version_promoter ||= GemVersionPromoter.new
end

#groups

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 375

def groups
  dependencies.flat_map(&:groups).uniq
end

#lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 379

def lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)
  if [true, false, nil].include?(file_or_preserve_unknown_sections)
    target_lockfile = lockfile
    preserve_unknown_sections = file_or_preserve_unknown_sections
  else
    target_lockfile = file_or_preserve_unknown_sections
    preserve_unknown_sections = preserve_unknown_sections_or_unused

    suggestion = if target_lockfile == lockfile
      "To fix this warning, remove it from the `Definition#lock` call."
    else
      "Instead, instantiate a new definition passing `#{target_lockfile}`, and call `lock` without a file argument on that definition"
    end

    msg = "`Definition#lock` was passed a target file argument. #{suggestion}"

    Bundler::SharedHelpers.feature_removed! msg
  end

  write_lock(target_lockfile, preserve_unknown_sections)
end

#locked_dependencies

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 316

def locked_dependencies
  @locked_deps.values
end

#locked_ruby_version

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 439

def locked_ruby_version
  return unless ruby_version
  if @unlocking_ruby || !@locked_ruby_version
    Bundler::RubyVersion.system
  else
    @locked_ruby_version
  end
end

#locked_ruby_version_object

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 448

def locked_ruby_version_object
  return unless @locked_ruby_version
  @locked_ruby_version_object ||= begin
    unless version = RubyVersion.from_string(@locked_ruby_version)
      raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \
        "#{@lockfile} could not be parsed. " \
        "Try running bundle update --ruby to resolve this."
    end
    version
  end
end

#lockfile_changed_reason (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 893

def lockfile_changed_reason
  [
    [@source_changes, "the list of sources changed"],
    [@dependency_changes, "the dependencies in your gemfile changed"],
    [@current_platform_missing, "your lockfile is missing the current platform"],
    [@new_platforms.any?, "you are adding a new platform to your lockfile"],
    [@path_changes, "the gemspecs for path gems changed"],
    [@local_changes, "the gemspecs for git local gems changed"],
    [@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""],
    [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
    [@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""],
    [@locked_spec_with_empty_checksums, "your lockfile has an empty CHECKSUMS entry for \"#{@locked_spec_with_empty_checksums}\""],
    [@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
    [@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
  ].select(&:first).map(&:last).join(", ")
end

#lockfile_changes_summary(update_refused_reason) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 575

def lockfile_changes_summary(update_refused_reason)
  added =   []
  deleted = []
  changed = []

  added.concat @new_platforms.map {|p| "* platform: #{p}" }
  deleted.concat @removed_platforms.map {|p| "* platform: #{p}" }

  added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
  deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?

  both_sources = Hash.new {|h, k| h[k] = [] }
  current_dependencies.each {|d| both_sources[d.name][0] = d }
  current_locked_dependencies.each {|d| both_sources[d.name][1] = d }

  both_sources.each do |name, (dep, lock_dep)|
    next if dep.nil? || lock_dep.nil?

    gemfile_source = dep.source || default_source
    lock_source = lock_dep.source || default_source
    next if lock_source.include?(gemfile_source)

    gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
    lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
    changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
  end

  return unless added.any? || deleted.any? || changed.any? || resolve_needed?

  msg = String.new("#{change_reason[0].upcase}#{change_reason[1..-1].strip}, but ")
  msg << "the lockfile " unless msg.start_with?("Your lockfile")
  msg << "can't be updated because #{update_refused_reason}"
  msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
  msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
  msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
  msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control.\n" unless unlocking?
  msg
end

#lockfiles_equal?(current, proposed, preserve_unknown_sections) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1267

def lockfiles_equal?(current, proposed, preserve_unknown_sections)
  if preserve_unknown_sections
    sections_to_ignore = LockfileParser.sections_to_ignore(@locked_bundler_version)
    sections_to_ignore += LockfileParser.unknown_sections_in_lockfile(current)
    sections_to_ignore << LockfileParser::RUBY
    sections_to_ignore << LockfileParser::BUNDLED unless @unlocking_bundler
    pattern = /#{Regexp.union(sections_to_ignore)}\n(\s{2,}.*\n)+/
    whitespace_cleanup = /\n{2,}/
    current = current.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
    proposed = proposed.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
  end
  current == proposed
end

#materialize(dependencies) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 690

def materialize(dependencies)
  specs = begin
    resolve.materialize(dependencies)
  rescue IncorrectLockfileDependencies => e
    raise if Bundler.frozen_bundle?

    reresolve_without([e.spec])
    retry
  end

  missing_specs = resolve.missing_specs

  if missing_specs.any?
    missing_specs.each do |s|
      locked_gem = @locked_specs[s.name].last
      next if locked_gem.nil? || locked_gem.version != s.version || sources.local_mode?

      message = if sources.implicit_global_source?
        "Because your Gemfile specifies no global remote source, your bundle is locked to " \
        "#{locked_gem} from #{locked_gem.source}. However, #{locked_gem} is not installed. You'll " \
        "need to either add a global remote source to your Gemfile or make sure #{locked_gem} is " \
        "available locally before rerunning Bundler."
      else
        "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
        "no longer be found in that source. That means either the author of #{locked_gem} has removed it, " \
        "or you no longer have access to that source. You'll need to update your bundle to a version other " \
        "than #{locked_gem} that hasn't been removed, or check your credentials and access rights for " \
        "#{locked_gem.source}, in order to install."
      end

      raise GemNotFound, message
    end

    missing_specs_list = missing_specs.group_by(&:source).map do |source, missing_specs_for_source|
      "#{missing_specs_for_source.map(&:full_name).join(", ")} in #{source}"
    end

    raise GemNotFound, "Could not find #{missing_specs_list.join(" nor ")}"
  end

  partially_missing_specs = resolve.partially_missing_specs

  if partially_missing_specs.any? && !sources.local_mode?
    Bundler.ui.warn "Some locked specs have possibly been yanked (#{partially_missing_specs.map(&:full_name).join(", ")}). Ignoring them..."

    resolve.delete(partially_missing_specs)
  end

  incomplete_specs = resolve.incomplete_specs
  loop do
    break if incomplete_specs.empty?

    Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
    sources.remote!
    reresolve_without(incomplete_specs)
    specs = resolve.materialize(dependencies)

    still_incomplete_specs = resolve.incomplete_specs

    if still_incomplete_specs == incomplete_specs
      resolver.raise_incomplete! incomplete_specs
    end

    incomplete_specs = still_incomplete_specs
  end

  insecurely_materialized_specs = resolve.insecurely_materialized_specs

  if insecurely_materialized_specs.any?
    Bundler.ui.warn "The following platform specific gems are getting installed, yet the lockfile includes only their generic ruby version:\n" \
                    " * #{insecurely_materialized_specs.map(&:full_name).join("\n * ")}\n" \
                    "Please run `bundle lock --normalize-platforms` and commit the resulting lockfile.\n" \
                    "Alternatively, you may run `bundle lock --add-platform <list-of-platforms-that-you-want-to-support>`"
  end

  bundler = sources..specs.search(["bundler", Bundler.gem_version]).last
  specs["bundler"] = bundler

  specs
end

#metadata_dependencies (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1180

def 
  @metadata_dependencies ||= [
    Dependency.new("Ruby\0", Bundler::RubyVersion.system.gem_version),
    Dependency.new("RubyGems\0", Gem::VERSION),
  ]
end

#needed_git_sources (private)

Git sources needed for the requested groups (excludes sources only used by --without groups)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1212

def needed_git_sources
  needed_deps = dependencies_for(requested_groups)
  sources.git_sources.select do |source|
    needed_deps.any? {|d| d.source == source }
  end
end

#new_deps

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 320

def new_deps
  @new_deps ||= @dependencies - locked_dependencies
end

#new_resolution_base(last_resolve:, unlock:) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1331

def new_resolution_base(last_resolve:, unlock:)
  new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
  Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms, overrides: @overrides, explicit_unlocks: @explicit_unlocks)
end

#new_resolver(base) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1336

def new_resolver(base)
  Resolver.new(base, gem_version_promoter, @most_specific_locked_platform)
end

#new_resolver_for_full_update (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1323

def new_resolver_for_full_update
  new_resolver(unlocked_resolution_base)
end

#new_specs

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 259

def new_specs
  specs - @locked_specs
end

#non_dependency_api_warning (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 818

def non_dependency_api_warning
  non_api_sources = sources.non_global_rubygems_sources.reject(&:dependency_api_available?)
  non_api_source_names = non_api_sources.map {|d| "  * #{d}" }.join("\n")

  msg = String.new
  msg << "Your Gemfile contains scoped sources that don't implement a dependency API, namely:\n\n"
  msg << non_api_source_names
  msg << "\n\nUsing the above gem servers may result in installing unexpected gems. " \
    "To resolve this warning, make sure you use gem servers that implement dependency APIs, " \
    "such as gemstash or geminabox gem servers."
  Bundler.ui.warn msg
end

#normalize_platforms

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 520

def normalize_platforms
  resolve.normalize_platforms!(current_dependencies, platforms)

  @resolve = SpecSet.new(resolve.for(current_dependencies, @platforms))
end

#prefer_local!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 233

def prefer_local!
  @prefer_local = true

  sources.prefer_local!
end

#preload_git_source_worker (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1191

def preload_git_source_worker
  workers = Bundler.settings.installation_parallelization

  @preload_git_source_worker ||= Bundler::Worker.new(workers, "Git source preloading", ->(source, _) { source.specs })
end

#preload_git_sources (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1197

def preload_git_sources
  if Gem.ruby_version < Gem::Version.new("3.3")
    # Ruby 3.2 has a bug that incorrectly triggers a circular dependency warning. This version will continue to
    # fetch git repositories one by one.
    return
  end

  begin
    needed_git_sources.each {|source| preload_git_source_worker.enq(source) }
  ensure
    preload_git_source_worker.stop
  end
end

#pretty_dep(dep) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 910

def pretty_dep(dep)
  SharedHelpers.pretty_dependency(dep)
end

#release_resolution_memory!

Releases memory only needed during resolution, such as remote spec indexes and resolver state. Only safe to call once resolution is complete and the result has been materialized, since any further resolution will need to refetch remote specs.

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 243

def release_resolution_memory!
  @resolver = nil
  @resolution_base = nil
  sources.release_resolution_memory!
end

#relevant_deps?(dep) ⇒ Boolean

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 310

def relevant_deps?(dep)
  platforms_array = [Bundler.generic_local_platform].freeze

  dep.should_include? && !dep.gem_platforms(platforms_array).empty?
end

#remotely!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 228

def remotely!
  sources.cached!
  sources.remote!
end

#remove_invalid_platforms! (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1304

def remove_invalid_platforms!
  return if Bundler.frozen_bundle?

  skips = (@new_platforms + [Bundler.local_platform]).uniq

  # We should probably avoid removing non-ruby platforms, since that means
  # lockfile will no longer install on those platforms, so a error to give
  # heads up to the user may be better. However, we have tests expecting
  # non ruby platform autoremoval to work, so leaving that in place for
  # now.
  skips |= platforms - [Gem::Platform::RUBY] if @dependency_changes

  @originally_invalid_platforms = @originally_locked_specs.remove_invalid_platforms!(current_dependencies, platforms, skips: skips)
end

#remove_platform(platform)

Raises:

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 533

def remove_platform(platform)
  raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" unless @platforms.include?(platform)

  @removed_platforms << platform
  @platforms.delete(platform)
end

#removed_specs

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 263

def removed_specs
  @locked_specs - specs
end

#requested_dependencies

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 292

def requested_dependencies
  dependencies_for(requested_groups)
end

#requested_groups (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1261

def requested_groups
  values = groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
  values &= Bundler.settings[:only] unless Bundler.settings[:only].empty?
  values
end

#requested_specs

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 288

def requested_specs
  specs_for(requested_groups)
end

#reresolve_without(incomplete_specs) (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 771

def reresolve_without(incomplete_specs)
  resolution_base.delete(incomplete_specs)
  @resolve = start_resolution
end

#resolution_base (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 675

def resolution_base
  @resolution_base ||= begin
    last_resolve = converge_locked_specs
    remove_invalid_platforms!
    base = new_resolution_base(last_resolve: last_resolve, unlock: @unlocking_all || @gems_to_unlock)
    base = additional_base_requirements_to_prevent_downgrades(base)
    base = additional_base_requirements_to_force_updates(base)
    base
  end
end

#resolveSpecSet

Resolve all the dependencies specified in Gemfile. It ensures that dependencies that have been already resolved via locked file and are fresh are reused when resolving dependencies

Returns:

  • (SpecSet)

    resolved dependencies

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 348

def resolve
  @resolve ||= if Bundler.frozen_bundle?
    Bundler.ui.debug "Frozen, using resolution from the lockfile"
    @locked_specs
  elsif no_resolve_needed?
    if deleted_deps.any?
      Bundler.ui.debug "Some dependencies were deleted, using a subset of the resolution from the lockfile"
      SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
    else
      Bundler.ui.debug "Found no changes, using resolution from the lockfile"
      if @removed_platforms.any? || @locked_gems.may_include_redundant_platform_specific_gems?
        SpecSet.new(filter_specs(@locked_specs, @dependencies))
      else
        @locked_specs
      end
    end
  else
    Bundler.ui.debug resolve_needed_reason

    start_resolution
  end
end

#resolve_needed_reason (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 853

def resolve_needed_reason
  if lockfile_exists?
    if unlocking?
      "Re-resolving dependencies because #{unlocking_reason}"
    else
      "Found changes from the lockfile, re-resolving dependencies because #{lockfile_changed_reason}"
    end
  else
    "Resolving dependencies because there's no lockfile"
  end
end

#resolve_remotely!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 222

def resolve_remotely!
  remotely!

  resolve
end

#resolve_with_cache!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 211

def resolve_with_cache!
  with_cache!

  resolve
end

#resolver (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 647

def resolver
  @resolver ||= new_resolver(resolution_base)
end

#setup_domain!(options = {}) ⇒ Boolean

Setup sources according to the given options and the state of the definition.

Returns:

  • (Boolean)

    Whether fetching remote information will be necessary or not

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 196

def setup_domain!(options = {})
  prefer_local! if options[:"prefer-local"]

  sources.cached!

  if options[:add_checksums] || (!options[:local] && install_needed?)
    sources.remote!
    true
  else
    Bundler.settings.set_command_option(:jobs, 1) unless install_needed? # to avoid the overhead of Bundler::Worker
    sources.local!
    false
  end
end

#source_map (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1319

def source_map
  @source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
end

#source_requirements (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1187

def source_requirements
  @source_requirements ||= find_source_requirements
end

#spec_git_paths

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 371

def spec_git_paths
  sources.git_sources.filter_map {|s| File.realpath(s.path) if File.exist?(s.path) }
end

#specsBundler::SpecSet

For given dependency list returns a SpecSet with Gemspec of all the required dependencies.

  1. The method first resolves the dependencies specified in Gemfile
  2. After that it tries and fetches gemspec of resolved dependencies
[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 255

def specs
  @specs ||= materialize(requested_dependencies)
end

#specs_changed?(source) ⇒ Boolean (private)

Check if the specs of the given source changed according to the locked source.

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 916

def specs_changed?(source)
  locked = @locked_sources.find {|s| s == source }

  !locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
end

#specs_for(groups)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 328

def specs_for(groups)
  return specs if groups.empty?
  deps = dependencies_for(groups)
  materialize(deps)
end

#specs_for_source_changed?(source) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 929

def specs_for_source_changed?(source)
  locked_index = Index.new
  locked_index.use(@locked_specs.select {|s| s.replace_source_with!(source) })

  !locked_index.subset?(source.specs)
rescue PathError, GitError => e
  Bundler.ui.debug "Assuming that #{source} has not changed since fetching its specs errored (#{e})"
  false
end

#start_resolution (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 776

def start_resolution
  local_platform_needed_for_resolvability = @most_specific_non_local_locked_platform && !@platforms.include?(Bundler.local_platform)
  @platforms << Bundler.local_platform if local_platform_needed_for_resolvability

  result = SpecSet.new(resolver.start)

  @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version

  @new_platforms.each do |platform|
    incomplete_specs = result.incomplete_specs_for_platform(current_dependencies, platform)

    if incomplete_specs.any?
      resolver.raise_incomplete! incomplete_specs
    end
  end

  if @most_specific_non_local_locked_platform
    if result.incomplete_for_platform?(current_dependencies, @most_specific_non_local_locked_platform)
      @platforms.delete(@most_specific_non_local_locked_platform)
    elsif local_platform_needed_for_resolvability
      @platforms.delete(Bundler.local_platform)
    end
  end

  if should_add_extra_platforms?
    result.add_extra_platforms!(platforms)
  elsif @originally_invalid_platforms.any?
    result.add_originally_invalid_platforms!(platforms, @originally_invalid_platforms)
  end

  SpecSet.new(result.for(dependencies, @platforms | [Gem::Platform::RUBY]))
end

#to_lock

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 464

def to_lock
  require_relative "lockfile_generator"
  LockfileGenerator.generate(self)
end

#unlocked_resolution_base (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 1327

def unlocked_resolution_base
  new_resolution_base(last_resolve: SpecSet.new([]), unlock: true)
end

#unlocking_reason (private)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 877

def unlocking_reason
  unlock_targets = if @gems_to_unlock.any?
    ["gems", @gems_to_unlock]
  elsif @sources_to_unlock.any?
    ["sources", @sources_to_unlock]
  end

  unlock_reason = if unlock_targets
    "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
  else
    @unlocking_ruby ? "ruby" : ""
  end

  "bundler is unlocking #{unlock_reason}"
end

#validate_platforms!

Raises:

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 512

def validate_platforms!
  return if current_platform_locked? || @platforms.include?(Gem::Platform::RUBY)

  raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
    "but your local platform is #{Bundler.local_platform}. " \
    "Add the current platform to the lockfile with\n`bundle lock --add-platform #{Bundler.local_platform}` and try again."
end

#validate_ruby!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 493

def validate_ruby!
  return unless ruby_version

  if diff = ruby_version.diff(Bundler::RubyVersion.system)
    problem, expected, actual = diff

    msg = case problem
          when :engine
            "Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
          when :version
            "Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
          when :engine_version
            "Your #{Bundler::RubyVersion.system.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
    end

    raise RubyVersionMismatch, msg
  end
end

#validate_runtime!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 488

def validate_runtime!
  validate_ruby!
  validate_platforms!
end

#with_cache!

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 217

def with_cache!
  sources.local!
  sources.cached!
end

#write_lock(file, preserve_unknown_sections)

[ GitHub ]

  
# File 'lib/bundler/definition.rb', line 401

def write_lock(file, preserve_unknown_sections)
  return if Definition.no_lock || !lockfile || file.nil?

  contents = to_lock

  # Convert to \r\n if the existing lock has them
  # i.e., Windows with `git config core.autocrlf=true`
  contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")

  if @locked_bundler_version
    locked_major = @locked_bundler_version.segments.first
    current_major = bundler_version_to_lock.segments.first

    updating_major = locked_major < current_major
  end

  preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))

  if File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
    return if Bundler.frozen_bundle?
    SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
    return
  end

  if Bundler.frozen_bundle?
    Bundler.ui.error "Cannot write a changed lockfile while frozen."
    return
  end

  begin
    SharedHelpers.filesystem_access(file) do |p|
      File.open(p, "wb") {|f| f.puts(contents) }
    end
  rescue ReadOnlyFileSystemError
    raise ProductionError, lockfile_changes_summary("file system is read-only")
  end
end