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.
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