класс Bundler::Installer
Атрибуты
Публичные методы класса
# File lib/bundler/installer.rb, line 22
def self.install(root, definition, options = {})
installer = new(root, definition)
Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL_ALL, definition.dependencies)
installer.run(options)
Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL_ALL, definition.dependencies)
installer
end Начинает процесс установки для Bundler. Дополнительную информацию см. в методе run этого класса.
# File lib/bundler/installer.rb, line 30
def initialize(root, definition)
@root = root
@definition = definition
@post_install_messages = {}
end Публичные методы экземпляра
# File lib/bundler/installer.rb, line 99
def generate_bundler_executable_stubs(spec, options = {})
if options[:binstubs_cmd] && spec.executables.empty?
options = {}
spec.runtime_dependencies.each do |dep|
bins = @definition.specs[dep].first.executables
options[dep.name] = bins unless bins.empty?
end
if options.any?
Bundler.ui.warn "#{spec.name} has no executables, but you may want " \
"one from a gem it depends on."
options.each {|name, bins| Bundler.ui.warn " #{name} has: #{bins.join(", ")}" }
else
Bundler.ui.warn "There are no executables for the gem #{spec.name}."
end
return
end
# double-assignment to avoid warnings about variables that will be used by ERB
bin_path = Bundler.bin_path
bin_path = bin_path
relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path)
relative_gemfile_path = relative_gemfile_path
ruby_command = Thor::Util.ruby_command
ruby_command = ruby_command
template_path = File.expand_path("../templates/Executable", __FILE__)
if spec.name == "bundler"
template_path += ".bundler"
spec.executables = %(bundle)
end
template = File.read(template_path)
exists = []
spec.executables.each do |executable|
binstub_path = "#{bin_path}/#{executable}"
if File.exist?(binstub_path) && !options[:force]
exists << executable
next
end
File.open(binstub_path, "w", 0o777 & ~File.umask) do |f|
if RUBY_VERSION >= "2.6"
f.puts ERB.new(template, :trim_mode => "-").result(binding)
else
f.puts ERB.new(template, nil, "-").result(binding)
end
end
end
if options[:binstubs_cmd] && exists.any?
case exists.size
when 1
Bundler.ui.warn "Skipped #{exists[0]} since it already exists."
when 2
Bundler.ui.warn "Skipped #{exists.join(" and ")} since they already exist."
else
items = exists[0...-1].empty? ? nil : exists[0...-1].join(", ")
skipped = [items, exists[-1]].compact.join(" and ")
Bundler.ui.warn "Skipped #{skipped} since they already exist."
end
Bundler.ui.warn "If you want to overwrite skipped stubs, use --force."
end
end # File lib/bundler/installer.rb, line 162
def generate_standalone_bundler_executable_stubs(spec)
# double-assignment to avoid warnings about variables that will be used by ERB
bin_path = Bundler.bin_path
unless path = Bundler.settings[:path]
raise "Can't standalone without an explicit path set"
end
standalone_path = Bundler.root.join(path).relative_path_from(bin_path)
standalone_path = standalone_path
template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__))
ruby_command = Thor::Util.ruby_command
ruby_command = ruby_command
spec.executables.each do |executable|
next if executable == "bundle"
executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
executable_path = executable_path
File.open "#{bin_path}/#{executable}", "w", 0o755 do |f|
if RUBY_VERSION >= "2.6"
f.puts ERB.new(template, :trim_mode => "-").result(binding)
else
f.puts ERB.new(template, nil, "-").result(binding)
end
end
end
end # File lib/bundler/installer.rb, line 70
def run(options)
create_bundle_path
ProcessLock.lock do
if Bundler.frozen_bundle?
@definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
end
if @definition.dependencies.empty?
Bundler.ui.warn "The Gemfile specifies no dependencies"
lock
return
end
if resolve_if_needed(options)
ensure_specs_are_compatible!
warn_on_incompatible_bundler_deps
load_plugins
options.delete(:jobs)
else
options[:jobs] = 1 # to avoid the overhead of Bundler::Worker
end
install(options)
lock unless Bundler.frozen_bundle?
Standalone.new(options[:standalone], @definition).generate if options[:standalone]
end
end Выполняет процедуры установки для определённого Gemfile.
Сначала этот метод проверяет существование `Bundler.bundle_path`. Если его нет, то Bundler создаёт каталог. Это обычно та же директория, что и RubyGems, типичный путь - `~/.gem`, если не указано иное.
Во-вторых, он проверяет, настроен ли Bundler на «замораживание» («frozen»). Замораживание гарантирует, что Gemfile и Gemfile.lock совпадают. Это предотвращает ситуации, когда разработчик обновляет Gemfile, но не выполняет `bundle install`, что приводит к тому, что Gemfile.lock не обновляется правильно. Если этот файл не обновлён, любой другой разработчик, выполняющий `bundle install`, потенциально установит неверные gem'ы.
В-третьих, Bundler проверяет, есть ли зависимые компоненты, указанные в Gemfile. Если зависимостей нет, Bundler выводит предупреждение и метод завершает работу.
В-четвёртых, Bundler проверяет существование Gemfile.lock и, если оно есть, устанавливает определение на основе Gemfile и Gemfile.lock. На этом шаге Bundler также загрузит информацию о новых gem'ах, отсутствующих в Gemfile.lock, и, при необходимости, разрешит зависимости.
В-пятых, Bundler разрешает зависимости, либо из кэша gem'ов, либо удалённо. Затем происходит установка gem'ов, а также создание заглушек для их исполняемых файлов, только если опция –binstubs была передана или Bundler.options был установлен ранее.
В-шестых, создаётся новый Gemfile.lock на основе установленных gem'ов, чтобы при следующем запуске `bundle install` пользователь получал любые обновления из этого процесса.
Наконец, если пользователь указал флаг standalone, Bundler сгенерирует необходимые пути require и сохранит их в файле setup.rb. Для получения дополнительной информации см. `bundle standalone –help`.
Приватные методы экземпляра
# File lib/bundler/installer.rb, line 277 def can_install_in_parallel? true end
# File lib/bundler/installer.rb, line 288
def create_bundle_path
SharedHelpers.filesystem_access(Bundler.bundle_path.to_s) do |p|
Bundler.mkdir_p(p)
end unless Bundler.bundle_path.exist?
rescue Errno::EEXIST
raise PathError, "Could not install to path `#{Bundler.bundle_path}` " \
"because a file already exists at that path. Either remove or rename the file so the directory can be created."
end # File lib/bundler/installer.rb, line 243
def ensure_specs_are_compatible!
system_ruby = Bundler::RubyVersion.system
rubygems_version = Gem::Version.create(Gem::VERSION)
@definition.specs.each do |spec|
if required_ruby_version = spec.required_ruby_version
unless required_ruby_version.satisfied_by?(system_ruby.gem_version)
raise InstallError, "#{spec.full_name} requires ruby version #{required_ruby_version}, " \
"which is incompatible with the current version, #{system_ruby}"
end
end
next unless required_rubygems_version = spec.required_rubygems_version
unless required_rubygems_version.satisfied_by?(rubygems_version)
raise InstallError, "#{spec.full_name} requires rubygems version #{required_rubygems_version}, " \
"which is incompatible with the current version, #{rubygems_version}"
end
end
end # File lib/bundler/installer.rb, line 194 def install(options) force = options["force"] jobs = installation_parallelization(options) install_in_parallel jobs, options[:standalone], force end
Порядок, предоставляемый решателем, важен, так как зависимости могут повлиять на установку gem. Однако это редкая ситуация (кроме rake), и параллельная установка намного быстрее. Поэтому мы позволяем людям выбирать.
# File lib/bundler/installer.rb, line 281
def install_in_parallel(size, standalone, force = false)
spec_installations = ParallelInstaller.call(self, @definition.specs, size, standalone, force)
spec_installations.each do |installation|
post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
end
end # File lib/bundler/installer.rb, line 200
def installation_parallelization(options)
if jobs = options.delete(:jobs)
return jobs
end
return 1 unless can_install_in_parallel?
auto_config_jobs = Bundler.feature_flag.auto_config_jobs?
if jobs = Bundler.settings[:jobs]
if auto_config_jobs
jobs
else
[jobs.pred, 1].max
end
elsif auto_config_jobs
processor_count
else
1
end
end # File lib/bundler/installer.rb, line 228
def load_plugins
Bundler.rubygems.load_plugins
requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
path_plugin_files = requested_path_gems.map do |spec|
begin
Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
rescue TypeError
error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
raise Gem::InvalidSpecificationException, error_message
end
end.flatten
Bundler.rubygems.load_plugin_files(path_plugin_files)
end # File lib/bundler/installer.rb, line 307
def lock(opts = {})
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
end # File lib/bundler/installer.rb, line 221 def processor_count require "etc" Etc.nprocessors rescue StandardError 1 end
# File lib/bundler/installer.rb, line 298
def resolve_if_needed(options)
if !@definition.unlocking? && !options["force"] && !options["all-platforms"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file?
return false if @definition.nothing_changed? && !@definition.missing_specs?
end
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
true
end возвращает, нужно ли повторное разрешение
# File lib/bundler/installer.rb, line 261
def warn_on_incompatible_bundler_deps
bundler_version = Gem::Version.create(Bundler::VERSION)
@definition.specs.each do |spec|
spec.dependencies.each do |dep|
next if dep.type == :development
next unless dep.name == "bundler".freeze
next if dep.requirement.satisfied_by?(bundler_version)
Bundler.ui.warn "#{spec.name} (#{spec.version}) has dependency" \
" #{SharedHelpers.pretty_dependency(dep)}" \
", which is unsatisfied by the current bundler version #{VERSION}" \
", so the dependency is being ignored"
end
end
end
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.