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

Public Instance methods

Return the text for an application file.

[Source]

     # File lib/rubygems/installer.rb, line 328
328:   def app_script_text(bin_file_name)
329:     "\#{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"
330:   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 368
368:   def build_extensions
369:     return if @spec.extensions.empty?
370:     say "Building native extensions.  This could take a while..."
371:     start_dir = Dir.pwd
372:     dest_path = File.join @gem_dir, @spec.require_paths.first
373:     ran_rake = false # only run rake once
374: 
375:     @spec.extensions.each do |extension|
376:       break if ran_rake
377:       results = []
378: 
379:       builder = case extension
380:                 when /extconf/ then
381:                   Gem::Ext::ExtConfBuilder
382:                 when /configure/ then
383:                   Gem::Ext::ConfigureBuilder
384:                 when /rakefile/i, /mkrf_conf/i then
385:                   ran_rake = true
386:                   Gem::Ext::RakeBuilder
387:                 else
388:                   results = ["No builder for extension '#{extension}'"]
389:                   nil
390:                 end
391: 
392:       begin
393:         Dir.chdir File.join(@gem_dir, File.dirname(extension))
394:         results = builder.build(extension, @gem_dir, dest_path, results)
395: 
396:         say results.join("\n") if Gem.configuration.really_verbose
397: 
398:       rescue => ex
399:         results = results.join "\n"
400: 
401:         File.open('gem_make.out', 'wb') { |f| f.puts results }
402: 
403:         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"
404: 
405:         raise ExtensionBuildError, message
406:       ensure
407:         Dir.chdir start_dir
408:       end
409:     end
410:   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 164
164:   def ensure_dependency(spec, dependency)
165:     unless installation_satisfies_dependency? dependency then
166:       raise Gem::InstallError, "#{spec.name} requires #{dependency}"
167:     end
168: 
169:     true
170:   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 424
424:   def extract_files
425:     expand_and_validate_gem_dir
426: 
427:     raise ArgumentError, "format required to extract from" if @format.nil?
428: 
429:     @format.file_entries.each do |entry, file_data|
430:       path = entry['path'].untaint
431: 
432:       if path =~ /\A\// then # for extra sanity
433:         raise Gem::InstallError,
434:               "attempt to install file into #{entry['path'].inspect}"
435:       end
436: 
437:       path = File.expand_path File.join(@gem_dir, path)
438: 
439:       if path !~ /\A#{Regexp.escape @gem_dir}/ then
440:         msg = "attempt to install file into %p under %p" %
441:                 [entry['path'], @gem_dir]
442:         raise Gem::InstallError, msg
443:       end
444: 
445:       FileUtils.mkdir_p File.dirname(path)
446: 
447:       File.open(path, "wb") do |out|
448:         out.write file_data
449:       end
450: 
451:       say path if Gem.configuration.really_verbose
452:     end
453:   end

Prefix and suffix the program filename the same as ruby.

[Source]

     # File lib/rubygems/installer.rb, line 456
456:   def formatted_program_filename(filename)
457:     if @format_executable then
458:       self.class.exec_format % File.basename(filename)
459:     else
460:       filename
461:     end
462:   end

[Source]

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

Creates the scripts to run the applications in the gem.

[Source]

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

Creates windows .bat files for easy running of commands

[Source]

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

True if the current installed gems satisfy the given dependency.

dependency :Gem::Dependency

[Source]

     # File lib/rubygems/installer.rb, line 176
176:   def installation_satisfies_dependency?(dependency)
177:     current_index = Gem::SourceIndex.from_installed_gems
178:     current_index.find_name(dependency.name, dependency.version_requirements).size > 0
179:   end

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

[Source]

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

Unpacks the gem into the given directory.

[Source]

     # File lib/rubygems/installer.rb, line 184
184:   def unpack(directory)
185:     @gem_dir = directory
186:     @format = Gem::Format.from_file_by_path @gem, @security_policy
187:     extract_files
188:   end

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

[Source]

     # File lib/rubygems/installer.rb, line 354
354:   def windows_stub_script(bindir, bin_file_name)
355:     "@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"
356:   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 197
197:   def write_spec
198:     rubycode = @spec.to_ruby
199: 
200:     file_name = File.join @gem_home, 'specifications',
201:                           "#{@spec.full_name}.gemspec"
202:     file_name.untaint
203: 
204:     File.open(file_name, "w") do |file|
205:       file.puts rubycode
206:     end
207:   end

[Validate]