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_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

Public Class methods

Defaults to use Ruby‘s program prefix and suffix.

[Source]

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

[Source]

     # 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

Public Instance methods

Return the text for an application file.

[Source]

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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

     # 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

[Source]

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

[Source]

     # 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

[Source]

     # 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

[Source]

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

[Source]

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

[Source]

     # 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

[Source]

     # 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

[Source]

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

[Source]

     # 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

[Validate]