Geminstaller C0 Coverage Information - RCov

spec/fixture/rubygems_dist/rubygems-trunk/lib/rubygems/installer.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
spec/fixture/rubygems_dist/rubygems-trunk/lib/rubygems/installer.rb 541 300
57.86%
41.33%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 #--
2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3 # All rights reserved.
4 # See LICENSE.txt for permissions.
5 #++
6 
7 require 'fileutils'
8 require 'rbconfig'
9 
10 require 'rubygems/format'
11 require 'rubygems/ext'
12 require 'rubygems/require_paths_builder'
13 
14 ##
15 # The installer class processes RubyGem .gem files and installs the files
16 # contained in the .gem into the Gem.path.
17 #
18 # Gem::Installer does the work of putting files in all the right places on the
19 # filesystem including unpacking the gem into its gem dir, installing the
20 # gemspec in the specifications dir, storing the cached gem in the cache dir,
21 # and installing either wrappers or symlinks for executables.
22 #
23 # The installer fires pre and post install hooks.  Hooks can be added either
24 # through a rubygems_plugin.rb file in an installed gem or via a
25 # rubygems/defaults/#{RUBY_ENGINE}.rb or rubygems/defaults/operating_system.rb
26 # file.  See Gem.pre_install and Gem.post_install for details.
27 
28 class Gem::Installer
29 
30   ##
31   # Paths where env(1) might live.  Some systems are broken and have it in
32   # /bin
33 
34   ENV_PATHS = %w[/usr/bin/env /bin/env]
35 
36   ##
37   # Raised when there is an error while building extensions.
38   #
39   class ExtensionBuildError < Gem::InstallError; end
40 
41   include Gem::UserInteraction
42 
43   include Gem::RequirePathsBuilder
44 
45   ##
46   # The directory a gem's executables will be installed into
47 
48   attr_reader :bin_dir
49 
50   ##
51   # The gem repository the gem will be installed into
52 
53   attr_reader :gem_home
54 
55   ##
56   # The Gem::Specification for the gem being installed
57 
58   attr_reader :spec
59 
60   @path_warning = false
61 
62   class << self
63 
64     ##
65     # True if we've warned about PATH not including Gem.bindir
66 
67     attr_accessor :path_warning
68 
69     attr_writer :exec_format
70 
71     # Defaults to use Ruby's program prefix and suffix.
72     def exec_format
73       @exec_format ||= Gem.default_exec_format
74     end
75 
76   end
77 
78   ##
79   # Constructs an Installer instance that will install the gem located at
80   # +gem+.  +options+ is a Hash with the following keys:
81   #
82   # :env_shebang:: Use /usr/bin/env in bin wrappers.
83   # :force:: Overrides all version checks and security policy checks, except
84   #          for a signed-gems-only policy.
85   # :ignore_dependencies:: Don't raise if a dependency is missing.
86   # :install_dir:: The directory to install the gem into.
87   # :format_executable:: Format the executable the same as the ruby executable.
88   #                      If your ruby is ruby18, foo_exec will be installed as
89   #                      foo_exec18.
90   # :security_policy:: Use the specified security policy.  See Gem::Security
91   # :wrappers:: Install wrappers if true, symlinks if false.
92 
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
143 
144   ##
145   # Installs the gem and returns a loaded Gem::Specification for the installed
146   # gem.
147   #
148   # The gem will be installed with the following structure:
149   #
150   #   @gem_home/
151   #     cache/<gem-version>.gem #=> a cached copy of the installed gem
152   #     gems/<gem-version>/... #=> extracted files
153   #     specifications/<gem-version>.gemspec #=> the Gem::Specification
154 
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
223 
224   ##
225   # Ensure that the dependency is satisfied by the current installation of
226   # gem.  If it is not an exception is raised.
227   #
228   # spec       :: Gem::Specification
229   # dependency :: Gem::Dependency
230 
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
238 
239   ##
240   # True if the gems in the source_index satisfy +dependency+.
241 
242   def installation_satisfies_dependency?(dependency)
243     @source_index.find_name(dependency.name, dependency.requirement).size > 0
244   end
245 
246   ##
247   # Unpacks the gem into the given directory.
248 
249   def unpack(directory)
250     @gem_dir = directory
251     @format = Gem::Format.from_file_by_path @gem, @security_policy
252     extract_files
253   end
254 
255   ##
256   # Writes the .gemspec specification (in Ruby) to the gem home's
257   # specifications directory.
258 
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
270 
271   ##
272   # Creates windows .bat files for easy running of commands
273 
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
285 
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
310 
311   ##
312   # Creates the scripts to run the applications in the gem.
313   #--
314   # The Windows script is generated in addition to the regular one due to a
315   # bug or misfeature in the Windows shell's pipe.  See
316   # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
317 
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
340 
341   ##
342   # Creates the symlinks to run the applications in the gem.  Moves
343   # the symlink if the gem being installed has a newer version.
344 
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
366 
367   ##
368   # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
369   # necessary.
370 
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
393 
394   ##
395   # Return the text for an application file.
396 
397   def app_script_text(bin_file_name)
398     <<-TEXT
399 #{shebang bin_file_name}
400 #
401 # This file was generated by RubyGems.
402 #
403 # The application '#{@spec.name}' is installed as part of a gem, and
404 # this file is here to facilitate running it.
405 #
406 
407 require 'rubygems'
408 
409 version = "#{Gem::Requirement.default}"
410 
411 if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
412   version = $1
413   ARGV.shift
414 end
415 
416 gem '#{@spec.name}', version
417 load Gem.bin_path('#{@spec.name}', '#{bin_file_name}', version)
418 TEXT
419   end
420 
421   ##
422   # return the stub script text used to launch the true ruby script
423 
424   def windows_stub_script(bindir, bin_file_name)
425     <<-TEXT
426 @ECHO OFF
427 IF NOT "%~f0" == "~f0" GOTO :WinNT
428 @"#{File.basename(Gem.ruby).chomp('"')}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
429 GOTO :EOF
430 :WinNT
431 @"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
432 TEXT
433   end
434 
435   ##
436   # Builds extensions.  Valid types of extensions are extconf.rb files,
437   # configure scripts and rakefiles or mkrf_conf files.
438 
439   def build_extensions
440     return if @spec.extensions.empty?
441     say "Building native extensions.  This could take a while..."
442     start_dir = Dir.pwd
443     dest_path = File.join @gem_dir, @spec.require_paths.first
444     ran_rake = false # only run rake once
445 
446     @spec.extensions.each do |extension|
447       break if ran_rake
448       results = []
449 
450       builder = case extension
451                 when /extconf/ then
452                   Gem::Ext::ExtConfBuilder
453                 when /configure/ then
454                   Gem::Ext::ConfigureBuilder
455                 when /rakefile/i, /mkrf_conf/i then
456                   ran_rake = true
457                   Gem::Ext::RakeBuilder
458                 else
459                   results = ["No builder for extension '#{extension}'"]
460                   nil
461                 end
462 
463       begin
464         Dir.chdir File.join(@gem_dir, File.dirname(extension))
465         results = builder.build(extension, @gem_dir, dest_path, results)
466 
467         say results.join("\n") if Gem.configuration.really_verbose
468 
469       rescue => ex
470         results = results.join "\n"
471 
472         File.open('gem_make.out', 'wb') { |f| f.puts results }
473 
474         message = <<-EOF
475 ERROR: Failed to build gem native extension.
476 
477 #{results}
478 
479 Gem files will remain installed in #{@gem_dir} for inspection.
480 Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
481         EOF
482 
483         raise ExtensionBuildError, message
484       ensure
485         Dir.chdir start_dir
486       end
487     end
488   end
489 
490   ##
491   # Reads the file index and extracts each file into the gem directory.
492   #
493   # Ensures that files can't be installed outside the gem directory.
494 
495   def extract_files
496     @gem_dir = File.expand_path @gem_dir
497 
498     raise ArgumentError, "format required to extract from" if @format.nil?
499 
500     @format.file_entries.each do |entry, file_data|
501       path = entry['path'].untaint
502 
503       if path =~ /\A\// then # for extra sanity
504         raise Gem::InstallError,
505               "attempt to install file into #{entry['path'].inspect}"
506       end
507 
508       path = File.expand_path File.join(@gem_dir, path)
509 
510       if path !~ /\A#{Regexp.escape @gem_dir}/ then
511         msg = "attempt to install file into %p under %p" %
512                 [entry['path'], @gem_dir]
513         raise Gem::InstallError, msg
514       end
515 
516       FileUtils.rm_rf(path) if File.exists?(path)
517       FileUtils.mkdir_p File.dirname(path)
518 
519       File.open(path, "wb") do |out|
520         out.write file_data
521       end
522 
523       FileUtils.chmod entry['mode'], path
524 
525       say path if Gem.configuration.really_verbose
526     end
527   end
528 
529   ##
530   # Prefix and suffix the program filename the same as ruby.
531 
532   def formatted_program_filename(filename)
533     if @format_executable then
534       self.class.exec_format % File.basename(filename)
535     else
536       filename
537     end
538   end
539 
540 end
541 

Generated on Mon May 10 23:40:28 -0700 2010 with rcov 0.9.8