diff options
Diffstat (limited to 'lib/rubygems')
-rw-r--r-- | lib/rubygems/dependency_installer.rb | 1 | ||||
-rw-r--r-- | lib/rubygems/installer.rb | 23 | ||||
-rw-r--r-- | lib/rubygems/remote_fetcher.rb | 2 | ||||
-rw-r--r-- | lib/rubygems/request_set.rb | 15 | ||||
-rw-r--r-- | lib/rubygems/resolver.rb | 7 | ||||
-rw-r--r-- | lib/rubygems/resolver/api_set.rb | 47 | ||||
-rw-r--r-- | lib/rubygems/resolver/api_set/gem_parser.rb | 20 | ||||
-rw-r--r-- | lib/rubygems/resolver/api_specification.rb | 7 | ||||
-rw-r--r-- | lib/rubygems/resolver/best_set.rb | 2 | ||||
-rw-r--r-- | lib/rubygems/resolver/index_specification.rb | 15 | ||||
-rw-r--r-- | lib/rubygems/resolver/installer_set.rb | 64 | ||||
-rw-r--r-- | lib/rubygems/resolver/spec_specification.rb | 14 | ||||
-rw-r--r-- | lib/rubygems/resolver/specification.rb | 12 | ||||
-rw-r--r-- | lib/rubygems/source.rb | 16 |
14 files changed, 171 insertions, 74 deletions
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index fb555a46d4..400a5de5cf 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -286,6 +286,7 @@ class Gem::DependencyInstaller installer_set = Gem::Resolver::InstallerSet.new @domain installer_set.ignore_installed = (@minimal_deps == false) || @only_install_dir + installer_set.force = @force if consider_local? if dep_or_name =~ /\.gem$/ and File.file? dep_or_name diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 2c583743b9..3ec6d743a2 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -638,27 +638,6 @@ class Gem::Installer end end - def ensure_required_ruby_version_met # :nodoc: - if rrv = spec.required_ruby_version - ruby_version = Gem.ruby_version - unless rrv.satisfied_by? ruby_version - raise Gem::RuntimeRequirementNotMetError, - "#{spec.name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}." - end - end - end - - def ensure_required_rubygems_version_met # :nodoc: - if rrgv = spec.required_rubygems_version - unless rrgv.satisfied_by? Gem.rubygems_version - rg_version = Gem::VERSION - raise Gem::RuntimeRequirementNotMetError, - "#{spec.name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " + - "Try 'gem update --system' to update RubyGems itself." - end - end - end - def ensure_dependencies_met # :nodoc: deps = spec.runtime_dependencies deps |= spec.development_dependencies if @development @@ -914,8 +893,6 @@ TEXT return true if @force - ensure_required_ruby_version_met - ensure_required_rubygems_version_met ensure_dependencies_met unless @ignore_dependencies true diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 8ebe6acc70..285c80a95f 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -215,7 +215,7 @@ class Gem::RemoteFetcher case response when Net::HTTPOK, Net::HTTPNotModified then - response.uri = uri if response.respond_to? :uri + response.uri = uri head ? response : response.body when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, Net::HTTPTemporaryRedirect then diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb index a8a06d0b95..5190cfc904 100644 --- a/lib/rubygems/request_set.rb +++ b/lib/rubygems/request_set.rb @@ -195,19 +195,8 @@ class Gem::RequestSet yield req, installer if block_given? end rescue Gem::RuntimeRequirementNotMetError => e - recent_match = req.spec.set.find_all(req.request).sort_by(&:version).reverse_each.find do |s| - s = s.spec - s.required_ruby_version.satisfied_by?(Gem.ruby_version) && - s.required_rubygems_version.satisfied_by?(Gem.rubygems_version) && - Gem::Platform.installable?(s) - end - if recent_match - suggestion = "The last version of #{req.request} to support your Ruby & RubyGems was #{recent_match.version}. Try installing it with `gem install #{recent_match.name} -v #{recent_match.version}`" - suggestion += " and then running the current command again" unless @always_install.include?(req.spec.spec) - else - suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" - suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) - end + suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" + suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) e.suggestion = suggestion raise end diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb index e1c0d2dd0a..71c35ea3d3 100644 --- a/lib/rubygems/resolver.rb +++ b/lib/rubygems/resolver.rb @@ -261,7 +261,12 @@ class Gem::Resolver end def requirement_satisfied_by?(requirement, activated, spec) - requirement.matches_spec? spec + matches_spec = requirement.matches_spec? spec + return matches_spec if @soft_missing + + matches_spec && + spec.spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && + spec.spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) end def name_for(dependency) diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb index cafcd5b472..21c9b8920c 100644 --- a/lib/rubygems/resolver/api_set.rb +++ b/lib/rubygems/resolver/api_set.rb @@ -4,6 +4,8 @@ # Returns instances of APISpecification. class Gem::Resolver::APISet < Gem::Resolver::Set + autoload :GemParser, File.expand_path("api_set/gem_parser", __dir__) + ## # The URI for the dependency API this APISet uses. @@ -24,13 +26,13 @@ class Gem::Resolver::APISet < Gem::Resolver::Set # API URL +dep_uri+ which is described at # https://guides.rubygems.org/rubygems-org-api - def initialize(dep_uri = 'https://rubygems.org/api/v1/dependencies') + def initialize(dep_uri = 'https://index.rubygems.org/info/') super() dep_uri = URI dep_uri unless URI === dep_uri @dep_uri = dep_uri - @uri = dep_uri + '../..' + @uri = dep_uri + '..' @data = Hash.new {|h,k| h[k] = [] } @source = Gem::Source.new @uri @@ -75,20 +77,8 @@ class Gem::Resolver::APISet < Gem::Resolver::Set 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] = [] + needed.sort.each do |name| + versions(name) end end @@ -111,13 +101,32 @@ class Gem::Resolver::APISet < Gem::Resolver::Set return @data[name] end - uri = @dep_uri + "?gems=#{name}" + uri = @dep_uri + name str = Gem::RemoteFetcher.fetcher.fetch_path uri - Marshal.load(str).each do |ver| - @data[ver[:name]] << ver + lines(str).each do |ver| + number, platform, dependencies, requirements = parse_gem(ver) + + platform ||= "ruby" + dependencies = dependencies.map {|dep_name, reqs| [dep_name, reqs.join(", ")] } + requirements = requirements.map {|req_name, reqs| [req_name.to_sym, reqs] }.to_h + + @data[name] << { name: name, number: number, platform: platform, dependencies: dependencies, requirements: requirements } end @data[name] end + + private + + def lines(str) + lines = str.split("\n") + header = lines.index("---") + header ? lines[header + 1..-1] : lines + end + + def parse_gem(string) + @gem_parser ||= GemParser.new + @gem_parser.parse(string) + end end diff --git a/lib/rubygems/resolver/api_set/gem_parser.rb b/lib/rubygems/resolver/api_set/gem_parser.rb new file mode 100644 index 0000000000..685c39558d --- /dev/null +++ b/lib/rubygems/resolver/api_set/gem_parser.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Gem::Resolver::APISet::GemParser + def parse(line) + version_and_platform, rest = line.split(" ", 2) + version, platform = version_and_platform.split("-", 2) + dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest + dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] + requirements = requirements ? requirements.map {|d| parse_dependency(d) } : [] + [version, platform, dependencies, requirements] + end + + private + + def parse_dependency(string) + dependency = string.split(":") + dependency[-1] = dependency[-1].split("&") if dependency.size > 1 + dependency + end +end diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb index 589ea1ba61..b5aa0b71d4 100644 --- a/lib/rubygems/resolver/api_specification.rb +++ b/lib/rubygems/resolver/api_specification.rb @@ -35,6 +35,8 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification @dependencies = api_data[:dependencies].map do |name, ver| Gem::Dependency.new(name, ver.split(/\s*,\s*/)).freeze end.freeze + @required_ruby_version = Gem::Requirement.new(api_data.dig(:requirements, :ruby)).freeze + @required_rubygems_version = Gem::Requirement.new(api_data.dig(:requirements, :rubygems)).freeze end def ==(other) # :nodoc: @@ -42,12 +44,11 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification @set == other.set and @name == other.name and @version == other.version and - @platform == other.platform and - @dependencies == other.dependencies + @platform == other.platform end def hash - @set.hash ^ @name.hash ^ @version.hash ^ @platform.hash ^ @dependencies.hash + @set.hash ^ @name.hash ^ @version.hash ^ @platform.hash end def fetch_development_dependencies # :nodoc: diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb index 7a708ee391..b6a46a9403 100644 --- a/lib/rubygems/resolver/best_set.rb +++ b/lib/rubygems/resolver/best_set.rb @@ -60,7 +60,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet def replace_failed_api_set(error) # :nodoc: uri = error.uri uri = URI uri unless URI === uri - uri.query = nil + uri = uri + "." raise error unless api_set = @sets.find do |set| Gem::Resolver::APISet === set and set.dep_uri == uri diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb index 3b75b719b0..45ff130a14 100644 --- a/lib/rubygems/resolver/index_specification.rb +++ b/lib/rubygems/resolver/index_specification.rb @@ -33,6 +33,21 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification spec.dependencies end + ## + # The required_ruby_version constraint for this specification + + def required_ruby_version + spec.required_ruby_version + end + + ## + # The required_rubygems_version constraint for this specification + # + + def required_rubygems_version + spec.required_rubygems_version + end + def ==(other) self.class === other && @name == other.name && diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb index b0e4ce5a37..60181315b0 100644 --- a/lib/rubygems/resolver/installer_set.rb +++ b/lib/rubygems/resolver/installer_set.rb @@ -26,6 +26,12 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set attr_reader :remote_set # :nodoc: ## + # Ignore ruby & rubygems specification constraints. + # + + attr_accessor :force # :nodoc: + + ## # Creates a new InstallerSet that will look for gems in +domain+. def initialize(domain) @@ -41,6 +47,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set @local = {} @local_source = Gem::Source::Local.new @remote_set = Gem::Resolver::BestSet.new + @force = false @specs = {} end @@ -63,15 +70,30 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set Gem::Platform.local === s.platform end - if found.empty? - exc = Gem::UnsatisfiableDependencyError.new request - exc.errors = errors - - raise exc + found = found.sort_by do |s| + [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1] end - newest = found.max_by do |s| - [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1] + newest = found.last + + unless @force + found_matching_metadata = found.select do |spec| + metadata_satisfied?(spec) + end + + if found_matching_metadata.empty? + if newest + ensure_required_ruby_version_met(newest.spec) + ensure_required_rubygems_version_met(newest.spec) + else + exc = Gem::UnsatisfiableDependencyError.new request + exc.errors = errors + + raise exc + end + else + newest = found_matching_metadata.last + end end @always_install << newest.spec @@ -221,4 +243,32 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set @domain = :local unless remote end end + + private + + def metadata_satisfied?(spec) + spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && + spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) + end + + def ensure_required_ruby_version_met(spec) # :nodoc: + if rrv = spec.required_ruby_version + ruby_version = Gem.ruby_version + unless rrv.satisfied_by? ruby_version + raise Gem::RuntimeRequirementNotMetError, + "#{spec.full_name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}." + end + end + end + + def ensure_required_rubygems_version_met(spec) # :nodoc: + if rrgv = spec.required_rubygems_version + unless rrgv.satisfied_by? Gem.rubygems_version + rg_version = Gem::VERSION + raise Gem::RuntimeRequirementNotMetError, + "#{spec.full_name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " + + "Try 'gem update --system' to update RubyGems itself." + end + end + end end diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb index bde5d9cddc..7b665fe876 100644 --- a/lib/rubygems/resolver/spec_specification.rb +++ b/lib/rubygems/resolver/spec_specification.rb @@ -23,6 +23,20 @@ class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification end ## + # The required_ruby_version constraint for this specification + + def required_ruby_version + spec.required_ruby_version + end + + ## + # The required_rubygems_version constraint for this specification + + def required_rubygems_version + spec.required_rubygems_version + end + + ## # The name and version of the specification. # # Unlike Gem::Specification#full_name, the platform is not included. diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb index 5ae5f15813..8c6fc9afcf 100644 --- a/lib/rubygems/resolver/specification.rb +++ b/lib/rubygems/resolver/specification.rb @@ -44,6 +44,16 @@ class Gem::Resolver::Specification attr_reader :version ## + # The required_ruby_version constraint for this specification. + + attr_reader :required_ruby_version + + ## + # The required_ruby_version constraint for this specification. + + attr_reader :required_rubygems_version + + ## # Sets default instance variables for the specification. def initialize @@ -53,6 +63,8 @@ class Gem::Resolver::Specification @set = nil @source = nil @version = nil + @required_ruby_version = Gem::Requirement.default + @required_rubygems_version = Gem::Requirement.default end ## diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb index a2848112e7..4ae84cf532 100644 --- a/lib/rubygems/source.rb +++ b/lib/rubygems/source.rb @@ -79,7 +79,15 @@ class Gem::Source def dependency_resolver_set # :nodoc: return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme - bundler_api_uri = enforce_trailing_slash(uri) + './api/v1/dependencies' + fetch_uri = if uri.host == "rubygems.org" + index_uri = uri.dup + index_uri.host = "index.rubygems.org" + index_uri + else + uri + end + + bundler_api_uri = enforce_trailing_slash(fetch_uri) begin fetcher = Gem::RemoteFetcher.fetcher @@ -87,11 +95,7 @@ class Gem::Source rescue Gem::RemoteFetcher::FetchError Gem::Resolver::IndexSet.new self else - if response.respond_to? :uri - Gem::Resolver::APISet.new response.uri - else - Gem::Resolver::APISet.new bundler_api_uri - end + Gem::Resolver::APISet.new response.uri + "./info/" end end |