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.

Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CODE_OF_CONDUCT.md +40 -0
  5. data/CVE-2015-3900.txt +40 -0
  6. data/History.txt +173 -2
  7. data/Manifest.txt +14 -1
  8. data/Rakefile +36 -1
  9. data/lib/rubygems.rb +32 -14
  10. data/lib/rubygems/basic_specification.rb +31 -9
  11. data/lib/rubygems/commands/dependency_command.rb +25 -15
  12. data/lib/rubygems/commands/environment_command.rb +2 -0
  13. data/lib/rubygems/commands/help_command.rb +0 -10
  14. data/lib/rubygems/commands/install_command.rb +1 -1
  15. data/lib/rubygems/commands/list_command.rb +1 -1
  16. data/lib/rubygems/commands/pristine_command.rb +11 -1
  17. data/lib/rubygems/commands/query_command.rb +1 -1
  18. data/lib/rubygems/commands/sources_command.rb +1 -1
  19. data/lib/rubygems/commands/update_command.rb +2 -2
  20. data/lib/rubygems/config_file.rb +4 -4
  21. data/lib/rubygems/core_ext/kernel_require.rb +2 -2
  22. data/lib/rubygems/dependency.rb +9 -6
  23. data/lib/rubygems/dependency_list.rb +3 -0
  24. data/lib/rubygems/ext/builder.rb +2 -0
  25. data/lib/rubygems/ext/ext_conf_builder.rb +6 -1
  26. data/lib/rubygems/indexer.rb +26 -91
  27. data/lib/rubygems/installer.rb +58 -26
  28. data/lib/rubygems/installer_test_case.rb +2 -2
  29. data/lib/rubygems/package.rb +18 -6
  30. data/lib/rubygems/package/old.rb +2 -2
  31. data/lib/rubygems/package/tar_reader/entry.rb +7 -1
  32. data/lib/rubygems/package/tar_test_case.rb +12 -3
  33. data/lib/rubygems/package/tar_writer.rb +19 -1
  34. data/lib/rubygems/platform.rb +3 -2
  35. data/lib/rubygems/rdoc.rb +1 -2
  36. data/lib/rubygems/remote_fetcher.rb +25 -6
  37. data/lib/rubygems/request/connection_pools.rb +8 -4
  38. data/lib/rubygems/request_set.rb +3 -4
  39. data/lib/rubygems/request_set/gem_dependency_api.rb +2 -2
  40. data/lib/rubygems/request_set/lockfile.rb +1 -1
  41. data/lib/rubygems/request_set/lockfile/parser.rb +54 -43
  42. data/lib/rubygems/request_set/lockfile/tokenizer.rb +16 -13
  43. data/lib/rubygems/resolver.rb +47 -242
  44. data/lib/rubygems/resolver/activation_request.rb +2 -1
  45. data/lib/rubygems/resolver/conflict.rb +0 -1
  46. data/lib/rubygems/resolver/dependency_request.rb +4 -1
  47. data/lib/rubygems/resolver/git_specification.rb +1 -2
  48. data/lib/rubygems/resolver/molinillo.rb +1 -0
  49. data/lib/rubygems/resolver/molinillo/lib/molinillo.rb +5 -0
  50. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +266 -0
  51. data/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +69 -0
  52. data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +3 -0
  53. data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb +99 -0
  54. data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb +63 -0
  55. data/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +430 -0
  56. data/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb +43 -0
  57. data/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb +51 -0
  58. data/lib/rubygems/resolver/specification.rb +1 -1
  59. data/lib/rubygems/specification.rb +256 -86
  60. data/lib/rubygems/stub_specification.rb +37 -29
  61. data/lib/rubygems/test_case.rb +65 -28
  62. data/lib/rubygems/test_utilities.rb +18 -18
  63. data/lib/rubygems/text.rb +0 -2
  64. data/lib/rubygems/uninstaller.rb +1 -1
  65. data/lib/rubygems/util.rb +4 -4
  66. data/lib/rubygems/util/licenses.rb +309 -0
  67. data/lib/rubygems/util/list.rb +9 -21
  68. data/lib/rubygems/version.rb +24 -14
  69. data/test/rubygems/simple_gem.rb +1 -1
  70. data/test/rubygems/test_config.rb +10 -1
  71. data/test/rubygems/test_gem.rb +58 -11
  72. data/test/rubygems/test_gem_available_set.rb +2 -1
  73. data/test/rubygems/test_gem_commands_cleanup_command.rb +6 -5
  74. data/test/rubygems/test_gem_commands_dependency_command.rb +9 -1
  75. data/test/rubygems/test_gem_commands_install_command.rb +17 -28
  76. data/test/rubygems/test_gem_commands_mirror.rb +0 -13
  77. data/test/rubygems/test_gem_commands_outdated_command.rb +2 -3
  78. data/test/rubygems/test_gem_commands_pristine_command.rb +33 -5
  79. data/test/rubygems/test_gem_commands_query_command.rb +123 -158
  80. data/test/rubygems/test_gem_commands_server_command.rb +2 -2
  81. data/test/rubygems/test_gem_commands_specification_command.rb +4 -4
  82. data/test/rubygems/test_gem_commands_stale_command.rb +2 -0
  83. data/test/rubygems/test_gem_commands_uninstall_command.rb +5 -4
  84. data/test/rubygems/test_gem_commands_unpack_command.rb +4 -6
  85. data/test/rubygems/test_gem_commands_update_command.rb +22 -52
  86. data/test/rubygems/test_gem_commands_which_command.rb +1 -0
  87. data/test/rubygems/test_gem_config_file.rb +1 -1
  88. data/test/rubygems/test_gem_dependency.rb +7 -3
  89. data/test/rubygems/test_gem_dependency_installer.rb +5 -5
  90. data/test/rubygems/test_gem_doctor.rb +1 -1
  91. data/test/rubygems/test_gem_ext_builder.rb +2 -0
  92. data/test/rubygems/test_gem_ext_configure_builder.rb +8 -4
  93. data/test/rubygems/test_gem_ext_ext_conf_builder.rb +25 -21
  94. data/test/rubygems/test_gem_indexer.rb +4 -4
  95. data/test/rubygems/test_gem_install_update_options.rb +2 -2
  96. data/test/rubygems/test_gem_installer.rb +32 -26
  97. data/test/rubygems/test_gem_package.rb +46 -1
  98. data/test/rubygems/test_gem_package_tar_reader_entry.rb +8 -1
  99. data/test/rubygems/test_gem_package_tar_writer.rb +10 -1
  100. data/test/rubygems/test_gem_package_task.rb +5 -2
  101. data/test/rubygems/test_gem_platform.rb +11 -0
  102. data/test/rubygems/test_gem_remote_fetcher.rb +64 -3
  103. data/test/rubygems/test_gem_request.rb +1 -1
  104. data/test/rubygems/test_gem_request_connection_pools.rb +10 -1
  105. data/test/rubygems/test_gem_request_set.rb +5 -8
  106. data/test/rubygems/test_gem_request_set_lockfile.rb +2 -4
  107. data/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb +1 -1
  108. data/test/rubygems/test_gem_resolver.rb +12 -31
  109. data/test/rubygems/test_gem_resolver_git_specification.rb +1 -0
  110. data/test/rubygems/test_gem_resolver_installer_set.rb +7 -11
  111. data/test/rubygems/test_gem_resolver_lock_specification.rb +3 -2
  112. data/test/rubygems/test_gem_security_trust_dir.rb +2 -0
  113. data/test/rubygems/test_gem_server.rb +4 -0
  114. data/test/rubygems/test_gem_specification.rb +344 -61
  115. data/test/rubygems/test_gem_stream_ui.rb +6 -6
  116. data/test/rubygems/test_gem_stub_specification.rb +21 -6
  117. data/test/rubygems/test_gem_text.rb +2 -0
  118. data/test/rubygems/test_gem_uninstaller.rb +2 -1
  119. data/test/rubygems/test_gem_util.rb +8 -0
  120. data/test/rubygems/test_require.rb +156 -125
  121. data/util/generate_spdx_license_list.rb +21 -0
  122. data/util/update_bundled_ca_certificates.rb +2 -1
  123. metadata +42 -6
  124. metadata.gz.sig +0 -0
  125. 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
- net_http_args = [uri.host, uri.port]
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?(uri.host, no_proxy) then
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
- proxy_uri.host,
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? uri.host, no_proxy then
79
+ elsif no_proxy? hostname, no_proxy then
76
80
  net_http_args + [nil, nil]
77
81
  else
78
82
  net_http_args
@@ -159,16 +159,13 @@ class Gem::RequestSet
159
159
 
160
160
  path = req.download cache_dir
161
161
 
162
- inst = Gem::Installer.new path, options
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 # :nodoc:
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 path option was handled.
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
@@ -185,7 +185,7 @@ class Gem::RequestSet::Lockfile
185
185
 
186
186
  platforms = platforms.sort_by { |platform| platform.to_s }
187
187
 
188
- platforms.sort.each do |platform|
188
+ platforms.each do |platform|
189
189
  out << " #{platform}"
190
190
  end
191
191
 
@@ -11,13 +11,13 @@ class Gem::RequestSet::Lockfile::Parser
11
11
 
12
12
  def parse
13
13
  until @tokens.empty? do
14
- type, data, column, line = get
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 data
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
- type, = get until @tokens.empty? or peek.first == :section
32
+ token = get until @tokens.empty? or peek.first == :section
33
33
  end
34
34
  else
35
- raise "BUG: unhandled token #{type} (#{data.inspect}) at line #{line} column #{column}"
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
- current_token = @tokens.shift
44
+ token = @tokens.shift
45
45
 
46
- type, value, column, line = current_token
46
+ if expected_types and not Array(expected_types).include? token.type then
47
+ unget token
47
48
 
48
- if expected_types and not Array(expected_types).include? type then
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 current_token
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
- current_token
65
+ token
68
66
  end
69
67
 
70
68
  def parse_DEPENDENCIES # :nodoc:
71
- while not @tokens.empty? and :text == peek.first do
72
- _, name, = get :text
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(name)
78
+ requirements << pinned_requirement(token.value)
81
79
  when :l_paren then
82
80
  get :l_paren
83
81
 
84
82
  loop do
85
- _, op, = get :requirement
86
- _, version, = get :text
83
+ op = get(:requirement).value
84
+ version = get(:text).value
87
85
 
88
86
  requirements << "#{op} #{version}"
89
87
 
90
- break unless peek[0] == :comma
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(name)
97
+ requirements << pinned_requirement(token.value)
100
98
 
101
99
  get :bang
102
100
  end
103
101
  end
104
102
 
105
- @set.gem name, *requirements
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
- _, data, = get :text
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.first do
132
- _, name, column, = get :text
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
- type, data, = get [:text, :requirement]
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
- _, repository, = get :text
174
+ repository = get(:text).value
173
175
 
174
176
  skip :newline
175
177
 
176
178
  get :entry, 'revision'
177
- _, revision, = get :text
179
+ revision = get(:text).value
178
180
 
179
181
  skip :newline
180
182
 
181
- type, value = peek.first 2
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.first do
199
- _, name, column, = get :text
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
- type, data, = get [:text, :requirement]
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
- _, directory, = get :text
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
- _, name, column, = get :text
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
- type, data, = get [:text, :requirement]
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
- _, name, = get :text
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
- _, version, = get :text
299
+ version = get(:text).value
289
300
 
290
301
  requirements = ["#{op} #{version}"]
291
302
 
292
- while peek[0] == :comma do
303
+ while peek.type == :comma do
293
304
  get :comma
294
- _, op, = get :requirement
295
- _, version, = get :text
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.first == type
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 || [:EOF]
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 = [:newline, nil, *token_pos(pos)]
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
- [:text, text, *token_pos(pos)]
85
+ Token.new(:text, text, *token_pos(pos))
83
86
  else
84
- [:section, s.matched, *token_pos(pos)]
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
- [:entry, s[1], *token_pos(pos)]
91
+ Token.new(:entry, s[1], *token_pos(pos))
89
92
  when s.scan(/\(/) then
90
- [:l_paren, nil, *token_pos(pos)]
93
+ Token.new(:l_paren, nil, *token_pos(pos))
91
94
  when s.scan(/\)/) then
92
- [:r_paren, nil, *token_pos(pos)]
95
+ Token.new(:r_paren, nil, *token_pos(pos))
93
96
  when s.scan(/<=|>=|=|~>|<|>|!=/) then
94
- [:requirement, s.matched, *token_pos(pos)]
97
+ Token.new(:requirement, s.matched, *token_pos(pos))
95
98
  when s.scan(/,/) then
96
- [:comma, nil, *token_pos(pos)]
99
+ Token.new(:comma, nil, *token_pos(pos))
97
100
  when s.scan(/!/) then
98
- [:bang, nil, *token_pos(pos)]
101
+ Token.new(:bang, nil, *token_pos(pos))
99
102
  when s.scan(/[^\s),!]*/) then
100
- [:text, s.matched, *token_pos(pos)]
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
@@ -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=nil # :nodoc:
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.add Gem::Resolver::DependencyRequest.new(d, act)
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
- @needed.reverse_each do |n|
188
- request = Gem::Resolver::DependencyRequest.new n, nil
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
- @stats.record_requirements needed
179
+ def debug?
180
+ DEBUG_RESOLVER
181
+ end
195
182
 
196
- res = resolve_for needed, nil
183
+ include Molinillo::SpecificationProvider
197
184
 
198
- raise Gem::DependencyResolutionError, res if
199
- res.kind_of? Gem::Resolver::Conflict
185
+ ##
186
+ # Proceed with resolution! Returns an array of ActivationRequest objects.
200
187
 
201
- res.to_a
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
- # The meat of the algorithm. Given +needed+ DependencyRequest objects and
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
- case matching.size
349
- when 0
350
- resolve_for_zero dep, all
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
- # There are multiple +possible+ specifications for this +dep+. Updates
384
- # +needed+, +specs+ and +states+ for further resolution of the +possible+
385
- # choices.
386
-
387
- def resolve_for_multiple needed, specs, states, dep, possible # :nodoc:
388
- # Sort them so that we try the highest versions first.
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
- spec, act = activation_request dep, possible
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
- # Add the spec from the +possible+ list to +specs+ and process the spec's
412
- # dependencies by adding them to +needed+.
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
- # When there are no possible specifications for +dep+ our work is done.
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
- # Returns the gems in +specs+ that match the local platform.
249
+ def name_for(dependency)
250
+ dependency.name
251
+ end
445
252
 
446
- def select_local_platforms specs # :nodoc:
447
- specs.select do |spec|
448
- Gem::Platform.installable? spec
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
-