Skip to content

Commit

Permalink
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.