Class | Gem::Installer |
In: |
lib/rubygems/installer.rb
|
Parent: | Object |
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.
bin_dir | [R] | The directory a gem‘s executables will be installed into |
exec_format | [W] | |
gem_home | [R] | The gem repository the gem will be installed into |
home_install_warning | [RW] | True if we‘ve warned about ~/.gems install |
path_warning | [RW] | True if we‘ve warned about PATH not including Gem.bindir |
spec | [R] | The Gem::Specification for the gem being installed |
Defaults to use Ruby‘s program prefix and suffix.
# File lib/rubygems/installer.rb, line 73 73: def exec_format 74: @exec_format ||= Gem.default_exec_format 75: 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 94 94: def initialize(gem, options={}) 95: @gem = gem 96: 97: options = { 98: :bin_dir => nil, 99: :env_shebang => false, 100: :exec_format => false, 101: :force => false, 102: :install_dir => Gem.dir, 103: :source_index => Gem.source_index, 104: }.merge options 105: 106: @env_shebang = options[:env_shebang] 107: @force = options[:force] 108: gem_home = options[:install_dir] 109: @gem_home = Pathname.new(gem_home).expand_path 110: @ignore_dependencies = options[:ignore_dependencies] 111: @format_executable = options[:format_executable] 112: @security_policy = options[:security_policy] 113: @wrappers = options[:wrappers] 114: @bin_dir = options[:bin_dir] 115: @development = options[:development] 116: @source_index = options[:source_index] 117: 118: begin 119: @format = Gem::Format.from_file_by_path @gem, @security_policy 120: rescue Gem::Package::FormatError 121: raise Gem::InstallError, "invalid gem format for #{@gem}" 122: end 123: 124: begin 125: FileUtils.mkdir_p @gem_home 126: rescue Errno::EACCES, Errno::ENOTDIR 127: # We'll divert to ~/.gems below 128: end 129: 130: if not File.writable? @gem_home or 131: # TODO: Shouldn't have to test for existence of bindir; tests need it. 132: (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and 133: not File.writable? Gem.bindir) then 134: if options[:user_install] == false then # You don't want to use ~ 135: raise Gem::FilePermissionError, @gem_home 136: elsif options[:user_install].nil? then 137: unless self.class.home_install_warning or options[:unpack] then 138: alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t #{Gem.bindir} aren't both writable." 139: self.class.home_install_warning = true 140: end 141: end 142: options[:user_install] = true 143: end 144: 145: if options[:user_install] and not options[:unpack] then 146: @gem_home = Gem.user_dir 147: 148: user_bin_dir = File.join(@gem_home, 'bin') 149: unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then 150: unless self.class.path_warning then 151: alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." 152: self.class.path_warning = true 153: end 154: end 155: 156: FileUtils.mkdir_p @gem_home unless File.directory? @gem_home 157: # If it's still not writable, you've got issues. 158: raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home 159: end 160: 161: @spec = @format.spec 162: 163: @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint 164: end
Return the text for an application file.
# File lib/rubygems/installer.rb, line 418 418: def app_script_text(bin_file_name) 419: "\#{shebang bin_file_name}\n#\n# This file was generated by RubyGems.\n#\n# The application '\#{@spec.name}' is installed as part of a gem, and\n# this file is here to facilitate running it.\n#\n\nrequire 'rubygems'\n\nversion = \"\#{Gem::Requirement.default}\"\n\nif ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then\nversion = $1\nARGV.shift\nend\n\ngem '\#{@spec.name}', version\nload Gem.bin_path('\#{@spec.name}', '\#{bin_file_name}', version)\n" 420: 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 462 462: def build_extensions 463: return if @spec.extensions.empty? 464: say "Building native extensions. This could take a while..." 465: start_dir = Dir.pwd 466: dest_path = File.join @gem_dir, @spec.require_paths.first 467: ran_rake = false # only run rake once 468: 469: @spec.extensions.each do |extension| 470: break if ran_rake 471: results = [] 472: 473: builder = case extension 474: when /extconf/ then 475: Gem::Ext::ExtConfBuilder 476: when /configure/ then 477: Gem::Ext::ConfigureBuilder 478: when /rakefile/i, /mkrf_conf/i then 479: ran_rake = true 480: Gem::Ext::RakeBuilder 481: else 482: results = ["No builder for extension '#{extension}'"] 483: nil 484: end 485: 486: begin 487: Dir.chdir File.join(@gem_dir, File.dirname(extension)) 488: results = builder.build(extension, @gem_dir, dest_path, results) 489: 490: say results.join("\n") if Gem.configuration.really_verbose 491: 492: rescue => ex 493: results = results.join "\n" 494: 495: File.open('gem_make.out', 'wb') { |f| f.puts results } 496: 497: message = "ERROR: Failed to build gem native extension.\n\n\#{results}\n\nGem files will remain installed in \#{@gem_dir} for inspection.\nResults logged to \#{File.join(Dir.pwd, 'gem_make.out')}\n" 498: 499: raise ExtensionBuildError, message 500: ensure 501: Dir.chdir start_dir 502: end 503: end 504: 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 |
# File lib/rubygems/installer.rb, line 253 253: def ensure_dependency(spec, dependency) 254: unless installation_satisfies_dependency? dependency then 255: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 256: end 257: 258: true 259: 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 519 519: def extract_files 520: expand_and_validate_gem_dir 521: 522: raise ArgumentError, "format required to extract from" if @format.nil? 523: 524: @format.file_entries.each do |entry, file_data| 525: path = entry['path'].untaint 526: 527: if path =~ /\A\// then # for extra sanity 528: raise Gem::InstallError, 529: "attempt to install file into #{entry['path'].inspect}" 530: end 531: 532: path = File.expand_path File.join(@gem_dir, path) 533: 534: if path !~ /\A#{Regexp.escape @gem_dir}/ then 535: msg = "attempt to install file into %p under %p" % 536: [entry['path'], @gem_dir] 537: raise Gem::InstallError, msg 538: end 539: 540: FileUtils.rm_rf(path) if File.exists?(path) 541: FileUtils.mkdir_p File.dirname(path) 542: 543: File.open(path, "wb") do |out| 544: out.write file_data 545: end 546: 547: FileUtils.chmod entry['mode'], path 548: 549: say path if Gem.configuration.really_verbose 550: end 551: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 556 556: def formatted_program_filename(filename) 557: if @format_executable then 558: self.class.exec_format % File.basename(filename) 559: else 560: filename 561: end 562: end
# File lib/rubygems/installer.rb, line 309 309: def generate_bin 310: return if @spec.executables.nil? or @spec.executables.empty? 311: 312: # If the user has asked for the gem to be installed in a directory that is 313: # the system gem directory, then use the system bin directory, else create 314: # (or use) a new bin dir under the gem_home. 315: bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home) 316: 317: Dir.mkdir bindir unless File.exist? bindir 318: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 319: 320: @spec.executables.each do |filename| 321: filename.untaint 322: bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename) 323: mode = File.stat(bin_path).mode | 0111 324: File.chmod mode, bin_path 325: 326: if @wrappers then 327: generate_bin_script filename, bindir 328: else 329: generate_bin_symlink filename, bindir 330: end 331: end 332: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 341 341: def generate_bin_script(filename, bindir) 342: bin_script_path = File.join bindir, formatted_program_filename(filename) 343: 344: exec_path = File.join @gem_dir, @spec.bindir, filename 345: 346: # HACK some gems don't have #! in their executables, restore 2008/06 347: #if File.read(exec_path, 2) == '#!' then 348: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 349: 350: File.open bin_script_path, 'w', 0755 do |file| 351: file.print app_script_text(filename) 352: end 353: 354: say bin_script_path if Gem.configuration.really_verbose 355: 356: generate_windows_script bindir, filename 357: #else 358: # FileUtils.rm_f bin_script_path 359: # FileUtils.cp exec_path, bin_script_path, 360: # :verbose => Gem.configuration.really_verbose 361: #end 362: 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 368 368: def generate_bin_symlink(filename, bindir) 369: if Gem.win_platform? then 370: alert_warning "Unable to use symlinks on Windows, installing wrapper" 371: generate_bin_script filename, bindir 372: return 373: end 374: 375: src = File.join @gem_dir, 'bin', filename 376: dst = File.join bindir, formatted_program_filename(filename) 377: 378: if File.exist? dst then 379: if File.symlink? dst then 380: link = File.readlink(dst).split File::SEPARATOR 381: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 382: return if @spec.version < cur_version 383: end 384: File.unlink dst 385: end 386: 387: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 388: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 297 297: def generate_windows_script(bindir, filename) 298: if Gem.win_platform? then 299: script_name = filename + ".bat" 300: script_path = File.join bindir, File.basename(script_name) 301: File.open script_path, 'w' do |file| 302: file.puts windows_stub_script(bindir, filename) 303: end 304: 305: say script_path if Gem.configuration.really_verbose 306: end 307: 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 177 177: def install 178: # If we're forcing the install then disable security unless the security 179: # policy says that we only install singed gems. 180: @security_policy = nil if @force and @security_policy and 181: not @security_policy.only_signed 182: 183: unless @force then 184: if rrv = @spec.required_ruby_version then 185: unless rrv.satisfied_by? Gem.ruby_version then 186: raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}" 187: end 188: end 189: 190: if rrgv = @spec.required_rubygems_version then 191: unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then 192: raise Gem::InstallError, 193: "#{@spec.name} requires RubyGems version #{rrgv}" 194: end 195: end 196: 197: unless @ignore_dependencies then 198: deps = @spec.runtime_dependencies 199: deps |= @spec.development_dependencies if @development 200: 201: deps.each do |dep_gem| 202: ensure_dependency @spec, dep_gem 203: end 204: end 205: end 206: 207: Gem.pre_install_hooks.each do |hook| 208: hook.call self 209: end 210: 211: FileUtils.mkdir_p @gem_home unless File.directory? @gem_home 212: 213: Gem.ensure_gem_subdirectories @gem_home 214: 215: FileUtils.mkdir_p @gem_dir 216: 217: extract_files 218: generate_bin 219: build_extensions 220: write_spec 221: 222: write_require_paths_file_if_needed 223: 224: # HACK remove? Isn't this done in multiple places? 225: cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop 226: unless File.exist? cached_gem then 227: FileUtils.cp @gem, File.join(@gem_home, "cache") 228: end 229: 230: say @spec.post_install_message unless @spec.post_install_message.nil? 231: 232: @spec.loaded_from = File.join(@gem_home, 'specifications', 233: "#{@spec.full_name}.gemspec") 234: 235: @source_index.add_spec @spec 236: 237: Gem.post_install_hooks.each do |hook| 238: hook.call self 239: end 240: 241: return @spec 242: rescue Zlib::GzipFile::Error 243: raise Gem::InstallError, "gzip error installing #{@gem}" 244: end
True if the gems in the source_index satisfy dependency.
# File lib/rubygems/installer.rb, line 264 264: def installation_satisfies_dependency?(dependency) 265: @source_index.find_name(dependency.name, dependency.version_requirements).size > 0 266: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 394 394: def shebang(bin_file_name) 395: if @env_shebang then 396: "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] 397: else 398: path = File.join @gem_dir, @spec.bindir, bin_file_name 399: 400: File.open(path, "rb") do |file| 401: first_line = file.gets 402: if first_line =~ /^#!/ then 403: # Preserve extra words on shebang line, like "-w". Thanks RPA. 404: shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}") 405: else 406: # Create a plain shebang line. 407: shebang = "#!#{Gem.ruby}" 408: end 409: 410: shebang.strip # Avoid nasty ^M issues. 411: end 412: end 413: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 271 271: def unpack(directory) 272: @gem_dir = directory 273: @format = Gem::Format.from_file_by_path @gem, @security_policy 274: extract_files 275: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 446 446: def windows_stub_script(bindir, bin_file_name) 447: "@ECHO OFF\nIF NOT \"%~f0\" == \"~f0\" GOTO :WinNT\n@\"\#{File.basename(Gem.ruby).chomp('\"')}\" \"\#{File.join(bindir, bin_file_name)}\" %1 %2 %3 %4 %5 %6 %7 %8 %9\nGOTO :EOF\n:WinNT\n@\"\#{File.basename(Gem.ruby).chomp('\"')}\" \"%~dpn0\" %*\n" 448: end
Writes the .gemspec specification (in Ruby) to the gem home‘s specifications directory.
# File lib/rubygems/installer.rb, line 281 281: def write_spec 282: rubycode = @spec.to_ruby 283: 284: file_name = File.join @gem_home, 'specifications', 285: "#{@spec.full_name}.gemspec" 286: 287: file_name.untaint 288: 289: File.open(file_name, "w") do |file| 290: file.puts rubycode 291: end 292: end