diff options
author | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-07-09 23:21:36 +0000 |
---|---|---|
committer | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-07-09 23:21:36 +0000 |
commit | 47f0248b0858898dd24d1e654cedf174059ca677 (patch) | |
tree | 493e84160f8609db408d88349f0624a3ff92c3c2 /lib/rubygems/dependency_resolver | |
parent | cd9f9e471977447a991ced4ea38efb2309459ef5 (diff) | |
download | ruby-47f0248b0858898dd24d1e654cedf174059ca677.tar.gz |
* lib/rubygems: Import RubyGems 2.1
* test/rubygems: Ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41873 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rubygems/dependency_resolver')
-rw-r--r-- | lib/rubygems/dependency_resolver/activation_request.rb | 109 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/api_set.rb | 65 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/api_specification.rb | 36 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/composed_set.rb | 18 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/current_set.rb | 16 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/dependency_conflict.rb | 85 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/dependency_request.rb | 51 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/index_set.rb | 59 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/index_specification.rb | 53 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/installed_specification.rb | 38 | ||||
-rw-r--r-- | lib/rubygems/dependency_resolver/installer_set.rb | 130 |
11 files changed, 660 insertions, 0 deletions
diff --git a/lib/rubygems/dependency_resolver/activation_request.rb b/lib/rubygems/dependency_resolver/activation_request.rb new file mode 100644 index 0000000000..25af6378ac --- /dev/null +++ b/lib/rubygems/dependency_resolver/activation_request.rb @@ -0,0 +1,109 @@ +## +# Specifies a Specification object that should be activated. +# Also contains a dependency that was used to introduce this +# activation. + +class Gem::DependencyResolver::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::DependencyResolver::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_possible = nil + others_possible = ' (others possible)' if @others_possible + + '#<%s for %p from %s%s>' % [ + self.class, @spec, @request, others_possible + ] + end + + ## + # Indicates if the requested gem has already been installed. + + def installed? + this_spec = full_spec + + Gem::Specification.any? do |s| + s == this_spec + 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? + @others_possible + 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 + + + q.breakable + q.text ' (other possible)' if @others_possible + end + end + + def version + @spec.version + end + +end + diff --git a/lib/rubygems/dependency_resolver/api_set.rb b/lib/rubygems/dependency_resolver/api_set.rb new file mode 100644 index 0000000000..469c005a09 --- /dev/null +++ b/lib/rubygems/dependency_resolver/api_set.rb @@ -0,0 +1,65 @@ +## +# The global rubygems pool, available via the rubygems.org API. +# Returns instances of APISpecification. + +class Gem::DependencyResolver::APISet + + def initialize + @data = Hash.new { |h,k| h[k] = [] } + @dep_uri = URI 'https://rubygems.org/api/v1/dependencies' + 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::DependencyResolver::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 + 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/dependency_resolver/api_specification.rb b/lib/rubygems/dependency_resolver/api_specification.rb new file mode 100644 index 0000000000..5ad07396cf --- /dev/null +++ b/lib/rubygems/dependency_resolver/api_specification.rb @@ -0,0 +1,36 @@ +## +# Represents a specification retrieved via the rubygems.org +# API. This is used to avoid having to load the full +# Specification object when all we need is the name, version, +# and dependencies. + +class Gem::DependencyResolver::APISpecification + + attr_reader :dependencies + attr_reader :name + attr_reader :set # :nodoc: + attr_reader :version + + def initialize(set, api_data) + @set = set + @name = api_data[:name] + @version = Gem::Version.new api_data[:number] + @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 + @dependencies == other.dependencies + end + + def full_name + "#{@name}-#{@version}" + end + +end + diff --git a/lib/rubygems/dependency_resolver/composed_set.rb b/lib/rubygems/dependency_resolver/composed_set.rb new file mode 100644 index 0000000000..fb38128bb0 --- /dev/null +++ b/lib/rubygems/dependency_resolver/composed_set.rb @@ -0,0 +1,18 @@ +class Gem::DependencyResolver::ComposedSet + + 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/dependency_resolver/current_set.rb b/lib/rubygems/dependency_resolver/current_set.rb new file mode 100644 index 0000000000..13bc490e9e --- /dev/null +++ b/lib/rubygems/dependency_resolver/current_set.rb @@ -0,0 +1,16 @@ +## +# A set which represents the installed gems. Respects +# all the normal settings that control where to look +# for installed gems. + +class Gem::DependencyResolver::CurrentSet + + def find_all req + req.dependency.matching_specs + end + + def prefetch gems + end + +end + diff --git a/lib/rubygems/dependency_resolver/dependency_conflict.rb b/lib/rubygems/dependency_resolver/dependency_conflict.rb new file mode 100644 index 0000000000..1755d910c3 --- /dev/null +++ b/lib/rubygems/dependency_resolver/dependency_conflict.rb @@ -0,0 +1,85 @@ +## +# Used internally to indicate that a dependency conflicted +# with a spec that would be activated. + +class Gem::DependencyResolver::DependencyConflict + + attr_reader :activated + + attr_reader :dependency + + def initialize(dependency, activated, failed_dep=dependency) + @dependency = dependency + @activated = activated + @failed_dep = failed_dep + 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 + end + + ## + # Return the Specification that listed the dependency + + def requester + @failed_dep.requester + end + +end + diff --git a/lib/rubygems/dependency_resolver/dependency_request.rb b/lib/rubygems/dependency_resolver/dependency_request.rb new file mode 100644 index 0000000000..05e447c3be --- /dev/null +++ b/lib/rubygems/dependency_resolver/dependency_request.rb @@ -0,0 +1,51 @@ +## +# Used Internally. Wraps a Dependency object to also track which spec +# contained the Dependency. + +class Gem::DependencyResolver::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::DependencyResolver::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 + + 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 to_s # :nodoc: + @dependency.to_s + end + +end + diff --git a/lib/rubygems/dependency_resolver/index_set.rb b/lib/rubygems/dependency_resolver/index_set.rb new file mode 100644 index 0000000000..fcf919d81b --- /dev/null +++ b/lib/rubygems/dependency_resolver/index_set.rb @@ -0,0 +1,59 @@ +## +# The global rubygems pool represented via the traditional +# source index. + +class Gem::DependencyResolver::IndexSet + + def initialize + @f = Gem::SpecFetcher.fetcher + + @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 + res << Gem::DependencyResolver::IndexSpecification.new( + self, n.name, n.version, uri, n.platform) + end + end + + res + end + + ## + # Called from IndexSpecification to get a true Specification + # object. + + def load_spec name, ver, source + key = "#{name}-#{ver}" + @specs[key] ||= source.fetch_spec(Gem::NameTuple.new(name, ver)) + end + + ## + # No prefetching needed since we load the whole index in + # initially. + + def prefetch gems + end + +end + diff --git a/lib/rubygems/dependency_resolver/index_specification.rb b/lib/rubygems/dependency_resolver/index_specification.rb new file mode 100644 index 0000000000..371018ba44 --- /dev/null +++ b/lib/rubygems/dependency_resolver/index_specification.rb @@ -0,0 +1,53 @@ +## +# 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::DependencyResolver::IndexSpecification + + attr_reader :name + + attr_reader :source + + attr_reader :version + + def initialize set, name, version, source, plat + @set = set + @name = name + @version = version + @source = source + @platform = plat + + @spec = nil + end + + def dependencies + spec.dependencies + end + + def full_name + "#{@name}-#{@version}" + 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 + + q.breakable + q.text ' source ' + q.pp @source + end + end + + def spec + @spec ||= @set.load_spec(@name, @version, @source) + end + +end + diff --git a/lib/rubygems/dependency_resolver/installed_specification.rb b/lib/rubygems/dependency_resolver/installed_specification.rb new file mode 100644 index 0000000000..af167572bf --- /dev/null +++ b/lib/rubygems/dependency_resolver/installed_specification.rb @@ -0,0 +1,38 @@ +class Gem::DependencyResolver::InstalledSpecification + + attr_reader :spec + + def initialize set, spec, source=nil + @set = set + @source = source + @spec = spec + end + + def == other # :nodoc: + self.class === other and + @set == other.set and + @spec == other.spec + end + + def dependencies + @spec.dependencies + end + + def full_name + "#{@spec.name}-#{@spec.version}" + end + + def name + @spec.name + end + + def source + @source ||= Gem::Source::Installed.new + end + + def version + @spec.version + end + +end + diff --git a/lib/rubygems/dependency_resolver/installer_set.rb b/lib/rubygems/dependency_resolver/installer_set.rb new file mode 100644 index 0000000000..7de052df77 --- /dev/null +++ b/lib/rubygems/dependency_resolver/installer_set.rb @@ -0,0 +1,130 @@ +class Gem::DependencyResolver::InstallerSet + + ## + # List of Gem::Specification objects that must always be installed. + + attr_reader :always_install + + ## + # Only install gems in the always_install list + + attr_accessor :ignore_dependencies + + ## + # 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 + + 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? + @domain == :both or @domain == :local + end + + ## + # Should remote gems should be considered? + + def consider_remote? + @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::DependencyResolver::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( + 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::DependencyResolver::IndexSpecification.new( + self, n.name, n.version, remote_source, n.platform) + end + end + end + + res + end + + def inspect # :nodoc: + '#<%s domain: %s specs: %p>' % [ self.class, @domain, @specs.keys ] + end + + ## + # Loads remote prerelease specs if +dep+ is a prerelease dependency + + def load_remote_specs dep + 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, source + key = "#{name}-#{ver}" + @specs[key] ||= source.fetch_spec Gem::NameTuple.new name, ver + end + + ## + # No prefetching needed since we load the whole index in initially. + + def prefetch(reqs) + end + +end + |