summaryrefslogtreecommitdiff
path: root/lib/rubygems/installer.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/installer.rb')
-rw-r--r--lib/rubygems/installer.rb359
1 files changed, 221 insertions, 138 deletions
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index cf5ed7b502..8f6f9a5aa8 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -1,17 +1,17 @@
# frozen_string_literal: true
+
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
-require_relative 'command'
-require_relative 'installer_uninstaller_utils'
-require_relative 'exceptions'
-require_relative 'deprecate'
-require_relative 'package'
-require_relative 'ext'
-require_relative 'user_interaction'
+require_relative "installer_uninstaller_utils"
+require_relative "exceptions"
+require_relative "deprecate"
+require_relative "package"
+require_relative "ext"
+require_relative "user_interaction"
##
# The installer installs the files contained in the .gem into the Gem.home.
@@ -68,19 +68,28 @@ class Gem::Installer
@path_warning = false
- @install_lock = Thread::Mutex.new
-
class << self
- ##
- # True if we've warned about PATH not including Gem.bindir
+ #
+ # Changes in rubygems to lazily loading `rubygems/command` (in order to
+ # lazily load `optparse` as a side effect) affect bundler's custom installer
+ # which uses `Gem::Command` without requiring it (up until bundler 2.2.29).
+ # This hook is to compensate for that missing require.
+ #
+ # TODO: Remove when rubygems no longer supports running on bundler older
+ # than 2.2.29.
- attr_accessor :path_warning
+ def inherited(klass)
+ if klass.name == "Bundler::RubyGemsGemInstaller"
+ require "rubygems/command"
+ end
+
+ super(klass)
+ end
##
- # Certain aspects of the install process are not thread-safe. This lock is
- # used to allow multiple threads to install Gems at the same time.
+ # True if we've warned about PATH not including Gem.bindir
- attr_reader :install_lock
+ attr_accessor :path_warning
##
# Overrides the executable format.
@@ -117,14 +126,14 @@ class Gem::Installer
@spec = spec
end
- def extract_files(destination_dir, pattern = '*')
+ def extract_files(destination_dir, pattern = "*")
FileUtils.mkdir_p destination_dir
spec.files.each do |file|
file = File.join destination_dir, file
next if File.exist? file
FileUtils.mkdir_p File.dirname(file)
- File.open file, 'w' do |fp|
+ File.open file, "w" do |fp|
fp.puts "# #{file}"
end
end
@@ -169,7 +178,7 @@ class Gem::Installer
# :post_install_message:: Print gem post install message if true
def initialize(package, options={})
- require 'fileutils'
+ require "fileutils"
@options = options
@package = package
@@ -180,10 +189,12 @@ class Gem::Installer
@package.prog_mode = options[:prog_mode]
@package.data_mode = options[:data_mode]
- if options[:user_install]
- @gem_home = Gem.user_dir
- @bin_dir = Gem.bindir gem_home unless options[:bin_dir]
- @plugins_dir = Gem.plugindir(gem_home)
+ if @gem_home == Gem.user_dir
+ # If we get here, then one of the following likely happened:
+ # - `--user-install` was specified
+ # - `Gem::PathSupport#home` fell back to `Gem.user_dir`
+ # - GEM_HOME was manually set to `Gem.user_dir`
+
check_that_user_bin_dir_is_in_path
end
end
@@ -193,8 +204,8 @@ class Gem::Installer
#
# If +@force+ is set +filename+ is overwritten.
#
- # If +filename+ exists and is a RubyGems wrapper for different gem the user
- # is consulted.
+ # If +filename+ exists and it is a RubyGems wrapper for a different gem, then
+ # the user is consulted.
#
# If +filename+ exists and +@bin_dir+ is Gem.default_bindir (/usr/local) the
# user is consulted.
@@ -211,34 +222,45 @@ class Gem::Installer
ruby_executable = false
existing = nil
- File.open generated_bin, 'rb' do |io|
- next unless io.gets =~ /^#!/ # shebang
+ File.open generated_bin, "rb" do |io|
+ line = io.gets
+ shebang = /^#!.*ruby/o
+
+ # TruffleRuby uses a bash prelude in default launchers
+ if load_relative_enabled? || RUBY_ENGINE == "truffleruby"
+ until line.nil? || shebang.match?(line) do
+ line = io.gets
+ end
+ end
+
+ next unless line&.match?(shebang)
+
io.gets # blankline
- # TODO detect a specially formatted comment instead of trying
- # to run a regexp against Ruby code.
- next unless io.gets =~ /This file was generated by RubyGems/
+ # TODO: detect a specially formatted comment instead of trying
+ # to find a string inside Ruby code.
+ next unless io.gets&.include?("This file was generated by RubyGems")
ruby_executable = true
- existing = io.read.slice(%r{
+ existing = io.read.slice(/
^\s*(
gem \s |
load \s Gem\.bin_path\( |
load \s Gem\.activate_bin_path\(
)
(['"])(.*?)(\2),
- }x, 3)
+ /x, 3)
end
return if spec.name == existing
# somebody has written to RubyGems' directory, overwrite, too bad
- return if Gem.default_bindir != @bin_dir and not ruby_executable
+ return if Gem.default_bindir != @bin_dir && !ruby_executable
question = "#{spec.name}'s executable \"#{filename}\" conflicts with ".dup
if ruby_executable
- question << (existing || 'an unknown executable')
+ question << (existing || "an unknown executable")
return if ask_yes_no "#{question}\nOverwrite the executable?", false
@@ -267,8 +289,6 @@ class Gem::Installer
def spec
@package.spec
- rescue Gem::Package::Error => e
- raise Gem::InstallError, "invalid gem: #{e.message}"
end
##
@@ -285,8 +305,6 @@ class Gem::Installer
def install
pre_install_checks
- FileUtils.rm_f File.join gem_home, 'specifications', spec.spec_name
-
run_pre_install_hooks
# Set loaded_from to ensure extension_dir is correct
@@ -301,7 +319,7 @@ class Gem::Installer
FileUtils.rm_rf spec.extension_dir
dir_mode = options[:dir_mode]
- FileUtils.mkdir_p gem_dir, :mode => dir_mode && 0755
+ FileUtils.mkdir_p gem_dir, mode: dir_mode && 0o755
if @options[:install_as_default]
extract_bin
@@ -326,39 +344,37 @@ class Gem::Installer
say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil?
- Gem::Installer.install_lock.synchronize { Gem::Specification.reset }
+ Gem::Specification.add_spec(spec)
+
+ load_plugin
run_post_install_hooks
spec
-
- # TODO This rescue is in the wrong place. What is raising this exception?
- # move this rescue to around the code that actually might raise it.
- rescue Zlib::GzipFile::Error
- raise Gem::InstallError, "gzip error installing #{gem}"
+ rescue Errno::EACCES => e
+ # Permission denied - /path/to/foo
+ raise Gem::FilePermissionError, e.message.split(" - ").last
end
def run_pre_install_hooks # :nodoc:
Gem.pre_install_hooks.each do |hook|
- if hook.call(self) == false
- location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/
+ next unless hook.call(self) == false
+ location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/
- message = "pre-install hook#{location} failed for #{spec.full_name}"
- raise Gem::InstallError, message
- end
+ message = "pre-install hook#{location} failed for #{spec.full_name}"
+ raise Gem::InstallError, message
end
end
def run_post_build_hooks # :nodoc:
Gem.post_build_hooks.each do |hook|
- if hook.call(self) == false
- FileUtils.rm_rf gem_dir
+ next unless hook.call(self) == false
+ FileUtils.rm_rf gem_dir
- location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/
+ location = " at #{$1}" if hook.inspect =~ /[ @](.*:\d+)/
- message = "post-build hook#{location} failed for #{spec.full_name}"
- raise Gem::InstallError, message
- end
+ message = "post-build hook#{location} failed for #{spec.full_name}"
+ raise Gem::InstallError, message
end
end
@@ -374,11 +390,11 @@ class Gem::Installer
# we'll be installing into.
def installed_specs
- @specs ||= begin
+ @installed_specs ||= begin
specs = []
Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
- spec = Gem::Specification.load path.tap(&Gem::UNTAINT)
+ spec = Gem::Specification.load path
specs << spec if spec
end
@@ -404,10 +420,10 @@ class Gem::Installer
# True if the gems in the system satisfy +dependency+.
def installation_satisfies_dependency?(dependency)
- return true if @options[:development] and dependency.type == :development
+ return true if @options[:development] && dependency.type == :development
return true if installed_specs.detect {|s| dependency.matches_spec? s }
return false if @only_install_dir
- not dependency.matching_specs.empty?
+ !dependency.matching_specs.empty?
end
##
@@ -440,23 +456,20 @@ class Gem::Installer
# specifications directory.
def write_spec
- File.open spec_file, 'w' do |file|
- spec.installed_by_version = Gem.rubygems_version
+ spec.installed_by_version = Gem.rubygems_version
- file.puts spec.to_ruby_for_cache
-
- file.fsync rescue nil # for filesystems without fsync(2)
- end
+ Gem.write_binary(spec_file, spec.to_ruby_for_cache)
end
##
# Writes the full .gemspec specification (in Ruby) to the gem home's
# specifications/default directory.
+ #
+ # In contrast to #write_spec, this keeps file lists, so the `gem contents`
+ # command works.
def write_default_spec
- File.open(default_spec_file, "w") do |file|
- file.puts spec.to_ruby
- end
+ Gem.write_binary(default_spec_file, spec.to_ruby)
end
##
@@ -466,7 +479,7 @@ class Gem::Installer
if Gem.win_platform?
script_name = formatted_program_filename(filename) + ".bat"
script_path = File.join bindir, File.basename(script_name)
- File.open script_path, 'w' do |file|
+ File.open script_path, "w" do |file|
file.puts windows_stub_script(bindir, filename)
end
@@ -475,28 +488,19 @@ class Gem::Installer
end
def generate_bin # :nodoc:
- return if spec.executables.nil? or spec.executables.empty?
+ return if spec.executables.nil? || spec.executables.empty?
ensure_writable_dir @bin_dir
spec.executables.each do |filename|
- filename.tap(&Gem::UNTAINT)
bin_path = File.join gem_dir, spec.bindir, filename
-
- unless File.exist? bin_path
- if File.symlink? bin_path
- alert_warning "`#{bin_path}` is dangling symlink pointing to `#{File.readlink bin_path}`"
- else
- alert_warning "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?"
- end
- next
- end
+ next unless File.exist? bin_path
mode = File.stat(bin_path).mode
- dir_mode = options[:prog_mode] || (mode | 0111)
+ dir_mode = options[:prog_mode] || (mode | 0o111)
unless dir_mode == mode
- require 'fileutils'
+ require "fileutils"
FileUtils.chmod dir_mode, bin_path
end
@@ -511,7 +515,7 @@ class Gem::Installer
end
def generate_plugins # :nodoc:
- latest = Gem::Installer.install_lock.synchronize { Gem::Specification.latest_spec_for(spec.name) }
+ latest = Gem::Specification.latest_spec_for(spec.name)
return if latest && latest.version > spec.version
ensure_writable_dir @plugins_dir
@@ -521,6 +525,8 @@ class Gem::Installer
else
regenerate_plugins_for(spec, @plugins_dir)
end
+ rescue ArgumentError => e
+ raise e, "#{latest.name} #{latest.version} #{spec.name} #{spec.version}: #{e.message}"
end
##
@@ -528,17 +534,17 @@ class Gem::Installer
#--
# The Windows script is generated in addition to the regular one due to a
# bug or misfeature in the Windows shell's pipe. See
- # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
+ # https://blade.ruby-lang.org/ruby-talk/193379
def generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename)
- require 'fileutils'
+ require "fileutils"
FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
- File.open bin_script_path, 'wb', 0755 do |file|
+ File.open bin_script_path, "wb", 0o755 do |file|
file.print app_script_text(filename)
- file.chmod(options[:prog_mode] || 0755)
+ file.chmod(options[:prog_mode] || 0o755)
end
verbose bin_script_path
@@ -557,13 +563,13 @@ class Gem::Installer
if File.exist? dst
if File.symlink? dst
link = File.readlink(dst).split File::SEPARATOR
- cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
+ cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ""))
return if spec.version < cur_version
end
File.unlink dst
end
- FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
+ FileUtils.symlink src, dst, verbose: Gem.configuration.really_verbose
rescue NotImplementedError, SystemCallError
alert_warning "Unable to use symlinks, installing wrapper"
generate_bin_script filename, bindir
@@ -585,9 +591,8 @@ class Gem::Installer
#
def shebang(bin_file_name)
- ruby_name = RbConfig::CONFIG['ruby_install_name'] if @env_shebang
path = File.join gem_dir, spec.bindir, bin_file_name
- first_line = File.open(path, "rb") {|file| file.gets } || ""
+ first_line = File.open(path, "rb", &:gets) || ""
if first_line.start_with?("#!")
# Preserve extra words on shebang line, like "-w". Thanks RPA.
@@ -598,7 +603,7 @@ class Gem::Installer
if which = Gem.configuration[:custom_shebang]
# replace bin_file_name with "ruby" to avoid endless loops
- which = which.gsub(/ #{bin_file_name}$/," #{RbConfig::CONFIG['ruby_install_name']}")
+ which = which.gsub(/ #{bin_file_name}$/," #{ruby_install_name}")
which = which.gsub(/\$(\w+)/) do
case $1
@@ -614,14 +619,12 @@ class Gem::Installer
end
"#!#{which}"
- elsif not ruby_name
- "#!#{Gem.ruby}#{opts}"
- elsif opts
- "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
- else
+ elsif @env_shebang
# Create a plain shebang line.
@env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
- "#!#{@env_path} #{ruby_name}"
+ "#!#{@env_path} #{ruby_install_name}"
+ else
+ "#{bash_prolog_script}#!#{Gem.ruby}#{opts}"
end
end
@@ -631,7 +634,6 @@ class Gem::Installer
def ensure_loadable_spec
ruby = spec.to_ruby_for_cache
- ruby.tap(&Gem::UNTAINT)
begin
eval ruby
@@ -652,36 +654,41 @@ class Gem::Installer
def process_options # :nodoc:
@options = {
- :bin_dir => nil,
- :env_shebang => false,
- :force => false,
- :only_install_dir => false,
- :post_install_message => true,
+ bin_dir: nil,
+ env_shebang: false,
+ force: false,
+ only_install_dir: false,
+ post_install_message: true,
}.merge options
@env_shebang = options[:env_shebang]
@force = options[:force]
@install_dir = options[:install_dir]
- @gem_home = options[:install_dir] || Gem.dir
- @plugins_dir = Gem.plugindir(@gem_home)
+ @user_install = options[:user_install]
@ignore_dependencies = options[:ignore_dependencies]
@format_executable = options[:format_executable]
@wrappers = options[:wrappers]
@only_install_dir = options[:only_install_dir]
+ @bin_dir = options[:bin_dir]
+ @development = options[:development]
+ @build_root = options[:build_root]
+
+ @build_args = options[:build_args]
+
+ @gem_home = @install_dir || user_install_dir || Gem.dir
+
# If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home.
- @bin_dir = options[:bin_dir] || Gem.bindir(gem_home)
- @development = options[:development]
- @build_root = options[:build_root]
+ @bin_dir ||= Gem.bindir(@gem_home)
- @build_args = options[:build_args] || Gem::Command.build_args
+ @plugins_dir = Gem.plugindir(@gem_home)
unless @build_root.nil?
- @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ''))
- @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ''))
- @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, ''))
+ @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ""))
+ @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ""))
+ @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, ""))
alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}\n Plugins dir: #{@plugins_dir}"
end
end
@@ -692,7 +699,7 @@ class Gem::Installer
user_bin_dir = @bin_dir || Gem.bindir(gem_home)
user_bin_dir = user_bin_dir.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
- path = ENV['PATH']
+ path = ENV["PATH"]
path = path.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
if Gem.win_platform?
@@ -703,7 +710,7 @@ class Gem::Installer
path = path.split(File::PATH_SEPARATOR)
unless path.include? user_bin_dir
- unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV['HOME'], '~'))
+ unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV["HOME"], "~"))
alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
self.class.path_warning = true
end
@@ -711,28 +718,27 @@ class Gem::Installer
end
def verify_gem_home # :nodoc:
- FileUtils.mkdir_p gem_home, :mode => options[:dir_mode] && 0755
- raise Gem::FilePermissionError, gem_home unless File.writable?(gem_home)
+ FileUtils.mkdir_p gem_home, mode: options[:dir_mode] && 0o755
end
def verify_spec
- unless spec.name =~ Gem::Specification::VALID_NAME_PATTERN
+ unless Gem::Specification::VALID_NAME_PATTERN.match?(spec.name)
raise Gem::InstallError, "#{spec} has an invalid name"
end
- if spec.raw_require_paths.any?{|path| path =~ /\R/ }
+ if spec.raw_require_paths.any? {|path| path =~ /\R/ }
raise Gem::InstallError, "#{spec} has an invalid require_paths"
end
- if spec.extensions.any?{|ext| ext =~ /\R/ }
+ if spec.extensions.any? {|ext| ext =~ /\R/ }
raise Gem::InstallError, "#{spec} has an invalid extensions"
end
- if spec.platform.to_s =~ /\R/
+ if /\R/.match?(spec.platform.to_s)
raise Gem::InstallError, "#{spec.platform} is an invalid platform"
end
- unless spec.specification_version.to_s =~ /\A\d+\z/
+ unless /\A\d+\z/.match?(spec.specification_version.to_s)
raise Gem::InstallError, "#{spec} has an invalid specification_version"
end
@@ -749,9 +755,9 @@ class Gem::Installer
# Return the text for an application file.
def app_script_text(bin_file_name)
- # note that the `load` lines cannot be indented, as old RG versions match
+ # NOTE: that the `load` lines cannot be indented, as old RG versions match
# against the beginning of the line
- return <<-TEXT
+ <<-TEXT
#{shebang bin_file_name}
#
# This file was generated by RubyGems.
@@ -768,7 +774,7 @@ str = ARGV.first
if str
str = str.b[/\\A_(.*)_\\z/, 1]
if str and Gem::Version.correct?(str)
- version = str
+ #{explicit_version_requirement(spec.name)}
ARGV.shift
end
end
@@ -783,7 +789,7 @@ TEXT
end
def gemdeps_load(name)
- return '' if name == "bundler"
+ return "" if name == "bundler"
<<-TEXT
@@ -791,18 +797,27 @@ Gem.use_gemdeps
TEXT
end
+ def explicit_version_requirement(name)
+ code = "version = str"
+ return code unless name == "bundler"
+
+ code += <<-TEXT
+
+ ENV['BUNDLER_VERSION'] = str
+TEXT
+ end
+
##
# return the stub script text used to launch the true Ruby script
def windows_stub_script(bindir, bin_file_name)
- rb_config = RbConfig::CONFIG
rb_topdir = RbConfig::TOPDIR || File.dirname(rb_config["bindir"])
# get ruby executable file name from RbConfig
- ruby_exe = "#{rb_config['RUBY_INSTALL_NAME']}#{rb_config['EXEEXT']}"
+ ruby_exe = "#{rb_config["RUBY_INSTALL_NAME"]}#{rb_config["EXEEXT"]}"
ruby_exe = "ruby.exe" if ruby_exe.empty?
- if File.exist?(File.join bindir, ruby_exe)
+ if File.exist?(File.join(bindir, ruby_exe))
# stub & ruby.exe within same folder. Portable
<<-TEXT
@ECHO OFF
@@ -810,7 +825,7 @@ TEXT
TEXT
elsif bindir.downcase.start_with? rb_topdir.downcase
# stub within ruby folder, but not standard bin. Portable
- require 'pathname'
+ require "pathname"
from = Pathname.new bindir
to = Pathname.new "#{rb_topdir}/bin"
rel = to.relative_path_from from
@@ -832,7 +847,7 @@ TEXT
# configure scripts and rakefiles or mkrf_conf files.
def build_extensions
- builder = Gem::Ext::Builder.new spec, @build_args
+ builder = Gem::Ext::Builder.new spec, build_args
builder.build_extensions
end
@@ -919,17 +934,17 @@ TEXT
# extensions.
def write_build_info_file
- return if @build_args.empty?
+ return if build_args.empty?
- build_info_dir = File.join gem_home, 'build_info'
+ build_info_dir = File.join gem_home, "build_info"
dir_mode = options[:dir_mode]
- FileUtils.mkdir_p build_info_dir, :mode => dir_mode && 0755
+ FileUtils.mkdir_p build_info_dir, mode: dir_mode && 0o755
build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
- File.open build_info_file, 'w' do |io|
- @build_args.each do |arg|
+ File.open build_info_file, "w" do |io|
+ build_args.each do |arg|
io.puts arg
end
end
@@ -941,17 +956,85 @@ TEXT
# Writes the .gem file to the cache directory
def write_cache_file
- cache_file = File.join gem_home, 'cache', spec.file_name
+ cache_file = File.join gem_home, "cache", spec.file_name
@package.copy_to cache_file
end
def ensure_writable_dir(dir) # :nodoc:
begin
- Dir.mkdir dir, *[options[:dir_mode] && 0755].compact
+ Dir.mkdir dir, *[options[:dir_mode] && 0o755].compact
rescue SystemCallError
raise unless File.directory? dir
end
raise Gem::FilePermissionError.new(dir) unless File.writable? dir
end
+
+ private
+
+ def user_install_dir
+ # never install to user home in --build-root mode
+ return unless @build_root.nil?
+
+ # Please note that @user_install might have three states:
+ # * `true`: `--user-install`
+ # * `false`: `--no-user-install` and
+ # * `nil`: option was not specified
+ if @user_install || (@user_install.nil? && Gem.default_user_install)
+ Gem.user_dir
+ end
+ end
+
+ def build_args
+ @build_args ||= begin
+ require_relative "command"
+ Gem::Command.build_args
+ end
+ end
+
+ def rb_config
+ RbConfig::CONFIG
+ end
+
+ def ruby_install_name
+ rb_config["ruby_install_name"]
+ end
+
+ def load_relative_enabled?
+ rb_config["LIBRUBY_RELATIVE"] == "yes"
+ end
+
+ def bash_prolog_script
+ if load_relative_enabled?
+ script = +<<~EOS
+ bindir="${0%/*}"
+ EOS
+
+ script << %(exec "$bindir/#{ruby_install_name}" "-x" "$0" "$@"\n)
+
+ <<~EOS
+ #!/bin/sh
+ # -*- ruby -*-
+ _=_\\
+ =begin
+ #{script.chomp}
+ =end
+ EOS
+ else
+ ""
+ end
+ end
+
+ def load_plugin
+ specs = Gem::Specification.find_all_by_name(spec.name)
+ # If old version already exists, this plugin isn't loaded
+ # immediately. It's for avoiding a case that multiple versions
+ # are loaded at the same time.
+ return unless specs.size == 1
+
+ plugin_files = spec.plugins.map do |plugin|
+ File.join(@plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}")
+ end
+ Gem.load_plugin_files(plugin_files)
+ end
end