diff options
Diffstat (limited to 'lib/rubygems/request_set.rb')
| -rw-r--r-- | lib/rubygems/request_set.rb | 147 |
1 files changed, 92 insertions, 55 deletions
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb index a8a06d0b95..eb8b4658f3 100644 --- a/lib/rubygems/request_set.rb +++ b/lib/rubygems/request_set.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -require 'tsort' + +require_relative "vendored_tsort" ## # A RequestSet groups a request to activate a set of dependencies. @@ -15,7 +16,7 @@ require 'tsort' # #=> ["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 @@ -107,7 +108,7 @@ class Gem::RequestSet @requests = [] @sets = [] @soft_missing = false - @sorted = nil + @sorted_requests = nil @specs = nil @vendor_set = nil @source_set = nil @@ -151,7 +152,7 @@ class Gem::RequestSet @prerelease = options[:prerelease] requests = [] - download_queue = Queue.new + download_queue = Thread::Queue.new # Create a thread-safe list of gems to download sorted_requests.each do |req| @@ -159,7 +160,7 @@ class Gem::RequestSet end # Create N threads in a pool, have them download all the gems - threads = Gem.configuration.concurrent_downloads.times.map do + threads = Array.new(Gem.configuration.concurrent_downloads) do # When a thread pops this item, it knows to stop running. The symbol # is queued here so that there will be one symbol per thread. download_queue << :stop @@ -180,13 +181,10 @@ class Gem::RequestSet # Install requested gems after they have been downloaded sorted_requests.each do |req| - if req.installed? - req.spec.spec.build_extensions - - if @always_install.none? {|spec| spec == req.spec.spec } - yield req, nil if block_given? - next - end + if req.installed? && @always_install.none? {|spec| spec == req.spec.spec } + req.spec.spec.build_extensions unless options[:build_extension] == false + yield req, nil if block_given? + next end spec = @@ -195,19 +193,8 @@ class Gem::RequestSet yield req, installer if block_given? end rescue Gem::RuntimeRequirementNotMetError => e - recent_match = req.spec.set.find_all(req.request).sort_by(&:version).reverse_each.find do |s| - s = s.spec - s.required_ruby_version.satisfied_by?(Gem.ruby_version) && - s.required_rubygems_version.satisfied_by?(Gem.rubygems_version) && - Gem::Platform.installable?(s) - end - if recent_match - suggestion = "The last version of #{req.request} to support your Ruby & RubyGems was #{recent_match.version}. Try installing it with `gem install #{recent_match.name} -v #{recent_match.version}`" - suggestion += " and then running the current command again" unless @always_install.include?(req.spec.spec) - else - suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" - suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) - end + suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" + suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) e.suggestion = suggestion raise end @@ -247,10 +234,6 @@ class Gem::RequestSet sorted_requests.each do |spec| puts " #{spec.full_name}" end - - if Gem.configuration.really_verbose - @resolver.stats.display - end else installed = install options, &block @@ -265,7 +248,8 @@ class Gem::RequestSet end def install_into(dir, force = true, options = {}) - gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir + gem_home = ENV["GEM_HOME"] + ENV["GEM_HOME"] = dir existing = force ? [] : specs_in(dir) existing.delete_if {|s| @always_install.include? s } @@ -298,7 +282,7 @@ class Gem::RequestSet installed ensure - ENV['GEM_HOME'] = gem_home + ENV["GEM_HOME"] = gem_home end ## @@ -314,7 +298,7 @@ class Gem::RequestSet end end - require "rubygems/dependency_installer" + require_relative "dependency_installer" inst = Gem::DependencyInstaller.new options inst.installed_gems.replace specs @@ -333,12 +317,9 @@ class Gem::RequestSet @git_set.root_dir = @install_dir - lock_file = "#{File.expand_path(path)}.lock".dup.tap(&Gem::UNTAINT) - begin - tokenizer = Gem::RequestSet::Lockfile::Tokenizer.from_file lock_file - parser = tokenizer.make_parser self, [] - parser.parse - rescue Errno::ENOENT + lock_file = "#{File.expand_path(path)}.lock" + if File.exist?(lock_file) + load_lockfile lock_file end gf = Gem::RequestSet::GemDependencyAPI.new self, path @@ -347,33 +328,90 @@ class Gem::RequestSet gf.load end + def load_lockfile(lock_file) # :nodoc: + require "bundler" + require "bundler/lockfile_parser" + + # Bundler::Source::Path resolves relative `remote:` paths against + # Bundler.root, which raises when there is no Gemfile in the working + # directory. Anchor it to the lockfile's directory so PATH sections in a + # `gem install -g` lockfile can be parsed without a Bundler environment. + previous_root = Bundler.instance_variable_get(:@root) + Bundler.instance_variable_set(:@root, Pathname.new(File.expand_path(File.dirname(lock_file)))) + + parser = Bundler::LockfileParser.new(File.read(lock_file), lockfile_path: lock_file) + + parser.specs.group_by(&:source).each do |source, specs| + case source + when Bundler::Source::Rubygems + remotes = source.remotes.map {|remote| Gem::Source.new(remote.to_s) } + remotes << Gem::Source.new(Gem::DEFAULT_HOST) if remotes.empty? + lock_set = Gem::Resolver::LockSet.new(remotes) + specs.each do |spec| + added = lock_set.add(spec.name, spec.version.to_s, spec.platform) + spec.dependencies.each do |dep| + added.each {|s| s.add_dependency dep } + end + end + @sets << lock_set + when Bundler::Source::Git + git_set = Gem::Resolver::GitSet.new + git_set.root_dir = @install_dir + specs.each do |spec| + git_spec = git_set.add_git_spec( + spec.name, + spec.version.to_s, + source.uri.to_s, + source.revision, + source.submodules || false + ) + spec.dependencies.each {|dep| git_spec.add_dependency dep } + end + @sets << git_set + when Bundler::Source::Path + vendor_set = Gem::Resolver::VendorSet.new + specs.each do |spec| + loaded = vendor_set.add_vendor_gem(spec.name, source.path.to_s) + spec.dependencies.each {|dep| loaded.dependencies << dep } + end + @sets << vendor_set + end + end + + parser.dependencies.each_value do |dep| + gem dep.name, *dep.requirement.as_list + end + ensure + Bundler.instance_variable_set(:@root, previous_root) if defined?(previous_root) + end + def pretty_print(q) # :nodoc: - q.group 2, '[RequestSet:', ']' do + q.group 2, "[RequestSet:", "]" do q.breakable if @remote - q.text 'remote' + q.text "remote" q.breakable end if @prerelease - q.text 'prerelease' + q.text "prerelease" q.breakable end if @development_shallow - q.text 'shallow development' + q.text "shallow development" q.breakable elsif @development - q.text 'development' + q.text "development" q.breakable end if @soft_missing - q.text 'soft missing' + q.text "soft missing" end - q.group 2, '[dependencies:', ']' do + q.group 2, "[dependencies:", "]" do q.breakable @dependencies.map do |dep| q.text dep.to_s @@ -382,10 +420,10 @@ class Gem::RequestSet end q.breakable - q.text 'sets:' + q.text "sets:" q.breakable - q.pp @sets.map {|set| set.class } + q.pp @sets.map(&:class) end end @@ -435,11 +473,11 @@ class Gem::RequestSet end def sorted_requests - @sorted ||= strongly_connected_components.flatten + @sorted_requests ||= strongly_connected_components.flatten end def specs - @specs ||= @requests.map {|r| r.full_spec } + @specs ||= @requests.map(&:full_spec) end def specs_in(dir) @@ -454,14 +492,14 @@ class Gem::RequestSet def tsort_each_child(node) # :nodoc: node.spec.dependencies.each do |dep| - next if dep.type == :development and not @development + next if dep.type == :development && !@development match = @requests.find do |r| - dep.match? r.spec.name, r.spec.version, @prerelease + dep.match?(r.spec.name, r.spec.version, r.spec.is_a?(Gem::Resolver::InstalledSpecification) || @prerelease) end unless match - next if dep.type == :development and @development_shallow + next if dep.type == :development && @development_shallow next if @soft_missing raise Gem::DependencyError, "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})" @@ -472,6 +510,5 @@ class Gem::RequestSet end end -require 'rubygems/request_set/gem_dependency_api' -require 'rubygems/request_set/lockfile' -require 'rubygems/request_set/lockfile/tokenizer' +require_relative "request_set/gem_dependency_api" +require_relative "request_set/lockfile" |
