mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* lib/rubygems.rb, lib/rubygems/*, test/rubygems/*: Update rubygems
HEAD(2c6d256). It contains to update vendored Molinillo to 0.5.0. https://github.com/rubygems/rubygems/pull/1638 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55441 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
513345607d
commit
f20ad4889d
32 changed files with 984 additions and 260 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
Sat Jun 18 13:59:54 2016 SHIBATA Hiroshi <hsbt@ruby-lang.org>
|
||||||
|
|
||||||
|
* lib/rubygems.rb, lib/rubygems/*, test/rubygems/*: Update rubygems
|
||||||
|
HEAD(2c6d256). It contains to update vendored Molinillo to 0.5.0.
|
||||||
|
https://github.com/rubygems/rubygems/pull/1638
|
||||||
|
|
||||||
Sat Jun 18 10:13:37 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Sat Jun 18 10:13:37 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* common.mk (build-ext), ext/extmk.rb: use variable EXTENCS
|
* common.mk (build-ext), ext/extmk.rb: use variable EXTENCS
|
||||||
|
|
|
@ -154,6 +154,26 @@ module Gem
|
||||||
specifications/default
|
specifications/default
|
||||||
]
|
]
|
||||||
|
|
||||||
|
##
|
||||||
|
# Exception classes used in a Gem.read_binary +rescue+ statement. Not all of
|
||||||
|
# these are defined in Ruby 1.8.7, hence the need for this convoluted setup.
|
||||||
|
|
||||||
|
READ_BINARY_ERRORS = begin
|
||||||
|
read_binary_errors = [Errno::EACCES]
|
||||||
|
read_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
|
||||||
|
read_binary_errors
|
||||||
|
end.freeze
|
||||||
|
|
||||||
|
##
|
||||||
|
# Exception classes used in Gem.write_binary +rescue+ statement. Not all of
|
||||||
|
# these are defined in Ruby 1.8.7.
|
||||||
|
|
||||||
|
WRITE_BINARY_ERRORS = begin
|
||||||
|
write_binary_errors = []
|
||||||
|
write_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
|
||||||
|
write_binary_errors
|
||||||
|
end.freeze
|
||||||
|
|
||||||
@@win_platform = nil
|
@@win_platform = nil
|
||||||
|
|
||||||
@configuration = nil
|
@configuration = nil
|
||||||
|
@ -829,7 +849,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
||||||
f.flock(File::LOCK_EX)
|
f.flock(File::LOCK_EX)
|
||||||
f.read
|
f.read
|
||||||
end
|
end
|
||||||
rescue Errno::EACCES
|
rescue *READ_BINARY_ERRORS
|
||||||
open path, 'rb' do |f|
|
open path, 'rb' do |f|
|
||||||
f.read
|
f.read
|
||||||
end
|
end
|
||||||
|
@ -843,6 +863,26 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Safely write a file in binary mode on all platforms.
|
||||||
|
def self.write_binary(path, data)
|
||||||
|
open(path, 'wb') do |io|
|
||||||
|
begin
|
||||||
|
io.flock(File::LOCK_EX)
|
||||||
|
rescue *WRITE_BINARY_ERRORS
|
||||||
|
end
|
||||||
|
io.write data
|
||||||
|
end
|
||||||
|
rescue Errno::ENOLCK # NFS
|
||||||
|
if Thread.main != Thread.current
|
||||||
|
raise
|
||||||
|
else
|
||||||
|
open(path, 'wb') do |io|
|
||||||
|
io.write data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# The path to the running Ruby interpreter.
|
# The path to the running Ruby interpreter.
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,7 @@ class Gem::Installer
|
||||||
|
|
||||||
# Completely remove any previous gem files
|
# Completely remove any previous gem files
|
||||||
FileUtils.rm_rf gem_dir
|
FileUtils.rm_rf gem_dir
|
||||||
|
FileUtils.rm_rf spec.extension_dir
|
||||||
|
|
||||||
FileUtils.mkdir_p gem_dir
|
FileUtils.mkdir_p gem_dir
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,9 @@ class Gem::Package
|
||||||
stat = File.lstat file
|
stat = File.lstat file
|
||||||
|
|
||||||
if stat.symlink?
|
if stat.symlink?
|
||||||
tar.add_symlink file, File.readlink(file), stat.mode
|
relative_dir = File.dirname(file).sub("#{Dir.pwd}/", '')
|
||||||
|
target_path = File.join(relative_dir, File.readlink(file))
|
||||||
|
tar.add_symlink file, target_path, stat.mode
|
||||||
end
|
end
|
||||||
|
|
||||||
next unless stat.file?
|
next unless stat.file?
|
||||||
|
|
|
@ -310,27 +310,21 @@ class Gem::Package::TarWriter
|
||||||
# Splits +name+ into a name and prefix that can fit in the TarHeader
|
# Splits +name+ into a name and prefix that can fit in the TarHeader
|
||||||
|
|
||||||
def split_name(name) # :nodoc:
|
def split_name(name) # :nodoc:
|
||||||
if name.bytesize > 256
|
if name.bytesize > 256 then
|
||||||
raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)")
|
raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)")
|
||||||
end
|
end
|
||||||
|
|
||||||
if name.bytesize <= 100 then
|
prefix = ''
|
||||||
prefix = ""
|
if name.bytesize > 100 then
|
||||||
else
|
parts = name.split('/', -1) # parts are never empty here
|
||||||
parts = name.split(/\//)
|
name = parts.pop # initially empty for names with a trailing slash ("foo/.../bar/")
|
||||||
newname = parts.pop
|
prefix = parts.join('/') # if empty, then it's impossible to split (parts is empty too)
|
||||||
nxt = ""
|
while !parts.empty? && (prefix.bytesize > 155 || name.empty?)
|
||||||
|
name = parts.pop + '/' + name
|
||||||
loop do
|
prefix = parts.join('/')
|
||||||
nxt = parts.pop
|
|
||||||
break if newname.bytesize + 1 + nxt.bytesize > 100
|
|
||||||
newname = nxt + "/" + newname
|
|
||||||
end
|
end
|
||||||
|
|
||||||
prefix = (parts + [nxt]).join "/"
|
if name.bytesize > 100 or prefix.empty? then
|
||||||
name = newname
|
|
||||||
|
|
||||||
if name.bytesize > 100
|
|
||||||
raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
|
raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -328,20 +328,7 @@ class Gem::RemoteFetcher
|
||||||
end
|
end
|
||||||
|
|
||||||
if update and path
|
if update and path
|
||||||
begin
|
Gem.write_binary(path, data)
|
||||||
open(path, 'wb') do |io|
|
|
||||||
io.flock(File::LOCK_EX)
|
|
||||||
io.write data
|
|
||||||
end
|
|
||||||
rescue Errno::ENOLCK # NFS
|
|
||||||
if Thread.main != Thread.current
|
|
||||||
raise
|
|
||||||
else
|
|
||||||
open(path, 'wb') do |io|
|
|
||||||
io.write data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
data
|
data
|
||||||
|
@ -427,4 +414,3 @@ class Gem::RemoteFetcher
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
# @!visibility private
|
||||||
|
module Delegates
|
||||||
|
# Delegates all {Gem::Resolver::Molinillo::ResolutionState} methods to a `#state` property.
|
||||||
|
module ResolutionState
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#name)
|
||||||
|
def name
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.name
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#requirements)
|
||||||
|
def requirements
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.requirements
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#activated)
|
||||||
|
def activated
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.activated
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#requirement)
|
||||||
|
def requirement
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.requirement
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#possibilities)
|
||||||
|
def possibilities
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.possibilities
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#depth)
|
||||||
|
def depth
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.depth
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::ResolutionState#conflicts)
|
||||||
|
def conflicts
|
||||||
|
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
|
||||||
|
current_state.conflicts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,80 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
module Delegates
|
||||||
|
# Delegates all {Gem::Resolver::Molinillo::SpecificationProvider} methods to a
|
||||||
|
# `#specification_provider` property.
|
||||||
|
module SpecificationProvider
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#search_for)
|
||||||
|
def search_for(dependency)
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.search_for(dependency)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_for)
|
||||||
|
def dependencies_for(specification)
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.dependencies_for(specification)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#requirement_satisfied_by?)
|
||||||
|
def requirement_satisfied_by?(requirement, activated, spec)
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.requirement_satisfied_by?(requirement, activated, spec)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#name_for)
|
||||||
|
def name_for(dependency)
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.name_for(dependency)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_explicit_dependency_source)
|
||||||
|
def name_for_explicit_dependency_source
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.name_for_explicit_dependency_source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_locking_dependency_source)
|
||||||
|
def name_for_locking_dependency_source
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.name_for_locking_dependency_source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#sort_dependencies)
|
||||||
|
def sort_dependencies(dependencies, activated, conflicts)
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.sort_dependencies(dependencies, activated, conflicts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Gem::Resolver::Molinillo::SpecificationProvider#allow_missing?)
|
||||||
|
def allow_missing?(dependency)
|
||||||
|
with_no_such_dependency_error_handling do
|
||||||
|
specification_provider.allow_missing?(dependency)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Ensures any raised {NoSuchDependencyError} has its
|
||||||
|
# {NoSuchDependencyError#required_by} set.
|
||||||
|
# @yield
|
||||||
|
def with_no_such_dependency_error_handling
|
||||||
|
yield
|
||||||
|
rescue NoSuchDependencyError => error
|
||||||
|
if state
|
||||||
|
vertex = activated.vertex_named(name_for(error.dependency))
|
||||||
|
error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
|
||||||
|
error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
|
||||||
|
end
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,9 @@
|
||||||
require 'set'
|
require 'set'
|
||||||
require 'tsort'
|
require 'tsort'
|
||||||
|
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log'
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex'
|
||||||
|
|
||||||
module Gem::Resolver::Molinillo
|
module Gem::Resolver::Molinillo
|
||||||
# A directed acyclic graph that is tuned to hold named dependencies
|
# A directed acyclic graph that is tuned to hold named dependencies
|
||||||
class DependencyGraph
|
class DependencyGraph
|
||||||
|
@ -10,15 +13,16 @@ module Gem::Resolver::Molinillo
|
||||||
# Enumerates through the vertices of the graph.
|
# Enumerates through the vertices of the graph.
|
||||||
# @return [Array<Vertex>] The graph's vertices.
|
# @return [Array<Vertex>] The graph's vertices.
|
||||||
def each
|
def each
|
||||||
|
return vertices.values.each unless block_given?
|
||||||
vertices.values.each { |v| yield v }
|
vertices.values.each { |v| yield v }
|
||||||
end
|
end
|
||||||
|
|
||||||
include TSort
|
include TSort
|
||||||
|
|
||||||
# @visibility private
|
# @!visibility private
|
||||||
alias_method :tsort_each_node, :each
|
alias tsort_each_node each
|
||||||
|
|
||||||
# @visibility private
|
# @!visibility private
|
||||||
def tsort_each_child(vertex, &block)
|
def tsort_each_child(vertex, &block)
|
||||||
vertex.successors.each(&block)
|
vertex.successors.each(&block)
|
||||||
end
|
end
|
||||||
|
@ -44,9 +48,27 @@ module Gem::Resolver::Molinillo
|
||||||
# by {Vertex#name}
|
# by {Vertex#name}
|
||||||
attr_reader :vertices
|
attr_reader :vertices
|
||||||
|
|
||||||
|
# @return [Log] the op log for this graph
|
||||||
|
attr_reader :log
|
||||||
|
|
||||||
# Initializes an empty dependency graph
|
# Initializes an empty dependency graph
|
||||||
def initialize
|
def initialize
|
||||||
@vertices = {}
|
@vertices = {}
|
||||||
|
@log = Log.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tags the current state of the dependency as the given tag
|
||||||
|
# @param [Object] tag an opaque tag for the current state of the graph
|
||||||
|
# @return [Void]
|
||||||
|
def tag(tag)
|
||||||
|
log.tag(self, tag)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rewinds the graph to the state tagged as `tag`
|
||||||
|
# @param [Object] tag the tag to rewind to
|
||||||
|
# @return [Void]
|
||||||
|
def rewind_to(tag)
|
||||||
|
log.rewind_to(self, tag)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
|
# Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
|
||||||
|
@ -55,6 +77,7 @@ module Gem::Resolver::Molinillo
|
||||||
def initialize_copy(other)
|
def initialize_copy(other)
|
||||||
super
|
super
|
||||||
@vertices = {}
|
@vertices = {}
|
||||||
|
@log = other.log.dup
|
||||||
traverse = lambda do |new_v, old_v|
|
traverse = lambda do |new_v, old_v|
|
||||||
return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
|
return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
|
||||||
old_v.outgoing_edges.each do |edge|
|
old_v.outgoing_edges.each do |edge|
|
||||||
|
@ -75,6 +98,22 @@ module Gem::Resolver::Molinillo
|
||||||
"#{self.class}:#{vertices.values.inspect}"
|
"#{self.class}:#{vertices.values.inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [String] Returns a dot format representation of the graph
|
||||||
|
def to_dot
|
||||||
|
dot_vertices = []
|
||||||
|
dot_edges = []
|
||||||
|
vertices.each do |n, v|
|
||||||
|
dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
|
||||||
|
v.outgoing_edges.each do |e|
|
||||||
|
dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=\"#{e.requirement}\"]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dot_vertices.sort!
|
||||||
|
dot_edges.sort!
|
||||||
|
dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
|
||||||
|
dot.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
# @return [Boolean] whether the two dependency graphs are equal, determined
|
# @return [Boolean] whether the two dependency graphs are equal, determined
|
||||||
# by a recursive traversal of each {#root_vertices} and its
|
# by a recursive traversal of each {#root_vertices} and its
|
||||||
# {Vertex#successors}
|
# {Vertex#successors}
|
||||||
|
@ -93,12 +132,9 @@ module Gem::Resolver::Molinillo
|
||||||
# @param [Object] requirement the requirement that is requiring the child
|
# @param [Object] requirement the requirement that is requiring the child
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def add_child_vertex(name, payload, parent_names, requirement)
|
def add_child_vertex(name, payload, parent_names, requirement)
|
||||||
vertex = add_vertex(name, payload)
|
root = !parent_names.delete(nil) { true }
|
||||||
|
vertex = add_vertex(name, payload, root)
|
||||||
parent_names.each do |parent_name|
|
parent_names.each do |parent_name|
|
||||||
unless parent_name
|
|
||||||
vertex.root = true
|
|
||||||
next
|
|
||||||
end
|
|
||||||
parent_node = vertex_named(parent_name)
|
parent_node = vertex_named(parent_name)
|
||||||
add_edge(parent_node, vertex, requirement)
|
add_edge(parent_node, vertex, requirement)
|
||||||
end
|
end
|
||||||
|
@ -110,10 +146,7 @@ module Gem::Resolver::Molinillo
|
||||||
# @param [Object] payload
|
# @param [Object] payload
|
||||||
# @return [Vertex] the vertex that was added to `self`
|
# @return [Vertex] the vertex that was added to `self`
|
||||||
def add_vertex(name, payload, root = false)
|
def add_vertex(name, payload, root = false)
|
||||||
vertex = vertices[name] ||= Vertex.new(name, payload)
|
log.add_vertex(self, name, payload, root)
|
||||||
vertex.payload ||= payload
|
|
||||||
vertex.root ||= root
|
|
||||||
vertex
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
|
# Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
|
||||||
|
@ -121,16 +154,7 @@ module Gem::Resolver::Molinillo
|
||||||
# @param [String] name
|
# @param [String] name
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def detach_vertex_named(name)
|
def detach_vertex_named(name)
|
||||||
return unless vertex = vertices.delete(name)
|
log.detach_vertex_named(self, name)
|
||||||
vertex.outgoing_edges.each do |e|
|
|
||||||
v = e.destination
|
|
||||||
v.incoming_edges.delete(e)
|
|
||||||
detach_vertex_named(v.name) unless v.root? || v.predecessors.any?
|
|
||||||
end
|
|
||||||
vertex.incoming_edges.each do |e|
|
|
||||||
v = e.origin
|
|
||||||
v.outgoing_edges.delete(e)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [String] name
|
# @param [String] name
|
||||||
|
@ -158,134 +182,22 @@ module Gem::Resolver::Molinillo
|
||||||
add_edge_no_circular(origin, destination, requirement)
|
add_edge_no_circular(origin, destination, requirement)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Sets the payload of the vertex with the given name
|
||||||
|
# @param [String] name the name of the vertex
|
||||||
|
# @param [Object] payload the payload
|
||||||
|
# @return [Void]
|
||||||
|
def set_payload(name, payload)
|
||||||
|
log.set_payload(self, name, payload)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Adds a new {Edge} to the dependency graph without checking for
|
# Adds a new {Edge} to the dependency graph without checking for
|
||||||
# circularity.
|
# circularity.
|
||||||
|
# @param (see #add_edge)
|
||||||
|
# @return (see #add_edge)
|
||||||
def add_edge_no_circular(origin, destination, requirement)
|
def add_edge_no_circular(origin, destination, requirement)
|
||||||
edge = Edge.new(origin, destination, requirement)
|
log.add_edge_no_circular(self, origin.name, destination.name, requirement)
|
||||||
origin.outgoing_edges << edge
|
|
||||||
destination.incoming_edges << edge
|
|
||||||
edge
|
|
||||||
end
|
|
||||||
|
|
||||||
# A vertex in a {DependencyGraph} that encapsulates a {#name} and a
|
|
||||||
# {#payload}
|
|
||||||
class Vertex
|
|
||||||
# @return [String] the name of the vertex
|
|
||||||
attr_accessor :name
|
|
||||||
|
|
||||||
# @return [Object] the payload the vertex holds
|
|
||||||
attr_accessor :payload
|
|
||||||
|
|
||||||
# @return [Arrary<Object>] the explicit requirements that required
|
|
||||||
# this vertex
|
|
||||||
attr_reader :explicit_requirements
|
|
||||||
|
|
||||||
# @return [Boolean] whether the vertex is considered a root vertex
|
|
||||||
attr_accessor :root
|
|
||||||
alias_method :root?, :root
|
|
||||||
|
|
||||||
# Initializes a vertex with the given name and payload.
|
|
||||||
# @param [String] name see {#name}
|
|
||||||
# @param [Object] payload see {#payload}
|
|
||||||
def initialize(name, payload)
|
|
||||||
@name = name
|
|
||||||
@payload = payload
|
|
||||||
@explicit_requirements = []
|
|
||||||
@outgoing_edges = []
|
|
||||||
@incoming_edges = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Object>] all of the requirements that required
|
|
||||||
# this vertex
|
|
||||||
def requirements
|
|
||||||
incoming_edges.map(&:requirement) + explicit_requirements
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
|
||||||
# {Edge#origin}
|
|
||||||
attr_accessor :outgoing_edges
|
|
||||||
|
|
||||||
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
|
||||||
# {Edge#destination}
|
|
||||||
attr_accessor :incoming_edges
|
|
||||||
|
|
||||||
# @return [Array<Vertex>] the vertices of {#graph} that have an edge with
|
|
||||||
# `self` as their {Edge#destination}
|
|
||||||
def predecessors
|
|
||||||
incoming_edges.map(&:origin)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Vertex>] the vertices of {#graph} where `self` is a
|
|
||||||
# {#descendent?}
|
|
||||||
def recursive_predecessors
|
|
||||||
vertices = predecessors
|
|
||||||
vertices += vertices.map(&:recursive_predecessors).flatten(1)
|
|
||||||
vertices.uniq!
|
|
||||||
vertices
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Vertex>] the vertices of {#graph} that have an edge with
|
|
||||||
# `self` as their {Edge#origin}
|
|
||||||
def successors
|
|
||||||
outgoing_edges.map(&:destination)
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Array<Vertex>] the vertices of {#graph} where `self` is an
|
|
||||||
# {#ancestor?}
|
|
||||||
def recursive_successors
|
|
||||||
vertices = successors
|
|
||||||
vertices += vertices.map(&:recursive_successors).flatten(1)
|
|
||||||
vertices.uniq!
|
|
||||||
vertices
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [String] a string suitable for debugging
|
|
||||||
def inspect
|
|
||||||
"#{self.class}:#{name}(#{payload.inspect})"
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [Boolean] whether the two vertices are equal, determined
|
|
||||||
# by a recursive traversal of each {Vertex#successors}
|
|
||||||
def ==(other)
|
|
||||||
shallow_eql?(other) &&
|
|
||||||
successors.to_set == other.successors.to_set
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [Vertex] other the other vertex to compare to
|
|
||||||
# @return [Boolean] whether the two vertices are equal, determined
|
|
||||||
# solely by {#name} and {#payload} equality
|
|
||||||
def shallow_eql?(other)
|
|
||||||
other &&
|
|
||||||
name == other.name &&
|
|
||||||
payload == other.payload
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :eql?, :==
|
|
||||||
|
|
||||||
# @return [Fixnum] a hash for the vertex based upon its {#name}
|
|
||||||
def hash
|
|
||||||
name.hash
|
|
||||||
end
|
|
||||||
|
|
||||||
# Is there a path from `self` to `other` following edges in the
|
|
||||||
# dependency graph?
|
|
||||||
# @return true iff there is a path following edges within this {#graph}
|
|
||||||
def path_to?(other)
|
|
||||||
equal?(other) || successors.any? { |v| v.path_to?(other) }
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :descendent?, :path_to?
|
|
||||||
|
|
||||||
# Is there a path from `other` to `self` following edges in the
|
|
||||||
# dependency graph?
|
|
||||||
# @return true iff there is a path following edges within this {#graph}
|
|
||||||
def ancestor?(other)
|
|
||||||
other.path_to?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :is_reachable_from?, :ancestor?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# An action that modifies a {DependencyGraph} that is reversible.
|
||||||
|
# @abstract
|
||||||
|
class Action
|
||||||
|
# rubocop:disable Lint/UnusedMethodArgument
|
||||||
|
|
||||||
|
# @return [Symbol] The name of the action.
|
||||||
|
def self.name
|
||||||
|
raise 'Abstract'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Performs the action on the given graph.
|
||||||
|
# @param [DependencyGraph] graph the graph to perform the action on.
|
||||||
|
# @return [Void]
|
||||||
|
def up(graph)
|
||||||
|
raise 'Abstract'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reverses the action on the given graph.
|
||||||
|
# @param [DependencyGraph] graph the graph to reverse the action on.
|
||||||
|
# @return [Void]
|
||||||
|
def down(graph)
|
||||||
|
raise 'Abstract'
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Action,Nil] The previous action
|
||||||
|
attr_accessor :previous
|
||||||
|
|
||||||
|
# @return [Action,Nil] The next action
|
||||||
|
attr_accessor :next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,58 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# @!visibility private
|
||||||
|
# (see DependencyGraph#add_edge_no_circular)
|
||||||
|
class AddEdgeNoCircular < Action
|
||||||
|
# @!group Action
|
||||||
|
|
||||||
|
# (see Action.name)
|
||||||
|
def self.name
|
||||||
|
:add_vertex
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#up)
|
||||||
|
def up(graph)
|
||||||
|
edge = make_edge(graph)
|
||||||
|
edge.origin.outgoing_edges << edge
|
||||||
|
edge.destination.incoming_edges << edge
|
||||||
|
edge
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#down)
|
||||||
|
def down(graph)
|
||||||
|
edge = make_edge(graph)
|
||||||
|
edge.origin.outgoing_edges.delete(edge)
|
||||||
|
edge.destination.incoming_edges.delete(edge)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!group AddEdgeNoCircular
|
||||||
|
|
||||||
|
# @return [String] the name of the origin of the edge
|
||||||
|
attr_reader :origin
|
||||||
|
|
||||||
|
# @return [String] the name of the destination of the edge
|
||||||
|
attr_reader :destination
|
||||||
|
|
||||||
|
# @return [Object] the requirement that the edge represents
|
||||||
|
attr_reader :requirement
|
||||||
|
|
||||||
|
# @param [DependencyGraph] graph the graph to find vertices from
|
||||||
|
# @return [Edge] The edge this action adds
|
||||||
|
def make_edge(graph)
|
||||||
|
Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Initialize an action to add an edge to a dependency graph
|
||||||
|
# @param [String] origin the name of the origin of the edge
|
||||||
|
# @param [String] destination the name of the destination of the edge
|
||||||
|
# @param [Object] requirement the requirement that the edge represents
|
||||||
|
def initialize(origin, destination, requirement)
|
||||||
|
@origin = origin
|
||||||
|
@destination = destination
|
||||||
|
@requirement = requirement
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,61 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# @!visibility private
|
||||||
|
# (see DependencyGraph#add_vertex)
|
||||||
|
class AddVertex < Action # :nodoc:
|
||||||
|
# @!group Action
|
||||||
|
|
||||||
|
# (see Action.name)
|
||||||
|
def self.name
|
||||||
|
:add_vertex
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#up)
|
||||||
|
def up(graph)
|
||||||
|
if existing = graph.vertices[name]
|
||||||
|
@existing_payload = existing.payload
|
||||||
|
@existing_root = existing.root
|
||||||
|
end
|
||||||
|
vertex = existing || Vertex.new(name, payload)
|
||||||
|
graph.vertices[vertex.name] = vertex
|
||||||
|
vertex.payload ||= payload
|
||||||
|
vertex.root ||= root
|
||||||
|
vertex
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#down)
|
||||||
|
def down(graph)
|
||||||
|
if defined?(@existing_payload)
|
||||||
|
vertex = graph.vertices[name]
|
||||||
|
vertex.payload = @existing_payload
|
||||||
|
vertex.root = @existing_root
|
||||||
|
else
|
||||||
|
graph.vertices.delete(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!group AddVertex
|
||||||
|
|
||||||
|
# @return [String] the name of the vertex
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
# @return [Object] the payload for the vertex
|
||||||
|
attr_reader :payload
|
||||||
|
|
||||||
|
# @return [Boolean] whether the vertex is root or not
|
||||||
|
attr_reader :root
|
||||||
|
|
||||||
|
# Initialize an action to add a vertex to a dependency graph
|
||||||
|
# @param [String] name the name of the vertex
|
||||||
|
# @param [Object] payload the payload for the vertex
|
||||||
|
# @param [Boolean] root whether the vertex is root or not
|
||||||
|
def initialize(name, payload, root)
|
||||||
|
@name = name
|
||||||
|
@payload = payload
|
||||||
|
@root = root
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,53 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# @!visibility private
|
||||||
|
# @see DependencyGraph#detach_vertex_named
|
||||||
|
class DetachVertexNamed < Action
|
||||||
|
# @!group Action
|
||||||
|
|
||||||
|
# (see Action#name)
|
||||||
|
def self.name
|
||||||
|
:add_vertex
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#up)
|
||||||
|
def up(graph)
|
||||||
|
return unless @vertex = graph.vertices.delete(name)
|
||||||
|
@vertex.outgoing_edges.each do |e|
|
||||||
|
v = e.destination
|
||||||
|
v.incoming_edges.delete(e)
|
||||||
|
graph.detach_vertex_named(v.name) unless v.root? || v.predecessors.any?
|
||||||
|
end
|
||||||
|
@vertex.incoming_edges.each do |e|
|
||||||
|
v = e.origin
|
||||||
|
v.outgoing_edges.delete(e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#down)
|
||||||
|
def down(graph)
|
||||||
|
return unless @vertex
|
||||||
|
graph.vertices[@vertex.name] = @vertex
|
||||||
|
@vertex.outgoing_edges.each do |e|
|
||||||
|
e.destination.incoming_edges << e
|
||||||
|
end
|
||||||
|
@vertex.incoming_edges.each do |e|
|
||||||
|
e.origin.outgoing_edges << e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!group DetachVertexNamed
|
||||||
|
|
||||||
|
# @return [String] the name of the vertex to detach
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
# Initialize an action to detach a vertex from a dependency graph
|
||||||
|
# @param [String] name the name of the vertex to detach
|
||||||
|
def initialize(name)
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,114 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular'
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex'
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named'
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload'
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag'
|
||||||
|
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# A log for dependency graph actions
|
||||||
|
class Log
|
||||||
|
# Initializes an empty log
|
||||||
|
def initialize
|
||||||
|
@current_action = @first_action = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!macro [new] action
|
||||||
|
# {include:DependencyGraph#$0}
|
||||||
|
# @param [Graph] graph the graph to perform the action on
|
||||||
|
# @param (see DependencyGraph#$0)
|
||||||
|
# @return (see DependencyGraph#$0)
|
||||||
|
|
||||||
|
# @macro action
|
||||||
|
def tag(graph, tag)
|
||||||
|
push_action(graph, Tag.new(tag))
|
||||||
|
end
|
||||||
|
|
||||||
|
# @macro action
|
||||||
|
def add_vertex(graph, name, payload, root)
|
||||||
|
push_action(graph, AddVertex.new(name, payload, root))
|
||||||
|
end
|
||||||
|
|
||||||
|
# @macro action
|
||||||
|
def detach_vertex_named(graph, name)
|
||||||
|
push_action(graph, DetachVertexNamed.new(name))
|
||||||
|
end
|
||||||
|
|
||||||
|
# @macro action
|
||||||
|
def add_edge_no_circular(graph, origin, destination, requirement)
|
||||||
|
push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement))
|
||||||
|
end
|
||||||
|
|
||||||
|
# @macro action
|
||||||
|
def set_payload(graph, name, payload)
|
||||||
|
push_action(graph, SetPayload.new(name, payload))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pops the most recent action from the log and undoes the action
|
||||||
|
# @param [DependencyGraph] graph
|
||||||
|
# @return [Action] the action that was popped off the log
|
||||||
|
def pop!(graph)
|
||||||
|
return unless action = @current_action
|
||||||
|
unless @current_action = action.previous
|
||||||
|
@first_action = nil
|
||||||
|
end
|
||||||
|
action.down(graph)
|
||||||
|
action
|
||||||
|
end
|
||||||
|
|
||||||
|
extend Enumerable
|
||||||
|
|
||||||
|
# @!visibility private
|
||||||
|
# Enumerates each action in the log
|
||||||
|
# @yield [Action]
|
||||||
|
def each
|
||||||
|
return enum_for unless block_given?
|
||||||
|
action = @first_action
|
||||||
|
loop do
|
||||||
|
break unless action
|
||||||
|
yield action
|
||||||
|
action = action.next
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!visibility private
|
||||||
|
# Enumerates each action in the log in reverse order
|
||||||
|
# @yield [Action]
|
||||||
|
def reverse_each
|
||||||
|
return enum_for(:reverse_each) unless block_given?
|
||||||
|
action = @current_action
|
||||||
|
loop do
|
||||||
|
break unless action
|
||||||
|
yield action
|
||||||
|
action = action.previous
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# @macro action
|
||||||
|
def rewind_to(graph, tag)
|
||||||
|
loop do
|
||||||
|
action = pop!(graph)
|
||||||
|
raise "No tag #{tag.inspect} found" unless action
|
||||||
|
break if action.class.name == :tag && action.tag == tag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Adds the given action to the log, running the action
|
||||||
|
# @param [DependencyGraph] graph
|
||||||
|
# @param [Action] action
|
||||||
|
# @return The value returned by `action.up`
|
||||||
|
def push_action(graph, action)
|
||||||
|
action.previous = @current_action
|
||||||
|
@current_action.next = action if @current_action
|
||||||
|
@current_action = action
|
||||||
|
@first_action ||= action
|
||||||
|
action.up(graph)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# @!visibility private
|
||||||
|
# @see DependencyGraph#set_payload
|
||||||
|
class SetPayload < Action # :nodoc:
|
||||||
|
# @!group Action
|
||||||
|
|
||||||
|
# (see Action.name)
|
||||||
|
def self.name
|
||||||
|
:set_payload
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#up)
|
||||||
|
def up(graph)
|
||||||
|
vertex = graph.vertex_named(name)
|
||||||
|
@old_payload = vertex.payload
|
||||||
|
vertex.payload = payload
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#down)
|
||||||
|
def down(graph)
|
||||||
|
graph.vertex_named(name).payload = @old_payload
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!group SetPayload
|
||||||
|
|
||||||
|
# @return [String] the name of the vertex
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
# @return [Object] the payload for the vertex
|
||||||
|
attr_reader :payload
|
||||||
|
|
||||||
|
# Initialize an action to add set the payload for a vertex in a dependency
|
||||||
|
# graph
|
||||||
|
# @param [String] name the name of the vertex
|
||||||
|
# @param [Object] payload the payload for the vertex
|
||||||
|
def initialize(name, payload)
|
||||||
|
@name = name
|
||||||
|
@payload = payload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# @!visibility private
|
||||||
|
# @see DependencyGraph#tag
|
||||||
|
class Tag < Action
|
||||||
|
# @!group Action
|
||||||
|
|
||||||
|
# (see Action.name)
|
||||||
|
def self.name
|
||||||
|
:tag
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#up)
|
||||||
|
def up(_graph)
|
||||||
|
end
|
||||||
|
|
||||||
|
# (see Action#down)
|
||||||
|
def down(_graph)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @!group Tag
|
||||||
|
|
||||||
|
# @return [Object] An opaque tag
|
||||||
|
attr_reader :tag
|
||||||
|
|
||||||
|
# Initialize an action to tag a state of a dependency graph
|
||||||
|
# @param [Object] tag an opaque tag
|
||||||
|
def initialize(tag)
|
||||||
|
@tag = tag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,123 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Gem::Resolver::Molinillo
|
||||||
|
class DependencyGraph
|
||||||
|
# A vertex in a {DependencyGraph} that encapsulates a {#name} and a
|
||||||
|
# {#payload}
|
||||||
|
class Vertex
|
||||||
|
# @return [String] the name of the vertex
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
# @return [Object] the payload the vertex holds
|
||||||
|
attr_accessor :payload
|
||||||
|
|
||||||
|
# @return [Arrary<Object>] the explicit requirements that required
|
||||||
|
# this vertex
|
||||||
|
attr_reader :explicit_requirements
|
||||||
|
|
||||||
|
# @return [Boolean] whether the vertex is considered a root vertex
|
||||||
|
attr_accessor :root
|
||||||
|
alias root? root
|
||||||
|
|
||||||
|
# Initializes a vertex with the given name and payload.
|
||||||
|
# @param [String] name see {#name}
|
||||||
|
# @param [Object] payload see {#payload}
|
||||||
|
def initialize(name, payload)
|
||||||
|
@name = name.frozen? ? name : name.dup.freeze
|
||||||
|
@payload = payload
|
||||||
|
@explicit_requirements = []
|
||||||
|
@outgoing_edges = []
|
||||||
|
@incoming_edges = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array<Object>] all of the requirements that required
|
||||||
|
# this vertex
|
||||||
|
def requirements
|
||||||
|
incoming_edges.map(&:requirement) + explicit_requirements
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
||||||
|
# {Edge#origin}
|
||||||
|
attr_accessor :outgoing_edges
|
||||||
|
|
||||||
|
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
||||||
|
# {Edge#destination}
|
||||||
|
attr_accessor :incoming_edges
|
||||||
|
|
||||||
|
# @return [Array<Vertex>] the vertices of {#graph} that have an edge with
|
||||||
|
# `self` as their {Edge#destination}
|
||||||
|
def predecessors
|
||||||
|
incoming_edges.map(&:origin)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array<Vertex>] the vertices of {#graph} where `self` is a
|
||||||
|
# {#descendent?}
|
||||||
|
def recursive_predecessors
|
||||||
|
vertices = predecessors
|
||||||
|
vertices += vertices.map(&:recursive_predecessors).flatten(1)
|
||||||
|
vertices.uniq!
|
||||||
|
vertices
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array<Vertex>] the vertices of {#graph} that have an edge with
|
||||||
|
# `self` as their {Edge#origin}
|
||||||
|
def successors
|
||||||
|
outgoing_edges.map(&:destination)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array<Vertex>] the vertices of {#graph} where `self` is an
|
||||||
|
# {#ancestor?}
|
||||||
|
def recursive_successors
|
||||||
|
vertices = successors
|
||||||
|
vertices += vertices.map(&:recursive_successors).flatten(1)
|
||||||
|
vertices.uniq!
|
||||||
|
vertices
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [String] a string suitable for debugging
|
||||||
|
def inspect
|
||||||
|
"#{self.class}:#{name}(#{payload.inspect})"
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Boolean] whether the two vertices are equal, determined
|
||||||
|
# by a recursive traversal of each {Vertex#successors}
|
||||||
|
def ==(other)
|
||||||
|
shallow_eql?(other) &&
|
||||||
|
successors.to_set == other.successors.to_set
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param [Vertex] other the other vertex to compare to
|
||||||
|
# @return [Boolean] whether the two vertices are equal, determined
|
||||||
|
# solely by {#name} and {#payload} equality
|
||||||
|
def shallow_eql?(other)
|
||||||
|
other &&
|
||||||
|
name == other.name &&
|
||||||
|
payload == other.payload
|
||||||
|
end
|
||||||
|
|
||||||
|
alias eql? ==
|
||||||
|
|
||||||
|
# @return [Fixnum] a hash for the vertex based upon its {#name}
|
||||||
|
def hash
|
||||||
|
name.hash
|
||||||
|
end
|
||||||
|
|
||||||
|
# Is there a path from `self` to `other` following edges in the
|
||||||
|
# dependency graph?
|
||||||
|
# @return true iff there is a path following edges within this {#graph}
|
||||||
|
def path_to?(other)
|
||||||
|
equal?(other) || successors.any? { |v| v.path_to?(other) }
|
||||||
|
end
|
||||||
|
|
||||||
|
alias descendent? path_to?
|
||||||
|
|
||||||
|
# Is there a path from `other` to `self` following edges in the
|
||||||
|
# dependency graph?
|
||||||
|
# @return true iff there is a path following edges within this {#graph}
|
||||||
|
def ancestor?(other)
|
||||||
|
other.path_to?(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias is_reachable_from? ancestor?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -26,7 +26,7 @@ module Gem::Resolver::Molinillo
|
||||||
def message
|
def message
|
||||||
sources = required_by.map { |r| "`#{r}`" }.join(' and ')
|
sources = required_by.map { |r| "`#{r}`" }.join(' and ')
|
||||||
message = "Unable to find a specification for `#{dependency}`"
|
message = "Unable to find a specification for `#{dependency}`"
|
||||||
message << " depended upon by #{sources}" unless sources.empty?
|
message += " depended upon by #{sources}" unless sources.empty?
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Gem::Resolver::Molinillo
|
module Gem::Resolver::Molinillo
|
||||||
# The version of Gem::Resolver::Molinillo.
|
# The version of Gem::Resolver::Molinillo.
|
||||||
VERSION = '0.4.3'.freeze
|
VERSION = '0.5.0'.freeze
|
||||||
end
|
end
|
||||||
|
|
|
@ -52,6 +52,7 @@ module Gem::Resolver::Molinillo
|
||||||
@base = base
|
@base = base
|
||||||
@states = []
|
@states = []
|
||||||
@iteration_counter = 0
|
@iteration_counter = 0
|
||||||
|
@parent_of = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Resolves the {#original_requested} dependencies into a full dependency
|
# Resolves the {#original_requested} dependencies into a full dependency
|
||||||
|
@ -67,7 +68,12 @@ module Gem::Resolver::Molinillo
|
||||||
indicate_progress
|
indicate_progress
|
||||||
if state.respond_to?(:pop_possibility_state) # DependencyState
|
if state.respond_to?(:pop_possibility_state) # DependencyState
|
||||||
debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
|
debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
|
||||||
state.pop_possibility_state.tap { |s| states.push(s) if s }
|
state.pop_possibility_state.tap do |s|
|
||||||
|
if s
|
||||||
|
states.push(s)
|
||||||
|
activated.tag(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
process_topmost_state
|
process_topmost_state
|
||||||
end
|
end
|
||||||
|
@ -118,27 +124,11 @@ module Gem::Resolver::Molinillo
|
||||||
require 'rubygems/resolver/molinillo/lib/molinillo/state'
|
require 'rubygems/resolver/molinillo/lib/molinillo/state'
|
||||||
require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider'
|
require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider'
|
||||||
|
|
||||||
ResolutionState.new.members.each do |member|
|
require 'rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state'
|
||||||
define_method member do |*args, &block|
|
require 'rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider'
|
||||||
current_state = state || ResolutionState.empty
|
|
||||||
current_state.send(member, *args, &block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
SpecificationProvider.instance_methods(false).each do |instance_method|
|
include Gem::Resolver::Molinillo::Delegates::ResolutionState
|
||||||
define_method instance_method do |*args, &block|
|
include Gem::Resolver::Molinillo::Delegates::SpecificationProvider
|
||||||
begin
|
|
||||||
specification_provider.send(instance_method, *args, &block)
|
|
||||||
rescue NoSuchDependencyError => error
|
|
||||||
if state
|
|
||||||
vertex = activated.vertex_named(name_for error.dependency)
|
|
||||||
error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
|
|
||||||
error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
|
|
||||||
end
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Processes the topmost available {RequirementState} on the stack
|
# Processes the topmost available {RequirementState} on the stack
|
||||||
# @return [void]
|
# @return [void]
|
||||||
|
@ -169,6 +159,7 @@ module Gem::Resolver::Molinillo
|
||||||
def initial_state
|
def initial_state
|
||||||
graph = DependencyGraph.new.tap do |dg|
|
graph = DependencyGraph.new.tap do |dg|
|
||||||
original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } }
|
original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } }
|
||||||
|
dg.tag(:initial_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
requirements = sort_dependencies(original_requested, graph, {})
|
requirements = sort_dependencies(original_requested, graph, {})
|
||||||
|
@ -189,8 +180,9 @@ module Gem::Resolver::Molinillo
|
||||||
def unwind_for_conflict
|
def unwind_for_conflict
|
||||||
debug(depth) { "Unwinding for conflict: #{requirement}" }
|
debug(depth) { "Unwinding for conflict: #{requirement}" }
|
||||||
conflicts.tap do |c|
|
conflicts.tap do |c|
|
||||||
states.slice!((state_index_for_unwind + 1)..-1)
|
sliced_states = states.slice!((state_index_for_unwind + 1)..-1)
|
||||||
raise VersionConflict.new(c) unless state
|
raise VersionConflict.new(c) unless state
|
||||||
|
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
|
||||||
state.conflicts = c
|
state.conflicts = c
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -217,20 +209,14 @@ module Gem::Resolver::Molinillo
|
||||||
# @return [Object] the requirement that led to `requirement` being added
|
# @return [Object] the requirement that led to `requirement` being added
|
||||||
# to the list of requirements.
|
# to the list of requirements.
|
||||||
def parent_of(requirement)
|
def parent_of(requirement)
|
||||||
return nil unless requirement
|
@parent_of[requirement]
|
||||||
seen = false
|
|
||||||
state = states.reverse_each.find do |s|
|
|
||||||
seen ||= s.requirement == requirement || s.requirements.include?(requirement)
|
|
||||||
seen && s.requirement != requirement && !s.requirements.include?(requirement)
|
|
||||||
end
|
|
||||||
state && state.requirement
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [Object] the requirement that led to a version of a possibility
|
# @return [Object] the requirement that led to a version of a possibility
|
||||||
# with the given name being activated.
|
# with the given name being activated.
|
||||||
def requirement_for_existing_name(name)
|
def requirement_for_existing_name(name)
|
||||||
return nil unless activated.vertex_named(name).payload
|
return nil unless activated.vertex_named(name).payload
|
||||||
states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement
|
states.find { |s| s.name == name }.requirement
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [ResolutionState] the state whose `requirement` is the given
|
# @return [ResolutionState] the state whose `requirement` is the given
|
||||||
|
@ -250,19 +236,25 @@ module Gem::Resolver::Molinillo
|
||||||
# the {#possibility} in conjunction with the current {#state}
|
# the {#possibility} in conjunction with the current {#state}
|
||||||
def create_conflict
|
def create_conflict
|
||||||
vertex = activated.vertex_named(name)
|
vertex = activated.vertex_named(name)
|
||||||
requirements = {
|
locked_requirement = locked_requirement_named(name)
|
||||||
name_for_explicit_dependency_source => vertex.explicit_requirements,
|
|
||||||
name_for_locking_dependency_source => Array(locked_requirement_named(name)),
|
requirements = {}
|
||||||
}
|
unless vertex.explicit_requirements.empty?
|
||||||
|
requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
|
||||||
|
end
|
||||||
|
requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
|
||||||
vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) }
|
vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) }
|
||||||
|
|
||||||
|
activated_by_name = {}
|
||||||
|
activated.each { |v| activated_by_name[v.name] = v.payload if v.payload }
|
||||||
conflicts[name] = Conflict.new(
|
conflicts[name] = Conflict.new(
|
||||||
requirement,
|
requirement,
|
||||||
Hash[requirements.select { |_, r| !r.empty? }],
|
requirements,
|
||||||
vertex.payload,
|
vertex.payload,
|
||||||
possibility,
|
possibility,
|
||||||
locked_requirement_named(name),
|
locked_requirement,
|
||||||
requirement_trees,
|
requirement_trees,
|
||||||
Hash[activated.map { |v| [v.name, v.payload] }.select(&:last)]
|
activated_by_name
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -341,15 +333,16 @@ module Gem::Resolver::Molinillo
|
||||||
# spec with the given name
|
# spec with the given name
|
||||||
# @return [Boolean] Whether the possibility was swapped into {#activated}
|
# @return [Boolean] Whether the possibility was swapped into {#activated}
|
||||||
def attempt_to_swap_possibility
|
def attempt_to_swap_possibility
|
||||||
swapped = activated.dup
|
activated.tag(:swap)
|
||||||
vertex = swapped.vertex_named(name)
|
vertex = activated.vertex_named(name)
|
||||||
vertex.payload = possibility
|
activated.set_payload(name, possibility)
|
||||||
return unless vertex.requirements.
|
if !vertex.requirements.
|
||||||
all? { |r| requirement_satisfied_by?(r, swapped, possibility) }
|
all? { |r| requirement_satisfied_by?(r, activated, possibility) } ||
|
||||||
return unless new_spec_satisfied?
|
!new_spec_satisfied?
|
||||||
actual_vertex = activated.vertex_named(name)
|
activated.rewind_to(:swap)
|
||||||
actual_vertex.payload = possibility
|
return
|
||||||
fixup_swapped_children(actual_vertex)
|
end
|
||||||
|
fixup_swapped_children(vertex)
|
||||||
activate_spec
|
activate_spec
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -363,7 +356,13 @@ module Gem::Resolver::Molinillo
|
||||||
if !dep_names.include?(succ.name) && !succ.root? && succ.predecessors.to_a == [vertex]
|
if !dep_names.include?(succ.name) && !succ.root? && succ.predecessors.to_a == [vertex]
|
||||||
debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
|
debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
|
||||||
activated.detach_vertex_named(succ.name)
|
activated.detach_vertex_named(succ.name)
|
||||||
requirements.delete_if { |r| name_for(r) == succ.name }
|
|
||||||
|
all_successor_names = succ.recursive_successors.map(&:name)
|
||||||
|
|
||||||
|
requirements.delete_if do |requirement|
|
||||||
|
requirement_name = name_for(requirement)
|
||||||
|
(requirement_name == succ.name) || all_successor_names.include?(requirement_name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -406,8 +405,7 @@ module Gem::Resolver::Molinillo
|
||||||
def activate_spec
|
def activate_spec
|
||||||
conflicts.delete(name)
|
conflicts.delete(name)
|
||||||
debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
|
debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
|
||||||
vertex = activated.vertex_named(name)
|
activated.set_payload(name, possibility)
|
||||||
vertex.payload = possibility
|
|
||||||
require_nested_dependencies_for(possibility)
|
require_nested_dependencies_for(possibility)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -418,19 +416,22 @@ module Gem::Resolver::Molinillo
|
||||||
def require_nested_dependencies_for(activated_spec)
|
def require_nested_dependencies_for(activated_spec)
|
||||||
nested_dependencies = dependencies_for(activated_spec)
|
nested_dependencies = dependencies_for(activated_spec)
|
||||||
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
|
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
|
||||||
nested_dependencies.each { |d| activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d) }
|
nested_dependencies.each do |d|
|
||||||
|
activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d)
|
||||||
|
@parent_of[d] = requirement
|
||||||
|
end
|
||||||
|
|
||||||
push_state_for_requirements(requirements + nested_dependencies, nested_dependencies.size > 0)
|
push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pushes a new {DependencyState} that encapsulates both existing and new
|
# Pushes a new {DependencyState} that encapsulates both existing and new
|
||||||
# requirements
|
# requirements
|
||||||
# @param [Array] new_requirements
|
# @param [Array] new_requirements
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated.dup)
|
def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
|
||||||
new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
|
new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
|
||||||
new_requirement = new_requirements.shift
|
new_requirement = new_requirements.shift
|
||||||
new_name = new_requirement ? name_for(new_requirement) : ''
|
new_name = new_requirement ? name_for(new_requirement) : ''.freeze
|
||||||
possibilities = new_requirement ? search_for(new_requirement) : []
|
possibilities = new_requirement ? search_for(new_requirement) : []
|
||||||
handle_missing_or_push_dependency_state DependencyState.new(
|
handle_missing_or_push_dependency_state DependencyState.new(
|
||||||
new_name, new_requirements, new_activated,
|
new_name, new_requirements, new_activated,
|
||||||
|
@ -451,7 +452,7 @@ module Gem::Resolver::Molinillo
|
||||||
state.activated.detach_vertex_named(state.name)
|
state.activated.detach_vertex_named(state.name)
|
||||||
push_state_for_requirements(state.requirements.dup, false, state.activated)
|
push_state_for_requirements(state.requirements.dup, false, state.activated)
|
||||||
else
|
else
|
||||||
states.push state
|
states.push(state).tap { activated.tag(state) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,12 +36,14 @@ module Gem::Resolver::Molinillo
|
||||||
PossibilityState.new(
|
PossibilityState.new(
|
||||||
name,
|
name,
|
||||||
requirements.dup,
|
requirements.dup,
|
||||||
activated.dup,
|
activated,
|
||||||
requirement,
|
requirement,
|
||||||
[possibilities.pop],
|
[possibilities.pop],
|
||||||
depth + 1,
|
depth + 1,
|
||||||
conflicts.dup
|
conflicts.dup
|
||||||
)
|
).tap do |state|
|
||||||
|
state.activated.tag(state)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,8 @@ class Gem::Security::Signer
|
||||||
def sign data
|
def sign data
|
||||||
return unless @key
|
return unless @key
|
||||||
|
|
||||||
|
raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty?
|
||||||
|
|
||||||
if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
|
if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
|
||||||
re_sign_key
|
re_sign_key
|
||||||
end
|
end
|
||||||
|
|
|
@ -209,9 +209,9 @@ class Gem::Specification < Gem::BasicSpecification
|
||||||
##
|
##
|
||||||
# Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
|
# Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
|
||||||
# activated.
|
# activated.
|
||||||
#
|
#--
|
||||||
# See also #require_paths
|
# See also #require_paths
|
||||||
#
|
#++
|
||||||
# If you have an extension you do not need to add <code>"ext"</code> to the
|
# If you have an extension you do not need to add <code>"ext"</code> to the
|
||||||
# require path, the extension build process will copy the extension files
|
# require path, the extension build process will copy the extension files
|
||||||
# into "lib" for you.
|
# into "lib" for you.
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
|
|
||||||
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
|
|
||||||
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
|
|
||||||
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
|
|
||||||
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
|
|
||||||
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
|
|
||||||
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
|
|
||||||
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
|
|
||||||
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
|
|
||||||
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
|
||||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
|
|
||||||
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
|
|
||||||
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
|
|
||||||
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
|
|
||||||
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
|
|
||||||
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
|
|
||||||
-----END CERTIFICATE-----
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
|
||||||
|
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
|
||||||
|
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
|
||||||
|
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
|
||||||
|
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
|
||||||
|
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
|
||||||
|
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
|
||||||
|
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
|
||||||
|
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
|
||||||
|
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
|
||||||
|
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
|
||||||
|
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
|
||||||
|
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
|
||||||
|
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
|
||||||
|
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
|
||||||
|
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
|
||||||
|
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
|
||||||
|
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
|
||||||
|
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -8,7 +8,7 @@ require 'rubygems/request'
|
||||||
# The tested hosts are explained in detail here: https://github.com/rubygems/rubygems/commit/5e16a5428f973667cabfa07e94ff939e7a83ebd9
|
# The tested hosts are explained in detail here: https://github.com/rubygems/rubygems/commit/5e16a5428f973667cabfa07e94ff939e7a83ebd9
|
||||||
#
|
#
|
||||||
|
|
||||||
if ENV["TRAVIS"] || ENV["TEST_SSL"]
|
if ENV["CI"] || ENV["TEST_SSL"]
|
||||||
class TestBundledCA < Gem::TestCase
|
class TestBundledCA < Gem::TestCase
|
||||||
|
|
||||||
THIS_FILE = File.expand_path __FILE__
|
THIS_FILE = File.expand_path __FILE__
|
||||||
|
|
|
@ -1106,6 +1106,77 @@ gem 'other', version
|
||||||
assert_path_exists expected_makefile
|
assert_path_exists expected_makefile
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_install_extension_dir_is_removed_on_reinstall
|
||||||
|
@spec.extensions << "extconf.rb"
|
||||||
|
write_file File.join(@tempdir, "extconf.rb") do |io|
|
||||||
|
io.write <<-RUBY
|
||||||
|
require "mkmf"
|
||||||
|
create_makefile("#{@spec.name}")
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec.files += %w[extconf.rb]
|
||||||
|
|
||||||
|
path = Gem::Package.build @spec
|
||||||
|
|
||||||
|
# Install a gem with an extension
|
||||||
|
use_ui @ui do
|
||||||
|
installer = Gem::Installer.at path
|
||||||
|
installer.install
|
||||||
|
end
|
||||||
|
|
||||||
|
# pretend that a binary file was created as part of the build
|
||||||
|
should_be_removed = File.join(@spec.extension_dir, "#{@spec.name}.so")
|
||||||
|
write_file should_be_removed do |io|
|
||||||
|
io.write "DELETE ME ON REINSTALL"
|
||||||
|
end
|
||||||
|
assert_path_exists should_be_removed
|
||||||
|
|
||||||
|
# reinstall the gem, this is also the same as pristine
|
||||||
|
use_ui @ui do
|
||||||
|
installer = Gem::Installer.at path
|
||||||
|
installer.install
|
||||||
|
end
|
||||||
|
|
||||||
|
refute_path_exists should_be_removed
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_find_lib_file_after_install
|
||||||
|
@spec.extensions << "extconf.rb"
|
||||||
|
write_file File.join(@tempdir, "extconf.rb") do |io|
|
||||||
|
io.write <<-RUBY
|
||||||
|
require "mkmf"
|
||||||
|
create_makefile("#{@spec.name}")
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
write_file File.join(@tempdir, "a.c") do |io|
|
||||||
|
io.write <<-C
|
||||||
|
#include <ruby.h>
|
||||||
|
void Init_a() { }
|
||||||
|
C
|
||||||
|
end
|
||||||
|
|
||||||
|
Dir.mkdir File.join(@tempdir, "lib")
|
||||||
|
write_file File.join(@tempdir, 'lib', "b.rb") do |io|
|
||||||
|
io.write "# b.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec.files += %w[extconf.rb lib/b.rb a.c]
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
path = Gem::Package.build @spec
|
||||||
|
|
||||||
|
installer = Gem::Installer.at path
|
||||||
|
installer.install
|
||||||
|
end
|
||||||
|
|
||||||
|
expected = File.join @spec.full_require_paths.find { |path|
|
||||||
|
File.exist? File.join path, 'b.rb'
|
||||||
|
}, 'b.rb'
|
||||||
|
assert_equal expected, @spec.matches_for_glob('b.rb').first
|
||||||
|
end
|
||||||
|
|
||||||
def test_install_extension_and_script
|
def test_install_extension_and_script
|
||||||
@spec.extensions << "extconf.rb"
|
@spec.extensions << "extconf.rb"
|
||||||
write_file File.join(@tempdir, "extconf.rb") do |io|
|
write_file File.join(@tempdir, "extconf.rb") do |io|
|
||||||
|
|
|
@ -141,7 +141,9 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
|
|
||||||
FileUtils.mkdir_p 'lib'
|
FileUtils.mkdir_p 'lib'
|
||||||
open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
|
open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
|
||||||
File.symlink('lib/code.rb', 'lib/code_sym.rb')
|
|
||||||
|
# NOTE: 'code.rb' is correct, because it's relative to lib/code_sym.rb
|
||||||
|
File.symlink('code.rb', 'lib/code_sym.rb')
|
||||||
|
|
||||||
package = Gem::Package.new 'bogus.gem'
|
package = Gem::Package.new 'bogus.gem'
|
||||||
package.spec = spec
|
package.spec = spec
|
||||||
|
@ -156,12 +158,16 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
|
|
||||||
Gem::Package::TarReader.new tar do |tar_io|
|
Gem::Package::TarReader.new tar do |tar_io|
|
||||||
tar_io.each_entry do |entry|
|
tar_io.each_entry do |entry|
|
||||||
(entry.symlink? ? symlinks : files) << entry.full_name
|
if entry.symlink?
|
||||||
|
symlinks << { entry.full_name => entry.header.linkname }
|
||||||
|
else
|
||||||
|
files << entry.full_name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal %w[lib/code.rb], files
|
assert_equal %w[lib/code.rb], files
|
||||||
assert_equal %w[lib/code_sym.rb], symlinks
|
assert_equal [{'lib/code_sym.rb' => 'lib/code.rb'}], symlinks
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_build
|
def test_build
|
||||||
|
|
|
@ -229,6 +229,22 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
||||||
|
|
||||||
assert_equal ["#{'qwer/' * 19}bla", 'a' * 151],
|
assert_equal ["#{'qwer/' * 19}bla", 'a' * 151],
|
||||||
@tar_writer.split_name("#{'a' * 151}/#{'qwer/' * 19}bla")
|
@tar_writer.split_name("#{'a' * 151}/#{'qwer/' * 19}bla")
|
||||||
|
names = [
|
||||||
|
([''] + ['123456789'] * 9 + ['1234567890']).join('/'), # 101 bytes (several pieces)
|
||||||
|
(['123456789'] * 9 + ['1234567890'] + ['']).join('/'), # 101 bytes (several pieces)
|
||||||
|
'/' * 99,
|
||||||
|
'/' * 100,
|
||||||
|
'/' * 101,
|
||||||
|
'/' * 102,
|
||||||
|
]
|
||||||
|
names.each do |name|
|
||||||
|
newname, prefix = @tar_writer.split_name(name)
|
||||||
|
assert(!(newname.empty?), "split_name() returned empty name")
|
||||||
|
assert(newname.bytesize <= 100, "split_name() returned name longer than 100 bytes: '#{newname}' for '#{name}'")
|
||||||
|
assert(prefix.bytesize <= 155, "split_name() returned prefix longer than 155 bytes: '#{prefix}' for '#{name}'")
|
||||||
|
newname = [prefix, newname].join('/') unless prefix.empty?
|
||||||
|
assert_equal name, newname
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_split_name_too_long_name
|
def test_split_name_too_long_name
|
||||||
|
@ -240,6 +256,14 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
||||||
@tar_writer.split_name name
|
@tar_writer.split_name name
|
||||||
end
|
end
|
||||||
assert_includes exception.message, name
|
assert_includes exception.message, name
|
||||||
|
|
||||||
|
# note, GNU tar 1.28 is unable to handle this case too,
|
||||||
|
# tested with "tar --format=ustar -cPf /tmp/foo.tartar -- /aaaaaa....a"
|
||||||
|
name = '/' + 'a' * 100
|
||||||
|
exception = assert_raises Gem::Package::TooLongFileName do
|
||||||
|
@tar_writer.split_name name
|
||||||
|
end
|
||||||
|
assert_includes exception.message, name
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_split_name_too_long_prefix
|
def test_split_name_too_long_prefix
|
||||||
|
|
|
@ -81,6 +81,7 @@ gems:
|
||||||
# Generated via:
|
# Generated via:
|
||||||
# x = OpenSSL::PKey::DH.new(2048) # wait a while...
|
# x = OpenSSL::PKey::DH.new(2048) # wait a while...
|
||||||
# x.to_s => pem
|
# x.to_s => pem
|
||||||
|
# x.priv_key.to_s => hex for OpenSSL::BN.new
|
||||||
TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
|
TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
|
||||||
-----BEGIN DH PARAMETERS-----
|
-----BEGIN DH PARAMETERS-----
|
||||||
MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV
|
MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV
|
||||||
|
@ -92,6 +93,17 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
||||||
-----END DH PARAMETERS-----
|
-----END DH PARAMETERS-----
|
||||||
_end_of_pem_
|
_end_of_pem_
|
||||||
|
|
||||||
|
TEST_KEY_DH2048.priv_key = OpenSSL::BN.new("108911488509734781344423639" \
|
||||||
|
"5585749502236089033416160524030987005037540379474123441273555416835" \
|
||||||
|
"4725688238369352738266590757370603937618499698665047757588998555345" \
|
||||||
|
"3446251978586372525530219375408331096098220027413238477359960428372" \
|
||||||
|
"0195464393332338164504352015535549496585792320286513563739305843396" \
|
||||||
|
"9294344974028713065472959376197728193162272314514335882399554394661" \
|
||||||
|
"5306385003430991221886779612878793446851681835397455333989268503748" \
|
||||||
|
"7862488679178398716189205737442996155432191656080664090596502674943" \
|
||||||
|
"7902481557157485795980326766117882761941455140582265347052939604724" \
|
||||||
|
"964857770053363840471912215799994973597613931991572884", 16)
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
|
@proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
|
||||||
@old_proxies = @proxies.map {|k| ENV[k] }
|
@old_proxies = @proxies.map {|k| ENV[k] }
|
||||||
|
|
|
@ -205,5 +205,13 @@ c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_sign_no_certs
|
||||||
|
signer = Gem::Security::Signer.new ALTERNATE_KEY, []
|
||||||
|
|
||||||
|
assert_raises Gem::Security::Exception do
|
||||||
|
signer.sign 'hello'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end if defined?(OpenSSL::SSL)
|
end if defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue