forked from rubygems/bundler
/
lockfile_parser.rb
127 lines (106 loc) · 3.71 KB
/
lockfile_parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
require "strscan"
# Some versions of the Bundler 1.1 RC series introduced corrupted
# lockfiles. There were two major problems:
#
# * multiple copies of the same GIT section appeared in the lockfile
# * when this happened, those sections got multiple copies of gems
# in those sections.
#
# As a result, Bundler 1.1 contains code that fixes the earlier
# corruption. We will remove this fix-up code in Bundler 1.2.
module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms
def initialize(lockfile)
@platforms = []
@sources = []
@dependencies = []
@specs = []
@state = :source
lockfile.split(/(\r?\n)+/).each do |line|
if line == "DEPENDENCIES"
@state = :dependency
elsif line == "PLATFORMS"
@state = :platform
else
send("parse_#{@state}", line)
end
end
end
private
TYPES = {
"GIT" => Bundler::Source::Git,
"GEM" => Bundler::Source::Rubygems,
"PATH" => Bundler::Source::Path
}
def parse_source(line)
case line
when "GIT", "GEM", "PATH"
@current_source = nil
@opts, @type = {}, line
when " specs:"
@current_source = TYPES[@type].from_lock(@opts)
# Strip out duplicate GIT sections
if @sources.include?(@current_source) && @current_source.is_a?(Bundler::Source::Git)
@current_source = @sources.find { |s| s == @current_source }
end
@sources << @current_source
when /^ ([a-z]+): (.*)$/i
value = $2
value = true if value == "true"
value = false if value == "false"
key = $1
if @opts[key]
@opts[key] = Array(@opts[key])
@opts[key] << value
else
@opts[key] = value
end
else
parse_spec(line)
end
end
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
def parse_dependency(line)
if line =~ %r{^ {2}#{NAME_VERSION}(!)?$}
name, version, pinned = $1, $2, $4
version = version.split(",").map { |d| d.strip } if version
dep = Bundler::Dependency.new(name, version)
if pinned && dep.name != 'bundler'
spec = @specs.find { |s| s.name == dep.name }
dep.source = spec.source if spec
# Path sources need to know what the default name / version
# to use in the case that there are no gemspecs present. A fake
# gemspec is created based on the version set on the dependency
# TODO: Use the version from the spec instead of from the dependency
if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
dep.source.name = name
dep.source.version = $1
end
end
@dependencies << dep
end
end
def parse_spec(line)
if line =~ %r{^ {4}#{NAME_VERSION}$}
name, version = $1, Gem::Version.new($2)
platform = $3 ? Gem::Platform.new($3) : Gem::Platform::RUBY
@current_spec = LazySpecification.new(name, version, platform)
@current_spec.source = @current_source
# Avoid introducing multiple copies of the same spec (caused by
# duplicate GIT sections)
@specs << @current_spec unless @specs.include?(@current_spec)
elsif line =~ %r{^ {6}#{NAME_VERSION}$}
name, version = $1, $2
version = version.split(',').map { |d| d.strip } if version
dep = Gem::Dependency.new(name, version)
@current_spec.dependencies << dep
end
end
def parse_platform(line)
if line =~ /^ (.*)$/
@platforms << Gem::Platform.new($1)
end
end
end
end