1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/rubygems/request_set/lockfile.rb
drbrain a7fa4d5d9a * lib/rubygems: Update to RubyGems master 6a3d9f9. Changes include:
Compatibly renamed Gem::DependencyResolver to Gem::Resolver.

  Added support for git gems in gem.deps.rb and Gemfile.

  Fixed resolver bugs.

* test/rubygems: ditto.

* lib/rubygems/LICENSE.txt:  Updated to license from RubyGems trunk.
  [ruby-trunk - Bug #9086]

* lib/rubygems/commands/which_command.rb:  RubyGems now indicates
  failure when any file is missing.  [ruby-trunk - Bug #9004]

* lib/rubygems/ext/builder:  Extensions are now installed into the
  extension install directory and the first directory in the require
  path from the gem.  This allows backwards compatibility with msgpack
  and other gems that calculate full require paths.
  [ruby-trunk - Bug #9106]



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43714 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2013-11-19 00:34:13 +00:00

356 lines
7.5 KiB
Ruby

class Gem::RequestSet::Lockfile
##
# Raised when a lockfile cannot be parsed
class ParseError < Gem::Exception
##
# The column where the error was encountered
attr_reader :column
##
# The line where the error was encountered
attr_reader :line
##
# The location of the lock file
attr_reader :path
##
# Raises a ParseError with the given +message+ which was encountered at a
# +line+ and +column+ while parsing.
def initialize message, line, column, path
@line = line
@column = column
@path = path
super "#{message} (at #{line}:#{column})"
end
end
##
# The platforms for this Lockfile
attr_reader :platforms
##
# Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
# location.
def initialize request_set, gem_deps_file
@set = request_set
@gem_deps_file = File.expand_path(gem_deps_file)
@gem_deps_dir = File.dirname(@gem_deps_file)
@current_token = nil
@line = 0
@line_pos = 0
@platforms = []
@tokens = []
end
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
requirement = dependency.requirement
out << " #{dependency.name}#{source_dep}#{requirement.for_lockfile}"
end
out << nil
end
def add_GEM out # :nodoc:
out << "GEM"
source_groups = @spec_groups.values.flatten.group_by do |request|
request.spec.source.uri
end
source_groups.map do |group, requests|
out << " remote: #{group}"
out << " specs:"
requests.sort_by { |request| request.name }.each do |request|
platform = "-#{request.spec.platform}" unless
Gem::Platform::RUBY == request.spec.platform
out << " #{request.name} (#{request.version}#{platform})"
request.full_spec.dependencies.sort.each do |dependency|
requirement = dependency.requirement
out << " #{dependency.name}#{requirement.for_lockfile}"
end
end
end
out << nil
end
def relative_path_from(dest, base)
dest = File.expand_path(dest)
base = File.expand_path(base)
if dest.index(base) == 0
return dest[base.size+1..-1]
else
dest
end
end
def add_PATH out # :nodoc:
return unless path_requests =
@spec_groups.delete(Gem::Resolver::VendorSpecification)
out << "PATH"
path_requests.each do |request|
directory = File.expand_path(request.spec.source.uri)
out << " remote: #{relative_path_from directory, @gem_deps_dir}"
out << " specs:"
out << " #{request.name} (#{request.version})"
end
out << nil
end
def add_PLATFORMS out # :nodoc:
out << "PLATFORMS"
platforms = @requests.map { |request| request.spec.platform }.uniq
platforms.delete Gem::Platform::RUBY if platforms.length > 1
platforms.each do |platform|
out << " #{platform}"
end
out << nil
end
##
# Gets the next token for a Lockfile
def get expected_type = nil, expected_value = nil # :nodoc:
@current_token = @tokens.shift
type, value, line, column = @current_token
if expected_type and expected_type != type then
unget
message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
"expected #{expected_type.inspect}"
raise ParseError.new message, line, column, "#{@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}]"
raise ParseError.new message, line, column, "#{@gem_deps_file}.lock"
end
@current_token
end
def parse # :nodoc:
tokenize
until @tokens.empty? do
type, data, column, line = get
case type
when :section then
skip :newline
case data
when 'DEPENDENCIES' then
parse_DEPENDENCIES
when 'GEM' then
parse_GEM
when 'PLATFORMS' then
parse_PLATFORMS
else
type, = get until @tokens.empty? or peek.first == :section
end
else
raise "BUG: unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
end
end
end
def parse_DEPENDENCIES # :nodoc:
while not @tokens.empty? and :text == peek.first do
_, name, = get :text
@set.gem name
skip :newline
end
end
def parse_GEM # :nodoc:
get :entry, 'remote'
_, data, = get :text
source = Gem::Source.new data
skip :newline
get :entry, 'specs'
skip :newline
set = Gem::Resolver::LockSet.new source
while not @tokens.empty? and :text == peek.first do
_, name, = get :text
case peek[0]
when :newline then # ignore
when :l_paren then
get :l_paren
_, version, = get :text
get :r_paren
set.add name, version, Gem::Platform::RUBY
else
raise "BUG: unknown token #{peek}"
end
skip :newline
end
@set.sets << set
end
def parse_PLATFORMS # :nodoc:
while not @tokens.empty? and :text == peek.first do
_, name, = get :text
@platforms << name
skip :newline
end
end
##
# Peeks at the next token for Lockfile
def peek # :nodoc:
@tokens.first
end
def skip type # :nodoc:
get while not @tokens.empty? and peek.first == type
end
def to_s
@set.resolve
out = []
@requests = @set.sorted_requests
@spec_groups = @requests.group_by do |request|
request.spec.class
end
add_PATH out
add_GEM out
add_PLATFORMS out
add_DEPENDENCIES out
out.join "\n"
end
##
# Calculates the column (by byte) and the line of the current token based on
# +byte_offset+.
def token_pos byte_offset # :nodoc:
[byte_offset - @line_pos, @line]
end
def tokenize # :nodoc:
@line = 0
@line_pos = 0
@platforms = []
@tokens = []
@current_token = nil
lock_file = "#{@gem_deps_file}.lock"
@input = File.read lock_file
s = StringScanner.new @input
until s.eos? do
pos = s.pos
# leading whitespace is for the user's convenience
next if s.scan(/ +/)
if s.scan(/[<|=>]{7}/) then
message = "your #{lock_file} contains merge conflict markers"
line, column = token_pos pos
raise ParseError.new message, line, column, lock_file
end
@tokens <<
case
when s.scan(/\r?\n/) then
token = [:newline, nil, *token_pos(pos)]
@line_pos = s.pos
@line += 1
token
when s.scan(/[A-Z]+/) then
[:section, s.matched, *token_pos(pos)]
when s.scan(/([a-z]+):\s/) then
s.pos -= 1 # rewind for possible newline
[:entry, s[1], *token_pos(pos)]
when s.scan(/\(/) then
[:l_paren, nil, *token_pos(pos)]
when s.scan(/\)/) then
[:r_paren, 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}"
end
end
@tokens
end
##
# Ungets the last token retrieved by #get
def unget # :nodoc:
@tokens.unshift @current_token
end
end