summaryrefslogtreecommitdiff
path: root/ruby_1_9_3/lib/rubygems/installer.rb
diff options
context:
space:
mode:
Diffstat (limited to 'ruby_1_9_3/lib/rubygems/installer.rb')
-rw-r--r--ruby_1_9_3/lib/rubygems/installer.rb626
1 files changed, 626 insertions, 0 deletions
diff --git a/ruby_1_9_3/lib/rubygems/installer.rb b/ruby_1_9_3/lib/rubygems/installer.rb
new file mode 100644
index 0000000000..514316f099
--- /dev/null
+++ b/ruby_1_9_3/lib/rubygems/installer.rb
@@ -0,0 +1,626 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/format'
+require 'rubygems/exceptions'
+require 'rubygems/ext'
+require 'rubygems/require_paths_builder'
+require 'rubygems/user_interaction'
+
+##
+# 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.
+
+class Gem::Installer
+
+ ##
+ # Paths where env(1) might live. Some systems are broken and have it in
+ # /bin
+
+ ENV_PATHS = %w[/usr/bin/env /bin/env]
+
+ ##
+ # Raised when there is an error while building extensions.
+ #
+ class ExtensionBuildError < Gem::InstallError; end
+
+ include Gem::UserInteraction
+
+ include Gem::RequirePathsBuilder if Gem::QUICKLOADER_SUCKAGE
+
+ attr_reader :gem
+
+ ##
+ # The directory a gem's executables will be installed into
+
+ attr_reader :bin_dir
+
+ ##
+ # The gem repository the gem will be installed into
+
+ attr_reader :gem_home
+
+ ##
+ # The options passed when the Gem::Installer was instantiated.
+
+ attr_reader :options
+
+ @path_warning = false
+
+ class << self
+
+ ##
+ # True if we've warned about PATH not including Gem.bindir
+
+ attr_accessor :path_warning
+
+ attr_writer :exec_format
+
+ # Defaults to use Ruby's program prefix and suffix.
+ def exec_format
+ @exec_format ||= Gem.default_exec_format
+ end
+
+ end
+
+ ##
+ # 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.
+
+ 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
+
+ ##
+ # Lazy accessor for the spec's gem directory.
+
+ def gem_dir
+ @gem_dir ||= spec.gem_dir.dup.untaint
+ end
+
+ ##
+ # Lazy accessor for the installer's Gem::Format instance.
+
+ 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
+
+ ##
+ # Lazy accessor for the installer's spec.
+
+ def spec
+ @spec ||= format.spec
+ 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
+
+ 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
+
+ ##
+ # 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
+
+ def ensure_dependency(spec, dependency)
+ unless installation_satisfies_dependency? dependency then
+ raise Gem::InstallError, "#{spec.name} requires #{dependency}"
+ end
+ true
+ end
+
+ ##
+ # True if the gems in the source_index satisfy +dependency+.
+
+ def installation_satisfies_dependency?(dependency)
+ not dependency.matching_specs.empty?
+ end
+
+ ##
+ # Unpacks the gem into the given directory.
+
+ def unpack(directory)
+ @gem_dir = directory
+ @format = Gem::Format.from_file_by_path gem, @security_policy
+ extract_files
+ end
+
+ ##
+ # Writes the .gemspec specification (in Ruby) to the gem home's
+ # specifications directory.
+
+ def write_spec
+ file_name = spec.spec_file.untaint
+
+ File.open(file_name, "w") do |file|
+ file.puts spec.to_ruby_for_cache
+ end
+ end
+
+ ##
+ # Creates windows .bat files for easy running of commands
+
+ 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
+
+ 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.
+ #--
+ # 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
+
+ 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.
+
+ 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
+
+ ##
+ # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
+ # necessary.
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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 text for an application file.
+
+ 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
+
+ ##
+ # return the stub script text used to launch the true ruby script
+
+ 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
+
+ ##
+ # Builds extensions. Valid types of extensions are extconf.rb files,
+ # configure scripts and rakefiles or mkrf_conf files.
+
+ 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
+
+ ##
+ # Reads the file index and extracts each file into the gem directory.
+ #
+ # Ensures that files can't be installed outside the gem directory.
+
+ 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
+
+ ##
+ # Prefix and suffix the program filename the same as ruby.
+
+ def formatted_program_filename(filename)
+ if @format_executable then
+ self.class.exec_format % File.basename(filename)
+ else
+ filename
+ end
+ end
+
+ ##
+ #
+ # Return the target directory where the gem is to be installed. This
+ # directory is not guaranteed to be populated.
+ #
+
+ def dir
+ gem_dir.to_s
+ end
+end
+