Extended maintenance of Ruby versions 1.8.7 and 1.9.2 ended on July 31, 2014. Read more

In Files

  • rubygems/installer.rb

Class/Module Index [+]

Quicksearch

Gem::Installer

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 fires 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.

Constants

ENV_PATHS

Paths where env(1) might live. Some systems are broken and have it in /bin

Attributes

exec_format[W]
path_warning[RW]

True if we’ve warned about PATH not including Gem.bindir

bin_dir[R]

The directory a gem’s executables will be installed into

gem_home[R]

The gem repository the gem will be installed into

spec[R]

The Gem::Specification for the gem being installed

Public Class Methods

new(gem, options={}) click to toggle source

Constructs an Installer instance that will install the gem located at gem. options is a Hash with the following keys:

:env_shebang

Use /usr/bin/env in bin wrappers.

:force

Overrides all version checks and security policy checks, except for a signed-gems-only policy.

:ignore_dependencies

Don’t raise if a dependency is missing.

:install_dir

The directory to install the gem into.

:format_executable

Format the executable the same as the ruby executable. If your ruby is ruby18, foo_exec will be installed as foo_exec18.

:security_policy

Use the specified security policy. See Gem::Security

:wrappers

Install wrappers if true, symlinks if false.

 
               # File rubygems/installer.rb, line 93
def initialize(gem, options={})
  @gem = gem

  options = {
    :bin_dir      => nil,
    :env_shebang  => false,
    :exec_format  => false,
    :force        => false,
    :install_dir  => Gem.dir,
    :source_index => Gem.source_index,
  }.merge options

  @env_shebang         = options[:env_shebang]
  @force               = options[:force]
  gem_home             = options[:install_dir]
  @gem_home            = File.expand_path(gem_home)
  @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]
  @source_index        = options[:source_index]

  begin
    @format = Gem::Format.from_file_by_path @gem, @security_policy
  rescue Gem::Package::FormatError
    raise Gem::InstallError, "invalid gem format for #{@gem}"
  end

  if options[:user_install] and not options[:unpack] then
    @gem_home = Gem.user_dir

    user_bin_dir = File.join(@gem_home, 'bin')
    unless ENV['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

  FileUtils.mkdir_p @gem_home
  raise Gem::FilePermissionError, @gem_home unless
    options[:unpack] or File.writable? @gem_home

  @spec = @format.spec

  @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
end
            

Public Instance Methods

app_script_text(bin_file_name) click to toggle source

Return the text for an application file.

 
               # File rubygems/installer.rb, line 397
  def app_script_text(bin_file_name)
    <<-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 =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
  version = $1
  ARGV.shift
end

gem '#{@spec.name}', version
load Gem.bin_path('#{@spec.name}', '#{bin_file_name}', version)
TEXT
  end
            
build_extensions() click to toggle source

Builds extensions. Valid types of extensions are extconf.rb files, configure scripts and rakefiles or mkrf_conf files.

 
               # File rubygems/installer.rb, line 439
  def build_extensions
    return if @spec.extensions.empty?
    say "Building native extensions.  This could take a while..."
    start_dir = Dir.pwd
    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

      begin
        Dir.chdir File.join(@gem_dir, File.dirname(extension))
        results = builder.build(extension, @gem_dir, dest_path, results)

        say results.join("\n") if Gem.configuration.really_verbose

      rescue => ex
        results = results.join "\n"

        File.open('gem_make.out', 'wb') { |f| f.puts results }

        message = <<-EOF
ERROR: Failed to build gem native extension.

#{results}

Gem files will remain installed in #{@gem_dir} for inspection.
Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
        EOF

        raise ExtensionBuildError, message
      ensure
        Dir.chdir start_dir
      end
    end
  end
            
ensure_dependency(spec, dependency) click to toggle source

Ensure that the dependency is satisfied by the current installation of gem. If it is not an exception is raised.

spec

Gem::Specification

dependency

Gem::Dependency

 
               # File rubygems/installer.rb, line 231
def ensure_dependency(spec, dependency)
  unless installation_satisfies_dependency? dependency then
    raise Gem::InstallError, "#{spec.name} requires #{dependency}"
  end

  true
end
            
exec_format() click to toggle source

Defaults to use Ruby’s program prefix and suffix.

 
               # File rubygems/installer.rb, line 72
def exec_format
  @exec_format ||= Gem.default_exec_format
end
            
extract_files() click to toggle source

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 495
def extract_files
  @gem_dir = File.expand_path @gem_dir

  raise ArgumentError, "format required to extract from" if @format.nil?

  @format.file_entries.each do |entry, file_data|
    path = entry['path'].untaint

    if path =~ /\A\// then # for extra sanity
      raise Gem::InstallError,
            "attempt to install file into #{entry['path'].inspect}"
    end

    path = File.expand_path File.join(@gem_dir, path)

    if path !~ /\A#{Regexp.escape @gem_dir}/ then
      msg = "attempt to install file into %p under %p" %
              [entry['path'], @gem_dir]
      raise Gem::InstallError, msg
    end

    FileUtils.rm_rf(path) if File.exists?(path)
    FileUtils.mkdir_p File.dirname(path)

    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
            
formatted_program_filename(filename) click to toggle source

Prefix and suffix the program filename the same as ruby.

 
               # File rubygems/installer.rb, line 532
def formatted_program_filename(filename)
  if @format_executable then
    self.class.exec_format % File.basename(filename)
  else
    filename
  end
end
            
generate_bin() click to toggle source
 
               # File rubygems/installer.rb, line 286
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 ? @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 "#{@spec.bindir}/#{filename}", @gem_dir
    mode = File.stat(bin_path).mode | 0111
    File.chmod mode, bin_path

    if @wrappers then
      generate_bin_script filename, bindir
    else
      generate_bin_symlink filename, bindir
    end
  end
end
            
generate_bin_script(filename, bindir) click to toggle source

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)

  exec_path = File.join @gem_dir, @spec.bindir, filename

  # HACK some gems don't have #! in their executables, restore 2008/06
  #if File.read(exec_path, 2) == '#!' then
    FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers

    File.open bin_script_path, 'w', 0755 do |file|
      file.print app_script_text(filename)
    end

    say bin_script_path if Gem.configuration.really_verbose

    generate_windows_script filename, bindir
  #else
  #  FileUtils.rm_f bin_script_path
  #  FileUtils.cp exec_path, bin_script_path,
  #               :verbose => Gem.configuration.really_verbose
  #end
end
            
generate_windows_script(filename, bindir) click to toggle source

Creates windows .bat files for easy running of commands

 
               # File rubygems/installer.rb, line 274
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
            
install() click to toggle source

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 155
def install
  # If we're forcing the install then disable security unless the security
  # policy says that we only install singed gems.
  @security_policy = nil if @force and @security_policy and
                            not @security_policy.only_signed

  unless @force then
    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

    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

    unless @ignore_dependencies then
      deps = @spec.runtime_dependencies
      deps |= @spec.development_dependencies if @development

      deps.each do |dep_gem|
        ensure_dependency @spec, dep_gem
      end
    end
  end

  Gem.pre_install_hooks.each do |hook|
    hook.call self
  end

  FileUtils.mkdir_p @gem_home unless File.directory? @gem_home

  Gem.ensure_gem_subdirectories @gem_home

  FileUtils.mkdir_p @gem_dir

  extract_files
  generate_bin
  build_extensions
  write_spec

  write_require_paths_file_if_needed

  # HACK remove?  Isn't this done in multiple places?
  cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop
  unless File.exist? cached_gem then
    FileUtils.cp @gem, File.join(@gem_home, "cache")
  end

  say @spec.post_install_message unless @spec.post_install_message.nil?

  @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name)

  @source_index.add_spec @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}"
end
            
installation_satisfies_dependency?(dependency) click to toggle source

True if the gems in the source_index satisfy dependency.

 
               # File rubygems/installer.rb, line 242
def installation_satisfies_dependency?(dependency)
  @source_index.find_name(dependency.name, dependency.requirement).size > 0
end
            
shebang(bin_file_name) click to toggle source

Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.

 
               # File rubygems/installer.rb, line 371
def shebang(bin_file_name)
  ruby_name = Gem::ConfigMap[: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}

  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
            
unpack(directory) click to toggle source

Unpacks the gem into the given directory.

 
               # File rubygems/installer.rb, line 249
def unpack(directory)
  @gem_dir = directory
  @format = Gem::Format.from_file_by_path @gem, @security_policy
  extract_files
end
            
windows_stub_script(bindir, bin_file_name) click to toggle source

return the stub script text used to launch the true ruby script

 
               # File rubygems/installer.rb, line 424
  def windows_stub_script(bindir, bin_file_name)
    <<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@"#{File.basename(Gem.ruby).chomp('"')}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
TEXT
  end
            
write_spec() click to toggle source

Writes the .gemspec specification (in Ruby) to the gem home’s specifications directory.

 
               # File rubygems/installer.rb, line 259
def write_spec
  rubycode = @spec.to_ruby

  file_name = File.join @gem_home, 'specifications', @spec.spec_name

  file_name.untaint

  File.open(file_name, "w") do |file|
    file.puts rubycode
  end
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.

blog comments powered by Disqus