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 60 60: def initialize(gem, options={}) 61: @gem = gem 62: 63: options = { 64: :force => false, 65: :install_dir => Gem.dir, 66: :exec_format => false, 67: :env_shebang => false, 68: :bin_dir => nil 69: }.merge options 70: 71: @env_shebang = options[:env_shebang] 72: @force = options[:force] 73: gem_home = options[:install_dir] 74: @gem_home = Pathname.new(gem_home).expand_path 75: @ignore_dependencies = options[:ignore_dependencies] 76: @format_executable = options[:format_executable] 77: @security_policy = options[:security_policy] 78: @wrappers = options[:wrappers] 79: @bin_dir = options[:bin_dir] 80: @development = options[:development] 81: 82: begin 83: @format = Gem::Format.from_file_by_path @gem, @security_policy 84: rescue Gem::Package::FormatError 85: raise Gem::InstallError, "invalid gem format for #{@gem}" 86: end 87: 88: @spec = @format.spec 89: 90: @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint 91: end
Return the text for an application file.
# File lib/rubygems/installer.rb, line 340 340: def app_script_text(bin_file_name) 341: "\#{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" 342: 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 384 384: def build_extensions 385: return if @spec.extensions.empty? 386: say "Building native extensions. This could take a while..." 387: start_dir = Dir.pwd 388: dest_path = File.join @gem_dir, @spec.require_paths.first 389: ran_rake = false # only run rake once 390: 391: @spec.extensions.each do |extension| 392: break if ran_rake 393: results = [] 394: 395: builder = case extension 396: when /extconf/ then 397: Gem::Ext::ExtConfBuilder 398: when /configure/ then 399: Gem::Ext::ConfigureBuilder 400: when /rakefile/i, /mkrf_conf/i then 401: ran_rake = true 402: Gem::Ext::RakeBuilder 403: else 404: results = ["No builder for extension '#{extension}'"] 405: nil 406: end 407: 408: begin 409: Dir.chdir File.join(@gem_dir, File.dirname(extension)) 410: results = builder.build(extension, @gem_dir, dest_path, results) 411: 412: say results.join("\n") if Gem.configuration.really_verbose 413: 414: rescue => ex 415: results = results.join "\n" 416: 417: File.open('gem_make.out', 'wb') { |f| f.puts results } 418: 419: 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" 420: 421: raise ExtensionBuildError, message 422: ensure 423: Dir.chdir start_dir 424: end 425: end 426: 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 173 173: def ensure_dependency(spec, dependency) 174: unless installation_satisfies_dependency? dependency then 175: raise Gem::InstallError, "#{spec.name} requires #{dependency}" 176: end 177: 178: true 179: 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 441 441: def extract_files 442: expand_and_validate_gem_dir 443: 444: raise ArgumentError, "format required to extract from" if @format.nil? 445: 446: @format.file_entries.each do |entry, file_data| 447: path = entry['path'].untaint 448: 449: if path =~ /\A\// then # for extra sanity 450: raise Gem::InstallError, 451: "attempt to install file into #{entry['path'].inspect}" 452: end 453: 454: path = File.expand_path File.join(@gem_dir, path) 455: 456: if path !~ /\A#{Regexp.escape @gem_dir}/ then 457: msg = "attempt to install file into %p under %p" % 458: [entry['path'], @gem_dir] 459: raise Gem::InstallError, msg 460: end 461: 462: FileUtils.mkdir_p File.dirname(path) 463: 464: File.open(path, "wb") do |out| 465: out.write file_data 466: end 467: 468: FileUtils.chmod entry['mode'], path 469: 470: say path if Gem.configuration.really_verbose 471: end 472: end
Prefix and suffix the program filename the same as ruby.
# File lib/rubygems/installer.rb, line 477 477: def formatted_program_filename(filename) 478: if @format_executable then 479: self.class.exec_format % File.basename(filename) 480: else 481: filename 482: end 483: end
# File lib/rubygems/installer.rb, line 231 231: def generate_bin 232: return if @spec.executables.nil? or @spec.executables.empty? 233: 234: # If the user has asked for the gem to be installed in a directory that is 235: # the system gem directory, then use the system bin directory, else create 236: # (or use) a new bin dir under the gem_home. 237: bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home) 238: 239: Dir.mkdir bindir unless File.exist? bindir 240: raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir 241: 242: @spec.executables.each do |filename| 243: filename.untaint 244: bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename) 245: mode = File.stat(bin_path).mode | 0111 246: File.chmod mode, bin_path 247: 248: if @wrappers then 249: generate_bin_script filename, bindir 250: else 251: generate_bin_symlink filename, bindir 252: end 253: end 254: end
Creates the scripts to run the applications in the gem.
# File lib/rubygems/installer.rb, line 263 263: def generate_bin_script(filename, bindir) 264: bin_script_path = File.join bindir, formatted_program_filename(filename) 265: 266: exec_path = File.join @gem_dir, @spec.bindir, filename 267: 268: # HACK some gems don't have #! in their executables, restore 2008/06 269: #if File.read(exec_path, 2) == '#!' then 270: FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers 271: 272: File.open bin_script_path, 'w', 0755 do |file| 273: file.print app_script_text(filename) 274: end 275: 276: say bin_script_path if Gem.configuration.really_verbose 277: 278: generate_windows_script bindir, filename 279: #else 280: # FileUtils.rm_f bin_script_path 281: # FileUtils.cp exec_path, bin_script_path, 282: # :verbose => Gem.configuration.really_verbose 283: #end 284: 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 290 290: def generate_bin_symlink(filename, bindir) 291: if Gem.win_platform? then 292: alert_warning "Unable to use symlinks on Windows, installing wrapper" 293: generate_bin_script filename, bindir 294: return 295: end 296: 297: src = File.join @gem_dir, 'bin', filename 298: dst = File.join bindir, formatted_program_filename(filename) 299: 300: if File.exist? dst then 301: if File.symlink? dst then 302: link = File.readlink(dst).split File::SEPARATOR 303: cur_version = Gem::Version.create(link[-3].sub(/^.*-/, '')) 304: return if @spec.version < cur_version 305: end 306: File.unlink dst 307: end 308: 309: FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose 310: end
Creates windows .bat files for easy running of commands
# File lib/rubygems/installer.rb, line 219 219: def generate_windows_script(bindir, filename) 220: if Gem.win_platform? then 221: script_name = filename + ".bat" 222: script_path = File.join bindir, File.basename(script_name) 223: File.open script_path, 'w' do |file| 224: file.puts windows_stub_script(bindir, filename) 225: end 226: 227: say script_path if Gem.configuration.really_verbose 228: end 229: 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 104 104: def install 105: # If we're forcing the install then disable security unless the security 106: # policy says that we only install singed gems. 107: @security_policy = nil if @force and @security_policy and 108: not @security_policy.only_signed 109: 110: unless @force then 111: if rrv = @spec.required_ruby_version then 112: unless rrv.satisfied_by? Gem.ruby_version then 113: raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}" 114: end 115: end 116: 117: if rrgv = @spec.required_rubygems_version then 118: unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then 119: raise Gem::InstallError, 120: "#{@spec.name} requires RubyGems version #{rrgv}" 121: end 122: end 123: 124: unless @ignore_dependencies then 125: deps = @spec.runtime_dependencies 126: deps |= @spec.development_dependencies if @development 127: 128: deps.each do |dep_gem| 129: ensure_dependency @spec, dep_gem 130: end 131: end 132: end 133: 134: FileUtils.mkdir_p @gem_home unless File.directory? @gem_home 135: raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home 136: 137: Gem.ensure_gem_subdirectories @gem_home 138: 139: FileUtils.mkdir_p @gem_dir 140: 141: extract_files 142: generate_bin 143: build_extensions 144: write_spec 145: 146: write_require_paths_file_if_needed 147: 148: # HACK remove? Isn't this done in multiple places? 149: cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop 150: unless File.exist? cached_gem then 151: FileUtils.cp @gem, File.join(@gem_home, "cache") 152: end 153: 154: say @spec.post_install_message unless @spec.post_install_message.nil? 155: 156: @spec.loaded_from = File.join(@gem_home, 'specifications', 157: "#{@spec.full_name}.gemspec") 158: 159: Gem.source_index.add_spec @spec 160: 161: return @spec 162: rescue Zlib::GzipFile::Error 163: raise Gem::InstallError, "gzip error installing #{@gem}" 164: end
True if the gems in Gem.source_index satisfy dependency.
# File lib/rubygems/installer.rb, line 184 184: def installation_satisfies_dependency?(dependency) 185: Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0 186: end
Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.
# File lib/rubygems/installer.rb, line 316 316: def shebang(bin_file_name) 317: if @env_shebang then 318: "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] 319: else 320: path = File.join @gem_dir, @spec.bindir, bin_file_name 321: 322: File.open(path, "rb") do |file| 323: first_line = file.gets 324: if first_line =~ /^#!/ then 325: # Preserve extra words on shebang line, like "-w". Thanks RPA. 326: shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}") 327: else 328: # Create a plain shebang line. 329: shebang = "#!#{Gem.ruby}" 330: end 331: 332: shebang.strip # Avoid nasty ^M issues. 333: end 334: end 335: end
Unpacks the gem into the given directory.
# File lib/rubygems/installer.rb, line 191 191: def unpack(directory) 192: @gem_dir = directory 193: @format = Gem::Format.from_file_by_path @gem, @security_policy 194: extract_files 195: end
return the stub script text used to launch the true ruby script
# File lib/rubygems/installer.rb, line 368 368: def windows_stub_script(bindir, bin_file_name) 369: "@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" 370: 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 204 204: def write_spec 205: rubycode = @spec.to_ruby 206: 207: file_name = File.join @gem_home, 'specifications', 208: "#{@spec.full_name}.gemspec" 209: file_name.untaint 210: 211: File.open(file_name, "w") do |file| 212: file.puts rubycode 213: end 214: end