[前][次][番号順一覧][スレッド一覧]

ruby-changes:69509

From: Frederik <ko1@a...>
Date: Fri, 29 Oct 2021 18:19:10 +0900 (JST)
Subject: [ruby-changes:69509] 17fb785d15 (master): [rubygems/rubygems] Vendor tsort into rubygems

https://git.ruby-lang.org/ruby.git/commit/?id=17fb785d15

From 17fb785d1557d35fc9e28af59bdbef50ddbd08d9 Mon Sep 17 00:00:00 2001
From: Frederik Dudzik <frederik.dudzik@s...>
Date: Thu, 21 Oct 2021 12:23:08 -0700
Subject: [rubygems/rubygems] Vendor tsort into rubygems

So that it loads a consistent version of the library and `rubygems` is
never affected by gem activation conflicts related to `tsort`.

Getting CI green required updating one `bundler` spec, because `tsort`
is no longer loaded by `bundle clean` until after `BUNDLE_PATH` has been
changed, so to ensure it is found, it needs to be installed under
`BUNDLE_PATH` as well (which will be different from the global system
path on Bundler 3, meaning installing `tsort` to the global system path
is not enough there). This spec workaround can be removed once we also
vendor `tsort` inside `bundler`.

https://github.com/rubygems/rubygems/commit/d326880999
---
 lib/rubygems/dependency_list.rb                    |   4 +-
 lib/rubygems/request_set.rb                        |   4 +-
 .../molinillo/lib/molinillo/dependency_graph.rb    |   4 +-
 lib/rubygems/tsort.rb                              |   3 +
 lib/rubygems/tsort/lib/tsort.rb                    | 454 +++++++++++++++++++++
 5 files changed, 463 insertions(+), 6 deletions(-)
 create mode 100644 lib/rubygems/tsort.rb
 create mode 100644 lib/rubygems/tsort/lib/tsort.rb

diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index 8f61869874d..10e08fc703c 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -5,7 +5,7 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_list.rb#L5
 # See LICENSE.txt for permissions.
 #++
 
-require 'tsort'
+require_relative 'tsort'
 require_relative 'deprecate'
 
 ##
@@ -20,7 +20,7 @@ class Gem::DependencyList https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_list.rb#L20
   attr_reader :specs
 
   include Enumerable
-  include TSort
+  include Gem::TSort
 
   ##
   # Allows enabling/disabling use of development dependencies
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 9286c542219..01b01599a8f 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,5 +1,5 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/request_set.rb#L1
 # frozen_string_literal: true
-require 'tsort'
+require_relative 'tsort'
 
 ##
 # A RequestSet groups a request to activate a set of dependencies.
@@ -15,7 +15,7 @@ require 'tsort' https://github.com/ruby/ruby/blob/trunk/lib/rubygems/request_set.rb#L15
 #   #=> ["nokogiri-1.6.0", "mini_portile-0.5.1", "pg-0.17.0"]
 
 class Gem::RequestSet
-  include TSort
+  include Gem::TSort
 
   ##
   # Array of gems to install even if already installed
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
index 16430a79f5b..95f8416b967 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
@@ -1,6 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb#L1
 # frozen_string_literal: true
 
-require 'tsort'
+require_relative '../../../../tsort'
 
 require_relative 'dependency_graph/log'
 require_relative 'dependency_graph/vertex'
@@ -17,7 +17,7 @@ module Gem::Resolver::Molinillo https://github.com/ruby/ruby/blob/trunk/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb#L17
       vertices.values.each { |v| yield v }
     end
 
-    include TSort
+    include Gem::TSort
 
     # @!visibility private
     alias tsort_each_node each
diff --git a/lib/rubygems/tsort.rb b/lib/rubygems/tsort.rb
new file mode 100644
index 00000000000..ebe7c3364b7
--- /dev/null
+++ b/lib/rubygems/tsort.rb
@@ -0,0 +1,3 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/tsort.rb#L1
+# frozen_string_literal: true
+
+require_relative 'tsort/lib/tsort'
diff --git a/lib/rubygems/tsort/lib/tsort.rb b/lib/rubygems/tsort/lib/tsort.rb
new file mode 100644
index 00000000000..f68c5947d30
--- /dev/null
+++ b/lib/rubygems/tsort/lib/tsort.rb
@@ -0,0 +1,454 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/tsort/lib/tsort.rb#L1
+# frozen_string_literal: true
+
+#--
+# tsort.rb - provides a module for topological sorting and strongly connected components.
+#++
+#
+
+#
+# Gem::TSort implements topological sorting using Tarjan's algorithm for
+# strongly connected components.
+#
+# Gem::TSort is designed to be able to be used with any object which can be
+# interpreted as a directed graph.
+#
+# Gem::TSort requires two methods to interpret an object as a graph,
+# tsort_each_node and tsort_each_child.
+#
+# * tsort_each_node is used to iterate for all nodes over a graph.
+# * tsort_each_child is used to iterate for child nodes of a given node.
+#
+# The equality of nodes are defined by eql? and hash since
+# Gem::TSort uses Hash internally.
+#
+# == A Simple Example
+#
+# The following example demonstrates how to mix the Gem::TSort module into an
+# existing class (in this case, Hash). Here, we're treating each key in
+# the hash as a node in the graph, and so we simply alias the required
+# #tsort_each_node method to Hash's #each_key method. For each key in the
+# hash, the associated value is an array of the node's child nodes. This
+# choice in turn leads to our implementation of the required #tsort_each_child
+# method, which fetches the array of child nodes and then iterates over that
+# array using the user-supplied block.
+#
+#   require 'rubygems/tsort/lib/tsort'
+#
+#   class Hash
+#     include Gem::TSort
+#     alias tsort_each_node each_key
+#     def tsort_each_child(node, &block)
+#       fetch(node).each(&block)
+#     end
+#   end
+#
+#   {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
+#   #=> [3, 2, 1, 4]
+#
+#   {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
+#   #=> [[4], [2, 3], [1]]
+#
+# == A More Realistic Example
+#
+# A very simple `make' like tool can be implemented as follows:
+#
+#   require 'rubygems/tsort/lib/tsort'
+#
+#   class Make
+#     def initialize
+#       @dep = {}
+#       @dep.default = []
+#     end
+#
+#     def rule(outputs, inputs=[], &block)
+#       triple = [outputs, inputs, block]
+#       outputs.each {|f| @dep[f] = [triple]}
+#       @dep[triple] = inputs
+#     end
+#
+#     def build(target)
+#       each_strongly_connected_component_from(target) {|ns|
+#         if ns.length != 1
+#           fs = ns.delete_if {|n| Array === n}
+#           raise Gem::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
+#         end
+#         n = ns.first
+#         if Array === n
+#           outputs, inputs, block = n
+#           inputs_time = inputs.map {|f| File.mtime f}.max
+#           begin
+#             outputs_time = outputs.map {|f| File.mtime f}.min
+#           rescue Errno::ENOENT
+#             outputs_time = nil
+#           end
+#           if outputs_time == nil ||
+#              inputs_time != nil && outputs_time <= inputs_time
+#             sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
+#             block.call
+#           end
+#         end
+#       }
+#     end
+#
+#     def tsort_each_child(node, &block)
+#       @dep[node].each(&block)
+#     end
+#     include Gem::TSort
+#   end
+#
+#   def command(arg)
+#     print arg, "\n"
+#     system arg
+#   end
+#
+#   m = Make.new
+#   m.rule(%w[t1]) { command 'date > t1' }
+#   m.rule(%w[t2]) { command 'date > t2' }
+#   m.rule(%w[t3]) { command 'date > t3' }
+#   m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
+#   m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
+#   m.build('t5')
+#
+# == Bugs
+#
+# * 'tsort.rb' is wrong name because this library uses
+#   Tarjan's algorithm for strongly connected components.
+#   Although 'strongly_connected_components.rb' is correct but too long.
+#
+# == References
+#
+# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
+# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
+#
+
+module Gem
+  module TSort
+    class Cyclic < StandardError
+    end
+
+    # Returns a topologically sorted array of nodes.
+    # The array is sorted from children to parents, i.e.
+    # the first element has no child and the last node has no parent.
+    #
+    # If there is a cycle, Gem::TSort::Cyclic is raised.
+    #
+    #   class G
+    #     include Gem::TSort
+    #     def initialize(g)
+    #       @g = g
+    #     end
+    #     def tsort_each_child(n, &b) @g[n].each(&b) end
+    #     def tsort_each_node(&b) @g.each_key(&b) end
+    #   end
+    #
+    #   graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+    #   p graph.tsort #=> [4, 2, 3, 1]
+    #
+    #   graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+    #   p graph.tsort # raises Gem::TSort::Cyclic
+    #
+    def tsort
+      each_node = method(:tsort_each_node)
+      each_child = method(:tsort_each_child)
+      Gem::TSort.tsort(each_node, each_child)
+    end
+
+    # Returns a topologically sorted array of nodes.
+    # The array is sorted from children to parents, i.e.
+    # the first element has no child and the last node has no parent.
+    #
+    # The graph is represented by _each_node_ and _each_child_.
+    # _each_node_ should have +call+ method which yields for each node in the graph.
+    # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+    #
+    # If there is a cycle, Gem::TSort::Cyclic is raised.
+    #
+    #   g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+    #   each_node = lambda {|&b| g.each_key(&b) }
+    #   each_child = lambda {|n, &b| g[n].each(&b) }
+    #   p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
+    #
+    #   g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+    #   each_node = lambda {|&b| g.each_key(&b) }
+    #   each_child = lambda {|n, &b| g[n].each(&b) }
+    #   p Gem::T (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]