ruby-changes:45925
From: hsbt <ko1@a...>
Date: Fri, 17 Mar 2017 10:29:29 +0900 (JST)
Subject: [ruby-changes:45925] hsbt:r57998 (trunk): Merge rubygems-2.6.11
hsbt 2017-03-17 10:29:22 +0900 (Fri, 17 Mar 2017) New Revision: 57998 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=57998 Log: Merge rubygems-2.6.11 This version fixed regression of rubygems-2.6.10. https://github.com/rubygems/rubygems/pull/1856 See details of changelogs for 2.6.11 release: https://github.com/rubygems/rubygems/blob/adfcf40502716080bd9cdfdd2e43bd4296872784/History.txt#L3 Modified files: trunk/lib/rubygems/request_set/lockfile/tokenizer.rb trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb trunk/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb trunk/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb trunk/lib/rubygems/resolver/set.rb trunk/lib/rubygems/resolver.rb trunk/lib/rubygems/specification.rb trunk/lib/rubygems/test_case.rb trunk/lib/rubygems.rb trunk/test/rubygems/test_gem.rb trunk/test/rubygems/test_gem_ext_rake_builder.rb trunk/test/rubygems/test_gem_resolver.rb Index: lib/rubygems.rb =================================================================== --- lib/rubygems.rb (revision 57997) +++ lib/rubygems.rb (revision 57998) @@ -10,7 +10,7 @@ require 'rbconfig' https://github.com/ruby/ruby/blob/trunk/lib/rubygems.rb#L10 require 'thread' module Gem - VERSION = "2.6.10" + VERSION = "2.6.11" end # Must be first since it unloads the prelude from 1.9.2 Index: lib/rubygems/request_set/lockfile/tokenizer.rb =================================================================== --- lib/rubygems/request_set/lockfile/tokenizer.rb (revision 57997) +++ lib/rubygems/request_set/lockfile/tokenizer.rb (revision 57998) @@ -1,5 +1,4 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/request_set/lockfile/tokenizer.rb#L1 # frozen_string_literal: true -require 'strscan' require 'rubygems/request_set/lockfile/parser' class Gem::RequestSet::Lockfile::Tokenizer @@ -58,6 +57,7 @@ class Gem::RequestSet::Lockfile::Tokeniz https://github.com/ruby/ruby/blob/trunk/lib/rubygems/request_set/lockfile/tokenizer.rb#L57 private def tokenize input + require 'strscan' s = StringScanner.new input until s.eos? do Index: lib/rubygems/resolver.rb =================================================================== --- lib/rubygems/resolver.rb (revision 57997) +++ lib/rubygems/resolver.rb (revision 57998) @@ -4,9 +4,6 @@ require 'rubygems/exceptions' https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver.rb#L4 require 'rubygems/util' require 'rubygems/util/list' -require 'uri' -require 'net/http' - ## # Given a set of Gem::Dependency objects as +needed+ and a way to query the # set of available specs via +set+, calculates a set of ActivationRequest @@ -256,6 +253,44 @@ class Gem::Resolver https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver.rb#L253 @soft_missing end + def sort_dependencies(dependencies, activated, conflicts) + dependencies.sort_by do |dependency| + name = name_for(dependency) + [ + activated.vertex_named(name).payload ? 0 : 1, + amount_constrained(dependency), + conflicts[name] ? 0 : 1, + activated.vertex_named(name).payload ? 0 : search_for(dependency).count, + ] + end + end + + SINGLE_POSSIBILITY_CONSTRAINT_PENALTY = 1_000_000 + private_constant :SINGLE_POSSIBILITY_CONSTRAINT_PENALTY if defined?(private_constant) + + # returns an integer \in (-\infty, 0] + # a number closer to 0 means the dependency is less constraining + # + # dependencies w/ 0 or 1 possibilities (ignoring version requirements) + # are given very negative values, so they _always_ sort first, + # before dependencies that are unconstrained + def amount_constrained(dependency) + @amount_constrained ||= {} + @amount_constrained[dependency.name] ||= begin + name_dependency = Gem::Dependency.new(dependency.name) + dependency_request_for_name = Gem::Resolver::DependencyRequest.new(name_dependency, dependency.requester) + all = @set.find_all(dependency_request_for_name).size + + if all <= 1 + all - SINGLE_POSSIBILITY_CONSTRAINT_PENALTY + else + search = search_for(dependency).size + search - all + end + end + end + private :amount_constrained + end ## Index: lib/rubygems/resolver/set.rb =================================================================== --- lib/rubygems/resolver/set.rb (revision 57997) +++ lib/rubygems/resolver/set.rb (revision 57998) @@ -21,6 +21,7 @@ class Gem::Resolver::Set https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/set.rb#L21 attr_accessor :prerelease def initialize # :nodoc: + require 'uri' @prerelease = false @remote = true @errors = [] @@ -54,4 +55,3 @@ class Gem::Resolver::Set https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/set.rb#L55 end end - Index: lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb =================================================================== --- lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb (revision 57997) +++ lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb (revision 57998) @@ -48,7 +48,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb#L48 if debug? debug_info = yield debug_info = debug_info.inspect unless debug_info.is_a?(String) - output.puts debug_info.split("\n").map { |s| ' ' * depth + s } + output.puts debug_info.split("\n").map { |s| ' ' * depth + s } end end Index: lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb =================================================================== --- lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb (revision 57997) +++ lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb (revision 57998) @@ -1,5 +1,5 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb#L1 # frozen_string_literal: true module Gem::Resolver::Molinillo # The version of Gem::Resolver::Molinillo. - VERSION = '0.5.5'.freeze + VERSION = '0.5.7'.freeze end Index: lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb =================================================================== --- lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb (revision 57997) +++ lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb (revision 57998) @@ -23,8 +23,8 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb#L23 # (see Action#down) def down(graph) edge = make_edge(graph) - edge.origin.outgoing_edges.delete(edge) - edge.destination.incoming_edges.delete(edge) + delete_first(edge.origin.outgoing_edges, edge) + delete_first(edge.destination.incoming_edges, edge) end # @!group AddEdgeNoCircular @@ -53,6 +53,13 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb#L53 @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 Index: lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb =================================================================== --- lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb (revision 57997) +++ lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb (revision 57998) @@ -10,7 +10,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb#L10 # @return [Object] the payload the vertex holds attr_accessor :payload - # @return [Arrary<Object>] the explicit requirements that required + # @return [Array<Object>] the explicit requirements that required # this vertex attr_reader :explicit_requirements Index: lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb =================================================================== --- lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb (revision 57997) +++ lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb (revision 57998) @@ -98,18 +98,27 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb#L98 "#{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 + 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| - dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=\"#{e.requirement}\"]" + 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 @@ -123,7 +132,8 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb#L132 vertices.each do |name, vertex| other_vertex = other.vertex_named(name) return false unless other_vertex - return false unless other_vertex.successors.map(&:name).to_set == vertex.successors.map(&:name).to_set + return false unless vertex.payload == other_vertex.payload + return false unless other_vertex.successors.to_set == vertex.successors.to_set end end Index: lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb =================================================================== --- lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb (revision 57997) +++ lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb (revision 57998) @@ -52,7 +52,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L52 @base = base @states = [] @iteration_counter = 0 - @parent_of = {} + @parents_of = Hash.new { |h, k| h[k] = [] } end # Resolves the {#original_requested} dependencies into a full dependency @@ -105,7 +105,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L105 handle_missing_or_push_dependency_state(initial_state) - debug { "Starting resolution (#{@started_at})" } + debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" } resolver_ui.before_resolution end @@ -178,14 +178,14 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L178 # Unwinds the states stack because a conflict has been encountered # @return [void] def unwind_for_conflict - debug(depth) { "Unwinding for conflict: #{requirement}" } + debug(depth) { "Unwinding for conflict: #{requirement} to #{state_index_for_unwind / 2}" } conflicts.tap do |c| 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 index = states.size - 1 - @parent_of.reject! { |_, i| i >= index } + @parents_of.each { |_, a| a.reject! { |i| i >= index } } end end @@ -214,7 +214,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L214 # to the list of requirements. def parent_of(requirement) return unless requirement - return unless index = @parent_of[requirement] + return unless index = @parents_of[requirement].last return unless parent_state = @states[index] parent_state.requirement end @@ -356,16 +356,25 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L356 # Ensures there are no orphaned successors to the given {vertex}. # @param [DependencyGraph::Vertex] vertex the vertex to fix up. # @return [void] - def fixup_swapped_children(vertex) + def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity payload = vertex.payload deps = dependencies_for(payload).group_by(&method(:name_for)) vertex.outgoing_edges.each do |outgoing_edge| - @parent_of[outgoing_edge.requirement] = states.size - 1 + requirement = outgoing_edge.requirement + parent_index = @parents_of[requirement].last succ = outgoing_edge.destination matching_deps = Array(deps[succ.name]) + dep_matched = matching_deps.include?(requirement) + + # only push the current index when it was originally required by the + # same named spec + if parent_index && states[parent_index].name == name + @parents_of[requirement].push(states.size - 1) + end + if matching_deps.empty? && !succ.root? && succ.predecessors.to_a == [vertex] debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" } - succ.requirements.each { |r| @parent_of.delete(r) } + succ.requirements.each { |r| @parents_of.delete(r) } removed_names = activated.detach_vertex_named(succ.name).map(&:name) requirements.delete_if do |r| @@ -373,9 +382,14 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L382 # so it's safe to delete only based upon name here removed_names.include?(name_for(r)) end - elsif !matching_deps.include?(outgoing_edge.requirement) + elsif !dep_matched + debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" } + # also reset if we're removing the edge, but only if its parent has + # already been fixed up + @parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty? + activated.delete_edge(outgoing_edge) - requirements.delete(outgoing_edge.requirement) + requirements.delete(requirement) end end end @@ -395,13 +409,18 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L409 # @return [Boolean] whether the current spec is satisfied as a new # possibility. def new_spec_satisfied? + unless requirement_satisfied_by?(requirement, activated, possibility) + debug(depth) { 'Unsatisfied by requested spec' } + return false + end + locked_requirement = locked_requirement_named(name) - requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility) + locked_spec_satisfied = !locked_requirement || requirement_satisfied_by?(locked_requirement, activated, possibility) - debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied - requested_spec_satisfied && locked_spec_satisfied + + locked_spec_satisfied end # @param [String] requirement_name the spec name to search for @@ -417,7 +436,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L436 # @return [void] def activate_spec conflicts.delete(name) - debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s } + debug(depth) { "Activated #{name} at #{possibility}" } activated.set_payload(name, possibility) require_nested_dependencies_for(possibility) end @@ -432,7 +451,8 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb#L451 nested_dependencies.each do |d| activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d) parent_index = states.size - 1 - @parent_of[d] ||= parent_index + parents = @parents_of[d] + parents << parent_index if parents.empty? end push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?) Index: lib/rubygems/test_case.rb =================================================================== --- lib/rubygems/test_case.rb (revision 57997) +++ lib/rubygems/test_case.rb (revision 57998) @@ -1334,7 +1334,7 @@ Also, a list: https://github.com/ruby/ruby/blob/trunk/lib/rubygems/test_case.rb#L1334 end ## - # create_gemspec creates gem specification in given +direcotry+ or '.' + # create_gemspec creates gem specification in given +directory+ or '.' # for the given +name+ and +version+. # # Yields the +specification+ to the block, if given Index: lib/rubygems/specification.rb =================================================================== --- lib/rubygems/specification.rb (revision 57997) +++ lib/rubygems/specification.rb (revision 57998) @@ -1128,9 +1128,6 @@ class Gem::Specification < Gem::BasicSpe https://github.com/ruby/ruby/blob/trunk/lib/rubygems/specification.rb#L1128 native = {} specs.reverse_each do |spec| - unless spec - raise Gem::Exception, "unexpectedly spec is nil: #{specs}" - end next if spec.version.prerelease? unless prerelease native[spec.name] = spec.version if spec.platform == Gem::Platform::RUBY Index: test/rubygems/test_gem.rb =================================================================== --- test/rubygems/test_gem.rb (revision 57997) +++ test/rubygems/test_gem.rb (revision 57998) @@ -1443,21 +1443,24 @@ class TestGem < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem.rb#L1443 ENV['RUBYGEMS_GEMDEPS'] = "-" path = File.join @tempdir, "gem.deps.rb" + cmd = [Gem.ruby.dup.untaint, "-I#{LIB_PATH.untaint}", "-rubygems"] + if RUBY_VERSION < '1.9' + cmd << "-e 'puts Gem.loaded_specs.values.map(&:full_name).sort'" + cmd = cmd.join(' ') + else + cmd << "-eputs Gem.loaded_specs.values.map(&:full_name).sort" + end File.open path, "w" do |f| f.puts "gem 'a'" end - out0 = IO.popen([Gem.ruby.dup.untaint, "-I#{LIB_PATH}", "-rubygems", - "-eputs Gem.loaded_specs.values.map(&:full_name).sort"], - &:read).split(/\n/) + out0 = IO.popen(cmd, &:read).split(/\n/) File.open path, "a" do |f| f.puts "gem 'b'" f.puts "gem 'c'" end - out = IO.popen([Gem.ruby.dup.untaint, "-I#{LIB_PATH}", "-rubygems", - "-eputs Gem.loaded_specs.values.map(&:full_name).sort"], - &:read).split(/\n/) + out = IO.popen(cmd, &:read).split(/\n/) assert_equal ["b-1", "c-1"], out - out0 end @@ -1482,21 +1485,24 @@ class TestGem < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem.rb#L1485 Dir.mkdir "sub1" path = File.join @tempdir, "gem.deps.rb" + cmd = [Gem.ruby.dup.untaint, "-Csub1", "-I#{LIB_PATH.untaint}", "-rubygems"] + if RUBY_VERSION < '1.9' + cmd << "-e 'puts Gem.loaded_specs.values.map(&:full_name).sort'" + cmd = cmd.join(' ') + else + cmd << "-eputs Gem.loaded_specs.values.map(&:full_name).sort" + end File.open path, "w" do |f| f.puts "gem 'a'" end - out0 = IO.popen([Gem.ruby.dup.untaint, "-Csub1", "-I#{LIB_PATH}", "-rubygems", - "-eputs Gem.loaded_specs.va (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/