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>
 | 
			
		||||
 | 
			
		||||
	* common.mk (build-ext), ext/extmk.rb: use variable EXTENCS
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -154,6 +154,26 @@ module Gem
 | 
			
		|||
    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
 | 
			
		||||
 | 
			
		||||
  @configuration = nil
 | 
			
		||||
| 
						 | 
				
			
			@ -829,7 +849,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
 | 
			
		|||
      f.flock(File::LOCK_EX)
 | 
			
		||||
      f.read
 | 
			
		||||
    end
 | 
			
		||||
  rescue Errno::EACCES
 | 
			
		||||
  rescue *READ_BINARY_ERRORS
 | 
			
		||||
    open path, 'rb' do |f|
 | 
			
		||||
      f.read
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -843,6 +863,26 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
 | 
			
		|||
    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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -284,6 +284,7 @@ class Gem::Installer
 | 
			
		|||
 | 
			
		||||
    # Completely remove any previous gem files
 | 
			
		||||
    FileUtils.rm_rf gem_dir
 | 
			
		||||
    FileUtils.rm_rf spec.extension_dir
 | 
			
		||||
 | 
			
		||||
    FileUtils.mkdir_p gem_dir
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -211,7 +211,9 @@ class Gem::Package
 | 
			
		|||
      stat = File.lstat file
 | 
			
		||||
 | 
			
		||||
      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
 | 
			
		||||
 | 
			
		||||
      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
 | 
			
		||||
 | 
			
		||||
  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)")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if name.bytesize <= 100 then
 | 
			
		||||
      prefix = ""
 | 
			
		||||
    else
 | 
			
		||||
      parts = name.split(/\//)
 | 
			
		||||
      newname = parts.pop
 | 
			
		||||
      nxt = ""
 | 
			
		||||
 | 
			
		||||
      loop do
 | 
			
		||||
        nxt = parts.pop
 | 
			
		||||
        break if newname.bytesize + 1 + nxt.bytesize > 100
 | 
			
		||||
        newname = nxt + "/" + newname
 | 
			
		||||
    prefix = ''
 | 
			
		||||
    if name.bytesize > 100 then
 | 
			
		||||
      parts = name.split('/', -1) # parts are never empty here
 | 
			
		||||
      name = parts.pop            # initially empty for names with a trailing slash ("foo/.../bar/")
 | 
			
		||||
      prefix = parts.join('/')    # if empty, then it's impossible to split (parts is empty too)
 | 
			
		||||
      while !parts.empty? && (prefix.bytesize > 155 || name.empty?)
 | 
			
		||||
        name = parts.pop + '/' + name
 | 
			
		||||
        prefix = parts.join('/')
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      prefix = (parts + [nxt]).join "/"
 | 
			
		||||
      name = newname
 | 
			
		||||
 | 
			
		||||
      if name.bytesize > 100
 | 
			
		||||
      if name.bytesize > 100 or prefix.empty? then
 | 
			
		||||
        raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -328,20 +328,7 @@ class Gem::RemoteFetcher
 | 
			
		|||
    end
 | 
			
		||||
 | 
			
		||||
    if update and path
 | 
			
		||||
      begin
 | 
			
		||||
        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
 | 
			
		||||
      Gem.write_binary(path, data)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    data
 | 
			
		||||
| 
						 | 
				
			
			@ -427,4 +414,3 @@ class Gem::RemoteFetcher
 | 
			
		|||
    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 'tsort'
 | 
			
		||||
 | 
			
		||||
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log'
 | 
			
		||||
require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex'
 | 
			
		||||
 | 
			
		||||
module Gem::Resolver::Molinillo
 | 
			
		||||
  # A directed acyclic graph that is tuned to hold named dependencies
 | 
			
		||||
  class DependencyGraph
 | 
			
		||||
| 
						 | 
				
			
			@ -10,15 +13,16 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
    # Enumerates through the vertices of the graph.
 | 
			
		||||
    # @return [Array<Vertex>] The graph's vertices.
 | 
			
		||||
    def each
 | 
			
		||||
      return vertices.values.each unless block_given?
 | 
			
		||||
      vertices.values.each { |v| yield v }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    include TSort
 | 
			
		||||
 | 
			
		||||
    # @visibility private
 | 
			
		||||
    alias_method :tsort_each_node, :each
 | 
			
		||||
    # @!visibility private
 | 
			
		||||
    alias tsort_each_node each
 | 
			
		||||
 | 
			
		||||
    # @visibility private
 | 
			
		||||
    # @!visibility private
 | 
			
		||||
    def tsort_each_child(vertex, &block)
 | 
			
		||||
      vertex.successors.each(&block)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +48,27 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
    #   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}
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +77,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
    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|
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +98,22 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      "#{self.class}:#{vertices.values.inspect}"
 | 
			
		||||
    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
 | 
			
		||||
    #   by a recursive traversal of each {#root_vertices} and its
 | 
			
		||||
    #   {Vertex#successors}
 | 
			
		||||
| 
						 | 
				
			
			@ -93,12 +132,9 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
    # @param [Object] requirement the requirement that is requiring the child
 | 
			
		||||
    # @return [void]
 | 
			
		||||
    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|
 | 
			
		||||
        unless parent_name
 | 
			
		||||
          vertex.root = true
 | 
			
		||||
          next
 | 
			
		||||
        end
 | 
			
		||||
        parent_node = vertex_named(parent_name)
 | 
			
		||||
        add_edge(parent_node, vertex, requirement)
 | 
			
		||||
      end
 | 
			
		||||
| 
						 | 
				
			
			@ -110,10 +146,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
    # @param [Object] payload
 | 
			
		||||
    # @return [Vertex] the vertex that was added to `self`
 | 
			
		||||
    def add_vertex(name, payload, root = false)
 | 
			
		||||
      vertex = vertices[name] ||= Vertex.new(name, payload)
 | 
			
		||||
      vertex.payload ||= payload
 | 
			
		||||
      vertex.root ||= root
 | 
			
		||||
      vertex
 | 
			
		||||
      log.add_vertex(self, name, payload, root)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
 | 
			
		||||
| 
						 | 
				
			
			@ -121,16 +154,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
    # @param [String] name
 | 
			
		||||
    # @return [void]
 | 
			
		||||
    def detach_vertex_named(name)
 | 
			
		||||
      return unless vertex = vertices.delete(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
 | 
			
		||||
      log.detach_vertex_named(self, name)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # @param [String] name
 | 
			
		||||
| 
						 | 
				
			
			@ -158,134 +182,22 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      add_edge_no_circular(origin, destination, 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)
 | 
			
		||||
      edge = Edge.new(origin, destination, 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?
 | 
			
		||||
      log.add_edge_no_circular(self, origin.name, destination.name, requirement)
 | 
			
		||||
    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
 | 
			
		||||
      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 += " depended upon by #{sources}" unless sources.empty?
 | 
			
		||||
      message
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
module Gem::Resolver::Molinillo
 | 
			
		||||
  # The version of Gem::Resolver::Molinillo.
 | 
			
		||||
  VERSION = '0.4.3'.freeze
 | 
			
		||||
  VERSION = '0.5.0'.freeze
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
        @base = base
 | 
			
		||||
        @states = []
 | 
			
		||||
        @iteration_counter = 0
 | 
			
		||||
        @parent_of = {}
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Resolves the {#original_requested} dependencies into a full dependency
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +68,12 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
          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 { |s| states.push(s) if s }
 | 
			
		||||
            state.pop_possibility_state.tap do |s|
 | 
			
		||||
              if s
 | 
			
		||||
                states.push(s)
 | 
			
		||||
                activated.tag(s)
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
          process_topmost_state
 | 
			
		||||
        end
 | 
			
		||||
| 
						 | 
				
			
			@ -118,27 +124,11 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      require 'rubygems/resolver/molinillo/lib/molinillo/state'
 | 
			
		||||
      require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider'
 | 
			
		||||
 | 
			
		||||
      ResolutionState.new.members.each do |member|
 | 
			
		||||
        define_method member do |*args, &block|
 | 
			
		||||
          current_state = state || ResolutionState.empty
 | 
			
		||||
          current_state.send(member, *args, &block)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      require 'rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state'
 | 
			
		||||
      require 'rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider'
 | 
			
		||||
 | 
			
		||||
      SpecificationProvider.instance_methods(false).each do |instance_method|
 | 
			
		||||
        define_method instance_method do |*args, &block|
 | 
			
		||||
          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
 | 
			
		||||
      include Gem::Resolver::Molinillo::Delegates::ResolutionState
 | 
			
		||||
      include Gem::Resolver::Molinillo::Delegates::SpecificationProvider
 | 
			
		||||
 | 
			
		||||
      # Processes the topmost available {RequirementState} on the stack
 | 
			
		||||
      # @return [void]
 | 
			
		||||
| 
						 | 
				
			
			@ -169,6 +159,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      def initial_state
 | 
			
		||||
        graph = DependencyGraph.new.tap do |dg|
 | 
			
		||||
          original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } }
 | 
			
		||||
          dg.tag(:initial_state)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        requirements = sort_dependencies(original_requested, graph, {})
 | 
			
		||||
| 
						 | 
				
			
			@ -189,8 +180,9 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      def unwind_for_conflict
 | 
			
		||||
        debug(depth) { "Unwinding for conflict: #{requirement}" }
 | 
			
		||||
        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
 | 
			
		||||
          activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
 | 
			
		||||
          state.conflicts = c
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
| 
						 | 
				
			
			@ -217,20 +209,14 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      # @return [Object] the requirement that led to `requirement` being added
 | 
			
		||||
      #   to the list of requirements.
 | 
			
		||||
      def parent_of(requirement)
 | 
			
		||||
        return nil unless 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
 | 
			
		||||
        @parent_of[requirement]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @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 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
 | 
			
		||||
 | 
			
		||||
      # @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}
 | 
			
		||||
      def create_conflict
 | 
			
		||||
        vertex = activated.vertex_named(name)
 | 
			
		||||
        requirements = {
 | 
			
		||||
          name_for_explicit_dependency_source => vertex.explicit_requirements,
 | 
			
		||||
          name_for_locking_dependency_source => Array(locked_requirement_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 { |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(
 | 
			
		||||
          requirement,
 | 
			
		||||
          Hash[requirements.select { |_, r| !r.empty? }],
 | 
			
		||||
          requirements,
 | 
			
		||||
          vertex.payload,
 | 
			
		||||
          possibility,
 | 
			
		||||
          locked_requirement_named(name),
 | 
			
		||||
          locked_requirement,
 | 
			
		||||
          requirement_trees,
 | 
			
		||||
          Hash[activated.map { |v| [v.name, v.payload] }.select(&:last)]
 | 
			
		||||
          activated_by_name
 | 
			
		||||
        )
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -341,15 +333,16 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      # spec with the given name
 | 
			
		||||
      # @return [Boolean] Whether the possibility was swapped into {#activated}
 | 
			
		||||
      def attempt_to_swap_possibility
 | 
			
		||||
        swapped = activated.dup
 | 
			
		||||
        vertex = swapped.vertex_named(name)
 | 
			
		||||
        vertex.payload = possibility
 | 
			
		||||
        return unless vertex.requirements.
 | 
			
		||||
            all? { |r| requirement_satisfied_by?(r, swapped, possibility) }
 | 
			
		||||
        return unless new_spec_satisfied?
 | 
			
		||||
        actual_vertex = activated.vertex_named(name)
 | 
			
		||||
        actual_vertex.payload = possibility
 | 
			
		||||
        fixup_swapped_children(actual_vertex)
 | 
			
		||||
        activated.tag(:swap)
 | 
			
		||||
        vertex = activated.vertex_named(name)
 | 
			
		||||
        activated.set_payload(name, possibility)
 | 
			
		||||
        if !vertex.requirements.
 | 
			
		||||
           all? { |r| requirement_satisfied_by?(r, activated, possibility) } ||
 | 
			
		||||
            !new_spec_satisfied?
 | 
			
		||||
          activated.rewind_to(:swap)
 | 
			
		||||
          return
 | 
			
		||||
        end
 | 
			
		||||
        fixup_swapped_children(vertex)
 | 
			
		||||
        activate_spec
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -363,7 +356,13 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
          if !dep_names.include?(succ.name) && !succ.root? && succ.predecessors.to_a == [vertex]
 | 
			
		||||
            debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{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
 | 
			
		||||
| 
						 | 
				
			
			@ -406,8 +405,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      def activate_spec
 | 
			
		||||
        conflicts.delete(name)
 | 
			
		||||
        debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
 | 
			
		||||
        vertex = activated.vertex_named(name)
 | 
			
		||||
        vertex.payload = possibility
 | 
			
		||||
        activated.set_payload(name, possibility)
 | 
			
		||||
        require_nested_dependencies_for(possibility)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -418,19 +416,22 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      def require_nested_dependencies_for(activated_spec)
 | 
			
		||||
        nested_dependencies = dependencies_for(activated_spec)
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
      # Pushes a new {DependencyState} that encapsulates both existing and new
 | 
			
		||||
      # requirements
 | 
			
		||||
      # @param [Array] new_requirements
 | 
			
		||||
      # @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_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) : []
 | 
			
		||||
        handle_missing_or_push_dependency_state DependencyState.new(
 | 
			
		||||
          new_name, new_requirements, new_activated,
 | 
			
		||||
| 
						 | 
				
			
			@ -451,7 +452,7 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
          state.activated.detach_vertex_named(state.name)
 | 
			
		||||
          push_state_for_requirements(state.requirements.dup, false, state.activated)
 | 
			
		||||
        else
 | 
			
		||||
          states.push state
 | 
			
		||||
          states.push(state).tap { activated.tag(state) }
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,12 +36,14 @@ module Gem::Resolver::Molinillo
 | 
			
		|||
      PossibilityState.new(
 | 
			
		||||
        name,
 | 
			
		||||
        requirements.dup,
 | 
			
		||||
        activated.dup,
 | 
			
		||||
        activated,
 | 
			
		||||
        requirement,
 | 
			
		||||
        [possibilities.pop],
 | 
			
		||||
        depth + 1,
 | 
			
		||||
        conflicts.dup
 | 
			
		||||
      )
 | 
			
		||||
      ).tap do |state|
 | 
			
		||||
        state.activated.tag(state)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,6 +102,8 @@ class Gem::Security::Signer
 | 
			
		|||
  def sign data
 | 
			
		||||
    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
 | 
			
		||||
      re_sign_key
 | 
			
		||||
    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
 | 
			
		||||
  # activated.
 | 
			
		||||
  #
 | 
			
		||||
  #--
 | 
			
		||||
  # See also #require_paths
 | 
			
		||||
  #
 | 
			
		||||
  #++
 | 
			
		||||
  # 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
 | 
			
		||||
  # 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
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
if ENV["TRAVIS"] || ENV["TEST_SSL"]
 | 
			
		||||
if ENV["CI"] || ENV["TEST_SSL"]
 | 
			
		||||
  class TestBundledCA < Gem::TestCase
 | 
			
		||||
 | 
			
		||||
    THIS_FILE = File.expand_path __FILE__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1106,6 +1106,77 @@ gem 'other', version
 | 
			
		|||
    assert_path_exists expected_makefile
 | 
			
		||||
  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
 | 
			
		||||
    @spec.extensions << "extconf.rb"
 | 
			
		||||
    write_file File.join(@tempdir, "extconf.rb") do |io|
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,7 +141,9 @@ class TestGemPackage < Gem::Package::TarTestCase
 | 
			
		|||
 | 
			
		||||
    FileUtils.mkdir_p 'lib'
 | 
			
		||||
    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.spec = spec
 | 
			
		||||
| 
						 | 
				
			
			@ -156,12 +158,16 @@ class TestGemPackage < Gem::Package::TarTestCase
 | 
			
		|||
 | 
			
		||||
    Gem::Package::TarReader.new tar do |tar_io|
 | 
			
		||||
      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
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  def test_build
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -229,6 +229,22 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
 | 
			
		|||
 | 
			
		||||
    assert_equal ["#{'qwer/' * 19}bla", 'a' * 151],
 | 
			
		||||
                 @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
 | 
			
		||||
 | 
			
		||||
  def test_split_name_too_long_name
 | 
			
		||||
| 
						 | 
				
			
			@ -240,6 +256,14 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
 | 
			
		|||
      @tar_writer.split_name name
 | 
			
		||||
    end
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  def test_split_name_too_long_prefix
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,6 +81,7 @@ gems:
 | 
			
		|||
  # Generated via:
 | 
			
		||||
  #   x = OpenSSL::PKey::DH.new(2048) # wait a while...
 | 
			
		||||
  #   x.to_s => pem
 | 
			
		||||
  #   x.priv_key.to_s => hex for OpenSSL::BN.new
 | 
			
		||||
  TEST_KEY_DH2048 =  OpenSSL::PKey::DH.new <<-_end_of_pem_
 | 
			
		||||
-----BEGIN DH PARAMETERS-----
 | 
			
		||||
MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +93,17 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
 | 
			
		|||
-----END DH PARAMETERS-----
 | 
			
		||||
    _end_of_pem_
 | 
			
		||||
 | 
			
		||||
  TEST_KEY_DH2048.priv_key = OpenSSL::BN.new("108911488509734781344423639" \
 | 
			
		||||
     "5585749502236089033416160524030987005037540379474123441273555416835" \
 | 
			
		||||
     "4725688238369352738266590757370603937618499698665047757588998555345" \
 | 
			
		||||
     "3446251978586372525530219375408331096098220027413238477359960428372" \
 | 
			
		||||
     "0195464393332338164504352015535549496585792320286513563739305843396" \
 | 
			
		||||
     "9294344974028713065472959376197728193162272314514335882399554394661" \
 | 
			
		||||
     "5306385003430991221886779612878793446851681835397455333989268503748" \
 | 
			
		||||
     "7862488679178398716189205737442996155432191656080664090596502674943" \
 | 
			
		||||
     "7902481557157485795980326766117882761941455140582265347052939604724" \
 | 
			
		||||
     "964857770053363840471912215799994973597613931991572884", 16)
 | 
			
		||||
 | 
			
		||||
  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]
 | 
			
		||||
    @old_proxies = @proxies.map {|k| ENV[k] }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -205,5 +205,13 @@ c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
 | 
			
		|||
    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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue