diff options
Diffstat (limited to 'trunk/lib/rubygems/installer.rb')
-rw-r--r-- | trunk/lib/rubygems/installer.rb | 528 |
1 files changed, 0 insertions, 528 deletions
diff --git a/trunk/lib/rubygems/installer.rb b/trunk/lib/rubygems/installer.rb deleted file mode 100644 index 5e2e8e0f42..0000000000 --- a/trunk/lib/rubygems/installer.rb +++ /dev/null @@ -1,528 +0,0 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - -require 'fileutils' -require 'pathname' -require 'rbconfig' - -require 'rubygems/format' -require 'rubygems/ext' -require 'rubygems/require_paths_builder' - -## -# 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. -class Gem::Installer - - ## - # Raised when there is an error while building extensions. - # - class ExtensionBuildError < Gem::InstallError; end - - include Gem::UserInteraction - - include Gem::RequirePathsBuilder - - class << self - - 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={}) - @gem = gem - - options = { - :force => false, - :install_dir => Gem.dir, - :exec_format => false, - :env_shebang => false, - :bin_dir => nil - }.merge options - - @env_shebang = options[:env_shebang] - @force = options[:force] - gem_home = options[:install_dir] - @gem_home = Pathname.new(gem_home).expand_path - @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] - - 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 not File.writable? @gem_home or - # TODO: Shouldn't have to test for existence of bindir; tests need it. - (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and - not File.writable? Gem.bindir) - if options[:user_install] == false # You explicitly don't want to use ~ - raise Gem::FilePermissionError, @gem_home - elsif options[:user_install].nil? - say "Warning: falling back to user-level install since #{@gem_home} and #{@bin_dir} aren't both writable." - end - options[:user_install] = true - end - - if options[:user_install] - @gem_home = File.join(ENV['HOME'], '.gem') - - user_bin_dir = File.join(@gem_home, 'gems', 'bin') - if !ENV['PATH'].split(':').include?(user_bin_dir) - say "You don't have #{user_bin_dir} in your PATH." - say "You won't be able to run gem-installed executables until you add it." - end - - Dir.mkdir @gem_home if ! File.directory? @gem_home - # If it's still not writable, you've got issues. - raise Gem::FilePermissionError, @gem_home if ! File.writable? @gem_home - end - - @spec = @format.spec - - @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint - 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 - # 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::RubyGemsVersion) then - raise Gem::InstallError, - "#{@spec.name} requires RubyGems version #{rrgv}" - 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 - - 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.full_name}.gemspec") - - Gem.source_index.add_spec @spec - - return @spec - rescue Zlib::GzipFile::Error - raise Gem::InstallError, "gzip error installing #{@gem}" - 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 Gem.source_index satisfy +dependency+. - - def installation_satisfies_dependency?(dependency) - Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0 - 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 supplied - # spec_path. - # - # spec:: [Gem::Specification] The Gem specification to output - # spec_path:: [String] The location (path) to write the gemspec to - - def write_spec - rubycode = @spec.to_ruby - - file_name = File.join @gem_home, 'specifications', - "#{@spec.full_name}.gemspec" - - file_name.untaint - - File.open(file_name, "w") do |file| - file.puts rubycode - end - end - - ## - # Creates windows .bat files for easy running of commands - - def generate_windows_script(bindir, filename) - 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 ? @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) - 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 - - ## - # 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) - - 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 bindir, filename - #else - # FileUtils.rm_f bin_script_path - # FileUtils.cp exec_path, bin_script_path, - # :verbose => Gem.configuration.really_verbose - #end - 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, 'bin', 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) - if @env_shebang then - "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] - else - path = File.join @gem_dir, @spec.bindir, bin_file_name - - File.open(path, "rb") do |file| - first_line = file.gets - if first_line =~ /^#!/ then - # Preserve extra words on shebang line, like "-w". Thanks RPA. - shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}") - else - # Create a plain shebang line. - shebang = "#!#{Gem.ruby}" - end - - shebang.strip # Avoid nasty ^M issues. - end - end - end - - ## - # Return the text for an application file. - - 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 '#{bin_file_name}' -TEXT - end - - ## - # return the stub script text used to launch the true ruby script - - def windows_stub_script(bindir, bin_file_name) - <<-TEXT -@ECHO OFF -IF NOT "%~f0" == "~f0" GOTO :WinNT -@"#{File.basename(Gem.ruby)}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9 -GOTO :EOF -:WinNT -@"#{File.basename(Gem.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..." - 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 - - ## - # 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 - expand_and_validate_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.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 - - ## - # 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 - - private - - ## - # HACK Pathname is broken on windows. - - def absolute_path? pathname - pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i) - end - - def expand_and_validate_gem_dir - @gem_dir = Pathname.new(@gem_dir).expand_path - - unless absolute_path?(@gem_dir) then # HACK is this possible after #expand_path? - raise ArgumentError, "install directory %p not absolute" % @gem_dir - end - - @gem_dir = @gem_dir.to_s - end - -end - |