summaryrefslogtreecommitdiff
path: root/lib/bundler/lazy_specification.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bundler/lazy_specification.rb')
-rw-r--r--lib/bundler/lazy_specification.rb135
1 files changed, 75 insertions, 60 deletions
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 4eb228f314..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
+ 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 || platform.nil?
+ @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 || platform.nil?
- 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
@@ -75,45 +92,51 @@ module Bundler
out
end
- def __materialize__
- @specification = if source.is_a?(Source::Gemspec) && source.gemspec.name == name
- source.gemspec.tap {|s| s.source = source }
+ def materialize_for_installation
+ source.local!
+
+ matching_specs = source.specs.search(use_exact_resolved_specifications? ? self : [name, version])
+ return self if matching_specs.empty?
+
+ candidates = if use_exact_resolved_specifications?
+ matching_specs
else
- search_object = if source.is_a?(Source::Path)
- Dependency.new(name, version)
- else
- ruby_platform_materializes_to_ruby_platform? ? self : Dependency.new(name, version)
- end
- platform_object = Gem::Platform.new(platform)
- candidates = source.specs.search(search_object)
- same_platform_candidates = candidates.select do |spec|
- MatchPlatform.platforms_match?(spec.platform, platform_object)
- end
- installable_candidates = same_platform_candidates.select do |spec|
- !spec.is_a?(EndpointSpecification) ||
- (spec.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
- spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version))
+ target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : local_platform
+
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
+
+ specification = __materialize__(installable_candidates, fallback_to_non_installable: false)
+ return specification unless specification.nil?
+
+ if target_platform != platform
+ installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
end
- search = installable_candidates.last || same_platform_candidates.last
- search.dependencies = dependencies if search && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
- 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 || platform.nil?
- "#{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
@@ -121,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
@@ -152,7 +165,9 @@ module Bundler
# explicitly add a more specific platform.
#
def ruby_platform_materializes_to_ruby_platform?
- !Bundler.most_specific_locked_platform?(Gem::Platform::RUBY) || 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