~ubuntu-branches/ubuntu/trusty/ruby1.9/trusty

« back to all changes in this revision

Viewing changes to lib/rubygems/installer.rb

  • Committer: Bazaar Package Importer
  • Author(s): Stephan Hermann
  • Date: 2008-01-24 11:42:29 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20080124114229-jw2f87rdxlq6gp11
Tags: 1.9.0.0-2ubuntu1
* Merge from debian unstable, remaining changes:
  - Robustify check for target_os, fixing build failure on lpia.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 'pathname'
 
9
require 'rbconfig'
 
10
 
 
11
require 'rubygems/format'
 
12
require 'rubygems/ext'
 
13
require 'rubygems/require_paths_builder'
 
14
 
 
15
##
 
16
# The installer class processes RubyGem .gem files and installs the
 
17
# files contained in the .gem into the Gem.path.
 
18
#
 
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
 
24
 
 
25
  ##
 
26
  # Raised when there is an error while building extensions.
 
27
  #
 
28
  class ExtensionBuildError < Gem::InstallError; end
 
29
 
 
30
  include Gem::UserInteraction
 
31
 
 
32
  include Gem::RequirePathsBuilder
 
33
 
 
34
  class << self
 
35
 
 
36
    attr_writer :exec_format
 
37
 
 
38
    # Defaults to use Ruby's program prefix and suffix.
 
39
    def exec_format
 
40
      @exec_format ||= Gem.default_exec_format
 
41
    end
 
42
 
 
43
  end
 
44
 
 
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.
 
59
  def initialize(gem, options={})
 
60
    @gem = gem
 
61
 
 
62
    options = {
 
63
      :force => false,
 
64
      :install_dir => Gem.dir,
 
65
      :exec_format => false,
 
66
    }.merge options
 
67
 
 
68
    @env_shebang = options[:env_shebang]
 
69
    @force = options[:force]
 
70
    gem_home = options[:install_dir]
 
71
    @gem_home = Pathname.new(gem_home).expand_path
 
72
    @ignore_dependencies = options[:ignore_dependencies]
 
73
    @format_executable = options[:format_executable]
 
74
    @security_policy = options[:security_policy]
 
75
    @wrappers = options[:wrappers]
 
76
 
 
77
    begin
 
78
      @format = Gem::Format.from_file_by_path @gem, @security_policy
 
79
    rescue Gem::Package::FormatError
 
80
      raise Gem::InstallError, "invalid gem format for #{@gem}"
 
81
    end
 
82
 
 
83
    @spec = @format.spec
 
84
 
 
85
    @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
 
86
  end
 
87
 
 
88
  ##
 
89
  # Installs the gem and returns a loaded Gem::Specification for the installed
 
90
  # gem.
 
91
  #
 
92
  # The gem will be installed with the following structure:
 
93
  #
 
94
  #   @gem_home/
 
95
  #     cache/<gem-version>.gem #=> a cached copy of the installed gem
 
96
  #     gems/<gem-version>/... #=> extracted files
 
97
  #     specifications/<gem-version>.gemspec #=> the Gem::Specification
 
98
  def install
 
99
    # If we're forcing the install then disable security unless the security
 
100
    # policy says that we only install singed gems.
 
101
    @security_policy = nil if @force and @security_policy and
 
102
                              not @security_policy.only_signed
 
103
 
 
104
    unless @force then
 
105
      if rrv = @spec.required_ruby_version then
 
106
        unless rrv.satisfied_by? Gem::Version.new(RUBY_VERSION) then
 
107
          raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}"
 
108
        end
 
109
      end
 
110
 
 
111
      if rrgv = @spec.required_rubygems_version then
 
112
        unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then
 
113
          raise Gem::InstallError,
 
114
                "#{@spec.name} requires RubyGems version #{rrgv}"
 
115
        end
 
116
      end
 
117
 
 
118
      unless @ignore_dependencies then
 
119
        @spec.dependencies.each do |dep_gem|
 
120
          ensure_dependency @spec, dep_gem
 
121
        end
 
122
      end
 
123
    end
 
124
 
 
125
    FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
 
126
    raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
 
127
 
 
128
    Gem.ensure_gem_subdirectories @gem_home
 
129
 
 
130
    FileUtils.mkdir_p @gem_dir
 
131
 
 
132
    extract_files
 
133
    generate_bin
 
134
    build_extensions
 
135
    write_spec
 
136
 
 
137
    write_require_paths_file_if_needed
 
138
 
 
139
    # HACK remove?  Isn't this done in multiple places?
 
140
    cached_gem = File.join @gem_home, "cache", @gem.split(/\//).pop
 
141
    unless File.exist? cached_gem then
 
142
      FileUtils.cp @gem, File.join(@gem_home, "cache")
 
143
    end
 
144
 
 
145
    say @spec.post_install_message unless @spec.post_install_message.nil?
 
146
 
 
147
    @spec.loaded_from = File.join(@gem_home, 'specifications',
 
148
                                  "#{@spec.full_name}.gemspec")
 
149
 
 
150
    return @spec
 
151
  rescue Zlib::GzipFile::Error
 
152
    raise Gem::InstallError, "gzip error installing #{@gem}"
 
153
  end
 
154
 
 
155
  ##
 
156
  # Ensure that the dependency is satisfied by the current installation of
 
157
  # gem.  If it is not an exception is raised.
 
158
  #
 
159
  # spec       :: Gem::Specification
 
160
  # dependency :: Gem::Dependency
 
161
  def ensure_dependency(spec, dependency)
 
162
    unless installation_satisfies_dependency? dependency then
 
163
      raise Gem::InstallError, "#{spec.name} requires #{dependency}"
 
164
    end
 
165
 
 
166
    true
 
167
  end
 
168
 
 
169
  ##
 
170
  # True if the current installed gems satisfy the given dependency.
 
171
  #
 
172
  # dependency :: Gem::Dependency
 
173
  def installation_satisfies_dependency?(dependency)
 
174
    current_index = Gem::SourceIndex.from_installed_gems
 
175
    current_index.find_name(dependency.name, dependency.version_requirements).size > 0
 
176
  end
 
177
 
 
178
  ##
 
179
  # Unpacks the gem into the given directory.
 
180
  #
 
181
  def unpack(directory)
 
182
    @gem_dir = directory
 
183
    @format = Gem::Format.from_file_by_path @gem, @security_policy
 
184
    extract_files
 
185
  end
 
186
 
 
187
  ##
 
188
  # Writes the .gemspec specification (in Ruby) to the supplied
 
189
  # spec_path.
 
190
  #
 
191
  # spec:: [Gem::Specification] The Gem specification to output
 
192
  # spec_path:: [String] The location (path) to write the gemspec to
 
193
  #
 
194
  def write_spec
 
195
    rubycode = @spec.to_ruby
 
196
 
 
197
    file_name = File.join @gem_home, 'specifications',
 
198
                          "#{@spec.full_name}.gemspec"
 
199
    file_name.untaint
 
200
 
 
201
    File.open(file_name, "w") do |file|
 
202
      file.puts rubycode
 
203
    end
 
204
  end
 
205
 
 
206
  ##
 
207
  # Creates windows .bat files for easy running of commands
 
208
  #
 
209
  def generate_windows_script(bindir, filename)
 
210
    if Gem.win_platform? then
 
211
      script_name = filename + ".bat"
 
212
      script_path = File.join bindir, File.basename(script_name)
 
213
      File.open script_path, 'w' do |file|
 
214
        file.puts windows_stub_script(bindir, filename)
 
215
      end
 
216
 
 
217
      say script_path if Gem.configuration.really_verbose
 
218
    end
 
219
  end
 
220
 
 
221
  def generate_bin
 
222
    return if @spec.executables.nil? or @spec.executables.empty?
 
223
 
 
224
    # If the user has asked for the gem to be installed in a directory that is
 
225
    # the system gem directory, then use the system bin directory, else create
 
226
    # (or use) a new bin dir under the gem_home.
 
227
    bindir = Gem.bindir @gem_home
 
228
 
 
229
    Dir.mkdir bindir unless File.exist? bindir
 
230
    raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
 
231
 
 
232
    @spec.executables.each do |filename|
 
233
      filename.untaint
 
234
      bin_path = File.expand_path File.join(@gem_dir, @spec.bindir, filename)
 
235
      mode = File.stat(bin_path).mode | 0111
 
236
      File.chmod mode, bin_path
 
237
 
 
238
      if @wrappers then
 
239
        generate_bin_script filename, bindir
 
240
      else
 
241
        generate_bin_symlink filename, bindir
 
242
      end
 
243
    end
 
244
  end
 
245
 
 
246
  ##
 
247
  # Creates the scripts to run the applications in the gem.
 
248
  #--
 
249
  # The Windows script is generated in addition to the regular one due to a
 
250
  # bug or misfeature in the Windows shell's pipe.  See
 
251
  # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
 
252
  #
 
253
  def generate_bin_script(filename, bindir)
 
254
    bin_script_path = File.join bindir, formatted_program_filename(filename)
 
255
 
 
256
    exec_path = File.join @gem_dir, @spec.bindir, filename
 
257
 
 
258
    # HACK some gems don't have #! in their executables, restore 2008/06
 
259
    #if File.read(exec_path, 2) == '#!' then
 
260
      File.open bin_script_path, 'w', 0755 do |file|
 
261
        file.print app_script_text(filename)
 
262
      end
 
263
 
 
264
      say bin_script_path if Gem.configuration.really_verbose
 
265
 
 
266
      generate_windows_script bindir, filename
 
267
    #else
 
268
    #  FileUtils.rm_f bin_script_path
 
269
    #  FileUtils.cp exec_path, bin_script_path,
 
270
    #               :verbose => Gem.configuration.really_verbose
 
271
    #end
 
272
  end
 
273
 
 
274
  ##
 
275
  # Creates the symlinks to run the applications in the gem.  Moves
 
276
  # the symlink if the gem being installed has a newer version.
 
277
  #
 
278
  def generate_bin_symlink(filename, bindir)
 
279
    if Gem.win_platform? then
 
280
      alert_warning "Unable to use symlinks on Windows, installing wrapper"
 
281
      generate_bin_script filename, bindir
 
282
      return
 
283
    end
 
284
 
 
285
    src = File.join @gem_dir, 'bin', filename
 
286
    dst = File.join bindir, formatted_program_filename(filename)
 
287
 
 
288
    if File.exist? dst then
 
289
      if File.symlink? dst then
 
290
        link = File.readlink(dst).split File::SEPARATOR
 
291
        cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
 
292
        return if @spec.version < cur_version
 
293
      end
 
294
      File.unlink dst
 
295
    end
 
296
 
 
297
    FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
 
298
  end
 
299
 
 
300
  ##
 
301
  # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
 
302
  # necessary.
 
303
  def shebang(bin_file_name)
 
304
    if @env_shebang then
 
305
      "#!/usr/bin/env ruby"
 
306
    else
 
307
      path = File.join @gem_dir, @spec.bindir, bin_file_name
 
308
 
 
309
      File.open(path, "rb") do |file|
 
310
        first_line = file.gets
 
311
        if first_line =~ /^#!/ then
 
312
          # Preserve extra words on shebang line, like "-w".  Thanks RPA.
 
313
          shebang = first_line.sub(/\A\#!.*?ruby\S*/, "#!#{Gem.ruby}")
 
314
        else
 
315
          # Create a plain shebang line.
 
316
          shebang = "#!#{Gem.ruby}"
 
317
        end
 
318
 
 
319
        shebang.strip # Avoid nasty ^M issues.
 
320
      end
 
321
    end
 
322
  end
 
323
 
 
324
  # Return the text for an application file.
 
325
  def app_script_text(bin_file_name)
 
326
    <<-TEXT
 
327
#{shebang bin_file_name}
 
328
#
 
329
# This file was generated by RubyGems.
 
330
#
 
331
# The application '#{@spec.name}' is installed as part of a gem, and
 
332
# this file is here to facilitate running it.
 
333
#
 
334
 
 
335
require 'rubygems'
 
336
 
 
337
version = "#{Gem::Requirement.default}"
 
338
 
 
339
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
 
340
  version = $1
 
341
  ARGV.shift
 
342
end
 
343
 
 
344
gem '#{@spec.name}', version
 
345
load '#{bin_file_name}'
 
346
TEXT
 
347
  end
 
348
 
 
349
  # return the stub script text used to launch the true ruby script
 
350
  def windows_stub_script(bindir, bin_file_name)
 
351
    <<-TEXT
 
352
@ECHO OFF
 
353
IF NOT "%~f0" == "~f0" GOTO :WinNT
 
354
@"#{Gem.ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
 
355
GOTO :EOF
 
356
:WinNT
 
357
"%~dp0ruby.exe" "%~dpn0" %*
 
358
TEXT
 
359
  end
 
360
 
 
361
  # Builds extensions.  Valid types of extensions are extconf.rb files,
 
362
  # configure scripts and rakefiles or mkrf_conf files.
 
363
  def build_extensions
 
364
    return if @spec.extensions.empty?
 
365
    say "Building native extensions.  This could take a while..."
 
366
    start_dir = Dir.pwd
 
367
    dest_path = File.join @gem_dir, @spec.require_paths.first
 
368
    ran_rake = false # only run rake once
 
369
 
 
370
    @spec.extensions.each do |extension|
 
371
      break if ran_rake
 
372
      results = []
 
373
 
 
374
      builder = case extension
 
375
                when /extconf/ then
 
376
                  Gem::Ext::ExtConfBuilder
 
377
                when /configure/ then
 
378
                  Gem::Ext::ConfigureBuilder
 
379
                when /rakefile/i, /mkrf_conf/i then
 
380
                  ran_rake = true
 
381
                  Gem::Ext::RakeBuilder
 
382
                else
 
383
                  results = ["No builder for extension '#{extension}'"]
 
384
                  nil
 
385
                end
 
386
 
 
387
      begin
 
388
        Dir.chdir File.join(@gem_dir, File.dirname(extension))
 
389
        results = builder.build(extension, @gem_dir, dest_path, results)
 
390
 
 
391
        say results.join("\n") if Gem.configuration.really_verbose
 
392
 
 
393
      rescue => ex
 
394
        results = results.join "\n"
 
395
 
 
396
        File.open('gem_make.out', 'wb') { |f| f.puts results }
 
397
 
 
398
        message = <<-EOF
 
399
ERROR: Failed to build gem native extension.
 
400
 
 
401
#{results}
 
402
 
 
403
Gem files will remain installed in #{@gem_dir} for inspection.
 
404
Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
 
405
        EOF
 
406
 
 
407
        raise ExtensionBuildError, message
 
408
      ensure
 
409
        Dir.chdir start_dir
 
410
      end
 
411
    end
 
412
  end
 
413
 
 
414
  ##
 
415
  # Reads the file index and extracts each file into the gem directory.
 
416
  #
 
417
  # Ensures that files can't be installed outside the gem directory.
 
418
  def extract_files
 
419
    expand_and_validate_gem_dir
 
420
 
 
421
    raise ArgumentError, "format required to extract from" if @format.nil?
 
422
 
 
423
    @format.file_entries.each do |entry, file_data|
 
424
      path = entry['path'].untaint
 
425
 
 
426
      if path =~ /\A\// then # for extra sanity
 
427
        raise Gem::InstallError,
 
428
              "attempt to install file into #{entry['path'].inspect}"
 
429
      end
 
430
 
 
431
      path = File.expand_path File.join(@gem_dir, path)
 
432
 
 
433
      if path !~ /\A#{Regexp.escape @gem_dir}/ then
 
434
        msg = "attempt to install file into %p under %p" %
 
435
                [entry['path'], @gem_dir]
 
436
        raise Gem::InstallError, msg
 
437
      end
 
438
 
 
439
      FileUtils.mkdir_p File.dirname(path)
 
440
 
 
441
      File.open(path, "wb") do |out|
 
442
        out.write file_data
 
443
      end
 
444
 
 
445
      say path if Gem.configuration.really_verbose
 
446
    end
 
447
  end
 
448
 
 
449
  # Prefix and suffix the program filename the same as ruby.
 
450
  def formatted_program_filename(filename)
 
451
    if @format_executable then
 
452
      self.class.exec_format % File.basename(filename)
 
453
    else
 
454
      filename
 
455
    end
 
456
  end
 
457
 
 
458
  private
 
459
 
 
460
  # HACK Pathname is broken on windows.
 
461
  def absolute_path? pathname
 
462
    pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i)
 
463
  end
 
464
 
 
465
  def expand_and_validate_gem_dir
 
466
    @gem_dir = Pathname.new(@gem_dir).expand_path
 
467
 
 
468
    unless absolute_path?(@gem_dir) then # HACK is this possible after #expand_path?
 
469
      raise ArgumentError, "install directory %p not absolute" % @gem_dir
 
470
    end
 
471
 
 
472
    @gem_dir = @gem_dir.to_s
 
473
  end
 
474
 
 
475
end
 
476