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.
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 lib/rubygems/installer.rb, line 72 72: def exec_format 73: @exec_format ||= Gem.default_exec_format 74: 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. |
# File lib/rubygems/installer.rb, line 93 93: def initialize(gem, options={}) 94: @gem = gem 95: 96: options = { 97: :bin_dir => nil, 98: :env_shebang => false, 99: :exec_format => false, 100: :force => false, 101: :install_dir => Gem.dir, 102: :source_index => Gem.source_index, 103: }.merge options 104: 105: @env_shebang = options[:env_shebang] 106: @force = options[:force] 107: gem_home = options[:install_dir] 108: @gem_home = File.expand_path(gem_home) 109: @ignore_dependencies = options[:ignore_dependencies] 110: @format_executable = options[:format_executable] 111: @security_policy = options[:security_policy] 112: @wrappers = options[:wrappers] 113: @bin_dir = options[:bin_dir] 114: @development = options[:development] 115: @source_index = options[:source_index] 116: 117: begin 118: @format = Gem::Format.from_file_by_path @gem, @security_policy 119: rescue Gem::Package::FormatError 120: raise Gem::InstallError, "invalid gem format for #{@gem}" 121: end 122: 123: if options[:user_install] and not options[:unpack] then 124: @gem_home = Gem.user_dir 125: 126: user_bin_dir = File.join(@gem_home, 'bin') 127: unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then 128: unless self.class.path_warning then 129: alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." 130: self.class.path_warning = true 131: end 132: end 133: end 134: 135: FileUtils.mkdir_p @gem_home 136: raise Gem::FilePermissionError, @gem_home unless 137: options[:unpack] or File.writable? @gem_home 138: 139: @spec = @format.spec 140: 141: @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint 142: end
Return the text for an application file.
# File lib/rubygems/installer.rb, line 397 397: def app_script_text(bin_file_name) 398: #{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.shiftendgem '#{@spec.name}', versionload Gem.bin_path('#{@spec.name}', '#{bin_file_name}', version) 399: end
Builds extensions. Valid types of extensions are extconf.rb files, configure scripts and rakefiles or mkrf_conf files.
# File lib/rubygems/installer.rb, line 439 439: def build_extensions 440: return if @spec.extensions.empty? 441: say "Building native extensions. This could take a while..." 442: start_dir = Dir.pwd 443: dest_path = File.join @gem_dir, @spec.require_paths.first 444: ran_rake = false # only run rake once 445: 446: @spec.extensions.each do |extension| 447: break if ran_rake 448: results = [] 449: 450: builder = case extension 451: when /extconf/ then 452: Gem::Ext::ExtConfBuilder 453: when /configure/ then 454: Gem::Ext::ConfigureBuilder 455: when /rakefile/, /mkrf_conf/ then 456: ran_rake = true 457: Gem::Ext::RakeBuilder 458: else 459: results = ["No builder for extension '#{extension}'"] 460: nil 461: end 462: 463: begin 464: Dir.chdir File.join(@gem_dir, File.dirname(extension)) 465: results = builder.build(extension, @gem_dir, dest_path, results) 466: 467: say results.join("\n") if Gem.configuration.really_verbose 468: 469: rescue => ex 470: results = results.join "\n" 471: 472: File.open('gem_make.out', 'wb') { |f| f.puts results } 473: 474: message = 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')} 475: 476: raise ExtensionBuildError, message 477: ensure 478: Dir.chdir start_dir 479: end 480: end 481: end
Ensure that the dependency is satisfied by the current installation of gem. If it is not an exception is raised.
spec | |
dependency |
# File lib/rubygems/installer.rb, line 231 231: def ensure_dependency(spec, dependency) 232: unless installation_satisfies_dependency? dependency then 233: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 234: end 235: 236: true 237: 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 lib/rubygems/installer.rb, line 495 495: def extract_files 496: @gem_dir = File.expand_path @gem_dir 497: 498: raise ArgumentError, "format required to extract from" if @format.nil? 499: 500: @format.file_entries.each do |entry, file_data| 501: path = entry['path'].untaint 502: 503: if path =~ /\A\// then # for extra sanity 504: raise Gem::InstallError, 505: "attempt to install file into #{entry['path'].inspect}" 506: end 507: 508: path = File.expand_path File.join(@gem_dir, path) 509: 510: if path !~ /\A#{Regexp.escape @gem_dir}/ then 511: msg = "attempt to install file into %p under %p" % 512: [entry['path'], @gem_dir] 513: raise Gem::InstallError, msg 514: end 515: 516: FileUtils.rm_rf(path) if File.exists?(path) 517: FileUtils.mkdir_p File.dirname(path) 518: 519: File.open(path, "wb") do |out| 520: out.write file_data 521: end 522: 523: FileUtils.chmod entry['mode'], path 524: 525: say path if Gem.configuration.really_verbose 526: end 527: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 532 532: def formatted_program_filename(filename) 533: if @format_executable then 534: self.class.exec_format % File.basename(filename) 535: else 536: filename 537: end 538: end
# File lib/rubygems/installer.rb, line 286 286: def generate_bin 287: return if @spec.executables.nil? or @spec.executables.empty? 288: 289: # If the user has asked for the gem to be installed in a directory that is 290: # the system gem directory, then use the system bin directory, else create 291: # (or use) a new bin dir under the gem_home. 292: bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home) 293: 294: Dir.mkdir bindir unless File.exist? bindir 295: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 296: 297: @spec.executables.each do |filename| 298: filename.untaint 299: bin_path = File.expand_path "#{@spec.bindir}/#{filename}", @gem_dir 300: mode = File.stat(bin_path).mode | 0111 301: File.chmod mode, bin_path 302: 303: if @wrappers then 304: generate_bin_script filename, bindir 305: else 306: generate_bin_symlink filename, bindir 307: end 308: end 309: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 318 318: def generate_bin_script(filename, bindir) 319: bin_script_path = File.join bindir, formatted_program_filename(filename) 320: 321: exec_path = File.join @gem_dir, @spec.bindir, filename 322: 323: # HACK some gems don't have #! in their executables, restore 2008/06 324: #if File.read(exec_path, 2) == '#!' then 325: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 326: 327: File.open bin_script_path, 'w', 0755 do |file| 328: file.print app_script_text(filename) 329: end 330: 331: say bin_script_path if Gem.configuration.really_verbose 332: 333: generate_windows_script filename, bindir 334: #else 335: # FileUtils.rm_f bin_script_path 336: # FileUtils.cp exec_path, bin_script_path, 337: # :verbose => Gem.configuration.really_verbose 338: #end 339: end
Creates the symlinks to run the applications in the gem. Moves the symlink if the gem being installed has a newer version.
# File lib/rubygems/installer.rb, line 345 345: def generate_bin_symlink(filename, bindir) 346: if Gem.win_platform? then 347: alert_warning "Unable to use symlinks on Windows, installing wrapper" 348: generate_bin_script filename, bindir 349: return 350: end 351: 352: src = File.join @gem_dir, 'bin', filename 353: dst = File.join bindir, formatted_program_filename(filename) 354: 355: if File.exist? dst then 356: if File.symlink? dst then 357: link = File.readlink(dst).split File::SEPARATOR 358: cur_version = Gem::Version.create(link[3].sub(/^.*-/, '')) 359: return if @spec.version < cur_version 360: end 361: File.unlink dst 362: end 363: 364: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 365: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 274 274: def generate_windows_script(filename, bindir) 275: if Gem.win_platform? then 276: script_name = filename + ".bat" 277: script_path = File.join bindir, File.basename(script_name) 278: File.open script_path, 'w' do |file| 279: file.puts windows_stub_script(bindir, filename) 280: end 281: 282: say script_path if Gem.configuration.really_verbose 283: end 284: 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 lib/rubygems/installer.rb, line 155 155: def install 156: # If we're forcing the install then disable security unless the security 157: # policy says that we only install singed gems. 158: @security_policy = nil if @force and @security_policy and 159: not @security_policy.only_signed 160: 161: unless @force then 162: if rrv = @spec.required_ruby_version then 163: unless rrv.satisfied_by? Gem.ruby_version then 164: raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}." 165: end 166: end 167: 168: if rrgv = @spec.required_rubygems_version then 169: unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then 170: raise Gem::InstallError, 171: "#{@spec.name} requires RubyGems version #{rrgv}. " + 172: "Try 'gem update --system' to update RubyGems itself." 173: end 174: end 175: 176: unless @ignore_dependencies then 177: deps = @spec.runtime_dependencies 178: deps |= @spec.development_dependencies if @development 179: 180: deps.each do |dep_gem| 181: ensure_dependency @spec, dep_gem 182: end 183: end 184: end 185: 186: Gem.pre_install_hooks.each do |hook| 187: hook.call self 188: end 189: 190: FileUtils.mkdir_p @gem_home unless File.directory? @gem_home 191: 192: Gem.ensure_gem_subdirectories @gem_home 193: 194: FileUtils.mkdir_p @gem_dir 195: 196: extract_files 197: generate_bin 198: build_extensions 199: write_spec 200: 201: write_require_paths_file_if_needed 202: 203: # HACK remove? Isn't this done in multiple places? 204: cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop 205: unless File.exist? cached_gem then 206: FileUtils.cp @gem, File.join(@gem_home, "cache") 207: end 208: 209: say @spec.post_install_message unless @spec.post_install_message.nil? 210: 211: @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name) 212: 213: @source_index.add_spec @spec 214: 215: Gem.post_install_hooks.each do |hook| 216: hook.call self 217: end 218: 219: return @spec 220: rescue Zlib::GzipFile::Error 221: raise Gem::InstallError, "gzip error installing #{@gem}" 222: end
True if the gems in the source_index satisfy dependency.
# File lib/rubygems/installer.rb, line 242 242: def installation_satisfies_dependency?(dependency) 243: @source_index.find_name(dependency.name, dependency.requirement).size > 0 244: end
Generates a #! line for bin_file_name’s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 371 371: def shebang(bin_file_name) 372: ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang 373: path = File.join @gem_dir, @spec.bindir, bin_file_name 374: first_line = File.open(path, "rb") {|file| file.gets} 375: 376: if /\A#!/ =~ first_line then 377: # Preserve extra words on shebang line, like "-w". Thanks RPA. 378: shebang = first_line.sub(/\A\#!.*?ruby\S*(?=(\s+\S+))/, "#!#{Gem.ruby}") 379: opts = $1 380: shebang.strip! # Avoid nasty ^M issues. 381: end 382: 383: if not ruby_name then 384: "#!#{Gem.ruby}#{opts}" 385: elsif opts then 386: "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" 387: else 388: # Create a plain shebang line. 389: @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } 390: "#!#{@env_path} #{ruby_name}" 391: end 392: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 249 249: def unpack(directory) 250: @gem_dir = directory 251: @format = Gem::Format.from_file_by_path @gem, @security_policy 252: extract_files 253: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 424 424: def windows_stub_script(bindir, bin_file_name) 425: @ECHO OFFIF NOT "%~f0" == "~f0" GOTO :WinNT@"#{File.basename(Gem.ruby).chomp('"')}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9GOTO :EOF:WinNT@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %* 426: end
Writes the .gemspec specification (in Ruby) to the gem home’s specifications directory.
# File lib/rubygems/installer.rb, line 259 259: def write_spec 260: rubycode = @spec.to_ruby 261: 262: file_name = File.join @gem_home, 'specifications', @spec.spec_name 263: 264: file_name.untaint 265: 266: File.open(file_name, "w") do |file| 267: file.puts rubycode 268: end 269: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.