diff options
author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2020-12-23 08:45:19 +0900 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2020-12-23 10:17:41 +0900 |
commit | 339227363ce0cf967fa17efa4489d823932ddabd (patch) | |
tree | 576482ce00d03439f2dbf4714a6f309293884c2f /lib/bundler | |
parent | 733ed1e18498f97250b788f169c37b170e0cf2b6 (diff) |
Merge RubyGems 3.2.3 and Bundler 2.2.3
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/3982
Diffstat (limited to 'lib/bundler')
-rw-r--r-- | lib/bundler/cli/update.rb | 2 | ||||
-rw-r--r-- | lib/bundler/compact_index_client/cache.rb | 18 | ||||
-rw-r--r-- | lib/bundler/compact_index_client/gem_parser.rb | 28 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 40 | ||||
-rw-r--r-- | lib/bundler/gem_helpers.rb | 54 | ||||
-rw-r--r-- | lib/bundler/lazy_specification.rb | 21 | ||||
-rw-r--r-- | lib/bundler/resolver/spec_group.rb | 30 | ||||
-rw-r--r-- | lib/bundler/rubygems_integration.rb | 5 | ||||
-rw-r--r-- | lib/bundler/spec_set.rb | 13 | ||||
-rw-r--r-- | lib/bundler/version.rb | 2 |
10 files changed, 120 insertions, 93 deletions
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb index ae908be65e..94699484d4 100644 --- a/lib/bundler/cli/update.rb +++ b/lib/bundler/cli/update.rb @@ -82,7 +82,7 @@ module Bundler locked_spec = locked_info[:spec] new_spec = Bundler.definition.specs[name].first unless new_spec - if Bundler.rubygems.platforms.none? {|p| locked_spec.match_platform(p) } + unless locked_spec.match_platform(Bundler.local_platform) Bundler.ui.warn "Bundler attempted to update #{name} but it was not considered because it is for a different platform from the current one" end diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb index 8f73298fbe..c2cd069ec1 100644 --- a/lib/bundler/compact_index_client/cache.rb +++ b/lib/bundler/compact_index_client/cache.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "gem_parser" + module Bundler class CompactIndexClient class Cache @@ -92,19 +94,9 @@ module Bundler header ? lines[header + 1..-1] : lines end - def parse_gem(string) - version_and_platform, rest = string.split(" ", 2) - version, platform = version_and_platform.split("-", 2) - dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest - dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] - requirements = requirements ? requirements.map {|r| parse_dependency(r) } : [] - [version, platform, dependencies, requirements] - end - - def parse_dependency(string) - dependency = string.split(":") - dependency[-1] = dependency[-1].split("&") if dependency.size > 1 - dependency + def parse_gem(line) + @dependency_parser ||= GemParser.new + @dependency_parser.parse(line) end def info_roots diff --git a/lib/bundler/compact_index_client/gem_parser.rb b/lib/bundler/compact_index_client/gem_parser.rb new file mode 100644 index 0000000000..e7bf4c6001 --- /dev/null +++ b/lib/bundler/compact_index_client/gem_parser.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Bundler + class CompactIndexClient + if defined?(Gem::Resolver::APISet::GemParser) + GemParser = Gem::Resolver::APISet::GemParser + else + class GemParser + def parse(line) + version_and_platform, rest = line.split(" ", 2) + version, platform = version_and_platform.split("-", 2) + dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest + dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] + requirements = requirements ? requirements.map {|d| parse_dependency(d) } : [] + [version, platform, dependencies, requirements] + end + + private + + def parse_dependency(string) + dependency = string.split(":") + dependency[-1] = dependency[-1].split("&") if dependency.size > 1 + dependency + end + end + end + end +end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index fdd093fbb3..b22363d119 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -118,7 +118,7 @@ module Bundler end @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version) - add_current_platform unless Bundler.frozen_bundle? + add_current_platform unless current_ruby_platform_locked? || Bundler.frozen_bundle? converge_path_sources_to_gemspec_sources @path_changes = converge_paths @@ -157,7 +157,7 @@ module Bundler end def resolve_remotely! - raise "Specs already loaded" if @specs + return if @specs @remote = true sources.remote! specs @@ -269,9 +269,8 @@ module Bundler else # Run a resolve against the locally available gems Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}") - platforms_for_resolve = platforms.one? {|p| generic(p) == Gem::Platform::RUBY } ? platforms : platforms.reject{|p| p == Gem::Platform::RUBY } - expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote, platforms_for_resolve.map {|p| generic(p) }) - last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms_for_resolve) + expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote) + last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms) end # filter out gems that _can_ be installed on multiple platforms, but don't need @@ -507,15 +506,11 @@ module Bundler end def validate_platforms! - return if @platforms.any? do |bundle_platform| - Bundler.rubygems.platforms.any? do |local_platform| - MatchPlatform.platforms_match?(bundle_platform, local_platform) - end - end + return if current_platform_locked? raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \ - "but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \ - "there's no compatible match between those two lists." + "but your local platform is #{Bundler.local_platform}. " \ + "Add the current platform to the lockfile with `bundle lock --add-platform #{Bundler.local_platform}` and try again." end def add_platform(platform) @@ -528,6 +523,12 @@ module Bundler raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" end + def most_specific_locked_platform + @platforms.min_by do |bundle_platform| + platform_specificity_match(bundle_platform, local_platform) + end + end + def find_resolved_spec(current_spec) specs.find_by_name_and_platform(current_spec.name, current_spec.platform) end @@ -549,6 +550,18 @@ module Bundler private + def current_ruby_platform_locked? + return false unless generic_local_platform == Gem::Platform::RUBY + + current_platform_locked? + end + + def current_platform_locked? + @platforms.any? do |bundle_platform| + MatchPlatform.platforms_match?(bundle_platform, Bundler.local_platform) + end + end + def add_current_platform add_platform(local_platform) end @@ -871,8 +884,7 @@ module Bundler end end - def expand_dependencies(dependencies, remote = false, platforms = nil) - platforms ||= @platforms + def expand_dependencies(dependencies, remote = false) deps = [] dependencies.each do |dep| dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index 2a097375c0..b271b8d229 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -35,41 +35,33 @@ module Bundler def platform_specificity_match(spec_platform, user_platform) spec_platform = Gem::Platform.new(spec_platform) - return PlatformMatch::EXACT_MATCH if spec_platform == user_platform - return PlatformMatch::WORST_MATCH if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY - - PlatformMatch.new( - PlatformMatch.os_match(spec_platform, user_platform), - PlatformMatch.cpu_match(spec_platform, user_platform), - PlatformMatch.platform_version_match(spec_platform, user_platform) - ) + + PlatformMatch.specificity_score(spec_platform, user_platform) end module_function :platform_specificity_match def select_best_platform_match(specs, platform) - specs.select {|spec| spec.match_platform(platform) }. - min_by {|spec| platform_specificity_match(spec.platform, platform) } + matching = specs.select {|spec| spec.match_platform(platform) } + exact = matching.select {|spec| spec.platform == platform } + return exact if exact.any? + + sorted_matching = matching.sort_by {|spec| platform_specificity_match(spec.platform, platform) } + exemplary_spec = sorted_matching.first + + sorted_matching.take_while{|spec| same_specificity(platform, spec, exemplary_spec) && same_deps(spec, exemplary_spec) } end module_function :select_best_platform_match - PlatformMatch = Struct.new(:os_match, :cpu_match, :platform_version_match) class PlatformMatch - def <=>(other) - return nil unless other.is_a?(PlatformMatch) + def self.specificity_score(spec_platform, user_platform) + return -1 if spec_platform == user_platform + return 1_000_000 if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY - m = os_match <=> other.os_match - return m unless m.zero? - - m = cpu_match <=> other.cpu_match - return m unless m.zero? - - m = platform_version_match <=> other.platform_version_match - m + os_match(spec_platform, user_platform) + + cpu_match(spec_platform, user_platform) * 10 + + platform_version_match(spec_platform, user_platform) * 100 end - EXACT_MATCH = new(-1, -1, -1).freeze - WORST_MATCH = new(1_000_000, 1_000_000, 1_000_000).freeze - def self.os_match(spec_platform, user_platform) if spec_platform.os == user_platform.os 0 @@ -100,5 +92,19 @@ module Bundler end end end + + def same_specificity(platform, spec, exemplary_spec) + platform_specificity_match(spec.platform, platform) == platform_specificity_match(exemplary_spec.platform, platform) + end + module_function :same_specificity + + def same_deps(spec, exemplary_spec) + same_runtime_deps = spec.dependencies.sort == exemplary_spec.dependencies.sort + return same_runtime_deps unless spec.is_a?(Gem::Specification) && exemplary_spec.is_a?(Gem::Specification) + + same_metadata_deps = spec.required_ruby_version == exemplary_spec.required_ruby_version && spec.required_rubygems_version == exemplary_spec.required_rubygems_version + same_runtime_deps && same_metadata_deps + end + module_function :same_deps end end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 7b1d28b0c3..1081910816 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -4,7 +4,7 @@ require_relative "match_platform" module Bundler class LazySpecification - Identifier = Struct.new(:name, :version, :source, :platform, :dependencies) + Identifier = Struct.new(:name, :version, :platform) class Identifier include Comparable def <=>(other) @@ -108,7 +108,7 @@ module Bundler end def identifier - @__identifier ||= Identifier.new(name, version, source, platform, dependencies) + @__identifier ||= Identifier.new(name, version, platform) end def git_version @@ -131,17 +131,16 @@ module Bundler end # - # Bundler 2.2.0 was the first version that records the full resolution - # including platform specific gems in the lockfile, which means that if a - # gem with RUBY platform is recorded, the RUBY platform version of the gem - # should be installed. Previously bundler would record only generic versions - # in the lockfile and then install the most specific platform variant if - # available. + # For backwards compatibility with existing lockfiles, if the most specific + # locked platform is RUBY, 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 + # on newer bundlers unless users generate the lockfile from scratch or + # explicitly add a more specific platform. # def ruby_platform_materializes_to_ruby_platform? - locked_bundler_version = Bundler.locked_bundler_version - - locked_bundler_version.nil? || Gem::Version.new(locked_bundler_version) >= Gem::Version.new("2.2.0") + !Bundler.most_specific_locked_platform?(Gem::Platform::RUBY) end end end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 38dc175ff9..34780f9528 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -25,11 +25,15 @@ module Bundler def to_specs @activated_platforms.map do |p| - next unless s = @specs[p] - lazy_spec = LazySpecification.new(name, version, s.platform, source) - lazy_spec.dependencies.replace s.dependencies - lazy_spec - end.compact.uniq + specs = @specs[p] + next unless specs.any? + + specs.map do |s| + lazy_spec = LazySpecification.new(name, version, s.platform, source) + lazy_spec.dependencies.replace s.dependencies + lazy_spec + end + end.flatten.compact.uniq end def copy_for(platforms) @@ -42,12 +46,8 @@ module Bundler copied_sg end - def spec_for(platform) - @specs[platform] - end - def for?(platform) - !spec_for(platform).nil? + @specs[platform].any? end def to_s @@ -58,7 +58,7 @@ module Bundler def dependencies_for_activated_platforms dependencies = @activated_platforms.map {|p| __dependencies[p] } metadata_dependencies = @activated_platforms.map do |platform| - metadata_dependencies(@specs[platform], platform) + metadata_dependencies(@specs[platform].first, platform) end dependencies.concat(metadata_dependencies).flatten end @@ -94,7 +94,8 @@ module Bundler def __dependencies @dependencies = Hash.new do |dependencies, platform| dependencies[platform] = [] - if spec = @specs[platform] + specs = @specs[platform] + if spec = specs.first spec.dependencies.each do |dep| next if dep.type == :development next if @ignores_bundler_dependencies && dep.name == "bundler".freeze @@ -106,10 +107,7 @@ module Bundler end def metadata_dependencies(spec, platform) - return [] unless spec - # Only allow endpoint specifications since they won't hit the network to - # fetch the full gemspec when calling required_ruby_version - return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification) + return [] unless spec && spec.is_a?(Gem::Specification) dependencies = [] if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index c577001043..d060e21f50 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -110,11 +110,6 @@ module Bundler obj.to_s end - def platforms - return [Gem::Platform::RUBY] if Bundler.settings[:force_ruby_platform] - Gem.platforms - end - def configuration require_relative "psyched_yaml" Gem.configuration diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index aef854ce0c..a0b9552c18 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -22,10 +22,11 @@ module Bundler break unless dep = deps.shift next if !handled.add?(dep) || skip.include?(dep.name) - if spec = spec_for_dependency(dep, match_current_platform) - specs << spec + specs_for_dep = spec_for_dependency(dep, match_current_platform) + if specs_for_dep.any? + specs += specs_for_dep - spec.dependencies.each do |d| + specs_for_dep.first.dependencies.each do |d| next if d.type == :development d = DepProxy.new(d, dep.__platform) unless match_current_platform deps << d @@ -184,11 +185,7 @@ module Bundler def spec_for_dependency(dep, match_current_platform) specs_for_platforms = lookup[dep.name] if match_current_platform - Bundler.rubygems.platforms.reverse_each do |pl| - match = GemHelpers.select_best_platform_match(specs_for_platforms, pl) - return match if match - end - nil + GemHelpers.select_best_platform_match(specs_for_platforms, Bundler.local_platform) else GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform) end diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index c730934258..8db404377c 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.2.2".freeze + VERSION = "2.2.3".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i |