From 7c144368d7b3adb435e302bc5e14a1ac0b42654c Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Tue, 18 Aug 2020 21:11:32 +0200 Subject: [PATCH 1/8] Sort requirements in Gem::Requirement to succeed comparison with different order --- lib/rubygems/requirement.rb | 1 + test/rubygems/test_gem_requirement.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index afbf42b78962..ed9a193a5313 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -131,6 +131,7 @@ def initialize(*requirements) requirements = requirements.flatten requirements.compact! requirements.uniq! + requirements.sort! if requirements.empty? @requirements = [DefaultRequirement] diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index af9d8077010c..5d8c58ca3119 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -22,6 +22,8 @@ def test_equals2 refute_requirement_equal "~> 1.3", "~> 1.3.0" refute_requirement_equal "~> 1.3.0", "~> 1.3" + assert_requirement_equal ["> 2", "~> 1.3"], ["~> 1.3", "> 2"] + assert_requirement_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"] assert_requirement_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"] From 0d8c3377539f20d7f8473b51455767cbe4f001c0 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Tue, 18 Aug 2020 23:54:57 +0200 Subject: [PATCH 2/8] Fix tests that relied on order of requirements not been sorted --- test/rubygems/test_gem_specification.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 462f6df758ff..bb312861140c 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -2557,11 +2557,11 @@ def test_to_ruby_fancy if s.respond_to? :add_runtime_dependency then s.add_runtime_dependency(%q.freeze, [\"> 0.4\"]) s.add_runtime_dependency(%q.freeze, [\"> 0.0.0\"]) - s.add_runtime_dependency(%q.freeze, [\"> 0.4\", \"<= 0.6\"]) + s.add_runtime_dependency(%q.freeze, [\"<= 0.6\", \"> 0.4\"]) else s.add_dependency(%q.freeze, [\"> 0.4\"]) s.add_dependency(%q.freeze, [\"> 0.0.0\"]) - s.add_dependency(%q.freeze, [\"> 0.4\", \"<= 0.6\"]) + s.add_dependency(%q.freeze, [\"<= 0.6\", \"> 0.4\"]) end end SPEC @@ -2578,7 +2578,7 @@ def test_to_ruby_keeps_requirements_as_originally_specified s.add_dependency 'b', ['~> 1.0', '>= 1.0.0'] end - assert_includes spec.to_ruby, '"~> 1.0", ">= 1.0.0"' + assert_includes spec.to_ruby, '">= 1.0.0", "~> 1.0"' end def test_to_ruby_legacy From 94182189d8bca1006d9945b41559f16496b9824c Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Wed, 19 Aug 2020 00:23:14 +0200 Subject: [PATCH 3/8] Ensure sorting doesn't fail on incompatible objects --- lib/rubygems/requirement.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index ed9a193a5313..5df0c221901c 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -131,7 +131,7 @@ def initialize(*requirements) requirements = requirements.flatten requirements.compact! requirements.uniq! - requirements.sort! + requirements.sort_by!(&:to_s) if requirements.empty? @requirements = [DefaultRequirement] From e241b7401728fda12090d1a4e6823fd702ec1775 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Wed, 2 Sep 2020 23:02:16 +0200 Subject: [PATCH 4/8] Sort requirements only for comparison, preserve the original order otherwise --- lib/rubygems/requirement.rb | 9 ++++++--- test/rubygems/test_gem_requirement.rb | 2 +- test/rubygems/test_gem_specification.rb | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 5df0c221901c..55a52d750d0a 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -131,7 +131,6 @@ def initialize(*requirements) requirements = requirements.flatten requirements.compact! requirements.uniq! - requirements.sort_by!(&:to_s) if requirements.empty? @requirements = [DefaultRequirement] @@ -271,7 +270,7 @@ def ==(other) # :nodoc: return unless Gem::Requirement === other # An == check is always necessary - return false unless requirements == other.requirements + return false unless _sorted_requirements == other._sorted_requirements # An == check is sufficient unless any requirements use ~> return true unless _tilde_requirements.any? @@ -283,8 +282,12 @@ def ==(other) # :nodoc: protected + def _sorted_requirements + requirements.sort_by(&:to_s) + end + def _tilde_requirements - requirements.select {|r| r.first == "~>" } + _sorted_requirements.select {|r| r.first == "~>" } end private diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index 5d8c58ca3119..20127a1e1533 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -22,7 +22,7 @@ def test_equals2 refute_requirement_equal "~> 1.3", "~> 1.3.0" refute_requirement_equal "~> 1.3.0", "~> 1.3" - assert_requirement_equal ["> 2", "~> 1.3"], ["~> 1.3", "> 2"] + assert_requirement_equal ["> 2", "~> 1.3", "~> 1.3.1"], ["~> 1.3.1", "~> 1.3", "> 2"] assert_requirement_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"] assert_requirement_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"] diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index bb312861140c..462f6df758ff 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -2557,11 +2557,11 @@ def test_to_ruby_fancy if s.respond_to? :add_runtime_dependency then s.add_runtime_dependency(%q.freeze, [\"> 0.4\"]) s.add_runtime_dependency(%q.freeze, [\"> 0.0.0\"]) - s.add_runtime_dependency(%q.freeze, [\"<= 0.6\", \"> 0.4\"]) + s.add_runtime_dependency(%q.freeze, [\"> 0.4\", \"<= 0.6\"]) else s.add_dependency(%q.freeze, [\"> 0.4\"]) s.add_dependency(%q.freeze, [\"> 0.0.0\"]) - s.add_dependency(%q.freeze, [\"<= 0.6\", \"> 0.4\"]) + s.add_dependency(%q.freeze, [\"> 0.4\", \"<= 0.6\"]) end end SPEC @@ -2578,7 +2578,7 @@ def test_to_ruby_keeps_requirements_as_originally_specified s.add_dependency 'b', ['~> 1.0', '>= 1.0.0'] end - assert_includes spec.to_ruby, '">= 1.0.0", "~> 1.0"' + assert_includes spec.to_ruby, '"~> 1.0", ">= 1.0.0"' end def test_to_ruby_legacy From a1b8dac2542f7bc3d867f8d187fe7eb815b64331 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Thu, 3 Sep 2020 22:44:04 +0200 Subject: [PATCH 5/8] Memoize _sorted_requirements and _tilde_requirements which can be used multiple times during same comparison --- lib/rubygems/requirement.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 55a52d750d0a..430060e2ff58 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -283,11 +283,11 @@ def ==(other) # :nodoc: protected def _sorted_requirements - requirements.sort_by(&:to_s) + @_sorted_requirements ||= requirements.sort_by(&:to_s) end def _tilde_requirements - _sorted_requirements.select {|r| r.first == "~>" } + @_tilde_requirements ||= _sorted_requirements.select {|r| r.first == "~>" } end private From 53c937762ab4d666dc97d3fdd48485aae7c9c32d Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Sat, 5 Sep 2020 00:14:51 +0200 Subject: [PATCH 6/8] Add bundler test for gems with platform-specific dependency having different requirements order --- bundler/spec/install/gemfile/platform_spec.rb | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/bundler/spec/install/gemfile/platform_spec.rb b/bundler/spec/install/gemfile/platform_spec.rb index dd58aef29b1d..199d61f4d868 100644 --- a/bundler/spec/install/gemfile/platform_spec.rb +++ b/bundler/spec/install/gemfile/platform_spec.rb @@ -256,6 +256,35 @@ expect(the_bundle).not_to include_gem "CFPropertyList" end + it "works with gems with platform-specific dependency having different requirements order", :rubygems => ">= 3.2.0.rc.2" do + simulate_platform x64_mac + + update_repo2 do + build_gem "fspath", "3" + build_gem "image_optim_pack", "1.2.3" do |s| + s.add_runtime_dependency "fspath", ">= 2.1", "< 4" + end + build_gem "image_optim_pack", "1.2.3" do |s| + s.platform = "universal-darwin" + s.add_runtime_dependency "fspath", "< 4", ">= 2.1" + end + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + G + + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + + gem "image_optim_pack" + G + + expect(err).not_to include "Unable to use the platform-specific" + + expect(the_bundle).to include_gem "image_optim_pack 1.2.3 universal-darwin" + end + it "fetches gems again after changing the version of Ruby" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" From c33914fe2b8c0ebba1f0b4b72f04152e640254c5 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Sat, 5 Sep 2020 19:22:45 +0200 Subject: [PATCH 7/8] Backport the fix to bundler to work with older versions of rubygems --- bundler/lib/bundler/rubygems_ext.rb | 28 +++++++++++++++++++ bundler/spec/install/gemfile/platform_spec.rb | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/bundler/lib/bundler/rubygems_ext.rb b/bundler/lib/bundler/rubygems_ext.rb index 1d3bdc5565be..05c3a55c0a7d 100644 --- a/bundler/lib/bundler/rubygems_ext.rb +++ b/bundler/lib/bundler/rubygems_ext.rb @@ -129,6 +129,34 @@ def to_lock end end + unless Gem::Requirement.new("> 1", "< 2") == Gem::Requirement.new("< 2", "> 1") + class Requirement + module OrderIndependentComparison + def ==(other) + if _requirements_sorted? && other._requirements_sorted? + super + else + _with_sorted_requirements == other._with_sorted_requirements + end + end + + protected + + def _requirements_sorted? + return @_are_requirements_sorted if defined?(@_are_requirements_sorted) + strings = as_list + @_are_requirements_sorted = strings == strings.sort + end + + def _with_sorted_requirements + @_with_sorted_requirements ||= _requirements_sorted? ? self : self.class.new(as_list.sort) + end + end + + prepend OrderIndependentComparison + end + end + class Platform JAVA = Gem::Platform.new("java") unless defined?(JAVA) MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN) diff --git a/bundler/spec/install/gemfile/platform_spec.rb b/bundler/spec/install/gemfile/platform_spec.rb index 199d61f4d868..545b44507b4e 100644 --- a/bundler/spec/install/gemfile/platform_spec.rb +++ b/bundler/spec/install/gemfile/platform_spec.rb @@ -256,7 +256,7 @@ expect(the_bundle).not_to include_gem "CFPropertyList" end - it "works with gems with platform-specific dependency having different requirements order", :rubygems => ">= 3.2.0.rc.2" do + it "works with gems with platform-specific dependency having different requirements order" do simulate_platform x64_mac update_repo2 do From 66fa8e024bc30c1eda86e4fa33dd7f0be8d558e9 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Sun, 6 Sep 2020 15:17:14 +0200 Subject: [PATCH 8/8] Add comment to be able to find code to delete when support for rubygems < 3.2.0 is dropped --- bundler/lib/bundler/rubygems_ext.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/bundler/lib/bundler/rubygems_ext.rb b/bundler/lib/bundler/rubygems_ext.rb index 05c3a55c0a7d..82816d24fab2 100644 --- a/bundler/lib/bundler/rubygems_ext.rb +++ b/bundler/lib/bundler/rubygems_ext.rb @@ -129,6 +129,7 @@ def to_lock end end + # comparison is done order independently since rubygems 3.2.0.rc.2 unless Gem::Requirement.new("> 1", "< 2") == Gem::Requirement.new("< 2", "> 1") class Requirement module OrderIndependentComparison