diff options
author | Samuel Giddins <[email protected]> | 2025-05-18 11:37:35 -0400 |
---|---|---|
committer | Hiroshi SHIBATA <[email protected]> | 2025-06-06 10:22:18 +0900 |
commit | c0a1e877b3c0c5dd69bb634262bd4e73a07eb27e () | |
tree | dabebfbdd5389f04e180f4f4c4286f7a8800168a | |
parent | 6a9af9f0b566f8a13f82a1ca402efa99a3464794 (diff) |
Move most of Bundler::GemHelpers to Gem::Platform
This will help centralize wheel platform selection logic eventually Signed-off-by: Samuel Giddins <[email protected]>
-rw-r--r-- | lib/bundler.rb | 5 | ||||
-rw-r--r-- | lib/bundler/cli/outdated.rb | 2 | ||||
-rw-r--r-- | lib/bundler/cli/update.rb | 2 | ||||
-rw-r--r-- | lib/bundler/current_ruby.rb | 2 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 26 | ||||
-rw-r--r-- | lib/bundler/dependency.rb | 2 | ||||
-rw-r--r-- | lib/bundler/dsl.rb | 2 | ||||
-rw-r--r-- | lib/bundler/gem_helpers.rb | 144 | ||||
-rw-r--r-- | lib/bundler/lazy_specification.rb | 8 | ||||
-rw-r--r-- | lib/bundler/lockfile_parser.rb | 4 | ||||
-rw-r--r-- | lib/bundler/match_platform.rb | 43 | ||||
-rw-r--r-- | lib/bundler/materialization.rb | 4 | ||||
-rw-r--r-- | lib/bundler/resolver.rb | 4 | ||||
-rw-r--r-- | lib/bundler/resolver/package.rb | 2 | ||||
-rw-r--r-- | lib/bundler/rubygems_ext.rb | 133 | ||||
-rw-r--r-- | lib/bundler/spec_set.rb | 6 | ||||
-rw-r--r-- | lib/rubygems/basic_specification.rb | 7 | ||||
-rw-r--r-- | lib/rubygems/platform.rb | 114 | ||||
-rw-r--r-- | spec/bundler/other/ext_spec.rb | 45 | ||||
-rw-r--r-- | spec/bundler/support/platforms.rb | 12 | ||||
-rw-r--r-- | test/rubygems/helper.rb | 3 | ||||
-rw-r--r-- | test/rubygems/test_gem_platform.rb | 165 |
22 files changed, 488 insertions, 247 deletions
@@ -53,7 +53,6 @@ module Bundler autoload :FeatureFlag, File.expand_path("bundler/feature_flag", __dir__) autoload :FREEBSD, File.expand_path("bundler/constants", __dir__) autoload :GemHelper, File.expand_path("bundler/gem_helper", __dir__) - autoload :GemHelpers, File.expand_path("bundler/gem_helpers", __dir__) autoload :GemVersionPromoter, File.expand_path("bundler/gem_version_promoter", __dir__) autoload :Graph, File.expand_path("bundler/graph", __dir__) autoload :Index, File.expand_path("bundler/index", __dir__) @@ -459,6 +458,10 @@ module Bundler Gem::Platform.local end def default_gemfile SharedHelpers.default_gemfile end @@ -155,7 +155,7 @@ module Bundler return active_spec if strict - active_specs = active_spec.source.specs.search(current_spec.name).select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version) if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1 active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? } end @@ -92,7 +92,7 @@ module Bundler locked_spec = locked_info[:spec] new_spec = Bundler.definition.specs[name].first unless new_spec - 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 @@ -32,7 +32,7 @@ module Bundler end.freeze def ruby? - return true if Bundler::GemHelpers.generic_local_platform_is_ruby? !windows? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby") end @@ -4,8 +4,6 @@ require_relative "lockfile_parser" module Bundler class Definition - include GemHelpers - class << self # Do not create or modify a lockfile (Makes #lock a noop) attr_accessor :no_lock @@ -282,7 +280,7 @@ module Bundler end def filter_relevant(dependencies) - platforms_array = [generic_local_platform].freeze dependencies.select do |d| d.should_include? && !d.gem_platforms(platforms_array).empty? end @@ -456,8 +454,8 @@ module Bundler 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 #{local_platform}. " \ - "Add the current platform to the lockfile with\n`bundle lock --add-platform #{local_platform}` and try again." end def normalize_platforms @@ -568,7 +566,7 @@ module Bundler end def should_add_extra_platforms? - !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform] end def lockfile_exists? @@ -632,7 +630,7 @@ module Bundler @resolution_base ||= begin last_resolve = converge_locked_specs remove_invalid_platforms! - new_resolution_platforms = @current_platform_missing ? @new_platforms + [local_platform] : @new_platforms base = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms) base = additional_base_requirements_to_prevent_downgrades(base) base = additional_base_requirements_to_force_updates(base) @@ -738,8 +736,8 @@ module Bundler end def start_resolution - local_platform_needed_for_resolvability = @most_specific_non_local_locked_platform && [email protected]?(local_platform) - @platforms << local_platform if local_platform_needed_for_resolvability add_platform(Gem::Platform::RUBY) if RUBY_ENGINE == "truffleruby" result = SpecSet.new(resolver.start) @@ -758,7 +756,7 @@ module Bundler 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(local_platform) end end @@ -777,17 +775,17 @@ module Bundler def current_platform_locked? @platforms.any? do |bundle_platform| - generic_local_platform == bundle_platform || local_platform === bundle_platform end end def add_current_platform - return if @platforms.include?(local_platform) @most_specific_non_local_locked_platform = find_most_specific_locked_platform return if @most_specific_non_local_locked_platform - @platforms << local_platform true end @@ -1167,7 +1165,7 @@ module Bundler def remove_invalid_platforms! return if Bundler.frozen_bundle? - skips = (@new_platforms + [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 @@ -99,7 +99,7 @@ module Bundler return RUBY_PLATFORM_ARRAY if force_ruby_platform return valid_platforms if platforms.empty? - valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) } end def expanded_platforms @@ -73,7 +73,7 @@ module Bundler case specs_by_name_and_version.size when 1 specs = specs_by_name_and_version.values.first - spec = specs.find {|s| s.match_platform(Bundler.local_platform) } || specs.first @gemspecs << spec @@ -1,144 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module GemHelpers - GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant - GENERICS = [ - Gem::Platform::JAVA, - *Gem::Platform::WINDOWS, - ].freeze - - def generic(p) - GENERIC_CACHE[p] ||= begin - found = GENERICS.find do |match| - p === match - end - found || Gem::Platform::RUBY - end - end - module_function :generic - - def generic_local_platform - generic(local_platform) - end - module_function :generic_local_platform - - def local_platform - Bundler.local_platform - end - module_function :local_platform - - def generic_local_platform_is_ruby? - generic_local_platform == Gem::Platform::RUBY - end - module_function :generic_local_platform_is_ruby? - - def platform_specificity_match(spec_platform, user_platform) - spec_platform = Gem::Platform.new(spec_platform) - - PlatformMatch.specificity_score(spec_platform, user_platform) - end - module_function :platform_specificity_match - - def select_all_platform_match(specs, platform, force_ruby: false, prefer_locked: false) - matching = if force_ruby - specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! } - else - specs.select {|spec| spec.match_platform(platform) } - end - - if prefer_locked - locked_originally = matching.select {|spec| spec.is_a?(LazySpecification) } - return locked_originally if locked_originally.any? - end - - matching - end - module_function :select_all_platform_match - - def select_best_platform_match(specs, platform, force_ruby: false, prefer_locked: false) - matching = select_all_platform_match(specs, platform, force_ruby: force_ruby, prefer_locked: prefer_locked) - - sort_and_filter_best_platform_match(matching, platform) - end - module_function :select_best_platform_match - - def select_best_local_platform_match(specs, force_ruby: false) - matching = select_all_platform_match(specs, local_platform, force_ruby: force_ruby).filter_map(&:materialized_for_installation) - - sort_best_platform_match(matching, local_platform) - end - module_function :select_best_local_platform_match - - def sort_and_filter_best_platform_match(matching, platform) - return matching if matching.one? - - exact = matching.select {|spec| spec.platform == platform } - return exact if exact.any? - - sorted_matching = sort_best_platform_match(matching, 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 :sort_and_filter_best_platform_match - - def sort_best_platform_match(matching, platform) - matching.sort_by {|spec| platform_specificity_match(spec.platform, platform) } - end - module_function :sort_best_platform_match - - class 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 - - os_match(spec_platform, user_platform) + - cpu_match(spec_platform, user_platform) * 10 + - platform_version_match(spec_platform, user_platform) * 100 - end - - def self.os_match(spec_platform, user_platform) - if spec_platform.os == user_platform.os - 0 - else - 1 - end - end - - def self.cpu_match(spec_platform, user_platform) - if spec_platform.cpu == user_platform.cpu - 0 - elsif spec_platform.cpu == "arm" && user_platform.cpu.to_s.start_with?("arm") - 0 - elsif spec_platform.cpu.nil? || spec_platform.cpu == "universal" - 1 - else - 2 - end - end - - def self.platform_version_match(spec_platform, user_platform) - if spec_platform.version == user_platform.version - 0 - elsif spec_platform.version.nil? - 1 - else - 2 - 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 - 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 @@ -142,15 +142,15 @@ module Bundler end else materialize([name, version]) do |matching_specs| - target_platform = source.is_a?(Source::Path) ? platform : local_platform - installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform) specification = choose_compatible(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 choose_compatible(installable_candidates) @@ -190,7 +190,7 @@ module Bundler end def ruby_platform_materializes_to_ruby_platform? - generic_platform = generic_local_platform == Gem::Platform::JAVA ? Gem::Platform::JAVA : Gem::Platform::RUBY (most_specific_locked_platform != generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] end @@ -4,8 +4,6 @@ require_relative "shared_helpers" module Bundler class LockfileParser - include GemHelpers - class Position attr_reader :line, :column def initialize(line, column) @@ -157,7 +155,7 @@ module Bundler end @most_specific_locked_platform = @platforms.min_by do |bundle_platform| - platform_specificity_match(bundle_platform, local_platform) end @specs = @specs.values.sort_by!(&:full_name).each do |spec| spec.most_specific_locked_platform = @most_specific_locked_platform @@ -1,23 +1,42 @@ # frozen_string_literal: true -require_relative "gem_helpers" - module Bundler module MatchPlatform - include GemHelpers - def match_platform(p) - MatchPlatform.platforms_match?(platform, p) end - def self.platforms_match?(gemspec_platform, local_platform) - return true if gemspec_platform.nil? - return true if gemspec_platform == Gem::Platform::RUBY - return true if local_platform == gemspec_platform - gemspec_platform = Gem::Platform.new(gemspec_platform) - return true if gemspec_platform === local_platform - false end end end @@ -22,9 +22,9 @@ module Bundler @specs ||= if @candidates.nil? [] elsif platform - GemHelpers.select_best_platform_match(@candidates, platform, force_ruby: dep.force_ruby_platform) else - GemHelpers.select_best_local_platform_match(@candidates, force_ruby: dep.force_ruby_platform || dep.default_force_ruby_platform) end end @@ -14,8 +14,6 @@ module Bundler require_relative "resolver/root" require_relative "resolver/strategy" - include GemHelpers - def initialize(base, gem_version_promoter, most_specific_locked_platform = nil) @source_requirements = base.source_requirements @base = base @@ -273,7 +271,7 @@ module Bundler next groups if platform_specs.all?(&:empty?) end - ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY) ruby_group = Resolver::SpecGroup.new(ruby_specs) unless ruby_group.empty? @@ -30,7 +30,7 @@ module Bundler def platform_specs(specs) platforms.map do |platform| prefer_locked = @new_platforms.include?(platform) ? false : !unlock? - GemHelpers.select_best_platform_match(specs, platform, prefer_locked: prefer_locked) end end @@ -52,16 +52,123 @@ module Gem require "rubygems/platform" class Platform - JAVA = Gem::Platform.new("java") - MSWIN = Gem::Platform.new("mswin32") - MSWIN64 = Gem::Platform.new("mswin64") - MINGW = Gem::Platform.new("x86-mingw32") - X64_MINGW_LEGACY = Gem::Platform.new("x64-mingw32") - X64_MINGW = Gem::Platform.new("x64-mingw-ucrt") - UNIVERSAL_MINGW = Gem::Platform.new("universal-mingw") - WINDOWS = [MSWIN, MSWIN64, UNIVERSAL_MINGW].flatten.freeze - X64_LINUX = Gem::Platform.new("x86_64-linux") - X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl") end require "rubygems/specification" @@ -80,7 +187,6 @@ module Gem require_relative "match_platform" include ::Bundler::MatchMetadata - include ::Bundler::MatchPlatform attr_accessor :remote, :relative_loaded_from @@ -285,6 +391,11 @@ module Gem @ignored = missing_extensions? end end end require "rubygems/name_tuple" @@ -76,7 +76,7 @@ module Bundler new_platforms = all_platforms.select do |platform| next if platforms.include?(platform) - next unless GemHelpers.generic(platform) == Gem::Platform::RUBY complete_platform(platform) end @@ -183,7 +183,7 @@ module Bundler end def find_by_name_and_platform(name, platform) - @specs.detect {|spec| spec.name == name && spec.match_platform(platform) } end def specs_with_additional_variants_from(other) @@ -280,7 +280,7 @@ module Bundler valid_platform = lookup.all? do |_, specs| spec = specs.first matching_specs = spec.source.specs.search([spec.name, spec.version]) - platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s| valid?(s) end @@ -256,6 +256,13 @@ class Gem::BasicSpecification raise NotImplementedError end def raw_require_paths # :nodoc: raise NotImplementedError end @@ -255,4 +255,118 @@ class Gem::Platform # This will be replaced with Gem::Platform::local. CURRENT = "current" end @@ -1,57 +1,20 @@ # frozen_string_literal: true -RSpec.describe "Gem::Specification#match_platform" do it "does not match platforms other than the gem platform" do darwin = gem "lol", "1.0", "platform_specific-1.0-x86-darwin-10" - expect(darwin.match_platform(pl("java"))).to eq(false) end context "when platform is a string" do it "matches when platform is a string" do lazy_spec = Bundler::LazySpecification.new("lol", "1.0", "universal-mingw32") - expect(lazy_spec.match_platform(pl("x86-mingw32"))).to eq(true) - expect(lazy_spec.match_platform(pl("x64-mingw32"))).to eq(true) end end end -RSpec.describe "Bundler::GemHelpers#generic" do - include Bundler::GemHelpers - - it "converts non-windows platforms into ruby" do - expect(generic(pl("x86-darwin-10"))).to eq(pl("ruby")) - expect(generic(pl("ruby"))).to eq(pl("ruby")) - end - - it "converts java platform variants into java" do - expect(generic(pl("universal-java-17"))).to eq(pl("java")) - expect(generic(pl("java"))).to eq(pl("java")) - end - - it "converts mswin platform variants into x86-mswin32" do - expect(generic(pl("mswin32"))).to eq(pl("x86-mswin32")) - expect(generic(pl("i386-mswin32"))).to eq(pl("x86-mswin32")) - expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32")) - end - - it "converts 32-bit mingw platform variants into universal-mingw" do - expect(generic(pl("i386-mingw32"))).to eq(pl("universal-mingw")) - expect(generic(pl("x86-mingw32"))).to eq(pl("universal-mingw")) - end - - it "converts 64-bit mingw platform variants into universal-mingw" do - expect(generic(pl("x64-mingw32"))).to eq(pl("universal-mingw")) - end - - it "converts x64 mingw UCRT platform variants into universal-mingw" do - expect(generic(pl("x64-mingw-ucrt"))).to eq(pl("universal-mingw")) - end - - it "converts aarch64 mingw UCRT platform variants into universal-mingw" do - expect(generic(pl("aarch64-mingw-ucrt"))).to eq(pl("universal-mingw")) - end -end - RSpec.describe "Gem::SourceIndex#refresh!" do before do install_gemfile <<-G @@ -2,12 +2,18 @@ module Spec module Platforms - include Bundler::GemHelpers - def not_local generic_local_platform == Gem::Platform::RUBY ? "java" : Gem::Platform::RUBY end def local_tag if Gem.java_platform? :jruby @@ -61,7 +67,7 @@ module Spec end def generic_default_locked_platform - return unless generic_local_platform_is_ruby? Gem::Platform::RUBY end @@ -418,6 +418,9 @@ class Gem::TestCase < Test::Unit::TestCase @orig_hooks[name] = Gem.send(name).dup end @marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" @orig_loaded_features = $LOADED_FEATURES.dup end @@ -509,6 +509,171 @@ class TestGemPlatform < Gem::TestCase end end def assert_local_match(name) assert_match Gem::Platform.local, name end |