From 1056489ea3e1c3f201936666b2725bfc142bb0f2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 28 Apr 2022 17:15:43 +0900 Subject: [PATCH] Merge https://github.com/rubygems/rubygems/commit/3f7d0352e84b29d4a2d4cd93b31e5ebdb5f79cc6 --- lib/bundler/definition.rb | 19 +- lib/bundler/resolver.rb | 83 ++- lib/bundler/resolver/spec_group.rb | 4 +- lib/bundler/ruby_version.rb | 13 - lib/bundler/rubygems_ext.rb | 17 + lib/bundler/source/metadata.rb | 2 +- .../lib/molinillo/dependency_graph.rb | 2 +- .../vendor/molinillo/lib/molinillo/errors.rb | 60 +- .../molinillo/lib/molinillo/gem_metadata.rb | 2 +- lib/bundler/vendor/tsort/lib/tsort.rb | 643 +++++++++--------- lib/rubygems.rb | 4 +- lib/rubygems/optparse/lib/optparse.rb | 77 ++- lib/rubygems/optparse/lib/optparse/ac.rb | 2 +- lib/rubygems/optparse/lib/optparse/date.rb | 2 +- lib/rubygems/optparse/lib/optparse/kwargs.rb | 2 +- .../optparse/lib/optparse/shellwords.rb | 2 +- lib/rubygems/optparse/lib/optparse/time.rb | 2 +- lib/rubygems/optparse/lib/optparse/uri.rb | 2 +- lib/rubygems/specification.rb | 10 + spec/bundler/bundler/ruby_version_spec.rb | 26 - spec/bundler/commands/lock_spec.rb | 34 + spec/bundler/commands/update_spec.rb | 4 + spec/bundler/install/gems/resolving_spec.rb | 37 +- spec/bundler/install/gems/standalone_spec.rb | 2 +- test/rubygems/helper.rb | 13 +- test/rubygems/test_gem.rb | 16 - .../custom_name/Cargo.lock | 8 +- .../custom_name/Cargo.toml | 2 +- .../rust_ruby_example/Cargo.lock | 8 +- .../rust_ruby_example/Cargo.toml | 2 +- .../test_gem_resolver_installer_set.rb | 2 +- 31 files changed, 613 insertions(+), 489 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 09be2db68c..c1d865d049 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -736,30 +736,13 @@ module Bundler def metadata_dependencies @metadata_dependencies ||= begin - ruby_versions = ruby_version_requirements(@ruby_version) [ - Dependency.new("Ruby\0", ruby_versions), + Dependency.new("Ruby\0", RubyVersion.system.gem_version), Dependency.new("RubyGems\0", Gem::VERSION), ] end end - def ruby_version_requirements(ruby_version) - return [] unless ruby_version - if ruby_version.patchlevel - [ruby_version.to_gem_version_with_patchlevel] - else - ruby_version.versions.map do |version| - requirement = Gem::Requirement.new(version) - if requirement.exact? - "~> #{version}.0" - else - requirement - end - end - end - end - def expand_dependencies(dependencies, remote = false) deps = [] dependencies.each do |dep| diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index be0751d9d2..433a5e53e1 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -312,29 +312,66 @@ module Bundler e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty? - solver_name = "Bundler" - possibility_type = "gem" e.message_with_trees( - :solver_name => solver_name, - :possibility_type => possibility_type, - :reduce_trees => lambda do |trees| + :full_message_for_conflict => lambda do |name, conflict| + o = if name.end_with?("\0") + String.new("Bundler found conflicting requirements for the #{name} version:") + else + String.new("Bundler could not find compatible versions for gem \"#{name}\":") + end + o << %(\n) + if conflict.locked_requirement + o << %( In snapshot (#{name_for_locking_dependency_source}):\n) + o << %( #{SharedHelpers.pretty_dependency(conflict.locked_requirement)}\n) + o << %(\n) + end + o << %( In #{name_for_explicit_dependency_source}:\n) + trees = conflict.requirement_trees + # called first, because we want to reduce the amount of work required to find maximal empty sets trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } # bail out if tree size is too big for Array#combination to make any sense - return trees if trees.size > 15 - maximal = 1.upto(trees.size).map do |size| - trees.map(&:last).flatten(1).combination(size).to_a - end.flatten(1).select do |deps| - Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) - end.min_by(&:size) + if trees.size <= 15 + maximal = 1.upto(trees.size).map do |size| + trees.map(&:last).flatten(1).combination(size).to_a + end.flatten(1).select do |deps| + Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) + end.min_by(&:size) - trees.reject! {|t| !maximal.include?(t.last) } if maximal + trees.reject! {|t| !maximal.include?(t.last) } if maximal + + trees.sort_by! {|t| t.reverse.map(&:name) } + end + + metadata_requirements = {} + + o << trees.map do |tree| + t = "".dup + depth = 2 + + base_tree = tree.first + base_tree_name = base_tree.name + + if base_tree_name.end_with?("\0") + metadata_requirements[base_tree_name] = base_tree + t = nil + else + tree.each do |req| + t << " " * depth << SharedHelpers.pretty_dependency(req) + unless tree.last == req + if spec = conflict.activated_by_name[req.name] + t << %( was resolved to #{spec.version}, which) + end + t << %( depends on) + end + t << %(\n) + depth += 1 + end + end + t + end.compact.join("\n") - trees.sort_by {|t| t.reverse.map(&:name) } - end, - :printable_requirement => lambda {|req| SharedHelpers.pretty_dependency(req) }, - :additional_message_for_conflict => lambda do |o, name, conflict| if name == "bundler" o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION})) @@ -355,11 +392,13 @@ module Bundler o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n" end end + elsif name.end_with?("\0") + o << %(\n Current #{name} version:\n #{SharedHelpers.pretty_dependency(metadata_requirements[name])}\n\n) elsif conflict.locked_requirement o << "\n" o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\n) o << %(the gems in your Gemfile, which may resolve the conflict.\n) - elsif !conflict.existing && !name.end_with?("\0") + elsif !conflict.existing o << "\n" relevant_source = conflict.requirement.source || source_for(name) @@ -372,14 +411,8 @@ module Bundler o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message) end - end, - :version_for_spec => lambda {|spec| spec.version }, - :incompatible_version_message_for_conflict => lambda do |name, _conflict| - if name.end_with?("\0") - %(#{solver_name} found conflicting requirements for the #{name} version:) - else - %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":) - end + + o end ) end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 232520de77..18ab5a52a3 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -97,10 +97,10 @@ module Bundler spec = @specs[platform].first return [] if spec.is_a?(LazySpecification) dependencies = [] - if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? + unless spec.required_ruby_version.none? dependencies << DepProxy.get_proxy(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) end - if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none? + unless spec.required_rubygems_version.none? dependencies << DepProxy.get_proxy(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) end dependencies diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb index d3b7963920..3f51cf4528 100644 --- a/lib/bundler/ruby_version.rb +++ b/lib/bundler/ruby_version.rb @@ -110,19 +110,6 @@ module Bundler @ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version) end - def to_gem_version_with_patchlevel - @gem_version_with_patch ||= begin - Gem::Version.create("#{@gem_version}.#{@patchlevel}") - rescue ArgumentError - @gem_version - end - end - - def exact? - return @exact if defined?(@exact) - @exact = versions.all? {|v| Gem::Requirement.create(v).exact? } - end - private def matches?(requirements, version) diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 78422ceb76..b0e35e005e 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -67,6 +67,23 @@ module Gem full_gem_path end + unless const_defined?(:LATEST_RUBY_WITHOUT_PATCH_VERSIONS) + LATEST_RUBY_WITHOUT_PATCH_VERSIONS = Gem::Version.new("2.1") + + alias_method :rg_required_ruby_version=, :required_ruby_version= + def required_ruby_version=(req) + self.rg_required_ruby_version = req + + @required_ruby_version.requirements.map! do |op, v| + if v >= LATEST_RUBY_WITHOUT_PATCH_VERSIONS && v.release.segments.size == 4 + [op == "~>" ? "=" : op, Gem::Version.new(v.segments.tap {|s| s.delete_at(3) }.join("."))] + else + [op, v] + end + end + end + end + def groups @groups ||= [] end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 524afd750a..185fe70824 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -5,7 +5,7 @@ module Bundler class Metadata < Source def specs @specs ||= Index.build do |idx| - idx << Gem::Specification.new("Ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) + idx << Gem::Specification.new("Ruby\0", RubyVersion.system.gem_version) idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s| s.required_rubygems_version = Gem::Requirement.default end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb index 10a25d2f08..4d577213b9 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb @@ -32,7 +32,7 @@ module Bundler::Molinillo # all belong to the same graph. # @return [Array] The sorted vertices. def self.tsort(vertices) - TSort.tsort( + Bundler::TSort.tsort( lambda { |b| vertices.each(&b) }, lambda { |v, &b| (v.successors & vertices).each(&b) } ) diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb index e210202b69..8c8cafb447 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb @@ -107,36 +107,42 @@ module Bundler::Molinillo end end - conflicts.sort.reduce(''.dup) do |o, (name, conflict)| - o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n" - if conflict.locked_requirement - o << %( In snapshot (#{name_for_locking_dependency_source}):\n) - o << %( #{printable_requirement.call(conflict.locked_requirement)}\n) - o << %(\n) - end - o << %( In #{name_for_explicit_dependency_source}:\n) - trees = reduce_trees.call(conflict.requirement_trees) - - o << trees.map do |tree| - t = ''.dup - depth = 2 - tree.each do |req| - t << ' ' * depth << printable_requirement.call(req) - unless tree.last == req - if spec = conflict.activated_by_name[name_for(req)] - t << %( was resolved to #{version_for_spec.call(spec)}, which) - end - t << %( depends on) - end - t << %(\n) - depth += 1 + full_message_for_conflict = opts.delete(:full_message_for_conflict) do + proc do |name, conflict| + o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n" + if conflict.locked_requirement + o << %( In snapshot (#{name_for_locking_dependency_source}):\n) + o << %( #{printable_requirement.call(conflict.locked_requirement)}\n) + o << %(\n) end - t - end.join("\n") + o << %( In #{name_for_explicit_dependency_source}:\n) + trees = reduce_trees.call(conflict.requirement_trees) - additional_message_for_conflict.call(o, name, conflict) + o << trees.map do |tree| + t = ''.dup + depth = 2 + tree.each do |req| + t << ' ' * depth << printable_requirement.call(req) + unless tree.last == req + if spec = conflict.activated_by_name[name_for(req)] + t << %( was resolved to #{version_for_spec.call(spec)}, which) + end + t << %( depends on) + end + t << %(\n) + depth += 1 + end + t + end.join("\n") - o + additional_message_for_conflict.call(o, name, conflict) + + o + end + end + + conflicts.sort.reduce(''.dup) do |o, (name, conflict)| + o << full_message_for_conflict.call(name, conflict) end.strip end end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb index e13a781a50..a0cfc21672 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb @@ -2,5 +2,5 @@ module Bundler::Molinillo # The version of Bundler::Molinillo. - VERSION = '0.7.0'.freeze + VERSION = '0.8.0'.freeze end diff --git a/lib/bundler/vendor/tsort/lib/tsort.rb b/lib/bundler/vendor/tsort/lib/tsort.rb index 8454583295..4a0e1a4e25 100644 --- a/lib/bundler/vendor/tsort/lib/tsort.rb +++ b/lib/bundler/vendor/tsort/lib/tsort.rb @@ -6,24 +6,24 @@ # # -# TSort implements topological sorting using Tarjan's algorithm for +# Bundler::TSort implements topological sorting using Tarjan's algorithm for # strongly connected components. # -# TSort is designed to be able to be used with any object which can be +# Bundler::TSort is designed to be able to be used with any object which can be # interpreted as a directed graph. # -# TSort requires two methods to interpret an object as a graph, +# Bundler::TSort requires two methods to interpret an object as a graph, # tsort_each_node and tsort_each_child. # # * tsort_each_node is used to iterate for all nodes over a graph. # * tsort_each_child is used to iterate for child nodes of a given node. # # The equality of nodes are defined by eql? and hash since -# TSort uses Hash internally. +# Bundler::TSort uses Hash internally. # # == A Simple Example # -# The following example demonstrates how to mix the TSort module into an +# The following example demonstrates how to mix the Bundler::TSort module into an # existing class (in this case, Hash). Here, we're treating each key in # the hash as a node in the graph, and so we simply alias the required # #tsort_each_node method to Hash's #each_key method. For each key in the @@ -32,10 +32,10 @@ # method, which fetches the array of child nodes and then iterates over that # array using the user-supplied block. # -# require 'tsort' +# require 'bundler/vendor/tsort/lib/tsort' # # class Hash -# include TSort +# include Bundler::TSort # alias tsort_each_node each_key # def tsort_each_child(node, &block) # fetch(node).each(&block) @@ -52,7 +52,7 @@ # # A very simple `make' like tool can be implemented as follows: # -# require 'tsort' +# require 'bundler/vendor/tsort/lib/tsort' # # class Make # def initialize @@ -70,7 +70,7 @@ # each_strongly_connected_component_from(target) {|ns| # if ns.length != 1 # fs = ns.delete_if {|n| Array === n} -# raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") +# raise Bundler::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}") # end # n = ns.first # if Array === n @@ -93,7 +93,7 @@ # def tsort_each_child(node, &block) # @dep[node].each(&block) # end -# include TSort +# include Bundler::TSort # end # # def command(arg) @@ -120,334 +120,333 @@ # R. E. Tarjan, "Depth First Search and Linear Graph Algorithms", # SIAM Journal on Computing, Vol. 1, No. 2, pp. 146-160, June 1972. # -module Bundler - module TSort - class Cyclic < StandardError - end - # Returns a topologically sorted array of nodes. - # The array is sorted from children to parents, i.e. - # the first element has no child and the last node has no parent. - # - # If there is a cycle, TSort::Cyclic is raised. - # - # class G - # include TSort - # def initialize(g) - # @g = g - # end - # def tsort_each_child(n, &b) @g[n].each(&b) end - # def tsort_each_node(&b) @g.each_key(&b) end - # end - # - # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) - # p graph.tsort #=> [4, 2, 3, 1] - # - # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) - # p graph.tsort # raises TSort::Cyclic - # - def tsort - each_node = method(:tsort_each_node) - each_child = method(:tsort_each_child) - TSort.tsort(each_node, each_child) - end +module Bundler::TSort + class Cyclic < StandardError + end - # Returns a topologically sorted array of nodes. - # The array is sorted from children to parents, i.e. - # the first element has no child and the last node has no parent. - # - # The graph is represented by _each_node_ and _each_child_. - # _each_node_ should have +call+ method which yields for each node in the graph. - # _each_child_ should have +call+ method which takes a node argument and yields for each child node. - # - # If there is a cycle, TSort::Cyclic is raised. - # - # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1] - # - # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # p TSort.tsort(each_node, each_child) # raises TSort::Cyclic - # - def TSort.tsort(each_node, each_child) - TSort.tsort_each(each_node, each_child).to_a - end + # Returns a topologically sorted array of nodes. + # The array is sorted from children to parents, i.e. + # the first element has no child and the last node has no parent. + # + # If there is a cycle, Bundler::TSort::Cyclic is raised. + # + # class G + # include Bundler::TSort + # def initialize(g) + # @g = g + # end + # def tsort_each_child(n, &b) @g[n].each(&b) end + # def tsort_each_node(&b) @g.each_key(&b) end + # end + # + # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) + # p graph.tsort #=> [4, 2, 3, 1] + # + # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) + # p graph.tsort # raises Bundler::TSort::Cyclic + # + def tsort + each_node = method(:tsort_each_node) + each_child = method(:tsort_each_child) + Bundler::TSort.tsort(each_node, each_child) + end - # The iterator version of the #tsort method. - # obj.tsort_each is similar to obj.tsort.each, but - # modification of _obj_ during the iteration may lead to unexpected results. - # - # #tsort_each returns +nil+. - # If there is a cycle, TSort::Cyclic is raised. - # - # class G - # include TSort - # def initialize(g) - # @g = g - # end - # def tsort_each_child(n, &b) @g[n].each(&b) end - # def tsort_each_node(&b) @g.each_key(&b) end - # end - # - # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) - # graph.tsort_each {|n| p n } - # #=> 4 - # # 2 - # # 3 - # # 1 - # - def tsort_each(&block) # :yields: node - each_node = method(:tsort_each_node) - each_child = method(:tsort_each_child) - TSort.tsort_each(each_node, each_child, &block) - end + # Returns a topologically sorted array of nodes. + # The array is sorted from children to parents, i.e. + # the first element has no child and the last node has no parent. + # + # The graph is represented by _each_node_ and _each_child_. + # _each_node_ should have +call+ method which yields for each node in the graph. + # _each_child_ should have +call+ method which takes a node argument and yields for each child node. + # + # If there is a cycle, Bundler::TSort::Cyclic is raised. + # + # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # p Bundler::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1] + # + # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # p Bundler::TSort.tsort(each_node, each_child) # raises Bundler::TSort::Cyclic + # + def self.tsort(each_node, each_child) + tsort_each(each_node, each_child).to_a + end - # The iterator version of the TSort.tsort method. - # - # The graph is represented by _each_node_ and _each_child_. - # _each_node_ should have +call+ method which yields for each node in the graph. - # _each_child_ should have +call+ method which takes a node argument and yields for each child node. - # - # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # TSort.tsort_each(each_node, each_child) {|n| p n } - # #=> 4 - # # 2 - # # 3 - # # 1 - # - def TSort.tsort_each(each_node, each_child) # :yields: node - return to_enum(__method__, each_node, each_child) unless block_given? + # The iterator version of the #tsort method. + # obj.tsort_each is similar to obj.tsort.each, but + # modification of _obj_ during the iteration may lead to unexpected results. + # + # #tsort_each returns +nil+. + # If there is a cycle, Bundler::TSort::Cyclic is raised. + # + # class G + # include Bundler::TSort + # def initialize(g) + # @g = g + # end + # def tsort_each_child(n, &b) @g[n].each(&b) end + # def tsort_each_node(&b) @g.each_key(&b) end + # end + # + # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) + # graph.tsort_each {|n| p n } + # #=> 4 + # # 2 + # # 3 + # # 1 + # + def tsort_each(&block) # :yields: node + each_node = method(:tsort_each_node) + each_child = method(:tsort_each_child) + Bundler::TSort.tsort_each(each_node, each_child, &block) + end - TSort.each_strongly_connected_component(each_node, each_child) {|component| - if component.size == 1 - yield component.first - else - raise Cyclic.new("topological sort failed: #{component.inspect}") - end - } - end + # The iterator version of the Bundler::TSort.tsort method. + # + # The graph is represented by _each_node_ and _each_child_. + # _each_node_ should have +call+ method which yields for each node in the graph. + # _each_child_ should have +call+ method which takes a node argument and yields for each child node. + # + # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # Bundler::TSort.tsort_each(each_node, each_child) {|n| p n } + # #=> 4 + # # 2 + # # 3 + # # 1 + # + def self.tsort_each(each_node, each_child) # :yields: node + return to_enum(__method__, each_node, each_child) unless block_given? - # Returns strongly connected components as an array of arrays of nodes. - # The array is sorted from children to parents. - # Each elements of the array represents a strongly connected component. - # - # class G - # include TSort - # def initialize(g) - # @g = g - # end - # def tsort_each_child(n, &b) @g[n].each(&b) end - # def tsort_each_node(&b) @g.each_key(&b) end - # end - # - # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) - # p graph.strongly_connected_components #=> [[4], [2], [3], [1]] - # - # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) - # p graph.strongly_connected_components #=> [[4], [2, 3], [1]] - # - def strongly_connected_components - each_node = method(:tsort_each_node) - each_child = method(:tsort_each_child) - TSort.strongly_connected_components(each_node, each_child) - end + each_strongly_connected_component(each_node, each_child) {|component| + if component.size == 1 + yield component.first + else + raise Cyclic.new("topological sort failed: #{component.inspect}") + end + } + end - # Returns strongly connected components as an array of arrays of nodes. - # The array is sorted from children to parents. - # Each elements of the array represents a strongly connected component. - # - # The graph is represented by _each_node_ and _each_child_. - # _each_node_ should have +call+ method which yields for each node in the graph. - # _each_child_ should have +call+ method which takes a node argument and yields for each child node. - # - # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # p TSort.strongly_connected_components(each_node, each_child) - # #=> [[4], [2], [3], [1]] - # - # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # p TSort.strongly_connected_components(each_node, each_child) - # #=> [[4], [2, 3], [1]] - # - def TSort.strongly_connected_components(each_node, each_child) - TSort.each_strongly_connected_component(each_node, each_child).to_a - end + # Returns strongly connected components as an array of arrays of nodes. + # The array is sorted from children to parents. + # Each elements of the array represents a strongly connected component. + # + # class G + # include Bundler::TSort + # def initialize(g) + # @g = g + # end + # def tsort_each_child(n, &b) @g[n].each(&b) end + # def tsort_each_node(&b) @g.each_key(&b) end + # end + # + # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) + # p graph.strongly_connected_components #=> [[4], [2], [3], [1]] + # + # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) + # p graph.strongly_connected_components #=> [[4], [2, 3], [1]] + # + def strongly_connected_components + each_node = method(:tsort_each_node) + each_child = method(:tsort_each_child) + Bundler::TSort.strongly_connected_components(each_node, each_child) + end - # The iterator version of the #strongly_connected_components method. - # obj.each_strongly_connected_component is similar to - # obj.strongly_connected_components.each, but - # modification of _obj_ during the iteration may lead to unexpected results. - # - # #each_strongly_connected_component returns +nil+. - # - # class G - # include TSort - # def initialize(g) - # @g = g - # end - # def tsort_each_child(n, &b) @g[n].each(&b) end - # def tsort_each_node(&b) @g.each_key(&b) end - # end - # - # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) - # graph.each_strongly_connected_component {|scc| p scc } - # #=> [4] - # # [2] - # # [3] - # # [1] - # - # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) - # graph.each_strongly_connected_component {|scc| p scc } - # #=> [4] - # # [2, 3] - # # [1] - # - def each_strongly_connected_component(&block) # :yields: nodes - each_node = method(:tsort_each_node) - each_child = method(:tsort_each_child) - TSort.each_strongly_connected_component(each_node, each_child, &block) - end + # Returns strongly connected components as an array of arrays of nodes. + # The array is sorted from children to parents. + # Each elements of the array represents a strongly connected component. + # + # The graph is represented by _each_node_ and _each_child_. + # _each_node_ should have +call+ method which yields for each node in the graph. + # _each_child_ should have +call+ method which takes a node argument and yields for each child node. + # + # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # p Bundler::TSort.strongly_connected_components(each_node, each_child) + # #=> [[4], [2], [3], [1]] + # + # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # p Bundler::TSort.strongly_connected_components(each_node, each_child) + # #=> [[4], [2, 3], [1]] + # + def self.strongly_connected_components(each_node, each_child) + each_strongly_connected_component(each_node, each_child).to_a + end - # The iterator version of the TSort.strongly_connected_components method. - # - # The graph is represented by _each_node_ and _each_child_. - # _each_node_ should have +call+ method which yields for each node in the graph. - # _each_child_ should have +call+ method which takes a node argument and yields for each child node. - # - # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } - # #=> [4] - # # [2] - # # [3] - # # [1] - # - # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} - # each_node = lambda {|&b| g.each_key(&b) } - # each_child = lambda {|n, &b| g[n].each(&b) } - # TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } - # #=> [4] - # # [2, 3] - # # [1] - # - def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes - return to_enum(__method__, each_node, each_child) unless block_given? + # The iterator version of the #strongly_connected_components method. + # obj.each_strongly_connected_component is similar to + # obj.strongly_connected_components.each, but + # modification of _obj_ during the iteration may lead to unexpected results. + # + # #each_strongly_connected_component returns +nil+. + # + # class G + # include Bundler::TSort + # def initialize(g) + # @g = g + # end + # def tsort_each_child(n, &b) @g[n].each(&b) end + # def tsort_each_node(&b) @g.each_key(&b) end + # end + # + # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) + # graph.each_strongly_connected_component {|scc| p scc } + # #=> [4] + # # [2] + # # [3] + # # [1] + # + # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) + # graph.each_strongly_connected_component {|scc| p scc } + # #=> [4] + # # [2, 3] + # # [1] + # + def each_strongly_connected_component(&block) # :yields: nodes + each_node = method(:tsort_each_node) + each_child = method(:tsort_each_child) + Bundler::TSort.each_strongly_connected_component(each_node, each_child, &block) + end - id_map = {} - stack = [] - each_node.call {|node| - unless id_map.include? node - TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c| + # The iterator version of the Bundler::TSort.strongly_connected_components method. + # + # The graph is represented by _each_node_ and _each_child_. + # _each_node_ should have +call+ method which yields for each node in the graph. + # _each_child_ should have +call+ method which takes a node argument and yields for each child node. + # + # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # Bundler::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } + # #=> [4] + # # [2] + # # [3] + # # [1] + # + # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} + # each_node = lambda {|&b| g.each_key(&b) } + # each_child = lambda {|n, &b| g[n].each(&b) } + # Bundler::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } + # #=> [4] + # # [2, 3] + # # [1] + # + def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes + return to_enum(__method__, each_node, each_child) unless block_given? + + id_map = {} + stack = [] + each_node.call {|node| + unless id_map.include? node + each_strongly_connected_component_from(node, each_child, id_map, stack) {|c| + yield c + } + end + } + nil + end + + # Iterates over strongly connected component in the subgraph reachable from + # _node_. + # + # Return value is unspecified. + # + # #each_strongly_connected_component_from doesn't call #tsort_each_node. + # + # class G + # include Bundler::TSort + # def initialize(g) + # @g = g + # end + # def tsort_each_child(n, &b) @g[n].each(&b) end + # def tsort_each_node(&b) @g.each_key(&b) end + # end + # + # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) + # graph.each_strongly_connected_component_from(2) {|scc| p scc } + # #=> [4] + # # [2] + # + # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) + # graph.each_strongly_connected_component_from(2) {|scc| p scc } + # #=> [4] + # # [2, 3] + # + def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes + Bundler::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block) + end + + # Iterates over strongly connected components in a graph. + # The graph is represented by _node_ and _each_child_. + # + # _node_ is the first node. + # _each_child_ should have +call+ method which takes a node argument + # and yields for each child node. + # + # Return value is unspecified. + # + # #Bundler::TSort.each_strongly_connected_component_from is a class method and + # it doesn't need a class to represent a graph which includes Bundler::TSort. + # + # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} + # each_child = lambda {|n, &b| graph[n].each(&b) } + # Bundler::TSort.each_strongly_connected_component_from(1, each_child) {|scc| + # p scc + # } + # #=> [4] + # # [2, 3] + # # [1] + # + def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes + return to_enum(__method__, node, each_child, id_map, stack) unless block_given? + + minimum_id = node_id = id_map[node] = id_map.size + stack_length = stack.length + stack << node + + each_child.call(node) {|child| + if id_map.include? child + child_id = id_map[child] + minimum_id = child_id if child_id && child_id < minimum_id + else + sub_minimum_id = + each_strongly_connected_component_from(child, each_child, id_map, stack) {|c| yield c } - end - } - nil - end - - # Iterates over strongly connected component in the subgraph reachable from - # _node_. - # - # Return value is unspecified. - # - # #each_strongly_connected_component_from doesn't call #tsort_each_node. - # - # class G - # include TSort - # def initialize(g) - # @g = g - # end - # def tsort_each_child(n, &b) @g[n].each(&b) end - # def tsort_each_node(&b) @g.each_key(&b) end - # end - # - # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) - # graph.each_strongly_connected_component_from(2) {|scc| p scc } - # #=> [4] - # # [2] - # - # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}) - # graph.each_strongly_connected_component_from(2) {|scc| p scc } - # #=> [4] - # # [2, 3] - # - def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes - TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block) - end - - # Iterates over strongly connected components in a graph. - # The graph is represented by _node_ and _each_child_. - # - # _node_ is the first node. - # _each_child_ should have +call+ method which takes a node argument - # and yields for each child node. - # - # Return value is unspecified. - # - # #TSort.each_strongly_connected_component_from is a class method and - # it doesn't need a class to represent a graph which includes TSort. - # - # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} - # each_child = lambda {|n, &b| graph[n].each(&b) } - # TSort.each_strongly_connected_component_from(1, each_child) {|scc| - # p scc - # } - # #=> [4] - # # [2, 3] - # # [1] - # - def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes - return to_enum(__method__, node, each_child, id_map, stack) unless block_given? - - minimum_id = node_id = id_map[node] = id_map.size - stack_length = stack.length - stack << node - - each_child.call(node) {|child| - if id_map.include? child - child_id = id_map[child] - minimum_id = child_id if child_id && child_id < minimum_id - else - sub_minimum_id = - TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c| - yield c - } - minimum_id = sub_minimum_id if sub_minimum_id < minimum_id - end - } - - if node_id == minimum_id - component = stack.slice!(stack_length .. -1) - component.each {|n| id_map[n] = nil} - yield component + minimum_id = sub_minimum_id if sub_minimum_id < minimum_id end + } - minimum_id + if node_id == minimum_id + component = stack.slice!(stack_length .. -1) + component.each {|n| id_map[n] = nil} + yield component end - # Should be implemented by a extended class. - # - # #tsort_each_node is used to iterate for all nodes over a graph. - # - def tsort_each_node # :yields: node - raise NotImplementedError.new - end + minimum_id + end - # Should be implemented by a extended class. - # - # #tsort_each_child is used to iterate for child nodes of _node_. - # - def tsort_each_child(node) # :yields: child - raise NotImplementedError.new - end + # Should be implemented by a extended class. + # + # #tsort_each_node is used to iterate for all nodes over a graph. + # + def tsort_each_node # :yields: node + raise NotImplementedError.new + end + + # Should be implemented by a extended class. + # + # #tsort_each_child is used to iterate for child nodes of _node_. + # + def tsort_each_child(node) # :yields: child + raise NotImplementedError.new end end diff --git a/lib/rubygems.rb b/lib/rubygems.rb index d2c404d7d3..137c08610b 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -864,9 +864,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} return @ruby_version if defined? @ruby_version version = RUBY_VERSION.dup - if defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1 - version << ".#{RUBY_PATCHLEVEL}" - elsif defined?(RUBY_DESCRIPTION) + unless defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1 if RUBY_ENGINE == "ruby" desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1] else diff --git a/lib/rubygems/optparse/lib/optparse.rb b/lib/rubygems/optparse/lib/optparse.rb index e4b1c61f79..98865612ba 100644 --- a/lib/rubygems/optparse/lib/optparse.rb +++ b/lib/rubygems/optparse/lib/optparse.rb @@ -50,7 +50,7 @@ # # === New to \Gem::OptionParser? # -# See the {Tutorial}[./doc/optparse/tutorial_rdoc.html]. +# See the {Tutorial}[optparse/tutorial.rdoc]. # # === Introduction # @@ -420,7 +420,7 @@ # === Further documentation # # The above examples, along with the accompanying -# {Tutorial}[./doc/optparse/tutorial_rdoc.html], +# {Tutorial}[optparse/tutorial.rdoc], # should be enough to learn how to use this class. # If you have any questions, file a ticket at http://bugs.ruby-lang.org. # @@ -674,6 +674,29 @@ class Gem::OptionParser end end + def pretty_print_contents(q) # :nodoc: + if @block + q.text ":" + @block.source_location.join(":") + ":" + first = false + else + first = true + end + [@short, @long].each do |list| + list.each do |opt| + if first + q.text ":" + first = false + end + q.breakable + q.text opt + end + end + end + + def pretty_print(q) # :nodoc: + q.object_group(self) {pretty_print_contents(q)} + end + # # Switch that takes no arguments. # @@ -693,6 +716,10 @@ class Gem::OptionParser def self.pattern Object end + + def pretty_head # :nodoc: + "NoArgument" + end end # @@ -710,6 +737,10 @@ class Gem::OptionParser end conv_arg(*parse_arg(arg, &method(:raise))) end + + def pretty_head # :nodoc: + "Required" + end end # @@ -727,6 +758,10 @@ class Gem::OptionParser conv_arg(arg) end end + + def pretty_head # :nodoc: + "Optional" + end end # @@ -750,6 +785,10 @@ class Gem::OptionParser end val end + + def pretty_head # :nodoc: + "Placed" + end end end @@ -781,6 +820,17 @@ class Gem::OptionParser @list = [] end + def pretty_print(q) # :nodoc: + q.group(1, "(", ")") do + @list.each do |sw| + next unless Switch === sw + q.group(1, "(" + sw.pretty_head, ")") do + sw.pretty_print_contents(q) + end + end + end + end + # # See Gem::OptionParser.accept. # @@ -1293,6 +1343,29 @@ XXX def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end alias to_s help + def pretty_print(q) # :nodoc: + q.object_group(self) do + first = true + if @stack.size > 2 + @stack.each_with_index do |s, i| + next if i < 2 + next if s.list.empty? + if first + first = false + q.text ":" + end + q.breakable + s.pretty_print(q) + end + end + end + end + + def inspect # :nodoc: + require 'pp' + pretty_print_inspect + end + # # Returns option summary list. # diff --git a/lib/rubygems/optparse/lib/optparse/ac.rb b/lib/rubygems/optparse/lib/optparse/ac.rb index ff2f4c2dc3..e84d01bf91 100644 --- a/lib/rubygems/optparse/lib/optparse/ac.rb +++ b/lib/rubygems/optparse/lib/optparse/ac.rb @@ -1,5 +1,5 @@ # frozen_string_literal: false -require 'rubygems/optparse/lib/optparse' +require_relative '../optparse' class Gem::OptionParser::AC < Gem::OptionParser private diff --git a/lib/rubygems/optparse/lib/optparse/date.rb b/lib/rubygems/optparse/lib/optparse/date.rb index 11131e92c2..d9a9f4f48a 100644 --- a/lib/rubygems/optparse/lib/optparse/date.rb +++ b/lib/rubygems/optparse/lib/optparse/date.rb @@ -1,5 +1,5 @@ # frozen_string_literal: false -require 'rubygems/optparse/lib/optparse' +require_relative '../optparse' require 'date' Gem::OptionParser.accept(DateTime) do |s,| diff --git a/lib/rubygems/optparse/lib/optparse/kwargs.rb b/lib/rubygems/optparse/lib/optparse/kwargs.rb index 9290344c39..6987a5ed62 100644 --- a/lib/rubygems/optparse/lib/optparse/kwargs.rb +++ b/lib/rubygems/optparse/lib/optparse/kwargs.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -require 'rubygems/optparse/lib/optparse' +require_relative '../optparse' class Gem::OptionParser # :call-seq: diff --git a/lib/rubygems/optparse/lib/optparse/shellwords.rb b/lib/rubygems/optparse/lib/optparse/shellwords.rb index 60dd91990c..d47ad60255 100644 --- a/lib/rubygems/optparse/lib/optparse/shellwords.rb +++ b/lib/rubygems/optparse/lib/optparse/shellwords.rb @@ -2,6 +2,6 @@ # -*- ruby -*- require 'shellwords' -require 'rubygems/optparse/lib/optparse' +require_relative '../optparse' Gem::OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)} diff --git a/lib/rubygems/optparse/lib/optparse/time.rb b/lib/rubygems/optparse/lib/optparse/time.rb index cb19f6e998..c59e1e4ced 100644 --- a/lib/rubygems/optparse/lib/optparse/time.rb +++ b/lib/rubygems/optparse/lib/optparse/time.rb @@ -1,5 +1,5 @@ # frozen_string_literal: false -require 'rubygems/optparse/lib/optparse' +require_relative '../optparse' require 'time' Gem::OptionParser.accept(Time) do |s,| diff --git a/lib/rubygems/optparse/lib/optparse/uri.rb b/lib/rubygems/optparse/lib/optparse/uri.rb index 088f309992..664d7f2af4 100644 --- a/lib/rubygems/optparse/lib/optparse/uri.rb +++ b/lib/rubygems/optparse/lib/optparse/uri.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false # -*- ruby -*- -require 'rubygems/optparse/lib/optparse' +require_relative '../optparse' require 'uri' Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s} diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 0d72cee51d..7e7599f7db 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -657,6 +657,8 @@ class Gem::Specification < Gem::BasicSpecification @rdoc_options ||= [] end + LATEST_RUBY_WITHOUT_PATCH_VERSIONS = Gem::Version.new("2.1") + ## # The version of Ruby required by this gem. The ruby version can be # specified to the patch-level: @@ -683,6 +685,14 @@ class Gem::Specification < Gem::BasicSpecification def required_ruby_version=(req) @required_ruby_version = Gem::Requirement.create req + + @required_ruby_version.requirements.map! do |op, v| + if v >= LATEST_RUBY_WITHOUT_PATCH_VERSIONS && v.release.segments.size == 4 + [op == "~>" ? "=" : op, Gem::Version.new(v.segments.tap {|s| s.delete_at(3) }.join("."))] + else + [op, v] + end + end end ## diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb index 8c6c071d7f..3e3850031c 100644 --- a/spec/bundler/bundler/ruby_version_spec.rb +++ b/spec/bundler/bundler/ruby_version_spec.rb @@ -498,31 +498,5 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do end end end - - describe "#to_gem_version_with_patchlevel" do - shared_examples_for "the patchlevel is omitted" do - it "does not include a patch level" do - expect(subject.to_gem_version_with_patchlevel.to_s).to eq(version) - end - end - - context "with nil patch number" do - let(:patchlevel) { nil } - - it_behaves_like "the patchlevel is omitted" - end - - context "with negative patch number" do - let(:patchlevel) { -1 } - - it_behaves_like "the patchlevel is omitted" - end - - context "with a valid patch number" do - it "uses the specified patchlevel as patchlevel" do - expect(subject.to_gem_version_with_patchlevel.to_s).to eq("#{version}.#{patchlevel}") - end - end - end end end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index ee1c3ffd46..9c5240157f 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -542,6 +542,40 @@ RSpec.describe "bundle lock" do bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } end + it "respects lower bound ruby requirements" do + skip "this spec does not work with prereleases because their version is actually lower than their reported `RUBY_VERSION`" if RUBY_PATCHLEVEL == -1 + + build_repo4 do + build_gem "our_private_gem", "0.1.0" do |s| + s.required_ruby_version = ">= #{RUBY_VERSION}" + end + end + + gemfile <<-G + source "https://localgemserver.test" + + gem "our_private_gem" + G + + lockfile <<-L + GEM + remote: https://localgemserver.test/ + specs: + our_private_gem (0.1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + our_private_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + end + context "when an update is available" do let(:repo) { gem_repo2 } diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index d013d5bd73..d45a565475 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -1182,6 +1182,8 @@ RSpec.describe "bundle update --bundler" do end it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do + skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1 + pristine_system_gems "bundler-2.3.9" build_repo4 do @@ -1226,6 +1228,8 @@ RSpec.describe "bundle update --bundler" do end it "errors if the explicit target version does not exist", :realworld do + skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1 + pristine_system_gems "bundler-2.3.9" build_repo4 do diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 209996f61f..83f1e60806 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -301,7 +301,28 @@ RSpec.describe "bundle install with install-time dependencies" do end let(:ruby_requirement) { %("#{RUBY_VERSION}") } - let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" } + let(:error_message_requirement) { "= #{RUBY_VERSION}" } + + it "raises a proper error that mentions the current Ruby version during resolution" do + install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false + source "http://localgemserver.test/" + gem 'require_ruby' + G + + expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000") + + nice_error = strip_whitespace(<<-E).strip + Bundler found conflicting requirements for the Ruby\0 version: + In Gemfile: + require_ruby was resolved to 1.0, which depends on + Ruby\0 (> 9000) + + Current Ruby\0 version: + Ruby\0 (#{error_message_requirement}) + + E + expect(err).to end_with(nice_error) + end shared_examples_for "ruby version conflicts" do it "raises an error during resolution" do @@ -316,10 +337,12 @@ RSpec.describe "bundle install with install-time dependencies" do nice_error = strip_whitespace(<<-E).strip Bundler found conflicting requirements for the Ruby\0 version: In Gemfile: - Ruby\0 (#{error_message_requirement}) - require_ruby was resolved to 1.0, which depends on Ruby\0 (> 9000) + + Current Ruby\0 version: + Ruby\0 (#{error_message_requirement}) + E expect(err).to end_with(nice_error) end @@ -329,7 +352,6 @@ RSpec.describe "bundle install with install-time dependencies" do describe "with a < requirement" do let(:ruby_requirement) { %("< 5000") } - let(:error_message_requirement) { "< 5000" } it_behaves_like "ruby version conflicts" end @@ -337,7 +359,6 @@ RSpec.describe "bundle install with install-time dependencies" do describe "with a compound requirement" do let(:reqs) { ["> 0.1", "< 5000"] } let(:ruby_requirement) { reqs.map(&:dump).join(", ") } - let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s } it_behaves_like "ruby version conflicts" end @@ -361,10 +382,12 @@ RSpec.describe "bundle install with install-time dependencies" do nice_error = strip_whitespace(<<-E).strip Bundler found conflicting requirements for the RubyGems\0 version: In Gemfile: - RubyGems\0 (= #{Gem::VERSION}) - require_rubygems was resolved to 1.0, which depends on RubyGems\0 (> 9000) + + Current RubyGems\0 version: + RubyGems\0 (= #{Gem::VERSION}) + E expect(err).to end_with(nice_error) end diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb index 66dd7f534a..5cbb484c68 100644 --- a/spec/bundler/install/gems/standalone_spec.rb +++ b/spec/bundler/install/gems/standalone_spec.rb @@ -120,7 +120,7 @@ RSpec.shared_examples "bundle install --standalone" do realworld_system_gems "tsort --version 0.1.0" - necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.0"] + necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.1"] necessary_system_gems += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.a") necessary_system_gems += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a") realworld_system_gems(*necessary_system_gems, :path => scoped_gem_path(bundled_app("bundle"))) diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index be9016b831..6b57ff066e 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -1098,7 +1098,7 @@ Also, a list: Zlib::Deflate.deflate data end - def util_set_RUBY_VERSION(version, patchlevel = nil, revision = nil, description = nil, engine = "ruby", engine_version = nil) + def util_set_RUBY_VERSION(version, patchlevel, revision, description, engine = "ruby", engine_version = nil) if Gem.instance_variables.include? :@ruby_version Gem.send :remove_instance_variable, :@ruby_version end @@ -1106,16 +1106,16 @@ Also, a list: @RUBY_VERSION = RUBY_VERSION @RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL) @RUBY_REVISION = RUBY_REVISION if defined?(RUBY_REVISION) - @RUBY_DESCRIPTION = RUBY_DESCRIPTION if defined?(RUBY_DESCRIPTION) + @RUBY_DESCRIPTION = RUBY_DESCRIPTION @RUBY_ENGINE = RUBY_ENGINE @RUBY_ENGINE_VERSION = RUBY_ENGINE_VERSION if defined?(RUBY_ENGINE_VERSION) util_clear_RUBY_VERSION Object.const_set :RUBY_VERSION, version - Object.const_set :RUBY_PATCHLEVEL, patchlevel if patchlevel - Object.const_set :RUBY_REVISION, revision if revision - Object.const_set :RUBY_DESCRIPTION, description if description + Object.const_set :RUBY_PATCHLEVEL, patchlevel + Object.const_set :RUBY_REVISION, revision + Object.const_set :RUBY_DESCRIPTION, description Object.const_set :RUBY_ENGINE, engine Object.const_set :RUBY_ENGINE_VERSION, engine_version if engine_version end @@ -1128,8 +1128,7 @@ Also, a list: defined?(@RUBY_PATCHLEVEL) Object.const_set :RUBY_REVISION, @RUBY_REVISION if defined?(@RUBY_REVISION) - Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION if - defined?(@RUBY_DESCRIPTION) + Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION Object.const_set :RUBY_ENGINE, @RUBY_ENGINE Object.const_set :RUBY_ENGINE_VERSION, @RUBY_ENGINE_VERSION if defined?(@RUBY_ENGINE_VERSION) diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index a8be078046..0ff8b4a1db 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -1106,22 +1106,6 @@ class TestGem < Gem::TestCase assert_equal Gem::Requirement.default, Gem.env_requirement('qux') end - def test_self_ruby_version_with_patchlevel_less_ancient_rubies - util_set_RUBY_VERSION '1.8.5' - - assert_equal Gem::Version.new('1.8.5'), Gem.ruby_version - ensure - util_restore_RUBY_VERSION - end - - def test_self_ruby_version_with_release - util_set_RUBY_VERSION '1.8.6', 287 - - assert_equal Gem::Version.new('1.8.6.287'), Gem.ruby_version - ensure - util_restore_RUBY_VERSION - end - def test_self_ruby_version_with_non_mri_implementations util_set_RUBY_VERSION '2.5.0', 0, 60928, 'jruby 9.2.0.0 (2.5.0) 2018-05-24 81156a8 OpenJDK 64-Bit Server VM 25.171-b11 on 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-b11 [linux-x86_64]' diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock index efee4b2f2c..a936e19db8 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock @@ -261,8 +261,8 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.6.0" -source = "git+https://github.com/ianks/rb-sys?tag=v0.6.0#1aa5b589e86a14e01aba806511818c19f85d71f6" +version = "0.7.3" +source = "git+https://github.com/ianks/rb-sys?tag=v0.7.3#4a5dd9782075fc6e197976eb2188231a388c3c95" dependencies = [ "bindgen", "libc", @@ -271,9 +271,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml index e0232f729d..d95d1fb2a5 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml @@ -7,4 +7,4 @@ crate-type = ["cdylib"] [dependencies] # Needed until bindgen has the `allowlist_file` feature -rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.6.0" } +rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.7.3" } diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index 9248f7101c..a55ee0dc76 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -254,8 +254,8 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.6.0" -source = "git+https://github.com/ianks/rb-sys?tag=v0.6.0#1aa5b589e86a14e01aba806511818c19f85d71f6" +version = "0.7.3" +source = "git+https://github.com/ianks/rb-sys?tag=v0.7.3#4a5dd9782075fc6e197976eb2188231a388c3c95" dependencies = [ "bindgen", "libc", @@ -264,9 +264,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index 66a98f3885..0591ebd493 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ crate-type = ["cdylib"] [dependencies] # Needed until bindgen has the `allowlist_file` feature -rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.6.0" } +rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.7.3" } diff --git a/test/rubygems/test_gem_resolver_installer_set.rb b/test/rubygems/test_gem_resolver_installer_set.rb index 1b2f0ab637..928a16b9d3 100644 --- a/test/rubygems/test_gem_resolver_installer_set.rb +++ b/test/rubygems/test_gem_resolver_installer_set.rb @@ -69,7 +69,7 @@ class TestGemResolverInstallerSet < Gem::TestCase fetcher.gem 'a', 1 end - # GitHub has an issue in which it will generate a misleading prerelease output in its RubyGems server API and + # Github has an issue in which it will generate a misleading prerelease output in its RubyGems server API and # returns a 0 version for the gem while it doesn't exist. @fetcher.data["#{@gem_repo}prerelease_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([ Gem::NameTuple.new('a', Gem::Version.new(0), 'ruby'),