Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into truffle-head
Browse files Browse the repository at this point in the history
eregon committed Mar 9, 2016

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents be26a3a + dc83f5d commit 88e39cf
Showing 66 changed files with 908 additions and 2,488 deletions.
50 changes: 0 additions & 50 deletions lib/ruby/stdlib/gauntlet_rubygems.rb

This file was deleted.

51 changes: 17 additions & 34 deletions lib/ruby/stdlib/rubygems.rb
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
require 'thread'

module Gem
VERSION = '2.5.1'
VERSION = '2.4.8'
end

# Must be first since it unloads the prelude from 1.9.2
@@ -26,12 +26,12 @@ module Gem
# For user documentation, see:
#
# * <tt>gem help</tt> and <tt>gem help [command]</tt>
# * {RubyGems User Guide}[http://guides.rubygems.org/]
# * {Frequently Asked Questions}[http://guides.rubygems.org/faqs]
# * {RubyGems User Guide}[http://docs.rubygems.org/read/book/1]
# * {Frequently Asked Questions}[http://docs.rubygems.org/read/book/3]
#
# For gem developer documentation see:
#
# * {Creating Gems}[http://guides.rubygems.org/make-your-own-gem]
# * {Creating Gems}[http://docs.rubygems.org/read/chapter/5]
# * Gem::Specification
# * Gem::Version for version dependency notes
#
@@ -156,7 +156,6 @@ module Gem
@@win_platform = nil

@configuration = nil
@gemdeps = nil
@loaded_specs = {}
LOADED_SPECS_MUTEX = Mutex.new
@path_to_default_spec_map = {}
@@ -185,9 +184,13 @@ def self.try_activate path
# or if it was ambiguous (and thus unresolved) the code in our custom
# require will try to activate the more specific version.

spec = Gem::Specification.find_by_path path
return false unless spec
return true if spec.activated?
spec = Gem::Specification.find_inactive_by_path path

unless spec
spec = Gem::Specification.find_by_path path
return true if spec && spec.activated?
return false
end

begin
spec.activate
@@ -307,10 +310,11 @@ def self.configuration=(config)
# package is not available as a gem, return nil.

def self.datadir(gem_name)
# TODO: deprecate
# TODO: deprecate and move to Gem::Specification
# and drop the extra ", gem_name" which is uselessly redundant
spec = @loaded_specs[gem_name]
return nil if spec.nil?
spec.datadir
File.join spec.full_gem_path, "data", gem_name
end

##
@@ -429,7 +433,7 @@ def self.find_files(glob, check_load_path=true)

files = find_files_from_load_path glob if check_load_path

files.concat Gem::Specification.stubs.map { |spec|
files.concat Gem::Specification.map { |spec|
spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
}.flatten

@@ -576,10 +580,6 @@ def self.host= host
# gem's paths are inserted before site lib directory by default.

def self.load_path_insert_index
$LOAD_PATH.each_with_index do |path, i|
return i if path.instance_variable_defined?(:@gem_prelude_index)
end

index = $LOAD_PATH.index RbConfig::CONFIG['sitelibdir']

index
@@ -596,9 +596,6 @@ def self.load_yaml

test_syck = ENV['TEST_SYCK']

# Only Ruby 1.8 and 1.9 have syck
test_syck = false unless /^1\./ =~ RUBY_VERSION

unless test_syck
begin
gem 'psych', '>= 1.2.1'
@@ -780,14 +777,6 @@ def self.read_binary(path)
open path, 'rb' do |f|
f.read
end
rescue Errno::ENOLCK # NFS
if Thread.main != Thread.current
raise
else
open path, 'rb' do |f|
f.read
end
end
end

##
@@ -1063,7 +1052,7 @@ def self.use_gemdeps path = nil
end

rs = Gem::RequestSet.new
@gemdeps = rs.load_gemdeps path
rs.load_gemdeps path

rs.resolve_current.map do |s|
sp = s.full_spec
@@ -1093,12 +1082,6 @@ class << self

attr_reader :loaded_specs

##
# GemDependencyAPI object, which is set when .use_gemdeps is called.
# This contains all the information from the Gemfile.

attr_reader :gemdeps

##
# Register a Gem::Specification for default gem.
#
@@ -1213,7 +1196,6 @@ def clear_default_specs
autoload :DependencyList, 'rubygems/dependency_list'
autoload :DependencyResolver, 'rubygems/resolver'
autoload :Installer, 'rubygems/installer'
autoload :Licenses, 'rubygems/util/licenses'
autoload :PathSupport, 'rubygems/path_support'
autoload :Platform, 'rubygems/platform'
autoload :RequestSet, 'rubygems/request_set'
@@ -1260,3 +1242,4 @@ def clear_default_specs
require 'rubygems/core_ext/kernel_require'

Gem.use_gemdeps

135 changes: 48 additions & 87 deletions lib/ruby/stdlib/rubygems/basic_specification.rb
Original file line number Diff line number Diff line change
@@ -22,29 +22,17 @@ class Gem::BasicSpecification
##
# The path this gemspec was loaded from. This attribute is not persisted.

attr_accessor :loaded_from
attr_reader :loaded_from

##
# Allows correct activation of git: and path: gems.

attr_writer :full_gem_path # :nodoc:

def initialize
internal_init
end

def self.default_specifications_dir
File.join(Gem.default_dir, "specifications", "default")
end

##
# The path to the gem.build_complete file within the extension install
# directory.

def gem_build_complete_path # :nodoc:
File.join extension_dir, 'gem.build_complete'
end

##
# True when the gem has been activated

@@ -58,24 +46,39 @@ def activated?
# eg: /usr/local/lib/ruby/gems/1.8

def base_dir
raise NotImplementedError
return Gem.dir unless loaded_from
@base_dir ||= if default_gem? then
File.dirname File.dirname File.dirname loaded_from
else
File.dirname File.dirname loaded_from
end
end

##
# Return true if this spec can require +file+.

def contains_requirable_file? file
if @ignored then
return false
elsif missing_extensions? then
@ignored = true

warn "Ignoring #{full_name} because its extensions are not built. " +
"Try: gem pristine #{name} --version #{version}"
return false
end
@contains_requirable_file ||= {}
@contains_requirable_file[file] ||=
begin
if instance_variable_defined?(:@ignored) then
return false
elsif missing_extensions? then
@ignored = true

warn "Ignoring #{full_name} because its extensions are not built. " +
"Try: gem pristine #{name} --version #{version}"
return false
end

have_file? file, Gem.suffixes
suffixes = Gem.suffixes

full_require_paths.any? do |dir|
base = "#{dir}/#{file}"
suffixes.any? { |suf| File.file? "#{base}#{suf}" }
end
end ? :yes : :no
@contains_requirable_file[file] == :yes
end

def default_gem?
@@ -87,7 +90,7 @@ def default_gem?
# Returns full path to the directory where gem's extensions are installed.

def extension_dir
@extension_dir ||= File.expand_path(File.join(extensions_dir, full_name)).untaint
@extension_dir ||= File.expand_path File.join(extensions_dir, full_name)
end

##
@@ -103,7 +106,7 @@ def find_full_gem_path # :nodoc:
# TODO: also, shouldn't it default to full_name if it hasn't been written?
path = File.expand_path File.join(gems_dir, full_name)
path.untaint
path
path if File.directory? path
end

private :find_full_gem_path
@@ -138,23 +141,15 @@ def full_require_paths
@full_require_paths ||=
begin
full_paths = raw_require_paths.map do |path|
File.join full_gem_path, path.untaint
File.join full_gem_path, path
end

full_paths << extension_dir if have_extensions?
full_paths << extension_dir unless @extensions.nil? || @extensions.empty?

full_paths
end
end

##
# The path to the data directory for this gem.

def datadir
# TODO: drop the extra ", gem_name" which is uselessly redundant
File.expand_path(File.join(gems_dir, full_name, "data", name)).untaint
end

##
# Full path of the target library file.
# If the file is not in this gem, return nil.
@@ -166,8 +161,8 @@ def to_fullpath path
begin
fullpath = nil
suffixes = Gem.suffixes
suffixes.find do |suf|
full_require_paths.find do |dir|
full_require_paths.find do |dir|
suffixes.find do |suf|
File.file?(fullpath = "#{dir}/#{path}#{suf}")
end
end ? fullpath : nil
@@ -190,15 +185,23 @@ def gem_dir
# gem directory. eg: /usr/local/lib/ruby/1.8/gems

def gems_dir
raise NotImplementedError
# TODO: this logic seems terribly broken, but tests fail if just base_dir
@gems_dir ||= File.join(loaded_from && base_dir || Gem.dir, "gems")
end

def internal_init # :nodoc:
##
# Set the path the Specification was loaded from. +path+ is converted to a
# String.

def loaded_from= path
@loaded_from = path && path.to_s

@extension_dir = nil
@extensions_dir = nil
@full_gem_path = nil
@gem_dir = nil
@ignored = nil
@gems_dir = nil
@base_dir = nil
end

##
@@ -216,7 +219,7 @@ def platform
end

def raw_require_paths # :nodoc:
raise NotImplementedError
Array(@require_paths)
end

##
@@ -237,7 +240,7 @@ def raw_require_paths # :nodoc:
# spec.require_path = '.'

def require_paths
return raw_require_paths unless have_extensions?
return raw_require_paths if @extensions.nil? || @extensions.empty?

[extension_dir].concat raw_require_paths
end
@@ -249,8 +252,8 @@ def require_paths
def source_paths
paths = raw_require_paths.dup

if have_extensions? then
ext_dirs = extensions.map do |extension|
if @extensions then
ext_dirs = @extensions.map do |extension|
extension.split(File::SEPARATOR, 2).first
end.uniq

@@ -260,30 +263,6 @@ def source_paths
paths.uniq
end

##
# Return all files in this gem that match for +glob+.

def matches_for_glob glob # TODO: rename?
# TODO: do we need these?? Kill it
glob = File.join(self.lib_dirs_glob, glob)

Dir[glob].map { |f| f.untaint } # FIX our tests are broken, run w/ SAFE=1
end

##
# Returns a string usable in Dir.glob to match all requirable paths
# for this spec.

def lib_dirs_glob
dirs = if self.require_paths.size > 1 then
"{#{self.require_paths.join(',')}}"
else
self.require_paths.first
end

"#{self.full_gem_path}/#{dirs}".untaint
end

##
# Return a Gem::Specification from this gem

@@ -306,23 +285,5 @@ def stubbed?
raise NotImplementedError
end

private

def have_extensions?; !extensions.empty?; end

def have_file? file, suffixes
return true if raw_require_paths.any? do |path|
base = File.join(gems_dir, full_name, path.untaint, file).untaint
suffixes.any? { |suf| File.file? base + suf }
end

if have_extensions?
base = File.join extension_dir, file
suffixes.any? { |suf| File.file? base + suf }
else
false
end
end

end

40 changes: 15 additions & 25 deletions lib/ruby/stdlib/rubygems/commands/dependency_command.rb
Original file line number Diff line number Diff line change
@@ -61,24 +61,27 @@ def fetch_remote_specs dependency # :nodoc:
ss.map { |spec, _| spec }
end

def fetch_specs name_pattern, dependency # :nodoc:
def fetch_specs dependency # :nodoc:
specs = []

if local?
specs.concat Gem::Specification.stubs.find_all { |spec|
name_pattern =~ spec.name and
dependency.requirement.satisfied_by? spec.version
}.map(&:to_spec)
end

specs.concat dependency.matching_specs if local?
specs.concat fetch_remote_specs dependency if remote?

ensure_specs specs

specs.uniq.sort
end

def gem_dependency pattern, version, prerelease # :nodoc:
def gem_dependency args, version, prerelease # :nodoc:
args << '' if args.empty?

pattern = if args.length == 1 and args.first =~ /\A\/(.*)\/(i)?\z/m then
flags = $2 ? Regexp::IGNORECASE : nil
Regexp.new $1, flags
else
/\A#{Regexp.union(*args)}/
end

dependency = Gem::Deprecate.skip_during {
Gem::Dependency.new pattern, version
}
@@ -118,12 +121,10 @@ def display_readable specs, reverse # :nodoc:
def execute
ensure_local_only_reverse_dependencies

pattern = name_pattern options[:args]

dependency =
gem_dependency pattern, options[:version], options[:prerelease]
gem_dependency options[:args], options[:version], options[:prerelease]

specs = fetch_specs pattern, dependency
specs = fetch_specs dependency

reverse = reverse_dependencies specs

@@ -202,16 +203,5 @@ def find_reverse_dependencies spec # :nodoc:
result
end

private

def name_pattern args
args << '' if args.empty?

if args.length == 1 and args.first =~ /\A\/(.*)\/(i)?\z/m then
flags = $2 ? Regexp::IGNORECASE : nil
Regexp.new $1, flags
else
/\A#{Regexp.union(*args)}/
end
end
end

2 changes: 0 additions & 2 deletions lib/ruby/stdlib/rubygems/commands/environment_command.rb
Original file line number Diff line number Diff line change
@@ -113,8 +113,6 @@ def show_environment # :nodoc:

out << " - INSTALLATION DIRECTORY: #{Gem.dir}\n"

out << " - USER INSTALLATION DIRECTORY: #{Gem.user_dir}\n"

out << " - RUBYGEMS PREFIX: #{Gem.prefix}\n" unless Gem.prefix.nil?

out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
10 changes: 10 additions & 0 deletions lib/ruby/stdlib/rubygems/commands/help_command.rb
Original file line number Diff line number Diff line change
@@ -370,5 +370,15 @@ def show_command_help command_name # :nodoc:
end
end

def show_help # :nodoc:
command = @command_manager[options[:help]]
if command then
# help with provided command
command.invoke("--help")
else
alert_error "Unknown command #{options[:help]}. Try 'gem help commands'"
end
end

end

2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems/commands/install_command.rb
Original file line number Diff line number Diff line change
@@ -275,7 +275,7 @@ def install_gem_without_dependencies name, req # :nodoc:
gem = fetcher.download_to_cache dependency
end

inst = Gem::Installer.at gem, options
inst = Gem::Installer.new gem, options
inst.install

require 'rubygems/dependency_installer'
4 changes: 2 additions & 2 deletions lib/ruby/stdlib/rubygems/commands/list_command.rb
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@

##
# An alternate to Gem::Commands::QueryCommand that searches for gems starting
# with the supplied argument.
# with the the supplied argument.

class Gem::Commands::ListCommand < Gem::Commands::QueryCommand

@@ -33,7 +33,7 @@ def description # :nodoc:
end

def usage # :nodoc:
"#{program_name} [REGEXP ...]"
"#{program_name} [STRING ...]"
end

end
12 changes: 1 addition & 11 deletions lib/ruby/stdlib/rubygems/commands/pristine_command.rb
Original file line number Diff line number Diff line change
@@ -21,11 +21,6 @@ def initialize
options[:all] = value
end

add_option('--skip=gem_name',
'used on --all, skip if name == gem_name') do |value, options|
options[:skip] = value
end

add_option('--[no-]extensions',
'Restore gems with extensions',
'in addition to regular gems') do |value, options|
@@ -114,11 +109,6 @@ def execute
next
end

if spec.name == options[:skip]
say "Skipped #{spec.full_name}, it was given through options"
next
end

if spec.bundled_gem_in_old_ruby?
say "Skipped #{spec.full_name}, it is bundled with old Ruby"
next
@@ -156,7 +146,7 @@ def execute
install_defaults.to_s['--env-shebang']
end

installer = Gem::Installer.at(gem,
installer = Gem::Installer.new(gem,
:wrappers => true,
:force => true,
:install_dir => spec.base_dir,
2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems/commands/query_command.rb
Original file line number Diff line number Diff line change
@@ -227,7 +227,7 @@ def entry_details entry, detail_tuple, specs, platforms

name_tuple, spec = detail_tuple

spec = spec.fetch_spec name_tuple if spec.respond_to? :fetch_spec
spec = spec.fetch_spec name_tuple unless Gem::Specification === spec

entry << "\n"

2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems/commands/sources_command.rb
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ def description # :nodoc:
RubyGems fetches gems from the sources you have configured (stored in your
~/.gemrc).
The default source is https://rubygems.org, but you may have other sources
The default source is https://rubygems.org, but you may have older sources
configured. This guide will help you update your sources or configure
yourself to use your own gem server.
4 changes: 2 additions & 2 deletions lib/ruby/stdlib/rubygems/commands/update_command.rb
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ def initialize
end

def arguments # :nodoc:
"GEMNAME name of gem to update"
"REGEXP regexp to search for in gem name"
end

def defaults_str # :nodoc:
@@ -64,7 +64,7 @@ def description # :nodoc:
end

def usage # :nodoc:
"#{program_name} GEMNAME [GEMNAME ...]"
"#{program_name} REGEXP [REGEXP ...]"
end

def check_latest_rubygems version # :nodoc:
8 changes: 4 additions & 4 deletions lib/ruby/stdlib/rubygems/config_file.rb
Original file line number Diff line number Diff line change
@@ -321,12 +321,12 @@ def rubygems_api_key= api_key
@rubygems_api_key = api_key
end

YAMLErrors = [ArgumentError]
YAMLErrors << Psych::SyntaxError if defined?(Psych::SyntaxError)

def load_file(filename)
Gem.load_yaml

yaml_errors = [ArgumentError]
yaml_errors << Psych::SyntaxError if defined?(Psych::SyntaxError)

return {} unless filename and File.exist? filename

begin
@@ -336,7 +336,7 @@ def load_file(filename)
return {}
end
return content
rescue *yaml_errors => e
rescue *YAMLErrors => e
warn "Failed to load #{filename}, #{e}"
rescue Errno::EACCES
warn "Failed to load #{filename} due to permissions problem."
8 changes: 5 additions & 3 deletions lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb
Original file line number Diff line number Diff line change
@@ -60,11 +60,13 @@ def require path
#--
# TODO request access to the C implementation of this to speed up RubyGems

spec = Gem::Specification.find_active_stub_by_path path
spec = Gem::Specification.stubs.find { |s|
s.activated? and s.contains_requirable_file? path
}

begin
RUBYGEMS_ACTIVATION_MONITOR.exit
return gem_original_require(path)
return gem_original_require(spec.to_fullpath(path) || path)
end if spec

# Attempt to find +path+ in any unresolved gems...
@@ -103,7 +105,7 @@ def require path

# Ok, now find a gem that has no conflicts, starting
# at the highest version.
valid = found_specs.reject { |s| s.has_conflicts? }.last
valid = found_specs.select { |s| s.conflicts.empty? }.last

unless valid then
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
15 changes: 6 additions & 9 deletions lib/ruby/stdlib/rubygems/dependency.rb
Original file line number Diff line number Diff line change
@@ -164,10 +164,6 @@ def type
@type ||= :runtime
end

def runtime?
@type == :runtime || !@type
end

def == other # :nodoc:
Gem::Dependency === other &&
self.name == other.name &&
@@ -274,13 +270,14 @@ def merge other
end

def matching_specs platform_only = false
matches = Gem::Specification.stubs_for(name).find_all { |spec|
requirement.satisfied_by? spec.version
matches = Gem::Specification.stubs.find_all { |spec|
self.name === spec.name and # TODO: == instead of ===
requirement.satisfied_by? spec.version
}.map(&:to_spec)

if platform_only
matches.reject! { |spec|
spec.nil? || !Gem::Platform.match(spec.platform)
not Gem::Platform.match spec.platform
}
end

@@ -326,11 +323,11 @@ def to_specs
def to_spec
matches = self.to_specs

active = matches.find { |spec| spec && spec.activated? }
active = matches.find { |spec| spec.activated? }

return active if active

matches.delete_if { |spec| spec.nil? || spec.version.prerelease? } unless prerelease?
matches.delete_if { |spec| spec.version.prerelease? } unless prerelease?

matches.last
end
3 changes: 0 additions & 3 deletions lib/ruby/stdlib/rubygems/dependency_list.rb
Original file line number Diff line number Diff line change
@@ -141,9 +141,6 @@ def why_not_ok? quick = false
def ok_to_remove?(full_name, check_dev=true)
gem_to_remove = find_name full_name

# If the state is inconsistent, at least don't crash
return true unless gem_to_remove

siblings = @specs.find_all { |s|
s.name == gem_to_remove.name &&
s.full_name != gem_to_remove.full_name
2 changes: 0 additions & 2 deletions lib/ruby/stdlib/rubygems/ext/builder.rb
Original file line number Diff line number Diff line change
@@ -66,11 +66,9 @@ def self.run(command, results, command_name = nil)
# TODO use Process.spawn when ruby 1.8 support is dropped.
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
if verbose
puts("current directory: #{Dir.pwd}")
puts(command)
system(command)
else
results << "current directory: #{Dir.pwd}"
results << command
results << `#{command} #{redirector}`
end
7 changes: 1 addition & 6 deletions lib/ruby/stdlib/rubygems/ext/ext_conf_builder.rb
Original file line number Diff line number Diff line change
@@ -35,12 +35,7 @@ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
begin
run cmd, results
ensure
if File.exist? 'mkmf.log'
results << "To see why this extension failed to compile, please check" \
" the mkmf.log which can be found here:\n"
results << " " + File.join(dest_path, 'mkmf.log') + "\n"
FileUtils.mv 'mkmf.log', dest_path
end
FileUtils.mv 'mkmf.log', dest_path if File.exist? 'mkmf.log'
siteconf.unlink
end

117 changes: 91 additions & 26 deletions lib/ruby/stdlib/rubygems/indexer.rb
Original file line number Diff line number Diff line change
@@ -93,30 +93,48 @@ def initialize(directory, options = {})
end

##
# Build various indices
# Abbreviate the spec for downloading. Abbreviated specs are only used for
# searching, downloading and related activities and do not need deployment
# specific information (e.g. list of files). So we abbreviate the spec,
# making it much smaller for quicker downloads.
#--
# TODO move to Gem::Specification

def abbreviate(spec)
spec.files = []
spec.test_files = []
spec.rdoc_options = []
spec.extra_rdoc_files = []
spec.cert_chain = []
spec
end

##
# Build various indicies

def build_indicies
Gem::Specification.dirs = []
Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list))

def build_indices
specs = map_gems_to_specs gem_file_list
Gem::Specification._resort! specs
build_marshal_gemspecs specs
build_modern_indices specs if @build_modern
build_marshal_gemspecs
build_modern_indicies if @build_modern

compress_indices
compress_indicies
end

##
# Builds Marshal quick index gemspecs.

def build_marshal_gemspecs specs
count = specs.count
def build_marshal_gemspecs
count = Gem::Specification.count { |s| not s.default_gem? }
progress = ui.progress_reporter count,
"Generating Marshal quick index gemspecs for #{count} gems",
"Complete"

files = []

Gem.time 'Generated Marshal quick index gemspecs' do
specs.each do |spec|
Gem::Specification.each do |spec|
next if spec.default_gem?
spec_file_name = "#{spec.original_name}.gemspec.rz"
marshal_name = File.join @quick_marshal_dir, spec_file_name
@@ -168,14 +186,16 @@ def build_modern_index(index, file, name)
end

##
# Builds indices for RubyGems 1.2 and newer. Handles full, latest, prerelease
# Builds indicies for RubyGems 1.2 and newer. Handles full, latest, prerelease

def build_modern_indicies
specs = Gem::Specification.reject { |s| s.default_gem? }

def build_modern_indices specs
prerelease, released = specs.partition { |s|
s.version.prerelease?
}
latest_specs =
Gem::Specification._latest_specs specs
Gem::Specification.latest_specs.reject { |s| s.default_gem? }

build_modern_index(released.sort, @specs_index, 'specs')
build_modern_index(latest_specs.sort, @latest_specs_index, 'latest specs')
@@ -201,8 +221,18 @@ def map_gems_to_specs gems
spec = Gem::Package.new(gemfile).spec
spec.loaded_from = gemfile

spec.abbreviate
spec.sanitize
# HACK: fuck this shit - borks all tests that use pl1
# if File.basename(gemfile, ".gem") != spec.original_name then
# exp = spec.full_name
# exp << " (#{spec.original_name})" if
# spec.original_name != spec.full_name
# msg = "Skipping misnamed gem: #{gemfile} should be named #{exp}"
# alert_warning msg
# next
# end

abbreviate spec
sanitize spec

spec
rescue SignalException
@@ -218,14 +248,14 @@ def map_gems_to_specs gems
end

##
# Compresses indices on disk
# Compresses indicies on disk
#--
# All future files should be compressed using gzip, not deflate

def compress_indices
say "Compressing indices"
def compress_indicies
say "Compressing indicies"

Gem.time 'Compressed indices' do
Gem.time 'Compressed indicies' do
if @build_modern then
gzip @specs_index
gzip @latest_specs_index
@@ -273,12 +303,12 @@ def gem_file_list
end

##
# Builds and installs indices.
# Builds and installs indicies.

def generate_index
make_temp_directories
build_indices
install_indices
build_indicies
install_indicies
rescue SignalException
ensure
FileUtils.rm_rf @directory
@@ -294,9 +324,9 @@ def gzip(filename)
end

##
# Install generated indices into the destination directory.
# Install generated indicies into the destination directory.

def install_indices
def install_indicies
verbose = Gem.configuration.really_verbose

say "Moving index into production dir #{@dest_directory}" if verbose
@@ -350,6 +380,38 @@ def paranoid(path, extension)
end
end

##
# Sanitize the descriptive fields in the spec. Sometimes non-ASCII
# characters will garble the site index. Non-ASCII characters will
# be replaced by their XML entity equivalent.

def sanitize(spec)
spec.summary = sanitize_string(spec.summary)
spec.description = sanitize_string(spec.description)
spec.post_install_message = sanitize_string(spec.post_install_message)
spec.authors = spec.authors.collect { |a| sanitize_string(a) }

spec
end

##
# Sanitize a single string.

def sanitize_string(string)
return string unless string

# HACK the #to_s is in here because RSpec has an Array of Arrays of
# Strings for authors. Need a way to disallow bad values on gemspec
# generation. (Probably won't happen.)
string = string.to_s

begin
Builder::XChar.encode string
rescue NameError, NoMethodError
string.to_xs
end
end

##
# Perform an in-place update of the repository from newly added gems.

@@ -373,7 +435,10 @@ def update_index
specs = map_gems_to_specs updated_gems
prerelease, released = specs.partition { |s| s.version.prerelease? }

files = build_marshal_gemspecs specs
Gem::Specification.dirs = []
Gem::Specification.add_specs(*specs)

files = build_marshal_gemspecs

Gem.time 'Updated indexes' do
update_specs_index released, @dest_specs_index, @specs_index
@@ -383,7 +448,7 @@ def update_index
@prerelease_specs_index)
end

compress_indices
compress_indicies

verbose = Gem.configuration.really_verbose

88 changes: 28 additions & 60 deletions lib/ruby/stdlib/rubygems/installer.rb
Original file line number Diff line number Diff line change
@@ -61,6 +61,11 @@ class Gem::Installer

attr_reader :options

##
# Sets the specification for .gem-less installs.

attr_writer :spec

@path_warning = false

@install_lock = Mutex.new
@@ -94,46 +99,6 @@ def exec_format

end

##
# Construct an installer object for the gem file located at +path+

def self.at path, options = {}
security_policy = options[:security_policy]
package = Gem::Package.new path, security_policy
new package, options
end

class FakePackage
attr_accessor :spec

def initialize(spec)
@spec = spec
end

def extract_files destination_dir, pattern = '*'
FileUtils.mkdir_p destination_dir

spec.files.each do |file|
file = File.join destination_dir, file
next if File.exist? file
FileUtils.mkdir_p File.dirname(file)
File.open file, 'w' do |fp| fp.puts "# #{file}" end
end
end

def copy_to path
end
end

##
# Construct an installer object for an ephemeral gem (one where we don't
# actually have a .gem file, just a spec)

def self.for_spec spec, options = {}
# FIXME: we should have a real Package class for this
new FakePackage.new(spec), options
end

##
# Constructs an Installer instance that will install the gem located at
# +gem+. +options+ is a Hash with the following keys:
@@ -157,22 +122,17 @@ def self.for_spec spec, options = {}
# :build_args:: An Array of arguments to pass to the extension builder
# process. If not set, then Gem::Command.build_args is used

def initialize(package, options={})
def initialize(gem, options={})
require 'fileutils'

@gem = gem
@options = options
if package.is_a? String
security_policy = options[:security_policy]
@package = Gem::Package.new package, security_policy
if $VERBOSE
warn "constructing an Installer object with a string is deprecated. Please use Gem::Installer.at (called from: #{caller.first})"
end
else
@package = package
end
@package = Gem::Package.new @gem

process_options

@package.security_policy = @security_policy

if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
@@ -251,7 +211,7 @@ def gem_dir
# Lazy accessor for the installer's spec.

def spec
@package.spec
@spec ||= @package.spec
rescue Gem::Package::Error => e
raise Gem::InstallError, "invalid gem: #{e.message}"
end
@@ -270,7 +230,7 @@ def spec
def install
pre_install_checks

FileUtils.rm_f File.join gem_home, 'specifications', spec.spec_name
FileUtils.rm_f File.join gem_home, 'specifications', @spec.spec_name

run_pre_install_hooks

@@ -279,12 +239,12 @@ def install

FileUtils.mkdir_p gem_dir

spec.loaded_from = spec_file

if @options[:install_as_default]
spec.loaded_from = default_spec_file
extract_bin
write_default_spec
else
spec.loaded_from = spec_file
extract_files

build_extensions
@@ -298,7 +258,7 @@ def install

say spec.post_install_message unless spec.post_install_message.nil?

Gem::Installer.install_lock.synchronize { Gem::Specification.reset }
Gem::Installer.install_lock.synchronize { Gem::Specification.add_spec spec }

run_post_install_hooks

@@ -391,19 +351,19 @@ def unpack(directory)
end

##
# The location of the spec file that is installed.
# The location of of the spec file that is installed.
#

def spec_file
File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
end

##
# The location of the default spec file for default gems.
# The location of of the default spec file for default gems.
#

def default_spec_file
File.join Gem::Specification.default_specifications_dir, "#{spec.full_name}.gemspec"
File.join gem_home, "specifications/default", "#{spec.full_name}.gemspec"
end

##
@@ -635,6 +595,7 @@ def process_options # :nodoc:
@gem_home = options[:install_dir] || Gem.dir
@ignore_dependencies = options[:ignore_dependencies]
@format_executable = options[:format_executable]
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
@only_install_dir = options[:only_install_dir]

@@ -700,7 +661,7 @@ def app_script_text(bin_file_name)
require 'rubygems'
version = "#{Gem::Requirement.default}.a"
version = "#{Gem::Requirement.default}"
if ARGV.first
str = ARGV.first
@@ -803,6 +764,11 @@ def dir
def pre_install_checks
verify_gem_home options[:unpack]

# If we're forcing the install then disable security unless the security
# policy says that we only install signed gems.
@security_policy = nil if
@force and @security_policy and not @security_policy.only_signed

ensure_loadable_spec

if options[:install_as_default]
@@ -845,7 +811,9 @@ def write_build_info_file

def write_cache_file
cache_file = File.join gem_home, 'cache', spec.file_name
@package.copy_to cache_file

FileUtils.cp @gem, cache_file unless File.exist? cache_file
end

end

4 changes: 2 additions & 2 deletions lib/ruby/stdlib/rubygems/installer_test_case.rb
Original file line number Diff line number Diff line change
@@ -176,15 +176,15 @@ def util_setup_gem(ui = @ui) # HACK fix use_ui to make this automatic
end
end

@installer = Gem::Installer.at @gem
@installer = Gem::Installer.new @gem
end

##
# Creates an installer for +spec+ that will install into +gem_home+. If
# +user+ is true a user-install will be performed.

def util_installer(spec, gem_home, user=false)
Gem::Installer.at(spec.cache_file,
Gem::Installer.new(spec.cache_file,
:install_dir => gem_home,
:user_install => user)
end
24 changes: 6 additions & 18 deletions lib/ruby/stdlib/rubygems/package.rb
Original file line number Diff line number Diff line change
@@ -123,7 +123,7 @@ def self.build spec, skip_validation=false
# If +gem+ is an existing file in the old format a Gem::Package::Old will be
# returned.

def self.new gem, security_policy = nil
def self.new gem
gem = if gem.is_a?(Gem::Package::Source)
gem
elsif gem.respond_to? :read
@@ -132,7 +132,7 @@ def self.new gem, security_policy = nil
Gem::Package::FileSource.new gem
end

return super unless Gem::Package == self
return super(gem) unless Gem::Package == self
return super unless gem.present?

return super unless gem.start
@@ -144,27 +144,20 @@ def self.new gem, security_policy = nil
##
# Creates a new package that will read or write to the file +gem+.

def initialize gem, security_policy # :notnew:
def initialize gem # :notnew:
@gem = gem

@build_time = Time.now
@checksums = {}
@contents = nil
@digests = Hash.new { |h, algorithm| h[algorithm] = {} }
@files = nil
@security_policy = security_policy
@security_policy = nil
@signatures = {}
@signer = nil
@spec = nil
end

##
# Copies this package to +path+ (if possible)

def copy_to path
FileUtils.cp @gem.path, path unless File.exist? path
end

##
# Adds a checksum for each entry in the gem to checksums.yaml.gz.

@@ -207,11 +200,7 @@ def add_contents tar # :nodoc:

def add_files tar # :nodoc:
@spec.files.each do |file|
stat = File.lstat file

if stat.symlink?
tar.add_symlink file, File.readlink(file), stat.mode
end
stat = File.stat file

next unless stat.file?

@@ -382,8 +371,6 @@ def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
FileUtils.chmod entry.header.mode, destination
end if entry.file?

File.symlink(install_location(entry.header.linkname, destination_dir), destination) if entry.symlink?

verbose destination
end
end
@@ -624,3 +611,4 @@ def verify_gz entry # :nodoc:
require 'rubygems/package/tar_reader'
require 'rubygems/package/tar_reader/entry'
require 'rubygems/package/tar_writer'

4 changes: 2 additions & 2 deletions lib/ruby/stdlib/rubygems/package/old.rb
Original file line number Diff line number Diff line change
@@ -18,14 +18,14 @@ class Gem::Package::Old < Gem::Package
# Creates a new old-format package reader for +gem+. Old-format packages
# cannot be written.

def initialize gem, security_policy
def initialize gem
require 'fileutils'
require 'zlib'
Gem.load_yaml

@contents = nil
@gem = gem
@security_policy = security_policy
@security_policy = nil
@spec = nil
end

8 changes: 1 addition & 7 deletions lib/ruby/stdlib/rubygems/package/tar_reader/entry.rb
Original file line number Diff line number Diff line change
@@ -102,13 +102,6 @@ def file?
@header.typeflag == "0"
end

##
# Is this tar entry a symlink?

def symlink?
@header.typeflag == "2"
end

##
# The position in the tar entry

@@ -151,3 +144,4 @@ def rewind
end

end

15 changes: 3 additions & 12 deletions lib/ruby/stdlib/rubygems/package/tar_test_case.rb
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ def calc_checksum(header)
SP(Z(to_oct(sum, 6)))
end

def header(type, fname, dname, length, mode, mtime, checksum = nil, linkname = "")
def header(type, fname, dname, length, mode, mtime, checksum = nil)
checksum ||= " " * 8

arr = [ # struct tarfile_entry_posix
@@ -83,7 +83,7 @@ def header(type, fname, dname, length, mode, mtime, checksum = nil, linkname = "
Z(to_oct(mtime, 11)), # char mtime[12]; 0 padded, octal, null
checksum, # char checksum[8]; 0 padded, octal, null, space
type, # char typeflag[1]; file: "0" dir: "5"
ASCIIZ(linkname, 100), # char linkname[100]; ASCII + (Z unless filled)
"\0" * 100, # char linkname[100]; ASCII + (Z unless filled)
"ustar\0", # char magic[6]; "ustar\0"
"00", # char version[2]; "00"
ASCIIZ("wheel", 32), # char uname[32]; ASCIIZ
@@ -117,12 +117,6 @@ def tar_file_header(fname, dname, mode, length, mtime)
header("0", fname, dname, length, mode, mtime, checksum)
end

def tar_symlink_header(fname, prefix, mode, mtime, linkname)
h = header("2", fname, prefix, 0, mode, mtime, nil, linkname)
checksum = calc_checksum(h)
header("2", fname, prefix, 0, mode, mtime, checksum, linkname)
end

def to_oct(n, pad_size)
"%0#{pad_size}o" % n
end
@@ -139,8 +133,5 @@ def util_dir_entry
util_entry tar_dir_header("foo", "bar", 0, Time.now)
end

def util_symlink_entry
util_entry tar_symlink_header("foo", "bar", 0, Time.now, "link")
end

end

20 changes: 1 addition & 19 deletions lib/ruby/stdlib/rubygems/package/tar_writer.rb
Original file line number Diff line number Diff line change
@@ -233,25 +233,6 @@ def add_file_simple(name, mode, size) # :yields: io
self
end

##
# Adds symlink +name+ with permissions +mode+, linking to +target+.

def add_symlink(name, target, mode)
check_closed

name, prefix = split_name name

header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
:size => 0, :typeflag => "2",
:linkname => target,
:prefix => prefix,
:mtime => Time.now).to_s

@io.write header

self
end

##
# Raises IOError if the TarWriter is closed

@@ -342,3 +323,4 @@ def split_name(name) # :nodoc:
end

end

5 changes: 2 additions & 3 deletions lib/ruby/stdlib/rubygems/platform.rb
Original file line number Diff line number Diff line change
@@ -95,7 +95,6 @@ def initialize(arch)
[os, version]
when /netbsdelf/ then [ 'netbsdelf', nil ]
when /openbsd(\d+\.\d+)?/ then [ 'openbsd', $1 ]
when /bitrig(\d+\.\d+)?/ then [ 'bitrig', $1 ]
when /solaris(\d+\.\d+)?/ then [ 'solaris', $1 ]
# test
when /^(\w+_platform)(\d+)?/ then [ $1, $2 ]
@@ -148,8 +147,8 @@ def ===(other)
return nil unless Gem::Platform === other

# cpu
([nil,'universal'].include?(@cpu) or [nil, 'universal'].include?(other.cpu) or @cpu == other.cpu or
(@cpu == 'arm' and other.cpu =~ /\Aarm/)) and
(@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu or
(@cpu == 'arm' and other.cpu =~ /\Aarm/)) and

# os
@os == other.os and
3 changes: 2 additions & 1 deletion lib/ruby/stdlib/rubygems/rdoc.rb
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
require 'rdoc/rubygems_hook'
loaded_hook = true
module Gem
RDoc = ::RDoc::RubygemsHook
RDoc = RDoc::RubygemsHook
end
rescue LoadError
end
@@ -332,3 +332,4 @@ def setup
end unless loaded_hook

Gem.done_installing(&Gem::RDoc.method(:generation_hook))

31 changes: 6 additions & 25 deletions lib/ruby/stdlib/rubygems/remote_fetcher.rb
Original file line number Diff line number Diff line change
@@ -51,8 +51,6 @@ def self.fetcher
@fetcher ||= self.new Gem.configuration[:http_proxy]
end

attr_accessor :headers

##
# Initialize a remote fetcher using the source URI and possible proxy
# information.
@@ -66,11 +64,8 @@ def self.fetcher
#
# +dns+: An object to use for DNS resolution of the API endpoint.
# By default, use Resolv::DNS.
#
# +headers+: A set of additional HTTP headers to be sent to the server when
# fetching the gem.

def initialize(proxy=nil, dns=Resolv::DNS.new, headers={})
def initialize(proxy=nil, dns=Resolv::DNS.new)
require 'net/http'
require 'stringio'
require 'time'
@@ -84,7 +79,6 @@ def initialize(proxy=nil, dns=Resolv::DNS.new, headers={})
@cert_files = Gem::Request.get_cert_files

@dns = dns
@headers = headers
end

##
@@ -97,8 +91,7 @@ def api_endpoint(uri)
begin
res = @dns.getresource "_rubygems._tcp.#{host}",
Resolv::DNS::Resource::IN::SRV
rescue Resolv::ResolvError => e
verbose "Getting SRV record failed: #{e}"
rescue Resolv::ResolvError
uri
else
target = res.target.to_s.strip
@@ -241,9 +234,7 @@ def fetch_file uri, *_

def fetch_http uri, last_modified = nil, head = false, depth = 0
fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
response = request uri, fetch_type, last_modified do |req|
headers.each { |k,v| req.add_field(k,v) }
end
response = request uri, fetch_type, last_modified

case response
when Net::HTTPOK, Net::HTTPNotModified then
@@ -321,19 +312,9 @@ def cache_update_path uri, path = nil, update = true
end

if update and path
begin
open(path, 'wb') do |io|
io.flock(File::LOCK_EX)
io.write data
end
rescue Errno::ENOLCK # NFS
if Thread.main != Thread.current
raise
else
open(path, 'wb') do |io|
io.write data
end
end
open(path, 'wb') do |io|
io.flock(File::LOCK_EX)
io.write data
end
end

5 changes: 1 addition & 4 deletions lib/ruby/stdlib/rubygems/request.rb
Original file line number Diff line number Diff line change
@@ -185,10 +185,6 @@ def perform_request request # :nodoc:

bad_response = true
retry
rescue Net::HTTPFatalError
verbose "fatal error"

raise Gem::RemoteFetcher::FetchError.new('fatal error', @uri)
# HACK work around EOFError bug in Net::HTTP
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
# to install gems.
@@ -245,3 +241,4 @@ def user_agent
require 'rubygems/request/http_pool'
require 'rubygems/request/https_pool'
require 'rubygems/request/connection_pools'

12 changes: 4 additions & 8 deletions lib/ruby/stdlib/rubygems/request/connection_pools.rb
Original file line number Diff line number Diff line change
@@ -61,22 +61,18 @@ def no_proxy? host, env_no_proxy
end

def net_http_args uri, proxy_uri
# URI::Generic#hostname was added in ruby 1.9.3, use it if exists, otherwise
# don't support IPv6 literals and use host.
hostname = uri.respond_to?(:hostname) ? uri.hostname : uri.host
net_http_args = [hostname, uri.port]
net_http_args = [uri.host, uri.port]

no_proxy = get_no_proxy_from_env

if proxy_uri and not no_proxy?(hostname, no_proxy) then
proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host
if proxy_uri and not no_proxy?(uri.host, no_proxy) then
net_http_args + [
proxy_hostname,
proxy_uri.host,
proxy_uri.port,
Gem::UriFormatter.new(proxy_uri.user).unescape,
Gem::UriFormatter.new(proxy_uri.password).unescape,
]
elsif no_proxy? hostname, no_proxy then
elsif no_proxy? uri.host, no_proxy then
net_http_args + [nil, nil]
else
net_http_args
7 changes: 4 additions & 3 deletions lib/ruby/stdlib/rubygems/request_set.rb
Original file line number Diff line number Diff line change
@@ -159,13 +159,16 @@ def install options, &block # :yields: request, installer

path = req.download cache_dir

inst = Gem::Installer.at path, options
inst = Gem::Installer.new path, options

yield req, inst if block_given?

requests << inst.install
end

requests
ensure
raise if $!
return requests if options[:gemdeps]

specs = requests.map do |request|
@@ -184,8 +187,6 @@ def install options, &block # :yields: request, installer
Gem.done_installing_hooks.each do |hook|
hook.call inst, specs
end unless Gem.done_installing_hooks.empty?

requests
end

##
4 changes: 2 additions & 2 deletions lib/ruby/stdlib/rubygems/request_set/gem_dependency_api.rb
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ class Gem::RequestSet::GemDependencyAPI
##
# A Hash containing gem names and files to require from those gems.

attr_reader :requires
attr_reader :requires # :nodoc:

##
# A set of gems that are loaded via the +:path+ option to #gem
@@ -396,7 +396,7 @@ def gem name, *requirements
##
# Handles the git: option from +options+ for gem +name+.
#
# Returns +true+ if the gist or git option was handled.
# Returns +true+ if the path option was handled.

def gem_git name, options # :nodoc:
if gist = options.delete(:gist) then
2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems/request_set/lockfile.rb
Original file line number Diff line number Diff line change
@@ -185,7 +185,7 @@ def add_PLATFORMS out # :nodoc:

platforms = platforms.sort_by { |platform| platform.to_s }

platforms.each do |platform|
platforms.sort.each do |platform|
out << " #{platform}"
end

97 changes: 43 additions & 54 deletions lib/ruby/stdlib/rubygems/request_set/lockfile/parser.rb
Original file line number Diff line number Diff line change
@@ -11,13 +11,13 @@ def initialize tokenizer, set, platforms, filename = nil

def parse
until @tokens.empty? do
token = get
type, data, column, line = get

case token.type
case type
when :section then
@tokens.skip :newline

case token.value
case data
when 'DEPENDENCIES' then
parse_DEPENDENCIES
when 'GIT' then
@@ -29,10 +29,10 @@ def parse
when 'PLATFORMS' then
parse_PLATFORMS
else
token = get until @tokens.empty? or peek.first == :section
type, = get until @tokens.empty? or peek.first == :section
end
else
raise "BUG: unhandled token #{token.type} (#{token.value.inspect}) at line #{token.line} column #{token.column}"
raise "BUG: unhandled token #{type} (#{data.inspect}) at line #{line} column #{column}"
end
end
end
@@ -41,51 +41,53 @@ def parse
# Gets the next token for a Lockfile

def get expected_types = nil, expected_value = nil # :nodoc:
token = @tokens.shift
current_token = @tokens.shift

if expected_types and not Array(expected_types).include? token.type then
unget token
type, value, column, line = current_token

message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
if expected_types and not Array(expected_types).include? type then
unget current_token

message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
"expected #{expected_types.inspect}"

raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
raise Gem::RequestSet::Lockfile::ParseError.new message, column, line, @filename
end

if expected_value and expected_value != token.value then
unget token
if expected_value and expected_value != value then
unget current_token

message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
"expected [#{expected_types.inspect}, " +
"#{expected_value.inspect}]"

raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
raise Gem::RequestSet::Lockfile::ParseError.new message, column, line, @filename
end

token
current_token
end

def parse_DEPENDENCIES # :nodoc:
while not @tokens.empty? and :text == peek.type do
token = get :text
while not @tokens.empty? and :text == peek.first do
_, name, = get :text

requirements = []

case peek[0]
when :bang then
get :bang

requirements << pinned_requirement(token.value)
requirements << pinned_requirement(name)
when :l_paren then
get :l_paren

loop do
op = get(:requirement).value
version = get(:text).value
_, op, = get :requirement
_, version, = get :text

requirements << "#{op} #{version}"

break unless peek.type == :comma
break unless peek[0] == :comma

get :comma
end
@@ -94,13 +96,13 @@ def parse_DEPENDENCIES # :nodoc:

if peek[0] == :bang then
requirements.clear
requirements << pinned_requirement(token.value)
requirements << pinned_requirement(name)

get :bang
end
end

@set.gem token.value, *requirements
@set.gem name, *requirements

skip :newline
end
@@ -111,7 +113,7 @@ def parse_GEM # :nodoc:

while [:entry, 'remote'] == peek.first(2) do
get :entry, 'remote'
data = get(:text).value
_, data, = get :text
skip :newline

sources << Gem::Source.new(data)
@@ -126,10 +128,8 @@ def parse_GEM # :nodoc:
set = Gem::Resolver::LockSet.new sources
last_specs = nil

while not @tokens.empty? and :text == peek.type do
token = get :text
name = token.value
column = token.column
while not @tokens.empty? and :text == peek.first do
_, name, column, = get :text

case peek[0]
when :newline then
@@ -139,9 +139,7 @@ def parse_GEM # :nodoc:
when :l_paren then
get :l_paren

token = get [:text, :requirement]
type = token.type
data = token.value
type, data, = get [:text, :requirement]

if type == :text and column == 4 then
version, platform = data.split '-', 2
@@ -171,17 +169,16 @@ def parse_GEM # :nodoc:

def parse_GIT # :nodoc:
get :entry, 'remote'
repository = get(:text).value
_, repository, = get :text

skip :newline

get :entry, 'revision'
revision = get(:text).value
_, revision, = get :text

skip :newline

type = peek.type
value = peek.value
type, value = peek.first 2
if type == :entry and %w[branch ref tag].include? value then
get
get :text
@@ -198,20 +195,16 @@ def parse_GIT # :nodoc:

last_spec = nil

while not @tokens.empty? and :text == peek.type do
token = get :text
name = token.value
column = token.column
while not @tokens.empty? and :text == peek.first do
_, name, column, = get :text

case peek[0]
when :newline then
last_spec.add_dependency Gem::Dependency.new name if column == 6
when :l_paren then
get :l_paren

token = get [:text, :requirement]
type = token.type
data = token.value
type, data, = get [:text, :requirement]

if type == :text and column == 4 then
last_spec = set.add_git_spec name, data, repository, revision, true
@@ -234,7 +227,7 @@ def parse_GIT # :nodoc:

def parse_PATH # :nodoc:
get :entry, 'remote'
directory = get(:text).value
_, directory, = get :text

skip :newline

@@ -246,19 +239,15 @@ def parse_PATH # :nodoc:
last_spec = nil

while not @tokens.empty? and :text == peek.first do
token = get :text
name = token.value
column = token.column
_, name, column, = get :text

case peek[0]
when :newline then
last_spec.add_dependency Gem::Dependency.new name if column == 6
when :l_paren then
get :l_paren

token = get [:text, :requirement]
type = token.type
data = token.value
type, data, = get [:text, :requirement]

if type == :text and column == 4 then
last_spec = set.add_vendor_gem name, directory
@@ -281,7 +270,7 @@ def parse_PATH # :nodoc:

def parse_PLATFORMS # :nodoc:
while not @tokens.empty? and :text == peek.first do
name = get(:text).value
_, name, = get :text

@platforms << name

@@ -296,14 +285,14 @@ def parse_PLATFORMS # :nodoc:
def parse_dependency name, op # :nodoc:
return Gem::Dependency.new name, op unless peek[0] == :text

version = get(:text).value
_, version, = get :text

requirements = ["#{op} #{version}"]

while peek.type == :comma do
while peek[0] == :comma do
get :comma
op = get(:requirement).value
version = get(:text).value
_, op, = get :requirement
_, version, = get :text

requirements << "#{op} #{version}"
end
29 changes: 13 additions & 16 deletions lib/ruby/stdlib/rubygems/request_set/lockfile/tokenizer.rb
Original file line number Diff line number Diff line change
@@ -2,9 +2,6 @@
require 'rubygems/request_set/lockfile/parser'

class Gem::RequestSet::Lockfile::Tokenizer
Token = Struct.new :type, :value, :column, :line
EOF = Token.new :EOF

def self.from_file file
new File.read(file), file
end
@@ -22,11 +19,11 @@ def make_parser set, platforms
end

def to_a
@tokens.map { |token| [token.type, token.value, token.column, token.line] }
@tokens
end

def skip type
@tokens.shift while not @tokens.empty? and peek.type == type
@tokens.shift while not @tokens.empty? and peek.first == type
end

##
@@ -51,7 +48,7 @@ def next_token
alias :shift :next_token

def peek
@tokens.first || EOF
@tokens.first || [:EOF]
end

private
@@ -74,33 +71,33 @@ def tokenize input
@tokens <<
case
when s.scan(/\r?\n/) then
token = Token.new(:newline, nil, *token_pos(pos))
token = [:newline, nil, *token_pos(pos)]
@line_pos = s.pos
@line += 1
token
when s.scan(/[A-Z]+/) then
if leading_whitespace then
text = s.matched
text += s.scan(/[^\s)]*/).to_s # in case of no match
Token.new(:text, text, *token_pos(pos))
[:text, text, *token_pos(pos)]
else
Token.new(:section, s.matched, *token_pos(pos))
[:section, s.matched, *token_pos(pos)]
end
when s.scan(/([a-z]+):\s/) then
s.pos -= 1 # rewind for possible newline
Token.new(:entry, s[1], *token_pos(pos))
[:entry, s[1], *token_pos(pos)]
when s.scan(/\(/) then
Token.new(:l_paren, nil, *token_pos(pos))
[:l_paren, nil, *token_pos(pos)]
when s.scan(/\)/) then
Token.new(:r_paren, nil, *token_pos(pos))
[:r_paren, nil, *token_pos(pos)]
when s.scan(/<=|>=|=|~>|<|>|!=/) then
Token.new(:requirement, s.matched, *token_pos(pos))
[:requirement, s.matched, *token_pos(pos)]
when s.scan(/,/) then
Token.new(:comma, nil, *token_pos(pos))
[:comma, nil, *token_pos(pos)]
when s.scan(/!/) then
Token.new(:bang, nil, *token_pos(pos))
[:bang, nil, *token_pos(pos)]
when s.scan(/[^\s),!]*/) then
Token.new(:text, s.matched, *token_pos(pos))
[:text, s.matched, *token_pos(pos)]
else
raise "BUG: can't create token for: #{s.string[s.pos..-1].inspect}"
end
6 changes: 3 additions & 3 deletions lib/ruby/stdlib/rubygems/requirement.rb
Original file line number Diff line number Diff line change
@@ -89,9 +89,9 @@ def self.source_set # :nodoc:
# specification, like <tt>">= 1.2"</tt>, or a simple version number,
# like <tt>"1.2"</tt>.
#
# parse("> 1.0") # => [">", Gem::Version.new("1.0")]
# parse("1.0") # => ["=", Gem::Version.new("1.0")]
# parse(Gem::Version.new("1.0")) # => ["=, Gem::Version.new("1.0")]
# parse("> 1.0") # => [">", "1.0"]
# parse("1.0") # => ["=", "1.0"]
# parse(Gem::Version.new("1.0")) # => ["=, "1.0"]

def self.parse obj
return ["=", obj] if Gem::Version === obj
289 changes: 242 additions & 47 deletions lib/ruby/stdlib/rubygems/resolver.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require 'rubygems/dependency'
require 'rubygems/exceptions'
require 'rubygems/util'
require 'rubygems/util/list'

require 'uri'
@@ -13,7 +12,6 @@
# all the requirements.

class Gem::Resolver
require 'rubygems/resolver/molinillo'

##
# If the DEBUG_RESOLVER environment variable is set then debugging mode is
@@ -22,6 +20,13 @@ class Gem::Resolver

DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?

require 'pp' if DEBUG_RESOLVER

##
# Contains all the conflicts encountered while doing resolution

attr_reader :conflicts

##
# Set to true if all development dependencies should be considered.

@@ -105,6 +110,7 @@ def initialize needed, set = nil
@set = set || Gem::Resolver::IndexSet.new
@needed = needed

@conflicts = []
@development = false
@development_shallow = false
@ignore_dependencies = false
@@ -147,7 +153,7 @@ def activation_request dep, possible # :nodoc:
return spec, activation_request
end

def requests s, act, reqs=[] # :nodoc:
def requests s, act, reqs=nil # :nodoc:
return reqs if @ignore_dependencies

s.fetch_development_dependencies if @development
@@ -159,7 +165,7 @@ def requests s, act, reqs=[] # :nodoc:
next if d.type == :development and @development_shallow and
act.parent

reqs << Gem::Resolver::DependencyRequest.new(d, act)
reqs.add Gem::Resolver::DependencyRequest.new(d, act)
@stats.requirement!
end

@@ -170,29 +176,29 @@ def requests s, act, reqs=[] # :nodoc:
reqs
end

include Molinillo::UI
##
# Proceed with resolution! Returns an array of ActivationRequest objects.

def output
@output ||= debug? ? $stdout : File.open(Gem::Util::NULL_DEVICE, 'w')
end
def resolve
@conflicts = []

def debug?
DEBUG_RESOLVER
end
needed = Gem::Resolver::RequirementList.new

include Molinillo::SpecificationProvider
@needed.reverse_each do |n|
request = Gem::Resolver::DependencyRequest.new n, nil

##
# Proceed with resolution! Returns an array of ActivationRequest objects.
needed.add request
@stats.requirement!
end

def resolve
locking_dg = Molinillo::DependencyGraph.new
Molinillo::Resolver.new(self, self).resolve(@needed.map { |d| DependencyRequest.new d, nil }, locking_dg).tsort.map(&:payload).compact
rescue Molinillo::VersionConflict => e
conflict = e.conflicts.values.first
raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement)
ensure
@output.close if @output and !debug?
@stats.record_requirements needed

res = resolve_for needed, nil

raise Gem::DependencyResolutionError, res if
res.kind_of? Gem::Resolver::Conflict

res.to_a
end

##
@@ -215,44 +221,232 @@ def find_possible dependency # :nodoc:
return matching_platform, all
end

##
# Returns the gems in +specs+ that match the local platform.
def handle_conflict(dep, existing) # :nodoc:
# There is a conflict! We return the conflict object which will be seen by
# the caller and be handled at the right level.

# If the existing activation indicates that there are other possibles for
# it, then issue the conflict on the dependency for the activation itself.
# Otherwise, if there was a requester, issue it on the requester's
# request itself.
# Finally, if the existing request has no requester (toplevel) unwind to
# it anyway.

if existing.others_possible?
conflict =
Gem::Resolver::Conflict.new dep, existing
elsif dep.requester
depreq = dep.requester.request
conflict =
Gem::Resolver::Conflict.new depreq, existing, dep
elsif existing.request.requester.nil?
conflict =
Gem::Resolver::Conflict.new dep, existing
else
raise Gem::DependencyError, "Unable to figure out how to unwind conflict"
end

def select_local_platforms specs # :nodoc:
specs.select do |spec|
Gem::Platform.installable? spec
@conflicts << conflict unless @conflicts.include? conflict

return conflict
end

# Contains the state for attempting activation of a set of possible specs.
# +needed+ is a Gem::List of DependencyRequest objects that, well, need
# to be satisfied.
# +specs+ is the List of ActivationRequest that are being tested.
# +dep+ is the DependencyRequest that was used to generate this state.
# +spec+ is the Specification for this state.
# +possible+ is List of DependencyRequest objects that can be tried to
# find a complete set.
# +conflicts+ is a [DependencyRequest, Conflict] hit tried to
# activate the state.
#
State = Struct.new(:needed, :specs, :dep, :spec, :possibles, :conflicts) do
def summary # :nodoc:
nd = needed.map { |s| s.to_s }.sort if nd

if specs then
ss = specs.map { |s| s.full_name }.sort
ss.unshift ss.length
end

d = dep.to_s
d << " from #{dep.requester.full_name}" if dep.requester

ps = possibles.map { |p| p.full_name }.sort
ps.unshift ps.length

cs = conflicts.map do |(s, c)|
[s.full_name, c.conflicting_dependencies.map { |cd| cd.to_s }]
end

{ :needed => nd, :specs => ss, :dep => d, :spec => spec.full_name,
:possibles => ps, :conflicts => cs }
end
end

def search_for(dependency)
possibles, all = find_possible(dependency)
if !@soft_missing && possibles.empty?
@missing << dependency
exc = Gem::UnsatisfiableDependencyError.new dependency, all
exc.errors = @set.errors
raise exc
##
# The meat of the algorithm. Given +needed+ DependencyRequest objects and
# +specs+ being a list to ActivationRequest, calculate a new list of
# ActivationRequest objects.

def resolve_for needed, specs # :nodoc:
# The State objects that are used to attempt the activation tree.
states = []

while !needed.empty?
@stats.iteration!

dep = needed.remove
explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
explain_list(:next5) { needed.next5 }
explain_list(:specs) { Array(specs).map { |x| x.full_name }.sort }

# If there is already a spec activated for the requested name...
if specs && existing = specs.find { |s| dep.name == s.name }
# then we're done since this new dep matches the existing spec.
next if dep.matches_spec? existing

conflict = handle_conflict dep, existing

return conflict unless dep.requester

explain :conflict, dep, :existing, existing.full_name

depreq = dep.requester.request

state = nil
until states.empty?
x = states.pop

i = existing.request.requester
explain :consider, x.spec.full_name, [depreq.name, dep.name, i ? i.name : :top]

if x.spec.name == depreq.name or
x.spec.name == dep.name or
(i && (i.name == x.spec.name))
explain :found, x.spec.full_name
state = x
break
end
end

return conflict unless state

@stats.backtracking!

needed, specs = resolve_for_conflict needed, specs, state

states << state unless state.possibles.empty?

next
end

matching, all = find_possible dep

case matching.size
when 0
resolve_for_zero dep, all
when 1
needed, specs =
resolve_for_single needed, specs, dep, matching
else
needed, specs =
resolve_for_multiple needed, specs, states, dep, matching
end
end
possibles.sort_by { |s| [s.source, s.version, s.platform.to_s == Gem::Platform.local.to_s ? 1 : 0] }.
map { |s| ActivationRequest.new s, dependency, [] }

specs
end

def dependencies_for(specification)
return [] if @ignore_dependencies
spec = specification.spec
requests(spec, specification)
##
# Rewinds +needed+ and +specs+ to a previous state in +state+ for a conflict
# between +dep+ and +existing+.

def resolve_for_conflict needed, specs, state # :nodoc:
# We exhausted the possibles so it's definitely not going to work out,
# bail out.
raise Gem::ImpossibleDependenciesError.new state.dep, state.conflicts if
state.possibles.empty?

# Retry resolution with this spec and add it's dependencies
spec, act = activation_request state.dep, state.possibles

needed = requests spec, act, state.needed.dup
specs = Gem::List.prepend state.specs, act

return needed, specs
end

def requirement_satisfied_by?(requirement, activated, spec)
requirement.matches_spec? spec
##
# There are multiple +possible+ specifications for this +dep+. Updates
# +needed+, +specs+ and +states+ for further resolution of the +possible+
# choices.

def resolve_for_multiple needed, specs, states, dep, possible # :nodoc:
# Sort them so that we try the highest versions first.
possible = possible.sort_by do |s|
[s.source, s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
end

spec, act = activation_request dep, possible

# We may need to try all of +possible+, so we setup state to unwind back
# to current +needed+ and +specs+ so we can try another. This is code is
# what makes conflict resolution possible.
states << State.new(needed.dup, specs, dep, spec, possible, [])

@stats.record_depth states

explain :states, states.map { |s| s.dep }

needed = requests spec, act, needed
specs = Gem::List.prepend specs, act

return needed, specs
end

def name_for(dependency)
dependency.name
##
# Add the spec from the +possible+ list to +specs+ and process the spec's
# dependencies by adding them to +needed+.

def resolve_for_single needed, specs, dep, possible # :nodoc:
spec, act = activation_request dep, possible

specs = Gem::List.prepend specs, act

# Put the deps for at the beginning of needed
# rather than the end to match the depth first
# searching done by the multiple case code below.
#
# This keeps the error messages consistent.
needed = requests spec, act, needed

return needed, specs
end

def allow_missing?(dependency)
@missing << dependency
@soft_missing
##
# When there are no possible specifications for +dep+ our work is done.

def resolve_for_zero dep, platform_mismatch # :nodoc:
@missing << dep

unless @soft_missing
exc = Gem::UnsatisfiableDependencyError.new dep, platform_mismatch
exc.errors = @set.errors

raise exc
end
end

##
# Returns the gems in +specs+ that match the local platform.

def select_local_platforms specs # :nodoc:
specs.select do |spec|
Gem::Platform.installable? spec
end
end

end
@@ -288,3 +482,4 @@ def allow_missing?(dependency)
require 'rubygems/resolver/local_specification'
require 'rubygems/resolver/lock_specification'
require 'rubygems/resolver/vendor_specification'

3 changes: 1 addition & 2 deletions lib/ruby/stdlib/rubygems/resolver/activation_request.rb
Original file line number Diff line number Diff line change
@@ -67,8 +67,6 @@ def full_name
@spec.full_name
end

alias_method :to_s, :full_name

##
# The Gem::Specification for this activation request.

@@ -171,3 +169,4 @@ def version
end

end

1 change: 1 addition & 0 deletions lib/ruby/stdlib/rubygems/resolver/conflict.rb
Original file line number Diff line number Diff line change
@@ -157,3 +157,4 @@ def requester
# TODO: Remove in RubyGems 3

Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict # :nodoc:

5 changes: 1 addition & 4 deletions lib/ruby/stdlib/rubygems/resolver/dependency_request.rb
Original file line number Diff line number Diff line change
@@ -67,10 +67,6 @@ def name
@dependency.name
end

def type
@dependency.type
end

##
# Indicate that the request is for a gem explicitly requested by the user

@@ -117,3 +113,4 @@ def to_s # :nodoc:
end

end

3 changes: 2 additions & 1 deletion lib/ruby/stdlib/rubygems/resolver/git_specification.rb
Original file line number Diff line number Diff line change
@@ -23,7 +23,8 @@ def add_dependency dependency # :nodoc:
def install options = {}
require 'rubygems/installer'

installer = Gem::Installer.for_spec spec, options
installer = Gem::Installer.new '', options
installer.spec = spec

yield installer if block_given?

1 change: 0 additions & 1 deletion lib/ruby/stdlib/rubygems/resolver/molinillo.rb

This file was deleted.

5 changes: 0 additions & 5 deletions lib/ruby/stdlib/rubygems/resolver/molinillo/lib/molinillo.rb

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 88e39cf

Please sign in to comment.