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 | [R] | |
gem_dir | [W] | 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 | [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 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: require 'fileutils' 96: 97: @gem = gem 98: @options = options 99: process_options 100: 101: if options[:user_install] and not options[:unpack] then 102: @gem_home = Gem.user_dir 103: check_that_user_bin_dir_is_in_path 104: end 105: end
Return the text for an application file.
# File lib/rubygems/installer.rb, line 462 462: def app_script_text(bin_file_name) 463: return "\#{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\nstr = ARGV.first\nstr = str.dup.force_encoding(\"BINARY\") if str.respond_to? :force_encoding\nif str =~ /\\\\A_(.*)_\\\\z/\nversion = $1\nARGV.shift\nend\nend\n\ngem '\#{spec.name}', version\nload Gem.bin_path('\#{spec.name}', '\#{bin_file_name}', version)\n" 464: 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 512 512: def build_extensions 513: return if spec.extensions.empty? 514: say "Building native extensions. This could take a while..." 515: dest_path = File.join gem_dir, spec.require_paths.first 516: ran_rake = false # only run rake once 517: 518: spec.extensions.each do |extension| 519: break if ran_rake 520: results = [] 521: 522: builder = case extension 523: when /extconf/ then 524: Gem::Ext::ExtConfBuilder 525: when /configure/ then 526: Gem::Ext::ConfigureBuilder 527: when /rakefile/i, /mkrf_conf/i then 528: ran_rake = true 529: Gem::Ext::RakeBuilder 530: else 531: results = ["No builder for extension '#{extension}'"] 532: nil 533: end 534: 535: 536: extension_dir = begin 537: File.join gem_dir, File.dirname(extension) 538: rescue TypeError # extension == nil 539: gem_dir 540: end 541: 542: 543: begin 544: Dir.chdir extension_dir do 545: results = builder.build(extension, gem_dir, dest_path, results) 546: 547: say results.join("\n") if Gem.configuration.really_verbose 548: end 549: rescue 550: results = results.join "\n" 551: 552: gem_make_out = File.join extension_dir, 'gem_make.out' 553: 554: open gem_make_out, 'wb' do |io| io.puts results end 555: 556: 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" 557: 558: raise ExtensionBuildError, message 559: end 560: end 561: end
# File lib/rubygems/installer.rb, line 437 437: def check_that_user_bin_dir_is_in_path 438: user_bin_dir = @bin_dir || Gem.bindir(gem_home) 439: user_bin_dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR 440: unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then 441: unless self.class.path_warning then 442: alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." 443: self.class.path_warning = true 444: end 445: end 446: end
Return the target directory where the gem is to be installed. This directory is not guaranteed to be populated.
# File lib/rubygems/installer.rb, line 626 626: def dir 627: gem_dir.to_s 628: end
# File lib/rubygems/installer.rb, line 405 405: def ensure_dependencies_met 406: deps = spec.runtime_dependencies 407: deps |= spec.development_dependencies if @development 408: 409: deps.each do |dep_gem| 410: ensure_dependency spec, dep_gem 411: end 412: 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 232 232: def ensure_dependency(spec, dependency) 233: unless installation_satisfies_dependency? dependency then 234: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 235: end 236: true 237: end
# File lib/rubygems/installer.rb, line 387 387: def ensure_required_ruby_version_met 388: if rrv = spec.required_ruby_version then 389: unless rrv.satisfied_by? Gem.ruby_version then 390: raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}." 391: end 392: end 393: end
# File lib/rubygems/installer.rb, line 395 395: def ensure_required_rubygems_version_met 396: if rrgv = spec.required_rubygems_version then 397: unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then 398: raise Gem::InstallError, 399: "#{spec.name} requires RubyGems version #{rrgv}. " + 400: "Try 'gem update --system' to update RubyGems itself." 401: end 402: end 403: 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 576 576: def extract_files 577: raise ArgumentError, "format required to extract from" if @format.nil? 578: 579: @format.file_entries.each do |entry, file_data| 580: path = entry['path'].untaint 581: 582: if path.start_with? "/" then # for extra sanity 583: raise Gem::InstallError, "attempt to install file into #{entry['path']}" 584: end 585: 586: path = File.expand_path File.join(gem_dir, path) 587: 588: unless path.start_with? gem_dir then 589: msg = "attempt to install file into %p under %s" % 590: [entry['path'], gem_dir] 591: raise Gem::InstallError, msg 592: end 593: 594: FileUtils.rm_rf(path) if File.exist? path 595: 596: dir = File.dirname path 597: FileUtils.mkdir_p dir unless File.exist? dir 598: 599: File.open(path, "wb") do |out| 600: out.write file_data 601: end 602: 603: FileUtils.chmod entry['mode'], path 604: 605: say path if Gem.configuration.really_verbose 606: end 607: end
Lazy accessor for the installer‘s Gem::Format instance.
# File lib/rubygems/installer.rb, line 117 117: def format 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: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 612 612: def formatted_program_filename(filename) 613: if @format_executable then 614: self.class.exec_format % File.basename(filename) 615: else 616: filename 617: end 618: end
# File lib/rubygems/installer.rb, line 282 282: def generate_bin 283: return if spec.executables.nil? or spec.executables.empty? 284: 285: # If the user has asked for the gem to be installed in a directory that is 286: # the system gem directory, then use the system bin directory, else create 287: # (or use) a new bin dir under the gem_home. 288: bindir = @bin_dir || Gem.bindir(gem_home) 289: 290: Dir.mkdir bindir unless File.exist? bindir 291: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 292: 293: spec.executables.each do |filename| 294: filename.untaint 295: bin_path = File.expand_path File.join(gem_dir, spec.bindir, filename) 296: 297: unless File.exist? bin_path 298: warn "Hey?!?! Where did #{bin_path} go??" 299: next 300: end 301: 302: mode = File.stat(bin_path).mode | 0111 303: FileUtils.chmod mode, bin_path 304: 305: if @wrappers then 306: generate_bin_script filename, bindir 307: else 308: generate_bin_symlink filename, bindir 309: end 310: end 311: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 320 320: def generate_bin_script(filename, bindir) 321: bin_script_path = File.join bindir, formatted_program_filename(filename) 322: 323: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 324: 325: File.open bin_script_path, 'wb', 0755 do |file| 326: file.print app_script_text(filename) 327: end 328: 329: say bin_script_path if Gem.configuration.really_verbose 330: 331: generate_windows_script filename, bindir 332: 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 338 338: def generate_bin_symlink(filename, bindir) 339: if Gem.win_platform? then 340: alert_warning "Unable to use symlinks on Windows, installing wrapper" 341: generate_bin_script filename, bindir 342: return 343: end 344: 345: src = File.join gem_dir, spec.bindir, filename 346: dst = File.join bindir, formatted_program_filename(filename) 347: 348: if File.exist? dst then 349: if File.symlink? dst then 350: link = File.readlink(dst).split File::SEPARATOR 351: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 352: return if spec.version < cur_version 353: end 354: File.unlink dst 355: end 356: 357: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 358: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 270 270: def generate_windows_script(filename, bindir) 271: if Gem.win_platform? then 272: script_name = filename + ".bat" 273: script_path = File.join bindir, File.basename(script_name) 274: File.open script_path, 'w' do |file| 275: file.puts windows_stub_script(bindir, filename) 276: end 277: 278: say script_path if Gem.configuration.really_verbose 279: end 280: 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 143 143: def install 144: current_home = Gem.dir 145: current_path = Gem.paths.path 146: 147: verify_spec_name 148: 149: verify_gem_home(options[:unpack]) 150: Gem.use_paths gem_home, current_path # HACK: shouldn't need Gem.paths.path 151: 152: # If we're forcing the install then disable security unless the security 153: # policy says that we only install signed gems. 154: @security_policy = nil if @force and @security_policy and 155: not @security_policy.only_signed 156: 157: unless @force 158: ensure_required_ruby_version_met 159: ensure_required_rubygems_version_met 160: ensure_dependencies_met unless @ignore_dependencies 161: end 162: 163: Gem.pre_install_hooks.each do |hook| 164: result = hook.call self 165: 166: if result == false then 167: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 168: 169: message = "pre-install hook#{location} failed for #{spec.full_name}" 170: raise Gem::InstallError, message 171: end 172: end 173: 174: Gem.ensure_gem_subdirectories gem_home 175: 176: # Completely remove any previous gem files 177: FileUtils.rm_rf(gem_dir) if File.exist? gem_dir 178: 179: FileUtils.mkdir_p gem_dir 180: 181: extract_files 182: build_extensions 183: 184: Gem.post_build_hooks.each do |hook| 185: result = hook.call self 186: 187: if result == false then 188: FileUtils.rm_rf gem_dir 189: 190: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 191: 192: message = "post-build hook#{location} failed for #{spec.full_name}" 193: raise Gem::InstallError, message 194: end 195: end 196: 197: generate_bin 198: write_spec 199: 200: write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE 201: 202: cache_file = spec.cache_file 203: FileUtils.cp gem, cache_file unless File.exist? cache_file 204: 205: say spec.post_install_message unless spec.post_install_message.nil? 206: 207: spec.loaded_from = spec.spec_file 208: 209: Gem::Specification.add_spec spec unless Gem::Specification.include? spec 210: 211: Gem.post_install_hooks.each do |hook| 212: hook.call self 213: end 214: 215: return spec 216: rescue Zlib::GzipFile::Error 217: raise Gem::InstallError, "gzip error installing #{gem}" 218: ensure 219: # conditional since we might be here because we're erroring out early. 220: if current_path 221: Gem.use_paths current_home, current_path 222: end 223: 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: not dependency.matching_specs.empty? 244: end
# File lib/rubygems/installer.rb, line 414 414: def process_options 415: @options = { 416: :bin_dir => nil, 417: :env_shebang => false, 418: :exec_format => false, 419: :force => false, 420: :install_dir => Gem.dir, 421: }.merge options 422: 423: @env_shebang = options[:env_shebang] 424: @force = options[:force] 425: @gem_home = options[:install_dir] 426: @ignore_dependencies = options[:ignore_dependencies] 427: @format_executable = options[:format_executable] 428: @security_policy = options[:security_policy] 429: @wrappers = options[:wrappers] 430: @bin_dir = options[:bin_dir] 431: @development = options[:development] 432: 433: raise "NOTE: Installer option :source_index is dead" if 434: options[:source_index] 435: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 364 364: def shebang(bin_file_name) 365: ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang 366: path = spec.bin_file bin_file_name 367: first_line = File.open(path, "rb") {|file| file.gets} 368: 369: if /\A#!/ =~ first_line then 370: # Preserve extra words on shebang line, like "-w". Thanks RPA. 371: shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}") 372: opts = $1 373: shebang.strip! # Avoid nasty ^M issues. 374: end 375: 376: if not ruby_name then 377: "#!#{Gem.ruby}#{opts}" 378: elsif opts then 379: "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" 380: else 381: # Create a plain shebang line. 382: @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } 383: "#!#{@env_path} #{ruby_name}" 384: end 385: 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
# File lib/rubygems/installer.rb, line 448 448: def verify_gem_home(unpack = false) 449: FileUtils.mkdir_p gem_home 450: raise Gem::FilePermissionError, gem_home unless 451: unpack or File.writable?(gem_home) 452: end
# File lib/rubygems/installer.rb, line 454 454: def verify_spec_name 455: return if spec.name =~ Gem::Specification::VALID_NAME_PATTERN 456: raise Gem::InstallError, "#{spec} has an invalid name" 457: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 494 494: def windows_stub_script(bindir, bin_file_name) 495: ruby = File.basename(Gem.ruby).chomp('"') 496: return "@ECHO OFF\nIF NOT \"%~f0\" == \"~f0\" GOTO :WinNT\n@\"\#{ruby}\" \"\#{File.join(bindir, bin_file_name)}\" %1 %2 %3 %4 %5 %6 %7 %8 %9\nGOTO :EOF\n:WinNT\n@\"\#{ruby}\" \"%~dpn0\" %*\n" 497: 498: end