Class: Bundler::ParallelInstaller
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Inherits: | Object |
| Defined in: | lib/bundler/installer/parallel_installer.rb |
Class Method Summary
Instance Attribute Summary
- #size readonly
- #bsd_make? ⇒ Boolean readonly private
- #finished_installing? ⇒ Boolean readonly private
-
#nmake? ⇒ Boolean
readonly
private
Mirror how RubyGems' extension builder picks the make program so the jobserver is only set up when a GNU-compatible make will consume it.
Instance Method Summary
- #call
- #do_download(spec_install, worker_num) private
- #do_install(spec_install, worker_num) private
- #download_worker_pool private
-
#enqueue_specs(installed_specs)
private
Queue every missing spec for download.
- #failed_specs private
- #handle_error private
- #install_serially private
- #install_with_worker private
-
#process_specs(installed_specs)
private
Process one completed download or installation.
- #require_tree_for_spec(spec) private
- #response_queue private
- #with_jobserver private
- #worker_pool private
Constructor Details
.new(installer, all_specs, size, standalone, force, local: false, skip: nil) ⇒ ParallelInstaller
# File 'lib/bundler/installer/parallel_installer.rb', line 63
def initialize(installer, all_specs, size, standalone, force, local: false, skip: nil) @installer = installer @size = size @standalone = standalone @force = force @local = local @specs = all_specs.map {|s| SpecInstallation.new(s) } specs_by_name = @specs.to_h {|s| [s.name, s] } @specs.each do |spec_install| spec_install.dependencies = spec_install.spec.dependencies.filter_map do |dep| specs_by_name[dep.name] unless dep.type == :development || dep.name == spec_install.name end end @specs.each do |spec_install| spec_install.state = :installed if skip.include?(spec_install.name) end if skip @spec_set = all_specs @rake = @specs.find {|s| s.name == "rake" unless s.installed? } end
Class Method Details
.call(*args, **kwargs)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 57
def self.call(*args, **kwargs) new(*args, **kwargs).call end
Instance Attribute Details
#bsd_make? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/bundler/installer/parallel_installer.rb', line 155
def bsd_make? return false unless Gem.freebsd_platform? make = ENV["MAKE"] || ENV["make"] || "make" !/\bgmake/i.match?(make) end
#finished_installing? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/bundler/installer/parallel_installer.rb', line 239
def finished_installing? @specs.all? do |spec| return true if spec.failed? spec.installed? end end
#nmake? ⇒ Boolean (readonly, private)
Mirror how RubyGems' extension builder picks the make program so the jobserver is only set up when a GNU-compatible make will consume it.
# File 'lib/bundler/installer/parallel_installer.rb', line 149
def nmake? make = ENV["MAKE"] || ENV["make"] make ||= "nmake" if RUBY_PLATFORM.include?("mswin") /\bnmake/i.match?(make.to_s) end
#size (readonly)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 61
attr_reader :size
Instance Method Details
#call
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 83
def call if @rake do_download(@rake, 0) do_install(@rake, 0) Gem::Specification.reset end if @size > 1 install_with_worker else install_serially end handle_error if failed_specs.any? @specs ensure @worker_pool&.stop @download_worker_pool&.stop end
#do_download(spec_install, worker_num) (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 184
def do_download(spec_install, worker_num) Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL, spec_install) gem_installer = Bundler::GemInstaller.new( spec_install.spec, @installer, @standalone, worker_num, @force, @local ) success, = gem_installer.download if success spec_install.state = :downloaded else spec_install.error = "#{}\n\n#{require_tree_for_spec(spec_install.spec)}" spec_install.state = :failed end spec_install end
#do_install(spec_install, worker_num) (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 203
def do_install(spec_install, worker_num) gem_installer = Bundler::GemInstaller.new( spec_install.spec, @installer, @standalone, worker_num, @force, @local ) success, = gem_installer.install_from_spec if success spec_install.state = :installed spec_install. = unless .nil? else spec_install.error = "#{}\n\n#{require_tree_for_spec(spec_install.spec)}" spec_install.state = :failed end Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL, spec_install) spec_install end
#download_worker_pool (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 170
def download_worker_pool @download_worker_pool ||= Bundler::Worker.new(@size, "Gem Downloader", ->(spec_install, worker_num) { do_download(spec_install, worker_num) }, response_queue: response_queue) end
#enqueue_specs(installed_specs) (private)
Queue every missing spec for download. #process_specs schedules each downloaded spec for installation once its dependencies are installed.
# File 'lib/bundler/installer/parallel_installer.rb', line 277
def enqueue_specs(installed_specs) @specs.each do |spec| if spec.installed? installed_specs[spec.name] = true next end spec.state = :enqueued download_worker_pool.enq spec end end
#failed_specs (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 105
def failed_specs @specs.select(&:failed?) end
#handle_error (private)
# File 'lib/bundler/installer/parallel_installer.rb', line 246
def handle_error errors = failed_specs.map(&:error) if exception = errors.find {|e| e.is_a?(Bundler::BundlerError) } raise exception end raise Bundler::InstallError, errors.join("\n\n") end
#install_serially (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 161
def install_serially until finished_installing? raise "failed to find a spec to enqueue while installing serially" unless spec_install = @specs.find(&:ready_to_enqueue?) spec_install.state = :enqueued do_download(spec_install, 0) do_install(spec_install, 0) end end
#install_with_worker (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 109
def install_with_worker with_jobserver do installed_specs = {} enqueue_specs(installed_specs) process_specs(installed_specs) until finished_installing? end end
#process_specs(installed_specs) (private)
Process one completed download or installation. Downloads can finish before their dependencies are installed, so check all downloaded specs after each completion and enqueue any that are now installable.
# File 'lib/bundler/installer/parallel_installer.rb', line 222
def process_specs(installed_specs) spec = worker_pool.deq if spec.installed? installed_specs[spec.name] = true elsif spec.failed? return end @specs.each do |candidate| next unless candidate.ready_to_install?(installed_specs) candidate.state = :installable worker_pool.enq(candidate, priority: candidate.enqueue_with_priority?) end end
#require_tree_for_spec(spec) (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 254
def require_tree_for_spec(spec) tree = @spec_set.what_required(spec) gemfile_name = begin File.basename(SharedHelpers.default_gemfile) rescue GemfileNotFound # This runs while reporting an install error. When no Gemfile can be # located (e.g. Bundler used as a library), raising here would mask # the original error, so fall back to a generic header instead. "Gemfile" end t = String.new("In #{gemfile_name}:\n") tree.each_with_index do |s, depth| t << " " * depth.succ << s.name unless tree.last == s t << %( was resolved to #{s.version}, which depends on) end t << %(\n) end t end
#response_queue (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 180
def response_queue @response_queue ||= Thread::Queue.new end
#with_jobserver (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 118
def with_jobserver # The jobserver hands tokens to child `make` processes through MAKEFLAGS # using the GNU make `--jobserver-auth` protocol. nmake, the default make # on mswin, instead reads MAKEFLAGS as bare option letters and aborts # every native extension build with `fatal error U1065: invalid option # '-'`. Skip the jobserver when nmake is in use. Other Windows toolchains # such as mingw use GNU make and keep working through the inherited pipe. return yield if nmake? || bsd_make? begin r, w = IO.pipe r.close_on_exec = false w.close_on_exec = false w.write("*" * @size) old_makeflags = ENV["MAKEFLAGS"] ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ") yield ensure # Restore MAKEFLAGS before closing the pipe so a close failure can't # leave the process with descriptors that point at a closed pipe. old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS") r&.close w&.close end end
#worker_pool (private)
[ GitHub ]# File 'lib/bundler/installer/parallel_installer.rb', line 175
def worker_pool @worker_pool ||= Bundler::Worker.new(@size, "Parallel Installer", ->(spec_install, worker_num) { do_install(spec_install, worker_num) }, response_queue: response_queue) end