Class | Gem::Installer |
In: |
lib/rubygems/installer.rb
lib/rubygems/installer_test_case.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 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.
ENV_PATHS | = | %w[/usr/bin/env /bin/env] | Paths where env(1) might live. Some systems are broken and have it in /bin |
bin_dir | [R] | The directory a gem‘s executables will be installed into |
env_shebang | [W] | Available through requiring rubygems/installer_test_case |
exec_format | [W] | |
format | [W] | Available through requiring rubygems/installer_test_case |
format_executable | [W] | Available through requiring rubygems/installer_test_case |
gem_dir | [RW] | Available through requiring rubygems/installer_test_case |
gem_home | [R] | The gem repository the gem will be installed into |
gem_home | [W] | Available through requiring rubygems/installer_test_case |
ignore_dependencies | [W] | Available through requiring rubygems/installer_test_case |
options | [R] | The options passed when the Gem::Installer was instantiated. |
path_warning | [RW] | True if we‘ve warned about PATH not including Gem.bindir |
security_policy | [W] | Available through requiring rubygems/installer_test_case |
spec | [R] | The Gem::Specification for the gem being installed |
spec | [W] | Available through requiring rubygems/installer_test_case |
wrappers | [W] | Available through requiring rubygems/installer_test_case |
Defaults to use Ruby‘s program prefix and suffix.
# File lib/rubygems/installer.rb, line 76 76: def exec_format 77: @exec_format ||= Gem.default_exec_format 78: 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 97 97: def initialize(gem, options={}) 98: require 'fileutils' 99: 100: @gem = gem 101: @options = options 102: process_options 103: load_gem_file 104: 105: if options[:user_install] and not options[:unpack] then 106: @gem_home = Gem.user_dir 107: check_that_user_bin_dir_is_in_path 108: end 109: 110: verify_gem_home(options[:unpack]) 111: 112: @spec = @format.spec 113: 114: @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint 115: end
Return the text for an application file.
# File lib/rubygems/installer.rb, line 434 434: def app_script_text(bin_file_name) 435: "\#{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" 436: 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 478 478: def build_extensions 479: return if @spec.extensions.empty? 480: say "Building native extensions. This could take a while..." 481: dest_path = File.join @gem_dir, @spec.require_paths.first 482: ran_rake = false # only run rake once 483: 484: @spec.extensions.each do |extension| 485: break if ran_rake 486: results = [] 487: 488: builder = case extension 489: when /extconf/ then 490: Gem::Ext::ExtConfBuilder 491: when /configure/ then 492: Gem::Ext::ConfigureBuilder 493: when /rakefile/i, /mkrf_conf/i then 494: ran_rake = true 495: Gem::Ext::RakeBuilder 496: else 497: results = ["No builder for extension '#{extension}'"] 498: nil 499: end 500: 501: 502: extension_dir = begin 503: File.join @gem_dir, File.dirname(extension) 504: rescue TypeError # extension == nil 505: @gem_dir 506: end 507: 508: 509: begin 510: Dir.chdir extension_dir do 511: results = builder.build(extension, @gem_dir, dest_path, results) 512: 513: say results.join("\n") if Gem.configuration.really_verbose 514: end 515: rescue 516: results = results.join "\n" 517: 518: gem_make_out = File.join extension_dir, 'gem_make.out' 519: 520: open gem_make_out, 'wb' do |io| io.puts results end 521: 522: 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 \#{gem_make_out}\n" 523: 524: raise ExtensionBuildError, message 525: end 526: end 527: end
# File lib/rubygems/installer.rb, line 415 415: def check_that_user_bin_dir_is_in_path 416: user_bin_dir = File.join(@gem_home, 'bin') 417: unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then 418: unless self.class.path_warning then 419: alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." 420: self.class.path_warning = true 421: end 422: end 423: end
# File lib/rubygems/installer.rb, line 375 375: def ensure_dependencies_met 376: deps = @spec.runtime_dependencies 377: deps |= @spec.development_dependencies if @development 378: 379: deps.each do |dep_gem| 380: ensure_dependency @spec, dep_gem 381: end 382: 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 204 204: def ensure_dependency(spec, dependency) 205: unless installation_satisfies_dependency? dependency then 206: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 207: end 208: true 209: end
# File lib/rubygems/installer.rb, line 357 357: def ensure_required_ruby_version_met 358: if rrv = @spec.required_ruby_version then 359: unless rrv.satisfied_by? Gem.ruby_version then 360: raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}." 361: end 362: end 363: end
# File lib/rubygems/installer.rb, line 365 365: def ensure_required_rubygems_version_met 366: if rrgv = @spec.required_rubygems_version then 367: unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then 368: raise Gem::InstallError, 369: "#{@spec.name} requires RubyGems version #{rrgv}. " + 370: "Try 'gem update --system' to update RubyGems itself." 371: end 372: end 373: 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 542 542: def extract_files 543: @gem_dir = File.expand_path @gem_dir 544: 545: raise ArgumentError, "format required to extract from" if @format.nil? 546: 547: dirs = [] 548: 549: @format.file_entries.each do |entry, file_data| 550: path = entry['path'].untaint 551: 552: if path =~ /\A\// then # for extra sanity 553: raise Gem::InstallError, 554: "attempt to install file into #{entry['path'].inspect}" 555: end 556: 557: path = File.expand_path File.join(@gem_dir, path) 558: 559: if path !~ /\A#{Regexp.escape @gem_dir}/ then 560: msg = "attempt to install file into %p under %p" % 561: [entry['path'], @gem_dir] 562: raise Gem::InstallError, msg 563: end 564: 565: FileUtils.rm_rf(path) if File.exists?(path) 566: 567: dir = File.dirname(path) 568: if !dirs.include?(dir) 569: dirs << dir 570: FileUtils.mkdir_p dir 571: end 572: 573: File.open(path, "wb") do |out| 574: out.write file_data 575: end 576: 577: FileUtils.chmod entry['mode'], path 578: 579: say path if Gem.configuration.really_verbose 580: end 581: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 586 586: def formatted_program_filename(filename) 587: if @format_executable then 588: self.class.exec_format % File.basename(filename) 589: else 590: filename 591: end 592: end
# File lib/rubygems/installer.rb, line 258 258: def generate_bin 259: return if @spec.executables.nil? or @spec.executables.empty? 260: 261: # If the user has asked for the gem to be installed in a directory that is 262: # the system gem directory, then use the system bin directory, else create 263: # (or use) a new bin dir under the gem_home. 264: bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home) 265: 266: Dir.mkdir bindir unless File.exist? bindir 267: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 268: 269: @spec.executables.each do |filename| 270: filename.untaint 271: bin_path = File.expand_path "#{@spec.bindir}/#{filename}", @gem_dir 272: mode = File.stat(bin_path).mode | 0111 273: File.chmod mode, bin_path 274: 275: if @wrappers then 276: generate_bin_script filename, bindir 277: else 278: generate_bin_symlink filename, bindir 279: end 280: end 281: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 290 290: def generate_bin_script(filename, bindir) 291: bin_script_path = File.join bindir, formatted_program_filename(filename) 292: 293: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 294: 295: File.open bin_script_path, 'w', 0755 do |file| 296: file.print app_script_text(filename) 297: end 298: 299: say bin_script_path if Gem.configuration.really_verbose 300: 301: generate_windows_script filename, bindir 302: 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 308 308: def generate_bin_symlink(filename, bindir) 309: if Gem.win_platform? then 310: alert_warning "Unable to use symlinks on Windows, installing wrapper" 311: generate_bin_script filename, bindir 312: return 313: end 314: 315: src = File.join @gem_dir, 'bin', filename 316: dst = File.join bindir, formatted_program_filename(filename) 317: 318: if File.exist? dst then 319: if File.symlink? dst then 320: link = File.readlink(dst).split File::SEPARATOR 321: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 322: return if @spec.version < cur_version 323: end 324: File.unlink dst 325: end 326: 327: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 328: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 246 246: def generate_windows_script(filename, bindir) 247: if Gem.win_platform? then 248: script_name = filename + ".bat" 249: script_path = File.join bindir, File.basename(script_name) 250: File.open script_path, 'w' do |file| 251: file.puts windows_stub_script(bindir, filename) 252: end 253: 254: say script_path if Gem.configuration.really_verbose 255: end 256: 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 128 128: def install 129: # If we're forcing the install then disable security unless the security 130: # policy says that we only install signed gems. 131: @security_policy = nil if @force and @security_policy and 132: not @security_policy.only_signed 133: 134: unless @force 135: ensure_required_ruby_version_met 136: ensure_required_rubygems_version_met 137: ensure_dependencies_met unless @ignore_dependencies 138: end 139: 140: Gem.pre_install_hooks.each do |hook| 141: result = hook.call self 142: 143: if result == false then 144: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 145: 146: message = "pre-install hook#{location} failed for #{@spec.full_name}" 147: raise Gem::InstallError, message 148: end 149: end 150: 151: Gem.ensure_gem_subdirectories @gem_home 152: 153: FileUtils.mkdir_p @gem_dir 154: 155: extract_files 156: build_extensions 157: 158: Gem.post_build_hooks.each do |hook| 159: result = hook.call self 160: 161: if result == false then 162: FileUtils.rm_rf @gem_dir 163: 164: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 165: 166: message = "post-build hook#{location} failed for #{@spec.full_name}" 167: raise Gem::InstallError, message 168: end 169: end 170: 171: generate_bin 172: write_spec 173: 174: write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE 175: 176: # HACK remove? Isn't this done in multiple places? 177: cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop 178: unless File.exist? cached_gem then 179: FileUtils.cp @gem, File.join(@gem_home, "cache") 180: end 181: 182: say @spec.post_install_message unless @spec.post_install_message.nil? 183: 184: @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name) 185: 186: @source_index.add_spec @spec 187: 188: Gem.post_install_hooks.each do |hook| 189: hook.call self 190: end 191: 192: return @spec 193: rescue Zlib::GzipFile::Error 194: raise Gem::InstallError, "gzip error installing #{@gem}" 195: end
True if the gems in the source_index satisfy dependency.
# File lib/rubygems/installer.rb, line 214 214: def installation_satisfies_dependency?(dependency) 215: @source_index.find_name(dependency.name, dependency.requirement).size > 0 216: end
# File lib/rubygems/installer.rb, line 407 407: def load_gem_file 408: begin 409: @format = Gem::Format.from_file_by_path @gem, @security_policy 410: rescue Gem::Package::FormatError 411: raise Gem::InstallError, "invalid gem format for #{@gem}" 412: end 413: end
# File lib/rubygems/installer.rb, line 384 384: def process_options 385: @options = { 386: :bin_dir => nil, 387: :env_shebang => false, 388: :exec_format => false, 389: :force => false, 390: :install_dir => Gem.dir, 391: :source_index => Gem.source_index, 392: }.merge options 393: 394: @env_shebang = options[:env_shebang] 395: @force = options[:force] 396: gem_home = options[:install_dir] 397: @gem_home = File.expand_path(gem_home) 398: @ignore_dependencies = options[:ignore_dependencies] 399: @format_executable = options[:format_executable] 400: @security_policy = options[:security_policy] 401: @wrappers = options[:wrappers] 402: @bin_dir = options[:bin_dir] 403: @development = options[:development] 404: @source_index = options[:source_index] 405: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 334 334: def shebang(bin_file_name) 335: ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang 336: path = File.join @gem_dir, @spec.bindir, bin_file_name 337: first_line = File.open(path, "rb") {|file| file.gets} 338: 339: if /\A#!/ =~ first_line then 340: # Preserve extra words on shebang line, like "-w". Thanks RPA. 341: shebang = first_line.sub(/\A\#!.*?ruby\S*(?=(\s+\S+))/, "#!#{Gem.ruby}") 342: opts = $1 343: shebang.strip! # Avoid nasty ^M issues. 344: end 345: 346: if not ruby_name then 347: "#!#{Gem.ruby}#{opts}" 348: elsif opts then 349: "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" 350: else 351: # Create a plain shebang line. 352: @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } 353: "#!#{@env_path} #{ruby_name}" 354: end 355: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 221 221: def unpack(directory) 222: @gem_dir = directory 223: @format = Gem::Format.from_file_by_path @gem, @security_policy 224: extract_files 225: end
# File lib/rubygems/installer.rb, line 425 425: def verify_gem_home(unpack = false) 426: FileUtils.mkdir_p @gem_home 427: raise Gem::FilePermissionError, @gem_home unless 428: unpack or File.writable? @gem_home 429: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 462 462: def windows_stub_script(bindir, bin_file_name) 463: "@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" 464: end
Writes the .gemspec specification (in Ruby) to the gem home‘s specifications directory.
# File lib/rubygems/installer.rb, line 231 231: def write_spec 232: rubycode = @spec.to_ruby 233: 234: file_name = File.join @gem_home, 'specifications', @spec.spec_name 235: 236: file_name.untaint 237: 238: File.open(file_name, "w") do |file| 239: file.puts rubycode 240: end 241: end