Extended maintenance of Ruby 1.9.3 ended on February 23, 2015. Read more
The installer class processes RubyGem .gem files and installs the files contained in the .gem into the Gem.path.
Gem::Installer does the work of putting files in all the right places on the filesystem including unpacking the gem into its gem dir, installing the gemspec in the specifications dir, storing the cached gem in the cache dir, and installing either wrappers or symlinks for executables.
The installer invokes pre and post install hooks. Hooks can be added either through a rubygems_plugin.rb file in an installed gem or via a rubygems/defaults/#{RUBY_ENGINE}.rb or rubygems/defaults/operating_system.rb file. See Gem.pre_install and Gem.post_install for details.
Paths where env(1) might live. Some systems are broken and have it in /bin
Defaults to use Ruby’s program prefix and suffix.
# File rubygems/installer.rb, line 73
def exec_format
@exec_format ||= Gem.default_exec_format
end
Constructs an Installer instance that will
install the gem located at gem. options is a
Hash with the following keys:
Use /usr/bin/env in bin wrappers.
Overrides all version checks and security policy checks, except for a signed-gems-only policy.
Don’t raise if a dependency is missing.
The directory to install the gem into.
Format the executable the same as the ruby executable. If your ruby is ruby18, foo_exec will be installed as foo_exec18.
Use the specified security policy. See Gem::Security
Install wrappers if true, symlinks if false.
# File rubygems/installer.rb, line 94
def initialize(gem, options={})
require 'fileutils'
@gem = gem
@options = options
process_options
if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir
check_that_user_bin_dir_is_in_path
end
end
Return the text for an application file.
# File rubygems/installer.rb, line 461
def app_script_text(bin_file_name)
return <<-TEXT
#{shebang bin_file_name}
#
# This file was generated by RubyGems.
#
# The application '#{spec.name}' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = "#{Gem::Requirement.default}"
if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\\A_(.*)_\\z/
version = $1
ARGV.shift
end
end
gem '#{spec.name}', version
load Gem.bin_path('#{spec.name}', '#{bin_file_name}', version)
TEXT
end
Builds extensions. Valid types of extensions are extconf.rb files, configure scripts and rakefiles or mkrf_conf files.
# File rubygems/installer.rb, line 509
def build_extensions
return if spec.extensions.empty?
say "Building native extensions. This could take a while..."
dest_path = File.join gem_dir, spec.require_paths.first
ran_rake = false # only run rake once
spec.extensions.each do |extension|
break if ran_rake
results = []
builder = case extension
when /extconf/ then
Gem::Ext::ExtConfBuilder
when /configure/ then
Gem::Ext::ConfigureBuilder
when /rakefile/i, /mkrf_conf/i then
ran_rake = true
Gem::Ext::RakeBuilder
else
results = ["No builder for extension '#{extension}'"]
nil
end
extension_dir = begin
File.join gem_dir, File.dirname(extension)
rescue TypeError # extension == nil
gem_dir
end
begin
Dir.chdir extension_dir do
results = builder.build(extension, gem_dir, dest_path, results)
say results.join("\n") if Gem.configuration.really_verbose
end
rescue
results = results.join "\n"
gem_make_out = File.join extension_dir, 'gem_make.out'
open gem_make_out, 'wb' do |io| io.puts results end
message = <<-EOF
ERROR: Failed to build gem native extension.
#{results}
Gem files will remain installed in #{gem_dir} for inspection.
Results logged to #{gem_make_out}
EOF
raise ExtensionBuildError, message
end
end
end
# File rubygems/installer.rb, line 435
def check_that_user_bin_dir_is_in_path
user_bin_dir = @bin_dir || Gem.bindir(gem_home)
user_bin_dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
path = ENV['PATH']
if Gem.win_platform? then
path = path.downcase
user_bin_dir = user_bin_dir.downcase
end
unless path.split(File::PATH_SEPARATOR).include? user_bin_dir then
unless self.class.path_warning then
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
end
end
Return the target directory where the gem is to be installed. This directory is not guaranteed to be populated.
# File rubygems/installer.rb, line 622
def dir
gem_dir.to_s
end
# File rubygems/installer.rb, line 403
def ensure_dependencies_met
deps = spec.runtime_dependencies
deps |= spec.development_dependencies if @development
deps.each do |dep_gem|
ensure_dependency spec, dep_gem
end
end
Ensure that the dependency is satisfied by the current installation of gem. If it is not an exception is raised.
# File rubygems/installer.rb, line 230
def ensure_dependency(spec, dependency)
unless installation_satisfies_dependency? dependency then
raise Gem::InstallError, "#{spec.name} requires #{dependency}"
end
true
end
# File rubygems/installer.rb, line 385
def ensure_required_ruby_version_met
if rrv = spec.required_ruby_version then
unless rrv.satisfied_by? Gem.ruby_version then
raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
end
end
end
# File rubygems/installer.rb, line 393
def ensure_required_rubygems_version_met
if rrgv = spec.required_rubygems_version then
unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then
raise Gem::InstallError,
"#{spec.name} requires RubyGems version #{rrgv}. " +
"Try 'gem update --system' to update RubyGems itself."
end
end
end
Reads the file index and extracts each file into the gem directory.
Ensures that files can’t be installed outside the gem directory.
# File rubygems/installer.rb, line 572
def extract_files
raise ArgumentError, "format required to extract from" if @format.nil?
@format.file_entries.each do |entry, file_data|
path = entry['path'].untaint
if path.start_with? "/" then # for extra sanity
raise Gem::InstallError, "attempt to install file into #{entry['path']}"
end
path = File.expand_path File.join(gem_dir, path)
unless path.start_with? gem_dir then
msg = "attempt to install file into %p under %s" %
[entry['path'], gem_dir]
raise Gem::InstallError, msg
end
FileUtils.rm_rf(path) if File.exist? path
dir = File.dirname path
FileUtils.mkdir_p dir unless File.exist? dir
File.open(path, "wb") do |out|
out.write file_data
end
FileUtils.chmod entry['mode'], path
say path if Gem.configuration.really_verbose
end
end
Lazy accessor for the installer’s Gem::Format instance.
# File rubygems/installer.rb, line 117
def format
begin
@format ||= Gem::Format.from_file_by_path gem, @security_policy
rescue Gem::Package::FormatError
raise Gem::InstallError, "invalid gem format for #{gem}"
end
end
Prefix and suffix the program filename the same as ruby.
# File rubygems/installer.rb, line 608
def formatted_program_filename(filename)
if @format_executable then
self.class.exec_format % File.basename(filename)
else
filename
end
end
Lazy accessor for the spec’s gem directory.
# File rubygems/installer.rb, line 110
def gem_dir
@gem_dir ||= spec.gem_dir.dup.untaint
end
# File rubygems/installer.rb, line 280
def generate_bin
return if spec.executables.nil? or spec.executables.empty?
# 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.
bindir = @bin_dir || Gem.bindir(gem_home)
Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
spec.executables.each do |filename|
filename.untaint
bin_path = File.expand_path File.join(gem_dir, spec.bindir, filename)
unless File.exist? bin_path
warn "Hey?!?! Where did #{bin_path} go??"
next
end
mode = File.stat(bin_path).mode | 0111
FileUtils.chmod mode, bin_path
if @wrappers then
generate_bin_script filename, bindir
else
generate_bin_symlink filename, bindir
end
end
end
Creates the scripts to run the applications in the gem.
# File rubygems/installer.rb, line 318
def generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename)
FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
File.open bin_script_path, 'wb', 0755 do |file|
file.print app_script_text(filename)
end
say bin_script_path if Gem.configuration.really_verbose
generate_windows_script filename, bindir
end
Creates the symlinks to run the applications in the gem. Moves the symlink if the gem being installed has a newer version.
# File rubygems/installer.rb, line 336
def generate_bin_symlink(filename, bindir)
if Gem.win_platform? then
alert_warning "Unable to use symlinks on Windows, installing wrapper"
generate_bin_script filename, bindir
return
end
src = File.join gem_dir, spec.bindir, filename
dst = File.join bindir, formatted_program_filename(filename)
if File.exist? dst then
if File.symlink? dst then
link = File.readlink(dst).split File::SEPARATOR
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
end
Creates windows .bat files for easy running of commands
# File rubygems/installer.rb, line 268
def generate_windows_script(filename, bindir)
if Gem.win_platform? then
script_name = filename + ".bat"
script_path = File.join bindir, File.basename(script_name)
File.open script_path, 'w' do |file|
file.puts windows_stub_script(bindir, filename)
end
say script_path if Gem.configuration.really_verbose
end
end
Installs the gem and returns a loaded Gem::Specification for the installed gem.
The gem will be installed with the following structure:
@gem_home/ cache/<gem-version>.gem #=> a cached copy of the installed gem gems/<gem-version>/... #=> extracted files specifications/<gem-version>.gemspec #=> the Gem::Specification
# File rubygems/installer.rb, line 143
def install
current_home = Gem.dir
current_path = Gem.paths.path
verify_gem_home(options[:unpack])
Gem.use_paths gem_home, current_path # HACK: shouldn't need Gem.paths.path
# If we're forcing the install then disable security unless the security
# policy says that we only install signed gems.
@security_policy = nil if @force and @security_policy and
not @security_policy.only_signed
unless @force
ensure_required_ruby_version_met
ensure_required_rubygems_version_met
ensure_dependencies_met unless @ignore_dependencies
end
Gem.pre_install_hooks.each do |hook|
result = hook.call self
if result == false then
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
message = "pre-install hook#{location} failed for #{spec.full_name}"
raise Gem::InstallError, message
end
end
Gem.ensure_gem_subdirectories gem_home
# Completely remove any previous gem files
FileUtils.rm_rf(gem_dir) if File.exist? gem_dir
FileUtils.mkdir_p gem_dir
extract_files
build_extensions
Gem.post_build_hooks.each do |hook|
result = hook.call self
if result == false then
FileUtils.rm_rf gem_dir
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
message = "post-build hook#{location} failed for #{spec.full_name}"
raise Gem::InstallError, message
end
end
generate_bin
write_spec
write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE
cache_file = spec.cache_file
FileUtils.cp gem, cache_file unless File.exist? cache_file
say spec.post_install_message unless spec.post_install_message.nil?
spec.loaded_from = spec.spec_file
Gem::Specification.add_spec spec unless Gem::Specification.include? spec
Gem.post_install_hooks.each do |hook|
hook.call self
end
return spec
rescue Zlib::GzipFile::Error
raise Gem::InstallError, "gzip error installing #{gem}"
ensure
# conditional since we might be here because we're erroring out early.
if current_path
Gem.use_paths current_home, current_path
end
end
True if the gems in the source_index satisfy dependency.
# File rubygems/installer.rb, line 240
def installation_satisfies_dependency?(dependency)
not dependency.matching_specs.empty?
end
# File rubygems/installer.rb, line 412
def process_options
@options = {
:bin_dir => nil,
:env_shebang => false,
:exec_format => false,
:force => false,
:install_dir => Gem.dir,
}.merge options
@env_shebang = options[:env_shebang]
@force = options[:force]
@gem_home = options[:install_dir]
@ignore_dependencies = options[:ignore_dependencies]
@format_executable = options[:format_executable]
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
@development = options[:development]
raise "NOTE: Installer option :source_index is dead" if
options[:source_index]
end
Generates a #! line for bin_file_name‘s wrapper copying
arguments if necessary.
# File rubygems/installer.rb, line 362
def shebang(bin_file_name)
ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang
path = spec.bin_file bin_file_name
first_line = File.open(path, "rb") {|file| file.gets}
if /\A#!/ =~ first_line then
# Preserve extra words on shebang line, like "-w". Thanks RPA.
shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}")
opts = $1
shebang.strip! # Avoid nasty ^M issues.
end
if not ruby_name then
"#!#{Gem.ruby}#{opts}"
elsif opts then
"#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
else
# Create a plain shebang line.
@env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
"#!#{@env_path} #{ruby_name}"
end
end
Lazy accessor for the installer’s spec.
# File rubygems/installer.rb, line 128
def spec
@spec ||= format.spec
end
Unpacks the gem into the given directory.
# File rubygems/installer.rb, line 247
def unpack(directory)
@gem_dir = directory
@format = Gem::Format.from_file_by_path gem, @security_policy
extract_files
end
# File rubygems/installer.rb, line 452
def verify_gem_home(unpack = false)
FileUtils.mkdir_p gem_home
raise Gem::FilePermissionError, gem_home unless
unpack or File.writable?(gem_home)
end
return the stub script text used to launch the true ruby script
# File rubygems/installer.rb, line 492
def windows_stub_script(bindir, bin_file_name)
ruby = File.basename(Gem.ruby).chomp('"')
return <<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
@"#{ruby}" "%~dpn0" %*
TEXT
end
Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.
If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.
If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.
If you want to help improve the Ruby documentation, please visit Documenting-ruby.org.