From 22857e2d63b700077b33398b0ae86201d06ebc79 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Mon, 30 Dec 2019 06:36:42 +0900 Subject: Improve platform specific gem resolution It resolves #6247. This changes includes the patches that add (RSpec) specs for this situation in #6247. If there is a platform specific gem but it can't be resolved available version, Bundler reports an error. For example, @index = build_index do gem "bar", "1.0.0" gem "foo", "1.0.0" gem "foo", "1.0.0", "x64-mingw32" do dep "bar", "< 1" end end dep "foo" platforms "x64-mingw32" raises an error because foo-1.0.0-x64-mingw32 requires bar<1 but there isn't bar<1. With this change, foo-1.0.0 (no x64-mingw32) is used as fallback. Because foo-1.0.0 doesn't depend on bar<1. --- lib/bundler/resolver.rb | 20 ++++++++++++++++---- lib/bundler/resolver/spec_group.rb | 9 +++++++-- spec/resolver/platform_spec.rb | 11 +++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index c7caf01c7d..9753efd088 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -146,7 +146,21 @@ module Bundler @gem_version_promoter.sort_versions(dependency, spec_groups) end end - search.select {|sg| sg.for?(platform) }.each {|sg| sg.activate_platform!(platform) } + selected_sgs = [] + search.each do |sg| + next unless sg.for?(platform) + sg.activate_platform!(platform) + if sg.spec(platform).platform != Gem::Platform::RUBY + sg_ruby = SpecGroup.new(sg.all_specs) + sg_ruby.ignores_bundler_dependencies = sg.ignores_bundler_dependencies + if sg_ruby.for?(Gem::Platform::RUBY) + sg_ruby.activate_platform!(Gem::Platform::RUBY) + selected_sgs << sg_ruby + end + end + selected_sgs << sg + end + selected_sgs end def index_for(dependency) @@ -183,9 +197,7 @@ module Bundler end def requirement_satisfied_by?(requirement, activated, spec) - return false unless requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) - spec.activate_platform!(requirement.__platform) if !@platforms || @platforms.include?(requirement.__platform) - true + requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) end def relevant_sources_for_vertex(vertex) diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index e5772eed81..f79797a3ca 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -5,10 +5,12 @@ module Bundler class SpecGroup include GemHelpers + attr_reader :all_specs attr_accessor :name, :version, :source attr_accessor :ignores_bundler_dependencies def initialize(all_specs) + @all_specs = all_specs raise ArgumentError, "cannot initialize with an empty value" unless exemplary_spec = all_specs.first @name = exemplary_spec.name @version = exemplary_spec.version @@ -37,9 +39,12 @@ module Bundler @activated_platforms << platform end + def spec(platform) + @specs[platform] + end + def for?(platform) - spec = @specs[platform] - !spec.nil? + !spec(platform).nil? end def to_s diff --git a/spec/resolver/platform_spec.rb b/spec/resolver/platform_spec.rb index 223320fe7b..9a97a2c67c 100644 --- a/spec/resolver/platform_spec.rb +++ b/spec/resolver/platform_spec.rb @@ -54,6 +54,17 @@ RSpec.describe "Resolving platform craziness" do should_resolve_as %w[foo-1.0.0] end + it "prefers the platform specific gem to the ruby version" do + @index = build_index do + gem "foo", "1.0.0" + gem "foo", "1.0.0", "x64-mingw32" + end + dep "foo" + platforms "x64-mingw32" + + should_resolve_as %w[foo-1.0.0-x64-mingw32] + end + it "takes the latest ruby gem if the platform specific gem doesn't match the required_ruby_version" do @index = build_index do gem "foo", "1.0.0" -- cgit v1.2.1