diff options
author | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-12-08 01:22:39 +0000 |
---|---|---|
committer | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-12-08 01:22:39 +0000 |
commit | 7ed9b794b4e3f3f9874f2ce19401461596d8a2c0 (patch) | |
tree | 5caaf13685de34b09d2949709a77b4c650b62741 /lib/rubygems/request_set | |
parent | 866b438c21ff05dfeabba8bc9aa9850e415be607 (diff) | |
download | ruby-7ed9b794b4e3f3f9874f2ce19401461596d8a2c0.tar.gz |
* lib/rubygems: Update to RubyGems master 14749ce. This fixes bugs
handling of gem dependencies lockfiles (Gemfile.lock).
* test/rubygems: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44054 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rubygems/request_set')
-rw-r--r-- | lib/rubygems/request_set/gem_dependency_api.rb | 42 | ||||
-rw-r--r-- | lib/rubygems/request_set/lockfile.rb | 220 |
2 files changed, 212 insertions, 50 deletions
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb index 0c27b1a61a..efce979177 100644 --- a/lib/rubygems/request_set/gem_dependency_api.rb +++ b/lib/rubygems/request_set/gem_dependency_api.rb @@ -221,13 +221,7 @@ class Gem::RequestSet::GemDependencyAPI return unless (groups & @without_groups).empty? - unless source_set then - raise ArgumentError, - "duplicate source (default) for gem #{name}" if - @gem_sources.include? name - - @gem_sources[name] = :default - end + pin_gem_source name, :default unless source_set gem_requires name, options @@ -246,9 +240,7 @@ class Gem::RequestSet::GemDependencyAPI return unless repository = options.delete(:git) - raise ArgumentError, - "duplicate source git: #{repository} for gem #{name}" if - @gem_sources.include? name + pin_gem_source name, :git, repository reference = nil reference ||= options.delete :ref @@ -260,8 +252,6 @@ class Gem::RequestSet::GemDependencyAPI @git_set.add_git_gem name, repository, reference, submodules - @gem_sources[name] = repository - true end @@ -310,14 +300,10 @@ class Gem::RequestSet::GemDependencyAPI def gem_path name, options # :nodoc: return unless directory = options.delete(:path) - raise ArgumentError, - "duplicate source path: #{directory} for gem #{name}" if - @gem_sources.include? name + pin_gem_source name, :path, directory @vendor_set.add_vendor_gem name, directory - @gem_sources[name] = directory - true end @@ -430,6 +416,28 @@ class Gem::RequestSet::GemDependencyAPI end ## + # Pins the gem +name+ to the given +source+. Adding a gem with the same + # name from a different +source+ will raise an exception. + + def pin_gem_source name, type = :default, source = nil + source_description = + case type + when :default then '(default)' + when :path then "path: #{source}" + when :git then "git: #{source}" + else '(unknown)' + end + + raise ArgumentError, + "duplicate source #{source_description} for gem #{name}" if + @gem_sources.fetch(name, source) != source + + @gem_sources[name] = source + end + + private :pin_gem_source + + ## # :category: Gem Dependencies DSL # # Block form for restricting gems to a particular platform. diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb index d70b09f7a2..81bc4d620d 100644 --- a/lib/rubygems/request_set/lockfile.rb +++ b/lib/rubygems/request_set/lockfile.rb @@ -1,3 +1,5 @@ +require 'strscan' + ## # Parses a gem.deps.rb.lock file and constructs a LockSet containing the # dependencies found inside. If the lock file is missing no LockSet is @@ -29,11 +31,11 @@ class Gem::RequestSet::Lockfile # Raises a ParseError with the given +message+ which was encountered at a # +line+ and +column+ while parsing. - def initialize message, line, column, path + def initialize message, column, line, path @line = line @column = column @path = path - super "#{message} (at #{line}:#{column})" + super "#{message} (at line #{line} column #{column})" end end @@ -62,30 +64,31 @@ class Gem::RequestSet::Lockfile def add_DEPENDENCIES out # :nodoc: out << "DEPENDENCIES" - @set.dependencies.sort.map do |dependency| - source = @requests.find do |req| - req.name == dependency.name and - req.spec.class == Gem::Resolver::VendorSpecification - end - - source_dep = '!' if source + @requests.sort_by { |r| r.name }.each do |request| + spec = request.spec - requirement = dependency.requirement + if [Gem::Resolver::VendorSpecification, + Gem::Resolver::GitSpecification].include? spec.class then + out << " #{request.name}!" + else + requirement = request.request.dependency.requirement - out << " #{dependency.name}#{source_dep}#{requirement.for_lockfile}" + out << " #{request.name}#{requirement.for_lockfile}" + end end out << nil end def add_GEM out # :nodoc: - out << "GEM" + return if @spec_groups.empty? source_groups = @spec_groups.values.flatten.group_by do |request| request.spec.source.uri end - source_groups.map do |group, requests| + source_groups.sort_by { |group,| group.to_s }.map do |group, requests| + out << "GEM" out << " remote: #{group}" out << " specs:" @@ -100,6 +103,33 @@ class Gem::RequestSet::Lockfile out << " #{dependency.name}#{requirement.for_lockfile}" end end + out << nil + end + end + + def add_GIT out + return unless git_requests = + @spec_groups.delete(Gem::Resolver::GitSpecification) + + by_repository_revision = git_requests.group_by do |request| + source = request.spec.source + [source.repository, source.rev_parse] + end + + out << "GIT" + by_repository_revision.each do |(repository, revision), requests| + out << " remote: #{repository}" + out << " revision: #{revision}" + out << " specs:" + + requests.sort_by { |request| request.name }.each do |request| + out << " #{request.name} (#{request.version})" + + dependencies = request.spec.dependencies.sort_by { |dep| dep.name } + dependencies.each do |dep| + out << " #{dep.name}#{dep.requirement.for_lockfile}" + end + end end out << nil @@ -148,27 +178,28 @@ class Gem::RequestSet::Lockfile ## # Gets the next token for a Lockfile - def get expected_type = nil, expected_value = nil # :nodoc: + def get expected_types = nil, expected_value = nil # :nodoc: @current_token = @tokens.shift - type, value, line, column = @current_token + type, value, column, line = @current_token - if expected_type and expected_type != type then + if expected_types and not Array(expected_types).include? type then unget message = "unexpected token [#{type.inspect}, #{value.inspect}], " + - "expected #{expected_type.inspect}" + "expected #{expected_types.inspect}" - raise ParseError.new message, line, column, "#{@gem_deps_file}.lock" + raise ParseError.new message, column, line, "#{@gem_deps_file}.lock" end if expected_value and expected_value != value then unget message = "unexpected token [#{type.inspect}, #{value.inspect}], " + - "expected [#{expected_type.inspect}, #{expected_value.inspect}]" + "expected [#{expected_types.inspect}, " + + "#{expected_value.inspect}]" - raise ParseError.new message, line, column, "#{@gem_deps_file}.lock" + raise ParseError.new message, column, line, "#{@gem_deps_file}.lock" end @current_token @@ -187,6 +218,8 @@ class Gem::RequestSet::Lockfile case data when 'DEPENDENCIES' then parse_DEPENDENCIES + when 'GIT' then + parse_GIT when 'GEM' then parse_GEM when 'PLATFORMS' then @@ -195,7 +228,7 @@ class Gem::RequestSet::Lockfile type, = get until @tokens.empty? or peek.first == :section end else - raise "BUG: unhandled token #{type} (#{data.inspect}) at #{line}:#{column}" + raise "BUG: unhandled token #{type} (#{data.inspect}) at line #{line} column #{column}" end end end @@ -204,7 +237,37 @@ class Gem::RequestSet::Lockfile while not @tokens.empty? and :text == peek.first do _, name, = get :text - @set.gem name + requirements = [] + + case peek[0] + when :bang then + get :bang + + git_spec = @set.sets.select { |set| + Gem::Resolver::GitSet === set + }.map { |set| + set.specs[name] + }.first + + requirements << git_spec.version + when :l_paren then + get :l_paren + + loop do + _, op, = get :requirement + _, version, = get :text + + requirements << "#{op} #{version}" + + break unless peek[0] == :comma + + get :comma + end + + get :r_paren + end + + @set.gem name, *requirements skip :newline end @@ -223,20 +286,76 @@ class Gem::RequestSet::Lockfile skip :newline set = Gem::Resolver::LockSet.new source + last_spec = nil while not @tokens.empty? and :text == peek.first do - _, name, = get :text + _, name, column, = get :text case peek[0] - when :newline then # ignore + when :newline then + last_spec.add_dependency Gem::Dependency.new name if column == 6 when :l_paren then get :l_paren - _, version, = get :text + type, data, = get [:text, :requirement] + + if type == :text and column == 4 then + last_spec = set.add name, data, Gem::Platform::RUBY + else + dependency = parse_dependency name, data + + last_spec.add_dependency dependency + end get :r_paren + else + raise "BUG: unknown token #{peek}" + end - set.add name, version, Gem::Platform::RUBY + skip :newline + end + + @set.sets << set + end + + def parse_GIT # :nodoc: + get :entry, 'remote' + _, repository, = get :text + + skip :newline + + get :entry, 'revision' + _, revision, = get :text + + skip :newline + + get :entry, 'specs' + + skip :newline + + set = Gem::Resolver::GitSet.new + last_spec = nil + + while not @tokens.empty? and :text == peek.first do + _, name, column, = get :text + + case peek[0] + when :newline then + last_spec.add_dependency Gem::Dependency.new name if column == 6 + when :l_paren then + get :l_paren + + type, data, = get [:text, :requirement] + + if type == :text and column == 4 then + last_spec = set.add_git_spec name, data, repository, revision, true + else + dependency = parse_dependency name, data + + last_spec.spec.dependencies << dependency + end + + get :r_paren else raise "BUG: unknown token #{peek}" end @@ -258,10 +377,32 @@ class Gem::RequestSet::Lockfile end ## + # Parses the requirements following the dependency +name+ and the +op+ for + # the first token of the requirements and returns a Gem::Dependency object. + + def parse_dependency name, op # :nodoc: + return Gem::Dependency.new name unless peek[0] == :text + + _, version, = get :text + + requirements = ["#{op} #{version}"] + + while peek[0] == :comma do + get :comma + _, op, = get :requirement + _, version, = get :text + + requirements << "#{op} #{version}" + end + + Gem::Dependency.new name, requirements + end + + ## # Peeks at the next token for Lockfile def peek # :nodoc: - @tokens.first + @tokens.first || :EOF end def skip type # :nodoc: @@ -284,6 +425,8 @@ class Gem::RequestSet::Lockfile add_PATH out + add_GIT out + add_GEM out add_PLATFORMS out @@ -321,14 +464,13 @@ class Gem::RequestSet::Lockfile until s.eos? do pos = s.pos - # leading whitespace is for the user's convenience - next if s.scan(/ +/) + pos = s.pos if leading_whitespace = s.scan(/ +/) if s.scan(/[<|=>]{7}/) then message = "your #{lock_file} contains merge conflict markers" - line, column = token_pos pos + column, line = token_pos pos - raise ParseError.new message, line, column, lock_file + raise ParseError.new message, column, line, lock_file end @tokens << @@ -339,7 +481,13 @@ class Gem::RequestSet::Lockfile @line += 1 token when s.scan(/[A-Z]+/) then - [:section, s.matched, *token_pos(pos)] + if leading_whitespace then + text = s.matched + text += s.scan(/[^\s)]*/).to_s # in case of no match + [:text, text, *token_pos(pos)] + else + [:section, s.matched, *token_pos(pos)] + end when s.scan(/([a-z]+):\s/) then s.pos -= 1 # rewind for possible newline [:entry, s[1], *token_pos(pos)] @@ -347,7 +495,13 @@ class Gem::RequestSet::Lockfile [:l_paren, nil, *token_pos(pos)] when s.scan(/\)/) then [:r_paren, nil, *token_pos(pos)] - when s.scan(/[^\s)]*/) then + when s.scan(/<=|>=|=|~>|<|>|!=/) then + [:requirement, s.matched, *token_pos(pos)] + when s.scan(/,/) then + [:comma, nil, *token_pos(pos)] + when s.scan(/!/) then + [:bang, nil, *token_pos(pos)] + when s.scan(/[^\s),!]*/) then [:text, s.matched, *token_pos(pos)] else raise "BUG: can't create token for: #{s.string[s.pos..-1].inspect}" |