diff options
Diffstat (limited to 'lib/bundler/lazy_specification.rb')
-rw-r--r-- | lib/bundler/lazy_specification.rb | 130 |
1 files changed, 68 insertions, 62 deletions
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 5b40bec5a8..8669e021c2 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -1,41 +1,63 @@ # frozen_string_literal: true -require_relative "match_platform" +require_relative "force_platform" module Bundler class LazySpecification + include MatchMetadata include MatchPlatform + include ForcePlatform - attr_reader :name, :version, :dependencies, :platform - attr_accessor :source, :remote, :force_ruby_platform + attr_reader :name, :version, :platform + attr_accessor :source, :remote, :force_ruby_platform, :dependencies, :required_ruby_version, :required_rubygems_version + + alias_method :runtime_dependencies, :dependencies + + def self.from_spec(s) + lazy_spec = new(s.name, s.version, s.platform, s.source) + lazy_spec.dependencies = s.dependencies + lazy_spec.required_ruby_version = s.required_ruby_version + lazy_spec.required_rubygems_version = s.required_rubygems_version + lazy_spec + end def initialize(name, version, platform, source = nil) @name = name @version = version @dependencies = [] + @required_ruby_version = Gem::Requirement.default + @required_rubygems_version = Gem::Requirement.default @platform = platform || Gem::Platform::RUBY @source = source - @specification = nil + @force_ruby_platform = default_force_ruby_platform end def full_name - if platform == Gem::Platform::RUBY + @full_name ||= if platform == Gem::Platform::RUBY "#{@name}-#{@version}" else "#{@name}-#{@version}-#{platform}" end end + def lock_name + @lock_name ||= name_tuple.lock_name + end + + def name_tuple + Gem::NameTuple.new(@name, @version, @platform) + end + def ==(other) - identifier == other.identifier + full_name == other.full_name end def eql?(other) - identifier.eql?(other.identifier) + full_name.eql?(other.full_name) end def hash - identifier.hash + full_name.hash end ## @@ -60,12 +82,7 @@ module Bundler def to_lock out = String.new - - if platform == Gem::Platform::RUBY - out << " #{name} (#{version})\n" - else - out << " #{name} (#{version}-#{platform})\n" - end + out << " #{lock_name}\n" dependencies.sort_by(&:to_s).uniq.each do |dep| next if dep.type == :development @@ -78,51 +95,48 @@ module Bundler def materialize_for_installation source.local! - candidates = if source.is_a?(Source::Path) || !ruby_platform_materializes_to_ruby_platform? - target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : Bundler.local_platform + matching_specs = source.specs.search(use_exact_resolved_specifications? ? self : [name, version]) + return self if matching_specs.empty? - source.specs.search(Dependency.new(name, version)).select do |spec| - MatchPlatform.platforms_match?(spec.platform, target_platform) - end + candidates = if use_exact_resolved_specifications? + matching_specs else - source.specs.search(self) - end + target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : local_platform - return self if candidates.empty? + installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform) - __materialize__(candidates) - end + specification = __materialize__(installable_candidates, fallback_to_non_installable: false) + return specification unless specification.nil? - def __materialize__(candidates) - @specification = begin - search = candidates.reverse.find do |spec| - spec.is_a?(StubSpecification) || - (spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && - spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version)) + if target_platform != platform + installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform) end - if search.nil? && Bundler.frozen_bundle? - search = candidates.last - else - search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification)) - end - search + + installable_candidates end - end - def respond_to?(*args) - super || @specification ? @specification.respond_to?(*args) : nil + __materialize__(candidates) end - def to_s - @__to_s ||= if platform == Gem::Platform::RUBY - "#{name} (#{version})" + # If in frozen mode, we fallback to a non-installable candidate because by + # doing this we avoid re-resolving and potentially end up changing the + # lock file, which is not allowed. In that case, we will give a proper error + # about the mismatch higher up the stack, right before trying to install the + # bad gem. + def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?) + search = candidates.reverse.find do |spec| + spec.is_a?(StubSpecification) || spec.matches_current_metadata? + end + if search.nil? && fallback_to_non_installable + search = candidates.last else - "#{name} (#{version}-#{platform})" + search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification)) end + search end - def identifier - @__identifier ||= [name, version, platform_string] + def to_s + lock_name end def git_version @@ -130,30 +144,20 @@ module Bundler " #{source.revision[0..6]}" end - protected - - def platform_string - platform_string = platform.to_s - platform_string == Index::RUBY ? Index::NULL : platform_string + def force_ruby_platform! + @force_ruby_platform = true end private - def to_ary - nil - end - - def method_missing(method, *args, &blk) - raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification - - return super unless respond_to?(method) - - @specification.send(method, *args, &blk) + def use_exact_resolved_specifications? + @use_exact_resolved_specifications ||= !source.is_a?(Source::Path) && ruby_platform_materializes_to_ruby_platform? end # # For backwards compatibility with existing lockfiles, if the most specific - # locked platform is RUBY, we keep the previous behaviour of resolving the + # locked platform is not a specific platform like x86_64-linux or + # universal-java-11, then we keep the previous behaviour of resolving the # best platform variant at materiliazation time. For previous bundler # versions (before 2.2.0) this was always the case (except when the lockfile # only included non-ruby platforms), but we're also keeping this behaviour @@ -161,7 +165,9 @@ module Bundler # explicitly add a more specific platform. # def ruby_platform_materializes_to_ruby_platform? - !Bundler.most_specific_locked_platform?(generic_local_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] + generic_platform = generic_local_platform == Gem::Platform::JAVA ? Gem::Platform::JAVA : Gem::Platform::RUBY + + !Bundler.most_specific_locked_platform?(generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] end end end |