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.

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

Public Instance methods

Return the text for an application file.

[Source]

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

Prefix and suffix the program filename the same as ruby.

[Source]

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

[Source]

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

Creates the scripts to run the applications in the gem.

[Source]

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

Creates windows .bat files for easy running of commands

[Source]

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

True if the gems in the source_index satisfy dependency.

[Source]

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

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

[Source]

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

Unpacks the gem into the given directory.

[Source]

     # File lib/rubygems/installer.rb, line 266
266:   def unpack(directory)
267:     @gem_dir = directory
268:     @format = Gem::Format.from_file_by_path @gem, @security_policy
269:     extract_files
270:   end

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

[Source]

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

[Source]

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

[Validate]