rubygems-update 2.4.8 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CVE-2015-3900.txt +40 -0
- data/History.txt +173 -2
- data/Manifest.txt +14 -1
- data/Rakefile +36 -1
- data/lib/rubygems.rb +32 -14
- data/lib/rubygems/basic_specification.rb +31 -9
- data/lib/rubygems/commands/dependency_command.rb +25 -15
- data/lib/rubygems/commands/environment_command.rb +2 -0
- data/lib/rubygems/commands/help_command.rb +0 -10
- data/lib/rubygems/commands/install_command.rb +1 -1
- data/lib/rubygems/commands/list_command.rb +1 -1
- data/lib/rubygems/commands/pristine_command.rb +11 -1
- data/lib/rubygems/commands/query_command.rb +1 -1
- data/lib/rubygems/commands/sources_command.rb +1 -1
- data/lib/rubygems/commands/update_command.rb +2 -2
- data/lib/rubygems/config_file.rb +4 -4
- data/lib/rubygems/core_ext/kernel_require.rb +2 -2
- data/lib/rubygems/dependency.rb +9 -6
- data/lib/rubygems/dependency_list.rb +3 -0
- data/lib/rubygems/ext/builder.rb +2 -0
- data/lib/rubygems/ext/ext_conf_builder.rb +6 -1
- data/lib/rubygems/indexer.rb +26 -91
- data/lib/rubygems/installer.rb +58 -26
- data/lib/rubygems/installer_test_case.rb +2 -2
- data/lib/rubygems/package.rb +18 -6
- data/lib/rubygems/package/old.rb +2 -2
- data/lib/rubygems/package/tar_reader/entry.rb +7 -1
- data/lib/rubygems/package/tar_test_case.rb +12 -3
- data/lib/rubygems/package/tar_writer.rb +19 -1
- data/lib/rubygems/platform.rb +3 -2
- data/lib/rubygems/rdoc.rb +1 -2
- data/lib/rubygems/remote_fetcher.rb +25 -6
- data/lib/rubygems/request/connection_pools.rb +8 -4
- data/lib/rubygems/request_set.rb +3 -4
- data/lib/rubygems/request_set/gem_dependency_api.rb +2 -2
- data/lib/rubygems/request_set/lockfile.rb +1 -1
- data/lib/rubygems/request_set/lockfile/parser.rb +54 -43
- data/lib/rubygems/request_set/lockfile/tokenizer.rb +16 -13
- data/lib/rubygems/resolver.rb +47 -242
- data/lib/rubygems/resolver/activation_request.rb +2 -1
- data/lib/rubygems/resolver/conflict.rb +0 -1
- data/lib/rubygems/resolver/dependency_request.rb +4 -1
- data/lib/rubygems/resolver/git_specification.rb +1 -2
- data/lib/rubygems/resolver/molinillo.rb +1 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo.rb +5 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +266 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +69 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +3 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb +99 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb +63 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +430 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb +43 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb +51 -0
- data/lib/rubygems/resolver/specification.rb +1 -1
- data/lib/rubygems/specification.rb +256 -86
- data/lib/rubygems/stub_specification.rb +37 -29
- data/lib/rubygems/test_case.rb +65 -28
- data/lib/rubygems/test_utilities.rb +18 -18
- data/lib/rubygems/text.rb +0 -2
- data/lib/rubygems/uninstaller.rb +1 -1
- data/lib/rubygems/util.rb +4 -4
- data/lib/rubygems/util/licenses.rb +309 -0
- data/lib/rubygems/util/list.rb +9 -21
- data/lib/rubygems/version.rb +24 -14
- data/test/rubygems/simple_gem.rb +1 -1
- data/test/rubygems/test_config.rb +10 -1
- data/test/rubygems/test_gem.rb +58 -11
- data/test/rubygems/test_gem_available_set.rb +2 -1
- data/test/rubygems/test_gem_commands_cleanup_command.rb +6 -5
- data/test/rubygems/test_gem_commands_dependency_command.rb +9 -1
- data/test/rubygems/test_gem_commands_install_command.rb +17 -28
- data/test/rubygems/test_gem_commands_mirror.rb +0 -13
- data/test/rubygems/test_gem_commands_outdated_command.rb +2 -3
- data/test/rubygems/test_gem_commands_pristine_command.rb +33 -5
- data/test/rubygems/test_gem_commands_query_command.rb +123 -158
- data/test/rubygems/test_gem_commands_server_command.rb +2 -2
- data/test/rubygems/test_gem_commands_specification_command.rb +4 -4
- data/test/rubygems/test_gem_commands_stale_command.rb +2 -0
- data/test/rubygems/test_gem_commands_uninstall_command.rb +5 -4
- data/test/rubygems/test_gem_commands_unpack_command.rb +4 -6
- data/test/rubygems/test_gem_commands_update_command.rb +22 -52
- data/test/rubygems/test_gem_commands_which_command.rb +1 -0
- data/test/rubygems/test_gem_config_file.rb +1 -1
- data/test/rubygems/test_gem_dependency.rb +7 -3
- data/test/rubygems/test_gem_dependency_installer.rb +5 -5
- data/test/rubygems/test_gem_doctor.rb +1 -1
- data/test/rubygems/test_gem_ext_builder.rb +2 -0
- data/test/rubygems/test_gem_ext_configure_builder.rb +8 -4
- data/test/rubygems/test_gem_ext_ext_conf_builder.rb +25 -21
- data/test/rubygems/test_gem_indexer.rb +4 -4
- data/test/rubygems/test_gem_install_update_options.rb +2 -2
- data/test/rubygems/test_gem_installer.rb +32 -26
- data/test/rubygems/test_gem_package.rb +46 -1
- data/test/rubygems/test_gem_package_tar_reader_entry.rb +8 -1
- data/test/rubygems/test_gem_package_tar_writer.rb +10 -1
- data/test/rubygems/test_gem_package_task.rb +5 -2
- data/test/rubygems/test_gem_platform.rb +11 -0
- data/test/rubygems/test_gem_remote_fetcher.rb +64 -3
- data/test/rubygems/test_gem_request.rb +1 -1
- data/test/rubygems/test_gem_request_connection_pools.rb +10 -1
- data/test/rubygems/test_gem_request_set.rb +5 -8
- data/test/rubygems/test_gem_request_set_lockfile.rb +2 -4
- data/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb +1 -1
- data/test/rubygems/test_gem_resolver.rb +12 -31
- data/test/rubygems/test_gem_resolver_git_specification.rb +1 -0
- data/test/rubygems/test_gem_resolver_installer_set.rb +7 -11
- data/test/rubygems/test_gem_resolver_lock_specification.rb +3 -2
- data/test/rubygems/test_gem_security_trust_dir.rb +2 -0
- data/test/rubygems/test_gem_server.rb +4 -0
- data/test/rubygems/test_gem_specification.rb +344 -61
- data/test/rubygems/test_gem_stream_ui.rb +6 -6
- data/test/rubygems/test_gem_stub_specification.rb +21 -6
- data/test/rubygems/test_gem_text.rb +2 -0
- data/test/rubygems/test_gem_uninstaller.rb +2 -1
- data/test/rubygems/test_gem_util.rb +8 -0
- data/test/rubygems/test_require.rb +156 -125
- data/util/generate_spdx_license_list.rb +21 -0
- data/util/update_bundled_ca_certificates.rb +2 -1
- metadata +42 -6
- metadata.gz.sig +0 -0
- data/lib/rubygems/util/stringio.rb +0 -34
@@ -61,18 +61,22 @@ class Gem::Request::ConnectionPools # :nodoc:
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def net_http_args uri, proxy_uri
|
64
|
-
|
64
|
+
# URI::Generic#hostname was added in ruby 1.9.3, use it if exists, otherwise
|
65
|
+
# don't support IPv6 literals and use host.
|
66
|
+
hostname = uri.respond_to?(:hostname) ? uri.hostname : uri.host
|
67
|
+
net_http_args = [hostname, uri.port]
|
65
68
|
|
66
69
|
no_proxy = get_no_proxy_from_env
|
67
70
|
|
68
|
-
if proxy_uri and not no_proxy?(
|
71
|
+
if proxy_uri and not no_proxy?(hostname, no_proxy) then
|
72
|
+
proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host
|
69
73
|
net_http_args + [
|
70
|
-
|
74
|
+
proxy_hostname,
|
71
75
|
proxy_uri.port,
|
72
76
|
Gem::UriFormatter.new(proxy_uri.user).unescape,
|
73
77
|
Gem::UriFormatter.new(proxy_uri.password).unescape,
|
74
78
|
]
|
75
|
-
elsif no_proxy?
|
79
|
+
elsif no_proxy? hostname, no_proxy then
|
76
80
|
net_http_args + [nil, nil]
|
77
81
|
else
|
78
82
|
net_http_args
|
data/lib/rubygems/request_set.rb
CHANGED
@@ -159,16 +159,13 @@ class Gem::RequestSet
|
|
159
159
|
|
160
160
|
path = req.download cache_dir
|
161
161
|
|
162
|
-
inst = Gem::Installer.
|
162
|
+
inst = Gem::Installer.at path, options
|
163
163
|
|
164
164
|
yield req, inst if block_given?
|
165
165
|
|
166
166
|
requests << inst.install
|
167
167
|
end
|
168
168
|
|
169
|
-
requests
|
170
|
-
ensure
|
171
|
-
raise if $!
|
172
169
|
return requests if options[:gemdeps]
|
173
170
|
|
174
171
|
specs = requests.map do |request|
|
@@ -187,6 +184,8 @@ class Gem::RequestSet
|
|
187
184
|
Gem.done_installing_hooks.each do |hook|
|
188
185
|
hook.call inst, specs
|
189
186
|
end unless Gem.done_installing_hooks.empty?
|
187
|
+
|
188
|
+
requests
|
190
189
|
end
|
191
190
|
|
192
191
|
##
|
@@ -174,7 +174,7 @@ class Gem::RequestSet::GemDependencyAPI
|
|
174
174
|
##
|
175
175
|
# A Hash containing gem names and files to require from those gems.
|
176
176
|
|
177
|
-
attr_reader :requires
|
177
|
+
attr_reader :requires
|
178
178
|
|
179
179
|
##
|
180
180
|
# A set of gems that are loaded via the +:path+ option to #gem
|
@@ -396,7 +396,7 @@ Gem dependencies file #{@path} requires #{name} more than once.
|
|
396
396
|
##
|
397
397
|
# Handles the git: option from +options+ for gem +name+.
|
398
398
|
#
|
399
|
-
# Returns +true+ if the
|
399
|
+
# Returns +true+ if the gist or git option was handled.
|
400
400
|
|
401
401
|
def gem_git name, options # :nodoc:
|
402
402
|
if gist = options.delete(:gist) then
|
@@ -11,13 +11,13 @@ class Gem::RequestSet::Lockfile::Parser
|
|
11
11
|
|
12
12
|
def parse
|
13
13
|
until @tokens.empty? do
|
14
|
-
|
14
|
+
token = get
|
15
15
|
|
16
|
-
case type
|
16
|
+
case token.type
|
17
17
|
when :section then
|
18
18
|
@tokens.skip :newline
|
19
19
|
|
20
|
-
case
|
20
|
+
case token.value
|
21
21
|
when 'DEPENDENCIES' then
|
22
22
|
parse_DEPENDENCIES
|
23
23
|
when 'GIT' then
|
@@ -29,10 +29,10 @@ class Gem::RequestSet::Lockfile::Parser
|
|
29
29
|
when 'PLATFORMS' then
|
30
30
|
parse_PLATFORMS
|
31
31
|
else
|
32
|
-
|
32
|
+
token = get until @tokens.empty? or peek.first == :section
|
33
33
|
end
|
34
34
|
else
|
35
|
-
raise "BUG: unhandled token #{type} (#{
|
35
|
+
raise "BUG: unhandled token #{token.type} (#{token.value.inspect}) at line #{token.line} column #{token.column}"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -41,35 +41,33 @@ class Gem::RequestSet::Lockfile::Parser
|
|
41
41
|
# Gets the next token for a Lockfile
|
42
42
|
|
43
43
|
def get expected_types = nil, expected_value = nil # :nodoc:
|
44
|
-
|
44
|
+
token = @tokens.shift
|
45
45
|
|
46
|
-
|
46
|
+
if expected_types and not Array(expected_types).include? token.type then
|
47
|
+
unget token
|
47
48
|
|
48
|
-
|
49
|
-
unget current_token
|
50
|
-
|
51
|
-
message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
|
49
|
+
message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
|
52
50
|
"expected #{expected_types.inspect}"
|
53
51
|
|
54
|
-
raise Gem::RequestSet::Lockfile::ParseError.new message, column, line, @filename
|
52
|
+
raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
|
55
53
|
end
|
56
54
|
|
57
|
-
if expected_value and expected_value != value then
|
58
|
-
unget
|
55
|
+
if expected_value and expected_value != token.value then
|
56
|
+
unget token
|
59
57
|
|
60
|
-
message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
|
58
|
+
message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " +
|
61
59
|
"expected [#{expected_types.inspect}, " +
|
62
60
|
"#{expected_value.inspect}]"
|
63
61
|
|
64
|
-
raise Gem::RequestSet::Lockfile::ParseError.new message, column, line, @filename
|
62
|
+
raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
|
65
63
|
end
|
66
64
|
|
67
|
-
|
65
|
+
token
|
68
66
|
end
|
69
67
|
|
70
68
|
def parse_DEPENDENCIES # :nodoc:
|
71
|
-
while not @tokens.empty? and :text == peek.
|
72
|
-
|
69
|
+
while not @tokens.empty? and :text == peek.type do
|
70
|
+
token = get :text
|
73
71
|
|
74
72
|
requirements = []
|
75
73
|
|
@@ -77,17 +75,17 @@ class Gem::RequestSet::Lockfile::Parser
|
|
77
75
|
when :bang then
|
78
76
|
get :bang
|
79
77
|
|
80
|
-
requirements << pinned_requirement(
|
78
|
+
requirements << pinned_requirement(token.value)
|
81
79
|
when :l_paren then
|
82
80
|
get :l_paren
|
83
81
|
|
84
82
|
loop do
|
85
|
-
|
86
|
-
|
83
|
+
op = get(:requirement).value
|
84
|
+
version = get(:text).value
|
87
85
|
|
88
86
|
requirements << "#{op} #{version}"
|
89
87
|
|
90
|
-
break unless peek
|
88
|
+
break unless peek.type == :comma
|
91
89
|
|
92
90
|
get :comma
|
93
91
|
end
|
@@ -96,13 +94,13 @@ class Gem::RequestSet::Lockfile::Parser
|
|
96
94
|
|
97
95
|
if peek[0] == :bang then
|
98
96
|
requirements.clear
|
99
|
-
requirements << pinned_requirement(
|
97
|
+
requirements << pinned_requirement(token.value)
|
100
98
|
|
101
99
|
get :bang
|
102
100
|
end
|
103
101
|
end
|
104
102
|
|
105
|
-
@set.gem
|
103
|
+
@set.gem token.value, *requirements
|
106
104
|
|
107
105
|
skip :newline
|
108
106
|
end
|
@@ -113,7 +111,7 @@ class Gem::RequestSet::Lockfile::Parser
|
|
113
111
|
|
114
112
|
while [:entry, 'remote'] == peek.first(2) do
|
115
113
|
get :entry, 'remote'
|
116
|
-
|
114
|
+
data = get(:text).value
|
117
115
|
skip :newline
|
118
116
|
|
119
117
|
sources << Gem::Source.new(data)
|
@@ -128,8 +126,10 @@ class Gem::RequestSet::Lockfile::Parser
|
|
128
126
|
set = Gem::Resolver::LockSet.new sources
|
129
127
|
last_specs = nil
|
130
128
|
|
131
|
-
while not @tokens.empty? and :text == peek.
|
132
|
-
|
129
|
+
while not @tokens.empty? and :text == peek.type do
|
130
|
+
token = get :text
|
131
|
+
name = token.value
|
132
|
+
column = token.column
|
133
133
|
|
134
134
|
case peek[0]
|
135
135
|
when :newline then
|
@@ -139,7 +139,9 @@ class Gem::RequestSet::Lockfile::Parser
|
|
139
139
|
when :l_paren then
|
140
140
|
get :l_paren
|
141
141
|
|
142
|
-
|
142
|
+
token = get [:text, :requirement]
|
143
|
+
type = token.type
|
144
|
+
data = token.value
|
143
145
|
|
144
146
|
if type == :text and column == 4 then
|
145
147
|
version, platform = data.split '-', 2
|
@@ -169,16 +171,17 @@ class Gem::RequestSet::Lockfile::Parser
|
|
169
171
|
|
170
172
|
def parse_GIT # :nodoc:
|
171
173
|
get :entry, 'remote'
|
172
|
-
|
174
|
+
repository = get(:text).value
|
173
175
|
|
174
176
|
skip :newline
|
175
177
|
|
176
178
|
get :entry, 'revision'
|
177
|
-
|
179
|
+
revision = get(:text).value
|
178
180
|
|
179
181
|
skip :newline
|
180
182
|
|
181
|
-
type
|
183
|
+
type = peek.type
|
184
|
+
value = peek.value
|
182
185
|
if type == :entry and %w[branch ref tag].include? value then
|
183
186
|
get
|
184
187
|
get :text
|
@@ -195,8 +198,10 @@ class Gem::RequestSet::Lockfile::Parser
|
|
195
198
|
|
196
199
|
last_spec = nil
|
197
200
|
|
198
|
-
while not @tokens.empty? and :text == peek.
|
199
|
-
|
201
|
+
while not @tokens.empty? and :text == peek.type do
|
202
|
+
token = get :text
|
203
|
+
name = token.value
|
204
|
+
column = token.column
|
200
205
|
|
201
206
|
case peek[0]
|
202
207
|
when :newline then
|
@@ -204,7 +209,9 @@ class Gem::RequestSet::Lockfile::Parser
|
|
204
209
|
when :l_paren then
|
205
210
|
get :l_paren
|
206
211
|
|
207
|
-
|
212
|
+
token = get [:text, :requirement]
|
213
|
+
type = token.type
|
214
|
+
data = token.value
|
208
215
|
|
209
216
|
if type == :text and column == 4 then
|
210
217
|
last_spec = set.add_git_spec name, data, repository, revision, true
|
@@ -227,7 +234,7 @@ class Gem::RequestSet::Lockfile::Parser
|
|
227
234
|
|
228
235
|
def parse_PATH # :nodoc:
|
229
236
|
get :entry, 'remote'
|
230
|
-
|
237
|
+
directory = get(:text).value
|
231
238
|
|
232
239
|
skip :newline
|
233
240
|
|
@@ -239,7 +246,9 @@ class Gem::RequestSet::Lockfile::Parser
|
|
239
246
|
last_spec = nil
|
240
247
|
|
241
248
|
while not @tokens.empty? and :text == peek.first do
|
242
|
-
|
249
|
+
token = get :text
|
250
|
+
name = token.value
|
251
|
+
column = token.column
|
243
252
|
|
244
253
|
case peek[0]
|
245
254
|
when :newline then
|
@@ -247,7 +256,9 @@ class Gem::RequestSet::Lockfile::Parser
|
|
247
256
|
when :l_paren then
|
248
257
|
get :l_paren
|
249
258
|
|
250
|
-
|
259
|
+
token = get [:text, :requirement]
|
260
|
+
type = token.type
|
261
|
+
data = token.value
|
251
262
|
|
252
263
|
if type == :text and column == 4 then
|
253
264
|
last_spec = set.add_vendor_gem name, directory
|
@@ -270,7 +281,7 @@ class Gem::RequestSet::Lockfile::Parser
|
|
270
281
|
|
271
282
|
def parse_PLATFORMS # :nodoc:
|
272
283
|
while not @tokens.empty? and :text == peek.first do
|
273
|
-
|
284
|
+
name = get(:text).value
|
274
285
|
|
275
286
|
@platforms << name
|
276
287
|
|
@@ -285,14 +296,14 @@ class Gem::RequestSet::Lockfile::Parser
|
|
285
296
|
def parse_dependency name, op # :nodoc:
|
286
297
|
return Gem::Dependency.new name, op unless peek[0] == :text
|
287
298
|
|
288
|
-
|
299
|
+
version = get(:text).value
|
289
300
|
|
290
301
|
requirements = ["#{op} #{version}"]
|
291
302
|
|
292
|
-
while peek
|
303
|
+
while peek.type == :comma do
|
293
304
|
get :comma
|
294
|
-
|
295
|
-
|
305
|
+
op = get(:requirement).value
|
306
|
+
version = get(:text).value
|
296
307
|
|
297
308
|
requirements << "#{op} #{version}"
|
298
309
|
end
|
@@ -2,6 +2,9 @@ require 'strscan'
|
|
2
2
|
require 'rubygems/request_set/lockfile/parser'
|
3
3
|
|
4
4
|
class Gem::RequestSet::Lockfile::Tokenizer
|
5
|
+
Token = Struct.new :type, :value, :column, :line
|
6
|
+
EOF = Token.new :EOF
|
7
|
+
|
5
8
|
def self.from_file file
|
6
9
|
new File.read(file), file
|
7
10
|
end
|
@@ -19,11 +22,11 @@ class Gem::RequestSet::Lockfile::Tokenizer
|
|
19
22
|
end
|
20
23
|
|
21
24
|
def to_a
|
22
|
-
@tokens
|
25
|
+
@tokens.map { |token| [token.type, token.value, token.column, token.line] }
|
23
26
|
end
|
24
27
|
|
25
28
|
def skip type
|
26
|
-
@tokens.shift while not @tokens.empty? and peek.
|
29
|
+
@tokens.shift while not @tokens.empty? and peek.type == type
|
27
30
|
end
|
28
31
|
|
29
32
|
##
|
@@ -48,7 +51,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
|
|
48
51
|
alias :shift :next_token
|
49
52
|
|
50
53
|
def peek
|
51
|
-
@tokens.first ||
|
54
|
+
@tokens.first || EOF
|
52
55
|
end
|
53
56
|
|
54
57
|
private
|
@@ -71,7 +74,7 @@ class Gem::RequestSet::Lockfile::Tokenizer
|
|
71
74
|
@tokens <<
|
72
75
|
case
|
73
76
|
when s.scan(/\r?\n/) then
|
74
|
-
token =
|
77
|
+
token = Token.new(:newline, nil, *token_pos(pos))
|
75
78
|
@line_pos = s.pos
|
76
79
|
@line += 1
|
77
80
|
token
|
@@ -79,25 +82,25 @@ class Gem::RequestSet::Lockfile::Tokenizer
|
|
79
82
|
if leading_whitespace then
|
80
83
|
text = s.matched
|
81
84
|
text += s.scan(/[^\s)]*/).to_s # in case of no match
|
82
|
-
|
85
|
+
Token.new(:text, text, *token_pos(pos))
|
83
86
|
else
|
84
|
-
|
87
|
+
Token.new(:section, s.matched, *token_pos(pos))
|
85
88
|
end
|
86
89
|
when s.scan(/([a-z]+):\s/) then
|
87
90
|
s.pos -= 1 # rewind for possible newline
|
88
|
-
|
91
|
+
Token.new(:entry, s[1], *token_pos(pos))
|
89
92
|
when s.scan(/\(/) then
|
90
|
-
|
93
|
+
Token.new(:l_paren, nil, *token_pos(pos))
|
91
94
|
when s.scan(/\)/) then
|
92
|
-
|
95
|
+
Token.new(:r_paren, nil, *token_pos(pos))
|
93
96
|
when s.scan(/<=|>=|=|~>|<|>|!=/) then
|
94
|
-
|
97
|
+
Token.new(:requirement, s.matched, *token_pos(pos))
|
95
98
|
when s.scan(/,/) then
|
96
|
-
|
99
|
+
Token.new(:comma, nil, *token_pos(pos))
|
97
100
|
when s.scan(/!/) then
|
98
|
-
|
101
|
+
Token.new(:bang, nil, *token_pos(pos))
|
99
102
|
when s.scan(/[^\s),!]*/) then
|
100
|
-
|
103
|
+
Token.new(:text, s.matched, *token_pos(pos))
|
101
104
|
else
|
102
105
|
raise "BUG: can't create token for: #{s.string[s.pos..-1].inspect}"
|
103
106
|
end
|
data/lib/rubygems/resolver.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rubygems/dependency'
|
2
2
|
require 'rubygems/exceptions'
|
3
|
+
require 'rubygems/util'
|
3
4
|
require 'rubygems/util/list'
|
4
5
|
|
5
6
|
require 'uri'
|
@@ -12,6 +13,7 @@ require 'net/http'
|
|
12
13
|
# all the requirements.
|
13
14
|
|
14
15
|
class Gem::Resolver
|
16
|
+
require 'rubygems/resolver/molinillo'
|
15
17
|
|
16
18
|
##
|
17
19
|
# If the DEBUG_RESOLVER environment variable is set then debugging mode is
|
@@ -20,13 +22,6 @@ class Gem::Resolver
|
|
20
22
|
|
21
23
|
DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?
|
22
24
|
|
23
|
-
require 'pp' if DEBUG_RESOLVER
|
24
|
-
|
25
|
-
##
|
26
|
-
# Contains all the conflicts encountered while doing resolution
|
27
|
-
|
28
|
-
attr_reader :conflicts
|
29
|
-
|
30
25
|
##
|
31
26
|
# Set to true if all development dependencies should be considered.
|
32
27
|
|
@@ -110,7 +105,6 @@ class Gem::Resolver
|
|
110
105
|
@set = set || Gem::Resolver::IndexSet.new
|
111
106
|
@needed = needed
|
112
107
|
|
113
|
-
@conflicts = []
|
114
108
|
@development = false
|
115
109
|
@development_shallow = false
|
116
110
|
@ignore_dependencies = false
|
@@ -153,7 +147,7 @@ class Gem::Resolver
|
|
153
147
|
return spec, activation_request
|
154
148
|
end
|
155
149
|
|
156
|
-
def requests s, act, reqs=
|
150
|
+
def requests s, act, reqs=[] # :nodoc:
|
157
151
|
return reqs if @ignore_dependencies
|
158
152
|
|
159
153
|
s.fetch_development_dependencies if @development
|
@@ -165,7 +159,7 @@ class Gem::Resolver
|
|
165
159
|
next if d.type == :development and @development_shallow and
|
166
160
|
act.parent
|
167
161
|
|
168
|
-
reqs
|
162
|
+
reqs << Gem::Resolver::DependencyRequest.new(d, act)
|
169
163
|
@stats.requirement!
|
170
164
|
end
|
171
165
|
|
@@ -176,29 +170,29 @@ class Gem::Resolver
|
|
176
170
|
reqs
|
177
171
|
end
|
178
172
|
|
179
|
-
|
180
|
-
# Proceed with resolution! Returns an array of ActivationRequest objects.
|
181
|
-
|
182
|
-
def resolve
|
183
|
-
@conflicts = []
|
184
|
-
|
185
|
-
needed = Gem::Resolver::RequirementList.new
|
173
|
+
include Molinillo::UI
|
186
174
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
needed.add request
|
191
|
-
@stats.requirement!
|
192
|
-
end
|
175
|
+
def output
|
176
|
+
@output ||= debug? ? $stdout : File.open(Gem::Util::NULL_DEVICE, 'w')
|
177
|
+
end
|
193
178
|
|
194
|
-
|
179
|
+
def debug?
|
180
|
+
DEBUG_RESOLVER
|
181
|
+
end
|
195
182
|
|
196
|
-
|
183
|
+
include Molinillo::SpecificationProvider
|
197
184
|
|
198
|
-
|
199
|
-
|
185
|
+
##
|
186
|
+
# Proceed with resolution! Returns an array of ActivationRequest objects.
|
200
187
|
|
201
|
-
|
188
|
+
def resolve
|
189
|
+
locking_dg = Molinillo::DependencyGraph.new
|
190
|
+
Molinillo::Resolver.new(self, self).resolve(@needed.map { |d| DependencyRequest.new d, nil }, locking_dg).tsort.map(&:payload)
|
191
|
+
rescue Molinillo::VersionConflict => e
|
192
|
+
conflict = e.conflicts.values.first
|
193
|
+
raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement)
|
194
|
+
ensure
|
195
|
+
@output.close if @output and !debug?
|
202
196
|
end
|
203
197
|
|
204
198
|
##
|
@@ -221,232 +215,44 @@ class Gem::Resolver
|
|
221
215
|
return matching_platform, all
|
222
216
|
end
|
223
217
|
|
224
|
-
def handle_conflict(dep, existing) # :nodoc:
|
225
|
-
# There is a conflict! We return the conflict object which will be seen by
|
226
|
-
# the caller and be handled at the right level.
|
227
|
-
|
228
|
-
# If the existing activation indicates that there are other possibles for
|
229
|
-
# it, then issue the conflict on the dependency for the activation itself.
|
230
|
-
# Otherwise, if there was a requester, issue it on the requester's
|
231
|
-
# request itself.
|
232
|
-
# Finally, if the existing request has no requester (toplevel) unwind to
|
233
|
-
# it anyway.
|
234
|
-
|
235
|
-
if existing.others_possible?
|
236
|
-
conflict =
|
237
|
-
Gem::Resolver::Conflict.new dep, existing
|
238
|
-
elsif dep.requester
|
239
|
-
depreq = dep.requester.request
|
240
|
-
conflict =
|
241
|
-
Gem::Resolver::Conflict.new depreq, existing, dep
|
242
|
-
elsif existing.request.requester.nil?
|
243
|
-
conflict =
|
244
|
-
Gem::Resolver::Conflict.new dep, existing
|
245
|
-
else
|
246
|
-
raise Gem::DependencyError, "Unable to figure out how to unwind conflict"
|
247
|
-
end
|
248
|
-
|
249
|
-
@conflicts << conflict unless @conflicts.include? conflict
|
250
|
-
|
251
|
-
return conflict
|
252
|
-
end
|
253
|
-
|
254
|
-
# Contains the state for attempting activation of a set of possible specs.
|
255
|
-
# +needed+ is a Gem::List of DependencyRequest objects that, well, need
|
256
|
-
# to be satisfied.
|
257
|
-
# +specs+ is the List of ActivationRequest that are being tested.
|
258
|
-
# +dep+ is the DependencyRequest that was used to generate this state.
|
259
|
-
# +spec+ is the Specification for this state.
|
260
|
-
# +possible+ is List of DependencyRequest objects that can be tried to
|
261
|
-
# find a complete set.
|
262
|
-
# +conflicts+ is a [DependencyRequest, Conflict] hit tried to
|
263
|
-
# activate the state.
|
264
|
-
#
|
265
|
-
State = Struct.new(:needed, :specs, :dep, :spec, :possibles, :conflicts) do
|
266
|
-
def summary # :nodoc:
|
267
|
-
nd = needed.map { |s| s.to_s }.sort if nd
|
268
|
-
|
269
|
-
if specs then
|
270
|
-
ss = specs.map { |s| s.full_name }.sort
|
271
|
-
ss.unshift ss.length
|
272
|
-
end
|
273
|
-
|
274
|
-
d = dep.to_s
|
275
|
-
d << " from #{dep.requester.full_name}" if dep.requester
|
276
|
-
|
277
|
-
ps = possibles.map { |p| p.full_name }.sort
|
278
|
-
ps.unshift ps.length
|
279
|
-
|
280
|
-
cs = conflicts.map do |(s, c)|
|
281
|
-
[s.full_name, c.conflicting_dependencies.map { |cd| cd.to_s }]
|
282
|
-
end
|
283
|
-
|
284
|
-
{ :needed => nd, :specs => ss, :dep => d, :spec => spec.full_name,
|
285
|
-
:possibles => ps, :conflicts => cs }
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
218
|
##
|
290
|
-
#
|
291
|
-
# +specs+ being a list to ActivationRequest, calculate a new list of
|
292
|
-
# ActivationRequest objects.
|
293
|
-
|
294
|
-
def resolve_for needed, specs # :nodoc:
|
295
|
-
# The State objects that are used to attempt the activation tree.
|
296
|
-
states = []
|
297
|
-
|
298
|
-
while !needed.empty?
|
299
|
-
@stats.iteration!
|
300
|
-
|
301
|
-
dep = needed.remove
|
302
|
-
explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
|
303
|
-
explain_list(:next5) { needed.next5 }
|
304
|
-
explain_list(:specs) { Array(specs).map { |x| x.full_name }.sort }
|
305
|
-
|
306
|
-
# If there is already a spec activated for the requested name...
|
307
|
-
if specs && existing = specs.find { |s| dep.name == s.name }
|
308
|
-
# then we're done since this new dep matches the existing spec.
|
309
|
-
next if dep.matches_spec? existing
|
310
|
-
|
311
|
-
conflict = handle_conflict dep, existing
|
312
|
-
|
313
|
-
return conflict unless dep.requester
|
314
|
-
|
315
|
-
explain :conflict, dep, :existing, existing.full_name
|
316
|
-
|
317
|
-
depreq = dep.requester.request
|
318
|
-
|
319
|
-
state = nil
|
320
|
-
until states.empty?
|
321
|
-
x = states.pop
|
322
|
-
|
323
|
-
i = existing.request.requester
|
324
|
-
explain :consider, x.spec.full_name, [depreq.name, dep.name, i ? i.name : :top]
|
325
|
-
|
326
|
-
if x.spec.name == depreq.name or
|
327
|
-
x.spec.name == dep.name or
|
328
|
-
(i && (i.name == x.spec.name))
|
329
|
-
explain :found, x.spec.full_name
|
330
|
-
state = x
|
331
|
-
break
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
return conflict unless state
|
336
|
-
|
337
|
-
@stats.backtracking!
|
338
|
-
|
339
|
-
needed, specs = resolve_for_conflict needed, specs, state
|
340
|
-
|
341
|
-
states << state unless state.possibles.empty?
|
342
|
-
|
343
|
-
next
|
344
|
-
end
|
345
|
-
|
346
|
-
matching, all = find_possible dep
|
219
|
+
# Returns the gems in +specs+ that match the local platform.
|
347
220
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
when 1
|
352
|
-
needed, specs =
|
353
|
-
resolve_for_single needed, specs, dep, matching
|
354
|
-
else
|
355
|
-
needed, specs =
|
356
|
-
resolve_for_multiple needed, specs, states, dep, matching
|
357
|
-
end
|
221
|
+
def select_local_platforms specs # :nodoc:
|
222
|
+
specs.select do |spec|
|
223
|
+
Gem::Platform.installable? spec
|
358
224
|
end
|
359
|
-
|
360
|
-
specs
|
361
|
-
end
|
362
|
-
|
363
|
-
##
|
364
|
-
# Rewinds +needed+ and +specs+ to a previous state in +state+ for a conflict
|
365
|
-
# between +dep+ and +existing+.
|
366
|
-
|
367
|
-
def resolve_for_conflict needed, specs, state # :nodoc:
|
368
|
-
# We exhausted the possibles so it's definitely not going to work out,
|
369
|
-
# bail out.
|
370
|
-
raise Gem::ImpossibleDependenciesError.new state.dep, state.conflicts if
|
371
|
-
state.possibles.empty?
|
372
|
-
|
373
|
-
# Retry resolution with this spec and add it's dependencies
|
374
|
-
spec, act = activation_request state.dep, state.possibles
|
375
|
-
|
376
|
-
needed = requests spec, act, state.needed.dup
|
377
|
-
specs = Gem::List.prepend state.specs, act
|
378
|
-
|
379
|
-
return needed, specs
|
380
225
|
end
|
381
226
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
possible = possible.sort_by do |s|
|
390
|
-
[s.source, s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
|
227
|
+
def search_for(dependency)
|
228
|
+
possibles, all = find_possible(dependency)
|
229
|
+
if !@soft_missing && possibles.empty?
|
230
|
+
@missing << dependency
|
231
|
+
exc = Gem::UnsatisfiableDependencyError.new dependency, all
|
232
|
+
exc.errors = @set.errors
|
233
|
+
raise exc
|
391
234
|
end
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
# We may need to try all of +possible+, so we setup state to unwind back
|
396
|
-
# to current +needed+ and +specs+ so we can try another. This is code is
|
397
|
-
# what makes conflict resolution possible.
|
398
|
-
states << State.new(needed.dup, specs, dep, spec, possible, [])
|
399
|
-
|
400
|
-
@stats.record_depth states
|
401
|
-
|
402
|
-
explain :states, states.map { |s| s.dep }
|
403
|
-
|
404
|
-
needed = requests spec, act, needed
|
405
|
-
specs = Gem::List.prepend specs, act
|
406
|
-
|
407
|
-
return needed, specs
|
235
|
+
possibles.sort_by { |s| [s.source, s.version, s.platform == Gem::Platform.local.to_s ? 1 : 0] }.
|
236
|
+
map { |s| ActivationRequest.new s, dependency, [] }
|
408
237
|
end
|
409
238
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
def resolve_for_single needed, specs, dep, possible # :nodoc:
|
415
|
-
spec, act = activation_request dep, possible
|
416
|
-
|
417
|
-
specs = Gem::List.prepend specs, act
|
418
|
-
|
419
|
-
# Put the deps for at the beginning of needed
|
420
|
-
# rather than the end to match the depth first
|
421
|
-
# searching done by the multiple case code below.
|
422
|
-
#
|
423
|
-
# This keeps the error messages consistent.
|
424
|
-
needed = requests spec, act, needed
|
425
|
-
|
426
|
-
return needed, specs
|
239
|
+
def dependencies_for(specification)
|
240
|
+
return [] if @ignore_dependencies
|
241
|
+
spec = specification.spec
|
242
|
+
requests(spec, specification)
|
427
243
|
end
|
428
244
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
def resolve_for_zero dep, platform_mismatch # :nodoc:
|
433
|
-
@missing << dep
|
434
|
-
|
435
|
-
unless @soft_missing
|
436
|
-
exc = Gem::UnsatisfiableDependencyError.new dep, platform_mismatch
|
437
|
-
exc.errors = @set.errors
|
438
|
-
|
439
|
-
raise exc
|
440
|
-
end
|
245
|
+
def requirement_satisfied_by?(requirement, activated, spec)
|
246
|
+
requirement.matches_spec? spec
|
441
247
|
end
|
442
248
|
|
443
|
-
|
444
|
-
|
249
|
+
def name_for(dependency)
|
250
|
+
dependency.name
|
251
|
+
end
|
445
252
|
|
446
|
-
def
|
447
|
-
|
448
|
-
|
449
|
-
end
|
253
|
+
def allow_missing?(dependency)
|
254
|
+
@missing << dependency
|
255
|
+
@soft_missing
|
450
256
|
end
|
451
257
|
|
452
258
|
end
|
@@ -482,4 +288,3 @@ require 'rubygems/resolver/installed_specification'
|
|
482
288
|
require 'rubygems/resolver/local_specification'
|
483
289
|
require 'rubygems/resolver/lock_specification'
|
484
290
|
require 'rubygems/resolver/vendor_specification'
|
485
|
-
|