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.

Methods

Included Modules

Gem::UserInteraction Gem::RequirePathsBuilder

Classes and Modules

Class Gem::Installer::ExtensionBuildError

Constants

ENV_PATHS = %w[/usr/bin/env /bin/env]   Paths where env(1) might live. Some systems are broken and have it in /bin

Attributes

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

Public Class methods

Defaults to use Ruby‘s program prefix and suffix.

[Source]

    # 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.

[Source]

     # 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

Public Instance methods

Return the text for an application file.

[Source]

     # 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 =~ /^_(.*)_$/ 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"
457:   end

Builds extensions. Valid types of extensions are extconf.rb files, configure scripts and rakefiles or mkrf_conf files.

[Source]

     # File lib/rubygems/installer.rb, line 501
501:   def build_extensions
502:     return if spec.extensions.empty?
503:     say "Building native extensions.  This could take a while..."
504:     dest_path = File.join gem_dir, spec.require_paths.first
505:     ran_rake = false # only run rake once
506: 
507:     spec.extensions.each do |extension|
508:       break if ran_rake
509:       results = []
510: 
511:       builder = case extension
512:                 when /extconf/ then
513:                   Gem::Ext::ExtConfBuilder
514:                 when /configure/ then
515:                   Gem::Ext::ConfigureBuilder
516:                 when /rakefile/i, /mkrf_conf/i then
517:                   ran_rake = true
518:                   Gem::Ext::RakeBuilder
519:                 else
520:                   results = ["No builder for extension '#{extension}'"]
521:                   nil
522:                 end
523: 
524: 
525:       extension_dir = begin
526:                         File.join gem_dir, File.dirname(extension)
527:                       rescue TypeError # extension == nil
528:                         gem_dir
529:                       end
530: 
531: 
532:       begin
533:         Dir.chdir extension_dir do
534:           results = builder.build(extension, gem_dir, dest_path, results)
535: 
536:           say results.join("\n") if Gem.configuration.really_verbose
537:         end
538:       rescue
539:         results = results.join "\n"
540: 
541:         gem_make_out = File.join extension_dir, 'gem_make.out'
542: 
543:         open gem_make_out, 'wb' do |io| io.puts results end
544: 
545:         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"
546: 
547:         raise ExtensionBuildError, message
548:       end
549:     end
550:   end

[Source]

     # 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.

[Source]

     # File lib/rubygems/installer.rb, line 615
615:   def dir
616:     gem_dir.to_s
617:   end

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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.

[Source]

     # File lib/rubygems/installer.rb, line 565
565:   def extract_files
566:     raise ArgumentError, "format required to extract from" if @format.nil?
567: 
568:     @format.file_entries.each do |entry, file_data|
569:       path = entry['path'].untaint
570: 
571:       if path.start_with? "/" then # for extra sanity
572:         raise Gem::InstallError, "attempt to install file into #{entry['path']}"
573:       end
574: 
575:       path = File.expand_path File.join(gem_dir, path)
576: 
577:       unless path.start_with? gem_dir then
578:         msg = "attempt to install file into %p under %s" %
579:                 [entry['path'], gem_dir]
580:         raise Gem::InstallError, msg
581:       end
582: 
583:       FileUtils.rm_rf(path) if File.exist? path
584: 
585:       dir = File.dirname path
586:       FileUtils.mkdir_p dir unless File.exist? dir
587: 
588:       File.open(path, "wb") do |out|
589:         out.write file_data
590:       end
591: 
592:       FileUtils.chmod entry['mode'], path
593: 
594:       say path if Gem.configuration.really_verbose
595:     end
596:   end

Lazy accessor for the installer‘s Gem::Format instance.

[Source]

     # 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.

[Source]

     # File lib/rubygems/installer.rb, line 601
601:   def formatted_program_filename(filename)
602:     if @format_executable then
603:       self.class.exec_format % File.basename(filename)
604:     else
605:       filename
606:     end
607:   end

Lazy accessor for the spec‘s gem directory.

[Source]

     # File lib/rubygems/installer.rb, line 110
110:   def gem_dir
111:     @gem_dir ||= spec.gem_dir.dup.untaint
112:   end

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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.

[Source]

     # File lib/rubygems/installer.rb, line 240
240:   def installation_satisfies_dependency?(dependency)
241:     not dependency.matching_specs.empty?
242:   end

[Source]

     # 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.

[Source]

     # 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

Lazy accessor for the installer‘s spec.

[Source]

     # File lib/rubygems/installer.rb, line 128
128:   def spec
129:     @spec ||= format.spec
130:   end

Unpacks the gem into the given directory.

[Source]

     # 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

[Source]

     # 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

[Source]

     # File lib/rubygems/installer.rb, line 483
483:   def windows_stub_script(bindir, bin_file_name)
484:     ruby = File.basename(Gem.ruby).chomp('"')
485:     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"
486: 
487:   end

Writes the .gemspec specification (in Ruby) to the gem home‘s specifications directory.

[Source]

     # File lib/rubygems/installer.rb, line 257
257:   def write_spec
258:     file_name = spec.spec_file.untaint
259: 
260:     File.open(file_name, "w") do |file|
261:       file.puts spec.to_ruby_for_cache
262:     end
263:   end

[Validate]