summaryrefslogtreecommitdiff
path: root/lib/rubygems/request_set/lockfile.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/request_set/lockfile.rb')
-rw-r--r--lib/rubygems/request_set/lockfile.rb445
1 files changed, 21 insertions, 424 deletions
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index 4f2fa0933f..2c788fb703 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -1,12 +1,9 @@
-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
# constructed.
class Gem::RequestSet::Lockfile
-
##
# Raised when a lockfile cannot be parsed
@@ -37,7 +34,6 @@ class Gem::RequestSet::Lockfile
@path = path
super "#{message} (at line #{line} column #{column})"
end
-
end
##
@@ -57,11 +53,7 @@ class Gem::RequestSet::Lockfile
@gem_deps_file.untaint unless gem_deps_file.tainted?
- @current_token = nil
- @line = 0
- @line_pos = 0
@platforms = []
- @tokens = []
end
def add_DEPENDENCIES out # :nodoc:
@@ -80,7 +72,7 @@ class Gem::RequestSet::Lockfile
[name, requirement_string]
end
else
- @requests.sort_by { |r| r.name }.map do |request|
+ requests.sort_by { |r| r.name }.map do |request|
spec = request.spec
name = request.name
requirement = request.request.dependency.requirement
@@ -106,10 +98,10 @@ class Gem::RequestSet::Lockfile
out << nil
end
- def add_GEM out # :nodoc:
- return if @spec_groups.empty?
+ def add_GEM out, spec_groups # :nodoc:
+ return if spec_groups.empty?
- source_groups = @spec_groups.values.flatten.group_by do |request|
+ source_groups = spec_groups.values.flatten.group_by do |request|
request.spec.source.uri
end
@@ -136,9 +128,8 @@ class Gem::RequestSet::Lockfile
end
end
- def add_GIT out
- return unless git_requests =
- @spec_groups.delete(Gem::Resolver::GitSpecification)
+ def add_GIT out, git_requests
+ return if git_requests.empty?
by_repository_revision = git_requests.group_by do |request|
source = request.spec.source
@@ -179,9 +170,8 @@ class Gem::RequestSet::Lockfile
end
end
- def add_PATH out # :nodoc:
- return unless path_requests =
- @spec_groups.delete(Gem::Resolver::VendorSpecification)
+ def add_PATH out, path_requests # :nodoc:
+ return if path_requests.empty?
out << "PATH"
path_requests.each do |request|
@@ -198,7 +188,7 @@ class Gem::RequestSet::Lockfile
def add_PLATFORMS out # :nodoc:
out << "PLATFORMS"
- platforms = @requests.map { |request| request.spec.platform }.uniq
+ platforms = requests.map { |request| request.spec.platform }.uniq
platforms = platforms.sort_by { |platform| platform.to_s }
@@ -209,319 +199,8 @@ class Gem::RequestSet::Lockfile
out << nil
end
- ##
- # Gets the next token for a Lockfile
-
- def get expected_types = nil, expected_value = nil # :nodoc:
- @current_token = @tokens.shift
-
- type, value, column, line = @current_token
-
- if expected_types and not Array(expected_types).include? type then
- unget
-
- message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
- "expected #{expected_types.inspect}"
-
- 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_types.inspect}, " +
- "#{expected_value.inspect}]"
-
- raise ParseError.new message, column, line, "#{@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 'GIT' then
- parse_GIT
- when 'GEM' then
- parse_GEM
- when 'PATH' then
- parse_PATH
- 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 #{line} column #{column}"
- end
- end
- end
-
- def parse_DEPENDENCIES # :nodoc:
- while not @tokens.empty? and :text == peek.first do
- _, name, = get :text
-
- requirements = []
-
- case peek[0]
- when :bang then
- get :bang
-
- requirements << pinned_requirement(name)
- 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
-
- if peek[0] == :bang then
- requirements.clear
- requirements << pinned_requirement(name)
-
- get :bang
- end
- end
-
- @set.gem name, *requirements
-
- skip :newline
- end
- end
-
- def parse_GEM # :nodoc:
- sources = []
-
- while [:entry, 'remote'] == peek.first(2) do
- get :entry, 'remote'
- _, data, = get :text
- skip :newline
-
- sources << Gem::Source.new(data)
- end
-
- sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty?
-
- get :entry, 'specs'
-
- skip :newline
-
- set = Gem::Resolver::LockSet.new sources
- last_specs = nil
-
- while not @tokens.empty? and :text == peek.first do
- _, name, column, = get :text
-
- case peek[0]
- when :newline then
- last_specs.each do |spec|
- spec.add_dependency Gem::Dependency.new name if column == 6
- end
- when :l_paren then
- get :l_paren
-
- type, data, = get [:text, :requirement]
-
- if type == :text and column == 4 then
- version, platform = data.split '-', 2
-
- platform =
- platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
-
- last_specs = set.add name, version, platform
- else
- dependency = parse_dependency name, data
-
- last_specs.each do |spec|
- spec.add_dependency dependency
- end
- end
-
- get :r_paren
- else
- raise "BUG: unknown token #{peek}"
- end
-
- 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
-
- type, value = peek.first 2
- if type == :entry and %w[branch ref tag].include? value then
- get
- get :text
-
- skip :newline
- end
-
- get :entry, 'specs'
-
- skip :newline
-
- set = Gem::Resolver::GitSet.new
- set.root_dir = @set.install_dir
-
- 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.add_dependency dependency
- end
-
- get :r_paren
- else
- raise "BUG: unknown token #{peek}"
- end
-
- skip :newline
- end
-
- @set.sets << set
- end
-
- def parse_PATH # :nodoc:
- get :entry, 'remote'
- _, directory, = get :text
-
- skip :newline
-
- get :entry, 'specs'
-
- skip :newline
-
- set = Gem::Resolver::VendorSet.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_vendor_gem name, directory
- else
- dependency = parse_dependency name, data
-
- last_spec.dependencies << dependency
- end
-
- get :r_paren
- 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
-
- ##
- # 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, op 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 || [:EOF]
- end
-
- def pinned_requirement name # :nodoc:
- spec = @set.sets.select { |set|
- Gem::Resolver::GitSet === set or
- Gem::Resolver::VendorSet === set
- }.map { |set|
- set.specs[name]
- }.compact.first
-
- spec.version
- end
-
- def skip type # :nodoc:
- get while not @tokens.empty? and peek.first == type
+ def spec_groups
+ requests.group_by { |request| request.spec.class }
end
##
@@ -532,17 +211,13 @@ class Gem::RequestSet::Lockfile
out = []
- @requests = @set.sorted_requests
-
- @spec_groups = @requests.group_by do |request|
- request.spec.class
- end
+ groups = spec_groups
- add_PATH out
+ add_PATH out, groups.delete(Gem::Resolver::VendorSpecification) { [] }
- add_GIT out
+ add_GIT out, groups.delete(Gem::Resolver::GitSpecification) { [] }
- add_GEM out
+ add_GEM out, groups
add_PLATFORMS out
@@ -552,90 +227,6 @@ class Gem::RequestSet::Lockfile
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
-
- ##
- # Converts a lock file into an Array of tokens. If the lock file is missing
- # an empty Array is returned.
-
- 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
-
- pos = s.pos if leading_whitespace = s.scan(/ +/)
-
- if s.scan(/[<|=>]{7}/) then
- message = "your #{lock_file} contains merge conflict markers"
- column, line = token_pos pos
-
- raise ParseError.new message, column, line, 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
- 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)]
- when s.scan(/\(/) then
- [:l_paren, nil, *token_pos(pos)]
- when s.scan(/\)/) then
- [:r_paren, nil, *token_pos(pos)]
- 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}"
- end
- end
-
- @tokens
- rescue Errno::ENOENT
- @tokens
- end
-
- ##
- # Ungets the last token retrieved by #get
-
- def unget # :nodoc:
- @tokens.unshift @current_token
- end
-
- ##
# Writes the lock file alongside the gem dependencies file
def write
@@ -646,5 +237,11 @@ class Gem::RequestSet::Lockfile
end
end
+ private
+
+ def requests
+ @set.sorted_requests
+ end
end
+require 'rubygems/request_set/lockfile/tokenizer'