bundler-maglev- 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +32 -0
  3. data/CHANGELOG.md +805 -0
  4. data/ISSUES.md +62 -0
  5. data/LICENSE +23 -0
  6. data/README.md +29 -0
  7. data/Rakefile +212 -0
  8. data/UPGRADING.md +103 -0
  9. data/bin/bundle +22 -0
  10. data/bundler.gemspec +30 -0
  11. data/lib/bundler.rb +286 -0
  12. data/lib/bundler/capistrano.rb +11 -0
  13. data/lib/bundler/cli.rb +520 -0
  14. data/lib/bundler/definition.rb +438 -0
  15. data/lib/bundler/dependency.rb +134 -0
  16. data/lib/bundler/deployment.rb +58 -0
  17. data/lib/bundler/dsl.rb +257 -0
  18. data/lib/bundler/environment.rb +47 -0
  19. data/lib/bundler/gem_helper.rb +151 -0
  20. data/lib/bundler/gem_installer.rb +9 -0
  21. data/lib/bundler/gem_tasks.rb +2 -0
  22. data/lib/bundler/graph.rb +130 -0
  23. data/lib/bundler/index.rb +138 -0
  24. data/lib/bundler/installer.rb +97 -0
  25. data/lib/bundler/lazy_specification.rb +74 -0
  26. data/lib/bundler/lockfile_parser.rb +108 -0
  27. data/lib/bundler/remote_specification.rb +59 -0
  28. data/lib/bundler/resolver.rb +464 -0
  29. data/lib/bundler/rubygems_ext.rb +237 -0
  30. data/lib/bundler/rubygems_integration.rb +349 -0
  31. data/lib/bundler/runtime.rb +152 -0
  32. data/lib/bundler/settings.rb +115 -0
  33. data/lib/bundler/setup.rb +23 -0
  34. data/lib/bundler/shared_helpers.rb +71 -0
  35. data/lib/bundler/source.rb +708 -0
  36. data/lib/bundler/spec_set.rb +135 -0
  37. data/lib/bundler/templates/Executable +16 -0
  38. data/lib/bundler/templates/Gemfile +4 -0
  39. data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
  40. data/lib/bundler/templates/newgem/Rakefile.tt +1 -0
  41. data/lib/bundler/templates/newgem/bin/newgem.tt +3 -0
  42. data/lib/bundler/templates/newgem/gitignore.tt +4 -0
  43. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +9 -0
  44. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
  45. data/lib/bundler/templates/newgem/newgem.gemspec.tt +24 -0
  46. data/lib/bundler/ui.rb +73 -0
  47. data/lib/bundler/vendor/thor.rb +358 -0
  48. data/lib/bundler/vendor/thor/actions.rb +314 -0
  49. data/lib/bundler/vendor/thor/actions/create_file.rb +105 -0
  50. data/lib/bundler/vendor/thor/actions/create_link.rb +57 -0
  51. data/lib/bundler/vendor/thor/actions/directory.rb +93 -0
  52. data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
  53. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +270 -0
  54. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +109 -0
  55. data/lib/bundler/vendor/thor/base.rb +576 -0
  56. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
  57. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  58. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
  59. data/lib/bundler/vendor/thor/error.rb +30 -0
  60. data/lib/bundler/vendor/thor/group.rb +273 -0
  61. data/lib/bundler/vendor/thor/invocation.rb +168 -0
  62. data/lib/bundler/vendor/thor/parser.rb +4 -0
  63. data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
  64. data/lib/bundler/vendor/thor/parser/arguments.rb +161 -0
  65. data/lib/bundler/vendor/thor/parser/option.rb +120 -0
  66. data/lib/bundler/vendor/thor/parser/options.rb +175 -0
  67. data/lib/bundler/vendor/thor/rake_compat.rb +66 -0
  68. data/lib/bundler/vendor/thor/runner.rb +309 -0
  69. data/lib/bundler/vendor/thor/shell.rb +88 -0
  70. data/lib/bundler/vendor/thor/shell/basic.rb +302 -0
  71. data/lib/bundler/vendor/thor/shell/color.rb +108 -0
  72. data/lib/bundler/vendor/thor/shell/html.rb +121 -0
  73. data/lib/bundler/vendor/thor/task.rb +113 -0
  74. data/lib/bundler/vendor/thor/util.rb +229 -0
  75. data/lib/bundler/vendor/thor/version.rb +3 -0
  76. data/lib/bundler/vendored_thor.rb +7 -0
  77. data/lib/bundler/version.rb +6 -0
  78. data/lib/bundler/vlad.rb +11 -0
  79. data/man/bundle-config.ronn +90 -0
  80. data/man/bundle-exec.ronn +111 -0
  81. data/man/bundle-install.ronn +317 -0
  82. data/man/bundle-package.ronn +59 -0
  83. data/man/bundle-update.ronn +176 -0
  84. data/man/bundle.ronn +80 -0
  85. data/man/gemfile.5.ronn +284 -0
  86. data/man/index.txt +6 -0
  87. data/spec/bundler/gem_helper_spec.rb +143 -0
  88. data/spec/cache/gems_spec.rb +230 -0
  89. data/spec/cache/git_spec.rb +12 -0
  90. data/spec/cache/path_spec.rb +27 -0
  91. data/spec/cache/platform_spec.rb +57 -0
  92. data/spec/install/deploy_spec.rb +197 -0
  93. data/spec/install/deprecated_spec.rb +37 -0
  94. data/spec/install/gems/c_ext_spec.rb +48 -0
  95. data/spec/install/gems/env_spec.rb +107 -0
  96. data/spec/install/gems/flex_spec.rb +313 -0
  97. data/spec/install/gems/groups_spec.rb +259 -0
  98. data/spec/install/gems/packed_spec.rb +84 -0
  99. data/spec/install/gems/platform_spec.rb +192 -0
  100. data/spec/install/gems/resolving_spec.rb +72 -0
  101. data/spec/install/gems/simple_case_spec.rb +770 -0
  102. data/spec/install/gems/sudo_spec.rb +74 -0
  103. data/spec/install/gems/win32_spec.rb +26 -0
  104. data/spec/install/gemspec_spec.rb +125 -0
  105. data/spec/install/git_spec.rb +570 -0
  106. data/spec/install/invalid_spec.rb +35 -0
  107. data/spec/install/path_spec.rb +405 -0
  108. data/spec/install/upgrade_spec.rb +26 -0
  109. data/spec/lock/git_spec.rb +35 -0
  110. data/spec/lock/lockfile_spec.rb +739 -0
  111. data/spec/other/check_spec.rb +221 -0
  112. data/spec/other/config_spec.rb +40 -0
  113. data/spec/other/console_spec.rb +54 -0
  114. data/spec/other/exec_spec.rb +248 -0
  115. data/spec/other/ext_spec.rb +37 -0
  116. data/spec/other/help_spec.rb +39 -0
  117. data/spec/other/init_spec.rb +40 -0
  118. data/spec/other/newgem_spec.rb +46 -0
  119. data/spec/other/open_spec.rb +35 -0
  120. data/spec/other/show_spec.rb +82 -0
  121. data/spec/quality_spec.rb +62 -0
  122. data/spec/resolver/basic_spec.rb +20 -0
  123. data/spec/resolver/platform_spec.rb +82 -0
  124. data/spec/runtime/executable_spec.rb +110 -0
  125. data/spec/runtime/load_spec.rb +107 -0
  126. data/spec/runtime/platform_spec.rb +90 -0
  127. data/spec/runtime/require_spec.rb +231 -0
  128. data/spec/runtime/setup_spec.rb +730 -0
  129. data/spec/runtime/with_clean_env_spec.rb +15 -0
  130. data/spec/spec_helper.rb +92 -0
  131. data/spec/support/builders.rb +597 -0
  132. data/spec/support/helpers.rb +239 -0
  133. data/spec/support/indexes.rb +112 -0
  134. data/spec/support/matchers.rb +77 -0
  135. data/spec/support/path.rb +71 -0
  136. data/spec/support/platforms.rb +53 -0
  137. data/spec/support/ruby_ext.rb +20 -0
  138. data/spec/support/rubygems_ext.rb +37 -0
  139. data/spec/support/rubygems_hax/platform.rb +11 -0
  140. data/spec/support/sudo.rb +21 -0
  141. data/spec/update/gems_spec.rb +122 -0
  142. data/spec/update/git_spec.rb +196 -0
  143. data/spec/update/source_spec.rb +51 -0
  144. metadata +296 -0
@@ -0,0 +1,108 @@
1
+ require "strscan"
2
+
3
+ module Bundler
4
+ class LockfileParser
5
+ attr_reader :sources, :dependencies, :specs, :platforms
6
+
7
+ def initialize(lockfile)
8
+ @platforms = []
9
+ @sources = []
10
+ @dependencies = []
11
+ @specs = []
12
+ @state = :source
13
+
14
+ lockfile.split(/(\r?\n)+/).each do |line|
15
+ if line == "DEPENDENCIES"
16
+ @state = :dependency
17
+ elsif line == "PLATFORMS"
18
+ @state = :platform
19
+ else
20
+ send("parse_#{@state}", line)
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ TYPES = {
28
+ "GIT" => Bundler::Source::Git,
29
+ "GEM" => Bundler::Source::Rubygems,
30
+ "PATH" => Bundler::Source::Path
31
+ }
32
+
33
+ def parse_source(line)
34
+ case line
35
+ when "GIT", "GEM", "PATH"
36
+ @current_source = nil
37
+ @opts, @type = {}, line
38
+ when " specs:"
39
+ @current_source = TYPES[@type].from_lock(@opts)
40
+ @sources << @current_source
41
+ when /^ ([a-z]+): (.*)$/i
42
+ value = $2
43
+ value = true if value == "true"
44
+ value = false if value == "false"
45
+
46
+ key = $1
47
+
48
+ if @opts[key]
49
+ @opts[key] = Array(@opts[key])
50
+ @opts[key] << value
51
+ else
52
+ @opts[key] = value
53
+ end
54
+ else
55
+ parse_spec(line)
56
+ end
57
+ end
58
+
59
+ NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
60
+
61
+ def parse_dependency(line)
62
+ if line =~ %r{^ {2}#{NAME_VERSION}(!)?$}
63
+ name, version, pinned = $1, $2, $4
64
+ version = version.split(",").map { |d| d.strip } if version
65
+
66
+ dep = Bundler::Dependency.new(name, version)
67
+
68
+ if pinned && dep.name != 'bundler'
69
+ spec = @specs.find { |s| s.name == dep.name }
70
+ dep.source = spec.source if spec
71
+
72
+ # Path sources need to know what the default name / version
73
+ # to use in the case that there are no gemspecs present. A fake
74
+ # gemspec is created based on the version set on the dependency
75
+ # TODO: Use the version from the spec instead of from the dependency
76
+ if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
77
+ dep.source.name = name
78
+ dep.source.version = $1
79
+ end
80
+ end
81
+
82
+ @dependencies << dep
83
+ end
84
+ end
85
+
86
+ def parse_spec(line)
87
+ if line =~ %r{^ {4}#{NAME_VERSION}$}
88
+ name, version = $1, Gem::Version.new($2)
89
+ platform = $3 ? Gem::Platform.new($3) : Gem::Platform::RUBY
90
+ @current_spec = LazySpecification.new(name, version, platform)
91
+ @current_spec.source = @current_source
92
+ @specs << @current_spec
93
+ elsif line =~ %r{^ {6}#{NAME_VERSION}$}
94
+ name, version = $1, $2
95
+ version = version.split(',').map { |d| d.strip } if version
96
+ dep = Gem::Dependency.new(name, version)
97
+ @current_spec.dependencies << dep
98
+ end
99
+ end
100
+
101
+ def parse_platform(line)
102
+ if line =~ /^ (.*)$/
103
+ @platforms << Gem::Platform.new($1)
104
+ end
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,59 @@
1
+ require "uri"
2
+ require "rubygems/spec_fetcher"
3
+
4
+ module Bundler
5
+ # Represents a lazily loaded gem specification, where the full specification
6
+ # is on the source server in rubygems' "quick" index. The proxy object is to
7
+ # be seeded with what we're given from the source's abbreviated index - the
8
+ # full specification will only be fetched when necesary.
9
+ class RemoteSpecification
10
+ include MatchPlatform
11
+
12
+ attr_reader :name, :version, :platform
13
+ attr_accessor :source
14
+
15
+ def initialize(name, version, platform, source_uri)
16
+ @name = name
17
+ @version = version
18
+ @platform = platform
19
+ @source_uri = source_uri
20
+ end
21
+
22
+ # Needed before installs, since the arch matters then and quick
23
+ # specs don't bother to include the arch in the platform string
24
+ def fetch_platform
25
+ @platform = _remote_specification.platform
26
+ end
27
+
28
+ def full_name
29
+ if platform == Gem::Platform::RUBY or platform.nil? then
30
+ "#{@name}-#{@version}"
31
+ else
32
+ "#{@name}-#{@version}-#{platform}"
33
+ end
34
+ end
35
+
36
+ # Because Rubyforge cannot be trusted to provide valid specifications
37
+ # once the remote gem is downloaded, the backend specification will
38
+ # be swapped out.
39
+ def __swap__(spec)
40
+ @specification = spec
41
+ end
42
+
43
+ private
44
+
45
+ def _remote_specification
46
+ @specification ||= begin
47
+ Gem::SpecFetcher.new.fetch_spec([@name, @version, @platform], URI(@source_uri.to_s))
48
+ end
49
+ end
50
+
51
+ def method_missing(method, *args, &blk)
52
+ if Gem::Specification.new.respond_to?(method)
53
+ _remote_specification.send(method, *args, &blk)
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,464 @@
1
+ require 'set'
2
+ # This is the latest iteration of the gem dependency resolving algorithm. As of now,
3
+ # it can resolve (as a success or failure) any set of gem dependencies we throw at it
4
+ # in a reasonable amount of time. The most iterations I've seen it take is about 150.
5
+ # The actual implementation of the algorithm is not as good as it could be yet, but that
6
+ # can come later.
7
+
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
+ module Bundler
23
+ class Resolver
24
+ ALL = Bundler::Dependency::PLATFORM_MAP.values.uniq.freeze
25
+
26
+ class SpecGroup < Array
27
+ include GemHelpers
28
+
29
+ attr_reader :activated, :required_by
30
+
31
+ def initialize(a)
32
+ super
33
+ @required_by = []
34
+ @activated = []
35
+ @dependencies = nil
36
+ @specs = {}
37
+
38
+ ALL.each do |p|
39
+ @specs[p] = reverse.find { |s| s.match_platform(p) }
40
+ end
41
+ end
42
+
43
+ def initialize_copy(o)
44
+ super
45
+ @required_by = o.required_by.dup
46
+ @activated = o.activated.dup
47
+ end
48
+
49
+ def to_specs
50
+ specs = {}
51
+
52
+ @activated.each do |p|
53
+ if s = @specs[p]
54
+ platform = generic(Gem::Platform.new(s.platform))
55
+ next if specs[platform]
56
+
57
+ lazy_spec = LazySpecification.new(name, version, platform, source)
58
+ lazy_spec.dependencies.replace s.dependencies
59
+ specs[platform] = lazy_spec
60
+ end
61
+ end
62
+ specs.values
63
+ end
64
+
65
+ def activate_platform(platform)
66
+ unless @activated.include?(platform)
67
+ @activated << platform
68
+ return __dependencies[platform] || []
69
+ end
70
+ []
71
+ end
72
+
73
+ def name
74
+ @name ||= first.name
75
+ end
76
+
77
+ def version
78
+ @version ||= first.version
79
+ end
80
+
81
+ def source
82
+ @source ||= first.source
83
+ end
84
+
85
+ def for?(platform)
86
+ @specs[platform]
87
+ end
88
+
89
+ def to_s
90
+ "#{name} (#{version})"
91
+ end
92
+
93
+ private
94
+
95
+ def __dependencies
96
+ @dependencies ||= begin
97
+ dependencies = {}
98
+ ALL.each do |p|
99
+ if spec = @specs[p]
100
+ dependencies[p] = []
101
+ spec.dependencies.each do |dep|
102
+ next if dep.type == :development
103
+ dependencies[p] << DepProxy.new(dep, p)
104
+ end
105
+ end
106
+ end
107
+ dependencies
108
+ end
109
+ end
110
+ end
111
+
112
+ attr_reader :errors
113
+
114
+ # Figures out the best possible configuration of gems that satisfies
115
+ # the list of passed dependencies and any child dependencies without
116
+ # causing any gem activation errors.
117
+ #
118
+ # ==== Parameters
119
+ # *dependencies<Gem::Dependency>:: The list of dependencies to resolve
120
+ #
121
+ # ==== Returns
122
+ # <GemBundle>,nil:: If the list of dependencies can be resolved, a
123
+ # collection of gemspecs is returned. Otherwise, nil is returned.
124
+ def self.resolve(requirements, index, source_requirements = {}, base = [])
125
+ base = SpecSet.new(base) unless base.is_a?(SpecSet)
126
+ resolver = new(index, source_requirements, base)
127
+ result = catch(:success) do
128
+ resolver.start(requirements)
129
+ raise resolver.version_conflict
130
+ nil
131
+ end
132
+ SpecSet.new(result)
133
+ end
134
+
135
+ def initialize(index, source_requirements, base)
136
+ @errors = {}
137
+ @stack = []
138
+ @base = base
139
+ @index = index
140
+ @gems_size = {}
141
+ @missing_gems = Hash.new(0)
142
+ @source_requirements = source_requirements
143
+ end
144
+
145
+ def debug
146
+ if ENV['DEBUG_RESOLVER']
147
+ debug_info = yield
148
+ debug_info = debug_info.inspect unless debug_info.is_a?(String)
149
+ $stderr.puts debug_info
150
+ end
151
+ end
152
+
153
+ def successify(activated)
154
+ activated.values.map { |s| s.to_specs }.flatten.compact
155
+ end
156
+
157
+ def start(reqs)
158
+ activated = {}
159
+
160
+ resolve(reqs, activated)
161
+ end
162
+
163
+ def resolve(reqs, activated)
164
+ # If the requirements are empty, then we are in a success state. Aka, all
165
+ # gem dependencies have been resolved.
166
+ throw :success, successify(activated) if reqs.empty?
167
+
168
+ debug { print "\e[2J\e[f" ; "==== Iterating ====\n\n" }
169
+
170
+ # Sort dependencies so that the ones that are easiest to resolve are first.
171
+ # Easiest to resolve is defined by:
172
+ # 1) Is this gem already activated?
173
+ # 2) Do the version requirements include prereleased gems?
174
+ # 3) Sort by number of gems available in the source.
175
+ reqs = reqs.sort_by do |a|
176
+ [ activated[a.name] ? 0 : 1,
177
+ a.requirement.prerelease? ? 0 : 1,
178
+ @errors[a.name] ? 0 : 1,
179
+ activated[a.name] ? 0 : gems_size(a) ]
180
+ end
181
+
182
+ debug { "Activated:\n" + activated.values.map {|a| " #{a}" }.join("\n") }
183
+ debug { "Requirements:\n" + reqs.map {|r| " #{r}"}.join("\n") }
184
+
185
+ activated = activated.dup
186
+
187
+ # Pull off the first requirement so that we can resolve it
188
+ current = reqs.shift
189
+
190
+ debug { "Attempting:\n #{current}"}
191
+
192
+ # Check if the gem has already been activated, if it has, we will make sure
193
+ # that the currently activated gem satisfies the requirement.
194
+ existing = activated[current.name]
195
+ if existing || current.name == 'bundler'
196
+ # Force the current
197
+ if current.name == 'bundler' && !existing
198
+ existing = search(DepProxy.new(Gem::Dependency.new('bundler', VERSION), Gem::Platform::RUBY)).first
199
+ raise GemNotFound, %Q{Bundler could not find gem "bundler" (#{VERSION})} unless existing
200
+ existing.required_by << existing
201
+ activated['bundler'] = existing
202
+ end
203
+
204
+ if current.requirement.satisfied_by?(existing.version)
205
+ debug { " * [SUCCESS] Already activated" }
206
+ @errors.delete(existing.name)
207
+ # Since the current requirement is satisfied, we can continue resolving
208
+ # the remaining requirements.
209
+
210
+ # I have no idea if this is the right way to do it, but let's see if it works
211
+ # The current requirement might activate some other platforms, so let's try
212
+ # adding those requirements here.
213
+ reqs.concat existing.activate_platform(current.__platform)
214
+
215
+ resolve(reqs, activated)
216
+ else
217
+ debug { " * [FAIL] Already activated" }
218
+ @errors[existing.name] = [existing, current]
219
+ debug { current.required_by.map {|d| " * #{d.name} (#{d.requirement})" }.join("\n") }
220
+ # debug { " * All current conflicts:\n" + @errors.keys.map { |c| " - #{c}" }.join("\n") }
221
+ # Since the current requirement conflicts with an activated gem, we need
222
+ # to backtrack to the current requirement's parent and try another version
223
+ # of it (maybe the current requirement won't be present anymore). If the
224
+ # current requirement is a root level requirement, we need to jump back to
225
+ # where the conflicting gem was activated.
226
+ parent = current.required_by.last
227
+ # `existing` could not respond to required_by if it is part of the base set
228
+ # of specs that was passed to the resolver (aka, instance of LazySpecification)
229
+ parent ||= existing.required_by.last if existing.respond_to?(:required_by)
230
+ # We track the spot where the current gem was activated because we need
231
+ # to keep a list of every spot a failure happened.
232
+ if parent && parent.name != 'bundler'
233
+ debug { " -> Jumping to: #{parent.name}" }
234
+ required_by = existing.respond_to?(:required_by) && existing.required_by.last
235
+ throw parent.name, required_by && required_by.name
236
+ else
237
+ # The original set of dependencies conflict with the base set of specs
238
+ # passed to the resolver. This is by definition an impossible resolve.
239
+ raise version_conflict
240
+ end
241
+ end
242
+ else
243
+ # There are no activated gems for the current requirement, so we are going
244
+ # to find all gems that match the current requirement and try them in decending
245
+ # order. We also need to keep a set of all conflicts that happen while trying
246
+ # this gem. This is so that if no versions work, we can figure out the best
247
+ # place to backtrack to.
248
+ conflicts = Set.new
249
+
250
+ # Fetch all gem versions matching the requirement
251
+ #
252
+ # TODO: Warn / error when no matching versions are found.
253
+ matching_versions = search(current)
254
+
255
+ if matching_versions.empty?
256
+ if current.required_by.empty?
257
+ if base = @base[current.name] and !base.empty?
258
+ version = base.first.version
259
+ message = "You have requested:\n" \
260
+ " #{current.name} #{current.requirement}\n\n" \
261
+ "The bundle currently has #{current.name} locked at #{version}.\n" \
262
+ "Try running `bundle update #{current.name}`"
263
+ elsif current.source
264
+ name = current.name
265
+ versions = @source_requirements[name][name].map { |s| s.version }
266
+ message = "Could not find gem '#{current}' in #{current.source}.\n"
267
+ if versions.any?
268
+ message << "Source contains '#{name}' at: #{versions.join(', ')}"
269
+ else
270
+ message << "Source does not contain any versions of '#{current}'"
271
+ end
272
+ else
273
+ message = "Could not find gem '#{current}' "
274
+ if @index.sources.include?(Bundler::Source::Rubygems)
275
+ message << "in any of the gem sources listed in your Gemfile."
276
+ else
277
+ message << "in the gems available on this machine."
278
+ end
279
+ end
280
+ raise GemNotFound, message
281
+ else
282
+ @errors[current.name] = [nil, current]
283
+ end
284
+ end
285
+
286
+ matching_versions.reverse_each do |spec_group|
287
+ conflict = resolve_requirement(spec_group, current, reqs.dup, activated.dup)
288
+ conflicts << conflict if conflict
289
+ end
290
+ # If the current requirement is a root level gem and we have conflicts, we
291
+ # can figure out the best spot to backtrack to.
292
+ if current.required_by.empty? && !conflicts.empty?
293
+ # Check the current "catch" stack for the first one that is included in the
294
+ # conflicts set. That is where the parent of the conflicting gem was required.
295
+ # By jumping back to this spot, we can try other version of the parent of
296
+ # the conflicting gem, hopefully finding a combination that activates correctly.
297
+ @stack.reverse_each do |savepoint|
298
+ if conflicts.include?(savepoint)
299
+ debug { " -> Jumping to: #{savepoint}" }
300
+ throw savepoint
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ def resolve_requirement(spec_group, requirement, reqs, activated)
308
+ # We are going to try activating the spec. We need to keep track of stack of
309
+ # requirements that got us to the point of activating this gem.
310
+ spec_group.required_by.replace requirement.required_by
311
+ spec_group.required_by << requirement
312
+
313
+ activated[spec_group.name] = spec_group
314
+ debug { " Activating: #{spec_group.name} (#{spec_group.version})" }
315
+ debug { spec_group.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\n") }
316
+
317
+ dependencies = spec_group.activate_platform(requirement.__platform)
318
+
319
+ # Now, we have to loop through all child dependencies and add them to our
320
+ # array of requirements.
321
+ debug { " Dependencies"}
322
+ dependencies.each do |dep|
323
+ next if dep.type == :development
324
+ debug { " * #{dep.name} (#{dep.requirement})" }
325
+ dep.required_by.replace(requirement.required_by)
326
+ dep.required_by << requirement
327
+ reqs << dep
328
+ end
329
+
330
+ # We create a savepoint and mark it by the name of the requirement that caused
331
+ # the gem to be activated. If the activated gem ever conflicts, we are able to
332
+ # jump back to this point and try another version of the gem.
333
+ length = @stack.length
334
+ @stack << requirement.name
335
+ retval = catch(requirement.name) do
336
+ resolve(reqs, activated)
337
+ end
338
+ # Since we're doing a lot of throw / catches. A push does not necessarily match
339
+ # up to a pop. So, we simply slice the stack back to what it was before the catch
340
+ # block.
341
+ @stack.slice!(length..-1)
342
+ retval
343
+ end
344
+
345
+ def gems_size(dep)
346
+ @gems_size[dep] ||= search(dep).size
347
+ end
348
+
349
+ def search(dep)
350
+ if base = @base[dep.name] and base.any?
351
+ reqs = [dep.requirement.as_list, base.first.version.to_s].flatten.compact
352
+ d = Gem::Dependency.new(base.first.name, *reqs)
353
+ else
354
+ d = dep.dep
355
+ end
356
+ index = @source_requirements[d.name] || @index
357
+ results = index.search_for_all_platforms(d, @base[d.name])
358
+
359
+ if results.any?
360
+ version = results.first.version
361
+ nested = [[]]
362
+ results.each do |spec|
363
+ if spec.version != version
364
+ nested << []
365
+ version = spec.version
366
+ end
367
+ nested.last << spec
368
+ end
369
+ nested.map { |a| SpecGroup.new(a) }.select { |sg| sg.for?(dep.__platform) }
370
+ else
371
+ []
372
+ end
373
+ end
374
+
375
+ def clean_req(req)
376
+ if req.to_s.include?(">= 0")
377
+ req.to_s.gsub(/ \(.*?\)$/, '')
378
+ else
379
+ req.to_s.gsub(/\, (runtime|development)\)$/, ')')
380
+ end
381
+ end
382
+
383
+ def version_conflict
384
+ VersionConflict.new(errors.keys, error_message)
385
+ end
386
+
387
+ # For a given conflicted requirement, print out what exactly went wrong
388
+ def gem_message(requirement)
389
+ m = ""
390
+
391
+ # A requirement that is required by itself is actually in the Gemfile, and does
392
+ # not "depend on" itself
393
+ if requirement.required_by.first && requirement.required_by.first.name != requirement.name
394
+ m << " #{clean_req(requirement.required_by.first)} depends on\n"
395
+ m << " #{clean_req(requirement)}\n"
396
+ else
397
+ m << " #{clean_req(requirement)}\n"
398
+ end
399
+ m << "\n"
400
+ end
401
+
402
+ def error_message
403
+ errors.inject("") do |o, (conflict, (origin, requirement))|
404
+
405
+ # origin is the SpecSet of specs from the Gemfile that is conflicted with
406
+ if origin
407
+
408
+ o << %{Bundler could not find compatible versions for gem "#{origin.name}":\n}
409
+ o << " In Gemfile:\n"
410
+
411
+ o << gem_message(requirement)
412
+
413
+ # If the origin is "bundler", the conflict is us
414
+ if origin.name == "bundler"
415
+ o << " Current Bundler version:\n"
416
+ other_bundler_required = !requirement.requirement.satisfied_by?(origin.version)
417
+ # If the origin is a LockfileParser, it does not respond_to :required_by
418
+ elsif !origin.respond_to?(:required_by) || !(origin.required_by.first)
419
+ o << " In snapshot (Gemfile.lock):\n"
420
+ end
421
+
422
+ o << gem_message(origin)
423
+
424
+ # If the bundle wants a newer bundler than the running bundler, explain
425
+ if origin.name == "bundler" && other_bundler_required
426
+ o << "This Gemfile requires a different version of Bundler.\n"
427
+ o << "Perhaps you need to update Bundler by running `gem install bundler`?"
428
+ end
429
+
430
+ # origin is nil if the required gem and version cannot be found in any of
431
+ # the specified sources
432
+ else
433
+
434
+ # if the gem cannot be found because of a version conflict between lockfile and gemfile,
435
+ # print a useful error that suggests running `bundle update`, which may fix things
436
+ #
437
+ # @base is a SpecSet of the gems in the lockfile
438
+ # conflict is the name of the gem that could not be found
439
+ if locked = @base[conflict].first
440
+ o << "Bundler could not find compatible versions for gem #{conflict.inspect}:\n"
441
+ o << " In snapshot (Gemfile.lock):\n"
442
+ o << " #{clean_req(locked)}\n\n"
443
+
444
+ o << " In Gemfile:\n"
445
+ o << gem_message(requirement)
446
+ o << "Running `bundle update` will rebuild your snapshot from scratch, using only\n"
447
+ o << "the gems in your Gemfile, which may resolve the conflict.\n"
448
+
449
+ # the rest of the time, the gem cannot be found because it does not exist in the known sources
450
+ else
451
+ if requirement.required_by.first
452
+ o << "Could not find gem '#{clean_req(requirement)}', which is required by "
453
+ o << "gem '#{clean_req(requirement.required_by.first)}', in any of the sources."
454
+ else
455
+ o << "Could not find gem '#{clean_req(requirement)} in any of the sources\n"
456
+ end
457
+ end
458
+
459
+ end
460
+ o
461
+ end
462
+ end
463
+ end
464
+ end