2
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
4
# See LICENSE.txt for permissions.
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.
26
# Raised when there is an error while building extensions.
28
class ExtensionBuildError < Gem::InstallError; end
30
include Gem::UserInteraction
32
include Gem::RequirePathsBuilder
36
attr_writer :exec_format
38
# Defaults to use Ruby's program prefix and suffix.
40
@exec_format ||= Gem.default_exec_format
46
# Constructs an Installer instance that will install the gem located at
47
# +gem+. +options+ is a Hash with the following keys:
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
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={})
64
:install_dir => Gem.dir,
65
:exec_format => false,
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]
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}"
85
@gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
89
# Installs the gem and returns a loaded Gem::Specification for the installed
92
# The gem will be installed with the following structure:
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
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
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}"
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}"
118
unless @ignore_dependencies then
119
@spec.dependencies.each do |dep_gem|
120
ensure_dependency @spec, dep_gem
125
FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
126
raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
128
Gem.ensure_gem_subdirectories @gem_home
130
FileUtils.mkdir_p @gem_dir
137
write_require_paths_file_if_needed
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")
145
say @spec.post_install_message unless @spec.post_install_message.nil?
147
@spec.loaded_from = File.join(@gem_home, 'specifications',
148
"#{@spec.full_name}.gemspec")
151
rescue Zlib::GzipFile::Error
152
raise Gem::InstallError, "gzip error installing #{@gem}"
156
# Ensure that the dependency is satisfied by the current installation of
157
# gem. If it is not an exception is raised.
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}"
170
# True if the current installed gems satisfy the given dependency.
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
179
# Unpacks the gem into the given directory.
181
def unpack(directory)
183
@format = Gem::Format.from_file_by_path @gem, @security_policy
188
# Writes the .gemspec specification (in Ruby) to the supplied
191
# spec:: [Gem::Specification] The Gem specification to output
192
# spec_path:: [String] The location (path) to write the gemspec to
195
rubycode = @spec.to_ruby
197
file_name = File.join @gem_home, 'specifications',
198
"#{@spec.full_name}.gemspec"
201
File.open(file_name, "w") do |file|
207
# Creates windows .bat files for easy running of commands
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)
217
say script_path if Gem.configuration.really_verbose
222
return if @spec.executables.nil? or @spec.executables.empty?
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
229
Dir.mkdir bindir unless File.exist? bindir
230
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
232
@spec.executables.each do |filename|
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
239
generate_bin_script filename, bindir
241
generate_bin_symlink filename, bindir
247
# Creates the scripts to run the applications in the gem.
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
253
def generate_bin_script(filename, bindir)
254
bin_script_path = File.join bindir, formatted_program_filename(filename)
256
exec_path = File.join @gem_dir, @spec.bindir, filename
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)
264
say bin_script_path if Gem.configuration.really_verbose
266
generate_windows_script bindir, filename
268
# FileUtils.rm_f bin_script_path
269
# FileUtils.cp exec_path, bin_script_path,
270
# :verbose => Gem.configuration.really_verbose
275
# Creates the symlinks to run the applications in the gem. Moves
276
# the symlink if the gem being installed has a newer version.
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
285
src = File.join @gem_dir, 'bin', filename
286
dst = File.join bindir, formatted_program_filename(filename)
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
297
FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
301
# Generates a #! line for +bin_file_name+'s wrapper copying arguments if
303
def shebang(bin_file_name)
305
"#!/usr/bin/env ruby"
307
path = File.join @gem_dir, @spec.bindir, bin_file_name
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}")
315
# Create a plain shebang line.
316
shebang = "#!#{Gem.ruby}"
319
shebang.strip # Avoid nasty ^M issues.
324
# Return the text for an application file.
325
def app_script_text(bin_file_name)
327
#{shebang bin_file_name}
329
# This file was generated by RubyGems.
331
# The application '#{@spec.name}' is installed as part of a gem, and
332
# this file is here to facilitate running it.
337
version = "#{Gem::Requirement.default}"
339
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
344
gem '#{@spec.name}', version
345
load '#{bin_file_name}'
349
# return the stub script text used to launch the true ruby script
350
def windows_stub_script(bindir, bin_file_name)
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
357
"%~dp0ruby.exe" "%~dpn0" %*
361
# Builds extensions. Valid types of extensions are extconf.rb files,
362
# configure scripts and rakefiles or mkrf_conf files.
364
return if @spec.extensions.empty?
365
say "Building native extensions. This could take a while..."
367
dest_path = File.join @gem_dir, @spec.require_paths.first
368
ran_rake = false # only run rake once
370
@spec.extensions.each do |extension|
374
builder = case extension
376
Gem::Ext::ExtConfBuilder
377
when /configure/ then
378
Gem::Ext::ConfigureBuilder
379
when /rakefile/i, /mkrf_conf/i then
381
Gem::Ext::RakeBuilder
383
results = ["No builder for extension '#{extension}'"]
388
Dir.chdir File.join(@gem_dir, File.dirname(extension))
389
results = builder.build(extension, @gem_dir, dest_path, results)
391
say results.join("\n") if Gem.configuration.really_verbose
394
results = results.join "\n"
396
File.open('gem_make.out', 'wb') { |f| f.puts results }
399
ERROR: Failed to build gem native extension.
403
Gem files will remain installed in #{@gem_dir} for inspection.
404
Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
407
raise ExtensionBuildError, message
415
# Reads the file index and extracts each file into the gem directory.
417
# Ensures that files can't be installed outside the gem directory.
419
expand_and_validate_gem_dir
421
raise ArgumentError, "format required to extract from" if @format.nil?
423
@format.file_entries.each do |entry, file_data|
424
path = entry['path'].untaint
426
if path =~ /\A\// then # for extra sanity
427
raise Gem::InstallError,
428
"attempt to install file into #{entry['path'].inspect}"
431
path = File.expand_path File.join(@gem_dir, path)
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
439
FileUtils.mkdir_p File.dirname(path)
441
File.open(path, "wb") do |out|
445
say path if Gem.configuration.really_verbose
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)
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)
465
def expand_and_validate_gem_dir
466
@gem_dir = Pathname.new(@gem_dir).expand_path
468
unless absolute_path?(@gem_dir) then # HACK is this possible after #expand_path?
469
raise ArgumentError, "install directory %p not absolute" % @gem_dir
472
@gem_dir = @gem_dir.to_s