From 4de117a61517e839f2c45eaf45d56fc243d6d5b2 Mon Sep 17 00:00:00 2001 From: hsbt Date: Sun, 14 Sep 2014 03:30:02 +0000 Subject: * lib/rubygems: Update to RubyGems 2.4.1 master(713ab65) Complete history at: https://github.com/rubygems/rubygems/blob/master/History.txt#L3-L216 * test/rubygems: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47582 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/resolver/activation_request.rb | 7 ++ lib/rubygems/resolver/api_set.rb | 14 +++- lib/rubygems/resolver/api_specification.rb | 6 ++ lib/rubygems/resolver/best_set.rb | 28 +++++++ lib/rubygems/resolver/composed_set.rb | 16 ++++ lib/rubygems/resolver/conflict.rb | 52 +++++++++++-- lib/rubygems/resolver/dependency_request.rb | 21 +++++- lib/rubygems/resolver/git_set.rb | 2 +- lib/rubygems/resolver/git_specification.rb | 26 ++++++- lib/rubygems/resolver/index_set.rb | 6 +- lib/rubygems/resolver/installed_specification.rb | 20 ++++- lib/rubygems/resolver/installer_set.rb | 94 +++++++++++++++++++++++- lib/rubygems/resolver/local_specification.rb | 25 +++++++ lib/rubygems/resolver/lock_set.rb | 22 +++--- lib/rubygems/resolver/lock_specification.rb | 28 ++++++- lib/rubygems/resolver/set.rb | 14 +++- lib/rubygems/resolver/spec_specification.rb | 2 - lib/rubygems/resolver/specification.rb | 25 ++++++- lib/rubygems/resolver/vendor_set.rb | 4 +- lib/rubygems/resolver/vendor_specification.rb | 2 +- 20 files changed, 378 insertions(+), 36 deletions(-) (limited to 'lib/rubygems/resolver') diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb index 2d48cfa927..56c6363e4f 100644 --- a/lib/rubygems/resolver/activation_request.rb +++ b/lib/rubygems/resolver/activation_request.rb @@ -38,6 +38,13 @@ class Gem::Resolver::ActivationRequest 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. diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb index 5475e626e6..dda3579878 100644 --- a/lib/rubygems/resolver/api_set.rb +++ b/lib/rubygems/resolver/api_set.rb @@ -34,6 +34,8 @@ class Gem::Resolver::APISet < Gem::Resolver::Set @data = Hash.new { |h,k| h[k] = [] } @source = Gem::Source.new @uri + + @to_fetch = [] end ## @@ -45,6 +47,10 @@ class Gem::Resolver::APISet < Gem::Resolver::Set 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) @@ -61,9 +67,13 @@ class Gem::Resolver::APISet < Gem::Resolver::Set def prefetch reqs return unless @remote names = reqs.map { |r| r.dependency.name } - needed = names - @data.keys + needed = names - @data.keys - @to_fetch + + @to_fetch += needed + end - return if needed.empty? + def prefetch_now + needed, @to_fetch = @to_fetch, [] uri = @dep_uri + "?gems=#{needed.sort.join ','}" str = Gem::RemoteFetcher.fetcher.fetch_path uri diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb index 67052af82e..bbd5a6427b 100644 --- a/lib/rubygems/resolver/api_specification.rb +++ b/lib/rubygems/resolver/api_specification.rb @@ -34,6 +34,12 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification @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 diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb index 20bb94827b..7e2d7e2647 100644 --- a/lib/rubygems/resolver/best_set.rb +++ b/lib/rubygems/resolver/best_set.rb @@ -28,6 +28,10 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet pick_sets if @remote and @sets.empty? super + rescue Gem::RemoteFetcher::FetchError => e + replace_failed_api_set e + + retry end def prefetch reqs # :nodoc: @@ -46,5 +50,29 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet 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/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb index 6f912b0afe..5b08f128ed 100644 --- a/lib/rubygems/resolver/composed_set.rb +++ b/lib/rubygems/resolver/composed_set.rb @@ -21,6 +21,18 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set @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. @@ -30,6 +42,10 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set @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. diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb index 8830e8d1fb..902c286b6b 100644 --- a/lib/rubygems/resolver/conflict.rb +++ b/lib/rubygems/resolver/conflict.rb @@ -52,11 +52,40 @@ class Gem::Resolver::Conflict def explanation activated = @activated.spec.full_name - requirement = @failed_dep.dependency.requirement + dependency = @failed_dep.dependency + requirement = dependency.requirement + alternates = dependency.matching_specs.map { |spec| spec.full_name } - " Activated %s via:\n %s\n instead of (%s) via:\n %s\n" % [ - activated, request_path(@activated).join(', '), - requirement, request_path(requester).join(', '), + 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 @@ -95,10 +124,19 @@ class Gem::Resolver::Conflict path = [] while current do - requirement = current.request.dependency.requirement - path << "#{current.spec.full_name} (#{requirement})" + 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.parent + 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? diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb index 1d51db4945..79690bec4c 100644 --- a/lib/rubygems/resolver/dependency_request.rb +++ b/lib/rubygems/resolver/dependency_request.rb @@ -35,7 +35,26 @@ class Gem::Resolver::DependencyRequest end ## - # Does this dependency request match +spec+ + # 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 diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb index d32710e3d6..5f1b368ac1 100644 --- a/lib/rubygems/resolver/git_set.rb +++ b/lib/rubygems/resolver/git_set.rb @@ -80,7 +80,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set prefetch nil specs.values.select do |spec| - req.matches_spec? spec + req.match? spec end end diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb index 113e7ea9de..55e180e525 100644 --- a/lib/rubygems/resolver/git_specification.rb +++ b/lib/rubygems/resolver/git_specification.rb @@ -12,11 +12,15 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification @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 + def install options = {} require 'rubygems/installer' installer = Gem::Installer.new '', options @@ -31,5 +35,25 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification 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/lib/rubygems/resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb index ef01f0f0ad..7c56c2bf99 100644 --- a/lib/rubygems/resolver/index_set.rb +++ b/lib/rubygems/resolver/index_set.rb @@ -18,7 +18,9 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set @all = Hash.new { |h,k| h[k] = [] } - list, = @f.available_specs :released + list, errors = @f.available_specs :complete + + @errors.concat errors list.each do |uri, specs| specs.each do |n| @@ -41,7 +43,7 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set name = req.dependency.name @all[name].each do |uri, n| - if req.dependency.match? n then + if req.match? n, @prerelease then res << Gem::Resolver::IndexSpecification.new( self, n.name, n.version, uri, n.platform) end diff --git a/lib/rubygems/resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb index a9438129fb..2a2b89a6c2 100644 --- a/lib/rubygems/resolver/installed_specification.rb +++ b/lib/rubygems/resolver/installed_specification.rb @@ -14,7 +14,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification # This is a null install as this specification is already installed. # +options+ are ignored. - def install options + def install options = {} yield nil end @@ -29,6 +29,24 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification 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 diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb index 045c893fdc..f53b496dc7 100644 --- a/lib/rubygems/resolver/installer_set.rb +++ b/lib/rubygems/resolver/installer_set.rb @@ -20,6 +20,11 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set 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+. @@ -34,10 +39,52 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set @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? @@ -52,6 +99,13 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set @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+. @@ -62,30 +116,53 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set dep = req.dependency return res if @ignore_dependencies and - @always_install.none? { |spec| dep.matches_spec? spec } + @always_install.none? { |spec| dep.match? spec } name = dep.name dep.matching_specs.each do |gemspec| - next if @always_install.include? 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 spec = local_source.find_gem(name, dep.requirement) then + if local_spec = local_source.find_gem(name, dep.requirement) then res << Gem::Resolver::IndexSpecification.new( - self, spec.name, spec.version, local_source, spec.platform) + 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) + end + + def prerelease= allow_prerelease + super + + @remote_set.prerelease = allow_prerelease + end + def inspect # :nodoc: always_install = @always_install.map { |s| s.full_name } @@ -108,6 +185,15 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set 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 diff --git a/lib/rubygems/resolver/local_specification.rb b/lib/rubygems/resolver/local_specification.rb index dcca6c736a..20a283f0ba 100644 --- a/lib/rubygems/resolver/local_specification.rb +++ b/lib/rubygems/resolver/local_specification.rb @@ -12,5 +12,30 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification 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/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb index f4987576ec..4ede5971fb 100644 --- a/lib/rubygems/resolver/lock_set.rb +++ b/lib/rubygems/resolver/lock_set.rb @@ -6,13 +6,16 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set attr_reader :specs # :nodoc: ## - # Creates a new LockSet from the given +source+ + # Creates a new LockSet from the given +sources+ - def initialize source + def initialize sources super() - @source = Gem::Source::Lock.new source - @specs = [] + @sources = sources.map do |source| + Gem::Source::Lock.new source + end + + @specs = [] end ## @@ -25,13 +28,14 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set def add name, version, platform # :nodoc: version = Gem::Version.new version - spec = - Gem::Resolver::LockSpecification.new self, name, version, @source, + specs = @sources.map do |source| + Gem::Resolver::LockSpecification.new self, name, version, source, platform + end - @specs << spec + @specs.concat specs - spec + specs end ## @@ -40,7 +44,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set def find_all req @specs.select do |spec| - req.matches_spec? spec + req.match? spec end end diff --git a/lib/rubygems/resolver/lock_specification.rb b/lib/rubygems/resolver/lock_specification.rb index 4bc21b9402..0013171469 100644 --- a/lib/rubygems/resolver/lock_specification.rb +++ b/lib/rubygems/resolver/lock_specification.rb @@ -23,7 +23,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification # This is a null install as a locked specification is considered installed. # +options+ are ignored. - def install options + def install options = {} destination = options[:install_dir] || Gem.dir if File.exist? File.join(destination, 'specifications', spec.spec_name) then @@ -41,10 +41,36 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification @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 diff --git a/lib/rubygems/resolver/set.rb b/lib/rubygems/resolver/set.rb index f053b65e15..b26dc45c7b 100644 --- a/lib/rubygems/resolver/set.rb +++ b/lib/rubygems/resolver/set.rb @@ -9,8 +9,20 @@ class Gem::Resolver::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: - @remote = true + @prerelease = false + @remote = true + @errors = [] end ## diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb index 0c411bdf5f..1350e8a7ab 100644 --- a/lib/rubygems/resolver/spec_specification.rb +++ b/lib/rubygems/resolver/spec_specification.rb @@ -4,8 +4,6 @@ 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 diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb index d158225474..4d77293262 100644 --- a/lib/rubygems/resolver/specification.rb +++ b/lib/rubygems/resolver/specification.rb @@ -30,6 +30,14 @@ class Gem::Resolver::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. @@ -47,6 +55,13 @@ class Gem::Resolver::Specification @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. # @@ -61,8 +76,11 @@ class Gem::Resolver::Specification # 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 + def install options = {} require 'rubygems/installer' destination = options[:install_dir] || Gem.dir @@ -75,7 +93,7 @@ class Gem::Resolver::Specification yield installer if block_given? - installer.install + @spec = installer.install end ## @@ -85,5 +103,8 @@ class Gem::Resolver::Specification Gem::Platform.match spec.platform end + def local? # :nodoc: + false + end end diff --git a/lib/rubygems/resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb index 6e867073be..614bd05382 100644 --- a/lib/rubygems/resolver/vendor_set.rb +++ b/lib/rubygems/resolver/vendor_set.rb @@ -43,6 +43,8 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set @specs[spec.name] = spec @directories[spec] = directory + + spec end ## @@ -51,7 +53,7 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set def find_all req @specs.values.select do |spec| - req.matches_spec? spec + req.match? spec end.map do |spec| source = Gem::Source::Vendor.new @directories[spec] Gem::Resolver::VendorSpecification.new self, spec, source diff --git a/lib/rubygems/resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb index c6a8e58d9b..a99b5f3cc1 100644 --- a/lib/rubygems/resolver/vendor_specification.rb +++ b/lib/rubygems/resolver/vendor_specification.rb @@ -16,7 +16,7 @@ class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification # This is a null install as this gem was unpacked into a directory. # +options+ are ignored. - def install options + def install options = {} yield nil end -- cgit v1.2.3