summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/rubygems/resolver
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rubygems/resolver
Fresh start
Diffstat (limited to 'jni/ruby/lib/rubygems/resolver')
-rw-r--r--jni/ruby/lib/rubygems/resolver/activation_request.rb172
-rw-r--r--jni/ruby/lib/rubygems/resolver/api_set.rb125
-rw-r--r--jni/ruby/lib/rubygems/resolver/api_specification.rb85
-rw-r--r--jni/ruby/lib/rubygems/resolver/best_set.rb78
-rw-r--r--jni/ruby/lib/rubygems/resolver/composed_set.rb66
-rw-r--r--jni/ruby/lib/rubygems/resolver/conflict.rb160
-rw-r--r--jni/ruby/lib/rubygems/resolver/current_set.rb13
-rw-r--r--jni/ruby/lib/rubygems/resolver/dependency_request.rb116
-rw-r--r--jni/ruby/lib/rubygems/resolver/git_set.rb122
-rw-r--r--jni/ruby/lib/rubygems/resolver/git_specification.rb59
-rw-r--r--jni/ruby/lib/rubygems/resolver/index_set.rb80
-rw-r--r--jni/ruby/lib/rubygems/resolver/index_specification.rb69
-rw-r--r--jni/ruby/lib/rubygems/resolver/installed_specification.rb58
-rw-r--r--jni/ruby/lib/rubygems/resolver/installer_set.rb224
-rw-r--r--jni/ruby/lib/rubygems/resolver/local_specification.rb41
-rw-r--r--jni/ruby/lib/rubygems/resolver/lock_set.rb84
-rw-r--r--jni/ruby/lib/rubygems/resolver/lock_specification.rb84
-rw-r--r--jni/ruby/lib/rubygems/resolver/requirement_list.rb81
-rw-r--r--jni/ruby/lib/rubygems/resolver/set.rb56
-rw-r--r--jni/ruby/lib/rubygems/resolver/spec_specification.rb56
-rw-r--r--jni/ruby/lib/rubygems/resolver/specification.rb110
-rw-r--r--jni/ruby/lib/rubygems/resolver/stats.rb44
-rw-r--r--jni/ruby/lib/rubygems/resolver/vendor_set.rb87
-rw-r--r--jni/ruby/lib/rubygems/resolver/vendor_specification.rb24
24 files changed, 2094 insertions, 0 deletions
diff --git a/jni/ruby/lib/rubygems/resolver/activation_request.rb b/jni/ruby/lib/rubygems/resolver/activation_request.rb
new file mode 100644
index 0000000..56c6363
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/activation_request.rb
@@ -0,0 +1,172 @@
+##
+# Specifies a Specification object that should be activated. Also contains a
+# dependency that was used to introduce this activation.
+
+class Gem::Resolver::ActivationRequest
+
+ ##
+ # The parent request for this activation request.
+
+ attr_reader :request
+
+ ##
+ # The specification to be activated.
+
+ attr_reader :spec
+
+ ##
+ # Creates a new ActivationRequest that will activate +spec+. The parent
+ # +request+ is used to provide diagnostics in case of conflicts.
+ #
+ # +others_possible+ indicates that other specifications may also match this
+ # activation request.
+
+ def initialize spec, request, others_possible = true
+ @spec = spec
+ @request = request
+ @others_possible = others_possible
+ end
+
+ def == other # :nodoc:
+ case other
+ when Gem::Specification
+ @spec == other
+ when Gem::Resolver::ActivationRequest
+ @spec == other.spec && @request == other.request
+ else
+ false
+ end
+ end
+
+ ##
+ # Is this activation request for a development dependency?
+
+ def development?
+ @request.development?
+ end
+
+ ##
+ # Downloads a gem at +path+ and returns the file path.
+
+ 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
+
+ ##
+ # The full name of the specification to be activated.
+
+ def full_name
+ @spec.full_name
+ end
+
+ ##
+ # The Gem::Specification for this activation request.
+
+ 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
+
+ ##
+ # True 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
+
+ ##
+ # The name of this activation request's specification
+
+ 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
+
+ ##
+ # The version of this activation request's specification
+
+ def version
+ @spec.version
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/api_set.rb b/jni/ruby/lib/rubygems/resolver/api_set.rb
new file mode 100644
index 0000000..17d602f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/api_set.rb
@@ -0,0 +1,125 @@
+##
+# 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:
+
+ ##
+ # The Gem::Source that gems are fetched from
+
+ attr_reader :source
+
+ ##
+ # The corresponding place to fetch gems.
+
+ attr_reader :uri
+
+ ##
+ # Creates a new APISet that will retrieve gems from +uri+ using the RubyGems
+ # API URL +dep_uri+ which is described at
+ # http://guides.rubygems.org/rubygems-org-api
+
+ def initialize dep_uri = 'https://rubygems.org/api/v1/dependencies'
+ super()
+
+ dep_uri = URI dep_uri unless URI === dep_uri # for ruby 1.8
+
+ @dep_uri = dep_uri
+ @uri = dep_uri + '../..'
+
+ @data = Hash.new { |h,k| h[k] = [] }
+ @source = Gem::Source.new @uri
+
+ @to_fetch = []
+ end
+
+ ##
+ # Return an array of APISpecification objects matching
+ # DependencyRequest +req+.
+
+ def find_all req
+ res = []
+
+ return res unless @remote
+
+ if @to_fetch.include?(req.name)
+ prefetch_now
+ end
+
+ 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
+ return unless @remote
+ names = reqs.map { |r| r.dependency.name }
+ needed = names - @data.keys - @to_fetch
+
+ @to_fetch += needed
+ end
+
+ def prefetch_now # :nodoc:
+ needed, @to_fetch = @to_fetch, []
+
+ uri = @dep_uri + "?gems=#{needed.sort.join ','}"
+ str = Gem::RemoteFetcher.fetcher.fetch_path uri
+
+ loaded = []
+
+ Marshal.load(str).each do |ver|
+ name = ver[:name]
+
+ @data[name] << ver
+ loaded << name
+ end
+
+ (needed - loaded).each do |missing|
+ @data[missing] = []
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[APISet', ']' do
+ q.breakable
+ q.text "URI: #{@dep_uri}"
+
+ q.breakable
+ q.text 'gem names:'
+ q.pp @data.keys
+ 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/jni/ruby/lib/rubygems/resolver/api_specification.rb b/jni/ruby/lib/rubygems/resolver/api_specification.rb
new file mode 100644
index 0000000..4960e66
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/api_specification.rb
@@ -0,0 +1,85 @@
+##
+# 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 = Gem::Platform.new 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
+
+ def fetch_development_dependencies # :nodoc:
+ spec = source.fetch_spec Gem::NameTuple.new @name, @version, @platform
+
+ @dependencies = spec.dependencies
+ end
+
+ def installable_platform? # :nodoc:
+ Gem::Platform.match @platform
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[APISpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp @dependencies
+
+ q.breakable
+ q.text "set uri: #{@set.dep_uri}"
+ end
+ end
+
+ ##
+ # Fetches a Gem::Specification for this APISpecification.
+
+ def spec # :nodoc:
+ @spec ||=
+ begin
+ tuple = Gem::NameTuple.new @name, @version, @platform
+
+ source.fetch_spec tuple
+ end
+ end
+
+ def source # :nodoc:
+ @set.source
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/best_set.rb b/jni/ruby/lib/rubygems/resolver/best_set.rb
new file mode 100644
index 0000000..7e2d7e2
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/best_set.rb
@@ -0,0 +1,78 @@
+##
+# 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 = sources
+ end
+
+ ##
+ # Picks which sets to use for the configured sources.
+
+ def pick_sets # :nodoc:
+ @sources.each_source do |source|
+ @sets << source.dependency_resolver_set
+ end
+ end
+
+ def find_all req # :nodoc:
+ pick_sets if @remote and @sets.empty?
+
+ super
+ rescue Gem::RemoteFetcher::FetchError => e
+ replace_failed_api_set e
+
+ retry
+ end
+
+ def prefetch reqs # :nodoc:
+ pick_sets if @remote and @sets.empty?
+
+ super
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[BestSet', ']' do
+ q.breakable
+ q.text 'sets:'
+
+ q.breakable
+ q.pp @sets
+ end
+ end
+
+ ##
+ # Replaces a failed APISet for the URI in +error+ with an IndexSet.
+ #
+ # If no matching APISet can be found the original +error+ is raised.
+ #
+ # The calling method must retry the exception to repeat the lookup.
+
+ def replace_failed_api_set error # :nodoc:
+ uri = error.uri
+ uri = URI uri unless URI === uri
+ uri.query = nil
+
+ raise error unless api_set = @sets.find { |set|
+ Gem::Resolver::APISet === set and set.dep_uri == uri
+ }
+
+ index_set = Gem::Resolver::IndexSet.new api_set.source
+
+ @sets.map! do |set|
+ next set unless set == api_set
+ index_set
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/composed_set.rb b/jni/ruby/lib/rubygems/resolver/composed_set.rb
new file mode 100644
index 0000000..5b08f12
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/composed_set.rb
@@ -0,0 +1,66 @@
+##
+# A ComposedSet allows multiple sets to be queried like a single set.
+#
+# To create a composed set with any number of sets use:
+#
+# Gem::Resolver.compose_sets set1, set2
+#
+# This method will eliminate nesting of composed sets.
+
+class Gem::Resolver::ComposedSet < Gem::Resolver::Set
+
+ attr_reader :sets # :nodoc:
+
+ ##
+ # Creates a new ComposedSet containing +sets+. Use
+ # Gem::Resolver::compose_sets instead.
+
+ def initialize *sets
+ super()
+
+ @sets = sets
+ end
+
+ ##
+ # When +allow_prerelease+ is set to +true+ prereleases gems are allowed to
+ # match dependencies.
+
+ def prerelease= allow_prerelease
+ super
+
+ sets.each do |set|
+ set.prerelease = allow_prerelease
+ end
+ end
+
+ ##
+ # Sets the remote network access for all composed sets.
+
+ def remote= remote
+ super
+
+ @sets.each { |set| set.remote = remote }
+ end
+
+ def errors
+ @errors + @sets.map { |set| set.errors }.flatten
+ end
+
+ ##
+ # Finds all specs matching +req+ in all sets.
+
+ def find_all req
+ @sets.map do |s|
+ s.find_all req
+ end.flatten
+ end
+
+ ##
+ # Prefetches +reqs+ in all sets.
+
+ def prefetch reqs
+ @sets.each { |s| s.prefetch(reqs) }
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/conflict.rb b/jni/ruby/lib/rubygems/resolver/conflict.rb
new file mode 100644
index 0000000..902c286
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/conflict.rb
@@ -0,0 +1,160 @@
+##
+# Used internally to indicate that a dependency conflicted
+# with a spec that would be activated.
+
+class Gem::Resolver::Conflict
+
+ ##
+ # The specification that was activated prior to the conflict
+
+ attr_reader :activated
+
+ ##
+ # The dependency that is in conflict with the activated gem.
+
+ attr_reader :dependency
+
+ attr_reader :failed_dep # :nodoc:
+
+ ##
+ # Creates a new resolver conflict when +dependency+ is in conflict with an
+ # already +activated+ specification.
+
+ def initialize(dependency, activated, failed_dep=dependency)
+ @dependency = dependency
+ @activated = activated
+ @failed_dep = failed_dep
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @dependency == other.dependency and
+ @activated == other.activated and
+ @failed_dep == other.failed_dep
+ end
+
+ ##
+ # A string explanation of the conflict.
+
+ 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
+ dependency = @failed_dep.dependency
+ requirement = dependency.requirement
+ alternates = dependency.matching_specs.map { |spec| spec.full_name }
+
+ unless alternates.empty? then
+ matching = <<-MATCHING.chomp
+
+ Gems matching %s:
+ %s
+ MATCHING
+
+ matching = matching % [
+ dependency,
+ alternates.join(', '),
+ ]
+ end
+
+ explanation = <<-EXPLANATION
+ Activated %s
+ which does not match conflicting dependency (%s)
+
+ Conflicting dependency chains:
+ %s
+
+ versus:
+ %s
+%s
+ EXPLANATION
+
+ explanation % [
+ activated, requirement,
+ request_path(@activated).reverse.join(", depends on\n "),
+ request_path(@failed_dep).reverse.join(", depends on\n "),
+ matching,
+ ]
+ end
+
+ ##
+ # Returns true if the conflicting dependency's name matches +spec+.
+
+ 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 activations from the +current+ list.
+
+ def request_path current
+ path = []
+
+ while current do
+ case current
+ when Gem::Resolver::ActivationRequest then
+ path <<
+ "#{current.request.dependency}, #{current.spec.version} activated"
+
+ current = current.parent
+ when Gem::Resolver::DependencyRequest then
+ path << "#{current.dependency}"
+
+ current = current.requester
+ else
+ raise Gem::Exception, "[BUG] unknown request class #{current.class}"
+ end
+ 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
+
+##
+# TODO: Remove in RubyGems 3
+
+Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict # :nodoc:
+
diff --git a/jni/ruby/lib/rubygems/resolver/current_set.rb b/jni/ruby/lib/rubygems/resolver/current_set.rb
new file mode 100644
index 0000000..4e8d340
--- /dev/null
+++ b/jni/ruby/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/jni/ruby/lib/rubygems/resolver/dependency_request.rb b/jni/ruby/lib/rubygems/resolver/dependency_request.rb
new file mode 100644
index 0000000..79690be
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/dependency_request.rb
@@ -0,0 +1,116 @@
+##
+# Used Internally. Wraps a Dependency object to also track which spec
+# contained the Dependency.
+
+class Gem::Resolver::DependencyRequest
+
+ ##
+ # The wrapped Gem::Dependency
+
+ attr_reader :dependency
+
+ ##
+ # The request for this dependency.
+
+ attr_reader :requester
+
+ ##
+ # Creates a new DependencyRequest for +dependency+ from +requester+.
+ # +requester may be nil if the request came from a user.
+
+ def initialize dependency, requester
+ @dependency = dependency
+ @requester = requester
+ end
+
+ def == other # :nodoc:
+ case other
+ when Gem::Dependency
+ @dependency == other
+ when Gem::Resolver::DependencyRequest
+ @dependency == other.dependency && @requester == other.requester
+ else
+ false
+ end
+ end
+
+ ##
+ # Is this dependency a development dependency?
+
+ def development?
+ @dependency.type == :development
+ end
+
+ ##
+ # Does this dependency request match +spec+?
+ #
+ # NOTE: #match? only matches prerelease versions when #dependency is a
+ # prerelease dependency.
+
+ def match? spec, allow_prerelease = false
+ @dependency.match? spec, nil, allow_prerelease
+ end
+
+ ##
+ # Does this dependency request match +spec+?
+ #
+ # NOTE: #matches_spec? matches prerelease versions. See also #match?
+
+ def matches_spec?(spec)
+ @dependency.matches_spec? spec
+ end
+
+ ##
+ # The name of the gem this dependency request is requesting.
+
+ 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 request 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
+
+ ##
+ # The version requirement for this dependency request
+
+ def requirement
+ @dependency.requirement
+ end
+
+ def to_s # :nodoc:
+ @dependency.to_s
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/git_set.rb b/jni/ruby/lib/rubygems/resolver/git_set.rb
new file mode 100644
index 0000000..5f1b368
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/git_set.rb
@@ -0,0 +1,122 @@
+##
+# 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
+
+ ##
+ # The root directory for git gems in this set. This is usually Gem.dir, the
+ # installation directory for regular gems.
+
+ attr_accessor :root_dir
+
+ ##
+ # 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:
+ super()
+
+ @git = ENV['git'] || 'git'
+ @need_submodules = {}
+ @repositories = {}
+ @root_dir = Gem.dir
+ @specs = {}
+ end
+
+ def add_git_gem name, repository, reference, submodules # :nodoc:
+ @repositories[name] = [repository, reference]
+ @need_submodules[repository] = submodules
+ end
+
+ ##
+ # Adds and returns a GitSpecification with the given +name+ and +version+
+ # which came from a +repository+ at the given +reference+. If +submodules+
+ # is true they are checked out along with the repository.
+ #
+ # This fills in the prefetch information as enough information about the gem
+ # is present in the arguments.
+
+ def add_git_spec name, version, repository, reference, submodules # :nodoc:
+ add_git_gem name, repository, reference, submodules
+
+ source = Gem::Source::Git.new name, repository, reference
+ source.root_dir = @root_dir
+
+ spec = Gem::Specification.new do |s|
+ s.name = name
+ s.version = version
+ end
+
+ git_spec = Gem::Resolver::GitSpecification.new self, spec, source
+
+ @specs[spec.name] = git_spec
+
+ git_spec
+ end
+
+ ##
+ # Finds all git gems matching +req+
+
+ def find_all req
+ prefetch nil
+
+ specs.values.select do |spec|
+ req.match? spec
+ end
+ end
+
+ ##
+ # Prefetches specifications from the git repositories in this set.
+
+ def prefetch reqs
+ return unless @specs.empty?
+
+ @repositories.each do |name, (repository, reference)|
+ source = Gem::Source::Git.new name, repository, reference
+ source.root_dir = @root_dir
+ source.remote = @remote
+
+ source.specs.each do |spec|
+ git_spec = Gem::Resolver::GitSpecification.new self, spec, source
+
+ @specs[spec.name] = git_spec
+ end
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[GitSet', ']' do
+ next if @repositories.empty?
+ q.breakable
+
+ repos = @repositories.map do |name, (repository, reference)|
+ "#{name}: #{repository}@#{reference}"
+ end
+
+ q.seplist repos do |repo|
+ q.text repo
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/git_specification.rb b/jni/ruby/lib/rubygems/resolver/git_specification.rb
new file mode 100644
index 0000000..55e180e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/git_specification.rb
@@ -0,0 +1,59 @@
+##
+# 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
+
+ def add_dependency dependency # :nodoc:
+ spec.dependencies << dependency
+ end
+
+ ##
+ # Installing a git gem only involves building the extensions and generating
+ # the executables.
+
+ def install options = {}
+ require 'rubygems/installer'
+
+ installer = Gem::Installer.new '', options
+ installer.spec = spec
+
+ yield installer if block_given?
+
+ installer.run_pre_install_hooks
+ installer.build_extensions
+ installer.run_post_build_hooks
+ installer.generate_bin
+ installer.run_post_install_hooks
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[GitSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp dependencies
+
+ q.breakable
+ q.text "source:"
+ q.breakable
+ q.pp @source
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/index_set.rb b/jni/ruby/lib/rubygems/resolver/index_set.rb
new file mode 100644
index 0000000..7c56c2b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/index_set.rb
@@ -0,0 +1,80 @@
+##
+# The global rubygems pool represented via the traditional
+# source index.
+
+class Gem::Resolver::IndexSet < Gem::Resolver::Set
+
+ def initialize source = nil # :nodoc:
+ super()
+
+ @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, errors = @f.available_specs :complete
+
+ @errors.concat errors
+
+ 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 = []
+
+ return res unless @remote
+
+ name = req.dependency.name
+
+ @all[name].each do |uri, n|
+ if req.match? n, @prerelease then
+ res << Gem::Resolver::IndexSpecification.new(
+ self, n.name, n.version, uri, n.platform)
+ end
+ end
+
+ res
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[IndexSet', ']' do
+ q.breakable
+ q.text 'sources:'
+ q.breakable
+ q.pp @f.sources
+
+ q.breakable
+ q.text 'specs:'
+
+ q.breakable
+
+ names = @all.values.map do |tuples|
+ tuples.map do |_, tuple|
+ tuple.full_name
+ end
+ end.flatten
+
+ q.seplist names do |name|
+ q.text name
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/index_specification.rb b/jni/ruby/lib/rubygems/resolver/index_specification.rb
new file mode 100644
index 0000000..56fecb5
--- /dev/null
+++ b/jni/ruby/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/jni/ruby/lib/rubygems/resolver/installed_specification.rb b/jni/ruby/lib/rubygems/resolver/installed_specification.rb
new file mode 100644
index 0000000..2a2b89a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/installed_specification.rb
@@ -0,0 +1,58 @@
+##
+# 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
+
+ ##
+ # This is a null install as this specification is already installed.
+ # +options+ are ignored.
+
+ def install options = {}
+ yield nil
+ 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.
+ return true if @source.kind_of? Gem::Source::SpecificFile
+
+ super
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[InstalledSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp spec.dependencies
+ end
+ end
+
+ ##
+ # The source for this specification
+
+ def source
+ @source ||= Gem::Source::Installed.new
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/installer_set.rb b/jni/ruby/lib/rubygems/resolver/installer_set.rb
new file mode 100644
index 0000000..a68ff09
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/installer_set.rb
@@ -0,0 +1,224 @@
+##
+# 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:
+
+ ##
+ # The remote_set looks up remote gems for installation.
+
+ attr_reader :remote_set # :nodoc:
+
+ ##
+ # Creates a new InstallerSet that will look for gems in +domain+.
+
+ def initialize domain
+ super()
+
+ @domain = domain
+ @remote = consider_remote?
+
+ @f = Gem::SpecFetcher.fetcher
+
+ @always_install = []
+ @ignore_dependencies = false
+ @ignore_installed = false
+ @local = {}
+ @remote_set = Gem::Resolver::BestSet.new
+ @specs = {}
+ end
+
+ ##
+ # Looks up the latest specification for +dependency+ and adds it to the
+ # always_install list.
+
+ def add_always_install dependency
+ request = Gem::Resolver::DependencyRequest.new dependency, nil
+
+ found = find_all request
+
+ found.delete_if { |s|
+ s.version.prerelease? and not s.local?
+ } unless dependency.prerelease?
+
+ found = found.select do |s|
+ Gem::Source::SpecificFile === s.source or
+ Gem::Platform::RUBY == s.platform or
+ Gem::Platform.local === s.platform
+ end
+
+ if found.empty? then
+ exc = Gem::UnsatisfiableDependencyError.new request
+ exc.errors = errors
+
+ raise exc
+ end
+
+ newest = found.max_by do |s|
+ [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ @always_install << newest.spec
+ end
+
+ ##
+ # Adds a local gem requested using +dep_name+ with the given +spec+ that can
+ # be loaded and installed using the +source+.
+
+ def add_local dep_name, spec, source
+ @local[dep_name] = [spec, source]
+ 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
+
+ ##
+ # Errors encountered while resolving gems
+
+ def errors
+ @errors + @remote_set.errors
+ 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.match? spec }
+
+ name = dep.name
+
+ dep.matching_specs.each do |gemspec|
+ next if @always_install.any? { |spec| spec.name == gemspec.name }
+
+ res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
+ end unless @ignore_installed
+
+ if consider_local? then
+ matching_local = @local.values.select do |spec, _|
+ req.match? spec
+ end.map do |spec, source|
+ Gem::Resolver::LocalSpecification.new self, spec, source
+ end
+
+ res.concat matching_local
+
+ local_source = Gem::Source::Local.new
+
+ if local_spec = local_source.find_gem(name, dep.requirement) then
+ res << Gem::Resolver::IndexSpecification.new(
+ self, local_spec.name, local_spec.version,
+ local_source, local_spec.platform)
+ end
+ end
+
+ res.delete_if do |spec|
+ spec.version.prerelease? and not dep.prerelease?
+ end
+
+ res.concat @remote_set.find_all req if consider_remote?
+
+ res
+ end
+
+ def prefetch(reqs)
+ @remote_set.prefetch(reqs) if consider_remote?
+ end
+
+ def prerelease= allow_prerelease
+ super
+
+ @remote_set.prerelease = allow_prerelease
+ 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
+
+ ##
+ # 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
+
+ ##
+ # Has a local gem for +dep_name+ been added to this set?
+
+ def local? dep_name # :nodoc:
+ spec, = @local[dep_name]
+
+ spec
+ 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
+
+ def remote= remote # :nodoc:
+ case @domain
+ when :local then
+ @domain = :both if remote
+ when :remote then
+ @domain = nil unless remote
+ when :both then
+ @domain = :local unless remote
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/local_specification.rb b/jni/ruby/lib/rubygems/resolver/local_specification.rb
new file mode 100644
index 0000000..20a283f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/local_specification.rb
@@ -0,0 +1,41 @@
+##
+# A LocalSpecification comes from a .gem file on the local filesystem.
+
+class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
+
+ ##
+ # Returns +true+ if this gem is installable for the current platform.
+
+ def installable_platform?
+ return true if @source.kind_of? Gem::Source::SpecificFile
+
+ super
+ end
+
+ def local? # :nodoc:
+ true
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LocalSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp dependencies
+
+ q.breakable
+ q.text "source: #{@source.path}"
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/lock_set.rb b/jni/ruby/lib/rubygems/resolver/lock_set.rb
new file mode 100644
index 0000000..4ede597
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/lock_set.rb
@@ -0,0 +1,84 @@
+##
+# 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 +sources+
+
+ def initialize sources
+ super()
+
+ @sources = sources.map do |source|
+ Gem::Source::Lock.new source
+ end
+
+ @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
+
+ specs = @sources.map do |source|
+ Gem::Resolver::LockSpecification.new self, name, version, source,
+ platform
+ end
+
+ @specs.concat specs
+
+ specs
+ end
+
+ ##
+ # Returns an Array of IndexSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.select do |spec|
+ req.match? 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
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LockSet', ']' do
+ q.breakable
+ q.text 'source:'
+
+ q.breakable
+ q.pp @source
+
+ q.breakable
+ q.text 'specs:'
+
+ q.breakable
+ q.pp @specs.map { |spec| spec.full_name }
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/lock_specification.rb b/jni/ruby/lib/rubygems/resolver/lock_specification.rb
new file mode 100644
index 0000000..0013171
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/lock_specification.rb
@@ -0,0 +1,84 @@
+##
+# The LockSpecification comes from a lockfile (Gem::RequestSet::Lockfile).
+#
+# A LockSpecification's dependency information is pre-filled from the
+# lockfile.
+
+class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
+
+ def initialize set, name, version, source, platform
+ super()
+
+ @name = name
+ @platform = platform
+ @set = set
+ @source = source
+ @version = version
+
+ @dependencies = []
+ @spec = nil
+ end
+
+ ##
+ # This is a null install as a locked specification is considered installed.
+ # +options+ are ignored.
+
+ def install options = {}
+ destination = options[:install_dir] || Gem.dir
+
+ if File.exist? File.join(destination, 'specifications', spec.spec_name) then
+ yield nil
+ return
+ end
+
+ super
+ end
+
+ ##
+ # Adds +dependency+ from the lockfile to this specification
+
+ def add_dependency dependency # :nodoc:
+ @dependencies << dependency
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LockSpecification', ']' do
+ q.breakable
+ q.text "name: #{@name}"
+
+ q.breakable
+ q.text "version: #{@version}"
+
+ unless @platform == Gem::Platform::RUBY then
+ q.breakable
+ q.text "platform: #{@platform}"
+ end
+
+ unless @dependencies.empty? then
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp @dependencies
+ end
+ end
+ end
+
+ ##
+ # A specification constructed from the lockfile is returned
+
+ def spec
+ @spec ||= Gem::Specification.find { |spec|
+ spec.name == @name and spec.version == @version
+ }
+
+ @spec ||= Gem::Specification.new do |s|
+ s.name = @name
+ s.version = @version
+ s.platform = @platform
+
+ s.dependencies.concat @dependencies
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/requirement_list.rb b/jni/ruby/lib/rubygems/resolver/requirement_list.rb
new file mode 100644
index 0000000..a6bfaab
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/requirement_list.rb
@@ -0,0 +1,81 @@
+##
+# The RequirementList is used to hold the requirements being considered
+# while resolving a set of gems.
+#
+# The RequirementList acts like a queue where the oldest items are removed
+# first.
+
+class Gem::Resolver::RequirementList
+
+ include Enumerable
+
+ ##
+ # Creates a new RequirementList.
+
+ def initialize
+ @exact = []
+ @list = []
+ end
+
+ def initialize_copy other # :nodoc:
+ @exact = @exact.dup
+ @list = @list.dup
+ end
+
+ ##
+ # Adds Resolver::DependencyRequest +req+ to this requirements list.
+
+ def add(req)
+ if req.requirement.exact?
+ @exact.push req
+ else
+ @list.push req
+ end
+ req
+ end
+
+ ##
+ # Enumerates requirements in the list
+
+ def each # :nodoc:
+ return enum_for __method__ unless block_given?
+
+ @exact.each do |requirement|
+ yield requirement
+ end
+
+ @list.each do |requirement|
+ yield requirement
+ end
+ end
+
+ ##
+ # How many elements are in the list
+
+ def size
+ @exact.size + @list.size
+ end
+
+ ##
+ # Is the list empty?
+
+ def empty?
+ @exact.empty? && @list.empty?
+ end
+
+ ##
+ # Remove the oldest DependencyRequest from the list.
+
+ def remove
+ return @exact.shift unless @exact.empty?
+ @list.shift
+ end
+
+ ##
+ # Returns the oldest five entries from the list.
+
+ def next5
+ x = @exact[0,5]
+ x + @list[0,5 - x.size]
+ end
+end
diff --git a/jni/ruby/lib/rubygems/resolver/set.rb b/jni/ruby/lib/rubygems/resolver/set.rb
new file mode 100644
index 0000000..b26dc45
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/set.rb
@@ -0,0 +1,56 @@
+##
+# Resolver sets are used to look up specifications (and their
+# dependencies) used in resolution. This set is abstract.
+
+class Gem::Resolver::Set
+
+ ##
+ # Set to true to disable network access for this set
+
+ attr_accessor :remote
+
+ ##
+ # Errors encountered when resolving gems
+
+ attr_accessor :errors
+
+ ##
+ # When true, allows matching of requests to prerelease gems.
+
+ attr_accessor :prerelease
+
+ def initialize # :nodoc:
+ @prerelease = false
+ @remote = true
+ @errors = []
+ end
+
+ ##
+ # 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
+
+ ##
+ # When true, this set is allowed to access the network when looking up
+ # specifications or dependencies.
+
+ def remote? # :nodoc:
+ @remote
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/spec_specification.rb b/jni/ruby/lib/rubygems/resolver/spec_specification.rb
new file mode 100644
index 0000000..1350e8a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/spec_specification.rb
@@ -0,0 +1,56 @@
+##
+# The Resolver::SpecSpecification contains common functionality for
+# Resolver specifications that are backed by a Gem::Specification.
+
+class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
+
+ ##
+ # 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/jni/ruby/lib/rubygems/resolver/specification.rb b/jni/ruby/lib/rubygems/resolver/specification.rb
new file mode 100644
index 0000000..4d77293
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/specification.rb
@@ -0,0 +1,110 @@
+##
+# 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 Gem::Specification for this Resolver::Specification.
+ #
+ # Implementers, note that #install updates @spec, so be sure to cache the
+ # Gem::Specification in @spec when overriding.
+
+ attr_reader :spec
+
+ ##
+ # 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
+
+ ##
+ # Fetches development dependencies if the source does not provide them by
+ # default (see APISpecification).
+
+ def fetch_development_dependencies # :nodoc:
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{@name}-#{@version}"
+ end
+
+ ##
+ # Installs this specification using the Gem::Installer +options+. The
+ # install method yields a Gem::Installer instance, which indicates the
+ # gem will be installed, or +nil+, which indicates the gem is already
+ # installed.
+ #
+ # After installation #spec is updated to point to the just-installed
+ # specification.
+
+ def install options = {}
+ require 'rubygems/installer'
+
+ destination = options[:install_dir] || Gem.dir
+
+ Gem.ensure_gem_subdirectories destination
+
+ gem = source.download spec, destination
+
+ installer = Gem::Installer.new gem, options
+
+ yield installer if block_given?
+
+ @spec = installer.install
+ end
+
+ ##
+ # Returns true if this specification is installable on this platform.
+
+ def installable_platform?
+ Gem::Platform.match spec.platform
+ end
+
+ def local? # :nodoc:
+ false
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/stats.rb b/jni/ruby/lib/rubygems/resolver/stats.rb
new file mode 100644
index 0000000..c31e5be
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/stats.rb
@@ -0,0 +1,44 @@
+class Gem::Resolver::Stats
+ def initialize
+ @max_depth = 0
+ @max_requirements = 0
+ @requirements = 0
+ @backtracking = 0
+ @iterations = 0
+ end
+
+ def record_depth(stack)
+ if stack.size > @max_depth
+ @max_depth = stack.size
+ end
+ end
+
+ def record_requirements(reqs)
+ if reqs.size > @max_requirements
+ @max_requirements = reqs.size
+ end
+ end
+
+ def requirement!
+ @requirements += 1
+ end
+
+ def backtracking!
+ @backtracking += 1
+ end
+
+ def iteration!
+ @iterations += 1
+ end
+
+ PATTERN = "%20s: %d\n"
+
+ def display
+ $stdout.puts "=== Resolver Statistics ==="
+ $stdout.printf PATTERN, "Max Depth", @max_depth
+ $stdout.printf PATTERN, "Total Requirements", @requirements
+ $stdout.printf PATTERN, "Max Requirements", @max_requirements
+ $stdout.printf PATTERN, "Backtracking #", @backtracking
+ $stdout.printf PATTERN, "Iteration #", @iterations
+ end
+end
diff --git a/jni/ruby/lib/rubygems/resolver/vendor_set.rb b/jni/ruby/lib/rubygems/resolver/vendor_set.rb
new file mode 100644
index 0000000..614bd05
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/vendor_set.rb
@@ -0,0 +1,87 @@
+##
+# 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
+
+ ##
+ # The specifications for this set.
+
+ attr_reader :specs # :nodoc:
+
+ def initialize # :nodoc:
+ super()
+
+ @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
+
+ spec.full_gem_path = File.expand_path directory
+
+ @specs[spec.name] = spec
+ @directories[spec] = directory
+
+ spec
+ end
+
+ ##
+ # Returns an Array of VendorSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.values.select do |spec|
+ req.match? 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+, +platform+ and +source+ are
+ # ignored.
+
+ def load_spec name, version, platform, source # :nodoc:
+ @specs.fetch name
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[VendorSet', ']' do
+ next if @directories.empty?
+ q.breakable
+
+ dirs = @directories.map do |spec, directory|
+ "#{spec.full_name}: #{directory}"
+ end
+
+ q.seplist dirs do |dir|
+ q.text dir
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/vendor_specification.rb b/jni/ruby/lib/rubygems/resolver/vendor_specification.rb
new file mode 100644
index 0000000..a99b5f3
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/vendor_specification.rb
@@ -0,0 +1,24 @@
+##
+# 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
+
+ ##
+ # This is a null install as this gem was unpacked into a directory.
+ # +options+ are ignored.
+
+ def install options = {}
+ yield nil
+ end
+
+end
+