Tag unstable CGI specs.
[rbx.git] / lib / rubygems / installer.rb
blobae699a90a0e0943cadf66eaa29cf7b09d6efb562
1 #--
2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3 # All rights reserved.
4 # See LICENSE.txt for permissions.
5 #++
7 require 'fileutils'
8 require 'pathname'
9 require 'rbconfig'
11 require 'rubygems/format'
12 require 'rubygems/ext'
13 require 'rubygems/require_paths_builder'
16 # The installer class processes RubyGem .gem files and installs the
17 # files contained in the .gem into the Gem.path.
19 # Gem::Installer does the work of putting files in all the right places on the
20 # filesystem including unpacking the gem into its gem dir, installing the
21 # gemspec in the specifications dir, storing the cached gem in the cache dir,
22 # and installing either wrappers or symlinks for executables.
23 class Gem::Installer
25   ##
26   # Raised when there is an error while building extensions.
27   #
28   class ExtensionBuildError < Gem::InstallError; end
30   include Gem::UserInteraction
32   include Gem::RequirePathsBuilder
34   class << self
36     attr_writer :exec_format
38     # Defaults to use Ruby's program prefix and suffix.
39     def exec_format
40       @exec_format ||= Gem.default_exec_format
41     end
43   end
45   ##
46   # Constructs an Installer instance that will install the gem located at
47   # +gem+.  +options+ is a Hash with the following keys:
48   #
49   # :env_shebang:: Use /usr/bin/env in bin wrappers.
50   # :force:: Overrides all version checks and security policy checks, except
51   #          for a signed-gems-only policy.
52   # :ignore_dependencies:: Don't raise if a dependency is missing.
53   # :install_dir:: The directory to install the gem into.
54   # :format_executable:: Format the executable the same as the ruby executable.
55   #                      If your ruby is ruby18, foo_exec will be installed as
56   #                      foo_exec18.
57   # :security_policy:: Use the specified security policy.  See Gem::Security
58   # :wrappers:: Install wrappers if true, symlinks if false.
60   def initialize(gem, options={})
61     @gem = gem
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
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]
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
88     @spec = @format.spec
90     @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
91   end
93   ##
94   # Installs the gem and returns a loaded Gem::Specification for the installed
95   # gem.
96   #
97   # The gem will be installed with the following structure:
98   #
99   #   @gem_home/
100   #     cache/<gem-version>.gem #=> a cached copy of the installed gem
101   #     gems/<gem-version>/... #=> extracted files
102   #     specifications/<gem-version>.gemspec #=> the Gem::Specification
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
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
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
124       unless @ignore_dependencies then
125         deps = @spec.runtime_dependencies
126         deps |= @spec.development_dependencies if @development
128         deps.each do |dep_gem|
129           ensure_dependency @spec, dep_gem
130         end
131       end
132     end
134     FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
135     raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
137     Gem.ensure_gem_subdirectories @gem_home
139     FileUtils.mkdir_p @gem_dir
141     extract_files
142     generate_bin
143     build_extensions
144     write_spec
146     write_require_paths_file_if_needed
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
154     say @spec.post_install_message unless @spec.post_install_message.nil?
156     @spec.loaded_from = File.join(@gem_home, 'specifications',
157                                   "#{@spec.full_name}.gemspec")
159     Gem.source_index.add_spec @spec
161     return @spec
162   rescue Zlib::GzipFile::Error
163     raise Gem::InstallError, "gzip error installing #{@gem}"
164   end
166   ##
167   # Ensure that the dependency is satisfied by the current installation of
168   # gem.  If it is not an exception is raised.
169   #
170   # spec       :: Gem::Specification
171   # dependency :: Gem::Dependency
173   def ensure_dependency(spec, dependency)
174     unless installation_satisfies_dependency? dependency then
175       raise Gem::InstallError, "#{spec.name} requires #{dependency}"
176     end
178     true
179   end
181   ##
182   # True if the gems in Gem.source_index satisfy +dependency+.
184   def installation_satisfies_dependency?(dependency)
185     Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
186   end
188   ##
189   # Unpacks the gem into the given directory.
191   def unpack(directory)
192     @gem_dir = directory
193     @format = Gem::Format.from_file_by_path @gem, @security_policy
194     extract_files
195   end
197   ##
198   # Writes the .gemspec specification (in Ruby) to the supplied
199   # spec_path.
200   #
201   # spec:: [Gem::Specification] The Gem specification to output
202   # spec_path:: [String] The location (path) to write the gemspec to
204   def write_spec
205     rubycode = @spec.to_ruby
207     file_name = File.join @gem_home, 'specifications',
208                           "#{@spec.full_name}.gemspec"
209     file_name.untaint
211     File.open(file_name, "w") do |file|
212       file.puts rubycode
213     end
214   end
216   ##
217   # Creates windows .bat files for easy running of commands
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
227       say script_path if Gem.configuration.really_verbose
228     end
229   end
231   def generate_bin
232     return if @spec.executables.nil? or @spec.executables.empty?
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)
239     Dir.mkdir bindir unless File.exist? bindir
240     raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
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
248       if @wrappers then
249         generate_bin_script filename, bindir
250       else
251         generate_bin_symlink filename, bindir
252       end
253     end
254   end
256   ##
257   # Creates the scripts to run the applications in the gem.
258   #--
259   # The Windows script is generated in addition to the regular one due to a
260   # bug or misfeature in the Windows shell's pipe.  See
261   # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
263   def generate_bin_script(filename, bindir)
264     bin_script_path = File.join bindir, formatted_program_filename(filename)
266     exec_path = File.join @gem_dir, @spec.bindir, filename
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
272       File.open bin_script_path, 'w', 0755 do |file|
273         file.print app_script_text(filename)
274       end
276       say bin_script_path if Gem.configuration.really_verbose
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
286   ##
287   # Creates the symlinks to run the applications in the gem.  Moves
288   # the symlink if the gem being installed has a newer version.
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
297     src = File.join @gem_dir, 'bin', filename
298     dst = File.join bindir, formatted_program_filename(filename)
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
309     FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
310   end
312   ##
313   # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
314   # necessary.
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
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
332         shebang.strip # Avoid nasty ^M issues.
333       end
334     end
335   end
337   ##
338   # Return the text for an application file.
340   def app_script_text(bin_file_name)
341     <<-TEXT
342 #{shebang bin_file_name}
344 # This file was generated by RubyGems.
346 # The application '#{@spec.name}' is installed as part of a gem, and
347 # this file is here to facilitate running it.
350 require 'rubygems'
352 version = "#{Gem::Requirement.default}"
354 if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
355   version = $1
356   ARGV.shift
359 gem '#{@spec.name}', version
360 load '#{bin_file_name}'
361 TEXT
362   end
364   ##
365   # return the stub script text used to launch the true ruby script
367   def windows_stub_script(bindir, bin_file_name)
368     <<-TEXT
369 @ECHO OFF
370 IF NOT "%~f0" == "~f0" GOTO :WinNT
371 @"#{File.basename(Gem.ruby)}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
372 GOTO :EOF
373 :WinNT
374 @"#{File.basename(Gem.ruby)}" "%~dpn0" %*
375 TEXT
376   end
378   ##
379   # Builds extensions.  Valid types of extensions are extconf.rb files,
380   # configure scripts and rakefiles or mkrf_conf files.
382   def build_extensions
383     return if @spec.extensions.empty?
384     say "Building native extensions.  This could take a while..."
385     start_dir = Dir.pwd
386     dest_path = File.join @gem_dir, @spec.require_paths.first
387     ran_rake = false # only run rake once
389     @spec.extensions.each do |extension|
390       break if ran_rake
391       results = []
393       builder = case extension
394                 when /extconf/ then
395                   Gem::Ext::ExtConfBuilder
396                 when /configure/ then
397                   Gem::Ext::ConfigureBuilder
398                 when /rakefile/i, /mkrf_conf/i then
399                   ran_rake = true
400                   Gem::Ext::RakeBuilder
401                 else
402                   results = ["No builder for extension '#{extension}'"]
403                   nil
404                 end
406       begin
407         Dir.chdir File.join(@gem_dir, File.dirname(extension))
408         results = builder.build(extension, @gem_dir, dest_path, results)
410         say results.join("\n") if Gem.configuration.really_verbose
412       rescue => ex
413         results = results.join "\n"
415         File.open('gem_make.out', 'wb') { |f| f.puts results }
417         message = <<-EOF
418 ERROR: Failed to build gem native extension.
420 #{results}
422 Gem files will remain installed in #{@gem_dir} for inspection.
423 Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
424         EOF
426         raise ExtensionBuildError, message
427       ensure
428         Dir.chdir start_dir
429       end
430     end
431   end
433   ##
434   # Reads the file index and extracts each file into the gem directory.
435   #
436   # Ensures that files can't be installed outside the gem directory.
438   def extract_files
439     expand_and_validate_gem_dir
441     raise ArgumentError, "format required to extract from" if @format.nil?
443     @format.file_entries.each do |entry, file_data|
444       path = entry['path'].untaint
446       if path =~ /\A\// then # for extra sanity
447         raise Gem::InstallError,
448               "attempt to install file into #{entry['path'].inspect}"
449       end
451       path = File.expand_path File.join(@gem_dir, path)
453       if path !~ /\A#{Regexp.escape @gem_dir}/ then
454         msg = "attempt to install file into %p under %p" %
455                 [entry['path'], @gem_dir]
456         raise Gem::InstallError, msg
457       end
459       FileUtils.mkdir_p File.dirname(path)
461       File.open(path, "wb") do |out|
462         out.write file_data
463       end
465       FileUtils.chmod entry['mode'], path
467       say path if Gem.configuration.really_verbose
468     end
469   end
471   ##
472   # Prefix and suffix the program filename the same as ruby.
474   def formatted_program_filename(filename)
475     if @format_executable then
476       self.class.exec_format % File.basename(filename)
477     else
478       filename
479     end
480   end
482   private
484   ##
485   # HACK Pathname is broken on windows.
487   def absolute_path? pathname
488     pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i)
489   end
491   def expand_and_validate_gem_dir
492     @gem_dir = Pathname.new(@gem_dir).expand_path
494     unless absolute_path?(@gem_dir) then # HACK is this possible after #expand_path?
495       raise ArgumentError, "install directory %p not absolute" % @gem_dir
496     end
498     @gem_dir = @gem_dir.to_s
499   end