summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-11-19 00:34:13 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-11-19 00:34:13 +0000
commita7fa4d5d9aab150ad4b0c3f3217fe444df69f527 (patch)
tree88ab96d22f7228b556337aa7c34042d4fd279394 /lib
parente7ec3dad907f2c77f17faddb40a98b2ef4523222 (diff)
* lib/rubygems: Update to RubyGems master 6a3d9f9. Changes include:
Compatibly renamed Gem::DependencyResolver to Gem::Resolver. Added support for git gems in gem.deps.rb and Gemfile. Fixed resolver bugs. * test/rubygems: ditto. * lib/rubygems/LICENSE.txt: Updated to license from RubyGems trunk. [ruby-trunk - Bug #9086] * lib/rubygems/commands/which_command.rb: RubyGems now indicates failure when any file is missing. [ruby-trunk - Bug #9004] * lib/rubygems/ext/builder: Extensions are now installed into the extension install directory and the first directory in the require path from the gem. This allows backwards compatibility with msgpack and other gems that calculate full require paths. [ruby-trunk - Bug #9106] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43714 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/rubygems.rb49
-rw-r--r--lib/rubygems/LICENSE.txt75
-rw-r--r--lib/rubygems/available_set.rb4
-rw-r--r--lib/rubygems/basic_specification.rb8
-rw-r--r--lib/rubygems/commands/install_command.rb23
-rw-r--r--lib/rubygems/commands/which_command.rb5
-rw-r--r--lib/rubygems/dependency_installer.rb32
-rw-r--r--lib/rubygems/doctor.rb20
-rw-r--r--lib/rubygems/errors.rb17
-rw-r--r--lib/rubygems/exceptions.rb10
-rw-r--r--lib/rubygems/ext/builder.rb3
-rw-r--r--lib/rubygems/ext/cmake_builder.rb2
-rw-r--r--lib/rubygems/ext/configure_builder.rb2
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb8
-rw-r--r--lib/rubygems/ext/rake_builder.rb2
-rw-r--r--lib/rubygems/remote_fetcher.rb2
-rw-r--r--lib/rubygems/request_set.rb19
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb149
-rw-r--r--lib/rubygems/request_set/lockfile.rb27
-rw-r--r--lib/rubygems/resolver.rb (renamed from lib/rubygems/dependency_resolver.rb)104
-rw-r--r--lib/rubygems/resolver/activation_request.rb (renamed from lib/rubygems/dependency_resolver/activation_request.rb)6
-rw-r--r--lib/rubygems/resolver/api_set.rb (renamed from lib/rubygems/dependency_resolver/api_set.rb)4
-rw-r--r--lib/rubygems/resolver/api_specification.rb (renamed from lib/rubygems/dependency_resolver/api_specification.rb)2
-rw-r--r--lib/rubygems/resolver/best_set.rb (renamed from lib/rubygems/dependency_resolver/best_set.rb)2
-rw-r--r--lib/rubygems/resolver/composed_set.rb (renamed from lib/rubygems/dependency_resolver/composed_set.rb)2
-rw-r--r--lib/rubygems/resolver/conflict.rb (renamed from lib/rubygems/dependency_resolver/dependency_conflict.rb)8
-rw-r--r--lib/rubygems/resolver/current_set.rb (renamed from lib/rubygems/dependency_resolver/current_set.rb)2
-rw-r--r--lib/rubygems/resolver/dependency_request.rb (renamed from lib/rubygems/dependency_resolver/dependency_request.rb)4
-rw-r--r--lib/rubygems/resolver/git_set.rb81
-rw-r--r--lib/rubygems/resolver/git_specification.rb16
-rw-r--r--lib/rubygems/resolver/index_set.rb (renamed from lib/rubygems/dependency_resolver/index_set.rb)4
-rw-r--r--lib/rubygems/resolver/index_specification.rb (renamed from lib/rubygems/dependency_resolver/index_specification.rb)2
-rw-r--r--lib/rubygems/resolver/installed_specification.rb (renamed from lib/rubygems/dependency_resolver/installed_specification.rb)2
-rw-r--r--lib/rubygems/resolver/installer_set.rb (renamed from lib/rubygems/dependency_resolver/installer_set.rb)8
-rw-r--r--lib/rubygems/resolver/lock_set.rb (renamed from lib/rubygems/dependency_resolver/lock_set.rb)6
-rw-r--r--lib/rubygems/resolver/requirement_list.rb40
-rw-r--r--lib/rubygems/resolver/set.rb (renamed from lib/rubygems/dependency_resolver/set.rb)9
-rw-r--r--lib/rubygems/resolver/spec_specification.rb (renamed from lib/rubygems/dependency_resolver/spec_specification.rb)6
-rw-r--r--lib/rubygems/resolver/specification.rb (renamed from lib/rubygems/dependency_resolver/specification.rb)4
-rw-r--r--lib/rubygems/resolver/vendor_set.rb (renamed from lib/rubygems/dependency_resolver/vendor_set.rb)6
-rw-r--r--lib/rubygems/resolver/vendor_specification.rb (renamed from lib/rubygems/dependency_resolver/vendor_specification.rb)2
-rw-r--r--lib/rubygems/source.rb25
-rw-r--r--lib/rubygems/source/git.rb189
-rw-r--r--lib/rubygems/source/installed.rb7
-rw-r--r--lib/rubygems/source/local.rb17
-rw-r--r--lib/rubygems/source/specific_file.rb17
-rw-r--r--lib/rubygems/source/vendor.rb18
-rw-r--r--lib/rubygems/source_list.rb15
-rw-r--r--lib/rubygems/spec_fetcher.rb26
-rw-r--r--lib/rubygems/specification.rb5
-rw-r--r--lib/rubygems/test_case.rb70
-rw-r--r--lib/rubygems/util.rb65
-rw-r--r--lib/rubygems/util/stringio.rb34
53 files changed, 1031 insertions, 234 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 1c84356bd7..2e8ad7bba1 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -8,7 +8,7 @@
require 'rbconfig'
module Gem
- VERSION = '2.2.0'
+ VERSION = '2.2.0.preview.2'
end
# Must be first since it unloads the prelude from 1.9.2
@@ -302,7 +302,6 @@ module Gem
# The path where gem executables are to be installed.
def self.bindir(install_dir=Gem.dir)
- # TODO: move to Gem::Dirs
return File.join install_dir, 'bin' unless
install_dir.to_s == Gem.default_dir.to_s
Gem.default_bindir
@@ -362,16 +361,21 @@ module Gem
Zlib::Deflate.deflate data
end
- # DOC: needs doc'd or :nodoc'd
+ # Retrieve the PathSupport object that RubyGems uses to
+ # lookup files.
+
def self.paths
@paths ||= Gem::PathSupport.new
end
- # DOC: needs doc'd or :nodoc'd
+ # Initialize the filesystem paths to use from +env+.
+ # +env+ is a hash-like object (typically ENV) that
+ # is queried for 'GEM_HOME', 'GEM_PATH', and 'GEM_SPEC_CACHE'
+
def self.paths=(env)
clear_paths
@paths = Gem::PathSupport.new env
- Gem::Specification.dirs = @paths.path # FIX: home is at end
+ Gem::Specification.dirs = @paths.path
end
##
@@ -380,12 +384,10 @@ module Gem
# FIXME deprecate these once everything else has been done -ebh
def self.dir
- # TODO: raise "no"
paths.home
end
def self.path
- # TODO: raise "no"
paths.path
end
@@ -552,42 +554,30 @@ module Gem
private_class_method :find_home
+ # FIXME deprecate these in 3.0
+
##
# Zlib::GzipReader wrapper that unzips +data+.
def self.gunzip(data)
- # TODO: move to utils
- require 'stringio'
- require 'zlib'
- data = StringIO.new data
-
- unzipped = Zlib::GzipReader.new(data).read
- unzipped.force_encoding Encoding::BINARY if Object.const_defined? :Encoding
- unzipped
+ require 'rubygems/util'
+ Gem::Util.gunzip data
end
##
# Zlib::GzipWriter wrapper that zips +data+.
def self.gzip(data)
- # TODO: move to utils
- require 'stringio'
- require 'zlib'
- zipped = StringIO.new
- zipped.set_encoding Encoding::BINARY if Object.const_defined? :Encoding
-
- Zlib::GzipWriter.wrap zipped do |io| io.write data end
-
- zipped.string
+ require 'rubygems/util'
+ Gem::Util.gzip data
end
##
# A Zlib::Inflate#inflate wrapper
def self.inflate(data)
- # TODO: move to utils
- require 'zlib'
- Zlib::Inflate.inflate data
+ require 'rubygems/util'
+ Gem::Util.inflate data
end
##
@@ -693,7 +683,6 @@ module Gem
file = $1
lineno = $2.to_i
- # TODO: it is ALWAYS joined! STUPID!
[file, lineno]
end
@@ -974,7 +963,6 @@ module Gem
paths = nil if paths == [nil]
paths = paths.first if Array === Array(paths).first
self.paths = { "GEM_HOME" => home, "GEM_PATH" => paths }
- # TODO: self.paths = home, paths
end
##
@@ -1169,7 +1157,8 @@ module Gem
autoload :ConfigFile, 'rubygems/config_file'
autoload :Dependency, 'rubygems/dependency'
autoload :DependencyList, 'rubygems/dependency_list'
- autoload :DependencyResolver, 'rubygems/dependency_resolver'
+ autoload :Resolver, 'rubygems/resolver'
+ autoload :DependencyResolver, 'rubygems/resolver'
autoload :PathSupport, 'rubygems/path_support'
autoload :Platform, 'rubygems/platform'
autoload :RequestSet, 'rubygems/request_set'
diff --git a/lib/rubygems/LICENSE.txt b/lib/rubygems/LICENSE.txt
index db88c5e118..8a0a51dec1 100644
--- a/lib/rubygems/LICENSE.txt
+++ b/lib/rubygems/LICENSE.txt
@@ -1,53 +1,54 @@
RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
Weirich and others. You can redistribute it and/or modify it under
-either the terms of the GPL (see the GPL.txt file), or the conditions
-below:
+either the terms of the MIT license (see the file MIT.txt), or the
+conditions below:
- 1. You may make and give away verbatim copies of the source form of the
- software without restriction, provided that you duplicate all of the
- original copyright notices and associated disclaimers.
+1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
- 2. You may modify your copy of the software in any way, provided that
- you do at least ONE of the following:
+2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
- a) place your modifications in the Public Domain or otherwise
- make them Freely Available, such as by posting said
- modifications to Usenet or an equivalent medium, or by allowing
- the author to include your modifications in the software.
+ a. place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
- b) use the modified software only within your corporation or
- organization.
+ b. use the modified software only within your corporation or
+ organization.
- c) rename any non-standard executables so the names do not conflict
- with standard executables, which must also be provided.
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
- d) make other distribution arrangements with the author.
+ d. make other distribution arrangements with the author.
- 3. You may distribute the software in object code or executable
- form, provided that you do at least ONE of the following:
+3. You may distribute the software in object code or executable
+ form, provided that you do at least ONE of the following:
- a) distribute the executables and library files of the software,
- together with instructions (in the manual page or equivalent)
- on where to get the original distribution.
+ a. distribute the executables and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
- b) accompany the distribution with the machine-readable source of
- the software.
+ b. accompany the distribution with the machine-readable source of
+ the software.
- c) give non-standard executables non-standard names, with
- instructions on where to get the original software distribution.
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
- d) make other distribution arrangements with the author.
+ d. make other distribution arrangements with the author.
- 4. You may modify and include the part of the software into any other
- software (possibly commercial).
+4. You may modify and include the part of the software into any other
+ software (possibly commercial).
- 5. The scripts and library files supplied as input to or produced as
- output from the software do not automatically fall under the
- copyright of the software, but belong to whomever generated them,
- and may be sold commercially, and may be aggregated with this
- software.
+5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
+
+6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
- 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE.
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index bb0b3a3abe..4ab4d77716 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -116,7 +116,7 @@ class Gem::AvailableSet
##
#
- # Used by the DependencyResolver, the protocol to use a AvailableSet as a
+ # Used by the Resolver, the protocol to use a AvailableSet as a
# search Set.
def find_all(req)
@@ -127,7 +127,7 @@ class Gem::AvailableSet
end
match.map do |t|
- Gem::DependencyResolver::InstalledSpecification.new(self, t.spec, t.source)
+ Gem::Resolver::InstalledSpecification.new(self, t.spec, t.source)
end
end
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index a29ed0aa6d..4f96fcac3d 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -107,7 +107,7 @@ class Gem::BasicSpecification
File.join full_gem_path, path
end
- full_paths << extension_install_dir unless @extensions.empty?
+ full_paths.unshift extension_install_dir unless @extensions.empty?
full_paths
end
@@ -155,6 +155,10 @@ class Gem::BasicSpecification
raise NotImplementedError
end
+ def raw_require_paths # :nodoc:
+ @require_paths
+ end
+
##
# Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
# activated.
@@ -179,7 +183,7 @@ class Gem::BasicSpecification
File.join '..', '..', 'extensions', Gem::Platform.local.to_s,
Gem.extension_api_version, full_name
- @require_paths + [relative_extension_install_dir]
+ [relative_extension_install_dir].concat @require_paths
end
##
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index ad90b37fdc..68a2fad129 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -56,6 +56,12 @@ class Gem::Commands::InstallCommand < Gem::Command
o[:install_as_default] = v
end
+ add_option(:"Install/Update", '--explain',
+ 'Rather than install the gems, indicate which would',
+ 'be installed') do |v,o|
+ o[:explain] = v
+ end
+
@installed_specs = nil
end
@@ -185,8 +191,23 @@ to write the specification by hand. For example:
return if options[:conservative] and
not Gem::Dependency.new(name, version).matching_specs.empty?
+ req = Gem::Requirement.create(version)
+
inst = Gem::DependencyInstaller.new options
- inst.install name, Gem::Requirement.create(version)
+
+ if options[:explain]
+ request_set = inst.resolve_dependencies name, req
+
+ puts "Gems to install:"
+
+ request_set.specs.map { |s| s.full_name }.sort.each do |s|
+ puts " #{s}"
+ end
+
+ return
+ else
+ inst.install name, req
+ end
@installed_specs.push(*inst.installed_gems)
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index 18706afdf5..96eeb86288 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -35,7 +35,7 @@ requiring to see why it does not behave as you expect.
end
def execute
- found = false
+ found = true
options[:args].each do |arg|
arg = arg.sub(/#{Regexp.union(*Gem.suffixes)}$/, '')
@@ -56,9 +56,10 @@ requiring to see why it does not behave as you expect.
if paths.empty? then
alert_error "Can't find ruby library file or shared library #{arg}"
+
+ found &&= false
else
say paths
- found = true
end
end
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 22ff6f5cb7..e404d42b3a 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -1,6 +1,5 @@
require 'rubygems'
require 'rubygems/dependency_list'
-require 'rubygems/dependency_resolver'
require 'rubygems/package'
require 'rubygems/installer'
require 'rubygems/spec_fetcher'
@@ -196,7 +195,7 @@ class Gem::DependencyInstaller
# sources. Gems are sorted with newer gems preferred over older gems, and
# local gems preferred over remote gems.
- def find_gems_with_sources dep # :nodoc:
+ def find_gems_with_sources dep, best_only=false # :nodoc:
set = Gem::AvailableSet.new
if consider_local?
@@ -211,7 +210,26 @@ class Gem::DependencyInstaller
if consider_remote?
begin
- found, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
+ # TODO this is pulled from #spec_for_dependency to allow
+ # us to filter tuples before fetching specs.
+ #
+ tuples, errors = Gem::SpecFetcher.fetcher.search_for_dependency dep
+
+ if best_only && !tuples.empty?
+ tuples.sort! { |a,b| b[0].version <=> a[0].version }
+ tuples = [tuples.first]
+ end
+
+ specs = []
+ tuples.each do |tup, source|
+ begin
+ spec = source.fetch_spec(tup)
+ rescue Gem::RemoteFetcher::FetchError => e
+ errors << Gem::SourceFetchProblem.new(source, e)
+ else
+ specs << [spec, source]
+ end
+ end
if @errors
@errors += errors
@@ -219,7 +237,7 @@ class Gem::DependencyInstaller
@errors = errors
end
- set << found
+ set << specs
rescue Gem::RemoteFetcher::FetchError => e
# FIX if there is a problem talking to the network, we either need to always tell
@@ -271,7 +289,7 @@ class Gem::DependencyInstaller
dep = Gem::Dependency.new gem_name, version
dep.prerelease = true if prerelease
- set = find_gems_with_sources(dep)
+ set = find_gems_with_sources(dep, true)
set.match_platform!
end
@@ -402,7 +420,7 @@ class Gem::DependencyInstaller
request_set = as.to_request_set install_development_deps
request_set.soft_missing = @force
- installer_set = Gem::DependencyResolver::InstallerSet.new @domain
+ installer_set = Gem::Resolver::InstallerSet.new @domain
installer_set.always_install.concat request_set.always_install
installer_set.ignore_installed = @only_install_dir
@@ -411,7 +429,7 @@ class Gem::DependencyInstaller
request_set.soft_missing = true
end
- composed_set = Gem::DependencyResolver.compose_sets as, installer_set
+ composed_set = Gem::Resolver.compose_sets as, installer_set
request_set.resolve composed_set
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index 0de337f7de..2cb8901b4d 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -1,6 +1,5 @@
require 'rubygems'
require 'rubygems/user_interaction'
-require 'pathname'
##
# Cleans up after a partially-failed uninstall or for an invalid
@@ -39,7 +38,7 @@ class Gem::Doctor
# If +dry_run+ is true no files or directories will be removed.
def initialize gem_repository, dry_run = false
- @gem_repository = Pathname(gem_repository)
+ @gem_repository = gem_repository
@dry_run = dry_run
@installed_specs = nil
@@ -97,26 +96,29 @@ class Gem::Doctor
# Removes files in +sub_directory+ with +extension+
def doctor_child sub_directory, extension # :nodoc:
- directory = @gem_repository + sub_directory
+ directory = File.join(@gem_repository, sub_directory)
- directory.children.sort.each do |child|
- next unless child.exist?
+ Dir.entries(directory).sort.each do |ent|
+ next if ent == "." || ent == ".."
- basename = child.basename(extension).to_s
+ child = File.join(directory, ent)
+ next unless File.exists?(child)
+
+ basename = File.basename(child, extension)
next if installed_specs.include? basename
next if /^rubygems-\d/ =~ basename
next if 'specifications' == sub_directory and 'default' == basename
- type = child.directory? ? 'directory' : 'file'
+ type = File.directory?(child) ? 'directory' : 'file'
action = if @dry_run then
'Extra'
else
- child.rmtree
+ FileUtils.rm_r(child)
'Removed'
end
- say "#{action} #{type} #{sub_directory}/#{child.basename}"
+ say "#{action} #{type} #{sub_directory}/#{File.basename(child)}"
end
rescue Errno::ENOENT
# ignore
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index 3c5486a800..fc9bfbc0dc 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -73,12 +73,27 @@ module Gem
# data from a source
class SourceFetchProblem < ErrorReason
+
+ ##
+ # Creates a new SourceFetchProblem for the given +source+ and +error+.
+
def initialize(source, error)
@source = source
@error = error
end
- attr_reader :source, :error
+ ##
+ # The source that had the fetch problem.
+
+ attr_reader :source
+
+ ##
+ # The fetch error which is an Exception subclass.
+
+ attr_reader :error
+
+ ##
+ # An English description of the error.
def wordy
"Unable to download data from #{@source.uri} - #{@error.message}"
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 6d92b144b6..ee3d8fecdf 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -23,7 +23,7 @@ class Gem::DependencyError < Gem::Exception; end
class Gem::DependencyRemovalException < Gem::Exception; end
##
-# Raised by Gem::DependencyResolver when a Gem::DependencyConflict reaches the
+# Raised by Gem::Resolver when a Gem::Dependency::Conflict reaches the
# toplevel. Indicates which dependencies were incompatible through #conflict
# and #conflicting_dependencies
@@ -117,7 +117,7 @@ class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException
end
##
-# Raised by Gem::DependencyResolver when dependencies conflict and create the
+# Raised by Gem::Resolver when dependencies conflict and create the
# inability to find a valid possible spec for a request.
class Gem::ImpossibleDependenciesError < Gem::Exception
@@ -211,20 +211,20 @@ class Gem::SystemExitException < SystemExit
end
##
-# Raised by DependencyResolver when a dependency requests a gem for which
+# Raised by Resolver when a dependency requests a gem for which
# there is no spec.
class Gem::UnsatisfiableDependencyError < Gem::Exception
##
# The unsatisfiable dependency. This is a
- # Gem::DependencyResolver::DependencyRequest, not a Gem::Dependency
+ # Gem::Resolver::DependencyRequest, not a Gem::Dependency
attr_reader :dependency
##
# Creates a new UnsatisfiableDepedencyError for the unsatisfiable
- # Gem::DependencyResolver::DependencyRequest +dep+
+ # Gem::Resolver::DependencyRequest +dep+
def initialize dep, platform_mismatch=nil
if platform_mismatch and !platform_mismatch.empty?
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index 761505636c..e9244c760c 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -149,6 +149,7 @@ EOF
extension ||= '' # I wish I knew why this line existed
extension_dir =
File.expand_path File.join @gem_dir, File.dirname(extension)
+ lib_dir = File.join @spec.full_gem_path, @spec.raw_require_paths.first
builder = builder_for extension
@@ -158,7 +159,7 @@ EOF
CHDIR_MUTEX.synchronize do
Dir.chdir extension_dir do
results = builder.build(extension, @gem_dir, dest_path,
- results, @build_args)
+ results, @build_args, lib_dir)
say results.join("\n") if Gem.configuration.really_verbose
end
diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb
index 17bd6c296e..24531bc75c 100644
--- a/lib/rubygems/ext/cmake_builder.rb
+++ b/lib/rubygems/ext/cmake_builder.rb
@@ -1,7 +1,7 @@
require 'rubygems/command'
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results)
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
unless File.exist?('Makefile') then
cmd = "cmake . -DCMAKE_INSTALL_PREFIX=#{dest_path}"
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb
index 2a542e6bd0..f66e39387a 100644
--- a/lib/rubygems/ext/configure_builder.rb
+++ b/lib/rubygems/ext/configure_builder.rb
@@ -6,7 +6,7 @@
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[])
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
unless File.exist?('Makefile') then
cmd = "sh ./configure --prefix=#{dest_path}"
cmd << " #{args.join ' '}" unless args.empty?
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 9a656a30a3..6e736d8062 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -10,7 +10,7 @@ require 'tempfile'
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry = FileUtils::Entry_ # :nodoc:
- def self.build(extension, directory, dest_path, results, args=[])
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
tmp_dest = Dir.mktmpdir(".gem.", ".")
t = nil
@@ -44,6 +44,12 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
if tmp_dest
FileEntry.new(tmp_dest).traverse do |ent|
+ # TODO remove in RubyGems 3
+ if lib_dir then
+ libent = ent.class.new lib_dir, ent.rel
+ libent.exist? or ent.copy libent.path
+ end
+
destent = ent.class.new(dest_path, ent.rel)
destent.exist? or File.rename(ent.path, destent.path)
end
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 984c54bc1b..2093bcabdd 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -6,7 +6,7 @@
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[])
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
if File.basename(extension) =~ /mkrf_conf/i then
cmd = "#{Gem.ruby} #{File.basename extension}"
cmd << " #{args.join " "}" unless args.empty?
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index b96c77033a..c6816e8f0f 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -90,7 +90,7 @@ class Gem::RemoteFetcher
rescue Resolv::ResolvError
uri
else
- URI.parse "#{res.target}#{uri.path}"
+ URI.parse "#{uri.scheme}://#{res.target}#{uri.path}"
end
end
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 3a997f32ee..42d457063f 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,6 +1,5 @@
require 'rubygems'
require 'rubygems/dependency'
-require 'rubygems/dependency_resolver'
require 'rubygems/dependency_list'
require 'rubygems/installer'
require 'tsort'
@@ -32,6 +31,11 @@ class Gem::RequestSet
attr_accessor :development
##
+ # The set of git gems imported via load_gemdeps.
+
+ attr_reader :git_set # :nodoc:
+
+ ##
# Sets used for resolution
attr_reader :sets # :nodoc:
@@ -61,6 +65,7 @@ class Gem::RequestSet
@always_install = []
@dependency_names = {}
@development = false
+ @git_set = nil
@requests = []
@sets = []
@soft_missing = false
@@ -184,7 +189,8 @@ class Gem::RequestSet
# Load a dependency management file.
def load_gemdeps path, without_groups = []
- @vendor_set = Gem::DependencyResolver::VendorSet.new
+ @git_set = Gem::Resolver::GitSet.new
+ @vendor_set = Gem::Resolver::VendorSet.new
gf = Gem::RequestSet::GemDependencyAPI.new self, path
gf.without_groups = without_groups if without_groups
@@ -195,13 +201,14 @@ class Gem::RequestSet
# Resolve the requested dependencies and return an Array of Specification
# objects to be activated.
- def resolve set = Gem::DependencyResolver::IndexSet.new
+ def resolve set = Gem::Resolver::IndexSet.new
@sets << set
+ @sets << @git_set
@sets << @vendor_set
- set = Gem::DependencyResolver.compose_sets(*@sets)
+ set = Gem::Resolver.compose_sets(*@sets)
- resolver = Gem::DependencyResolver.new @dependencies, set
+ resolver = Gem::Resolver.new @dependencies, set
resolver.development = @development
resolver.soft_missing = @soft_missing
@@ -213,7 +220,7 @@ class Gem::RequestSet
# and return an Array of Specification objects to be activated.
def resolve_current
- resolve Gem::DependencyResolver::CurrentSet.new
+ resolve Gem::Resolver::CurrentSet.new
end
def sorted_requests
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index e8f3138990..8e29eb87e5 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -108,6 +108,11 @@ class Gem::RequestSet::GemDependencyAPI
}
##
+ # A set of gems that are loaded via the +:git+ option to #gem
+
+ attr_reader :git_set # :nodoc:
+
+ ##
# A Hash containing gem names and files to require from those gems.
attr_reader :requires
@@ -130,13 +135,55 @@ class Gem::RequestSet::GemDependencyAPI
@set = set
@path = path
- @current_groups = nil
- @current_platform = nil
- @default_sources = true
- @requires = Hash.new { |h, name| h[name] = [] }
- @vendor_set = @set.vendor_set
- @gem_sources = {}
- @without_groups = []
+ @current_groups = nil
+ @current_platform = nil
+ @current_repository = nil
+ @default_sources = true
+ @git_set = @set.git_set
+ @requires = Hash.new { |h, name| h[name] = [] }
+ @vendor_set = @set.vendor_set
+ @gem_sources = {}
+ @without_groups = []
+ end
+
+ ##
+ # Adds +dependencies+ to the request set if any of the +groups+ are allowed.
+ # This is used for gemspec dependencies.
+
+ def add_dependencies groups, dependencies # :nodoc:
+ return unless (groups & @without_groups).empty?
+
+ dependencies.each do |dep|
+ @set.gem dep.name, *dep.requirement
+ end
+ end
+
+ private :add_dependencies
+
+ ##
+ # Finds a gemspec with the given +name+ that lives at +path+.
+
+ def find_gemspec name, path # :nodoc:
+ glob = File.join path, "#{name}.gemspec"
+
+ spec_files = Dir[glob]
+
+ case spec_files.length
+ when 1 then
+ spec_file = spec_files.first
+
+ spec = Gem::Specification.load spec_file
+
+ return spec if spec
+
+ raise ArgumentError, "invalid gemspec #{spec_file}"
+ when 0 then
+ raise ArgumentError, "no gemspecs found at #{Dir.pwd}"
+ else
+ raise ArgumentError,
+ "found multiple gemspecs at #{Dir.pwd}, " +
+ "use the name: option to specify the one you want"
+ end
end
##
@@ -160,7 +207,13 @@ class Gem::RequestSet::GemDependencyAPI
options = requirements.pop if requirements.last.kind_of?(Hash)
options ||= {}
- source_set = gem_path name, options
+ options[:git] = @current_repository if @current_repository
+
+ source_set = false
+
+ source_set ||= gem_path name, options
+ source_set ||= gem_git name, options
+ source_set ||= gem_github name, options
return unless gem_platforms options
@@ -182,6 +235,54 @@ class Gem::RequestSet::GemDependencyAPI
end
##
+ # Handles the git: option from +options+ for gem +name+.
+ #
+ # Returns +true+ if the path option was handled.
+
+ def gem_git name, options # :nodoc:
+ if gist = options.delete(:gist) then
+ options[:git] = "https://gist.github.com/#{gist}.git"
+ end
+
+ return unless repository = options.delete(:git)
+
+ raise ArgumentError,
+ "duplicate source git: #{repository} for gem #{name}" if
+ @gem_sources.include? name
+
+ reference = nil
+ reference ||= options.delete :ref
+ reference ||= options.delete :branch
+ reference ||= options.delete :tag
+ reference ||= 'master'
+
+ submodules = options.delete :submodules
+
+ @git_set.add_git_gem name, repository, reference, submodules
+
+ @gem_sources[name] = repository
+
+ true
+ end
+
+ private :gem_git
+
+ ##
+ # Handles the github: option from +options+ for gem +name+.
+ #
+ # Returns +true+ if the path option was handled.
+
+ def gem_github name, options # :nodoc:
+ return unless path = options.delete(:github)
+
+ options[:git] = "git://github.com/#{path}.git"
+
+ gem_git name, options
+
+ true
+ end
+
+ ##
# Handles the :group and :groups +options+ for the gem with the given
# +name+.
@@ -269,6 +370,15 @@ class Gem::RequestSet::GemDependencyAPI
private :gem_requires
+ def git repository
+ @current_repository = repository
+
+ yield
+
+ ensure
+ @current_repository = nil
+ end
+
##
# Returns the basename of the file the dependencies were loaded from
@@ -278,6 +388,29 @@ class Gem::RequestSet::GemDependencyAPI
##
# :category: Gem Dependencies DSL
+ #
+ # Loads dependencies from a gemspec file.
+
+ def gemspec options = {}
+ name = options.delete(:name) || '{,*}'
+ path = options.delete(:path) || '.'
+ development_group = options.delete(:development_group) || :development
+
+ spec = find_gemspec name, path
+
+ groups = gem_group spec.name, {}
+
+ add_dependencies groups, spec.runtime_dependencies
+
+ groups << development_group
+
+ add_dependencies groups, spec.development_dependencies
+
+ gem_requires spec.name, options
+ end
+
+ ##
+ # :category: Gem Dependencies DSL
# Block form for placing a dependency in the given +groups+.
def group *groups
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index a9c419549d..0073bfdcc5 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -1,5 +1,3 @@
-require 'pathname'
-
class Gem::RequestSet::Lockfile
##
@@ -46,8 +44,8 @@ class Gem::RequestSet::Lockfile
def initialize request_set, gem_deps_file
@set = request_set
- @gem_deps_file = Pathname(gem_deps_file).expand_path
- @gem_deps_dir = @gem_deps_file.dirname
+ @gem_deps_file = File.expand_path(gem_deps_file)
+ @gem_deps_dir = File.dirname(@gem_deps_file)
@current_token = nil
@line = 0
@@ -62,7 +60,7 @@ class Gem::RequestSet::Lockfile
@set.dependencies.sort.map do |dependency|
source = @requests.find do |req|
req.name == dependency.name and
- req.spec.class == Gem::DependencyResolver::VendorSpecification
+ req.spec.class == Gem::Resolver::VendorSpecification
end
source_dep = '!' if source
@@ -102,15 +100,26 @@ class Gem::RequestSet::Lockfile
out << nil
end
+ def relative_path_from(dest, base)
+ dest = File.expand_path(dest)
+ base = File.expand_path(base)
+
+ if dest.index(base) == 0
+ return dest[base.size+1..-1]
+ else
+ dest
+ end
+ end
+
def add_PATH out # :nodoc:
return unless path_requests =
- @spec_groups.delete(Gem::DependencyResolver::VendorSpecification)
+ @spec_groups.delete(Gem::Resolver::VendorSpecification)
out << "PATH"
path_requests.each do |request|
- directory = Pathname(request.spec.source.uri).expand_path
+ directory = File.expand_path(request.spec.source.uri)
- out << " remote: #{directory.relative_path_from @gem_deps_dir}"
+ out << " remote: #{relative_path_from directory, @gem_deps_dir}"
out << " specs:"
out << " #{request.name} (#{request.version})"
end
@@ -208,7 +217,7 @@ class Gem::RequestSet::Lockfile
skip :newline
- set = Gem::DependencyResolver::LockSet.new source
+ set = Gem::Resolver::LockSet.new source
while not @tokens.empty? and :text == peek.first do
_, name, = get :text
diff --git a/lib/rubygems/dependency_resolver.rb b/lib/rubygems/resolver.rb
index 35fbe925ad..2669cc4f24 100644
--- a/lib/rubygems/dependency_resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -12,7 +12,7 @@ require 'net/http'
# objects which indicate all the specs that should be activated to meet the
# all the requirements.
-class Gem::DependencyResolver
+class Gem::Resolver
##
# Contains all the conflicts encountered while doing resolution
@@ -38,20 +38,20 @@ class Gem::DependencyResolver
when 1 then
sets.first
else
- Gem::DependencyResolver::ComposedSet.new(*sets)
+ Gem::Resolver::ComposedSet.new(*sets)
end
end
##
- # Provide a DependencyResolver that queries only against the already
+ # Provide a Resolver that queries only against the already
# installed gems.
def self.for_current_gems needed
- new needed, Gem::DependencyResolver::CurrentSet.new
+ new needed, Gem::Resolver::CurrentSet.new
end
##
- # Create DependencyResolver object which will resolve the tree starting
+ # Create Resolver object which will resolve the tree starting
# with +needed+ Dependency objects.
#
# +set+ is an object that provides where to look for specifications to
@@ -59,7 +59,7 @@ class Gem::DependencyResolver
# rubygems.org.
def initialize needed, set = nil
- @set = set || Gem::DependencyResolver::IndexSet.new
+ @set = set || Gem::Resolver::IndexSet.new
@needed = needed
@conflicts = []
@@ -68,6 +68,15 @@ class Gem::DependencyResolver
@soft_missing = false
end
+ DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?
+
+ def explain(stage, *data)
+ if DEBUG_RESOLVER
+ d = data.map { |x| x.inspect }.join(", ")
+ STDOUT.printf "%20s %s\n", stage.to_s.upcase, d
+ end
+ end
+
##
# Creates an ActivationRequest for the given +dep+ and the last +possible+
# specification.
@@ -77,8 +86,10 @@ class Gem::DependencyResolver
def activation_request dep, possible # :nodoc:
spec = possible.pop
+ explain :activate, [spec.full_name, possible.size]
+
activation_request =
- Gem::DependencyResolver::ActivationRequest.new spec, dep, possible
+ Gem::Resolver::ActivationRequest.new spec, dep, possible
return spec, activation_request
end
@@ -86,7 +97,7 @@ class Gem::DependencyResolver
def requests s, act, reqs=nil
s.dependencies.reverse_each do |d|
next if d.type == :development and not @development
- reqs = Gem::List.new Gem::DependencyResolver::DependencyRequest.new(d, act), reqs
+ reqs.add Gem::Resolver::DependencyRequest.new(d, act)
end
@set.prefetch reqs
@@ -100,18 +111,18 @@ class Gem::DependencyResolver
def resolve
@conflicts = []
- needed = nil
+ needed = RequirementList.new
@needed.reverse_each do |n|
- request = Gem::DependencyResolver::DependencyRequest.new n, nil
+ request = Gem::Resolver::DependencyRequest.new n, nil
- needed = Gem::List.new request, needed
+ needed.add request
end
res = resolve_for needed, nil
raise Gem::DependencyResolutionError, res if
- res.kind_of? Gem::DependencyResolver::DependencyConflict
+ res.kind_of? Gem::Resolver::Conflict
res.to_a
end
@@ -128,6 +139,8 @@ class Gem::DependencyResolver
until states.empty? do
state = states.pop
+ explain :consider, state.dep, conflict.failed_dep
+
if conflict.for_spec? state.spec
state.conflicts << [state.spec, conflict]
return state
@@ -162,11 +175,11 @@ class Gem::DependencyResolver
# Otherwise, issue it on the requester's request itself.
if existing.others_possible? or existing.request.requester.nil? then
conflict =
- Gem::DependencyResolver::DependencyConflict.new dep, existing
+ Gem::Resolver::Conflict.new dep, existing
else
- depreq = existing.request.requester.request
+ depreq = dep.requester.request
conflict =
- Gem::DependencyResolver::DependencyConflict.new depreq, existing, dep
+ Gem::Resolver::Conflict.new depreq, existing, dep
end
@conflicts << conflict unless @conflicts.include? conflict
@@ -182,7 +195,7 @@ class Gem::DependencyResolver
# +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, DependencyConflict] hit tried to
+ # +conflicts+ is a [DependencyRequest, Conflict] hit tried to
# activate the state.
#
State = Struct.new(:needed, :specs, :dep, :spec, :possibles, :conflicts) do
@@ -218,9 +231,9 @@ class Gem::DependencyResolver
# The State objects that are used to attempt the activation tree.
states = []
- while needed
- dep = needed.value
- needed = needed.tail
+ while !needed.empty?
+ dep = needed.remove
+ explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
# If there is already a spec activated for the requested name...
if specs && existing = specs.find { |s| dep.name == s.name }
@@ -228,6 +241,7 @@ class Gem::DependencyResolver
next if dep.matches_spec? existing
conflict = handle_conflict dep, existing
+ explain :conflict, conflict.explain
state = find_conflict_state conflict, states
@@ -292,7 +306,9 @@ class Gem::DependencyResolver
# 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, specs, dep, spec, possible, [])
+ states << State.new(needed.dup, specs, dep, spec, possible, [])
+
+ explain :states, states.map { |s| s.dep }
needed = requests spec, act, needed
specs = Gem::List.prepend specs, act
@@ -341,24 +357,32 @@ class Gem::DependencyResolver
end
-require 'rubygems/dependency_resolver/activation_request'
-require 'rubygems/dependency_resolver/dependency_conflict'
-require 'rubygems/dependency_resolver/dependency_request'
-
-require 'rubygems/dependency_resolver/set'
-require 'rubygems/dependency_resolver/api_set'
-require 'rubygems/dependency_resolver/composed_set'
-require 'rubygems/dependency_resolver/best_set'
-require 'rubygems/dependency_resolver/current_set'
-require 'rubygems/dependency_resolver/index_set'
-require 'rubygems/dependency_resolver/installer_set'
-require 'rubygems/dependency_resolver/lock_set'
-require 'rubygems/dependency_resolver/vendor_set'
-
-require 'rubygems/dependency_resolver/specification'
-require 'rubygems/dependency_resolver/spec_specification'
-require 'rubygems/dependency_resolver/api_specification'
-require 'rubygems/dependency_resolver/index_specification'
-require 'rubygems/dependency_resolver/installed_specification'
-require 'rubygems/dependency_resolver/vendor_specification'
+##
+# TODO remove in RubyGems 3
+
+Gem::DependencyResolver = Gem::Resolver # :nodoc:
+
+require 'rubygems/resolver/activation_request'
+require 'rubygems/resolver/conflict'
+require 'rubygems/resolver/dependency_request'
+require 'rubygems/resolver/requirement_list'
+
+require 'rubygems/resolver/set'
+require 'rubygems/resolver/api_set'
+require 'rubygems/resolver/composed_set'
+require 'rubygems/resolver/best_set'
+require 'rubygems/resolver/current_set'
+require 'rubygems/resolver/git_set'
+require 'rubygems/resolver/index_set'
+require 'rubygems/resolver/installer_set'
+require 'rubygems/resolver/lock_set'
+require 'rubygems/resolver/vendor_set'
+
+require 'rubygems/resolver/specification'
+require 'rubygems/resolver/spec_specification'
+require 'rubygems/resolver/api_specification'
+require 'rubygems/resolver/git_specification'
+require 'rubygems/resolver/index_specification'
+require 'rubygems/resolver/installed_specification'
+require 'rubygems/resolver/vendor_specification'
diff --git a/lib/rubygems/dependency_resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
index c5d1e24d85..ca82ac408a 100644
--- a/lib/rubygems/dependency_resolver/activation_request.rb
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -3,7 +3,7 @@
# Also contains a dependency that was used to introduce this
# activation.
-class Gem::DependencyResolver::ActivationRequest
+class Gem::Resolver::ActivationRequest
attr_reader :request
@@ -19,7 +19,7 @@ class Gem::DependencyResolver::ActivationRequest
case other
when Gem::Specification
@spec == other
- when Gem::DependencyResolver::ActivationRequest
+ when Gem::Resolver::ActivationRequest
@spec == other.spec && @request == other.request
else
false
@@ -70,7 +70,7 @@ class Gem::DependencyResolver::ActivationRequest
def installed?
case @spec
- when Gem::DependencyResolver::VendorSpecification then
+ when Gem::Resolver::VendorSpecification then
true
else
this_spec = full_spec
diff --git a/lib/rubygems/dependency_resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index 9dd34562b1..60bf911063 100644
--- a/lib/rubygems/dependency_resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -2,7 +2,7 @@
# The global rubygems pool, available via the rubygems.org API.
# Returns instances of APISpecification.
-class Gem::DependencyResolver::APISet < Gem::DependencyResolver::Set
+class Gem::Resolver::APISet < Gem::Resolver::Set
##
# The URI for the dependency API this APISet uses.
@@ -28,7 +28,7 @@ class Gem::DependencyResolver::APISet < Gem::DependencyResolver::Set
versions(req.name).each do |ver|
if req.dependency.match? req.name, ver[:number]
- res << Gem::DependencyResolver::APISpecification.new(self, ver)
+ res << Gem::Resolver::APISpecification.new(self, ver)
end
end
diff --git a/lib/rubygems/dependency_resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 5178d7c28e..19611e17d8 100644
--- a/lib/rubygems/dependency_resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -4,7 +4,7 @@
# This is used to avoid loading the full Specification object when all we need
# is the name, version, and dependencies.
-class Gem::DependencyResolver::APISpecification < Gem::DependencyResolver::Specification
+class Gem::Resolver::APISpecification < Gem::Resolver::Specification
##
# Creates an APISpecification for the given +set+ from the rubygems.org
diff --git a/lib/rubygems/dependency_resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
index 987eea552e..533a0db58f 100644
--- a/lib/rubygems/dependency_resolver/best_set.rb
+++ b/lib/rubygems/resolver/best_set.rb
@@ -3,7 +3,7 @@
#
# It combines IndexSet and APISet
-class Gem::DependencyResolver::BestSet < Gem::DependencyResolver::ComposedSet
+class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
##
# Creates a BestSet for the given +sources+ or Gem::sources if none are
diff --git a/lib/rubygems/dependency_resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
index aeecf047b8..e4aa15e4d0 100644
--- a/lib/rubygems/dependency_resolver/composed_set.rb
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -1,4 +1,4 @@
-class Gem::DependencyResolver::ComposedSet < Gem::DependencyResolver::Set
+class Gem::Resolver::ComposedSet < Gem::Resolver::Set
attr_reader :sets # :nodoc:
diff --git a/lib/rubygems/dependency_resolver/dependency_conflict.rb b/lib/rubygems/resolver/conflict.rb
index 092f000cdb..b081972658 100644
--- a/lib/rubygems/dependency_resolver/dependency_conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -2,7 +2,7 @@
# Used internally to indicate that a dependency conflicted
# with a spec that would be activated.
-class Gem::DependencyResolver::DependencyConflict
+class Gem::Resolver::Conflict
attr_reader :activated
@@ -23,6 +23,10 @@ class Gem::DependencyResolver::DependencyConflict
@failed_dep == other.failed_dep
end
+ def explain
+ "<Conflict wanted: #{@failed_dep}, had: #{activated.spec.full_name}>"
+ end
+
##
# Return the 2 dependency objects that conflicted
@@ -94,3 +98,5 @@ class Gem::DependencyResolver::DependencyConflict
end
+Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict
+
diff --git a/lib/rubygems/dependency_resolver/current_set.rb b/lib/rubygems/resolver/current_set.rb
index ef15c9d7f3..4e8d34026b 100644
--- a/lib/rubygems/dependency_resolver/current_set.rb
+++ b/lib/rubygems/resolver/current_set.rb
@@ -3,7 +3,7 @@
# all the normal settings that control where to look
# for installed gems.
-class Gem::DependencyResolver::CurrentSet < Gem::DependencyResolver::Set
+class Gem::Resolver::CurrentSet < Gem::Resolver::Set
def find_all req
req.dependency.matching_specs
diff --git a/lib/rubygems/dependency_resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
index 36b77ab558..e63b443c62 100644
--- a/lib/rubygems/dependency_resolver/dependency_request.rb
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -2,7 +2,7 @@
# Used Internally. Wraps a Dependency object to also track which spec
# contained the Dependency.
-class Gem::DependencyResolver::DependencyRequest
+class Gem::Resolver::DependencyRequest
attr_reader :dependency
@@ -17,7 +17,7 @@ class Gem::DependencyResolver::DependencyRequest
case other
when Gem::Dependency
@dependency == other
- when Gem::DependencyResolver::DependencyRequest
+ when Gem::Resolver::DependencyRequest
@dependency == other.dependency && @requester == other.requester
else
false
diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb
new file mode 100644
index 0000000000..3c38d3dca0
--- /dev/null
+++ b/lib/rubygems/resolver/git_set.rb
@@ -0,0 +1,81 @@
+##
+# A GitSet represents gems that are sourced from git repositories.
+#
+# This is used for gem dependency file support.
+#
+# Example:
+#
+# set = Gem::Resolver::GitSet.new
+# set.add_git_gem 'rake', 'git://example/rake.git', tag: 'rake-10.1.0'
+
+class Gem::Resolver::GitSet < Gem::Resolver::Set
+
+ ##
+ # Contains repositories needing submodules
+
+ attr_reader :need_submodules # :nodoc:
+
+ ##
+ # A Hash containing git gem names for keys and a Hash of repository and
+ # git commit reference as values.
+
+ attr_reader :repositories # :nodoc:
+
+ ##
+ # A hash of gem names to Gem::Resolver::GitSpecifications
+
+ attr_reader :specs # :nodoc:
+
+ def initialize # :nodoc:
+ @git = ENV['git'] || 'git'
+ @need_submodules = {}
+ @repositories = {}
+ @specs = {}
+ end
+
+ def add_git_gem name, repository, reference, submodules # :nodoc:
+ @repositories[name] = [repository, reference]
+ @need_submodules[repository] = submodules
+ end
+
+ ##
+ # Finds all git gems matching +req+
+
+ def find_all req
+ @repositories.keys.select do |name|
+ name == req.name
+ end.map do |name|
+ @specs[name] || load_spec(name)
+ end.select do |spec|
+ req.matches_spec? spec
+ end
+ end
+
+ def load_spec name
+ repository, reference = @repositories[name]
+
+ source = Gem::Source::Git.new name, repository, reference
+
+ spec = source.load_spec name
+
+ git_spec =
+ Gem::Resolver::GitSpecification.new self, spec, source
+
+ @specs[name] = git_spec
+ end
+
+ ##
+ # Prefetches specifications from the git repositories in this set.
+
+ def prefetch reqs
+ names = reqs.map { |req| req.name }
+
+ @repositories.each_key do |name|
+ next unless names.include? name
+
+ load_spec name
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb
new file mode 100644
index 0000000000..ac8d4e9aeb
--- /dev/null
+++ b/lib/rubygems/resolver/git_specification.rb
@@ -0,0 +1,16 @@
+##
+# A GitSpecification represents a gem that is sourced from a git repository
+# and is being loaded through a gem dependencies file through the +git:+
+# option.
+
+class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @spec == other.spec and
+ @source == other.source
+ end
+
+end
+
diff --git a/lib/rubygems/dependency_resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb
index 04d6ec816f..0ba3c78a44 100644
--- a/lib/rubygems/dependency_resolver/index_set.rb
+++ b/lib/rubygems/resolver/index_set.rb
@@ -2,7 +2,7 @@
# The global rubygems pool represented via the traditional
# source index.
-class Gem::DependencyResolver::IndexSet < Gem::DependencyResolver::Set
+class Gem::Resolver::IndexSet < Gem::Resolver::Set
def initialize source = nil # :nodoc:
@f =
@@ -38,7 +38,7 @@ class Gem::DependencyResolver::IndexSet < Gem::DependencyResolver::Set
@all[name].each do |uri, n|
if req.dependency.match? n then
- res << Gem::DependencyResolver::IndexSpecification.new(
+ res << Gem::Resolver::IndexSpecification.new(
self, n.name, n.version, uri, n.platform)
end
end
diff --git a/lib/rubygems/dependency_resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
index 9b4057f0c8..56fecb5753 100644
--- a/lib/rubygems/dependency_resolver/index_specification.rb
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -3,7 +3,7 @@
# delay needed to download full Specification objects when only the +name+
# and +version+ are needed.
-class Gem::DependencyResolver::IndexSpecification < Gem::DependencyResolver::Specification
+class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
##
# An IndexSpecification is created from the index format described in `gem
diff --git a/lib/rubygems/dependency_resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb
index 4b591661a8..647ff7499a 100644
--- a/lib/rubygems/dependency_resolver/installed_specification.rb
+++ b/lib/rubygems/resolver/installed_specification.rb
@@ -2,7 +2,7 @@
# An InstalledSpecification represents a gem that is already installed
# locally.
-class Gem::DependencyResolver::InstalledSpecification < Gem::DependencyResolver::SpecSpecification
+class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
def == other # :nodoc:
self.class === other and
diff --git a/lib/rubygems/dependency_resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index 801b60a8bb..73d9e39651 100644
--- a/lib/rubygems/dependency_resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -2,7 +2,7 @@
# A set of gems for installation sourced from remote sources and local .gem
# files
-class Gem::DependencyResolver::InstallerSet < Gem::DependencyResolver::Set
+class Gem::Resolver::InstallerSet < Gem::Resolver::Set
##
# List of Gem::Specification objects that must always be installed.
@@ -64,14 +64,14 @@ class Gem::DependencyResolver::InstallerSet < Gem::DependencyResolver::Set
dep.matching_specs.each do |gemspec|
next if @always_install.include? gemspec
- res << Gem::DependencyResolver::InstalledSpecification.new(self, gemspec)
+ res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed
if consider_local? then
local_source = Gem::Source::Local.new
if spec = local_source.find_gem(name, dep.requirement) then
- res << Gem::DependencyResolver::IndexSpecification.new(
+ res << Gem::Resolver::IndexSpecification.new(
self, spec.name, spec.version, local_source, spec.platform)
end
end
@@ -81,7 +81,7 @@ class Gem::DependencyResolver::InstallerSet < Gem::DependencyResolver::Set
@all[name].each do |remote_source, n|
if dep.match? n then
- res << Gem::DependencyResolver::IndexSpecification.new(
+ res << Gem::Resolver::IndexSpecification.new(
self, n.name, n.version, remote_source, n.platform)
end
end
diff --git a/lib/rubygems/dependency_resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
index f95c7f0fd6..6885e70945 100644
--- a/lib/rubygems/dependency_resolver/lock_set.rb
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -1,7 +1,7 @@
##
# A set of gems from a gem dependencies lockfile.
-class Gem::DependencyResolver::LockSet < Gem::DependencyResolver::Set
+class Gem::Resolver::LockSet < Gem::Resolver::Set
attr_reader :specs # :nodoc:
@@ -24,8 +24,8 @@ class Gem::DependencyResolver::LockSet < Gem::DependencyResolver::Set
version = Gem::Version.new version
spec =
- Gem::DependencyResolver::IndexSpecification.new self, name, version,
- @source, platform
+ Gem::Resolver::IndexSpecification.new self, name, version, @source,
+ platform
@specs << spec
end
diff --git a/lib/rubygems/resolver/requirement_list.rb b/lib/rubygems/resolver/requirement_list.rb
new file mode 100644
index 0000000000..8123e84fc7
--- /dev/null
+++ b/lib/rubygems/resolver/requirement_list.rb
@@ -0,0 +1,40 @@
+##
+# Used internally to hold the requirements being considered
+# while attempting to find a proper activation set.
+
+class Gem::Resolver::RequirementList
+
+ include Enumerable
+
+ def initialize
+ @list = []
+ end
+
+ def initialize_copy(other)
+ @list = @list.dup
+ end
+
+ def add(req)
+ @list.push req
+ req
+ end
+
+ ##
+ # Enumerates requirements in the list
+
+ def each # :nodoc:
+ return enum_for __method__ unless block_given?
+
+ @list.each do |requirement|
+ yield requirement
+ end
+ end
+
+ def empty?
+ @list.empty?
+ end
+
+ def remove
+ @list.shift
+ end
+end
diff --git a/lib/rubygems/dependency_resolver/set.rb b/lib/rubygems/resolver/set.rb
index 65801871ac..32c137ef6b 100644
--- a/lib/rubygems/dependency_resolver/set.rb
+++ b/lib/rubygems/resolver/set.rb
@@ -1,13 +1,12 @@
##
-# DependencyResolver sets are used to look up specifications (and their
+# Resolver sets are used to look up specifications (and their
# dependencies) used in resolution. This set is abstract.
-class Gem::DependencyResolver::Set
+class Gem::Resolver::Set
##
- # The find_all method must be implemented. It returns all
- # DependencyResolver Specification objects matching the given
- # DependencyRequest +req+.
+ # The find_all method must be implemented. It returns all Resolver
+ # Specification objects matching the given DependencyRequest +req+.
def find_all req
raise NotImplementedError
diff --git a/lib/rubygems/dependency_resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
index cca1d58b9f..0c411bdf5f 100644
--- a/lib/rubygems/dependency_resolver/spec_specification.rb
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -1,8 +1,8 @@
##
-# The DependencyResolver::SpecSpecification contains common functionality for
-# DependencyResolver specifications that are backed by a Gem::Specification.
+# The Resolver::SpecSpecification contains common functionality for
+# Resolver specifications that are backed by a Gem::Specification.
-class Gem::DependencyResolver::SpecSpecification < Gem::DependencyResolver::Specification
+class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
attr_reader :spec # :nodoc:
diff --git a/lib/rubygems/dependency_resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index 6fbd241316..7dd4c2e829 100644
--- a/lib/rubygems/dependency_resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -1,9 +1,9 @@
##
-# A DependencyResolver::Specification contains a subset of the information
+# A Resolver::Specification contains a subset of the information
# contained in a Gem::Specification. Only the information necessary for
# dependency resolution in the resolver is included.
-class Gem::DependencyResolver::Specification
+class Gem::Resolver::Specification
##
# The dependencies of the gem for this specification
diff --git a/lib/rubygems/dependency_resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb
index 87eb6fd818..e9cbcd8303 100644
--- a/lib/rubygems/dependency_resolver/vendor_set.rb
+++ b/lib/rubygems/resolver/vendor_set.rb
@@ -6,14 +6,14 @@
#
# Example:
#
-# set = Gem::DependencyResolver::VendorSet.new
+# set = Gem::Resolver::VendorSet.new
#
# set.add_vendor_gem 'rake', 'vendor/rake'
#
# The directory vendor/rake must contain an unpacked rake gem along with a
# rake.gemspec (watching the given name).
-class Gem::DependencyResolver::VendorSet < Gem::DependencyResolver::Set
+class Gem::Resolver::VendorSet < Gem::Resolver::Set
def initialize # :nodoc:
@directories = {}
@@ -47,7 +47,7 @@ class Gem::DependencyResolver::VendorSet < Gem::DependencyResolver::Set
req.matches_spec? spec
end.map do |spec|
source = Gem::Source::Vendor.new @directories[spec]
- Gem::DependencyResolver::VendorSpecification.new self, spec, source
+ Gem::Resolver::VendorSpecification.new self, spec, source
end
end
diff --git a/lib/rubygems/dependency_resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb
index 27b2fd6df2..24e033d084 100644
--- a/lib/rubygems/dependency_resolver/vendor_specification.rb
+++ b/lib/rubygems/resolver/vendor_specification.rb
@@ -3,7 +3,7 @@
# and is being loaded through a gem dependencies file through the +path:+
# option.
-class Gem::DependencyResolver::VendorSpecification < Gem::DependencyResolver::SpecSpecification
+class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
def == other # :nodoc:
self.class === other and
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index b39b3ae69d..a40a27594b 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -28,7 +28,9 @@ class Gem::Source
case other
when Gem::Source::Installed,
Gem::Source::Local,
- Gem::Source::SpecificFile then
+ Gem::Source::SpecificFile,
+ Gem::Source::Git,
+ Gem::Source::Vendor then
-1
when Gem::Source then
if !@uri
@@ -62,9 +64,9 @@ class Gem::Source
fetcher = Gem::RemoteFetcher.fetcher
fetcher.fetch_path bundler_api_uri, nil, true
rescue Gem::RemoteFetcher::FetchError
- Gem::DependencyResolver::IndexSet.new self
+ Gem::Resolver::IndexSet.new self
else
- Gem::DependencyResolver::APISet.new bundler_api_uri
+ Gem::Resolver::APISet.new bundler_api_uri
end
end
@@ -90,12 +92,15 @@ class Gem::Source
end
end
- def fetch_spec(name)
+ ##
+ # Fetches a specification for the given +name_tuple+.
+
+ def fetch_spec name_tuple
fetcher = Gem::RemoteFetcher.fetcher
- spec_file_name = name.spec_name
+ spec_file_name = name_tuple.spec_name
- uri = @uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
+ uri = api_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
cache_dir = cache_dir uri
@@ -139,7 +144,7 @@ class Gem::Source
file = FILES[type]
fetcher = Gem::RemoteFetcher.fetcher
file_name = "#{file}.#{Gem.marshal_version}"
- spec_path = @uri + "#{file_name}.gz"
+ spec_path = api_uri + "#{file_name}.gz"
cache_dir = cache_dir spec_path
local_file = File.join(cache_dir, file_name)
retried = false
@@ -163,18 +168,22 @@ class Gem::Source
def download(spec, dir=Dir.pwd)
fetcher = Gem::RemoteFetcher.fetcher
- fetcher.download spec, @uri.to_s, dir
+ fetcher.download spec, api_uri.to_s, dir
end
def pretty_print q # :nodoc:
q.group 2, '[Remote:', ']' do
q.breakable
q.text @uri.to_s
+ if api = api_uri
+ g.text api
+ end
end
end
end
+require 'rubygems/source/git'
require 'rubygems/source/installed'
require 'rubygems/source/specific_file'
require 'rubygems/source/local'
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
new file mode 100644
index 0000000000..8453aa5fd7
--- /dev/null
+++ b/lib/rubygems/source/git.rb
@@ -0,0 +1,189 @@
+require 'digest'
+require 'rubygems/util'
+
+##
+# A git gem for use in a gem dependencies file.
+#
+# Example:
+#
+# source =
+# Gem::Source::Git.new 'rake', 'git@example:rake.git', 'rake-10.1.0', false
+#
+# spec = source.load_spec 'rake'
+#
+# source.checkout
+
+class Gem::Source::Git < Gem::Source
+
+ ##
+ # The name of the gem created by this git gem.
+
+ attr_reader :name
+
+ ##
+ # The commit reference used for checking out this git gem.
+
+ attr_reader :reference
+
+ ##
+ # The git repository this gem is sourced from.
+
+ attr_reader :repository
+
+ ##
+ # Does this repository need submodules checked out too?
+
+ attr_reader :need_submodules
+
+ ##
+ # Creates a new git gem source for a gem with the given +name+ that will be
+ # loaded from +reference+ in +repository+. If +submodules+ is true,
+ # submodules will be checked out when the gem is installed.
+
+ def initialize name, repository, reference, submodules = false
+ super(nil)
+
+ @name = name
+ @repository = repository
+ @reference = reference
+ @need_submodules = submodules
+
+ @git = ENV['git'] || 'git'
+ end
+
+ def <=> other
+ case other
+ when Gem::Source::Git then
+ 0
+ when Gem::Source::Installed then
+ -1
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
+ end
+
+ def == other # :nodoc:
+ super and
+ @name == other.name and
+ @repository == other.repository and
+ @reference == other.reference and
+ @need_submodules == other.need_submodules
+ end
+
+ ##
+ # Checks out the files for the repository into the install_dir.
+
+ def checkout # :nodoc:
+ cache
+
+ unless File.exist? install_dir then
+ system @git, 'clone', '--quiet', '--no-checkout',
+ repo_cache_dir, install_dir
+ end
+
+ Dir.chdir install_dir do
+ system @git, 'fetch', '--quiet', '--force', '--tags', install_dir
+
+ success = system @git, 'reset', '--quiet', '--hard', @reference
+
+ success &&=
+ system @git, 'submodule', 'update',
+ '--quiet', '--init', '--recursive' if @need_submodules
+
+ success
+ end
+ end
+
+ ##
+ # Creates a local cache repository for the git gem.
+
+ def cache # :nodoc:
+ if File.exist? repo_cache_dir then
+ Dir.chdir repo_cache_dir do
+ system @git, 'fetch', '--quiet', '--force', '--tags',
+ @repository, 'refs/heads/*:refs/heads/*'
+ end
+ else
+ system @git, 'clone', '--quiet', '--bare', '--no-hardlinks',
+ @repository, repo_cache_dir
+ end
+ end
+
+ ##
+ # A short reference for use in git gem directories
+
+ def dir_shortref # :nodoc:
+ rev_parse[0..11]
+ end
+
+ ##
+ # The directory where the git gem will be installed.
+
+ def install_dir # :nodoc:
+ File.join Gem.dir, 'bundler', 'gems', "#{@name}-#{dir_shortref}"
+ end
+
+ ##
+ # Loads a Gem::Specification for +name+ from this git repository.
+
+ def load_spec name
+ cache
+
+ gemspec_reference = "#{@reference}:#{name}.gemspec"
+
+ Dir.chdir repo_cache_dir do
+ source = Gem::Util.popen @git, 'show', gemspec_reference
+
+ source.force_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+ source.untaint
+
+ begin
+ spec = eval source, binding, gemspec_reference
+
+ return spec if Gem::Specification === spec
+
+ warn "git gem specification for #{@repository} #{gemspec_reference} is not a Gem::Specification (#{spec.class} instead)."
+ rescue SignalException, SystemExit
+ raise
+ rescue SyntaxError, Exception
+ warn "invalid git gem specification for #{@repository} #{gemspec_reference}"
+ end
+ end
+ end
+
+ ##
+ # The directory where the git gem's repository will be cached.
+
+ def repo_cache_dir # :nodoc:
+ File.join Gem.dir, 'cache', 'bundler', 'git', "#{@name}-#{uri_hash}"
+ end
+
+ ##
+ # Converts the git reference for the repository into a commit hash.
+
+ def rev_parse # :nodoc:
+ # HACK no safe equivalent of ` exists on 1.8.7
+ Dir.chdir repo_cache_dir do
+ Gem::Util.popen(@git, 'rev-parse', @reference).strip
+ end
+ end
+
+ ##
+ # A hash for the git gem based on the git repository URI.
+
+ def uri_hash # :nodoc:
+ normalized =
+ if @repository =~ %r%^\w+://(\w+@)?% then
+ uri = URI(@repository).normalize.to_s.sub %r%/$%,''
+ uri.sub(/\A(\w+)/) { $1.downcase }
+ else
+ @repository
+ end
+
+ Digest::SHA1.hexdigest normalized
+ end
+
+end
+
diff --git a/lib/rubygems/source/installed.rb b/lib/rubygems/source/installed.rb
index 8e3a3560bf..2661dd6844 100644
--- a/lib/rubygems/source/installed.rb
+++ b/lib/rubygems/source/installed.rb
@@ -1,6 +1,9 @@
+##
+# Represents an installed gem. This is used for dependency resolution.
+
class Gem::Source::Installed < Gem::Source
- def initialize
+ def initialize # :nodoc:
@uri = nil
end
@@ -9,6 +12,8 @@ class Gem::Source::Installed < Gem::Source
def <=> other
case other
+ when Gem::Source::Vendor then
+ -1
when Gem::Source::Installed then
0
when Gem::Source then
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
index 16a028b2f3..3aae20c8ed 100644
--- a/lib/rubygems/source/local.rb
+++ b/lib/rubygems/source/local.rb
@@ -1,5 +1,10 @@
+##
+# The local source finds gems in the current directory for fulfilling
+# dependencies.
+
class Gem::Source::Local < Gem::Source
- def initialize
+
+ def initialize # :nodoc:
@specs = nil
@api_uri = nil
@uri = nil
@@ -26,7 +31,7 @@ class Gem::Source::Local < Gem::Source
"#<%s specs: %p>" % [self.class, keys]
end
- def load_specs(type)
+ def load_specs type # :nodoc:
names = []
@specs = {}
@@ -68,8 +73,8 @@ class Gem::Source::Local < Gem::Source
names
end
- def find_gem(gem_name, version=Gem::Requirement.default,
- prerelease=false)
+ def find_gem gem_name, version = Gem::Requirement.default, # :nodoc:
+ prerelease = false
load_specs :complete
found = []
@@ -91,7 +96,7 @@ class Gem::Source::Local < Gem::Source
found.max_by { |s| s.version }
end
- def fetch_spec(name)
+ def fetch_spec name # :nodoc:
load_specs :complete
if data = @specs[name]
@@ -101,7 +106,7 @@ class Gem::Source::Local < Gem::Source
end
end
- def download(spec, cache_dir=nil)
+ def download spec, cache_dir = nil # :nodoc:
load_specs :complete
@specs.each do |name, data|
diff --git a/lib/rubygems/source/specific_file.rb b/lib/rubygems/source/specific_file.rb
index 8d328b38c2..a7b6c53542 100644
--- a/lib/rubygems/source/specific_file.rb
+++ b/lib/rubygems/source/specific_file.rb
@@ -1,4 +1,12 @@
+##
+# A source representing a single .gem file. This is used for installation of
+# local gems.
+
class Gem::Source::SpecificFile < Gem::Source
+
+ ##
+ # Creates a new SpecificFile for the gem in +file+
+
def initialize(file)
@uri = nil
@path = ::File.expand_path(file)
@@ -8,19 +16,22 @@ class Gem::Source::SpecificFile < Gem::Source
@name = @spec.name_tuple
end
+ ##
+ # The Gem::Specification extracted from this .gem.
+
attr_reader :spec
- def load_specs(*a)
+ def load_specs *a # :nodoc:
[@name]
end
- def fetch_spec(name)
+ def fetch_spec name # :nodoc:
return @spec if name == @name
raise Gem::Exception, "Unable to find '#{name}'"
@spec
end
- def download(spec, dir=nil)
+ def download spec, dir = nil # :nodoc:
return @path if spec == @spec
raise Gem::Exception, "Unable to download '#{spec.full_name}'"
end
diff --git a/lib/rubygems/source/vendor.rb b/lib/rubygems/source/vendor.rb
index f2cf540c8d..244c4201d8 100644
--- a/lib/rubygems/source/vendor.rb
+++ b/lib/rubygems/source/vendor.rb
@@ -3,8 +3,22 @@
class Gem::Source::Vendor < Gem::Source::Installed
- def initialize uri
- @uri = uri
+ ##
+ # Creates a new Vendor source for a gem that was unpacked at +path+.
+
+ def initialize path
+ @uri = path
+ end
+
+ def <=> other
+ case other
+ when Gem::Source::Vendor then
+ 0
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
end
end
diff --git a/lib/rubygems/source_list.rb b/lib/rubygems/source_list.rb
index e6da50c2e5..e01f11cc1e 100644
--- a/lib/rubygems/source_list.rb
+++ b/lib/rubygems/source_list.rb
@@ -1,5 +1,18 @@
require 'rubygems/source'
+##
+# The SourceList represents the sources rubygems has been configured to use.
+# A source may be created from an array of sources:
+#
+# Gem::SourceList.from %w[https://rubygems.example https://internal.example]
+#
+# Or by adding them:
+#
+# sources = Gem::SourceList.new
+# sources.add 'https://rubygems.example'
+#
+# The most common way to get a SourceList is Gem.sources.
+
class Gem::SourceList
include Enumerable
@@ -91,7 +104,7 @@ class Gem::SourceList
@sources.empty?
end
- def ==(other)
+ def == other # :nodoc:
to_a == other
end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 22fa68db69..12b8fb27d8 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -34,6 +34,10 @@ class Gem::SpecFetcher
@fetcher = nil
+ ##
+ # Default fetcher instance. Use this instead of ::new to reduce object
+ # allocation.
+
def self.fetcher
@fetcher ||= new
end
@@ -43,8 +47,8 @@ class Gem::SpecFetcher
end
##
- # Creates a new SpecFetcher. Ordinarily you want to use
- # Gem::SpecFetcher::fetcher which uses the Gem.sources.
+ # Creates a new SpecFetcher. Ordinarily you want to use the default fetcher
+ # from Gem::SpecFetcher::fetcher which uses the Gem.sources.
#
# If you need to retrieve specifications from a different +source+, you can
# send it as an argument.
@@ -84,7 +88,11 @@ class Gem::SpecFetcher
rejected_specs = {}
if dependency.prerelease?
- type = :complete
+ if dependency.specific?
+ type = :complete
+ else
+ type = :abs_latest
+ end
elsif dependency.latest_version?
type = :latest
else
@@ -224,6 +232,12 @@ class Gem::SpecFetcher
tuples_for(source, :released)
names.sort
+ when :abs_latest
+ names =
+ tuples_for(source, :prerelease, true) +
+ tuples_for(source, :latest)
+
+ names.sort
when :prerelease
tuples_for(source, :prerelease)
else
@@ -239,7 +253,11 @@ class Gem::SpecFetcher
[list, errors]
end
- def tuples_for(source, type, gracefully_ignore=false)
+ ##
+ # Retrieves NameTuples from +source+ of the given +type+ (:prerelease,
+ # etc.). If +gracefully_ignore+ is true, errors are ignored.
+
+ def tuples_for(source, type, gracefully_ignore=false) # :nodoc:
cache = @caches[type]
tuples =
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 308aa6f011..b95e2c0699 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -12,6 +12,7 @@ require 'rubygems/platform'
require 'rubygems/deprecate'
require 'rubygems/basic_specification'
require 'rubygems/stub_specification'
+require 'rubygems/util/stringio'
# :stopdoc:
# date.rb can't be loaded for `make install` due to miniruby
@@ -2165,7 +2166,7 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # Used by Gem::DependencyResolver to order Gem::Specification objects
+ # Used by Gem::Resolver to order Gem::Specification objects
def source # :nodoc:
self
@@ -2363,7 +2364,7 @@ class Gem::Specification < Gem::BasicSpecification
builder << self
ast = builder.tree
- io = StringIO.new
+ io = Gem::StringSink.new
io.set_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
Psych::Visitors::Emitter.new(io).accept(ast)
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index b04cbfc1f3..d1b471f619 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -233,6 +233,8 @@ class Gem::TestCase < MiniTest::Unit::TestCase
ruby
end
+ @git = ENV['GIT'] || 'git'
+
Gem.ensure_gem_subdirectories @gemhome
@orig_LOAD_PATH = $LOAD_PATH.dup
@@ -373,6 +375,64 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
##
+ # A git_gem is used with a gem dependencies file. The gem created here
+ # has no files, just a gem specification for the given +name+ and +version+.
+ #
+ # Yields the +specification+ to the block, if given
+
+ def git_gem name = 'a', version = 1
+ have_git?
+
+ directory = File.join 'git', name
+ directory = File.expand_path directory
+
+ git_spec = Gem::Specification.new name, version do |specification|
+ yield specification if block_given?
+ end
+
+ FileUtils.mkdir_p directory
+
+ gemspec = "#{name}.gemspec"
+
+ open File.join(directory, gemspec), 'w' do |io|
+ io.write git_spec.to_ruby
+ end
+
+ head = nil
+
+ Dir.chdir directory do
+ unless File.exist? '.git' then
+ system @git, 'init', '--quiet'
+ system @git, 'config', 'user.name', 'RubyGems Tests'
+ system @git, 'config', 'user.email', 'rubygems@example'
+ end
+
+ system @git, 'add', gemspec
+ system @git, 'commit', '-a', '-m', 'a non-empty commit message', '--quiet'
+ head = Gem::Util.popen('git', 'rev-parse', 'master').strip
+ end
+
+ return name, git_spec.version, directory, head
+ end
+
+ ##
+ # Skips this test unless you have a git executable
+
+ def have_git?
+ return if in_path? @git
+
+ skip 'cannot find git executable, use GIT environment variable to set'
+ end
+
+ def in_path? executable # :nodoc:
+ return true if %r%\A([A-Z]:|/)% =~ executable and File.exist? executable
+
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
+ File.exist? File.join directory, executable
+ end
+ end
+
+ ##
# Builds and installs the Gem::Specification +spec+
def install_gem spec, options = {}
@@ -1082,21 +1142,21 @@ Also, a list:
end
##
- # Constructs a Gem::DependencyResolver::DependencyRequest from a
+ # Constructs a Gem::Resolver::DependencyRequest from a
# Gem::Dependency +dep+, a +from_name+ and +from_version+ requesting the
# dependency and a +parent+ DependencyRequest
def dependency_request dep, from_name, from_version, parent = nil
remote = Gem::Source.new @uri
- parent ||= Gem::DependencyResolver::DependencyRequest.new \
+ parent ||= Gem::Resolver::DependencyRequest.new \
dep, nil
- spec = Gem::DependencyResolver::IndexSpecification.new \
+ spec = Gem::Resolver::IndexSpecification.new \
nil, from_name, from_version, remote, Gem::Platform::RUBY
- activation = Gem::DependencyResolver::ActivationRequest.new spec, parent
+ activation = Gem::Resolver::ActivationRequest.new spec, parent
- Gem::DependencyResolver::DependencyRequest.new dep, activation
+ Gem::Resolver::DependencyRequest.new dep, activation
end
##
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
new file mode 100644
index 0000000000..e862458d21
--- /dev/null
+++ b/lib/rubygems/util.rb
@@ -0,0 +1,65 @@
+module Gem::Util
+ ##
+ # Zlib::GzipReader wrapper that unzips +data+.
+
+ def self.gunzip(data)
+ require 'zlib'
+ require 'rubygems/util/stringio'
+ data = Gem::StringSource.new data
+
+ unzipped = Zlib::GzipReader.new(data).read
+ unzipped.force_encoding Encoding::BINARY if Object.const_defined? :Encoding
+ unzipped
+ end
+
+ ##
+ # Zlib::GzipWriter wrapper that zips +data+.
+
+ def self.gzip(data)
+ require 'zlib'
+ require 'rubygems/util/stringio'
+ zipped = Gem::StringSink.new
+ zipped.set_encoding Encoding::BINARY if Object.const_defined? :Encoding
+
+ Zlib::GzipWriter.wrap zipped do |io| io.write data end
+
+ zipped.string
+ end
+
+ ##
+ # A Zlib::Inflate#inflate wrapper
+
+ def self.inflate(data)
+ require 'zlib'
+ Zlib::Inflate.inflate data
+ end
+
+ ##
+ # This calls IO.popen where it accepts an array for a +command+ (Ruby 1.9+)
+ # and implements an IO.popen-like behavior where it does not accept an array
+ # for a command.
+
+ def self.popen *command
+ begin
+ r, = IO.popen command
+ rescue TypeError # ruby 1.8 only supports string command
+ r, w = IO.pipe
+
+ pid = fork do
+ STDIN.close
+ STDOUT.reopen w
+
+ exec(*command)
+ end
+
+ w.close
+
+ Process.wait pid
+
+ r
+ end
+
+ r.read
+ end
+
+end
diff --git a/lib/rubygems/util/stringio.rb b/lib/rubygems/util/stringio.rb
new file mode 100644
index 0000000000..2ea69617bc
--- /dev/null
+++ b/lib/rubygems/util/stringio.rb
@@ -0,0 +1,34 @@
+class Gem::StringSink
+ def initialize
+ @string = ""
+ end
+
+ attr_reader :string
+
+ def write(s)
+ @string += s
+ s.size
+ end
+
+ def set_encoding(enc)
+ @string.force_encoding enc
+ end
+end
+
+class Gem::StringSource
+ def initialize(str)
+ @string = str.dup
+ end
+
+ def read(count=nil)
+ if count
+ @string.slice!(0,count)
+ else
+ s = @string
+ @string = ""
+ s
+ end
+ end
+
+ alias_method :readpartial, :read
+end