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.
exec_format | [W] |
Defaults to use Ruby‘s program prefix and suffix.
# File lib/rubygems/installer.rb, line 39 39: def exec_format 40: @exec_format ||= Gem.default_exec_format 41: 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 59 59: def initialize(gem, options={}) 60: @gem = gem 61: 62: options = { 63: :force => false, 64: :install_dir => Gem.dir, 65: :exec_format => false, 66: :env_shebang => false, 67: :bin_dir => nil 68: }.merge options 69: 70: @env_shebang = options[:env_shebang] 71: @force = options[:force] 72: gem_home = options[:install_dir] 73: @gem_home = Pathname.new(gem_home).expand_path 74: @ignore_dependencies = options[:ignore_dependencies] 75: @format_executable = options[:format_executable] 76: @security_policy = options[:security_policy] 77: @wrappers = options[:wrappers] 78: @bin_dir = options[:bin_dir] 79: 80: begin 81: @format = Gem::Format.from_file_by_path @gem, @security_policy 82: rescue Gem::Package::FormatError 83: raise Gem::InstallError, "invalid gem format for #{@gem}" 84: end 85: 86: @spec = @format.spec 87: 88: @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint 89: end
Return the text for an application file.
# File lib/rubygems/installer.rb, line 328 328: def app_script_text(bin_file_name) 329: "\#{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" 330: 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 368 368: def build_extensions 369: return if @spec.extensions.empty? 370: say "Building native extensions. This could take a while..." 371: start_dir = Dir.pwd 372: dest_path = File.join @gem_dir, @spec.require_paths.first 373: ran_rake = false # only run rake once 374: 375: @spec.extensions.each do |extension| 376: break if ran_rake 377: results = [] 378: 379: builder = case extension 380: when /extconf/ then 381: Gem::Ext::ExtConfBuilder 382: when /configure/ then 383: Gem::Ext::ConfigureBuilder 384: when /rakefile/i, /mkrf_conf/i then 385: ran_rake = true 386: Gem::Ext::RakeBuilder 387: else 388: results = ["No builder for extension '#{extension}'"] 389: nil 390: end 391: 392: begin 393: Dir.chdir File.join(@gem_dir, File.dirname(extension)) 394: results = builder.build(extension, @gem_dir, dest_path, results) 395: 396: say results.join("\n") if Gem.configuration.really_verbose 397: 398: rescue => ex 399: results = results.join "\n" 400: 401: File.open('gem_make.out', 'wb') { |f| f.puts results } 402: 403: 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" 404: 405: raise ExtensionBuildError, message 406: ensure 407: Dir.chdir start_dir 408: end 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 164 164: def ensure_dependency(spec, dependency) 165: unless installation_satisfies_dependency? dependency then 166: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 167: end 168: 169: true 170: 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 424 424: def extract_files 425: expand_and_validate_gem_dir 426: 427: raise ArgumentError, "format required to extract from" if @format.nil? 428: 429: @format.file_entries.each do |entry, file_data| 430: path = entry['path'].untaint 431: 432: if path =~ /\A\// then # for extra sanity 433: raise Gem::InstallError, 434: "attempt to install file into #{entry['path'].inspect}" 435: end 436: 437: path = File.expand_path File.join(@gem_dir, path) 438: 439: if path !~ /\A#{Regexp.escape @gem_dir}/ then 440: msg = "attempt to install file into %p under %p" % 441: [entry['path'], @gem_dir] 442: raise Gem::InstallError, msg 443: end 444: 445: FileUtils.mkdir_p File.dirname(path) 446: 447: File.open(path, "wb") do |out| 448: out.write file_data 449: end 450: 451: say path if Gem.configuration.really_verbose 452: end 453: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 456 456: def formatted_program_filename(filename) 457: if @format_executable then 458: self.class.exec_format % File.basename(filename) 459: else 460: filename 461: end 462: end
# File lib/rubygems/installer.rb, line 224 224: def generate_bin 225: return if @spec.executables.nil? or @spec.executables.empty? 226: 227: # If the user has asked for the gem to be installed in a directory that is 228: # the system gem directory, then use the system bin directory, else create 229: # (or use) a new bin dir under the gem_home. 230: bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home) 231: 232: Dir.mkdir bindir unless File.exist? bindir 233: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 234: 235: @spec.executables.each do |filename| 236: filename.untaint 237: bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename) 238: mode = File.stat(bin_path).mode | 0111 239: File.chmod mode, bin_path 240: 241: if @wrappers then 242: generate_bin_script filename, bindir 243: else 244: generate_bin_symlink filename, bindir 245: end 246: end 247: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 256 256: def generate_bin_script(filename, bindir) 257: bin_script_path = File.join bindir, formatted_program_filename(filename) 258: 259: exec_path = File.join @gem_dir, @spec.bindir, filename 260: 261: # HACK some gems don't have #! in their executables, restore 2008/06 262: #if File.read(exec_path, 2) == '#!' then 263: File.open bin_script_path, 'w', 0755 do |file| 264: file.print app_script_text(filename) 265: end 266: 267: say bin_script_path if Gem.configuration.really_verbose 268: 269: generate_windows_script bindir, filename 270: #else 271: # FileUtils.rm_f bin_script_path 272: # FileUtils.cp exec_path, bin_script_path, 273: # :verbose => Gem.configuration.really_verbose 274: #end 275: 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 281 281: def generate_bin_symlink(filename, bindir) 282: if Gem.win_platform? then 283: alert_warning "Unable to use symlinks on Windows, installing wrapper" 284: generate_bin_script filename, bindir 285: return 286: end 287: 288: src = File.join @gem_dir, 'bin', filename 289: dst = File.join bindir, formatted_program_filename(filename) 290: 291: if File.exist? dst then 292: if File.symlink? dst then 293: link = File.readlink(dst).split File::SEPARATOR 294: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 295: return if @spec.version < cur_version 296: end 297: File.unlink dst 298: end 299: 300: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 301: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 212 212: def generate_windows_script(bindir, filename) 213: if Gem.win_platform? then 214: script_name = filename + ".bat" 215: script_path = File.join bindir, File.basename(script_name) 216: File.open script_path, 'w' do |file| 217: file.puts windows_stub_script(bindir, filename) 218: end 219: 220: say script_path if Gem.configuration.really_verbose 221: end 222: 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 101 101: def install 102: # If we're forcing the install then disable security unless the security 103: # policy says that we only install singed gems. 104: @security_policy = nil if @force and @security_policy and 105: not @security_policy.only_signed 106: 107: unless @force then 108: if rrv = @spec.required_ruby_version then 109: unless rrv.satisfied_by? Gem.ruby_version then 110: raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}" 111: end 112: end 113: 114: if rrgv = @spec.required_rubygems_version then 115: unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then 116: raise Gem::InstallError, 117: "#{@spec.name} requires RubyGems version #{rrgv}" 118: end 119: end 120: 121: unless @ignore_dependencies then 122: @spec.dependencies.each do |dep_gem| 123: ensure_dependency @spec, dep_gem 124: end 125: end 126: end 127: 128: FileUtils.mkdir_p @gem_home unless File.directory? @gem_home 129: raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home 130: 131: Gem.ensure_gem_subdirectories @gem_home 132: 133: FileUtils.mkdir_p @gem_dir 134: 135: extract_files 136: generate_bin 137: build_extensions 138: write_spec 139: 140: write_require_paths_file_if_needed 141: 142: # HACK remove? Isn't this done in multiple places? 143: cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop 144: unless File.exist? cached_gem then 145: FileUtils.cp @gem, File.join(@gem_home, "cache") 146: end 147: 148: say @spec.post_install_message unless @spec.post_install_message.nil? 149: 150: @spec.loaded_from = File.join(@gem_home, 'specifications', 151: "#{@spec.full_name}.gemspec") 152: 153: return @spec 154: rescue Zlib::GzipFile::Error 155: raise Gem::InstallError, "gzip error installing #{@gem}" 156: end
True if the current installed gems satisfy the given dependency.
dependency : | Gem::Dependency |
# File lib/rubygems/installer.rb, line 176 176: def installation_satisfies_dependency?(dependency) 177: current_index = Gem::SourceIndex.from_installed_gems 178: current_index.find_name(dependency.name, dependency.version_requirements).size > 0 179: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 306 306: def shebang(bin_file_name) 307: if @env_shebang then 308: "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] 309: else 310: path = File.join @gem_dir, @spec.bindir, bin_file_name 311: 312: File.open(path, "rb") do |file| 313: first_line = file.gets 314: if first_line =~ /^#!/ then 315: # Preserve extra words on shebang line, like "-w". Thanks RPA. 316: shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}") 317: else 318: # Create a plain shebang line. 319: shebang = "#!#{Gem.ruby}" 320: end 321: 322: shebang.strip # Avoid nasty ^M issues. 323: end 324: end 325: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 184 184: def unpack(directory) 185: @gem_dir = directory 186: @format = Gem::Format.from_file_by_path @gem, @security_policy 187: extract_files 188: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 354 354: def windows_stub_script(bindir, bin_file_name) 355: "@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" 356: 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 197 197: def write_spec 198: rubycode = @spec.to_ruby 199: 200: file_name = File.join @gem_home, 'specifications', 201: "#{@spec.full_name}.gemspec" 202: file_name.untaint 203: 204: File.open(file_name, "w") do |file| 205: file.puts rubycode 206: end 207: end