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.

The installer fires 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
exec_format  [W] 
gem_home  [R]  The gem repository the gem will be installed into
path_warning  [RW]  True if we‘ve warned about PATH not including Gem.bindir
spec  [R]  The Gem::Specification for the gem being installed

Public Class methods

Defaults to use Ruby‘s program prefix and suffix.

[Source]

    # File lib/rubygems/installer.rb, line 72
72:     def exec_format
73:       @exec_format ||= Gem.default_exec_format
74:     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 93
 93:   def initialize(gem, options={})
 94:     @gem = gem
 95: 
 96:     options = {
 97:       :bin_dir      => nil,
 98:       :env_shebang  => false,
 99:       :exec_format  => false,
100:       :force        => false,
101:       :install_dir  => Gem.dir,
102:       :source_index => Gem.source_index,
103:     }.merge options
104: 
105:     @env_shebang         = options[:env_shebang]
106:     @force               = options[:force]
107:     gem_home             = options[:install_dir]
108:     @gem_home            = File.expand_path(gem_home)
109:     @ignore_dependencies = options[:ignore_dependencies]
110:     @format_executable   = options[:format_executable]
111:     @security_policy     = options[:security_policy]
112:     @wrappers            = options[:wrappers]
113:     @bin_dir             = options[:bin_dir]
114:     @development         = options[:development]
115:     @source_index        = options[:source_index]
116: 
117:     begin
118:       @format = Gem::Format.from_file_by_path @gem, @security_policy
119:     rescue Gem::Package::FormatError
120:       raise Gem::InstallError, "invalid gem format for #{@gem}"
121:     end
122: 
123:     if options[:user_install] and not options[:unpack] then
124:       @gem_home = Gem.user_dir
125: 
126:       user_bin_dir = File.join(@gem_home, 'bin')
127:       unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then
128:         unless self.class.path_warning then
129:           alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t  gem executables will not run."
130:           self.class.path_warning = true
131:         end
132:       end
133:     end
134: 
135:     FileUtils.mkdir_p @gem_home
136:     raise Gem::FilePermissionError, @gem_home unless
137:       options[:unpack] or File.writable? @gem_home
138: 
139:     @spec = @format.spec
140: 
141:     @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
142:   end

Public Instance methods

Return the text for an application file.

[Source]

     # File lib/rubygems/installer.rb, line 397
397:   def app_script_text(bin_file_name)
398:     "\#{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"
399:   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 441
441:   def build_extensions
442:     return if @spec.extensions.empty?
443:     say "Building native extensions.  This could take a while..."
444:     start_dir = Dir.pwd
445:     dest_path = File.join @gem_dir, @spec.require_paths.first
446:     ran_rake = false # only run rake once
447: 
448:     @spec.extensions.each do |extension|
449:       break if ran_rake
450:       results = []
451: 
452:       builder = case extension
453:                 when /extconf/ then
454:                   Gem::Ext::ExtConfBuilder
455:                 when /configure/ then
456:                   Gem::Ext::ConfigureBuilder
457:                 when /rakefile/i, /mkrf_conf/i then
458:                   ran_rake = true
459:                   Gem::Ext::RakeBuilder
460:                 else
461:                   results = ["No builder for extension '#{extension}'"]
462:                   nil
463:                 end
464: 
465:       begin
466:         Dir.chdir File.join(@gem_dir, File.dirname(extension))
467:         results = builder.build(extension, @gem_dir, dest_path, results)
468: 
469:         say results.join("\n") if Gem.configuration.really_verbose
470: 
471:       rescue => ex
472:         results = results.join "\n"
473: 
474:         File.open('gem_make.out', 'wb') { |f| f.puts results }
475: 
476:         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"
477: 
478:         raise ExtensionBuildError, message
479:       ensure
480:         Dir.chdir start_dir
481:       end
482:     end
483:   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 231
231:   def ensure_dependency(spec, dependency)
232:     unless installation_satisfies_dependency? dependency then
233:       raise Gem::InstallError, "#{spec.name} requires #{dependency}"
234:     end
235: 
236:     true
237:   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 498
498:   def extract_files
499:     @gem_dir = File.expand_path @gem_dir
500: 
501:     raise ArgumentError, "format required to extract from" if @format.nil?
502: 
503:     @format.file_entries.each do |entry, file_data|
504:       path = entry['path'].untaint
505: 
506:       if path =~ /\A\// then # for extra sanity
507:         raise Gem::InstallError,
508:               "attempt to install file into #{entry['path'].inspect}"
509:       end
510: 
511:       path = File.expand_path File.join(@gem_dir, path)
512: 
513:       if path !~ /\A#{Regexp.escape @gem_dir}/ then
514:         msg = "attempt to install file into %p under %p" %
515:                 [entry['path'], @gem_dir]
516:         raise Gem::InstallError, msg
517:       end
518: 
519:       FileUtils.rm_rf(path) if File.exists?(path)
520:       FileUtils.mkdir_p File.dirname(path)
521: 
522:       File.open(path, "wb") do |out|
523:         out.write file_data
524:       end
525: 
526:       FileUtils.chmod entry['mode'], path
527: 
528:       say path if Gem.configuration.really_verbose
529:     end
530:   end

Prefix and suffix the program filename the same as ruby.

[Source]

     # File lib/rubygems/installer.rb, line 535
535:   def formatted_program_filename(filename)
536:     if @format_executable then
537:       self.class.exec_format % File.basename(filename)
538:     else
539:       filename
540:     end
541:   end

[Source]

     # File lib/rubygems/installer.rb, line 286
286:   def generate_bin
287:     return if @spec.executables.nil? or @spec.executables.empty?
288: 
289:     # If the user has asked for the gem to be installed in a directory that is
290:     # the system gem directory, then use the system bin directory, else create
291:     # (or use) a new bin dir under the gem_home.
292:     bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
293: 
294:     Dir.mkdir bindir unless File.exist? bindir
295:     raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
296: 
297:     @spec.executables.each do |filename|
298:       filename.untaint
299:       bin_path = File.expand_path "#{@spec.bindir}/#{filename}", @gem_dir
300:       mode = File.stat(bin_path).mode | 0111
301:       File.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:     exec_path = File.join @gem_dir, @spec.bindir, filename
322: 
323:     # HACK some gems don't have #! in their executables, restore 2008/06
324:     #if File.read(exec_path, 2) == '#!' then
325:       FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
326: 
327:       File.open bin_script_path, 'w', 0755 do |file|
328:         file.print app_script_text(filename)
329:       end
330: 
331:       say bin_script_path if Gem.configuration.really_verbose
332: 
333:       generate_windows_script filename, bindir
334:     #else
335:     #  FileUtils.rm_f bin_script_path
336:     #  FileUtils.cp exec_path, bin_script_path,
337:     #               :verbose => Gem.configuration.really_verbose
338:     #end
339:   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 345
345:   def generate_bin_symlink(filename, bindir)
346:     if Gem.win_platform? then
347:       alert_warning "Unable to use symlinks on Windows, installing wrapper"
348:       generate_bin_script filename, bindir
349:       return
350:     end
351: 
352:     src = File.join @gem_dir, 'bin', filename
353:     dst = File.join bindir, formatted_program_filename(filename)
354: 
355:     if File.exist? dst then
356:       if File.symlink? dst then
357:         link = File.readlink(dst).split File::SEPARATOR
358:         cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
359:         return if @spec.version < cur_version
360:       end
361:       File.unlink dst
362:     end
363: 
364:     FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
365:   end

Creates windows .bat files for easy running of commands

[Source]

     # File lib/rubygems/installer.rb, line 274
274:   def generate_windows_script(filename, bindir)
275:     if Gem.win_platform? then
276:       script_name = filename + ".bat"
277:       script_path = File.join bindir, File.basename(script_name)
278:       File.open script_path, 'w' do |file|
279:         file.puts windows_stub_script(bindir, filename)
280:       end
281: 
282:       say script_path if Gem.configuration.really_verbose
283:     end
284:   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 155
155:   def install
156:     # If we're forcing the install then disable security unless the security
157:     # policy says that we only install singed gems.
158:     @security_policy = nil if @force and @security_policy and
159:                               not @security_policy.only_signed
160: 
161:     unless @force then
162:       if rrv = @spec.required_ruby_version then
163:         unless rrv.satisfied_by? Gem.ruby_version then
164:           raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}."
165:         end
166:       end
167: 
168:       if rrgv = @spec.required_rubygems_version then
169:         unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then
170:           raise Gem::InstallError,
171:             "#{@spec.name} requires RubyGems version #{rrgv}. " +
172:             "Try 'gem update --system' to update RubyGems itself."
173:         end
174:       end
175: 
176:       unless @ignore_dependencies then
177:         deps = @spec.runtime_dependencies
178:         deps |= @spec.development_dependencies if @development
179: 
180:         deps.each do |dep_gem|
181:           ensure_dependency @spec, dep_gem
182:         end
183:       end
184:     end
185: 
186:     Gem.pre_install_hooks.each do |hook|
187:       hook.call self
188:     end
189: 
190:     FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
191: 
192:     Gem.ensure_gem_subdirectories @gem_home
193: 
194:     FileUtils.mkdir_p @gem_dir
195: 
196:     extract_files
197:     generate_bin
198:     build_extensions
199:     write_spec
200: 
201:     write_require_paths_file_if_needed
202: 
203:     # HACK remove?  Isn't this done in multiple places?
204:     cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop
205:     unless File.exist? cached_gem then
206:       FileUtils.cp @gem, File.join(@gem_home, "cache")
207:     end
208: 
209:     say @spec.post_install_message unless @spec.post_install_message.nil?
210: 
211:     @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name)
212: 
213:     @source_index.add_spec @spec
214: 
215:     Gem.post_install_hooks.each do |hook|
216:       hook.call self
217:     end
218: 
219:     return @spec
220:   rescue Zlib::GzipFile::Error
221:     raise Gem::InstallError, "gzip error installing #{@gem}"
222:   end

True if the gems in the source_index satisfy dependency.

[Source]

     # File lib/rubygems/installer.rb, line 242
242:   def installation_satisfies_dependency?(dependency)
243:     @source_index.find_name(dependency.name, dependency.requirement).size > 0
244:   end

Generates a #! line for bin_file_name‘s wrapper copying arguments if necessary.

[Source]

     # File lib/rubygems/installer.rb, line 371
371:   def shebang(bin_file_name)
372:     ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang
373:     path = File.join @gem_dir, @spec.bindir, bin_file_name
374:     first_line = File.open(path, "rb") {|file| file.gets}
375: 
376:     if /\A#!/ =~ first_line then
377:       # Preserve extra words on shebang line, like "-w".  Thanks RPA.
378:       shebang = first_line.sub(/\A\#!.*?ruby\S*(?=(\s+\S+))/, "#!#{Gem.ruby}")
379:       opts = $1
380:       shebang.strip! # Avoid nasty ^M issues.
381:     end
382: 
383:     if not ruby_name then
384:       "#!#{Gem.ruby}#{opts}"
385:     elsif opts then
386:       "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
387:     else
388:       # Create a plain shebang line.
389:       @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
390:       "#!#{@env_path} #{ruby_name}"
391:     end
392:   end

Unpacks the gem into the given directory.

[Source]

     # File lib/rubygems/installer.rb, line 249
249:   def unpack(directory)
250:     @gem_dir = directory
251:     @format = Gem::Format.from_file_by_path @gem, @security_policy
252:     extract_files
253:   end

return the stub script text used to launch the true ruby script

[Source]

     # File lib/rubygems/installer.rb, line 425
425:   def windows_stub_script(bindir, bin_file_name)
426:     "@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"
427:   end

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

[Source]

     # File lib/rubygems/installer.rb, line 259
259:   def write_spec
260:     rubycode = @spec.to_ruby
261: 
262:     file_name = File.join @gem_home, 'specifications', @spec.spec_name
263: 
264:     file_name.untaint
265: 
266:     File.open(file_name, "w") do |file|
267:       file.puts rubycode
268:     end
269:   end

[Validate]