summaryrefslogtreecommitdiff
path: root/lib/rubygems/resolver
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/rubygems/resolver
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/rubygems/resolver')
-rw-r--r--lib/rubygems/resolver/activation_request.rb138
-rw-r--r--lib/rubygems/resolver/api_set.rb75
-rw-r--r--lib/rubygems/resolver/api_specification.rb38
-rw-r--r--lib/rubygems/resolver/best_set.rb21
-rw-r--r--lib/rubygems/resolver/composed_set.rb20
-rw-r--r--lib/rubygems/resolver/conflict.rb102
-rw-r--r--lib/rubygems/resolver/current_set.rb13
-rw-r--r--lib/rubygems/resolver/dependency_request.rb71
-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.rb50
-rw-r--r--lib/rubygems/resolver/index_specification.rb69
-rw-r--r--lib/rubygems/resolver/installed_specification.rb34
-rw-r--r--lib/rubygems/resolver/installer_set.rb152
-rw-r--r--lib/rubygems/resolver/lock_set.rb60
-rw-r--r--lib/rubygems/resolver/requirement_list.rb40
-rw-r--r--lib/rubygems/resolver/set.rb27
-rw-r--r--lib/rubygems/resolver/spec_specification.rb58
-rw-r--r--lib/rubygems/resolver/specification.rb60
-rw-r--r--lib/rubygems/resolver/vendor_set.rb66
-rw-r--r--lib/rubygems/resolver/vendor_specification.rb16
21 files changed, 1207 insertions, 0 deletions
diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
new file mode 100644
index 0000000000..ca82ac408a
--- /dev/null
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -0,0 +1,138 @@
+##
+# Specifies a Specification object that should be activated.
+# Also contains a dependency that was used to introduce this
+# activation.
+
+class Gem::Resolver::ActivationRequest
+
+ attr_reader :request
+
+ attr_reader :spec
+
+ def initialize spec, req, others_possible = true
+ @spec = spec
+ @request = req
+ @others_possible = others_possible
+ end
+
+ def == other
+ case other
+ when Gem::Specification
+ @spec == other
+ when Gem::Resolver::ActivationRequest
+ @spec == other.spec && @request == other.request
+ else
+ false
+ end
+ end
+
+ def download path
+ if @spec.respond_to? :source
+ source = @spec.source
+ else
+ source = Gem.sources.first
+ end
+
+ Gem.ensure_gem_subdirectories path
+
+ source.download full_spec, path
+ end
+
+ def full_name
+ @spec.full_name
+ end
+
+ def full_spec
+ Gem::Specification === @spec ? @spec : @spec.spec
+ end
+
+ def inspect # :nodoc:
+ others =
+ case @others_possible
+ when true then # TODO remove at RubyGems 3
+ ' (others possible)'
+ when false then # TODO remove at RubyGems 3
+ nil
+ else
+ unless @others_possible.empty? then
+ others = @others_possible.map { |s| s.full_name }
+ " (others possible: #{others.join ', '})"
+ end
+ end
+
+ '#<%s for %p from %s%s>' % [
+ self.class, @spec, @request, others
+ ]
+ end
+
+ ##
+ # Indicates if the requested gem has already been installed.
+
+ def installed?
+ case @spec
+ when Gem::Resolver::VendorSpecification then
+ true
+ else
+ this_spec = full_spec
+
+ Gem::Specification.any? do |s|
+ s == this_spec
+ end
+ end
+ end
+
+ def name
+ @spec.name
+ end
+
+ ##
+ # Indicate if this activation is one of a set of possible
+ # requests for the same Dependency request.
+
+ def others_possible?
+ case @others_possible
+ when true, false then
+ @others_possible
+ else
+ not @others_possible.empty?
+ end
+ end
+
+ ##
+ # Return the ActivationRequest that contained the dependency
+ # that we were activated for.
+
+ def parent
+ @request.requester
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Activation request', ']' do
+ q.breakable
+ q.pp @spec
+
+ q.breakable
+ q.text ' for '
+ q.pp @request
+
+ case @others_possible
+ when false then
+ when true then
+ q.breakable
+ q.text 'others possible'
+ else
+ unless @others_possible.empty? then
+ q.breakable
+ q.text 'others '
+ q.pp @others_possible.map { |s| s.full_name }
+ end
+ end
+ end
+ end
+
+ def version
+ @spec.version
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
new file mode 100644
index 0000000000..60bf911063
--- /dev/null
+++ b/lib/rubygems/resolver/api_set.rb
@@ -0,0 +1,75 @@
+##
+# The global rubygems pool, available via the rubygems.org API.
+# Returns instances of APISpecification.
+
+class Gem::Resolver::APISet < Gem::Resolver::Set
+
+ ##
+ # The URI for the dependency API this APISet uses.
+
+ attr_reader :dep_uri # :nodoc:
+
+ ##
+ # Creates a new APISet that will retrieve gems from +uri+ using the RubyGems
+ # API described at http://guides.rubygems.org/rubygems-org-api
+
+ def initialize uri = 'https://rubygems.org/api/v1/dependencies'
+ uri = URI uri unless URI === uri # for ruby 1.8
+ @data = Hash.new { |h,k| h[k] = [] }
+ @dep_uri = uri
+ end
+
+ ##
+ # Return an array of APISpecification objects matching
+ # DependencyRequest +req+.
+
+ def find_all req
+ res = []
+
+ versions(req.name).each do |ver|
+ if req.dependency.match? req.name, ver[:number]
+ res << Gem::Resolver::APISpecification.new(self, ver)
+ end
+ end
+
+ res
+ end
+
+ ##
+ # A hint run by the resolver to allow the Set to fetch
+ # data for DependencyRequests +reqs+.
+
+ def prefetch reqs
+ names = reqs.map { |r| r.dependency.name }
+ needed = names.find_all { |d| !@data.key?(d) }
+
+ return if needed.empty?
+
+ uri = @dep_uri + "?gems=#{needed.sort.join ','}"
+ str = Gem::RemoteFetcher.fetcher.fetch_path uri
+
+ Marshal.load(str).each do |ver|
+ @data[ver[:name]] << ver
+ end
+ end
+
+ ##
+ # Return data for all versions of the gem +name+.
+
+ def versions name # :nodoc:
+ if @data.key?(name)
+ return @data[name]
+ end
+
+ uri = @dep_uri + "?gems=#{name}"
+ str = Gem::RemoteFetcher.fetcher.fetch_path uri
+
+ Marshal.load(str).each do |ver|
+ @data[ver[:name]] << ver
+ end
+
+ @data[name]
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
new file mode 100644
index 0000000000..19611e17d8
--- /dev/null
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -0,0 +1,38 @@
+##
+# Represents a specification retrieved via the rubygems.org API.
+#
+# This is used to avoid loading the full Specification object when all we need
+# is the name, version, and dependencies.
+
+class Gem::Resolver::APISpecification < Gem::Resolver::Specification
+
+ ##
+ # Creates an APISpecification for the given +set+ from the rubygems.org
+ # +api_data+.
+ #
+ # See http://guides.rubygems.org/rubygems-org-api/#misc_methods for the
+ # format of the +api_data+.
+
+ def initialize(set, api_data)
+ super()
+
+ @set = set
+ @name = api_data[:name]
+ @version = Gem::Version.new api_data[:number]
+ @platform = api_data[:platform]
+ @dependencies = api_data[:dependencies].map do |name, ver|
+ Gem::Dependency.new name, ver.split(/\s*,\s*/)
+ end
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @name == other.name and
+ @version == other.version and
+ @platform == other.platform and
+ @dependencies == other.dependencies
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
new file mode 100644
index 0000000000..533a0db58f
--- /dev/null
+++ b/lib/rubygems/resolver/best_set.rb
@@ -0,0 +1,21 @@
+##
+# The BestSet chooses the best available method to query a remote index.
+#
+# It combines IndexSet and APISet
+
+class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
+
+ ##
+ # Creates a BestSet for the given +sources+ or Gem::sources if none are
+ # specified. +sources+ must be a Gem::SourceList.
+
+ def initialize sources = Gem.sources
+ super()
+
+ sources.each_source do |source|
+ @sets << source.dependency_resolver_set
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
new file mode 100644
index 0000000000..e4aa15e4d0
--- /dev/null
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -0,0 +1,20 @@
+class Gem::Resolver::ComposedSet < Gem::Resolver::Set
+
+ attr_reader :sets # :nodoc:
+
+ def initialize *sets
+ @sets = sets
+ end
+
+ def find_all req
+ res = []
+ @sets.each { |s| res += s.find_all(req) }
+ res
+ end
+
+ def prefetch reqs
+ @sets.each { |s| s.prefetch(reqs) }
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
new file mode 100644
index 0000000000..b081972658
--- /dev/null
+++ b/lib/rubygems/resolver/conflict.rb
@@ -0,0 +1,102 @@
+##
+# Used internally to indicate that a dependency conflicted
+# with a spec that would be activated.
+
+class Gem::Resolver::Conflict
+
+ attr_reader :activated
+
+ attr_reader :dependency
+
+ attr_reader :failed_dep # :nodoc:
+
+ def initialize(dependency, activated, failed_dep=dependency)
+ @dependency = dependency
+ @activated = activated
+ @failed_dep = failed_dep
+ end
+
+ def == other
+ self.class === other and
+ @dependency == other.dependency and
+ @activated == other.activated and
+ @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
+
+ def conflicting_dependencies
+ [@failed_dep.dependency, @activated.request.dependency]
+ end
+
+ ##
+ # Explanation of the conflict used by exceptions to print useful messages
+
+ def explanation
+ activated = @activated.spec.full_name
+ requirement = @failed_dep.dependency.requirement
+
+ " Activated %s instead of (%s) via:\n %s\n" % [
+ activated, requirement, request_path.join(', ')
+ ]
+ end
+
+ def for_spec?(spec)
+ @dependency.name == spec.name
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Dependency conflict: ', ']' do
+ q.breakable
+
+ q.text 'activated '
+ q.pp @activated
+
+ q.breakable
+ q.text ' dependency '
+ q.pp @dependency
+
+ q.breakable
+ if @dependency == @failed_dep then
+ q.text ' failed'
+ else
+ q.text ' failed dependency '
+ q.pp @failed_dep
+ end
+ end
+ end
+
+ ##
+ # Path of specifications that requested this dependency
+
+ def request_path
+ current = requester
+ path = []
+
+ while current do
+ path << current.spec.full_name
+
+ current = current.request.requester
+ end
+
+ path = ['user request (gem command or Gemfile)'] if path.empty?
+
+ path
+ end
+
+ ##
+ # Return the Specification that listed the dependency
+
+ def requester
+ @failed_dep.requester
+ end
+
+end
+
+Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict
+
diff --git a/lib/rubygems/resolver/current_set.rb b/lib/rubygems/resolver/current_set.rb
new file mode 100644
index 0000000000..4e8d34026b
--- /dev/null
+++ b/lib/rubygems/resolver/current_set.rb
@@ -0,0 +1,13 @@
+##
+# A set which represents the installed gems. Respects
+# all the normal settings that control where to look
+# for installed gems.
+
+class Gem::Resolver::CurrentSet < Gem::Resolver::Set
+
+ def find_all req
+ req.dependency.matching_specs
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
new file mode 100644
index 0000000000..e63b443c62
--- /dev/null
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -0,0 +1,71 @@
+##
+# Used Internally. Wraps a Dependency object to also track which spec
+# contained the Dependency.
+
+class Gem::Resolver::DependencyRequest
+
+ attr_reader :dependency
+
+ attr_reader :requester
+
+ def initialize(dep, act)
+ @dependency = dep
+ @requester = act
+ end
+
+ def ==(other)
+ case other
+ when Gem::Dependency
+ @dependency == other
+ when Gem::Resolver::DependencyRequest
+ @dependency == other.dependency && @requester == other.requester
+ else
+ false
+ end
+ end
+
+ def matches_spec?(spec)
+ @dependency.matches_spec? spec
+ end
+
+ def name
+ @dependency.name
+ end
+
+ # Indicate that the request is for a gem explicitly requested by the user
+ def explicit?
+ @requester.nil?
+ end
+
+ # Indicate that the requset is for a gem requested as a dependency of another gem
+ def implicit?
+ !explicit?
+ end
+
+ # Return a String indicating who caused this request to be added (only
+ # valid for implicit requests)
+ def request_context
+ @requester ? @requester.request : "(unknown)"
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Dependency request ', ']' do
+ q.breakable
+ q.text @dependency.to_s
+
+ q.breakable
+ q.text ' requested by '
+ q.pp @requester
+ end
+ end
+
+ def requirement
+ @dependency.requirement
+ end
+
+ def to_s # :nodoc:
+ @dependency.to_s
+ end
+
+end
+
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/resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb
new file mode 100644
index 0000000000..0ba3c78a44
--- /dev/null
+++ b/lib/rubygems/resolver/index_set.rb
@@ -0,0 +1,50 @@
+##
+# The global rubygems pool represented via the traditional
+# source index.
+
+class Gem::Resolver::IndexSet < Gem::Resolver::Set
+
+ def initialize source = nil # :nodoc:
+ @f =
+ if source then
+ sources = Gem::SourceList.from [source]
+
+ Gem::SpecFetcher.new sources
+ else
+ Gem::SpecFetcher.fetcher
+ end
+
+ @all = Hash.new { |h,k| h[k] = [] }
+
+ list, = @f.available_specs :released
+
+ list.each do |uri, specs|
+ specs.each do |n|
+ @all[n.name] << [uri, n]
+ end
+ end
+
+ @specs = {}
+ end
+
+ ##
+ # Return an array of IndexSpecification objects matching
+ # DependencyRequest +req+.
+
+ def find_all req
+ res = []
+
+ name = req.dependency.name
+
+ @all[name].each do |uri, n|
+ if req.dependency.match? n then
+ res << Gem::Resolver::IndexSpecification.new(
+ self, n.name, n.version, uri, n.platform)
+ end
+ end
+
+ res
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
new file mode 100644
index 0000000000..56fecb5753
--- /dev/null
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -0,0 +1,69 @@
+##
+# Represents a possible Specification object returned from IndexSet. Used to
+# delay needed to download full Specification objects when only the +name+
+# and +version+ are needed.
+
+class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
+
+ ##
+ # An IndexSpecification is created from the index format described in `gem
+ # help generate_index`.
+ #
+ # The +set+ contains other specifications for this (URL) +source+.
+ #
+ # The +name+, +version+ and +platform+ are the name, version and platform of
+ # the gem.
+
+ def initialize set, name, version, source, platform
+ super()
+
+ @set = set
+ @name = name
+ @version = version
+ @source = source
+ @platform = platform.to_s
+
+ @spec = nil
+ end
+
+ ##
+ # The dependencies of the gem for this specification
+
+ def dependencies
+ spec.dependencies
+ end
+
+ def inspect # :nodoc:
+ '#<%s %s source %s>' % [self.class, full_name, @source]
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Index specification', ']' do
+ q.breakable
+ q.text full_name
+
+ unless Gem::Platform::RUBY == @platform then
+ q.breakable
+ q.text @platform.to_s
+ end
+
+ q.breakable
+ q.text 'source '
+ q.pp @source
+ end
+ end
+
+ ##
+ # Fetches a Gem::Specification for this IndexSpecification from the #source.
+
+ def spec # :nodoc:
+ @spec ||=
+ begin
+ tuple = Gem::NameTuple.new @name, @version, @platform
+
+ @source.fetch_spec tuple
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb
new file mode 100644
index 0000000000..647ff7499a
--- /dev/null
+++ b/lib/rubygems/resolver/installed_specification.rb
@@ -0,0 +1,34 @@
+##
+# An InstalledSpecification represents a gem that is already installed
+# locally.
+
+class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @spec == other.spec
+ end
+
+ ##
+ # Returns +true+ if this gem is installable for the current platform.
+
+ def installable_platform?
+ # BACKCOMPAT If the file is coming out of a specified file, then we
+ # ignore the platform. This code can be removed in RG 3.0.
+ if @source.kind_of? Gem::Source::SpecificFile
+ return true
+ else
+ Gem::Platform.match @spec.platform
+ end
+ end
+
+ ##
+ # The source for this specification
+
+ def source
+ @source ||= Gem::Source::Installed.new
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
new file mode 100644
index 0000000000..73d9e39651
--- /dev/null
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -0,0 +1,152 @@
+##
+# A set of gems for installation sourced from remote sources and local .gem
+# files
+
+class Gem::Resolver::InstallerSet < Gem::Resolver::Set
+
+ ##
+ # List of Gem::Specification objects that must always be installed.
+
+ attr_reader :always_install # :nodoc:
+
+ ##
+ # Only install gems in the always_install list
+
+ attr_accessor :ignore_dependencies # :nodoc:
+
+ ##
+ # Do not look in the installed set when finding specifications. This is
+ # used by the --install-dir option to `gem install`
+
+ attr_accessor :ignore_installed # :nodoc:
+
+ def initialize domain
+ @domain = domain
+
+ @f = Gem::SpecFetcher.fetcher
+
+ @all = Hash.new { |h,k| h[k] = [] }
+ @always_install = []
+ @ignore_dependencies = false
+ @ignore_installed = false
+ @loaded_remote_specs = []
+ @specs = {}
+ end
+
+ ##
+ # Should local gems should be considered?
+
+ def consider_local? # :nodoc:
+ @domain == :both or @domain == :local
+ end
+
+ ##
+ # Should remote gems should be considered?
+
+ def consider_remote? # :nodoc:
+ @domain == :both or @domain == :remote
+ end
+
+ ##
+ # Returns an array of IndexSpecification objects matching DependencyRequest
+ # +req+.
+
+ def find_all req
+ res = []
+
+ dep = req.dependency
+
+ return res if @ignore_dependencies and
+ @always_install.none? { |spec| dep.matches_spec? spec }
+
+ name = dep.name
+
+ dep.matching_specs.each do |gemspec|
+ next if @always_install.include? 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::Resolver::IndexSpecification.new(
+ self, spec.name, spec.version, local_source, spec.platform)
+ end
+ end
+
+ if consider_remote? then
+ load_remote_specs dep
+
+ @all[name].each do |remote_source, n|
+ if dep.match? n then
+ res << Gem::Resolver::IndexSpecification.new(
+ self, n.name, n.version, remote_source, n.platform)
+ end
+ end
+ end
+
+ res
+ end
+
+ def inspect # :nodoc:
+ always_install = @always_install.map { |s| s.full_name }
+
+ '#<%s domain: %s specs: %p always install: %p>' % [
+ self.class, @domain, @specs.keys, always_install,
+ ]
+ end
+
+ ##
+ # Loads remote prerelease specs if +dep+ is a prerelease dependency
+
+ def load_remote_specs dep # :nodoc:
+ types = [:released]
+ types << :prerelease if dep.prerelease?
+
+ types.each do |type|
+ next if @loaded_remote_specs.include? type
+ @loaded_remote_specs << type
+
+ list, = @f.available_specs type
+
+ list.each do |uri, specs|
+ specs.each do |n|
+ @all[n.name] << [uri, n]
+ end
+ end
+ end
+ end
+
+ ##
+ # Called from IndexSpecification to get a true Specification
+ # object.
+
+ def load_spec name, ver, platform, source # :nodoc:
+ key = "#{name}-#{ver}-#{platform}"
+
+ @specs.fetch key do
+ tuple = Gem::NameTuple.new name, ver, platform
+
+ @specs[key] = source.fetch_spec tuple
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[InstallerSet', ']' do
+ q.breakable
+ q.text "domain: #{@domain}"
+
+ q.breakable
+ q.text 'specs: '
+ q.pp @specs.keys
+
+ q.breakable
+ q.text 'always install: '
+ q.pp @always_install
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
new file mode 100644
index 0000000000..6885e70945
--- /dev/null
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -0,0 +1,60 @@
+##
+# A set of gems from a gem dependencies lockfile.
+
+class Gem::Resolver::LockSet < Gem::Resolver::Set
+
+ attr_reader :specs # :nodoc:
+
+ ##
+ # Creates a new LockSet from the given +source+
+
+ def initialize source
+ @source = source
+ @specs = []
+ end
+
+ ##
+ # Creates a new IndexSpecification in this set using the given +name+,
+ # +version+ and +platform+.
+ #
+ # The specification's set will be the current set, and the source will be
+ # the current set's source.
+
+ def add name, version, platform # :nodoc:
+ version = Gem::Version.new version
+
+ spec =
+ Gem::Resolver::IndexSpecification.new self, name, version, @source,
+ platform
+
+ @specs << spec
+ end
+
+ ##
+ # Returns an Array of IndexSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.select do |spec|
+ req.matches_spec? spec
+ end
+ end
+
+ ##
+ # Loads a Gem::Specification with the given +name+, +version+ and
+ # +platform+. +source+ is ignored.
+
+ def load_spec name, version, platform, source # :nodoc:
+ dep = Gem::Dependency.new name, version
+
+ found = @specs.find do |spec|
+ dep.matches_spec? spec and spec.platform == platform
+ end
+
+ tuple = Gem::NameTuple.new found.name, found.version, found.platform
+
+ found.source.fetch_spec tuple
+ end
+
+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/resolver/set.rb b/lib/rubygems/resolver/set.rb
new file mode 100644
index 0000000000..32c137ef6b
--- /dev/null
+++ b/lib/rubygems/resolver/set.rb
@@ -0,0 +1,27 @@
+##
+# Resolver sets are used to look up specifications (and their
+# dependencies) used in resolution. This set is abstract.
+
+class Gem::Resolver::Set
+
+ ##
+ # The find_all method must be implemented. It returns all Resolver
+ # Specification objects matching the given DependencyRequest +req+.
+
+ def find_all req
+ raise NotImplementedError
+ end
+
+ ##
+ # The #prefetch method may be overridden, but this is not necessary. This
+ # default implementation does nothing, which is suitable for sets where
+ # looking up a specification is cheap (such as installed gems).
+ #
+ # When overridden, the #prefetch method should look up specifications
+ # matching +reqs+.
+
+ def prefetch reqs
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
new file mode 100644
index 0000000000..0c411bdf5f
--- /dev/null
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -0,0 +1,58 @@
+##
+# The Resolver::SpecSpecification contains common functionality for
+# Resolver specifications that are backed by a Gem::Specification.
+
+class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
+
+ attr_reader :spec # :nodoc:
+
+ ##
+ # A SpecSpecification is created for a +set+ for a Gem::Specification in
+ # +spec+. The +source+ is either where the +spec+ came from, or should be
+ # loaded from.
+
+ def initialize set, spec, source = nil
+ @set = set
+ @source = source
+ @spec = spec
+ end
+
+ ##
+ # The dependencies of the gem for this specification
+
+ def dependencies
+ spec.dependencies
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{spec.name}-#{spec.version}"
+ end
+
+ ##
+ # The name of the gem for this specification
+
+ def name
+ spec.name
+ end
+
+ ##
+ # The platform this gem works on.
+
+ def platform
+ spec.platform
+ end
+
+ ##
+ # The version of the gem for this specification.
+
+ def version
+ spec.version
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
new file mode 100644
index 0000000000..7dd4c2e829
--- /dev/null
+++ b/lib/rubygems/resolver/specification.rb
@@ -0,0 +1,60 @@
+##
+# 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::Resolver::Specification
+
+ ##
+ # The dependencies of the gem for this specification
+
+ attr_reader :dependencies
+
+ ##
+ # The name of the gem for this specification
+
+ attr_reader :name
+
+ ##
+ # The platform this gem works on.
+
+ attr_reader :platform
+
+ ##
+ # The set this specification came from.
+
+ attr_reader :set
+
+ ##
+ # The source for this specification
+
+ attr_reader :source
+
+ ##
+ # The version of the gem for this specification.
+
+ attr_reader :version
+
+ ##
+ # Sets default instance variables for the specification.
+
+ def initialize
+ @dependencies = nil
+ @name = nil
+ @platform = nil
+ @set = nil
+ @source = nil
+ @version = nil
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{@name}-#{@version}"
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb
new file mode 100644
index 0000000000..e9cbcd8303
--- /dev/null
+++ b/lib/rubygems/resolver/vendor_set.rb
@@ -0,0 +1,66 @@
+##
+# A VendorSet represents gems that have been unpacked into a specific
+# directory that contains a gemspec.
+#
+# This is used for gem dependency file support.
+#
+# Example:
+#
+# 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::Resolver::VendorSet < Gem::Resolver::Set
+
+ def initialize # :nodoc:
+ @directories = {}
+ @specs = {}
+ end
+
+ ##
+ # Adds a specification to the set with the given +name+ which has been
+ # unpacked into the given +directory+.
+
+ def add_vendor_gem name, directory # :nodoc:
+ gemspec = File.join directory, "#{name}.gemspec"
+
+ spec = Gem::Specification.load gemspec
+
+ raise Gem::GemNotFoundException,
+ "unable to find #{gemspec} for gem #{name}" unless spec
+
+ key = "#{spec.name}-#{spec.version}-#{spec.platform}"
+
+ @specs[key] = spec
+ @directories[spec] = directory
+ end
+
+ ##
+ # Returns an Array of VendorSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.values.select do |spec|
+ req.matches_spec? spec
+ end.map do |spec|
+ source = Gem::Source::Vendor.new @directories[spec]
+ Gem::Resolver::VendorSpecification.new self, spec, source
+ end
+ end
+
+ ##
+ # Loads a spec with the given +name+, +version+ and +platform+. Since the
+ # +source+ is defined when the specification was added to index it is not
+ # used.
+
+ def load_spec name, version, platform, source # :nodoc:
+ key = "#{name}-#{version}-#{platform}"
+
+ @specs.fetch key
+ end
+
+end
+
diff --git a/lib/rubygems/resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb
new file mode 100644
index 0000000000..24e033d084
--- /dev/null
+++ b/lib/rubygems/resolver/vendor_specification.rb
@@ -0,0 +1,16 @@
+##
+# A VendorSpecification represents a gem that has been unpacked into a project
+# and is being loaded through a gem dependencies file through the +path:+
+# option.
+
+class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @spec == other.spec and
+ @source == other.source
+ end
+
+end
+