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

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
home_install_warning  [RW]  True if we‘ve warned about ~/.gems install
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 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:     @gem = gem
 96: 
 97:     options = {
 98:       :bin_dir      => nil,
 99:       :env_shebang  => false,
100:       :exec_format  => false,
101:       :force        => false,
102:       :install_dir  => Gem.dir,
103:       :source_index => Gem.source_index,
104:     }.merge options
105: 
106:     @env_shebang         = options[:env_shebang]
107:     @force               = options[:force]
108:     gem_home             = options[:install_dir]
109:     @gem_home            = Pathname.new(gem_home).expand_path
110:     @ignore_dependencies = options[:ignore_dependencies]
111:     @format_executable   = options[:format_executable]
112:     @security_policy     = options[:security_policy]
113:     @wrappers            = options[:wrappers]
114:     @bin_dir             = options[:bin_dir]
115:     @development         = options[:development]
116:     @source_index        = options[:source_index]
117: 
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: 
124:     begin
125:       FileUtils.mkdir_p @gem_home
126:     rescue Errno::EACCES, Errno::ENOTDIR
127:       # We'll divert to ~/.gems below
128:     end
129: 
130:     if not File.writable? @gem_home or
131:         # TODO: Shouldn't have to test for existence of bindir; tests need it.
132:         (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and
133:          not File.writable? Gem.bindir) then
134:       if options[:user_install] == false then # You don't want to use ~
135:         raise Gem::FilePermissionError, @gem_home
136:       elsif options[:user_install].nil? then
137:         unless self.class.home_install_warning or options[:unpack] then
138:           alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t  #{Gem.bindir} aren't both writable."
139:           self.class.home_install_warning = true
140:         end
141:       end
142:       options[:user_install] = true
143:     end
144: 
145:     if options[:user_install] and not options[:unpack] then
146:       @gem_home = Gem.user_dir
147: 
148:       user_bin_dir = File.join(@gem_home, 'bin')
149:       unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then
150:         unless self.class.path_warning then
151:           alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t  gem executables will not run."
152:           self.class.path_warning = true
153:         end
154:       end
155: 
156:       FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
157:       # If it's still not writable, you've got issues.
158:       raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
159:     end
160: 
161:     @spec = @format.spec
162: 
163:     @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
164:   end

Public Instance methods

Return the text for an application file.

[Source]

     # File lib/rubygems/installer.rb, line 418
418:   def app_script_text(bin_file_name)
419:     "\#{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"
420:   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 462
462:   def build_extensions
463:     return if @spec.extensions.empty?
464:     say "Building native extensions.  This could take a while..."
465:     start_dir = Dir.pwd
466:     dest_path = File.join @gem_dir, @spec.require_paths.first
467:     ran_rake = false # only run rake once
468: 
469:     @spec.extensions.each do |extension|
470:       break if ran_rake
471:       results = []
472: 
473:       builder = case extension
474:                 when /extconf/ then
475:                   Gem::Ext::ExtConfBuilder
476:                 when /configure/ then
477:                   Gem::Ext::ConfigureBuilder
478:                 when /rakefile/i, /mkrf_conf/i then
479:                   ran_rake = true
480:                   Gem::Ext::RakeBuilder
481:                 else
482:                   results = ["No builder for extension '#{extension}'"]
483:                   nil
484:                 end
485: 
486:       begin
487:         Dir.chdir File.join(@gem_dir, File.dirname(extension))
488:         results = builder.build(extension, @gem_dir, dest_path, results)
489: 
490:         say results.join("\n") if Gem.configuration.really_verbose
491: 
492:       rescue => ex
493:         results = results.join "\n"
494: 
495:         File.open('gem_make.out', 'wb') { |f| f.puts results }
496: 
497:         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"
498: 
499:         raise ExtensionBuildError, message
500:       ensure
501:         Dir.chdir start_dir
502:       end
503:     end
504:   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 253
253:   def ensure_dependency(spec, dependency)
254:     unless installation_satisfies_dependency? dependency then
255:       raise Gem::InstallError, "#{spec.name} requires #{dependency}"
256:     end
257: 
258:     true
259:   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 519
519:   def extract_files
520:     expand_and_validate_gem_dir
521: 
522:     raise ArgumentError, "format required to extract from" if @format.nil?
523: 
524:     @format.file_entries.each do |entry, file_data|
525:       path = entry['path'].untaint
526: 
527:       if path =~ /\A\// then # for extra sanity
528:         raise Gem::InstallError,
529:               "attempt to install file into #{entry['path'].inspect}"
530:       end
531: 
532:       path = File.expand_path File.join(@gem_dir, path)
533: 
534:       if path !~ /\A#{Regexp.escape @gem_dir}/ then
535:         msg = "attempt to install file into %p under %p" %
536:                 [entry['path'], @gem_dir]
537:         raise Gem::InstallError, msg
538:       end
539: 
540:       FileUtils.rm_rf(path) if File.exists?(path)
541:       FileUtils.mkdir_p File.dirname(path)
542: 
543:       File.open(path, "wb") do |out|
544:         out.write file_data
545:       end
546: 
547:       FileUtils.chmod entry['mode'], path
548: 
549:       say path if Gem.configuration.really_verbose
550:     end
551:   end

Prefix and suffix the program filename the same as ruby.

[Source]

     # File lib/rubygems/installer.rb, line 556
556:   def formatted_program_filename(filename)
557:     if @format_executable then
558:       self.class.exec_format % File.basename(filename)
559:     else
560:       filename
561:     end
562:   end

[Source]

     # File lib/rubygems/installer.rb, line 309
309:   def generate_bin
310:     return if @spec.executables.nil? or @spec.executables.empty?
311: 
312:     # If the user has asked for the gem to be installed in a directory that is
313:     # the system gem directory, then use the system bin directory, else create
314:     # (or use) a new bin dir under the gem_home.
315:     bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
316: 
317:     Dir.mkdir bindir unless File.exist? bindir
318:     raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
319: 
320:     @spec.executables.each do |filename|
321:       filename.untaint
322:       bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename)
323:       mode = File.stat(bin_path).mode | 0111
324:       File.chmod mode, bin_path
325: 
326:       if @wrappers then
327:         generate_bin_script filename, bindir
328:       else
329:         generate_bin_symlink filename, bindir
330:       end
331:     end
332:   end

Creates the scripts to run the applications in the gem.

[Source]

     # File lib/rubygems/installer.rb, line 341
341:   def generate_bin_script(filename, bindir)
342:     bin_script_path = File.join bindir, formatted_program_filename(filename)
343: 
344:     exec_path = File.join @gem_dir, @spec.bindir, filename
345: 
346:     # HACK some gems don't have #! in their executables, restore 2008/06
347:     #if File.read(exec_path, 2) == '#!' then
348:       FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
349: 
350:       File.open bin_script_path, 'w', 0755 do |file|
351:         file.print app_script_text(filename)
352:       end
353: 
354:       say bin_script_path if Gem.configuration.really_verbose
355: 
356:       generate_windows_script bindir, filename
357:     #else
358:     #  FileUtils.rm_f bin_script_path
359:     #  FileUtils.cp exec_path, bin_script_path,
360:     #               :verbose => Gem.configuration.really_verbose
361:     #end
362:   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 368
368:   def generate_bin_symlink(filename, bindir)
369:     if Gem.win_platform? then
370:       alert_warning "Unable to use symlinks on Windows, installing wrapper"
371:       generate_bin_script filename, bindir
372:       return
373:     end
374: 
375:     src = File.join @gem_dir, 'bin', filename
376:     dst = File.join bindir, formatted_program_filename(filename)
377: 
378:     if File.exist? dst then
379:       if File.symlink? dst then
380:         link = File.readlink(dst).split File::SEPARATOR
381:         cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
382:         return if @spec.version < cur_version
383:       end
384:       File.unlink dst
385:     end
386: 
387:     FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
388:   end

Creates windows .bat files for easy running of commands

[Source]

     # File lib/rubygems/installer.rb, line 297
297:   def generate_windows_script(bindir, filename)
298:     if Gem.win_platform? then
299:       script_name = filename + ".bat"
300:       script_path = File.join bindir, File.basename(script_name)
301:       File.open script_path, 'w' do |file|
302:         file.puts windows_stub_script(bindir, filename)
303:       end
304: 
305:       say script_path if Gem.configuration.really_verbose
306:     end
307:   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 177
177:   def install
178:     # If we're forcing the install then disable security unless the security
179:     # policy says that we only install singed gems.
180:     @security_policy = nil if @force and @security_policy and
181:                               not @security_policy.only_signed
182: 
183:     unless @force then
184:       if rrv = @spec.required_ruby_version then
185:         unless rrv.satisfied_by? Gem.ruby_version then
186:           raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}"
187:         end
188:       end
189: 
190:       if rrgv = @spec.required_rubygems_version then
191:         unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then
192:           raise Gem::InstallError,
193:                 "#{@spec.name} requires RubyGems version #{rrgv}"
194:         end
195:       end
196: 
197:       unless @ignore_dependencies then
198:         deps = @spec.runtime_dependencies
199:         deps |= @spec.development_dependencies if @development
200: 
201:         deps.each do |dep_gem|
202:           ensure_dependency @spec, dep_gem
203:         end
204:       end
205:     end
206: 
207:     Gem.pre_install_hooks.each do |hook|
208:       hook.call self
209:     end
210: 
211:     FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
212: 
213:     Gem.ensure_gem_subdirectories @gem_home
214: 
215:     FileUtils.mkdir_p @gem_dir
216: 
217:     extract_files
218:     generate_bin
219:     build_extensions
220:     write_spec
221: 
222:     write_require_paths_file_if_needed
223: 
224:     # HACK remove?  Isn't this done in multiple places?
225:     cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop
226:     unless File.exist? cached_gem then
227:       FileUtils.cp @gem, File.join(@gem_home, "cache")
228:     end
229: 
230:     say @spec.post_install_message unless @spec.post_install_message.nil?
231: 
232:     @spec.loaded_from = File.join(@gem_home, 'specifications',
233:                                   "#{@spec.full_name}.gemspec")
234: 
235:     @source_index.add_spec @spec
236: 
237:     Gem.post_install_hooks.each do |hook|
238:       hook.call self
239:     end
240: 
241:     return @spec
242:   rescue Zlib::GzipFile::Error
243:     raise Gem::InstallError, "gzip error installing #{@gem}"
244:   end

True if the gems in the source_index satisfy dependency.

[Source]

     # File lib/rubygems/installer.rb, line 264
264:   def installation_satisfies_dependency?(dependency)
265:     @source_index.find_name(dependency.name, dependency.version_requirements).size > 0
266:   end

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

[Source]

     # File lib/rubygems/installer.rb, line 394
394:   def shebang(bin_file_name)
395:     if @env_shebang then
396:       "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
397:     else
398:       path = File.join @gem_dir, @spec.bindir, bin_file_name
399: 
400:       File.open(path, "rb") do |file|
401:         first_line = file.gets
402:         if first_line =~ /^#!/ then
403:           # Preserve extra words on shebang line, like "-w".  Thanks RPA.
404:           shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}")
405:         else
406:           # Create a plain shebang line.
407:           shebang = "#!#{Gem.ruby}"
408:         end
409: 
410:         shebang.strip # Avoid nasty ^M issues.
411:       end
412:     end
413:   end

Unpacks the gem into the given directory.

[Source]

     # File lib/rubygems/installer.rb, line 271
271:   def unpack(directory)
272:     @gem_dir = directory
273:     @format = Gem::Format.from_file_by_path @gem, @security_policy
274:     extract_files
275:   end

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

[Source]

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

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

[Source]

     # File lib/rubygems/installer.rb, line 281
281:   def write_spec
282:     rubycode = @spec.to_ruby
283: 
284:     file_name = File.join @gem_home, 'specifications',
285:                           "#{@spec.full_name}.gemspec"
286: 
287:     file_name.untaint
288: 
289:     File.open(file_name, "w") do |file|
290:       file.puts rubycode
291:     end
292:   end

[Validate]