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

exec_format  [W] 

Public Class methods

Defaults to use Ruby‘s program prefix and suffix.

[Source]

    # File lib/rubygems/installer.rb, line 39
39:     def exec_format
40:       @exec_format ||= Gem.default_exec_format
41:     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 60
60:   def initialize(gem, options={})
61:     @gem = gem
62: 
63:     options = {
64:       :force => false,
65:       :install_dir => Gem.dir,
66:       :exec_format => false,
67:       :env_shebang => false,
68:       :bin_dir => nil
69:     }.merge options
70: 
71:     @env_shebang = options[:env_shebang]
72:     @force = options[:force]
73:     gem_home = options[:install_dir]
74:     @gem_home = Pathname.new(gem_home).expand_path
75:     @ignore_dependencies = options[:ignore_dependencies]
76:     @format_executable = options[:format_executable]
77:     @security_policy = options[:security_policy]
78:     @wrappers = options[:wrappers]
79:     @bin_dir = options[:bin_dir]
80:     @development = options[:development]
81: 
82:     begin
83:       @format = Gem::Format.from_file_by_path @gem, @security_policy
84:     rescue Gem::Package::FormatError
85:       raise Gem::InstallError, "invalid gem format for #{@gem}"
86:     end
87: 
88:     @spec = @format.spec
89: 
90:     @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
91:   end

Public Instance methods

Return the text for an application file.

[Source]

     # File lib/rubygems/installer.rb, line 340
340:   def app_script_text(bin_file_name)
341:     "\#{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"
342:   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 384
384:   def build_extensions
385:     return if @spec.extensions.empty?
386:     say "Building native extensions.  This could take a while..."
387:     start_dir = Dir.pwd
388:     dest_path = File.join @gem_dir, @spec.require_paths.first
389:     ran_rake = false # only run rake once
390: 
391:     @spec.extensions.each do |extension|
392:       break if ran_rake
393:       results = []
394: 
395:       builder = case extension
396:                 when /extconf/ then
397:                   Gem::Ext::ExtConfBuilder
398:                 when /configure/ then
399:                   Gem::Ext::ConfigureBuilder
400:                 when /rakefile/i, /mkrf_conf/i then
401:                   ran_rake = true
402:                   Gem::Ext::RakeBuilder
403:                 else
404:                   results = ["No builder for extension '#{extension}'"]
405:                   nil
406:                 end
407: 
408:       begin
409:         Dir.chdir File.join(@gem_dir, File.dirname(extension))
410:         results = builder.build(extension, @gem_dir, dest_path, results)
411: 
412:         say results.join("\n") if Gem.configuration.really_verbose
413: 
414:       rescue => ex
415:         results = results.join "\n"
416: 
417:         File.open('gem_make.out', 'wb') { |f| f.puts results }
418: 
419:         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"
420: 
421:         raise ExtensionBuildError, message
422:       ensure
423:         Dir.chdir start_dir
424:       end
425:     end
426:   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 173
173:   def ensure_dependency(spec, dependency)
174:     unless installation_satisfies_dependency? dependency then
175:       raise Gem::InstallError, "#{spec.name} requires #{dependency}"
176:     end
177: 
178:     true
179:   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 441
441:   def extract_files
442:     expand_and_validate_gem_dir
443: 
444:     raise ArgumentError, "format required to extract from" if @format.nil?
445: 
446:     @format.file_entries.each do |entry, file_data|
447:       path = entry['path'].untaint
448: 
449:       if path =~ /\A\// then # for extra sanity
450:         raise Gem::InstallError,
451:               "attempt to install file into #{entry['path'].inspect}"
452:       end
453: 
454:       path = File.expand_path File.join(@gem_dir, path)
455: 
456:       if path !~ /\A#{Regexp.escape @gem_dir}/ then
457:         msg = "attempt to install file into %p under %p" %
458:                 [entry['path'], @gem_dir]
459:         raise Gem::InstallError, msg
460:       end
461: 
462:       FileUtils.mkdir_p File.dirname(path)
463: 
464:       File.open(path, "wb") do |out|
465:         out.write file_data
466:       end
467: 
468:       FileUtils.chmod entry['mode'], path
469: 
470:       say path if Gem.configuration.really_verbose
471:     end
472:   end

Prefix and suffix the program filename the same as ruby.

[Source]

     # File lib/rubygems/installer.rb, line 477
477:   def formatted_program_filename(filename)
478:     if @format_executable then
479:       self.class.exec_format % File.basename(filename)
480:     else
481:       filename
482:     end
483:   end

[Source]

     # File lib/rubygems/installer.rb, line 231
231:   def generate_bin
232:     return if @spec.executables.nil? or @spec.executables.empty?
233: 
234:     # If the user has asked for the gem to be installed in a directory that is
235:     # the system gem directory, then use the system bin directory, else create
236:     # (or use) a new bin dir under the gem_home.
237:     bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
238: 
239:     Dir.mkdir bindir unless File.exist? bindir
240:     raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
241: 
242:     @spec.executables.each do |filename|
243:       filename.untaint
244:       bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename)
245:       mode = File.stat(bin_path).mode | 0111
246:       File.chmod mode, bin_path
247: 
248:       if @wrappers then
249:         generate_bin_script filename, bindir
250:       else
251:         generate_bin_symlink filename, bindir
252:       end
253:     end
254:   end

Creates the scripts to run the applications in the gem.

[Source]

     # File lib/rubygems/installer.rb, line 263
263:   def generate_bin_script(filename, bindir)
264:     bin_script_path = File.join bindir, formatted_program_filename(filename)
265: 
266:     exec_path = File.join @gem_dir, @spec.bindir, filename
267: 
268:     # HACK some gems don't have #! in their executables, restore 2008/06
269:     #if File.read(exec_path, 2) == '#!' then
270:       FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
271: 
272:       File.open bin_script_path, 'w', 0755 do |file|
273:         file.print app_script_text(filename)
274:       end
275: 
276:       say bin_script_path if Gem.configuration.really_verbose
277: 
278:       generate_windows_script bindir, filename
279:     #else
280:     #  FileUtils.rm_f bin_script_path
281:     #  FileUtils.cp exec_path, bin_script_path,
282:     #               :verbose => Gem.configuration.really_verbose
283:     #end
284:   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 290
290:   def generate_bin_symlink(filename, bindir)
291:     if Gem.win_platform? then
292:       alert_warning "Unable to use symlinks on Windows, installing wrapper"
293:       generate_bin_script filename, bindir
294:       return
295:     end
296: 
297:     src = File.join @gem_dir, 'bin', filename
298:     dst = File.join bindir, formatted_program_filename(filename)
299: 
300:     if File.exist? dst then
301:       if File.symlink? dst then
302:         link = File.readlink(dst).split File::SEPARATOR
303:         cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
304:         return if @spec.version < cur_version
305:       end
306:       File.unlink dst
307:     end
308: 
309:     FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
310:   end

Creates windows .bat files for easy running of commands

[Source]

     # File lib/rubygems/installer.rb, line 219
219:   def generate_windows_script(bindir, filename)
220:     if Gem.win_platform? then
221:       script_name = filename + ".bat"
222:       script_path = File.join bindir, File.basename(script_name)
223:       File.open script_path, 'w' do |file|
224:         file.puts windows_stub_script(bindir, filename)
225:       end
226: 
227:       say script_path if Gem.configuration.really_verbose
228:     end
229:   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 104
104:   def install
105:     # If we're forcing the install then disable security unless the security
106:     # policy says that we only install singed gems.
107:     @security_policy = nil if @force and @security_policy and
108:                               not @security_policy.only_signed
109: 
110:     unless @force then
111:       if rrv = @spec.required_ruby_version then
112:         unless rrv.satisfied_by? Gem.ruby_version then
113:           raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}"
114:         end
115:       end
116: 
117:       if rrgv = @spec.required_rubygems_version then
118:         unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then
119:           raise Gem::InstallError,
120:                 "#{@spec.name} requires RubyGems version #{rrgv}"
121:         end
122:       end
123: 
124:       unless @ignore_dependencies then
125:         deps = @spec.runtime_dependencies
126:         deps |= @spec.development_dependencies if @development
127: 
128:         deps.each do |dep_gem|
129:           ensure_dependency @spec, dep_gem
130:         end
131:       end
132:     end
133: 
134:     FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
135:     raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
136: 
137:     Gem.ensure_gem_subdirectories @gem_home
138: 
139:     FileUtils.mkdir_p @gem_dir
140: 
141:     extract_files
142:     generate_bin
143:     build_extensions
144:     write_spec
145: 
146:     write_require_paths_file_if_needed
147: 
148:     # HACK remove?  Isn't this done in multiple places?
149:     cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop
150:     unless File.exist? cached_gem then
151:       FileUtils.cp @gem, File.join(@gem_home, "cache")
152:     end
153: 
154:     say @spec.post_install_message unless @spec.post_install_message.nil?
155: 
156:     @spec.loaded_from = File.join(@gem_home, 'specifications',
157:                                   "#{@spec.full_name}.gemspec")
158: 
159:     Gem.source_index.add_spec @spec
160: 
161:     return @spec
162:   rescue Zlib::GzipFile::Error
163:     raise Gem::InstallError, "gzip error installing #{@gem}"
164:   end

True if the gems in Gem.source_index satisfy dependency.

[Source]

     # File lib/rubygems/installer.rb, line 184
184:   def installation_satisfies_dependency?(dependency)
185:     Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
186:   end

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

[Source]

     # File lib/rubygems/installer.rb, line 316
316:   def shebang(bin_file_name)
317:     if @env_shebang then
318:       "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
319:     else
320:       path = File.join @gem_dir, @spec.bindir, bin_file_name
321: 
322:       File.open(path, "rb") do |file|
323:         first_line = file.gets
324:         if first_line =~ /^#!/ then
325:           # Preserve extra words on shebang line, like "-w".  Thanks RPA.
326:           shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}")
327:         else
328:           # Create a plain shebang line.
329:           shebang = "#!#{Gem.ruby}"
330:         end
331: 
332:         shebang.strip # Avoid nasty ^M issues.
333:       end
334:     end
335:   end

Unpacks the gem into the given directory.

[Source]

     # File lib/rubygems/installer.rb, line 191
191:   def unpack(directory)
192:     @gem_dir = directory
193:     @format = Gem::Format.from_file_by_path @gem, @security_policy
194:     extract_files
195:   end

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

[Source]

     # File lib/rubygems/installer.rb, line 368
368:   def windows_stub_script(bindir, bin_file_name)
369:     "@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"
370:   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 204
204:   def write_spec
205:     rubycode = @spec.to_ruby
206: 
207:     file_name = File.join @gem_home, 'specifications',
208:                           "#{@spec.full_name}.gemspec"
209:     file_name.untaint
210: 
211:     File.open(file_name, "w") do |file|
212:       file.puts rubycode
213:     end
214:   end

[Validate]