This commit is contained in:
Hiroshi SHIBATA 2022-04-28 17:15:43 +09:00
parent cd2410f9d8
commit 1056489ea3
Notes: git 2022-04-28 19:09:12 +09:00
31 changed files with 613 additions and 489 deletions

View File

@ -736,30 +736,13 @@ module Bundler
def metadata_dependencies def metadata_dependencies
@metadata_dependencies ||= begin @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), Dependency.new("RubyGems\0", Gem::VERSION),
] ]
end end
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) def expand_dependencies(dependencies, remote = false)
deps = [] deps = []
dependencies.each do |dep| dependencies.each do |dep|

View File

@ -312,29 +312,66 @@ module Bundler
e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty? e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty?
solver_name = "Bundler"
possibility_type = "gem"
e.message_with_trees( e.message_with_trees(
:solver_name => solver_name, :full_message_for_conflict => lambda do |name, conflict|
:possibility_type => possibility_type, o = if name.end_with?("\0")
:reduce_trees => lambda do |trees| 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 # 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] } } 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 # bail out if tree size is too big for Array#combination to make any sense
return trees if trees.size > 15 if trees.size <= 15
maximal = 1.upto(trees.size).map do |size| maximal = 1.upto(trees.size).map do |size|
trees.map(&:last).flatten(1).combination(size).to_a trees.map(&:last).flatten(1).combination(size).to_a
end.flatten(1).select do |deps| end.flatten(1).select do |deps|
Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
end.min_by(&:size) 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" if name == "bundler"
o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION})) 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" 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
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 elsif conflict.locked_requirement
o << "\n" o << "\n"
o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\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) o << %(the gems in your Gemfile, which may resolve the conflict.\n)
elsif !conflict.existing && !name.end_with?("\0") elsif !conflict.existing
o << "\n" o << "\n"
relevant_source = conflict.requirement.source || source_for(name) 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) o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message)
end end
end,
:version_for_spec => lambda {|spec| spec.version }, o
: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
end end
) )
end end

View File

@ -97,10 +97,10 @@ module Bundler
spec = @specs[platform].first spec = @specs[platform].first
return [] if spec.is_a?(LazySpecification) return [] if spec.is_a?(LazySpecification)
dependencies = [] 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) dependencies << DepProxy.get_proxy(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform)
end 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) dependencies << DepProxy.get_proxy(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform)
end end
dependencies dependencies

View File

@ -110,19 +110,6 @@ module Bundler
@ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version) @ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
end 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 private
def matches?(requirements, version) def matches?(requirements, version)

View File

@ -67,6 +67,23 @@ module Gem
full_gem_path full_gem_path
end 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 def groups
@groups ||= [] @groups ||= []
end end

View File

@ -5,7 +5,7 @@ module Bundler
class Metadata < Source class Metadata < Source
def specs def specs
@specs ||= Index.build do |idx| @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| idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s|
s.required_rubygems_version = Gem::Requirement.default s.required_rubygems_version = Gem::Requirement.default
end end

View File

@ -32,7 +32,7 @@ module Bundler::Molinillo
# all belong to the same graph. # all belong to the same graph.
# @return [Array<Vertex>] The sorted vertices. # @return [Array<Vertex>] The sorted vertices.
def self.tsort(vertices) def self.tsort(vertices)
TSort.tsort( Bundler::TSort.tsort(
lambda { |b| vertices.each(&b) }, lambda { |b| vertices.each(&b) },
lambda { |v, &b| (v.successors & vertices).each(&b) } lambda { |v, &b| (v.successors & vertices).each(&b) }
) )

View File

@ -107,36 +107,42 @@ module Bundler::Molinillo
end end
end end
conflicts.sort.reduce(''.dup) do |o, (name, conflict)| full_message_for_conflict = opts.delete(:full_message_for_conflict) do
o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n" proc do |name, conflict|
if conflict.locked_requirement o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
o << %( In snapshot (#{name_for_locking_dependency_source}):\n) if conflict.locked_requirement
o << %( #{printable_requirement.call(conflict.locked_requirement)}\n) o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
o << %(\n) o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
end o << %(\n)
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
end end
t o << %( In #{name_for_explicit_dependency_source}:\n)
end.join("\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.strip
end end
end end

View File

@ -2,5 +2,5 @@
module Bundler::Molinillo module Bundler::Molinillo
# The version of Bundler::Molinillo. # The version of Bundler::Molinillo.
VERSION = '0.7.0'.freeze VERSION = '0.8.0'.freeze
end end

View File

@ -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. # 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. # 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 and tsort_each_child.
# #
# * tsort_each_node is used to iterate for all nodes over a graph. # * 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. # * 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 # The equality of nodes are defined by eql? and hash since
# TSort uses Hash internally. # Bundler::TSort uses Hash internally.
# #
# == A Simple Example # == 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 # 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 # 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 # #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 # method, which fetches the array of child nodes and then iterates over that
# array using the user-supplied block. # array using the user-supplied block.
# #
# require 'tsort' # require 'bundler/vendor/tsort/lib/tsort'
# #
# class Hash # class Hash
# include TSort # include Bundler::TSort
# alias tsort_each_node each_key # alias tsort_each_node each_key
# def tsort_each_child(node, &block) # def tsort_each_child(node, &block)
# fetch(node).each(&block) # fetch(node).each(&block)
@ -52,7 +52,7 @@
# #
# A very simple `make' like tool can be implemented as follows: # A very simple `make' like tool can be implemented as follows:
# #
# require 'tsort' # require 'bundler/vendor/tsort/lib/tsort'
# #
# class Make # class Make
# def initialize # def initialize
@ -70,7 +70,7 @@
# each_strongly_connected_component_from(target) {|ns| # each_strongly_connected_component_from(target) {|ns|
# if ns.length != 1 # if ns.length != 1
# fs = ns.delete_if {|n| Array === n} # 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 # end
# n = ns.first # n = ns.first
# if Array === n # if Array === n
@ -93,7 +93,7 @@
# def tsort_each_child(node, &block) # def tsort_each_child(node, &block)
# @dep[node].each(&block) # @dep[node].each(&block)
# end # end
# include TSort # include Bundler::TSort
# end # end
# #
# def command(arg) # def command(arg)
@ -120,334 +120,333 @@
# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms", # R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972. # <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
# #
module Bundler
module TSort
class Cyclic < StandardError
end
# Returns a topologically sorted array of nodes. module Bundler::TSort
# The array is sorted from children to parents, i.e. class Cyclic < StandardError
# the first element has no child and the last node has no parent. end
#
# 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
# Returns a topologically sorted array of nodes. # Returns a topologically sorted array of nodes.
# The array is sorted from children to parents, i.e. # The array is sorted from children to parents, i.e.
# the first element has no child and the last node has no parent. # the first element has no child and the last node has no parent.
# #
# The graph is represented by _each_node_ and _each_child_. # If there is a cycle, Bundler::TSort::Cyclic is raised.
# _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. # class G
# # include Bundler::TSort
# If there is a cycle, TSort::Cyclic is raised. # def initialize(g)
# # @g = g
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # end
# each_node = lambda {|&b| g.each_key(&b) } # def tsort_each_child(n, &b) @g[n].each(&b) end
# each_child = lambda {|n, &b| g[n].each(&b) } # def tsort_each_node(&b) @g.each_key(&b) end
# p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1] # end
# #
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
# each_node = lambda {|&b| g.each_key(&b) } # p graph.tsort #=> [4, 2, 3, 1]
# each_child = lambda {|n, &b| g[n].each(&b) } #
# p TSort.tsort(each_node, each_child) # raises TSort::Cyclic # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
# # p graph.tsort # raises Bundler::TSort::Cyclic
def TSort.tsort(each_node, each_child) #
TSort.tsort_each(each_node, each_child).to_a def tsort
end 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. # Returns a topologically sorted array of nodes.
# <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but # The array is sorted from children to parents, i.e.
# modification of _obj_ during the iteration may lead to unexpected results. # the first element has no child and the last node has no parent.
# #
# #tsort_each returns +nil+. # The graph is represented by _each_node_ and _each_child_.
# If there is a cycle, TSort::Cyclic is raised. # _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.
# class G #
# include TSort # If there is a cycle, Bundler::TSort::Cyclic is raised.
# def initialize(g) #
# @g = g # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
# end # each_node = lambda {|&b| g.each_key(&b) }
# def tsort_each_child(n, &b) @g[n].each(&b) end # each_child = lambda {|n, &b| g[n].each(&b) }
# def tsort_each_node(&b) @g.each_key(&b) end # p Bundler::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
# end #
# # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # each_node = lambda {|&b| g.each_key(&b) }
# graph.tsort_each {|n| p n } # each_child = lambda {|n, &b| g[n].each(&b) }
# #=> 4 # p Bundler::TSort.tsort(each_node, each_child) # raises Bundler::TSort::Cyclic
# # 2 #
# # 3 def self.tsort(each_node, each_child)
# # 1 tsort_each(each_node, each_child).to_a
# end
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
# The iterator version of the TSort.tsort method. # The iterator version of the #tsort method.
# # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
# The graph is represented by _each_node_ and _each_child_. # modification of _obj_ during the iteration may lead to unexpected results.
# _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. # #tsort_each returns +nil+.
# # 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) } # class G
# each_child = lambda {|n, &b| g[n].each(&b) } # include Bundler::TSort
# TSort.tsort_each(each_node, each_child) {|n| p n } # def initialize(g)
# #=> 4 # @g = g
# # 2 # end
# # 3 # def tsort_each_child(n, &b) @g[n].each(&b) end
# # 1 # def tsort_each_node(&b) @g.each_key(&b) end
# # end
def TSort.tsort_each(each_node, each_child) # :yields: node #
return to_enum(__method__, each_node, each_child) unless block_given? # 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| # The iterator version of the Bundler::TSort.tsort method.
if component.size == 1 #
yield component.first # The graph is represented by _each_node_ and _each_child_.
else # _each_node_ should have +call+ method which yields for each node in the graph.
raise Cyclic.new("topological sort failed: #{component.inspect}") # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
end #
} # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
end # 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. each_strongly_connected_component(each_node, each_child) {|component|
# The array is sorted from children to parents. if component.size == 1
# Each elements of the array represents a strongly connected component. yield component.first
# else
# class G raise Cyclic.new("topological sort failed: #{component.inspect}")
# include TSort end
# def initialize(g) }
# @g = g end
# 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
# Returns strongly connected components as an array of arrays of nodes. # Returns strongly connected components as an array of arrays of nodes.
# The array is sorted from children to parents. # The array is sorted from children to parents.
# Each elements of the array represents a strongly connected component. # Each elements of the array represents a strongly connected component.
# #
# The graph is represented by _each_node_ and _each_child_. # class G
# _each_node_ should have +call+ method which yields for each node in the graph. # include Bundler::TSort
# _each_child_ should have +call+ method which takes a node argument and yields for each child node. # def initialize(g)
# # @g = g
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} # end
# each_node = lambda {|&b| g.each_key(&b) } # def tsort_each_child(n, &b) @g[n].each(&b) end
# each_child = lambda {|n, &b| g[n].each(&b) } # def tsort_each_node(&b) @g.each_key(&b) end
# p TSort.strongly_connected_components(each_node, each_child) # end
# #=> [[4], [2], [3], [1]] #
# # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
# each_node = lambda {|&b| g.each_key(&b) } #
# each_child = lambda {|n, &b| g[n].each(&b) } # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
# p TSort.strongly_connected_components(each_node, each_child) # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
# #=> [[4], [2, 3], [1]] #
# def strongly_connected_components
def TSort.strongly_connected_components(each_node, each_child) each_node = method(:tsort_each_node)
TSort.each_strongly_connected_component(each_node, each_child).to_a each_child = method(:tsort_each_child)
end Bundler::TSort.strongly_connected_components(each_node, each_child)
end
# The iterator version of the #strongly_connected_components method. # Returns strongly connected components as an array of arrays of nodes.
# <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to # The array is sorted from children to parents.
# <tt><em>obj</em>.strongly_connected_components.each</tt>, but # Each elements of the array represents a strongly connected component.
# modification of _obj_ during the iteration may lead to unexpected results. #
# # The graph is represented by _each_node_ and _each_child_.
# #each_strongly_connected_component returns +nil+. # _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.
# class G #
# include TSort # g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
# def initialize(g) # each_node = lambda {|&b| g.each_key(&b) }
# @g = g # each_child = lambda {|n, &b| g[n].each(&b) }
# end # p Bundler::TSort.strongly_connected_components(each_node, each_child)
# def tsort_each_child(n, &b) @g[n].each(&b) end # #=> [[4], [2], [3], [1]]
# def tsort_each_node(&b) @g.each_key(&b) end #
# end # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
# # each_node = lambda {|&b| g.each_key(&b) }
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}) # each_child = lambda {|n, &b| g[n].each(&b) }
# graph.each_strongly_connected_component {|scc| p scc } # p Bundler::TSort.strongly_connected_components(each_node, each_child)
# #=> [4] # #=> [[4], [2, 3], [1]]
# # [2] #
# # [3] def self.strongly_connected_components(each_node, each_child)
# # [1] each_strongly_connected_component(each_node, each_child).to_a
# end
# 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
# The iterator version of the TSort.strongly_connected_components method. # The iterator version of the #strongly_connected_components method.
# # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
# The graph is represented by _each_node_ and _each_child_. # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
# _each_node_ should have +call+ method which yields for each node in the graph. # modification of _obj_ during the iteration may lead to unexpected results.
# _each_child_ should have +call+ method which takes a node argument and yields for each child node. #
# # #each_strongly_connected_component returns +nil+.
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]} #
# each_node = lambda {|&b| g.each_key(&b) } # class G
# each_child = lambda {|n, &b| g[n].each(&b) } # include Bundler::TSort
# TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } # def initialize(g)
# #=> [4] # @g = g
# # [2] # end
# # [3] # def tsort_each_child(n, &b) @g[n].each(&b) end
# # [1] # def tsort_each_node(&b) @g.each_key(&b) end
# # end
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]} #
# each_node = lambda {|&b| g.each_key(&b) } # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
# each_child = lambda {|n, &b| g[n].each(&b) } # graph.each_strongly_connected_component {|scc| p scc }
# TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc } # #=> [4]
# #=> [4] # # [2]
# # [2, 3] # # [3]
# # [1] # # [1]
# #
def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
return to_enum(__method__, each_node, each_child) unless block_given? # 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 = {} # The iterator version of the Bundler::TSort.strongly_connected_components method.
stack = [] #
each_node.call {|node| # The graph is represented by _each_node_ and _each_child_.
unless id_map.include? node # _each_node_ should have +call+ method which yields for each node in the graph.
TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c| # _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 yield c
} }
end minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
}
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
end end
}
minimum_id if node_id == minimum_id
component = stack.slice!(stack_length .. -1)
component.each {|n| id_map[n] = nil}
yield component
end end
# Should be implemented by a extended class. minimum_id
# end
# #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. # Should be implemented by a extended class.
# #
# #tsort_each_child is used to iterate for child nodes of _node_. # #tsort_each_node is used to iterate for all nodes over a graph.
# #
def tsort_each_child(node) # :yields: child def tsort_each_node # :yields: node
raise NotImplementedError.new raise NotImplementedError.new
end 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
end end

View File

@ -864,9 +864,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
return @ruby_version if defined? @ruby_version return @ruby_version if defined? @ruby_version
version = RUBY_VERSION.dup version = RUBY_VERSION.dup
if defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1 unless defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1
version << ".#{RUBY_PATCHLEVEL}"
elsif defined?(RUBY_DESCRIPTION)
if RUBY_ENGINE == "ruby" if RUBY_ENGINE == "ruby"
desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1] desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1]
else else

View File

@ -50,7 +50,7 @@
# #
# === New to \Gem::OptionParser? # === New to \Gem::OptionParser?
# #
# See the {Tutorial}[./doc/optparse/tutorial_rdoc.html]. # See the {Tutorial}[optparse/tutorial.rdoc].
# #
# === Introduction # === Introduction
# #
@ -420,7 +420,7 @@
# === Further documentation # === Further documentation
# #
# The above examples, along with the accompanying # 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. # should be enough to learn how to use this class.
# If you have any questions, file a ticket at http://bugs.ruby-lang.org. # If you have any questions, file a ticket at http://bugs.ruby-lang.org.
# #
@ -674,6 +674,29 @@ class Gem::OptionParser
end end
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. # Switch that takes no arguments.
# #
@ -693,6 +716,10 @@ class Gem::OptionParser
def self.pattern def self.pattern
Object Object
end end
def pretty_head # :nodoc:
"NoArgument"
end
end end
# #
@ -710,6 +737,10 @@ class Gem::OptionParser
end end
conv_arg(*parse_arg(arg, &method(:raise))) conv_arg(*parse_arg(arg, &method(:raise)))
end end
def pretty_head # :nodoc:
"Required"
end
end end
# #
@ -727,6 +758,10 @@ class Gem::OptionParser
conv_arg(arg) conv_arg(arg)
end end
end end
def pretty_head # :nodoc:
"Optional"
end
end end
# #
@ -750,6 +785,10 @@ class Gem::OptionParser
end end
val val
end end
def pretty_head # :nodoc:
"Placed"
end
end end
end end
@ -781,6 +820,17 @@ class Gem::OptionParser
@list = [] @list = []
end 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. # See Gem::OptionParser.accept.
# #
@ -1293,6 +1343,29 @@ XXX
def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
alias to_s help 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. # Returns option summary list.
# #

View File

@ -1,5 +1,5 @@
# frozen_string_literal: false # frozen_string_literal: false
require 'rubygems/optparse/lib/optparse' require_relative '../optparse'
class Gem::OptionParser::AC < Gem::OptionParser class Gem::OptionParser::AC < Gem::OptionParser
private private

View File

@ -1,5 +1,5 @@
# frozen_string_literal: false # frozen_string_literal: false
require 'rubygems/optparse/lib/optparse' require_relative '../optparse'
require 'date' require 'date'
Gem::OptionParser.accept(DateTime) do |s,| Gem::OptionParser.accept(DateTime) do |s,|

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rubygems/optparse/lib/optparse' require_relative '../optparse'
class Gem::OptionParser class Gem::OptionParser
# :call-seq: # :call-seq:

View File

@ -2,6 +2,6 @@
# -*- ruby -*- # -*- ruby -*-
require 'shellwords' require 'shellwords'
require 'rubygems/optparse/lib/optparse' require_relative '../optparse'
Gem::OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)} Gem::OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)}

View File

@ -1,5 +1,5 @@
# frozen_string_literal: false # frozen_string_literal: false
require 'rubygems/optparse/lib/optparse' require_relative '../optparse'
require 'time' require 'time'
Gem::OptionParser.accept(Time) do |s,| Gem::OptionParser.accept(Time) do |s,|

View File

@ -1,7 +1,7 @@
# frozen_string_literal: false # frozen_string_literal: false
# -*- ruby -*- # -*- ruby -*-
require 'rubygems/optparse/lib/optparse' require_relative '../optparse'
require 'uri' require 'uri'
Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s} Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s}

View File

@ -657,6 +657,8 @@ class Gem::Specification < Gem::BasicSpecification
@rdoc_options ||= [] @rdoc_options ||= []
end end
LATEST_RUBY_WITHOUT_PATCH_VERSIONS = Gem::Version.new("2.1")
## ##
# The version of Ruby required by this gem. The ruby version can be # The version of Ruby required by this gem. The ruby version can be
# specified to the patch-level: # specified to the patch-level:
@ -683,6 +685,14 @@ class Gem::Specification < Gem::BasicSpecification
def required_ruby_version=(req) def required_ruby_version=(req)
@required_ruby_version = Gem::Requirement.create 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 end
## ##

View File

@ -498,31 +498,5 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end end
end 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
end end

View File

@ -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 } bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
end 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 context "when an update is available" do
let(:repo) { gem_repo2 } let(:repo) { gem_repo2 }

View File

@ -1182,6 +1182,8 @@ RSpec.describe "bundle update --bundler" do
end end
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do 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" pristine_system_gems "bundler-2.3.9"
build_repo4 do build_repo4 do
@ -1226,6 +1228,8 @@ RSpec.describe "bundle update --bundler" do
end end
it "errors if the explicit target version does not exist", :realworld do 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" pristine_system_gems "bundler-2.3.9"
build_repo4 do build_repo4 do

View File

@ -301,7 +301,28 @@ RSpec.describe "bundle install with install-time dependencies" do
end end
let(:ruby_requirement) { %("#{RUBY_VERSION}") } 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 shared_examples_for "ruby version conflicts" do
it "raises an error during resolution" 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 nice_error = strip_whitespace(<<-E).strip
Bundler found conflicting requirements for the Ruby\0 version: Bundler found conflicting requirements for the Ruby\0 version:
In Gemfile: In Gemfile:
Ruby\0 (#{error_message_requirement})
require_ruby was resolved to 1.0, which depends on require_ruby was resolved to 1.0, which depends on
Ruby\0 (> 9000) Ruby\0 (> 9000)
Current Ruby\0 version:
Ruby\0 (#{error_message_requirement})
E E
expect(err).to end_with(nice_error) expect(err).to end_with(nice_error)
end end
@ -329,7 +352,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "with a < requirement" do describe "with a < requirement" do
let(:ruby_requirement) { %("< 5000") } let(:ruby_requirement) { %("< 5000") }
let(:error_message_requirement) { "< 5000" }
it_behaves_like "ruby version conflicts" it_behaves_like "ruby version conflicts"
end end
@ -337,7 +359,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "with a compound requirement" do describe "with a compound requirement" do
let(:reqs) { ["> 0.1", "< 5000"] } let(:reqs) { ["> 0.1", "< 5000"] }
let(:ruby_requirement) { reqs.map(&:dump).join(", ") } let(:ruby_requirement) { reqs.map(&:dump).join(", ") }
let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s }
it_behaves_like "ruby version conflicts" it_behaves_like "ruby version conflicts"
end end
@ -361,10 +382,12 @@ RSpec.describe "bundle install with install-time dependencies" do
nice_error = strip_whitespace(<<-E).strip nice_error = strip_whitespace(<<-E).strip
Bundler found conflicting requirements for the RubyGems\0 version: Bundler found conflicting requirements for the RubyGems\0 version:
In Gemfile: In Gemfile:
RubyGems\0 (= #{Gem::VERSION})
require_rubygems was resolved to 1.0, which depends on require_rubygems was resolved to 1.0, which depends on
RubyGems\0 (> 9000) RubyGems\0 (> 9000)
Current RubyGems\0 version:
RubyGems\0 (= #{Gem::VERSION})
E E
expect(err).to end_with(nice_error) expect(err).to end_with(nice_error)
end end

View File

@ -120,7 +120,7 @@ RSpec.shared_examples "bundle install --standalone" do
realworld_system_gems "tsort --version 0.1.0" 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 += ["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") 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"))) realworld_system_gems(*necessary_system_gems, :path => scoped_gem_path(bundled_app("bundle")))

View File

@ -1098,7 +1098,7 @@ Also, a list:
Zlib::Deflate.deflate data Zlib::Deflate.deflate data
end 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 if Gem.instance_variables.include? :@ruby_version
Gem.send :remove_instance_variable, :@ruby_version Gem.send :remove_instance_variable, :@ruby_version
end end
@ -1106,16 +1106,16 @@ Also, a list:
@RUBY_VERSION = RUBY_VERSION @RUBY_VERSION = RUBY_VERSION
@RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL) @RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
@RUBY_REVISION = RUBY_REVISION if defined?(RUBY_REVISION) @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 = RUBY_ENGINE
@RUBY_ENGINE_VERSION = RUBY_ENGINE_VERSION if defined?(RUBY_ENGINE_VERSION) @RUBY_ENGINE_VERSION = RUBY_ENGINE_VERSION if defined?(RUBY_ENGINE_VERSION)
util_clear_RUBY_VERSION util_clear_RUBY_VERSION
Object.const_set :RUBY_VERSION, version Object.const_set :RUBY_VERSION, version
Object.const_set :RUBY_PATCHLEVEL, patchlevel if patchlevel Object.const_set :RUBY_PATCHLEVEL, patchlevel
Object.const_set :RUBY_REVISION, revision if revision Object.const_set :RUBY_REVISION, revision
Object.const_set :RUBY_DESCRIPTION, description if description Object.const_set :RUBY_DESCRIPTION, description
Object.const_set :RUBY_ENGINE, engine Object.const_set :RUBY_ENGINE, engine
Object.const_set :RUBY_ENGINE_VERSION, engine_version if engine_version Object.const_set :RUBY_ENGINE_VERSION, engine_version if engine_version
end end
@ -1128,8 +1128,7 @@ Also, a list:
defined?(@RUBY_PATCHLEVEL) defined?(@RUBY_PATCHLEVEL)
Object.const_set :RUBY_REVISION, @RUBY_REVISION if Object.const_set :RUBY_REVISION, @RUBY_REVISION if
defined?(@RUBY_REVISION) defined?(@RUBY_REVISION)
Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION if Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION
defined?(@RUBY_DESCRIPTION)
Object.const_set :RUBY_ENGINE, @RUBY_ENGINE Object.const_set :RUBY_ENGINE, @RUBY_ENGINE
Object.const_set :RUBY_ENGINE_VERSION, @RUBY_ENGINE_VERSION if Object.const_set :RUBY_ENGINE_VERSION, @RUBY_ENGINE_VERSION if
defined?(@RUBY_ENGINE_VERSION) defined?(@RUBY_ENGINE_VERSION)

View File

@ -1106,22 +1106,6 @@ class TestGem < Gem::TestCase
assert_equal Gem::Requirement.default, Gem.env_requirement('qux') assert_equal Gem::Requirement.default, Gem.env_requirement('qux')
end 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 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]' 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]'

View File

@ -261,8 +261,8 @@ dependencies = [
[[package]] [[package]]
name = "rb-sys" name = "rb-sys"
version = "0.6.0" version = "0.7.3"
source = "git+https://github.com/ianks/rb-sys?tag=v0.6.0#1aa5b589e86a14e01aba806511818c19f85d71f6" source = "git+https://github.com/ianks/rb-sys?tag=v0.7.3#4a5dd9782075fc6e197976eb2188231a388c3c95"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"libc", "libc",
@ -271,9 +271,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.4" version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",

View File

@ -7,4 +7,4 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
# Needed until bindgen has the `allowlist_file` feature # 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" }

View File

@ -254,8 +254,8 @@ dependencies = [
[[package]] [[package]]
name = "rb-sys" name = "rb-sys"
version = "0.6.0" version = "0.7.3"
source = "git+https://github.com/ianks/rb-sys?tag=v0.6.0#1aa5b589e86a14e01aba806511818c19f85d71f6" source = "git+https://github.com/ianks/rb-sys?tag=v0.7.3#4a5dd9782075fc6e197976eb2188231a388c3c95"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"libc", "libc",
@ -264,9 +264,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.4" version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",

View File

@ -7,4 +7,4 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
# Needed until bindgen has the `allowlist_file` feature # 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" }

View File

@ -69,7 +69,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
fetcher.gem 'a', 1 fetcher.gem 'a', 1
end 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. # 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([ @fetcher.data["#{@gem_repo}prerelease_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
Gem::NameTuple.new('a', Gem::Version.new(0), 'ruby'), Gem::NameTuple.new('a', Gem::Version.new(0), 'ruby'),