bundler 1.8.9 → 1.9.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -7
  3. data/CHANGELOG.md +8 -33
  4. data/Rakefile +51 -8
  5. data/lib/bundler/cli/gem.rb +20 -1
  6. data/lib/bundler/cli/install.rb +1 -1
  7. data/lib/bundler/definition.rb +8 -12
  8. data/lib/bundler/dep_proxy.rb +2 -2
  9. data/lib/bundler/installer.rb +12 -18
  10. data/lib/bundler/resolver.rb +168 -383
  11. data/lib/bundler/rubygems_ext.rb +1 -1
  12. data/lib/bundler/rubygems_integration.rb +6 -14
  13. data/lib/bundler/runtime.rb +3 -0
  14. data/lib/bundler/shared_helpers.rb +12 -7
  15. data/lib/bundler/source.rb +0 -5
  16. data/lib/bundler/source/path.rb +2 -1
  17. data/lib/bundler/source/path/installer.rb +0 -2
  18. data/lib/bundler/source/rubygems.rb +9 -11
  19. data/lib/bundler/templates/newgem/Rakefile.tt +0 -1
  20. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  21. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo.rb +5 -0
  22. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/dependency_graph.rb +266 -0
  23. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/errors.rb +69 -0
  24. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/gem_metadata.rb +3 -0
  25. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/specification_provider.rb +90 -0
  26. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/modules/ui.rb +63 -0
  27. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolution.rb +412 -0
  28. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/resolver.rb +43 -0
  29. data/lib/bundler/vendor/Molinillo-0.2.1/lib/molinillo/state.rb +43 -0
  30. data/lib/bundler/vendor/{thor.rb → thor-0.19.1/lib/thor.rb} +57 -53
  31. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions.rb +34 -34
  32. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions/create_file.rb +7 -7
  33. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions/create_link.rb +2 -2
  34. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions/directory.rb +11 -11
  35. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions/empty_directory.rb +2 -2
  36. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions/file_manipulation.rb +14 -14
  37. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/actions/inject_into_file.rb +24 -24
  38. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/base.rb +71 -71
  39. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/command.rb +8 -8
  40. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/core_ext/hash_with_indifferent_access.rb +2 -2
  41. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/core_ext/io_binary_read.rb +1 -1
  42. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/core_ext/ordered_hash.rb +2 -2
  43. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/error.rb +3 -3
  44. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/group.rb +27 -27
  45. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/invocation.rb +16 -11
  46. data/lib/bundler/vendor/thor-0.19.1/lib/thor/line_editor.rb +17 -0
  47. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/line_editor/basic.rb +2 -2
  48. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/line_editor/readline.rb +7 -7
  49. data/lib/bundler/vendor/thor-0.19.1/lib/thor/parser.rb +4 -0
  50. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/parser/argument.rb +7 -7
  51. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/parser/arguments.rb +10 -10
  52. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/parser/option.rb +14 -10
  53. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/parser/options.rb +12 -12
  54. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/rake_compat.rb +14 -14
  55. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/runner.rb +76 -76
  56. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/shell.rb +18 -18
  57. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/shell/basic.rb +31 -30
  58. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/shell/color.rb +10 -10
  59. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/shell/html.rb +28 -28
  60. data/lib/bundler/vendor/{thor → thor-0.19.1/lib/thor}/util.rb +61 -61
  61. data/lib/bundler/vendor/thor-0.19.1/lib/thor/version.rb +3 -0
  62. data/lib/bundler/vendored_molinillo.rb +5 -0
  63. data/lib/bundler/vendored_thor.rb +3 -6
  64. data/lib/bundler/version.rb +1 -1
  65. metadata +44 -35
  66. data/lib/bundler/vendor/.document +0 -0
  67. data/lib/bundler/vendor/thor/line_editor.rb +0 -17
  68. data/lib/bundler/vendor/thor/parser.rb +0 -4
  69. data/lib/bundler/vendor/thor/version.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ddf096e691d8e5cc1625221906686441b6d48d45
4
- data.tar.gz: 0b899a722320c9828c1980fcc19838ba6f7cfe2c
3
+ metadata.gz: 86a33559b397d9ddda95f691097fa4811d9a9c12
4
+ data.tar.gz: f93e41d2292a01daa7566fc9ab92ce80441dd543
5
5
  SHA512:
6
- metadata.gz: 41d1b08f9febdc290c8b99c3fe7a37f4d7a7431c989f9fb141212d3f08b6b11348f8b5f60c71fd23e1f98677f59f7b5ffe4cbcb0f96506cce93965940f87e045
7
- data.tar.gz: ceb61499c262ea1ab1282d80f70322d36d12880dbcb11d5af528383d0b26a301afed9f59863fe926e29627fd7761d3aa7cf2c1cbffbdad9c9ea10b0e3f82016e
6
+ metadata.gz: f97a40fcec05c571ce3d189aa40dd94293f14b39fb3cf14ccb71e1dee73b17300234010b5ca13903eba84620e3afa5f95eb9d8e527607bf8c4cb1d7988a20fcc
7
+ data.tar.gz: 1c9aa9f983791d29fb606d85d5ab3d5048c0fbc92d5eb01cb3c8ba9ffcd33529d773a22c660582166aa99dc6cc32ae86233e7963ed7c7d8756d2114931adcfa2
@@ -27,6 +27,7 @@ notifications:
27
27
  - secure: JxBi7DDJGkIF/7f/FSN/HUHpvV4EKfQccZHTPd1b2pNJn3GXo6u+tNVbAw2WjxYzPyPQI3ZcYBCU9SEXp/i7VmG8uMzh8Kyildw+miSKYKVb90uYqcsXWzbxwyNBgJLvyDkzST45H5lgnyAicee3WkFes/WDZikIajbH7ztdb04=
28
28
 
29
29
  rvm:
30
+ - 2.2
30
31
  - 2.1
31
32
  - 2.0.0
32
33
  - 1.9.3
@@ -38,24 +39,25 @@ env:
38
39
  # We need to know if changes to rubygems will break bundler on release
39
40
  - RGV=master
40
41
  # Test the latest rubygems release with all of our supported rubies
41
- - RGV=v2.4.4
42
+ - RGV=v2.4.6
42
43
 
43
44
  matrix:
44
45
  fast_finish: true
45
46
  include:
47
+ # Ruby 2.2, Rubygems 2.4.5 and up (RG 2.4 is included by the matrix above)
46
48
  # Ruby 2.1, Rubygems 2.2.2 and up
47
49
  - rvm: 2.1
48
- env: RGV=v2.2.2
49
- # Ruby 2.0.0, Rubygems 2.0 and up
50
+ env: RGV=v2.2.3
51
+ # Ruby 2.0.0, Rubygems 2.0.0 and up
50
52
  - rvm: 2.0.0
51
- env: RGV=v2.2.2
53
+ env: RGV=v2.2.3
52
54
  - rvm: 2.0.0
53
55
  env: RGV=v2.1.11
54
56
  - rvm: 2.0.0
55
57
  env: RGV=v2.0.14
56
58
  # Ruby 1.9.3, Rubygems 1.5.3 and up
57
59
  - rvm: 1.9.3
58
- env: RGV=v2.2.2
60
+ env: RGV=v2.2.3
59
61
  - rvm: 1.9.3
60
62
  env: RGV=v2.1.11
61
63
  - rvm: 1.9.3
@@ -70,7 +72,7 @@ matrix:
70
72
  env: RGV=v1.5.3
71
73
  # Ruby 1.8.7, Rubygems 1.3.6 and up
72
74
  - rvm: 1.8.7
73
- env: RGV=v2.2.2
75
+ env: RGV=v2.2.3
74
76
  - rvm: 1.8.7
75
77
  env: RGV=v2.0.14
76
78
  - rvm: 1.8.7
@@ -95,7 +97,7 @@ matrix:
95
97
  # Ruby 1.9.2 sanity check
96
98
  # (but it's just too slow and sometimes goes over the Travis limit)
97
99
  - rvm: 1.9.2
98
- env: RGV=v2.3.0
100
+ env: RGV=v2.4.6
99
101
  # Ruby-head (we want to know how we're doing, but not fail the build)
100
102
  - rvm: ruby-head
101
103
  env: RGV=master
@@ -1,27 +1,14 @@
1
- ## 1.8.9 (2015-05-02)
1
+ ## 1.9.0.pre (2015-03-11)
2
2
 
3
- Bugfixes:
4
-
5
- - Support RubyGems versions above 2.4.6 (@tenderlove, @segiddins, @indirect)
6
-
7
- ## 1.8.8 (2015-04-29)
8
-
9
- Bugfixes:
10
-
11
- - Respect Gemfile sources when installing a gem present in two sources (#3585, @tmoore)
12
-
13
- ## 1.8.7 (2015-04-07)
14
-
15
- Bugfixes:
16
-
17
- - stop suppressing errors inside gems that get required (#3549, @indirect)
3
+ Features:
18
4
 
19
- ## 1.8.6 (2015-03-30)
5
+ - prefer gemspecs closest to the directory root (#3428, @segiddins)
6
+ - debug log for API request limits (#3452, @neerfri)
20
7
 
21
- Bugfixes:
8
+ "Features":
22
9
 
23
- - keep gems locked when updating another gem from the same source (#3250, @indirect)
24
- - resolve race that could build gems without saved arguments (#3404, @indirect)
10
+ - Molinillo resolver, shared with CocoaPods (@segiddins)
11
+ - updated Thor to v0.19.1 (@segiddins)
25
12
 
26
13
  ## 1.8.5 (2015-03-11)
27
14
 
@@ -109,6 +96,7 @@ Features:
109
96
  - add `package --cache-path` and `config cache_path` for cache location (@jnraine)
110
97
  - allow `config` to work even when a Gemfile is not present (@dholdren)
111
98
  - add `config gemfile /path` for other Gemfile locations (@dholdren)
99
+ - add `github` method alonside the `git` method (@BenMorganIO)
112
100
 
113
101
  Bugfixes:
114
102
 
@@ -121,19 +109,6 @@ Documentation:
121
109
 
122
110
  - add missing Gemfile global `path` explanation (@agenteo)
123
111
 
124
- ## 1.7.15 (2015-04-29)
125
-
126
- Bugfixes:
127
-
128
- - Respect Gemfile sources when installing a gem present in two sources (#3585, @tmoore)
129
-
130
- ## 1.7.14 (2015-03-30)
131
-
132
- Bugfixes:
133
-
134
- - Keep gems locked when updating another gem from the same source (#3250, @indirect)
135
- - Don't add extra quotes around long, quoted config values (@aroben, #3338)
136
-
137
112
  ## 1.7.13 (2015-02-07)
138
113
 
139
114
  Bugfixes:
data/Rakefile CHANGED
@@ -27,6 +27,52 @@ module Rake
27
27
  end
28
28
  end
29
29
 
30
+ namespace :molinillo do
31
+ task :namespace do
32
+ files = Dir.glob('lib/bundler/vendor/Molinillo*/**/*.rb')
33
+ sh "sed -i.bak 's/Molinillo/Bundler::Molinillo/g' #{files.join(' ')}"
34
+ sh "rm #{files.join('.bak ')}.bak"
35
+ end
36
+
37
+ task :clean do
38
+ files = Dir.glob('lib/bundler/vendor/Molinillo*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last }
39
+ puts files
40
+ sh "rm -r #{files.join(' ')}"
41
+ end
42
+
43
+ task :update, [:tag] => [] do |t, args|
44
+ tag = args[:tag]
45
+ Dir.chdir 'lib/bundler/vendor' do
46
+ `curl -L https://github.com/CocoaPods/molinillo/archive/#{tag}.tar.gz | tar -xz`
47
+ end
48
+ Rake::Task['molinillo:namespace'].invoke
49
+ Rake::Task['molinillo:clean'].invoke
50
+ end
51
+ end
52
+
53
+ namespace :thor do
54
+ task :namespace do
55
+ files = Dir.glob('lib/bundler/vendor/thor*/**/*.rb')
56
+ sh "sed -i.bak 's/Thor/Bundler::Thor/g' #{files.join(' ')}"
57
+ sh "rm #{files.join('.bak ')}.bak"
58
+ end
59
+
60
+ task :clean do
61
+ files = Dir.glob('lib/bundler/vendor/thor*/*', File::FNM_DOTMATCH).reject { |f| %(. .. lib).include? f.split('/').last }
62
+ puts files
63
+ sh "rm -r #{files.join(' ')}"
64
+ end
65
+
66
+ task :update, [:tag] => [] do |t, args|
67
+ tag = args[:tag]
68
+ Dir.chdir 'lib/bundler/vendor' do
69
+ `curl -L https://github.com/erikhuda/thor/archive/#{tag}.tar.gz | tar -xz`
70
+ end
71
+ Rake::Task['thor:namespace'].invoke
72
+ Rake::Task['thor:clean'].invoke
73
+ end
74
+ end
75
+
30
76
  namespace :spec do
31
77
  desc "Ensure spec dependencies are installed"
32
78
  task :deps do
@@ -111,8 +157,8 @@ begin
111
157
  namespace :rubygems do
112
158
  rubyopt = ENV["RUBYOPT"]
113
159
  # When editing this list, also edit .travis.yml!
114
- branches = %w(master 2.2)
115
- releases = %w(v1.3.6 v1.3.7 v1.4.2 v1.5.3 v1.6.2 v1.7.2 v1.8.29 v2.0.14 v2.1.11 v2.2.2 v2.4.4)
160
+ branches = %w(master)
161
+ releases = %w(v1.3.6 v1.3.7 v1.4.2 v1.5.3 v1.6.2 v1.7.2 v1.8.29 v2.0.14 v2.1.11 v2.2.3 v2.4.6)
116
162
  (branches + releases).each do |rg|
117
163
  desc "Run specs with Rubygems #{rg}"
118
164
  RSpec::Core::RakeTask.new(rg) do |t|
@@ -234,15 +280,12 @@ begin
234
280
  task :clean do
235
281
  rm_rf "lib/bundler/man"
236
282
  end
237
-
238
- task(:require) { }
239
283
  end
240
284
 
241
285
  rescue LoadError
242
286
  namespace :man do
243
- task(:require) { abort "Install the ronn gem to be able to release!" }
244
- task(:build) { warn "Install the ronn gem to build the help pages" }
245
- task(:clean) { }
287
+ task(:build) { warn "Install the ronn gem to be able to release!" }
288
+ task(:clean) { warn "Install the ronn gem to be able to release!" }
246
289
  end
247
290
  end
248
291
 
@@ -254,6 +297,6 @@ end
254
297
 
255
298
  require 'bundler/gem_tasks'
256
299
  task :build => ["man:clean", "man:build"]
257
- task :release => ["man:require", "man:clean", "man:build"]
300
+ task :release => ["man:clean", "man:build"]
258
301
 
259
302
  task :default => :spec
@@ -37,8 +37,10 @@ module Bundler
37
37
  :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
38
38
  :test => options[:test],
39
39
  :ext => options[:ext],
40
- :bin => options[:bin]
40
+ :bin => options[:bin],
41
+ :bundler_version => bundler_dependency_version
41
42
  }
43
+ ensure_safe_gem_name(opts[:name], opts[:constant_array])
42
44
 
43
45
  templates = {
44
46
  "Gemfile.tt" => "Gemfile",
@@ -161,5 +163,22 @@ module Bundler
161
163
  test_framework
162
164
  end
163
165
 
166
+ def bundler_dependency_version
167
+ v = Gem::Version.new(Bundler::VERSION)
168
+ req = v.segments[0..1]
169
+ req << v.segments.last if v.prerelease?
170
+ req.join(".")
171
+ end
172
+
173
+ def ensure_safe_gem_name name, constant_array
174
+ if name =~ /^\d/
175
+ Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
176
+ exit 1
177
+ elsif Object.const_defined?(constant_array.first)
178
+ Bundler.ui.error "Invalid gem name #{name} constant #{constant_array.join("::")} is already in use. Please choose another gem name."
179
+ exit 1
180
+ end
181
+ end
182
+
164
183
  end
165
184
  end
@@ -2,7 +2,7 @@ module Bundler
2
2
  class CLI::Install
3
3
  attr_reader :options
4
4
  def initialize(options)
5
- @options = options.dup
5
+ @options = options
6
6
  end
7
7
 
8
8
  def run
@@ -184,10 +184,11 @@ module Bundler
184
184
  # @return [SpecSet] resolved dependencies
185
185
  def resolve
186
186
  @resolve ||= begin
187
- last_resolve = converge_locked_specs
188
187
  if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?)
189
- last_resolve
188
+ @locked_specs
190
189
  else
190
+ last_resolve = converge_locked_specs
191
+
191
192
  # Run a resolve against the locally available gems
192
193
  last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve)
193
194
  end
@@ -271,7 +272,7 @@ module Bundler
271
272
  each do |spec|
272
273
  next if spec.name == 'bundler'
273
274
  out << spec.to_lock
274
- end
275
+ end
275
276
  out << "\n"
276
277
  end
277
278
 
@@ -520,9 +521,7 @@ module Bundler
520
521
 
521
522
  converged = []
522
523
  @locked_specs.each do |s|
523
- # Replace the locked dependency's source with the equivalent source from the Gemfile
524
- dep = @dependencies.find { |d| s.satisfies?(d) }
525
- s.source = (dep && dep.source) || sources.get(s.source)
524
+ s.source = sources.get(s.source)
526
525
 
527
526
  # Don't add a spec to the list if its source is expired. For example,
528
527
  # if you change a Git gem to Rubygems.
@@ -562,15 +561,12 @@ module Bundler
562
561
  resolve
563
562
  end
564
563
 
565
- def in_locked_deps?(dep, locked_dep)
566
- # Because the lockfile can't link a dep to a specific remote, we need to
567
- # treat sources as equivalent anytime the locked dep has all the remotes
568
- # that the Gemfile dep does.
569
- locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
564
+ def in_locked_deps?(dep, d)
565
+ d && dep.source == d.source
570
566
  end
571
567
 
572
568
  def satisfies_locked_spec?(dep)
573
- @locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
569
+ @locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source == dep.source) }
574
570
  end
575
571
 
576
572
  def expanded_dependencies
@@ -1,10 +1,10 @@
1
1
  module Bundler
2
2
  class DepProxy
3
3
 
4
- attr_reader :required_by, :__platform, :dep
4
+ attr_reader :__platform, :dep
5
5
 
6
6
  def initialize(dep, platform)
7
- @dep, @__platform, @required_by = dep, platform, []
7
+ @dep, @__platform = dep, platform
8
8
  end
9
9
 
10
10
  def hash
@@ -96,26 +96,20 @@ module Bundler
96
96
 
97
97
  def install_gem_from_spec(spec, standalone = false, worker = 0)
98
98
  # Fetch the build settings, if there are any
99
- settings = Bundler.settings["build.#{spec.name}"]
100
- messages = nil
101
-
102
- if settings
103
- Bundler.rubygems.with_build_args [settings] do
104
- messages = spec.source.install(spec)
99
+ settings = Bundler.settings["build.#{spec.name}"]
100
+ install_message = nil
101
+ post_install_message = nil
102
+ debug_message = nil
103
+ Bundler.rubygems.with_build_args [settings] do
104
+ install_message, post_install_message, debug_message = spec.source.install(spec)
105
+ if install_message.include? 'Installing'
106
+ Bundler.ui.confirm install_message
107
+ else
108
+ Bundler.ui.info install_message
105
109
  end
106
- else
107
- messages = spec.source.install(spec)
108
- end
109
-
110
- install_message, post_install_message, debug_message = *messages
111
-
112
- if install_message.include? 'Installing'
113
- Bundler.ui.confirm install_message
114
- else
115
- Bundler.ui.info install_message
110
+ Bundler.ui.debug debug_message if debug_message
111
+ Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
116
112
  end
117
- Bundler.ui.debug debug_message if debug_message
118
- Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
119
113
 
120
114
  if Bundler.settings[:bin] && standalone
121
115
  generate_standalone_bundler_executable_stubs(spec)
@@ -1,27 +1,73 @@
1
1
  require 'set'
2
+
2
3
  # This is the latest iteration of the gem dependency resolving algorithm. As of now,
3
4
  # it can resolve (as a success or failure) any set of gem dependencies we throw at it
4
5
  # in a reasonable amount of time. The most iterations I've seen it take is about 150.
5
6
  # The actual implementation of the algorithm is not as good as it could be yet, but that
6
7
  # can come later.
7
8
 
8
- # Extending Gem classes to add necessary tracking information
9
- module Gem
10
- class Specification
11
- def required_by
12
- @required_by ||= []
13
- end
14
- end
15
- class Dependency
16
- def required_by
17
- @required_by ||= []
18
- end
19
- end
20
- end
21
-
22
9
  module Bundler
23
10
  class Resolver
24
11
 
12
+ require 'bundler/vendored_molinillo'
13
+
14
+ class Molinillo::VersionConflict
15
+ def clean_req(req)
16
+ if req.to_s.include?(">= 0")
17
+ req.to_s.gsub(/ \(.*?\)$/, '')
18
+ else
19
+ req.to_s.gsub(/\, (runtime|development)\)$/, ')')
20
+ end
21
+ end
22
+
23
+ def message
24
+ conflicts.values.flatten.reduce('') do |o, conflict|
25
+ o << %(Bundler could not find compatible versions for gem "#{conflict.requirement.name}":\n)
26
+ if conflict.locked_requirement
27
+ o << %( In snapshot (Gemfile.lock):\n)
28
+ o << %( #{clean_req conflict.locked_requirement}\n)
29
+ o << %(\n)
30
+ end
31
+ o << %( In Gemfile:\n)
32
+ o << conflict.requirement_trees.map do |tree|
33
+ t = ''
34
+ depth = 2
35
+ tree.each do |req|
36
+ t << ' ' * depth << %(#{clean_req req})
37
+ t << %( depends on) unless tree[-1] == req
38
+ t << %(\n)
39
+ depth += 1
40
+ end
41
+ t
42
+ end.join("\n")
43
+
44
+ if conflict.requirement.name == 'bundler'
45
+ o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
46
+ other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new Bundler::VERSION)
47
+ end
48
+
49
+ if conflict.requirement.name == "bundler" && other_bundler_required
50
+ o << "\n"
51
+ o << "This Gemfile requires a different version of Bundler.\n"
52
+ o << "Perhaps you need to update Bundler by running `gem install bundler`?\n"
53
+ end
54
+ if conflict.locked_requirement
55
+ o << "\n"
56
+ o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\n)
57
+ o << %(the gems in your Gemfile, which may resolve the conflict.\n)
58
+ elsif !conflict.existing
59
+ if conflict.requirement_trees.first.size > 1
60
+ o << "Could not find gem '#{clean_req(conflict.requirement)}', which is required by "
61
+ o << "gem '#{clean_req(conflict.requirement_trees.first[-2])}', in any of the sources."
62
+ else
63
+ o << "Could not find gem '#{clean_req(conflict.requirement)} in any of the sources\n"
64
+ end
65
+ end
66
+ o
67
+ end
68
+ end
69
+ end
70
+
25
71
  ALL = Bundler::Dependency::PLATFORM_MAP.values.uniq.freeze
26
72
 
27
73
  class SpecGroup < Array
@@ -65,8 +111,10 @@ module Bundler
65
111
 
66
112
  def activate_platform(platform)
67
113
  unless @activated.include?(platform)
68
- @activated << platform
69
- return __dependencies[platform] || []
114
+ if for?(platform)
115
+ @activated << platform
116
+ return __dependencies[platform] || []
117
+ end
70
118
  end
71
119
  []
72
120
  end
@@ -91,6 +139,14 @@ module Bundler
91
139
  "#{name} (#{version})"
92
140
  end
93
141
 
142
+ def dependencies_for_activated_platforms
143
+ @activated.map { |p| __dependencies[p] }.flatten
144
+ end
145
+
146
+ def platforms_for_dependency_named(dependency)
147
+ __dependencies.select { |p, deps| deps.map(&:name).include? dependency }.keys
148
+ end
149
+
94
150
  private
95
151
 
96
152
  def __dependencies
@@ -110,8 +166,6 @@ module Bundler
110
166
  end
111
167
  end
112
168
 
113
- attr_reader :errors, :started_at, :iteration_rate, :iteration_counter, :initial_reqs
114
-
115
169
  # Figures out the best possible configuration of gems that satisfies
116
170
  # the list of passed dependencies and any child dependencies without
117
171
  # causing any gem activation errors.
@@ -123,289 +177,85 @@ module Bundler
123
177
  # <GemBundle>,nil:: If the list of dependencies can be resolved, a
124
178
  # collection of gemspecs is returned. Otherwise, nil is returned.
125
179
  def self.resolve(requirements, index, source_requirements = {}, base = [])
126
- Bundler.ui.info "Resolving dependencies...", false
127
180
  base = SpecSet.new(base) unless base.is_a?(SpecSet)
128
181
  resolver = new(index, source_requirements, base)
129
182
  result = resolver.start(requirements)
130
- Bundler.ui.info "" # new line now that dots are done
131
183
  SpecSet.new(result)
132
- rescue => e
133
- Bundler.ui.info "" # new line before the error
134
- raise e
135
- end
136
-
137
- def initialize(index, source_requirements, base)
138
- @initial_reqs = []
139
- @errors = {}
140
- @base = base
141
- @index = index
142
- @deps_for = {}
143
- @missing_gems = Hash.new(0)
144
- @prereleases_cache = Hash.new { |h,k| h[k] = k.prerelease? }
145
- @source_requirements = source_requirements
146
- @iteration_counter = 0
147
- @started_at = Time.now
148
184
  end
149
185
 
150
- def debug
151
- if ENV['DEBUG_RESOLVER']
152
- debug_info = yield
153
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
154
- $stderr.puts debug_info
155
- end
156
- end
157
186
 
158
- def successify(activated)
159
- activated.values.map { |s| s.to_specs }.flatten.compact
160
- end
161
-
162
- def start(reqs, current_traversal=false)
163
- @initial_reqs = reqs.dup unless current_traversal
164
- activated = {}
165
- @gems_size = Hash[reqs.map { |r| [r, gems_size(r)] }]
166
-
167
- resolve(reqs, activated, current_traversal)
187
+ def initialize(index, source_requirements, base)
188
+ @index = index
189
+ @source_requirements = source_requirements
190
+ @base = base
191
+ @resolver = Molinillo::Resolver.new(self, self)
192
+ @search_for = {}
193
+ @prereleases_cache = Hash.new { |h,k| h[k] = k.prerelease? }
194
+ @base_dg = Molinillo::DependencyGraph.new
195
+ @base.each { |ls| @base_dg.add_root_vertex ls.name, Dependency.new(ls.name, ls.version) }
168
196
  end
169
197
 
170
- class State < Struct.new(:reqs, :activated, :requirement, :possibles, :depth, :conflicts)
171
- def name
172
- requirement.name
173
- end
198
+ def start(requirements)
199
+ verify_gemfile_dependencies_are_found!(requirements)
200
+ dg = @resolver.resolve(requirements, @base_dg)
201
+ dg.map(&:payload).map(&:to_specs).flatten
202
+ rescue Molinillo::VersionConflict => e
203
+ raise VersionConflict.new(e.conflicts.keys.uniq, e.message)
204
+ rescue Molinillo::CircularDependencyError => e
205
+ names = e.dependencies.sort_by(&:name).map { |d| "gem '#{d.name}'"}
206
+ raise CyclicDependencyError, "Your Gemfile requires gems that depend" \
207
+ " on each other, creating an infinite loop. Please remove" \
208
+ " #{names.count > 1 ? 'either ' : '' }#{names.join(' or ')}" \
209
+ " and try again."
174
210
  end
175
211
 
176
- def handle_conflict(current, states, existing=nil)
177
- until current.nil?
178
- current_state = find_state(current, states)
179
- return current if state_any?(current_state)
180
- current = current.required_by.last if current
181
- end
212
+ include Molinillo::UI
182
213
 
183
- until existing.nil?
184
- existing_state = find_state(existing, states)
185
- return existing if state_any?(existing_state)
186
- existing = existing.required_by.last if existing
214
+ # Conveys debug information to the user.
215
+ #
216
+ # @param [Integer] depth the current depth of the resolution process.
217
+ # @return [void]
218
+ def debug(depth = 0)
219
+ if debug?
220
+ debug_info = yield
221
+ debug_info = debug_info.inspect unless debug_info.is_a?(String)
222
+ STDERR.puts debug_info.split("\n").map { |s| ' ' * depth + s }
187
223
  end
188
224
  end
189
225
 
190
- def state_any?(state)
191
- state && state.possibles.any?
226
+ def debug?
227
+ ENV['DEBUG_RESOLVER'] || ENV['DEBUG_RESOLVER_TREE']
192
228
  end
193
229
 
194
- def find_state(current, states)
195
- states.detect { |i| current && current.name == i.name }
230
+ def before_resolution
231
+ Bundler.ui.info 'Resolving dependencies...', false
196
232
  end
197
233
 
198
- def other_possible?(conflict, states)
199
- return unless conflict
200
- state = states.detect { |i| i.name == conflict.name }
201
- state && state.possibles.any?
234
+ def after_resolution
235
+ Bundler.ui.info ''
202
236
  end
203
237
 
204
- def find_conflict_state(conflict, states)
205
- return unless conflict
206
- until states.empty? do
207
- state = states.pop
208
- return state if conflict.name == state.name
209
- end
210
- end
211
-
212
- def activate_gem(reqs, activated, requirement, current)
213
- requirement.required_by.replace current.required_by
214
- requirement.required_by << current
215
- activated[requirement.name] = requirement
216
-
217
- debug { " Activating: #{requirement.name} (#{requirement.version})" }
218
- debug { requirement.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\n") }
219
-
220
- dependencies = requirement.activate_platform(current.__platform)
221
-
222
- debug { " Dependencies"}
223
- dependencies.each do |dep|
224
- next if dep.type == :development
225
- dep.required_by.replace(current.required_by)
226
- dep.required_by << current
227
- @gems_size[dep] ||= gems_size(dep)
228
- reqs << dep
229
- end
230
- end
231
-
232
- def resolve_for_conflict(state)
233
- return version_conflict if state.nil? || state.possibles.empty?
234
- reqs, activated, depth, conflicts = state.reqs.dup, state.activated.dup, state.depth, state.conflicts.dup
235
- requirement = state.requirement
236
- possible = state.possibles.pop
237
-
238
- activate_gem(reqs, activated, possible, requirement)
239
-
240
- return reqs, activated, depth, conflicts
238
+ def indicate_progress
239
+ Bundler.ui.info '.', false
241
240
  end
242
241
 
243
- def resolve_conflict(current, states)
244
- # Find the state where the conflict has occurred
245
- state = find_conflict_state(current, states)
246
-
247
- debug { " -> Going to: #{current.name} state" } if current
248
-
249
- # Resolve the conflicts by rewinding the state
250
- # when the conflicted gem was activated
251
- reqs, activated, depth, conflicts = resolve_for_conflict(state)
242
+ private
252
243
 
253
- # Keep the state around if it still has other possibilities
254
- states << state unless state.possibles.empty?
255
- clear_search_cache
244
+ include Molinillo::SpecificationProvider
256
245
 
257
- return reqs, activated, depth, conflicts
246
+ def dependencies_for(specification)
247
+ specification.dependencies_for_activated_platforms
258
248
  end
259
249
 
260
- def resolve(reqs, activated, current_traversal)
261
- states = []
262
- depth = 0
263
- conflicts = Set.new
264
-
265
- until reqs.empty?
266
-
267
- indicate_progress
268
-
269
- debug { print "\e[2J\e[f" ; "==== Iterating ====\n\n" }
270
-
271
- reqs = reqs.sort_by do |a|
272
- [ activated[a.name] ? 0 : 1,
273
- @prereleases_cache[a.requirement] ? 0 : 1,
274
- @errors[a.name] ? 0 : 1,
275
- activated[a.name] ? 0 : @gems_size[a] ]
276
- end
277
-
278
- debug { "Activated:\n" + activated.values.map {|a| " #{a}" }.join("\n") }
279
- debug { "Requirements:\n" + reqs.map {|r| " #{r}"}.join("\n") }
280
-
281
- current = reqs.shift
282
-
283
- $stderr.puts "#{' ' * depth}#{current}" if ENV['DEBUG_RESOLVER_TREE']
284
-
285
- debug { "Attempting:\n #{current}"}
286
-
287
- existing = activated[current.name]
288
-
289
-
290
- if existing || current.name == 'bundler'
291
- # Force the current
292
- if current.name == 'bundler' && !existing
293
- existing = search(DepProxy.new(Gem::Dependency.new('bundler', VERSION), Gem::Platform::RUBY)).first
294
- raise GemNotFound, %Q{Bundler could not find gem "bundler" (#{VERSION})} unless existing
295
- existing.required_by << existing
296
- activated['bundler'] = existing
297
- end
298
-
299
- if current.requirement.satisfied_by?(existing.version)
300
- debug { " * [SUCCESS] Already activated" }
301
- @errors.delete(existing.name)
302
- dependencies = existing.activate_platform(current.__platform)
303
- reqs.concat dependencies
304
-
305
- dependencies.each do |dep|
306
- next if dep.type == :development
307
- @gems_size[dep] ||= gems_size(dep)
308
- end
309
-
310
- depth += 1
311
- next
312
- else
313
- debug { " * [FAIL] Already activated" }
314
- @errors[existing.name] = [existing, current]
315
-
316
- conflicts << current.name
317
-
318
- parent = current.required_by.last
319
-
320
- if current_traversal
321
- parent = handle_conflict(current, states)
322
- else
323
- parent = handle_conflict(parent, states)
324
- end
325
-
326
-
327
- if parent.nil? && !conflicts.empty?
328
- parent = states.reverse.detect { |i| conflicts.include?(i.name) && state_any?(i)}
329
- end
330
-
331
- if existing.respond_to?(:required_by)
332
- parent = handle_conflict(parent, states, existing.required_by[-2]) unless other_possible?(parent, states)
333
- end
334
-
335
- return version_conflict(current_traversal) if parent.nil? || parent.name == 'bundler'
336
-
337
- reqs, activated, depth, conflicts = resolve_conflict(parent, states)
338
- end
339
- else
340
- matching_versions = search(current)
341
-
342
- # If we found no versions that match the current requirement
343
- if matching_versions.empty?
344
- # If this is a top-level Gemfile requirement
345
- if current.required_by.empty?
346
- if base = @base[current.name] and !base.empty?
347
- version = base.first.version
348
- message = "You have requested:\n" \
349
- " #{current.name} #{current.requirement}\n\n" \
350
- "The bundle currently has #{current.name} locked at #{version}.\n" \
351
- "Try running `bundle update #{current.name}`"
352
- elsif current.source
353
- name = current.name
354
- versions = @source_requirements[name][name].map { |s| s.version }
355
- message = "Could not find gem '#{current}' in #{current.source}.\n"
356
- if versions.any?
357
- message << "Source contains '#{name}' at: #{versions.join(', ')}"
358
- else
359
- message << "Source does not contain any versions of '#{current}'"
360
- end
361
- else
362
- message = "Could not find gem '#{current}' "
363
- if @index.source_types.include?(Bundler::Source::Rubygems)
364
- message << "in any of the gem sources listed in your Gemfile."
365
- else
366
- message << "in the gems available on this machine."
367
- end
368
- end
369
- raise GemNotFound, message
370
- # This is not a top-level Gemfile requirement
371
- else
372
- @errors[current.name] = [nil, current]
373
- parent = handle_conflict(current, states)
374
- reqs, activated, depth = resolve_conflict(parent, states)
375
- next
376
- end
377
- end
378
-
379
-
380
- state = State.new(reqs.dup, activated.dup, current, matching_versions, depth, conflicts)
381
- states << state
382
- requirement = state.possibles.pop
383
- activate_gem(reqs, activated, requirement, current)
250
+ def search_for(dependency)
251
+ platform = dependency.__platform
252
+ dependency = dependency.dep unless dependency.is_a? Gem::Dependency
253
+ search = @search_for[dependency] ||= begin
254
+ index = @source_requirements[dependency.name] || @index
255
+ results = index.search(dependency, @base[dependency.name])
256
+ if vertex = @base_dg.vertex_named(dependency.name)
257
+ locked_requirement = vertex.payload.requirement
384
258
  end
385
- end
386
- successify(activated)
387
- end
388
-
389
- def gems_size(dep)
390
- search(dep).size
391
- end
392
-
393
- def clear_search_cache
394
- @deps_for = {}
395
- end
396
-
397
- def search(dep)
398
- if base = @base[dep.name] and base.any?
399
- reqs = [dep.requirement.as_list, base.first.version.to_s].flatten.compact
400
- d = Gem::Dependency.new(base.first.name, *reqs)
401
- else
402
- d = dep.dep
403
- end
404
-
405
- @deps_for[d.hash] ||= begin
406
- index = @source_requirements[d.name] || @index
407
- results = index.search(d, @base[d.name])
408
-
409
259
  if results.any?
410
260
  version = results.first.version
411
261
  nested = [[]]
@@ -416,139 +266,74 @@ module Bundler
416
266
  end
417
267
  nested.last << spec
418
268
  end
419
- deps = nested.map{|a| SpecGroup.new(a) }.select{|sg| sg.for?(dep.__platform) }
269
+ groups = nested.map { |a| SpecGroup.new(a) }
270
+ !locked_requirement ? groups : groups.select { |sg| locked_requirement.satisfied_by? sg.version }
420
271
  else
421
- deps = []
272
+ []
422
273
  end
423
274
  end
275
+ search.select { |sg| sg.for?(platform) }.each { |sg| sg.activate_platform(platform) }
424
276
  end
425
277
 
426
- def clean_req(req)
427
- if req.to_s.include?(">= 0")
428
- req.to_s.gsub(/ \(.*?\)$/, '')
429
- else
430
- req.to_s.gsub(/\, (runtime|development)\)$/, ')')
431
- end
278
+ def name_for(dependency)
279
+ dependency.name
432
280
  end
433
281
 
434
- def version_conflict(current_traversal=true)
435
- raise VersionConflict.new(errors.keys, error_message) if current_traversal
436
- reset_state
437
- start(initial_reqs, true)
282
+ def name_for_explicit_dependency_source
283
+ 'Gemfile'
438
284
  end
439
285
 
440
- def reset_state
441
- clear_search_cache
442
- @errors = {}
286
+ def name_for_locking_dependency_source
287
+ 'Gemfile.lock'
443
288
  end
444
289
 
445
- # For a given conflicted requirement, print out what exactly went wrong
446
- def gem_message(requirement, required_by=[])
447
- m = ""
448
-
449
- # A requirement that is required by itself is actually in the Gemfile, and does
450
- # not "depend on" itself
451
- if requirement.required_by.first && requirement.required_by.first.name != requirement.name
452
- dependency_tree(m, required_by)
453
- m << "#{clean_req(requirement)}\n"
454
- else
455
- m << " #{clean_req(requirement)}\n"
456
- end
457
- m << "\n"
290
+ def requirement_satisfied_by?(requirement, activated, spec)
291
+ requirement.matches_spec?(spec)
458
292
  end
459
293
 
460
- def dependency_tree(m, requirements)
461
- requirements.each_with_index do |i, j|
462
- m << " " << (" " * j)
463
- m << "#{clean_req(i)}"
464
- m << " depends on\n"
294
+ def sort_dependencies(dependencies, activated, conflicts)
295
+ dependencies.sort_by do |dependency|
296
+ name = name_for(dependency)
297
+ [
298
+ activated.vertex_named(name).payload ? 0 : 1,
299
+ @prereleases_cache[dependency.requirement] ? 0 : 1,
300
+ conflicts[name] ? 0 : 1,
301
+ activated.vertex_named(name).payload ? 0 : search_for(dependency).count,
302
+ ]
465
303
  end
466
- m << " " << (" " * requirements.size)
467
304
  end
468
305
 
469
- def error_message
470
- errors.inject("") do |o, (conflict, (origin, requirement))|
471
-
472
- # origin is the SpecSet of specs from the Gemfile that is conflicted with
473
- if origin
474
-
475
- o << %{Bundler could not find compatible versions for gem "#{origin.name}":\n}
476
- o << " In Gemfile:\n"
477
-
478
- required_by = requirement.required_by
479
- o << gem_message(requirement, required_by)
480
-
481
- # If the origin is "bundler", the conflict is us
482
- if origin.name == "bundler"
483
- o << " Current Bundler version:\n"
484
- other_bundler_required = !requirement.requirement.satisfied_by?(origin.version)
485
- # If the origin is a LockfileParser, it does not respond_to :required_by
486
- elsif !origin.respond_to?(:required_by) || !(origin.required_by.first)
487
- o << " In snapshot (Gemfile.lock):\n"
488
- end
489
-
490
- required_by = origin.required_by[0..-2]
491
- o << gem_message(origin, required_by)
492
-
493
- # If the bundle wants a newer bundler than the running bundler, explain
494
- if origin.name == "bundler" && other_bundler_required
495
- o << "This Gemfile requires a different version of Bundler.\n"
496
- o << "Perhaps you need to update Bundler by running `gem install bundler`?"
497
- end
498
-
499
- # origin is nil if the required gem and version cannot be found in any of
500
- # the specified sources
501
- else
502
-
503
- # if the gem cannot be found because of a version conflict between lockfile and gemfile,
504
- # print a useful error that suggests running `bundle update`, which may fix things
505
- #
506
- # @base is a SpecSet of the gems in the lockfile
507
- # conflict is the name of the gem that could not be found
508
- if locked = @base[conflict].first
509
- o << "Bundler could not find compatible versions for gem #{conflict.inspect}:\n"
510
- o << " In snapshot (Gemfile.lock):\n"
511
- o << " #{clean_req(locked)}\n\n"
512
-
513
- o << " In Gemfile:\n"
514
-
515
- required_by = requirement.required_by
516
- o << gem_message(requirement, required_by)
517
- o << "Running `bundle update` will rebuild your snapshot from scratch, using only\n"
518
- o << "the gems in your Gemfile, which may resolve the conflict.\n"
519
-
520
- # the rest of the time, the gem cannot be found because it does not exist in the known sources
306
+ def verify_gemfile_dependencies_are_found!(requirements)
307
+ requirements.each do |requirement|
308
+ next if requirement.name == 'bundler'
309
+ if search_for(requirement).empty?
310
+ if base = @base[requirement.name] and !base.empty?
311
+ version = base.first.version
312
+ message = "You have requested:\n" \
313
+ " #{requirement.name} #{requirement.requirement}\n\n" \
314
+ "The bundle currently has #{requirement.name} locked at #{version}.\n" \
315
+ "Try running `bundle update #{requirement.name}`"
316
+ elsif requirement.source
317
+ name = requirement.name
318
+ versions = @source_requirements[name][name].map { |s| s.version }
319
+ message = "Could not find gem '#{requirement}' in #{requirement.source}.\n"
320
+ if versions.any?
321
+ message << "Source contains '#{name}' at: #{versions.join(', ')}"
322
+ else
323
+ message << "Source does not contain any versions of '#{requirement}'"
324
+ end
521
325
  else
522
- if requirement.required_by.first
523
- o << "Could not find gem '#{clean_req(requirement)}', which is required by "
524
- o << "gem '#{clean_req(requirement.required_by.first)}', in any of the sources."
326
+ message = "Could not find gem '#{requirement}' "
327
+ if @index.source_types.include?(Bundler::Source::Rubygems)
328
+ message << "in any of the gem sources listed in your Gemfile."
525
329
  else
526
- o << "Could not find gem '#{clean_req(requirement)} in any of the sources\n"
330
+ message << "in the gems available on this machine."
527
331
  end
528
332
  end
529
-
333
+ raise GemNotFound, message
530
334
  end
531
- o
532
335
  end
533
336
  end
534
337
 
535
- private
536
-
537
- # Indicates progress by writing a '.' every iteration_rate time which is
538
- # approximately every second. iteration_rate is calculated in the first
539
- # second of resolve running.
540
- def indicate_progress
541
- @iteration_counter += 1
542
-
543
- if iteration_rate.nil?
544
- if ((Time.now - started_at) % 3600).round >= 1
545
- @iteration_rate = iteration_counter
546
- end
547
- else
548
- if ((iteration_counter % iteration_rate) == 0)
549
- Bundler.ui.info ".", false
550
- end
551
- end
552
- end
553
338
  end
554
339
  end