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 455 455: def app_script_text(bin_file_name) 456: 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" 457: 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 505 505: def build_extensions 506: return if spec.extensions.empty? 507: say "Building native extensions. This could take a while..." 508: dest_path = File.join gem_dir, spec.require_paths.first 509: ran_rake = false # only run rake once 510: 511: spec.extensions.each do |extension| 512: break if ran_rake 513: results = [] 514: 515: builder = case extension 516: when /extconf/ then 517: Gem::Ext::ExtConfBuilder 518: when /configure/ then 519: Gem::Ext::ConfigureBuilder 520: when /rakefile/i, /mkrf_conf/i then 521: ran_rake = true 522: Gem::Ext::RakeBuilder 523: else 524: results = ["No builder for extension '#{extension}'"] 525: nil 526: end 527: 528: 529: extension_dir = begin 530: File.join gem_dir, File.dirname(extension) 531: rescue TypeError # extension == nil 532: gem_dir 533: end 534: 535: 536: begin 537: Dir.chdir extension_dir do 538: results = builder.build(extension, gem_dir, dest_path, results) 539: 540: say results.join("\n") if Gem.configuration.really_verbose 541: end 542: rescue 543: results = results.join "\n" 544: 545: gem_make_out = File.join extension_dir, 'gem_make.out' 546: 547: open gem_make_out, 'wb' do |io| io.puts results end 548: 549: 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" 550: 551: raise ExtensionBuildError, message 552: end 553: end 554: end
# File lib/rubygems/installer.rb, line 435 435: def check_that_user_bin_dir_is_in_path 436: user_bin_dir = @bin_dir || Gem.bindir(gem_home) 437: user_bin_dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR 438: unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then 439: unless self.class.path_warning then 440: alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." 441: self.class.path_warning = true 442: end 443: end 444: 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 619 619: def dir 620: gem_dir.to_s 621: end
# File lib/rubygems/installer.rb, line 403 403: def ensure_dependencies_met 404: deps = spec.runtime_dependencies 405: deps |= spec.development_dependencies if @development 406: 407: deps.each do |dep_gem| 408: ensure_dependency spec, dep_gem 409: end 410: 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 230 230: def ensure_dependency(spec, dependency) 231: unless installation_satisfies_dependency? dependency then 232: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 233: end 234: true 235: end
# File lib/rubygems/installer.rb, line 385 385: def ensure_required_ruby_version_met 386: if rrv = spec.required_ruby_version then 387: unless rrv.satisfied_by? Gem.ruby_version then 388: raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}." 389: end 390: end 391: end
# File lib/rubygems/installer.rb, line 393 393: def ensure_required_rubygems_version_met 394: if rrgv = spec.required_rubygems_version then 395: unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then 396: raise Gem::InstallError, 397: "#{spec.name} requires RubyGems version #{rrgv}. " + 398: "Try 'gem update --system' to update RubyGems itself." 399: end 400: end 401: 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 569 569: def extract_files 570: raise ArgumentError, "format required to extract from" if @format.nil? 571: 572: @format.file_entries.each do |entry, file_data| 573: path = entry['path'].untaint 574: 575: if path.start_with? "/" then # for extra sanity 576: raise Gem::InstallError, "attempt to install file into #{entry['path']}" 577: end 578: 579: path = File.expand_path File.join(gem_dir, path) 580: 581: unless path.start_with? gem_dir then 582: msg = "attempt to install file into %p under %s" % 583: [entry['path'], gem_dir] 584: raise Gem::InstallError, msg 585: end 586: 587: FileUtils.rm_rf(path) if File.exist? path 588: 589: dir = File.dirname path 590: FileUtils.mkdir_p dir unless File.exist? dir 591: 592: File.open(path, "wb") do |out| 593: out.write file_data 594: end 595: 596: FileUtils.chmod entry['mode'], path 597: 598: say path if Gem.configuration.really_verbose 599: end 600: 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 605 605: def formatted_program_filename(filename) 606: if @format_executable then 607: self.class.exec_format % File.basename(filename) 608: else 609: filename 610: end 611: end
# File lib/rubygems/installer.rb, line 280 280: def generate_bin 281: return if spec.executables.nil? or spec.executables.empty? 282: 283: # If the user has asked for the gem to be installed in a directory that is 284: # the system gem directory, then use the system bin directory, else create 285: # (or use) a new bin dir under the gem_home. 286: bindir = @bin_dir || Gem.bindir(gem_home) 287: 288: Dir.mkdir bindir unless File.exist? bindir 289: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 290: 291: spec.executables.each do |filename| 292: filename.untaint 293: bin_path = File.expand_path File.join(gem_dir, spec.bindir, filename) 294: 295: unless File.exist? bin_path 296: warn "Hey?!?! Where did #{bin_path} go??" 297: next 298: end 299: 300: mode = File.stat(bin_path).mode | 0111 301: FileUtils.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: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 322: 323: File.open bin_script_path, 'wb', 0755 do |file| 324: file.print app_script_text(filename) 325: end 326: 327: say bin_script_path if Gem.configuration.really_verbose 328: 329: generate_windows_script filename, bindir 330: 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 336 336: def generate_bin_symlink(filename, bindir) 337: if Gem.win_platform? then 338: alert_warning "Unable to use symlinks on Windows, installing wrapper" 339: generate_bin_script filename, bindir 340: return 341: end 342: 343: src = File.join gem_dir, spec.bindir, filename 344: dst = File.join bindir, formatted_program_filename(filename) 345: 346: if File.exist? dst then 347: if File.symlink? dst then 348: link = File.readlink(dst).split File::SEPARATOR 349: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 350: return if spec.version < cur_version 351: end 352: File.unlink dst 353: end 354: 355: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 356: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 268 268: def generate_windows_script(filename, bindir) 269: if Gem.win_platform? then 270: script_name = filename + ".bat" 271: script_path = File.join bindir, File.basename(script_name) 272: File.open script_path, 'w' do |file| 273: file.puts windows_stub_script(bindir, filename) 274: end 275: 276: say script_path if Gem.configuration.really_verbose 277: end 278: 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_gem_home(options[:unpack]) 148: Gem.use_paths gem_home, current_path # HACK: shouldn't need Gem.paths.path 149: 150: # If we're forcing the install then disable security unless the security 151: # policy says that we only install signed gems. 152: @security_policy = nil if @force and @security_policy and 153: not @security_policy.only_signed 154: 155: unless @force 156: ensure_required_ruby_version_met 157: ensure_required_rubygems_version_met 158: ensure_dependencies_met unless @ignore_dependencies 159: end 160: 161: Gem.pre_install_hooks.each do |hook| 162: result = hook.call self 163: 164: if result == false then 165: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 166: 167: message = "pre-install hook#{location} failed for #{spec.full_name}" 168: raise Gem::InstallError, message 169: end 170: end 171: 172: Gem.ensure_gem_subdirectories gem_home 173: 174: # Completely remove any previous gem files 175: FileUtils.rm_rf(gem_dir) if File.exist? gem_dir 176: 177: FileUtils.mkdir_p gem_dir 178: 179: extract_files 180: build_extensions 181: 182: Gem.post_build_hooks.each do |hook| 183: result = hook.call self 184: 185: if result == false then 186: FileUtils.rm_rf gem_dir 187: 188: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 189: 190: message = "post-build hook#{location} failed for #{spec.full_name}" 191: raise Gem::InstallError, message 192: end 193: end 194: 195: generate_bin 196: write_spec 197: 198: write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE 199: 200: cache_file = spec.cache_file 201: FileUtils.cp gem, cache_file unless File.exist? cache_file 202: 203: say spec.post_install_message unless spec.post_install_message.nil? 204: 205: spec.loaded_from = spec.spec_file 206: 207: Gem::Specification.add_spec spec unless Gem::Specification.include? spec 208: 209: Gem.post_install_hooks.each do |hook| 210: hook.call self 211: end 212: 213: return spec 214: rescue Zlib::GzipFile::Error 215: raise Gem::InstallError, "gzip error installing #{gem}" 216: ensure 217: # conditional since we might be here because we're erroring out early. 218: if current_path 219: Gem.use_paths current_home, current_path 220: end 221: end
True if the gems in the source_index satisfy dependency.
# File lib/rubygems/installer.rb, line 240 240: def installation_satisfies_dependency?(dependency) 241: not dependency.matching_specs.empty? 242: end
# File lib/rubygems/installer.rb, line 412 412: def process_options 413: @options = { 414: :bin_dir => nil, 415: :env_shebang => false, 416: :exec_format => false, 417: :force => false, 418: :install_dir => Gem.dir, 419: }.merge options 420: 421: @env_shebang = options[:env_shebang] 422: @force = options[:force] 423: @gem_home = options[:install_dir] 424: @ignore_dependencies = options[:ignore_dependencies] 425: @format_executable = options[:format_executable] 426: @security_policy = options[:security_policy] 427: @wrappers = options[:wrappers] 428: @bin_dir = options[:bin_dir] 429: @development = options[:development] 430: 431: raise "NOTE: Installer option :source_index is dead" if 432: options[:source_index] 433: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 362 362: def shebang(bin_file_name) 363: ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang 364: path = spec.bin_file bin_file_name 365: first_line = File.open(path, "rb") {|file| file.gets} 366: 367: if /\A#!/ =~ first_line then 368: # Preserve extra words on shebang line, like "-w". Thanks RPA. 369: shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}") 370: opts = $1 371: shebang.strip! # Avoid nasty ^M issues. 372: end 373: 374: if not ruby_name then 375: "#!#{Gem.ruby}#{opts}" 376: elsif opts then 377: "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" 378: else 379: # Create a plain shebang line. 380: @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } 381: "#!#{@env_path} #{ruby_name}" 382: end 383: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 247 247: def unpack(directory) 248: @gem_dir = directory 249: @format = Gem::Format.from_file_by_path gem, @security_policy 250: extract_files 251: end
# File lib/rubygems/installer.rb, line 446 446: def verify_gem_home(unpack = false) 447: FileUtils.mkdir_p gem_home 448: raise Gem::FilePermissionError, gem_home unless 449: unpack or File.writable?(gem_home) 450: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 487 487: def windows_stub_script(bindir, bin_file_name) 488: ruby = File.basename(Gem.ruby).chomp('"') 489: 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" 490: 491: end