Class: Gem::Installer

Inherits:
Object
  • Object
show all
Includes:
UserInteraction
Defined in:
lib/rubygems/installer.rb

Overview

The installer class processes RubyGem .gem files and installs the files contained in the .gem into the Gem.path.

Defined Under Namespace

Classes: ExtensionBuildError

Instance Method Summary collapse

Methods included from DefaultUserInteraction

#ui, ui, #ui=, ui=, #use_ui, use_ui

Constructor Details

#initialize(gem, options = {}) ⇒ Installer

Constructs an Installer instance

gem
String

The file name of the gem



37
38
39
40
# File 'lib/rubygems/installer.rb', line 37

def initialize(gem, options={})
  @gem = gem
  @options = options
end

Instance Method Details

#app_script_text(spec, install_dir, filename) ⇒ Object

Return the text for an application file.



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/rubygems/installer.rb', line 271

def app_script_text(spec, install_dir, filename)
  text = <<-TEXT
#{shebang(spec, install_dir, filename)}
#
# This file was generated by RubyGems.
#
# The application '#{spec.name}' is installed as part of a gem, and
# this file is here to facilitate running it. 
#

require 'rubygems'
version = "> 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
  version = $1
  ARGV.shift
end
gem '#{spec.name}', version
load '#{filename}'
TEXT
  text
end

#build_extensions(directory, spec) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/rubygems/installer.rb', line 293

def build_extensions(directory, spec)
  return unless spec.extensions.size > 0
  say "Building native extensions.  This could take a while..."
  start_dir = Dir.pwd
  dest_path = File.join(directory, spec.require_paths[0])
  ran_rake = false # only run rake once

  spec.extensions.each do |extension|
    break if ran_rake
    results = []

    case extension
    when /extconf/ then
      builder = ExtExtConfBuilder
    when /configure/ then
      builder = ExtConfigureBuilder
    when /rakefile/i, /mkrf_conf/i then
      builder = ExtRakeBuilder
      ran_rake = true
    else
      builder = nil
      results = ["No builder for extension '#{extension}'"]
    end

    begin
      Dir.chdir File.join(directory, File.dirname(extension))
      results = builder.build(extension, directory, dest_path, results)
    rescue => ex
      results = results.join "\n"

      File.open('gem_make.out', 'wb') { |f| f.puts results }

      message = <<-EOF
ERROR: Failed to build gem native extension.

#{results}

Gem files will remain installed in #{directory} for inspection.
Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
      EOF

      raise ExtensionBuildError, message
    ensure
      Dir.chdir start_dir
    end
  end
end

#build_support_directories(install_dir) ⇒ Object

Given a root gem directory, build supporting directories for gem if they do not already exist



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/rubygems/installer.rb', line 146

def build_support_directories(install_dir)
   unless File.exist? File.join(install_dir, "specifications")
     FileUtils.mkdir_p File.join(install_dir, "specifications")
   end
   unless File.exist? File.join(install_dir, "cache")
     FileUtils.mkdir_p File.join(install_dir, "cache")
   end
   unless File.exist? File.join(install_dir, "doc")
     FileUtils.mkdir_p File.join(install_dir, "doc")
   end
end

#ensure_dependency!(spec, dependency) ⇒ Object

Ensure that the dependency is satisfied by the current installation of gem. If it is not, then fail (i.e. throw and exception).

spec

Gem::Specification

dependency

Gem::Dependency

Raises:



121
122
123
124
# File 'lib/rubygems/installer.rb', line 121

def ensure_dependency!(spec, dependency)
  raise Gem::InstallError, "#{spec.name} requires #{dependency.name} #{dependency.version_requirements} " unless
    installation_satisfies_dependency?(dependency)
end

#extract_files(directory, format) ⇒ Object

Reads the YAML file index and then extracts each file into the supplied directory, building directories for the extracted files as needed.

directory
String

The root directory to extract files into

file
IO

The IO that contains the file data



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/rubygems/installer.rb', line 349

def extract_files(directory, format)
  unless File.expand_path(directory) == directory then
    raise ArgumentError, "install directory %p not absolute" % directory
  end

  format.file_entries.each do |entry, file_data|
    path = entry['path'].untaint
    if path =~ /\A\// then # for extra sanity
      raise Gem::InstallError,
            "attempt to install file into #{entry['path'].inspect}"
    end
    path = File.expand_path File.join(directory, path)
    if path !~ /\A#{Regexp.escape directory}/ then
      msg = "attempt to install file into %p under %p" %
              [entry['path'], directory]
      raise Gem::InstallError, msg
    end
    FileUtils.mkdir_p File.dirname(path)
    File.open(path, "wb") do |out|
      out.write file_data
    end
  end
end

#generate_bin(spec, install_dir = Gem.dir) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rubygems/installer.rb', line 185

def generate_bin(spec, install_dir=Gem.dir)
  return unless spec.executables && ! spec.executables.empty?
  
  # If the user has asked for the gem to be installed in
  # a directory that is the system gem directory, then
  # use the system bin directory, else create (or use) a
  # new bin dir under the install_dir.
  bindir = Gem.bindir(install_dir)

  Dir.mkdir bindir unless File.exist? bindir
  raise Gem::FilePermissionError.new(bindir) unless File.writable?(bindir)

  spec.executables.each do |filename|
    if @options[:wrappers] then
      generate_bin_script spec, filename, bindir, install_dir
    else
      generate_bin_symlink spec, filename, bindir, install_dir
    end
  end
end

#generate_bin_script(spec, filename, bindir, install_dir) ⇒ Object

Creates the scripts to run the applications in the gem.



209
210
211
212
213
214
# File 'lib/rubygems/installer.rb', line 209

def generate_bin_script(spec, filename, bindir, install_dir)
  File.open(File.join(bindir, File.basename(filename)), "w", 0755) do |file|
    file.print app_script_text(spec, install_dir, filename)
  end
  generate_windows_script bindir, filename
end

Creates the symlinks to run the applications in the gem. Moves the symlink if the gem being installed has a newer version.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/rubygems/installer.rb', line 220

def generate_bin_symlink(spec, filename, bindir, install_dir)
  if Config::CONFIG["arch"] =~ /dos|win32/i then
    warn "Unable to use symlinks on win32, installing wrapper"
    generate_bin_script spec, filename, bindir, install_dir
    return
  end

  src = File.join @directory, 'bin', filename
  dst = File.join bindir, File.basename(filename)

  if File.exist? dst then
    if File.symlink? dst then
      link = File.readlink(dst).split File::SEPARATOR
      cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
      return if spec.version < cur_version
    end
    File.unlink dst
  end

  File.symlink src, dst
end

#generate_windows_script(bindir, filename) ⇒ Object

Creates windows .cmd files for easy running of commands



176
177
178
179
180
181
182
183
# File 'lib/rubygems/installer.rb', line 176

def generate_windows_script(bindir, filename)
  if Config::CONFIG["arch"] =~ /dos|win32/i
    script_name = filename + ".cmd"
    File.open(File.join(bindir, File.basename(script_name)), "w") do |file|
      file.puts "@#{Gem.ruby} \"#{File.join(bindir,filename)}\" %*"
    end
  end
end

#install(force = false, install_dir = Gem.dir, ignore_this_parameter = false) ⇒ Object

Installs the gem in the Gem.path. This will fail (unless force=true) if a Gem has a requirement on another Gem that is not installed. The installation will install in the following structure:

Gem.path/
    specifications/<gem-version>.gemspec #=> the extracted YAML gemspec
    gems/<gem-version>/... #=> the extracted Gem files
    cache/<gem-version>.gem #=> a cached copy of the installed Gem
force
default = false

if false will fail if a required Gem is not

installed, or if the Ruby version is too low for the gem

install_dir
default = Gem.dir

directory that Gem is to be installed in

return
Gem::Specification

The specification for the newly installed

Gem.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rubygems/installer.rb', line 60

def install(force=false, install_dir=Gem.dir, ignore_this_parameter=false)
  # if we're forcing the install, then disable security, _unless_
  # the security policy says that we only install singed gems
  # (this includes Gem::Security::HighSecurity)
  security_policy = @options[:security_policy]
  security_policy = nil if force && security_policy && security_policy.only_signed != true

  begin
    format = Gem::Format.from_file_by_path @gem, security_policy
  rescue Gem::Package::FormatError
    raise Gem::InstallError, "invalid gem format for #{@gem}"
  end
  unless force
    spec = format.spec
    # Check the Ruby version.
    if (rrv = spec.required_ruby_version)
      unless rrv.satisfied_by?(Gem::Version.new(RUBY_VERSION))
        raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}"
      end
    end
    unless @options[:ignore_dependencies]
      spec.dependencies.each do |dep_gem|
        ensure_dependency!(spec, dep_gem)
      end
    end
  end

  raise Gem::FilePermissionError.new(install_dir) unless File.writable?(install_dir)

  # Build spec dir.
  @directory = File.join(install_dir, "gems", format.spec.full_name).untaint
  FileUtils.mkdir_p @directory

  extract_files(@directory, format)
  generate_bin(format.spec, install_dir)
  build_extensions(@directory, format.spec)

  # Build spec/cache/doc dir.
  build_support_directories(install_dir)

  # Write the spec and cache files.
  write_spec(format.spec, File.join(install_dir, "specifications"))
  unless File.exist? File.join(install_dir, "cache", @gem.split(/\//).pop)
    FileUtils.cp @gem, File.join(install_dir, "cache")
  end

  puts format.spec.post_install_message unless format.spec.post_install_message.nil?

  format.spec.loaded_from = File.join(install_dir, 'specifications', format.spec.full_name+".gemspec")
  return format.spec
rescue Zlib::GzipFile::Error
  raise InstallError, "gzip error installing #{@gem}"
end

#installation_satisfies_dependency?(dependency) ⇒ Boolean

True if the current installed gems satisfy the given dependency.

dependency

Gem::Dependency

Returns:

  • (Boolean)


130
131
132
133
# File 'lib/rubygems/installer.rb', line 130

def installation_satisfies_dependency?(dependency)
  current_index = SourceIndex.from_installed_gems
  current_index.find_name(dependency.name, dependency.version_requirements).size > 0
end

#shebang(spec, install_dir, bin_file_name) ⇒ Object



242
243
244
245
246
247
248
# File 'lib/rubygems/installer.rb', line 242

def shebang(spec, install_dir, bin_file_name)
  if @options[:env_shebang]
    shebang_env
  else
    shebang_default(spec, install_dir, bin_file_name)
  end
end

#shebang_default(spec, install_dir, bin_file_name) ⇒ Object



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/rubygems/installer.rb', line 250

def shebang_default(spec, install_dir, bin_file_name)
  path = File.join(install_dir, "gems", spec.full_name, spec.bindir, bin_file_name)
  File.open(path, "rb") do |file|
    first_line = file.readlines("\n").first 
    path_to_ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
    if first_line =~ /^#!/
      # Preserve extra words on shebang line, like "-w".  Thanks RPA.
      shebang = first_line.sub(/\A\#!\s*\S*ruby\S*/, "#!" + path_to_ruby)
    else
      # Create a plain shebang line.
      shebang = "#!" + path_to_ruby
    end
    return shebang.strip  # Avoid nasty ^M issues.
  end
end

#shebang_envObject



266
267
268
# File 'lib/rubygems/installer.rb', line 266

def shebang_env
  return "#!/usr/bin/env ruby"
end

#unpack(directory) ⇒ Object

Unpacks the gem into the given directory.



138
139
140
141
# File 'lib/rubygems/installer.rb', line 138

def unpack(directory)
  format = Gem::Format.from_file_by_path(@gem, @options[:security_policy])
  extract_files(directory, format)
end

#write_spec(spec, spec_path) ⇒ Object

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



165
166
167
168
169
170
171
# File 'lib/rubygems/installer.rb', line 165

def write_spec(spec, spec_path)
  rubycode = spec.to_ruby
  file_name = File.join(spec_path, spec.full_name+".gemspec").untaint
  File.open(file_name, "w") do |file|
    file.puts rubycode
  end
end