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