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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
# frozen_string_literal: false
##
# 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
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, column, line, path
@line = line
@column = column
@path = path
super "#{message} (at line #{line} column #{column})"
end
end
##
# Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
# location.
def self.build request_set, gem_deps_file, dependencies = nil
request_set.resolve
dependencies ||= requests_to_deps request_set.sorted_requests
new request_set, gem_deps_file, dependencies
end
def self.requests_to_deps requests # :nodoc:
deps = {}
requests.each do |request|
spec = request.spec
name = request.name
requirement = request.request.dependency.requirement
deps[name] = if [Gem::Resolver::VendorSpecification,
Gem::Resolver::GitSpecification].include? spec.class then
Gem::Requirement.source_set
else
requirement
end
end
deps
end
##
# The platforms for this Lockfile
attr_reader :platforms
def initialize request_set, gem_deps_file, dependencies
@set = request_set
@dependencies = dependencies
@gem_deps_file = File.expand_path(gem_deps_file)
@gem_deps_dir = File.dirname(@gem_deps_file)
@gem_deps_file.untaint unless gem_deps_file.tainted?
@platforms = []
end
def add_DEPENDENCIES out # :nodoc:
out << "DEPENDENCIES"
out.concat @dependencies.sort_by { |name,| name }.map { |name, requirement|
" #{name}#{requirement.for_lockfile}"
}
out << nil
end
def add_GEM out, spec_groups # :nodoc:
return if spec_groups.empty?
source_groups = spec_groups.values.flatten.group_by do |request|
request.spec.source.uri
end
source_groups.sort_by { |group,| group.to_s }.map do |group, requests|
out << "GEM"
out << " remote: #{group}"
out << " specs:"
requests.sort_by { |request| request.name }.each do |request|
next if request.spec.name == 'bundler'
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|
next if dependency.type == :development
requirement = dependency.requirement
out << " #{dependency.name}#{requirement.for_lockfile}"
end
end
out << nil
end
end
def add_GIT out, git_requests
return if git_requests.empty?
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
end
def relative_path_from dest, base # :nodoc:
dest = File.expand_path(dest)
base = File.expand_path(base)
if dest.index(base) == 0 then
offset = dest[base.size+1..-1]
return '.' unless offset
offset
else
dest
end
end
def add_PATH out, path_requests # :nodoc:
return if path_requests.empty?
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 = platforms.sort_by { |platform| platform.to_s }
platforms.each do |platform|
out << " #{platform}"
end
out << nil
end
def spec_groups
requests.group_by { |request| request.spec.class }
end
##
# The contents of the lock file.
def to_s
out = []
groups = spec_groups
add_PATH out, groups.delete(Gem::Resolver::VendorSpecification) { [] }
add_GIT out, groups.delete(Gem::Resolver::GitSpecification) { [] }
add_GEM out, groups
add_PLATFORMS out
add_DEPENDENCIES out
out.join "\n"
end
##
# Writes the lock file alongside the gem dependencies file
def write
content = to_s
open "#{@gem_deps_file}.lock", 'w' do |io|
io.write content
end
end
private
def requests
@set.sorted_requests
end
end
require 'rubygems/request_set/lockfile/tokenizer'
|