summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/rubygems/request_set.rb
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rubygems/request_set.rb
Fresh start
Diffstat (limited to 'jni/ruby/lib/rubygems/request_set.rb')
-rw-r--r--jni/ruby/lib/rubygems/request_set.rb413
1 files changed, 413 insertions, 0 deletions
diff --git a/jni/ruby/lib/rubygems/request_set.rb b/jni/ruby/lib/rubygems/request_set.rb
new file mode 100644
index 0000000..05bfcbe
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request_set.rb
@@ -0,0 +1,413 @@
+require 'tsort'
+
+##
+# A RequestSet groups a request to activate a set of dependencies.
+#
+# nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6'
+# pg = Gem::Dependency.new 'pg', '~> 0.14'
+#
+# set = Gem::RequestSet.new nokogiri, pg
+#
+# requests = set.resolve
+#
+# p requests.map { |r| r.full_name }
+# #=> ["nokogiri-1.6.0", "mini_portile-0.5.1", "pg-0.17.0"]
+
+class Gem::RequestSet
+
+ include TSort
+
+ ##
+ # Array of gems to install even if already installed
+
+ attr_accessor :always_install
+
+ attr_reader :dependencies
+
+ attr_accessor :development
+
+ ##
+ # Errors fetching gems during resolution.
+
+ attr_reader :errors
+
+ ##
+ # Set to true if you want to install only direct development dependencies.
+
+ attr_accessor :development_shallow
+
+ ##
+ # The set of git gems imported via load_gemdeps.
+
+ attr_reader :git_set # :nodoc:
+
+ ##
+ # When true, dependency resolution is not performed, only the requested gems
+ # are installed.
+
+ attr_accessor :ignore_dependencies
+
+ attr_reader :install_dir # :nodoc:
+
+ ##
+ # If true, allow dependencies to match prerelease gems.
+
+ attr_accessor :prerelease
+
+ ##
+ # When false no remote sets are used for resolving gems.
+
+ attr_accessor :remote
+
+ attr_reader :resolver # :nodoc:
+
+ ##
+ # Sets used for resolution
+
+ attr_reader :sets # :nodoc:
+
+ ##
+ # Treat missing dependencies as silent errors
+
+ attr_accessor :soft_missing
+
+ ##
+ # The set of vendor gems imported via load_gemdeps.
+
+ attr_reader :vendor_set # :nodoc:
+
+ ##
+ # Creates a RequestSet for a list of Gem::Dependency objects, +deps+. You
+ # can then #resolve and #install the resolved list of dependencies.
+ #
+ # nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6'
+ # pg = Gem::Dependency.new 'pg', '~> 0.14'
+ #
+ # set = Gem::RequestSet.new nokogiri, pg
+
+ def initialize *deps
+ @dependencies = deps
+
+ @always_install = []
+ @conservative = false
+ @dependency_names = {}
+ @development = false
+ @development_shallow = false
+ @errors = []
+ @git_set = nil
+ @ignore_dependencies = false
+ @install_dir = Gem.dir
+ @prerelease = false
+ @remote = true
+ @requests = []
+ @sets = []
+ @soft_missing = false
+ @sorted = nil
+ @specs = nil
+ @vendor_set = nil
+
+ yield self if block_given?
+ end
+
+ ##
+ # Declare that a gem of name +name+ with +reqs+ requirements is needed.
+
+ def gem name, *reqs
+ if dep = @dependency_names[name] then
+ dep.requirement.concat reqs
+ else
+ dep = Gem::Dependency.new name, reqs
+ @dependency_names[name] = dep
+ @dependencies << dep
+ end
+ end
+
+ ##
+ # Add +deps+ Gem::Dependency objects to the set.
+
+ def import deps
+ @dependencies.concat deps
+ end
+
+ ##
+ # Installs gems for this RequestSet using the Gem::Installer +options+.
+ #
+ # If a +block+ is given an activation +request+ and +installer+ are yielded.
+ # The +installer+ will be +nil+ if a gem matching the request was already
+ # installed.
+
+ def install options, &block # :yields: request, installer
+ if dir = options[:install_dir]
+ requests = install_into dir, false, options, &block
+ return requests
+ end
+
+ cache_dir = options[:cache_dir] || Gem.dir
+ @prerelease = options[:prerelease]
+
+ requests = []
+
+ sorted_requests.each do |req|
+ if req.installed? then
+ req.spec.spec.build_extensions
+
+ if @always_install.none? { |spec| spec == req.spec.spec } then
+ yield req, nil if block_given?
+ next
+ end
+ end
+
+ path = req.download cache_dir
+
+ inst = Gem::Installer.new path, options
+
+ yield req, inst if block_given?
+
+ requests << inst.install
+ end
+
+ requests
+ ensure
+ raise if $!
+ return requests if options[:gemdeps]
+
+ specs = requests.map do |request|
+ case request
+ when Gem::Resolver::ActivationRequest then
+ request.spec.spec
+ else
+ request
+ end
+ end
+
+ require 'rubygems/dependency_installer'
+ inst = Gem::DependencyInstaller.new options
+ inst.installed_gems.replace specs
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call inst, specs
+ end unless Gem.done_installing_hooks.empty?
+ end
+
+ ##
+ # Installs from the gem dependencies files in the +:gemdeps+ option in
+ # +options+, yielding to the +block+ as in #install.
+ #
+ # If +:without_groups+ is given in the +options+, those groups in the gem
+ # dependencies file are not used. See Gem::Installer for other +options+.
+
+ def install_from_gemdeps options, &block
+ gemdeps = options[:gemdeps]
+
+ @install_dir = options[:install_dir] || Gem.dir
+ @prerelease = options[:prerelease]
+ @remote = options[:domain] != :local
+ @conservative = true if options[:conservative]
+
+ gem_deps_api = load_gemdeps gemdeps, options[:without_groups], true
+
+ resolve
+
+ if options[:explain]
+ puts "Gems to install:"
+
+ sorted_requests.each do |spec|
+ puts " #{spec.full_name}"
+ end
+
+ if Gem.configuration.really_verbose
+ @resolver.stats.display
+ end
+ else
+ installed = install options, &block
+
+ if options.fetch :lock, true then
+ lockfile =
+ Gem::RequestSet::Lockfile.new self, gemdeps, gem_deps_api.dependencies
+ lockfile.write
+ end
+
+ installed
+ end
+ end
+
+ def install_into dir, force = true, options = {}
+ gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir
+
+ existing = force ? [] : specs_in(dir)
+ existing.delete_if { |s| @always_install.include? s }
+
+ dir = File.expand_path dir
+
+ installed = []
+
+ options[:development] = false
+ options[:install_dir] = dir
+ options[:only_install_dir] = true
+ @prerelease = options[:prerelease]
+
+ sorted_requests.each do |request|
+ spec = request.spec
+
+ if existing.find { |s| s.full_name == spec.full_name } then
+ yield request, nil if block_given?
+ next
+ end
+
+ spec.install options do |installer|
+ yield request, installer if block_given?
+ end
+
+ installed << request
+ end
+
+ installed
+ ensure
+ ENV['GEM_HOME'] = gem_home
+ end
+
+ ##
+ # Load a dependency management file.
+
+ def load_gemdeps path, without_groups = [], installing = false
+ @git_set = Gem::Resolver::GitSet.new
+ @vendor_set = Gem::Resolver::VendorSet.new
+
+ @git_set.root_dir = @install_dir
+
+ lockfile = Gem::RequestSet::Lockfile.new self, path
+ lockfile.parse
+
+ gf = Gem::RequestSet::GemDependencyAPI.new self, path
+ gf.installing = installing
+ gf.without_groups = without_groups if without_groups
+ gf.load
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[RequestSet:', ']' do
+ q.breakable
+
+ if @remote then
+ q.text 'remote'
+ q.breakable
+ end
+
+ if @prerelease then
+ q.text 'prerelease'
+ q.breakable
+ end
+
+ if @development_shallow then
+ q.text 'shallow development'
+ q.breakable
+ elsif @development then
+ q.text 'development'
+ q.breakable
+ end
+
+ if @soft_missing then
+ q.text 'soft missing'
+ end
+
+ q.group 2, '[dependencies:', ']' do
+ q.breakable
+ @dependencies.map do |dep|
+ q.text dep.to_s
+ q.breakable
+ end
+ end
+
+ q.breakable
+ q.text 'sets:'
+
+ q.breakable
+ q.pp @sets.map { |set| set.class }
+ end
+ end
+
+ ##
+ # Resolve the requested dependencies and return an Array of Specification
+ # objects to be activated.
+
+ def resolve set = Gem::Resolver::BestSet.new
+ @sets << set
+ @sets << @git_set
+ @sets << @vendor_set
+
+ set = Gem::Resolver.compose_sets(*@sets)
+ set.remote = @remote
+ set.prerelease = @prerelease
+
+ resolver = Gem::Resolver.new @dependencies, set
+ resolver.development = @development
+ resolver.development_shallow = @development_shallow
+ resolver.ignore_dependencies = @ignore_dependencies
+ resolver.soft_missing = @soft_missing
+
+ if @conservative
+ installed_gems = {}
+ Gem::Specification.find_all do |spec|
+ (installed_gems[spec.name] ||= []) << spec
+ end
+ resolver.skip_gems = installed_gems
+ end
+
+ @resolver = resolver
+
+ @requests = resolver.resolve
+
+ @errors = set.errors
+
+ @requests
+ end
+
+ ##
+ # Resolve the requested dependencies against the gems available via Gem.path
+ # and return an Array of Specification objects to be activated.
+
+ def resolve_current
+ resolve Gem::Resolver::CurrentSet.new
+ end
+
+ def sorted_requests
+ @sorted ||= strongly_connected_components.flatten
+ end
+
+ def specs
+ @specs ||= @requests.map { |r| r.full_spec }
+ end
+
+ def specs_in dir
+ Dir["#{dir}/specifications/*.gemspec"].map do |g|
+ Gem::Specification.load g
+ end
+ end
+
+ def tsort_each_node &block # :nodoc:
+ @requests.each(&block)
+ end
+
+ def tsort_each_child node # :nodoc:
+ node.spec.dependencies.each do |dep|
+ next if dep.type == :development and not @development
+
+ match = @requests.find { |r|
+ dep.match? r.spec.name, r.spec.version, @prerelease
+ }
+
+ unless match then
+ next if dep.type == :development and @development_shallow
+ next if @soft_missing
+ raise Gem::DependencyError,
+ "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
+ end
+
+ yield match
+ end
+ end
+
+end
+
+require 'rubygems/request_set/gem_dependency_api'
+require 'rubygems/request_set/lockfile'