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 438 438: def app_script_text(bin_file_name) 439: "\#{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" 440: 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 482 482: def build_extensions 483: return if @spec.extensions.empty? 484: say "Building native extensions. This could take a while..." 485: dest_path = File.join @gem_dir, @spec.require_paths.first 486: ran_rake = false # only run rake once 487: 488: @spec.extensions.each do |extension| 489: break if ran_rake 490: results = [] 491: 492: builder = case extension 493: when /extconf/ then 494: Gem::Ext::ExtConfBuilder 495: when /configure/ then 496: Gem::Ext::ConfigureBuilder 497: when /rakefile/i, /mkrf_conf/i then 498: ran_rake = true 499: Gem::Ext::RakeBuilder 500: else 501: results = ["No builder for extension '#{extension}'"] 502: nil 503: end 504: 505: 506: extension_dir = begin 507: File.join @gem_dir, File.dirname(extension) 508: rescue TypeError # extension == nil 509: @gem_dir 510: end 511: 512: 513: begin 514: Dir.chdir extension_dir do 515: results = builder.build(extension, @gem_dir, dest_path, results) 516: 517: say results.join("\n") if Gem.configuration.really_verbose 518: end 519: rescue 520: results = results.join "\n" 521: 522: gem_make_out = File.join extension_dir, 'gem_make.out' 523: 524: open gem_make_out, 'wb' do |io| io.puts results end 525: 526: 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" 527: 528: raise ExtensionBuildError, message 529: end 530: end 531: end
# File lib/rubygems/installer.rb, line 419 419: def check_that_user_bin_dir_is_in_path 420: user_bin_dir = File.join(@gem_home, 'bin') 421: unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then 422: unless self.class.path_warning then 423: alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." 424: self.class.path_warning = true 425: end 426: end 427: end
# File lib/rubygems/installer.rb, line 379 379: def ensure_dependencies_met 380: deps = @spec.runtime_dependencies 381: deps |= @spec.development_dependencies if @development 382: 383: deps.each do |dep_gem| 384: ensure_dependency @spec, dep_gem 385: end 386: 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 206 206: def ensure_dependency(spec, dependency) 207: unless installation_satisfies_dependency? dependency then 208: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 209: end 210: true 211: end
# File lib/rubygems/installer.rb, line 361 361: def ensure_required_ruby_version_met 362: if rrv = @spec.required_ruby_version then 363: unless rrv.satisfied_by? Gem.ruby_version then 364: raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}." 365: end 366: end 367: end
# File lib/rubygems/installer.rb, line 369 369: def ensure_required_rubygems_version_met 370: if rrgv = @spec.required_rubygems_version then 371: unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then 372: raise Gem::InstallError, 373: "#{@spec.name} requires RubyGems version #{rrgv}. " + 374: "Try 'gem update --system' to update RubyGems itself." 375: end 376: end 377: 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 546 546: def extract_files 547: @gem_dir = File.expand_path @gem_dir 548: 549: raise ArgumentError, "format required to extract from" if @format.nil? 550: 551: dirs = [] 552: 553: @format.file_entries.each do |entry, file_data| 554: path = entry['path'].untaint 555: 556: if path =~ /\A\// then # for extra sanity 557: raise Gem::InstallError, 558: "attempt to install file into #{entry['path'].inspect}" 559: end 560: 561: path = File.expand_path File.join(@gem_dir, path) 562: 563: if path !~ /\A#{Regexp.escape @gem_dir}/ then 564: msg = "attempt to install file into %p under %p" % 565: [entry['path'], @gem_dir] 566: raise Gem::InstallError, msg 567: end 568: 569: FileUtils.rm_rf(path) if File.exists?(path) 570: 571: dir = File.dirname(path) 572: if !dirs.include?(dir) 573: dirs << dir 574: FileUtils.mkdir_p dir 575: end 576: 577: File.open(path, "wb") do |out| 578: out.write file_data 579: end 580: 581: FileUtils.chmod entry['mode'], path 582: 583: say path if Gem.configuration.really_verbose 584: end 585: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 590 590: def formatted_program_filename(filename) 591: if @format_executable then 592: self.class.exec_format % File.basename(filename) 593: else 594: filename 595: end 596: end
# File lib/rubygems/installer.rb, line 260 260: def generate_bin 261: return if @spec.executables.nil? or @spec.executables.empty? 262: 263: # If the user has asked for the gem to be installed in a directory that is 264: # the system gem directory, then use the system bin directory, else create 265: # (or use) a new bin dir under the gem_home. 266: bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home) 267: 268: Dir.mkdir bindir unless File.exist? bindir 269: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 270: 271: @spec.executables.each do |filename| 272: filename.untaint 273: bin_path = File.expand_path "#{@spec.bindir}/#{filename}", @gem_dir 274: if File.exist?(bin_path) 275: mode = File.stat(bin_path).mode | 0111 276: File.chmod mode, bin_path 277: end 278: 279: if @wrappers then 280: generate_bin_script filename, bindir 281: else 282: generate_bin_symlink filename, bindir 283: end 284: end 285: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 294 294: def generate_bin_script(filename, bindir) 295: bin_script_path = File.join bindir, formatted_program_filename(filename) 296: 297: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 298: 299: File.open bin_script_path, 'wb', 0755 do |file| 300: file.print app_script_text(filename) 301: end 302: 303: say bin_script_path if Gem.configuration.really_verbose 304: 305: generate_windows_script filename, bindir 306: 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 312 312: def generate_bin_symlink(filename, bindir) 313: if Gem.win_platform? then 314: alert_warning "Unable to use symlinks on Windows, installing wrapper" 315: generate_bin_script filename, bindir 316: return 317: end 318: 319: src = File.join @gem_dir, 'bin', filename 320: dst = File.join bindir, formatted_program_filename(filename) 321: 322: if File.exist? dst then 323: if File.symlink? dst then 324: link = File.readlink(dst).split File::SEPARATOR 325: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 326: return if @spec.version < cur_version 327: end 328: File.unlink dst 329: end 330: 331: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 332: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 248 248: def generate_windows_script(filename, bindir) 249: if Gem.win_platform? then 250: script_name = filename + ".bat" 251: script_path = File.join bindir, File.basename(script_name) 252: File.open script_path, 'w' do |file| 253: file.puts windows_stub_script(bindir, filename) 254: end 255: 256: say script_path if Gem.configuration.really_verbose 257: end 258: 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: # Completely remove any previous gem files 154: FileUtils.rm_rf(@gem_dir) if File.exist?(@gem_dir) 155: 156: FileUtils.mkdir_p @gem_dir 157: 158: extract_files 159: build_extensions 160: 161: Gem.post_build_hooks.each do |hook| 162: result = hook.call self 163: 164: if result == false then 165: FileUtils.rm_rf @gem_dir 166: 167: location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ 168: 169: message = "post-build hook#{location} failed for #{@spec.full_name}" 170: raise Gem::InstallError, message 171: end 172: end 173: 174: generate_bin 175: write_spec 176: 177: write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE 178: 179: cached_gem = Gem.cache_gem(File.basename(@gem), @gem_home) 180: unless File.exist? cached_gem then 181: FileUtils.cp @gem, Gem.cache_dir(@gem_home) 182: end 183: 184: say @spec.post_install_message unless @spec.post_install_message.nil? 185: 186: @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name) 187: 188: @source_index.add_spec @spec 189: 190: Gem.post_install_hooks.each do |hook| 191: hook.call self 192: end 193: 194: return @spec 195: rescue Zlib::GzipFile::Error 196: raise Gem::InstallError, "gzip error installing #{@gem}" 197: end
True if the gems in the source_index satisfy dependency.
# File lib/rubygems/installer.rb, line 216 216: def installation_satisfies_dependency?(dependency) 217: @source_index.find_name(dependency.name, dependency.requirement).size > 0 218: end
# File lib/rubygems/installer.rb, line 411 411: def load_gem_file 412: begin 413: @format = Gem::Format.from_file_by_path @gem, @security_policy 414: rescue Gem::Package::FormatError 415: raise Gem::InstallError, "invalid gem format for #{@gem}" 416: end 417: end
# File lib/rubygems/installer.rb, line 388 388: def process_options 389: @options = { 390: :bin_dir => nil, 391: :env_shebang => false, 392: :exec_format => false, 393: :force => false, 394: :install_dir => Gem.dir, 395: :source_index => Gem.source_index, 396: }.merge options 397: 398: @env_shebang = options[:env_shebang] 399: @force = options[:force] 400: gem_home = options[:install_dir] 401: @gem_home = File.expand_path(gem_home) 402: @ignore_dependencies = options[:ignore_dependencies] 403: @format_executable = options[:format_executable] 404: @security_policy = options[:security_policy] 405: @wrappers = options[:wrappers] 406: @bin_dir = options[:bin_dir] 407: @development = options[:development] 408: @source_index = options[:source_index] 409: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 338 338: def shebang(bin_file_name) 339: ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang 340: path = File.join @gem_dir, @spec.bindir, bin_file_name 341: first_line = File.open(path, "rb") {|file| file.gets} 342: 343: if /\A#!/ =~ first_line then 344: # Preserve extra words on shebang line, like "-w". Thanks RPA. 345: shebang = first_line.sub(/\A\#!.*?ruby\S*(?=(\s+\S+))/, "#!#{Gem.ruby}") 346: opts = $1 347: shebang.strip! # Avoid nasty ^M issues. 348: end 349: 350: if not ruby_name then 351: "#!#{Gem.ruby}#{opts}" 352: elsif opts then 353: "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" 354: else 355: # Create a plain shebang line. 356: @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } 357: "#!#{@env_path} #{ruby_name}" 358: end 359: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 223 223: def unpack(directory) 224: @gem_dir = directory 225: @format = Gem::Format.from_file_by_path @gem, @security_policy 226: extract_files 227: end
# File lib/rubygems/installer.rb, line 429 429: def verify_gem_home(unpack = false) 430: FileUtils.mkdir_p @gem_home 431: raise Gem::FilePermissionError, @gem_home unless 432: unpack or File.writable? @gem_home 433: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 466 466: def windows_stub_script(bindir, bin_file_name) 467: "@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" 468: end
Writes the .gemspec specification (in Ruby) to the gem home‘s specifications directory.
# File lib/rubygems/installer.rb, line 233 233: def write_spec 234: rubycode = @spec.to_ruby_for_cache 235: 236: file_name = File.join @gem_home, 'specifications', @spec.spec_name 237: 238: file_name.untaint 239: 240: File.open(file_name, "w") do |file| 241: file.puts rubycode 242: end 243: end