Warning : file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found
in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88
Warning : Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215
Warning : Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216
Warning : Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217
Warning : Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218
Warning : Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219
Warning : Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK ! —®÷w w gem_metadata.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
# The version of Gem::Molinillo.
VERSION = '0.8.0'.freeze
end
PK ! Ë?ýÀp p errors.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
# An error that occurred during the resolution process
class ResolverError < StandardError; end
# An error caused by searching for a dependency that is completely unknown,
# i.e. has no versions available whatsoever.
class NoSuchDependencyError < ResolverError
# @return [Object] the dependency that could not be found
attr_accessor :dependency
# @return [Array] the specifications that depended upon {#dependency}
attr_accessor :required_by
# Initializes a new error with the given missing dependency.
# @param [Object] dependency @see {#dependency}
# @param [Array] required_by @see {#required_by}
def initialize(dependency, required_by = [])
@dependency = dependency
@required_by = required_by.uniq
super()
end
# The error message for the missing dependency, including the specifications
# that had this dependency.
def message
sources = required_by.map { |r| "`#{r}`" }.join(' and ')
message = "Unable to find a specification for `#{dependency}`"
message += " depended upon by #{sources}" unless sources.empty?
message
end
end
# An error caused by attempting to fulfil a dependency that was circular
#
# @note This exception will be thrown if and only if a {Vertex} is added to a
# {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
# existing {DependencyGraph::Vertex}
class CircularDependencyError < ResolverError
# [Set] the dependencies responsible for causing the error
attr_reader :dependencies
# Initializes a new error with the given circular vertices.
# @param [Array] vertices the vertices in the dependency
# that caused the error
def initialize(vertices)
super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
@dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
end
end
# An error caused by conflicts in version
class VersionConflict < ResolverError
# @return [{String => Resolution::Conflict}] the conflicts that caused
# resolution to fail
attr_reader :conflicts
# @return [SpecificationProvider] the specification provider used during
# resolution
attr_reader :specification_provider
# Initializes a new error with the given version conflicts.
# @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
# @param [SpecificationProvider] specification_provider see {#specification_provider}
def initialize(conflicts, specification_provider)
pairs = []
conflicts.values.flat_map(&:requirements).each do |conflicting|
conflicting.each do |source, conflict_requirements|
conflict_requirements.each do |c|
pairs << [c, source]
end
end
end
super "Unable to satisfy the following requirements:\n\n" \
"#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
@conflicts = conflicts
@specification_provider = specification_provider
end
require_relative 'delegates/specification_provider'
include Delegates::SpecificationProvider
# @return [String] An error message that includes requirement trees,
# which is much more detailed & customizable than the default message
# @param [Hash] opts the options to create a message with.
# @option opts [String] :solver_name The user-facing name of the solver
# @option opts [String] :possibility_type The generic name of a possibility
# @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
# @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
# @option opts [Proc] :additional_message_for_conflict A proc that appends additional
# messages for each conflict
# @option opts [Proc] :version_for_spec A proc that returns the version number for a
# possibility
def message_with_trees(opts = {})
solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
possibility_type = opts.delete(:possibility_type) { 'possibility named' }
reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
proc do |name, _conflict|
%(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
end
end
full_message_for_conflict = opts.delete(:full_message_for_conflict) do
proc do |name, conflict|
o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
if conflict.locked_requirement
o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
o << %(\n)
end
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
t
end.join("\n")
additional_message_for_conflict.call(o, name, conflict)
o
end
end
conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
o << full_message_for_conflict.call(name, conflict)
end.strip
end
end
end
PK ! N“讟 Ÿ dependency_graph/tag.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'action'
module Gem::Molinillo
class DependencyGraph
# @!visibility private
# @see DependencyGraph#tag
class Tag < Action
# @!group Action
# (see Action.action_name)
def self.action_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
PK ! Ü19»D D dependency_graph/vertex.rbnu „[µü¤ # frozen_string_literal: true
module Gem::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 [Array] 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] all of the requirements that required
# this vertex
def requirements
(incoming_edges.map(&:requirement) + explicit_requirements).uniq
end
# @return [Array] the edges of {#graph} that have `self` as their
# {Edge#origin}
attr_accessor :outgoing_edges
# @return [Array] the edges of {#graph} that have `self` as their
# {Edge#destination}
attr_accessor :incoming_edges
# @return [Array] the vertices of {#graph} that have an edge with
# `self` as their {Edge#destination}
def predecessors
incoming_edges.map(&:origin)
end
# @return [Set] the vertices of {#graph} where `self` is a
# {#descendent?}
def recursive_predecessors
_recursive_predecessors
end
# @param [Set] vertices the set to add the predecessors to
# @return [Set] the vertices of {#graph} where `self` is a
# {#descendent?}
def _recursive_predecessors(vertices = new_vertex_set)
incoming_edges.each do |edge|
vertex = edge.origin
next unless vertices.add?(vertex)
vertex._recursive_predecessors(vertices)
end
vertices
end
protected :_recursive_predecessors
# @return [Array] the vertices of {#graph} that have an edge with
# `self` as their {Edge#origin}
def successors
outgoing_edges.map(&:destination)
end
# @return [Set] the vertices of {#graph} where `self` is an
# {#ancestor?}
def recursive_successors
_recursive_successors
end
# @param [Set] vertices the set to add the successors to
# @return [Set] the vertices of {#graph} where `self` is an
# {#ancestor?}
def _recursive_successors(vertices = new_vertex_set)
outgoing_edges.each do |edge|
vertex = edge.destination
next unless vertices.add?(vertex)
vertex._recursive_successors(vertices)
end
vertices
end
protected :_recursive_successors
# @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)
return true if equal?(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)
return true if equal?(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 whether there is a path following edges within this {#graph}
def path_to?(other)
_path_to?(other)
end
alias descendent? path_to?
# @param [Vertex] other the vertex to check if there's a path to
# @param [Set] visited the vertices of {#graph} that have been visited
# @return [Boolean] whether there is a path to `other` from `self`
def _path_to?(other, visited = new_vertex_set)
return false unless visited.add?(self)
return true if equal?(other)
successors.any? { |v| v._path_to?(other, visited) }
end
protected :_path_to?
# Is there a path from `other` to `self` following edges in the
# dependency graph?
# @return whether there is a path following edges within this {#graph}
def ancestor?(other)
other.path_to?(self)
end
alias is_reachable_from? ancestor?
def new_vertex_set
require 'set'
Set.new
end
private :new_vertex_set
end
end
end
PK ! ŠåŠ?_ _ dependency_graph/add_vertex.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'action'
module Gem::Molinillo
class DependencyGraph
# @!visibility private
# (see DependencyGraph#add_vertex)
class AddVertex < Action # :nodoc:
# @!group Action
# (see Action.action_name)
def self.action_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
PK ! J„N˜ ˜ dependency_graph/action.rbnu „[µü¤ # frozen_string_literal: true
module Gem::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.action_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
PK ! •³8ú ú ' dependency_graph/detach_vertex_named.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'action'
module Gem::Molinillo
class DependencyGraph
# @!visibility private
# @see DependencyGraph#detach_vertex_named
class DetachVertexNamed < Action
# @!group Action
# (see Action#name)
def self.action_name
:add_vertex
end
# (see Action#up)
def up(graph)
return [] unless @vertex = graph.vertices.delete(name)
removed_vertices = [@vertex]
@vertex.outgoing_edges.each do |e|
v = e.destination
v.incoming_edges.delete(e)
if !v.root? && v.incoming_edges.empty?
removed_vertices.concat graph.detach_vertex_named(v.name)
end
end
@vertex.incoming_edges.each do |e|
v = e.origin
v.outgoing_edges.delete(e)
end
removed_vertices
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
PK ! U‰=… dependency_graph/delete_edge.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'action'
module Gem::Molinillo
class DependencyGraph
# @!visibility private
# (see DependencyGraph#delete_edge)
class DeleteEdge < Action
# @!group Action
# (see Action.action_name)
def self.action_name
:delete_edge
end
# (see Action#up)
def up(graph)
edge = make_edge(graph)
edge.origin.outgoing_edges.delete(edge)
edge.destination.incoming_edges.delete(edge)
end
# (see Action#down)
def down(graph)
edge = make_edge(graph)
edge.origin.outgoing_edges << edge
edge.destination.incoming_edges << edge
edge
end
# @!group DeleteEdge
# @return [String] the name of the origin of the edge
attr_reader :origin_name
# @return [String] the name of the destination of the edge
attr_reader :destination_name
# @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_name),
graph.vertex_named(destination_name),
requirement
)
end
# Initialize an action to add an edge to a dependency graph
# @param [String] origin_name the name of the origin of the edge
# @param [String] destination_name the name of the destination of the edge
# @param [Object] requirement the requirement that the edge represents
def initialize(origin_name, destination_name, requirement)
@origin_name = origin_name
@destination_name = destination_name
@requirement = requirement
end
end
end
end
PK ! Nèk k ( dependency_graph/add_edge_no_circular.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'action'
module Gem::Molinillo
class DependencyGraph
# @!visibility private
# (see DependencyGraph#add_edge_no_circular)
class AddEdgeNoCircular < Action
# @!group Action
# (see Action.action_name)
def self.action_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)
delete_first(edge.origin.outgoing_edges, edge)
delete_first(edge.destination.incoming_edges, 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
private
def delete_first(array, item)
return unless index = array.index(item)
array.delete_at(index)
end
end
end
end
PK ! fêg dependency_graph/log.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'add_edge_no_circular'
require_relative 'add_vertex'
require_relative 'delete_edge'
require_relative 'detach_vertex_named'
require_relative 'set_payload'
require_relative 'tag'
module Gem::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
# {include:DependencyGraph#delete_edge}
# @param [Graph] graph the graph to perform the action on
# @param [String] origin_name
# @param [String] destination_name
# @param [Object] requirement
# @return (see DependencyGraph#delete_edge)
def delete_edge(graph, origin_name, destination_name, requirement)
push_action(graph, DeleteEdge.new(origin_name, destination_name, 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.action_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
PK ! €û]€S S dependency_graph/set_payload.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'action'
module Gem::Molinillo
class DependencyGraph
# @!visibility private
# @see DependencyGraph#set_payload
class SetPayload < Action # :nodoc:
# @!group Action
# (see Action.action_name)
def self.action_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
PK ! êƒ
5b b # delegates/specification_provider.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
module Delegates
# Delegates all {Gem::Molinillo::SpecificationProvider} methods to a
# `#specification_provider` property.
module SpecificationProvider
# (see Gem::Molinillo::SpecificationProvider#search_for)
def search_for(dependency)
with_no_such_dependency_error_handling do
specification_provider.search_for(dependency)
end
end
# (see Gem::Molinillo::SpecificationProvider#dependencies_for)
def dependencies_for(specification)
with_no_such_dependency_error_handling do
specification_provider.dependencies_for(specification)
end
end
# (see Gem::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::Molinillo::SpecificationProvider#dependencies_equal?)
def dependencies_equal?(dependencies, other_dependencies)
with_no_such_dependency_error_handling do
specification_provider.dependencies_equal?(dependencies, other_dependencies)
end
end
# (see Gem::Molinillo::SpecificationProvider#name_for)
def name_for(dependency)
with_no_such_dependency_error_handling do
specification_provider.name_for(dependency)
end
end
# (see Gem::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::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::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::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
PK ! #:1V÷ ÷ delegates/resolution_state.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
# @!visibility private
module Delegates
# Delegates all {Gem::Molinillo::ResolutionState} methods to a `#state` property.
module ResolutionState
# (see Gem::Molinillo::ResolutionState#name)
def name
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.name
end
# (see Gem::Molinillo::ResolutionState#requirements)
def requirements
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.requirements
end
# (see Gem::Molinillo::ResolutionState#activated)
def activated
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.activated
end
# (see Gem::Molinillo::ResolutionState#requirement)
def requirement
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.requirement
end
# (see Gem::Molinillo::ResolutionState#possibilities)
def possibilities
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.possibilities
end
# (see Gem::Molinillo::ResolutionState#depth)
def depth
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.depth
end
# (see Gem::Molinillo::ResolutionState#conflicts)
def conflicts
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.conflicts
end
# (see Gem::Molinillo::ResolutionState#unused_unwind_options)
def unused_unwind_options
current_state = state || Gem::Molinillo::ResolutionState.empty
current_state.unused_unwind_options
end
end
end
end
PK ! Ë,á± resolver.rbnu „[µü¤ # frozen_string_literal: true
require_relative 'dependency_graph'
module Gem::Molinillo
# This class encapsulates a dependency resolver.
# The resolver is responsible for determining which set of dependencies to
# activate, with feedback from the {#specification_provider}
#
#
class Resolver
require_relative 'resolution'
# @return [SpecificationProvider] the specification provider used
# in the resolution process
attr_reader :specification_provider
# @return [UI] the UI module used to communicate back to the user
# during the resolution process
attr_reader :resolver_ui
# Initializes a new resolver.
# @param [SpecificationProvider] specification_provider
# see {#specification_provider}
# @param [UI] resolver_ui
# see {#resolver_ui}
def initialize(specification_provider, resolver_ui)
@specification_provider = specification_provider
@resolver_ui = resolver_ui
end
# Resolves the requested dependencies into a {DependencyGraph},
# locking to the base dependency graph (if specified)
# @param [Array] requested an array of 'requested' dependencies that the
# {#specification_provider} can understand
# @param [DependencyGraph,nil] base the base dependency graph to which
# dependencies should be 'locked'
def resolve(requested, base = DependencyGraph.new)
Resolution.new(specification_provider,
resolver_ui,
requested,
base).
resolve
end
end
end
PK ! ÕEÃ£Ç Ç
modules/ui.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
# Conveys information about the resolution process to a user.
module UI
# The {IO} object that should be used to print output. `STDOUT`, by default.
#
# @return [IO]
def output
STDOUT
end
# Called roughly every {#progress_rate}, this method should convey progress
# to the user.
#
# @return [void]
def indicate_progress
output.print '.' unless debug?
end
# How often progress should be conveyed to the user via
# {#indicate_progress}, in seconds. A third of a second, by default.
#
# @return [Float]
def progress_rate
0.33
end
# Called before resolution begins.
#
# @return [void]
def before_resolution
output.print 'Resolving dependencies...'
end
# Called after resolution ends (either successfully or with an error).
# By default, prints a newline.
#
# @return [void]
def after_resolution
output.puts
end
# Conveys debug information to the user.
#
# @param [Integer] depth the current depth of the resolution process.
# @return [void]
def debug(depth = 0)
if debug?
debug_info = yield
debug_info = debug_info.inspect unless debug_info.is_a?(String)
debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
output.puts debug_info
end
end
# Whether or not debug messages should be printed.
# By default, whether or not the `MOLINILLO_DEBUG` environment variable is
# set.
#
# @return [Boolean]
def debug?
return @debug_mode if defined?(@debug_mode)
@debug_mode = ENV['MOLINILLO_DEBUG']
end
end
end
PK ! ¦Ö…S S ! modules/specification_provider.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
# Provides information about specifications and dependencies to the resolver,
# allowing the {Resolver} class to remain generic while still providing power
# and flexibility.
#
# This module contains the methods that users of Gem::Molinillo must to implement,
# using knowledge of their own model classes.
module SpecificationProvider
# Search for the specifications that match the given dependency.
# The specifications in the returned array will be considered in reverse
# order, so the latest version ought to be last.
# @note This method should be 'pure', i.e. the return value should depend
# only on the `dependency` parameter.
#
# @param [Object] dependency
# @return [Array] the specifications that satisfy the given
# `dependency`.
def search_for(dependency)
[]
end
# Returns the dependencies of `specification`.
# @note This method should be 'pure', i.e. the return value should depend
# only on the `specification` parameter.
#
# @param [Object] specification
# @return [Array] the dependencies that are required by the given
# `specification`.
def dependencies_for(specification)
[]
end
# Determines whether the given `requirement` is satisfied by the given
# `spec`, in the context of the current `activated` dependency graph.
#
# @param [Object] requirement
# @param [DependencyGraph] activated the current dependency graph in the
# resolution process.
# @param [Object] spec
# @return [Boolean] whether `requirement` is satisfied by `spec` in the
# context of the current `activated` dependency graph.
def requirement_satisfied_by?(requirement, activated, spec)
true
end
# Determines whether two arrays of dependencies are equal, and thus can be
# grouped.
#
# @param [Array] dependencies
# @param [Array] other_dependencies
# @return [Boolean] whether `dependencies` and `other_dependencies` should
# be considered equal.
def dependencies_equal?(dependencies, other_dependencies)
dependencies == other_dependencies
end
# Returns the name for the given `dependency`.
# @note This method should be 'pure', i.e. the return value should depend
# only on the `dependency` parameter.
#
# @param [Object] dependency
# @return [String] the name for the given `dependency`.
def name_for(dependency)
dependency.to_s
end
# @return [String] the name of the source of explicit dependencies, i.e.
# those passed to {Resolver#resolve} directly.
def name_for_explicit_dependency_source
'user-specified dependency'
end
# @return [String] the name of the source of 'locked' dependencies, i.e.
# those passed to {Resolver#resolve} directly as the `base`
def name_for_locking_dependency_source
'Lockfile'
end
# Sort dependencies so that the ones that are easiest to resolve are first.
# Easiest to resolve is (usually) defined by:
# 1) Is this dependency already activated?
# 2) How relaxed are the requirements?
# 3) Are there any conflicts for this dependency?
# 4) How many possibilities are there to satisfy this dependency?
#
# @param [Array] dependencies
# @param [DependencyGraph] activated the current dependency graph in the
# resolution process.
# @param [{String => Array}] conflicts
# @return [Array] a sorted copy of `dependencies`.
def sort_dependencies(dependencies, activated, conflicts)
dependencies.sort_by do |dependency|
name = name_for(dependency)
[
activated.vertex_named(name).payload ? 0 : 1,
conflicts[name] ? 0 : 1,
]
end
end
# Returns whether this dependency, which has no possible matching
# specifications, can safely be ignored.
#
# @param [Object] dependency
# @return [Boolean] whether this dependency can safely be skipped.
def allow_missing?(dependency)
false
end
end
end
PK ! JTµÜ‚† ‚†
resolution.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
class Resolver
# A specific resolution from a given {Resolver}
class Resolution
# A conflict that the resolution process encountered
# @attr [Object] requirement the requirement that immediately led to the conflict
# @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
# @attr [Object, nil] existing the existing spec that was in conflict with
# the {#possibility}
# @attr [Object] possibility_set the set of specs that was unable to be
# activated due to a conflict.
# @attr [Object] locked_requirement the relevant locking requirement.
# @attr [Array>] requirement_trees the different requirement
# trees that led to every requirement for the conflicting name.
# @attr [{String=>Object}] activated_by_name the already-activated specs.
# @attr [Object] underlying_error an error that has occurred during resolution, and
# will be raised at the end of it if no resolution is found.
Conflict = Struct.new(
:requirement,
:requirements,
:existing,
:possibility_set,
:locked_requirement,
:requirement_trees,
:activated_by_name,
:underlying_error
)
class Conflict
# @return [Object] a spec that was unable to be activated due to a conflict
def possibility
possibility_set && possibility_set.latest_version
end
end
# A collection of possibility states that share the same dependencies
# @attr [Array] dependencies the dependencies for this set of possibilities
# @attr [Array] possibilities the possibilities
PossibilitySet = Struct.new(:dependencies, :possibilities)
class PossibilitySet
# String representation of the possibility set, for debugging
def to_s
"[#{possibilities.join(', ')}]"
end
# @return [Object] most up-to-date dependency in the possibility set
def latest_version
possibilities.last
end
end
# Details of the state to unwind to when a conflict occurs, and the cause of the unwind
# @attr [Integer] state_index the index of the state to unwind to
# @attr [Object] state_requirement the requirement of the state we're unwinding to
# @attr [Array] requirement_tree for the requirement we're relaxing
# @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
# @attr [Array] requirement_trees for the conflict
# @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
UnwindDetails = Struct.new(
:state_index,
:state_requirement,
:requirement_tree,
:conflicting_requirements,
:requirement_trees,
:requirements_unwound_to_instead
)
class UnwindDetails
include Comparable
# We compare UnwindDetails when choosing which state to unwind to. If
# two options have the same state_index we prefer the one most
# removed from a requirement that caused the conflict. Both options
# would unwind to the same state, but a `grandparent` option will
# filter out fewer of its possibilities after doing so - where a state
# is both a `parent` and a `grandparent` to requirements that have
# caused a conflict this is the correct behaviour.
# @param [UnwindDetail] other UnwindDetail to be compared
# @return [Integer] integer specifying ordering
def <=>(other)
if state_index > other.state_index
1
elsif state_index == other.state_index
reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
else
-1
end
end
# @return [Integer] index of state requirement in reversed requirement tree
# (the conflicting requirement itself will be at position 0)
def reversed_requirement_tree_index
@reversed_requirement_tree_index ||=
if state_requirement
requirement_tree.reverse.index(state_requirement)
else
999_999
end
end
# @return [Boolean] where the requirement of the state we're unwinding
# to directly caused the conflict. Note: in this case, it is
# impossible for the state we're unwinding to be a parent of
# any of the other conflicting requirements (or we would have
# circularity)
def unwinding_to_primary_requirement?
requirement_tree.last == state_requirement
end
# @return [Array] array of sub-dependencies to avoid when choosing a
# new possibility for the state we've unwound to. Only relevant for
# non-primary unwinds
def sub_dependencies_to_avoid
@requirements_to_avoid ||=
requirement_trees.map do |tree|
index = tree.index(state_requirement)
tree[index + 1] if index
end.compact
end
# @return [Array] array of all the requirements that led to the need for
# this unwind
def all_requirements
@all_requirements ||= requirement_trees.flatten(1)
end
end
# @return [SpecificationProvider] the provider that knows about
# dependencies, requirements, specifications, versions, etc.
attr_reader :specification_provider
# @return [UI] the UI that knows how to communicate feedback about the
# resolution process back to the user
attr_reader :resolver_ui
# @return [DependencyGraph] the base dependency graph to which
# dependencies should be 'locked'
attr_reader :base
# @return [Array] the dependencies that were explicitly required
attr_reader :original_requested
# Initializes a new resolution.
# @param [SpecificationProvider] specification_provider
# see {#specification_provider}
# @param [UI] resolver_ui see {#resolver_ui}
# @param [Array] requested see {#original_requested}
# @param [DependencyGraph] base see {#base}
def initialize(specification_provider, resolver_ui, requested, base)
@specification_provider = specification_provider
@resolver_ui = resolver_ui
@original_requested = requested
@base = base
@states = []
@iteration_counter = 0
@parents_of = Hash.new { |h, k| h[k] = [] }
end
# Resolves the {#original_requested} dependencies into a full dependency
# graph
# @raise [ResolverError] if successful resolution is impossible
# @return [DependencyGraph] the dependency graph of successfully resolved
# dependencies
def resolve
start_resolution
while state
break if !state.requirement && state.requirements.empty?
indicate_progress
if state.respond_to?(:pop_possibility_state) # DependencyState
debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
state.pop_possibility_state.tap do |s|
if s
states.push(s)
activated.tag(s)
end
end
end
process_topmost_state
end
resolve_activated_specs
ensure
end_resolution
end
# @return [Integer] the number of resolver iterations in between calls to
# {#resolver_ui}'s {UI#indicate_progress} method
attr_accessor :iteration_rate
private :iteration_rate
# @return [Time] the time at which resolution began
attr_accessor :started_at
private :started_at
# @return [Array] the stack of states for the resolution
attr_accessor :states
private :states
private
# Sets up the resolution process
# @return [void]
def start_resolution
@started_at = Time.now
push_initial_state
debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
resolver_ui.before_resolution
end
def resolve_activated_specs
activated.vertices.each do |_, vertex|
next unless vertex.payload
latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
end
activated.set_payload(vertex.name, latest_version)
end
activated.freeze
end
# Ends the resolution process
# @return [void]
def end_resolution
resolver_ui.after_resolution
debug do
"Finished resolution (#{@iteration_counter} steps) " \
"(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
end
debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
end
require_relative 'state'
require_relative 'modules/specification_provider'
require_relative 'delegates/resolution_state'
require_relative 'delegates/specification_provider'
include Gem::Molinillo::Delegates::ResolutionState
include Gem::Molinillo::Delegates::SpecificationProvider
# Processes the topmost available {RequirementState} on the stack
# @return [void]
def process_topmost_state
if possibility
attempt_to_activate
else
create_conflict
unwind_for_conflict
end
rescue CircularDependencyError => underlying_error
create_conflict(underlying_error)
unwind_for_conflict
end
# @return [Object] the current possibility that the resolution is trying
# to activate
def possibility
possibilities.last
end
# @return [RequirementState] the current state the resolution is
# operating upon
def state
states.last
end
# Creates and pushes the initial state for the resolution, based upon the
# {#requested} dependencies
# @return [void]
def push_initial_state
graph = DependencyGraph.new.tap do |dg|
original_requested.each do |requested|
vertex = dg.add_vertex(name_for(requested), nil, true)
vertex.explicit_requirements << requested
end
dg.tag(:initial_state)
end
push_state_for_requirements(original_requested, true, graph)
end
# Unwinds the states stack because a conflict has been encountered
# @return [void]
def unwind_for_conflict
details_for_unwind = build_details_for_unwind
unwind_options = unused_unwind_options
debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
conflicts.tap do |c|
sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
raise_error_unless_state(c)
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
state.conflicts = c
state.unused_unwind_options = unwind_options
filter_possibilities_after_unwind(details_for_unwind)
index = states.size - 1
@parents_of.each { |_, a| a.reject! { |i| i >= index } }
state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
end
end
# Raises a VersionConflict error, or any underlying error, if there is no
# current state
# @return [void]
def raise_error_unless_state(conflicts)
return if state
error = conflicts.values.map(&:underlying_error).compact.first
raise error || VersionConflict.new(conflicts, specification_provider)
end
# @return [UnwindDetails] Details of the nearest index to which we could unwind
def build_details_for_unwind
# Get the possible unwinds for the current conflict
current_conflict = conflicts[name]
binding_requirements = binding_requirements_for_conflict(current_conflict)
unwind_details = unwind_options_for_requirements(binding_requirements)
last_detail_for_current_unwind = unwind_details.sort.last
current_detail = last_detail_for_current_unwind
# Look for past conflicts that could be unwound to affect the
# requirement tree for the current conflict
all_reqs = last_detail_for_current_unwind.all_requirements
all_reqs_size = all_reqs.size
relevant_unused_unwinds = unused_unwind_options.select do |alternative|
diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
next if diff_reqs.size == all_reqs_size
# Find the highest index unwind whilst looping through
current_detail = alternative if alternative > current_detail
alternative
end
# Add the current unwind options to the `unused_unwind_options` array.
# The "used" option will be filtered out during `unwind_for_conflict`.
state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
# Update the requirements_unwound_to_instead on any relevant unused unwinds
relevant_unused_unwinds.each do |d|
(d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
end
unwind_details.each do |d|
(d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
end
current_detail
end
# @param [Array] binding_requirements array of requirements that combine to create a conflict
# @return [Array] array of UnwindDetails that have a chance
# of resolving the passed requirements
def unwind_options_for_requirements(binding_requirements)
unwind_details = []
trees = []
binding_requirements.reverse_each do |r|
partial_tree = [r]
trees << partial_tree
unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
# If this requirement has alternative possibilities, check if any would
# satisfy the other requirements that created this conflict
requirement_state = find_state_for(r)
if conflict_fixing_possibilities?(requirement_state, binding_requirements)
unwind_details << UnwindDetails.new(
states.index(requirement_state),
r,
partial_tree,
binding_requirements,
trees,
[]
)
end
# Next, look at the parent of this requirement, and check if the requirement
# could have been avoided if an alternative PossibilitySet had been chosen
parent_r = parent_of(r)
next if parent_r.nil?
partial_tree.unshift(parent_r)
requirement_state = find_state_for(parent_r)
if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
unwind_details << UnwindDetails.new(
states.index(requirement_state),
parent_r,
partial_tree,
binding_requirements,
trees,
[]
)
end
# Finally, look at the grandparent and up of this requirement, looking
# for any possibilities that wouldn't create their parent requirement
grandparent_r = parent_of(parent_r)
until grandparent_r.nil?
partial_tree.unshift(grandparent_r)
requirement_state = find_state_for(grandparent_r)
if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
unwind_details << UnwindDetails.new(
states.index(requirement_state),
grandparent_r,
partial_tree,
binding_requirements,
trees,
[]
)
end
parent_r = grandparent_r
grandparent_r = parent_of(parent_r)
end
end
unwind_details
end
# @param [DependencyState] state
# @param [Array] binding_requirements array of requirements
# @return [Boolean] whether or not the given state has any possibilities
# that could satisfy the given requirements
def conflict_fixing_possibilities?(state, binding_requirements)
return false unless state
state.possibilities.any? do |possibility_set|
possibility_set.possibilities.any? do |poss|
possibility_satisfies_requirements?(poss, binding_requirements)
end
end
end
# Filter's a state's possibilities to remove any that would not fix the
# conflict we've just rewound from
# @param [UnwindDetails] unwind_details details of the conflict just
# unwound from
# @return [void]
def filter_possibilities_after_unwind(unwind_details)
return unless state && !state.possibilities.empty?
if unwind_details.unwinding_to_primary_requirement?
filter_possibilities_for_primary_unwind(unwind_details)
else
filter_possibilities_for_parent_unwind(unwind_details)
end
end
# Filter's a state's possibilities to remove any that would not satisfy
# the requirements in the conflict we've just rewound from
# @param [UnwindDetails] unwind_details details of the conflict just unwound from
# @return [void]
def filter_possibilities_for_primary_unwind(unwind_details)
unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
unwinds_to_state << unwind_details
unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
state.possibilities.reject! do |possibility_set|
possibility_set.possibilities.none? do |poss|
unwind_requirement_sets.any? do |requirements|
possibility_satisfies_requirements?(poss, requirements)
end
end
end
end
# @param [Object] possibility a single possibility
# @param [Array] requirements an array of requirements
# @return [Boolean] whether the possibility satisfies all of the
# given requirements
def possibility_satisfies_requirements?(possibility, requirements)
name = name_for(possibility)
activated.tag(:swap)
activated.set_payload(name, possibility) if activated.vertex_named(name)
satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
activated.rewind_to(:swap)
satisfied
end
# Filter's a state's possibilities to remove any that would (eventually)
# create a requirement in the conflict we've just rewound from
# @param [UnwindDetails] unwind_details details of the conflict just unwound from
# @return [void]
def filter_possibilities_for_parent_unwind(unwind_details)
unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
unwinds_to_state << unwind_details
primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
parent_unwinds = unwinds_to_state.uniq - primary_unwinds
allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
states[unwind.state_index].possibilities.select do |possibility_set|
possibility_set.possibilities.any? do |poss|
possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
end
end
end
requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
state.possibilities.reject! do |possibility_set|
!allowed_possibility_sets.include?(possibility_set) &&
(requirements_to_avoid - possibility_set.dependencies).empty?
end
end
# @param [Conflict] conflict
# @return [Array] minimal array of requirements that would cause the passed
# conflict to occur.
def binding_requirements_for_conflict(conflict)
return [conflict.requirement] if conflict.possibility.nil?
possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
# When there's a `CircularDependency` error the conflicting requirement
# (the one causing the circular) won't be `conflict.requirement`
# (which won't be for the right state, because we won't have created it,
# because it's circular).
# We need to make sure we have that requirement in the conflict's list,
# otherwise we won't be able to unwind properly, so we just return all
# the requirements for the conflict.
return possible_binding_requirements if conflict.underlying_error
possibilities = search_for(conflict.requirement)
# If all the requirements together don't filter out all possibilities,
# then the only two requirements we need to consider are the initial one
# (where the dependency's version was first chosen) and the last
if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
end
# Loop through the possible binding requirements, removing each one
# that doesn't bind. Use a `reverse_each` as we want the earliest set of
# binding requirements, and don't use `reject!` as we wish to refine the
# array *on each iteration*.
binding_requirements = possible_binding_requirements.dup
possible_binding_requirements.reverse_each do |req|
next if req == conflict.requirement
unless binding_requirement_in_set?(req, binding_requirements, possibilities)
binding_requirements -= [req]
end
end
binding_requirements
end
# @param [Object] requirement we wish to check
# @param [Array] possible_binding_requirements array of requirements
# @param [Array] possibilities array of possibilities the requirements will be used to filter
# @return [Boolean] whether or not the given requirement is required to filter
# out all elements of the array of possibilities.
def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
possibilities.any? do |poss|
possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
end
end
# @param [Object] requirement
# @return [Object] the requirement that led to `requirement` being added
# to the list of requirements.
def parent_of(requirement)
return unless requirement
return unless index = @parents_of[requirement].last
return unless parent_state = @states[index]
parent_state.requirement
end
# @param [String] name
# @return [Object] the requirement that led to a version of a possibility
# with the given name being activated.
def requirement_for_existing_name(name)
return nil unless vertex = activated.vertex_named(name)
return nil unless vertex.payload
states.find { |s| s.name == name }.requirement
end
# @param [Object] requirement
# @return [ResolutionState] the state whose `requirement` is the given
# `requirement`.
def find_state_for(requirement)
return nil unless requirement
states.find { |i| requirement == i.requirement }
end
# @param [Object] underlying_error
# @return [Conflict] a {Conflict} that reflects the failure to activate
# the {#possibility} in conjunction with the current {#state}
def create_conflict(underlying_error = nil)
vertex = activated.vertex_named(name)
locked_requirement = 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 do |edge|
(requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
end
activated_by_name = {}
activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
conflicts[name] = Conflict.new(
requirement,
requirements,
vertex.payload && vertex.payload.latest_version,
possibility,
locked_requirement,
requirement_trees,
activated_by_name,
underlying_error
)
end
# @return [Array>] The different requirement
# trees that led to every requirement for the current spec.
def requirement_trees
vertex = activated.vertex_named(name)
vertex.requirements.map { |r| requirement_tree_for(r) }
end
# @param [Object] requirement
# @return [Array] the list of requirements that led to
# `requirement` being required.
def requirement_tree_for(requirement)
tree = []
while requirement
tree.unshift(requirement)
requirement = parent_of(requirement)
end
tree
end
# Indicates progress roughly once every second
# @return [void]
def indicate_progress
@iteration_counter += 1
@progress_rate ||= resolver_ui.progress_rate
if iteration_rate.nil?
if Time.now - started_at >= @progress_rate
self.iteration_rate = @iteration_counter
end
end
if iteration_rate && (@iteration_counter % iteration_rate) == 0
resolver_ui.indicate_progress
end
end
# Calls the {#resolver_ui}'s {UI#debug} method
# @param [Integer] depth the depth of the {#states} stack
# @param [Proc] block a block that yields a {#to_s}
# @return [void]
def debug(depth = 0, &block)
resolver_ui.debug(depth, &block)
end
# Attempts to activate the current {#possibility}
# @return [void]
def attempt_to_activate
debug(depth) { 'Attempting to activate ' + possibility.to_s }
existing_vertex = activated.vertex_named(name)
if existing_vertex.payload
debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
attempt_to_filter_existing_spec(existing_vertex)
else
latest = possibility.latest_version
possibility.possibilities.select! do |possibility|
requirement_satisfied_by?(requirement, activated, possibility)
end
if possibility.latest_version.nil?
# ensure there's a possibility for better error messages
possibility.possibilities << latest if latest
create_conflict
unwind_for_conflict
else
activate_new_spec
end
end
end
# Attempts to update the existing vertex's `PossibilitySet` with a filtered version
# @return [void]
def attempt_to_filter_existing_spec(vertex)
filtered_set = filtered_possibility_set(vertex)
if !filtered_set.possibilities.empty?
activated.set_payload(name, filtered_set)
new_requirements = requirements.dup
push_state_for_requirements(new_requirements, false)
else
create_conflict
debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
unwind_for_conflict
end
end
# Generates a filtered version of the existing vertex's `PossibilitySet` using the
# current state's `requirement`
# @param [Object] vertex existing vertex
# @return [PossibilitySet] filtered possibility set
def filtered_possibility_set(vertex)
PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
end
# @param [String] requirement_name the spec name to search for
# @return [Object] the locked spec named `requirement_name`, if one
# is found on {#base}
def locked_requirement_named(requirement_name)
vertex = base.vertex_named(requirement_name)
vertex && vertex.payload
end
# Add the current {#possibility} to the dependency graph of the current
# {#state}
# @return [void]
def activate_new_spec
conflicts.delete(name)
debug(depth) { "Activated #{name} at #{possibility}" }
activated.set_payload(name, possibility)
require_nested_dependencies_for(possibility)
end
# Requires the dependencies that the recently activated spec has
# @param [Object] possibility_set the PossibilitySet that has just been
# activated
# @return [void]
def require_nested_dependencies_for(possibility_set)
nested_dependencies = dependencies_for(possibility_set.latest_version)
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
nested_dependencies.each do |d|
activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
parent_index = states.size - 1
parents = @parents_of[d]
parents << parent_index if parents.empty?
end
push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
end
# Pushes a new {DependencyState} that encapsulates both existing and new
# requirements
# @param [Array] new_requirements
# @param [Boolean] requires_sort
# @param [Object] new_activated
# @return [void]
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_requirement = nil
loop do
new_requirement = new_requirements.shift
break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
end
new_name = new_requirement ? name_for(new_requirement) : ''.freeze
possibilities = possibilities_for_requirement(new_requirement)
handle_missing_or_push_dependency_state DependencyState.new(
new_name, new_requirements, new_activated,
new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
)
end
# Checks a proposed requirement with any existing locked requirement
# before generating an array of possibilities for it.
# @param [Object] requirement the proposed requirement
# @param [Object] activated
# @return [Array] possibilities
def possibilities_for_requirement(requirement, activated = self.activated)
return [] unless requirement
if locked_requirement_named(name_for(requirement))
return locked_requirement_possibility_set(requirement, activated)
end
group_possibilities(search_for(requirement))
end
# @param [Object] requirement the proposed requirement
# @param [Object] activated
# @return [Array] possibility set containing only the locked requirement, if any
def locked_requirement_possibility_set(requirement, activated = self.activated)
all_possibilities = search_for(requirement)
locked_requirement = locked_requirement_named(name_for(requirement))
# Longwinded way to build a possibilities array with either the locked
# requirement or nothing in it. Required, since the API for
# locked_requirement isn't guaranteed.
locked_possibilities = all_possibilities.select do |possibility|
requirement_satisfied_by?(locked_requirement, activated, possibility)
end
group_possibilities(locked_possibilities)
end
# Build an array of PossibilitySets, with each element representing a group of
# dependency versions that all have the same sub-dependency version constraints
# and are contiguous.
# @param [Array] possibilities an array of possibilities
# @return [Array] an array of possibility sets
def group_possibilities(possibilities)
possibility_sets = []
current_possibility_set = nil
possibilities.reverse_each do |possibility|
dependencies = dependencies_for(possibility)
if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
current_possibility_set.possibilities.unshift(possibility)
else
possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
current_possibility_set = possibility_sets.first
end
end
possibility_sets
end
# Pushes a new {DependencyState}.
# If the {#specification_provider} says to
# {SpecificationProvider#allow_missing?} that particular requirement, and
# there are no possibilities for that requirement, then `state` is not
# pushed, and the vertex in {#activated} is removed, and we continue
# resolving the remaining requirements.
# @param [DependencyState] state
# @return [void]
def handle_missing_or_push_dependency_state(state)
if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
state.activated.detach_vertex_named(state.name)
push_state_for_requirements(state.requirements.dup, false, state.activated)
else
states.push(state).tap { activated.tag(state) }
end
end
end
end
end
PK ! ™ÝØÝ$ $ state.rbnu „[µü¤ # frozen_string_literal: true
module Gem::Molinillo
# A state that a {Resolution} can be in
# @attr [String] name the name of the current requirement
# @attr [Array] requirements currently unsatisfied requirements
# @attr [DependencyGraph] activated the graph of activated dependencies
# @attr [Object] requirement the current requirement
# @attr [Object] possibilities the possibilities to satisfy the current requirement
# @attr [Integer] depth the depth of the resolution
# @attr [Hash] conflicts unresolved conflicts, indexed by dependency name
# @attr [Array] unused_unwind_options unwinds for previous conflicts that weren't explored
ResolutionState = Struct.new(
:name,
:requirements,
:activated,
:requirement,
:possibilities,
:depth,
:conflicts,
:unused_unwind_options
)
class ResolutionState
# Returns an empty resolution state
# @return [ResolutionState] an empty state
def self.empty
new(nil, [], DependencyGraph.new, nil, nil, 0, {}, [])
end
end
# A state that encapsulates a set of {#requirements} with an {Array} of
# possibilities
class DependencyState < ResolutionState
# Removes a possibility from `self`
# @return [PossibilityState] a state with a single possibility,
# the possibility that was removed from `self`
def pop_possibility_state
PossibilityState.new(
name,
requirements.dup,
activated,
requirement,
[possibilities.pop],
depth + 1,
conflicts.dup,
unused_unwind_options.dup
).tap do |state|
state.activated.tag(state)
end
end
end
# A state that encapsulates a single possibility to fulfill the given
# {#requirement}
class PossibilityState < ResolutionState
end
end
PK ! ß~R© © dependency_graph.rbnu „[µü¤ # frozen_string_literal: true
require_relative '../../../../vendored_tsort'
require_relative 'dependency_graph/log'
require_relative 'dependency_graph/vertex'
module Gem::Molinillo
# A directed acyclic graph that is tuned to hold named dependencies
class DependencyGraph
include Enumerable
# Enumerates through the vertices of the graph.
# @return [Array] The graph's vertices.
def each
return vertices.values.each unless block_given?
vertices.values.each { |v| yield v }
end
include Gem::TSort
# @!visibility private
alias tsort_each_node each
# @!visibility private
def tsort_each_child(vertex, &block)
vertex.successors.each(&block)
end
# Topologically sorts the given vertices.
# @param [Enumerable] vertices the vertices to be sorted, which must
# all belong to the same graph.
# @return [Array] The sorted vertices.
def self.tsort(vertices)
Gem::TSort.tsort(
lambda { |b| vertices.each(&b) },
lambda { |v, &b| (v.successors & vertices).each(&b) }
)
end
# A directed edge of a {DependencyGraph}
# @attr [Vertex] origin The origin of the directed edge
# @attr [Vertex] destination The destination of the directed edge
# @attr [Object] requirement The requirement the directed edge represents
Edge = Struct.new(:origin, :destination, :requirement)
# @return [{String => Vertex}] the vertices of the dependency graph, keyed
# by {Vertex#name}
attr_reader :vertices
# @return [Log] the op log for this graph
attr_reader :log
# Initializes an empty dependency graph
def initialize
@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
# Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
# are properly copied.
# @param [DependencyGraph] other the graph to copy.
def initialize_copy(other)
super
@vertices = {}
@log = other.log.dup
traverse = lambda do |new_v, old_v|
return if new_v.outgoing_edges.size == old_v.outgoing_edges.size
old_v.outgoing_edges.each do |edge|
destination = add_vertex(edge.destination.name, edge.destination.payload)
add_edge_no_circular(new_v, destination, edge.requirement)
traverse.call(destination, edge.destination)
end
end
other.vertices.each do |name, vertex|
new_vertex = add_vertex(name, vertex.payload, vertex.root?)
new_vertex.explicit_requirements.replace(vertex.explicit_requirements)
traverse.call(new_vertex, vertex)
end
end
# @return [String] a string suitable for debugging
def inspect
"#{self.class}:#{vertices.values.inspect}"
end
# @param [Hash] options options for dot output.
# @return [String] Returns a dot format representation of the graph
def to_dot(options = {})
edge_label = options.delete(:edge_label)
raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty?
dot_vertices = []
dot_edges = []
vertices.each do |n, v|
dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]"
v.outgoing_edges.each do |e|
label = edge_label ? edge_label.call(e) : e.requirement
dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]"
end
end
dot_vertices.uniq!
dot_vertices.sort!
dot_edges.uniq!
dot_edges.sort!
dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}')
dot.join("\n")
end
# @param [DependencyGraph] other
# @return [Boolean] whether the two dependency graphs are equal, determined
# by a recursive traversal of each {#root_vertices} and its
# {Vertex#successors}
def ==(other)
return false unless other
return true if equal?(other)
vertices.each do |name, vertex|
other_vertex = other.vertex_named(name)
return false unless other_vertex
return false unless vertex.payload == other_vertex.payload
return false unless other_vertex.successors.to_set == vertex.successors.to_set
end
end
# @param [String] name
# @param [Object] payload
# @param [Array] parent_names
# @param [Object] requirement the requirement that is requiring the child
# @return [void]
def add_child_vertex(name, payload, parent_names, requirement)
root = !parent_names.delete(nil) { true }
vertex = add_vertex(name, payload, root)
vertex.explicit_requirements << requirement if root
parent_names.each do |parent_name|
parent_vertex = vertex_named(parent_name)
add_edge(parent_vertex, vertex, requirement)
end
vertex
end
# Adds a vertex with the given name, or updates the existing one.
# @param [String] name
# @param [Object] payload
# @return [Vertex] the vertex that was added to `self`
def add_vertex(name, payload, root = false)
log.add_vertex(self, name, payload, root)
end
# Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
# removing any non-root vertices that were orphaned in the process
# @param [String] name
# @return [Array] the vertices which have been detached
def detach_vertex_named(name)
log.detach_vertex_named(self, name)
end
# @param [String] name
# @return [Vertex,nil] the vertex with the given name
def vertex_named(name)
vertices[name]
end
# @param [String] name
# @return [Vertex,nil] the root vertex with the given name
def root_vertex_named(name)
vertex = vertex_named(name)
vertex if vertex && vertex.root?
end
# Adds a new {Edge} to the dependency graph
# @param [Vertex] origin
# @param [Vertex] destination
# @param [Object] requirement the requirement that this edge represents
# @return [Edge] the added edge
def add_edge(origin, destination, requirement)
if destination.path_to?(origin)
raise CircularDependencyError.new(path(destination, origin))
end
add_edge_no_circular(origin, destination, requirement)
end
# Deletes an {Edge} from the dependency graph
# @param [Edge] edge
# @return [Void]
def delete_edge(edge)
log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement)
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
# Adds a new {Edge} to the dependency graph without checking for
# circularity.
# @param (see #add_edge)
# @return (see #add_edge)
def add_edge_no_circular(origin, destination, requirement)
log.add_edge_no_circular(self, origin.name, destination.name, requirement)
end
# Returns the path between two vertices
# @raise [ArgumentError] if there is no path between the vertices
# @param [Vertex] from
# @param [Vertex] to
# @return [Array] the shortest path from `from` to `to`
def path(from, to)
distances = Hash.new(vertices.size + 1)
distances[from.name] = 0
predecessors = {}
each do |vertex|
vertex.successors.each do |successor|
if distances[successor.name] > distances[vertex.name] + 1
distances[successor.name] = distances[vertex.name] + 1
predecessors[successor] = vertex
end
end
end
path = [to]
while before = predecessors[to]
path << before
to = before
break if to == from
end
unless path.last.equal?(from)
raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
end
path.reverse
end
end
end
PK ! —®÷w w gem_metadata.rbnu „[µü¤ PK ! Ë?ýÀp p ¶ errors.rbnu „[µü¤ PK ! N“讟 Ÿ _ dependency_graph/tag.rbnu „[µü¤ PK ! Ü19»D D E dependency_graph/vertex.rbnu „[µü¤ PK ! ŠåŠ?_ _ Ó0 dependency_graph/add_vertex.rbnu „[µü¤ PK ! J„N˜ ˜ €7 dependency_graph/action.rbnu „[µü¤ PK ! •³8ú ú ' b; dependency_graph/detach_vertex_named.rbnu „[µü¤ PK ! U‰=… ³A dependency_graph/delete_edge.rbnu „[µü¤ PK ! Nèk k ( I dependency_graph/add_edge_no_circular.rbnu „[µü¤ PK ! fêg ãP dependency_graph/log.rbnu „[µü¤ PK ! €û]€S S E_ dependency_graph/set_payload.rbnu „[µü¤ PK ! êƒ
5b b # çc delegates/specification_provider.rbnu „[µü¤ PK ! #:1V÷ ÷ œp delegates/resolution_state.rbnu „[µü¤ PK ! Ë,á± àw resolver.rbnu „[µü¤ PK ! ÕEÃ£Ç Ç
6~ modules/ui.rbnu „[µü¤ PK ! ¦Ö…S S ! :… modules/specification_provider.rbnu „[µü¤ PK ! JTµÜ‚† ‚†
Þ• resolution.rbnu „[µü¤ PK ! ™ÝØÝ$ $ state.rbnu „[µü¤ PK ! ß~R© © ù# dependency_graph.rbnu „[µü¤ PK † åD