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
@@ -0,0 +1,69 @@
1
+ module Bundler::Molinillo
2
+ # An error that occurred during the resolution process
3
+ class ResolverError < StandardError; end
4
+
5
+ # An error caused by searching for a dependency that is completely unknown,
6
+ # i.e. has no versions available whatsoever.
7
+ class NoSuchDependencyError < ResolverError
8
+ # @return [Object] the dependency that could not be found
9
+ attr_accessor :dependency
10
+
11
+ # @return [Array<Object>] the specifications that depended upon {#dependency}
12
+ attr_accessor :required_by
13
+
14
+ # @param [Object] dependency @see {#dependency}
15
+ # @param [Array<Object>] required_by @see {#required_by}
16
+ def initialize(dependency, required_by = [])
17
+ @dependency = dependency
18
+ @required_by = required_by
19
+ super()
20
+ end
21
+
22
+ def message
23
+ sources = required_by.map { |r| "`#{r}`" }.join(' and ')
24
+ message = "Unable to find a specification for `#{dependency}`"
25
+ message << " depended upon by #{sources}" unless sources.empty?
26
+ message
27
+ end
28
+ end
29
+
30
+ # An error caused by attempting to fulfil a dependency that was circular
31
+ #
32
+ # @note This exception will be thrown iff a {Vertex} is added to a
33
+ # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
34
+ # existing {DependencyGraph::Vertex}
35
+ class CircularDependencyError < ResolverError
36
+ # [Set<Object>] the dependencies responsible for causing the error
37
+ attr_reader :dependencies
38
+
39
+ # @param [Array<DependencyGraph::Vertex>] nodes the nodes in the dependency
40
+ # that caused the error
41
+ def initialize(nodes)
42
+ super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}"
43
+ @dependencies = nodes.map(&:payload).to_set
44
+ end
45
+ end
46
+
47
+ # An error caused by conflicts in version
48
+ class VersionConflict < ResolverError
49
+ # @return [{String => Resolution::Conflict}] the conflicts that caused
50
+ # resolution to fail
51
+ attr_reader :conflicts
52
+
53
+ # @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
54
+ def initialize(conflicts)
55
+ pairs = []
56
+ conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting|
57
+ conflicting.each do |source, conflict_requirements|
58
+ conflict_requirements.each do |c|
59
+ pairs << [c, source]
60
+ end
61
+ end
62
+ end
63
+
64
+ super "Unable to satisfy the following requirements:\n\n" \
65
+ "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
66
+ @conflicts = conflicts
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Bundler::Molinillo
2
+ VERSION = '0.2.1'
3
+ end
@@ -0,0 +1,90 @@
1
+ module Bundler::Molinillo
2
+ # Provides information about specifcations and dependencies to the resolver,
3
+ # allowing the {Resolver} class to remain generic while still providing power
4
+ # and flexibility.
5
+ #
6
+ # This module contains the methods that users of Bundler::Molinillo must to implement,
7
+ # using knowledge of their own model classes.
8
+ module SpecificationProvider
9
+ # Search for the specifications that match the given dependency.
10
+ # The specifications in the returned array will be considered in reverse
11
+ # order, so the latest version ought to be last.
12
+ # @note This method should be 'pure', i.e. the return value should depend
13
+ # only on the `dependency` parameter.
14
+ #
15
+ # @param [Object] dependency
16
+ # @return [Array<Object>] the specifications that satisfy the given
17
+ # `dependency`.
18
+ def search_for(dependency)
19
+ []
20
+ end
21
+
22
+ # Returns the dependencies of `specification`.
23
+ # @note This method should be 'pure', i.e. the return value should depend
24
+ # only on the `specification` parameter.
25
+ #
26
+ # @param [Object] specification
27
+ # @return [Array<Object>] the dependencies that are required by the given
28
+ # `specification`.
29
+ def dependencies_for(specification)
30
+ []
31
+ end
32
+
33
+ # Determines whether the given `requirement` is satisfied by the given
34
+ # `spec`, in the context of the current `activated` dependency graph.
35
+ #
36
+ # @param [Object] requirement
37
+ # @param [DependencyGraph] activated the current dependency graph in the
38
+ # resolution process.
39
+ # @param [Object] spec
40
+ # @return [Boolean] whether `requirement` is satisfied by `spec` in the
41
+ # context of the current `activated` dependency graph.
42
+ def requirement_satisfied_by?(requirement, activated, spec)
43
+ true
44
+ end
45
+
46
+ # Returns the name for the given `dependency`.
47
+ # @note This method should be 'pure', i.e. the return value should depend
48
+ # only on the `dependency` parameter.
49
+ #
50
+ # @param [Object] dependency
51
+ # @return [String] the name for the given `dependency`.
52
+ def name_for(dependency)
53
+ dependency.to_s
54
+ end
55
+
56
+ # @return [String] the name of the source of explicit dependencies, i.e.
57
+ # those passed to {Resolver#resolve} directly.
58
+ def name_for_explicit_dependency_source
59
+ 'user-specified dependency'
60
+ end
61
+
62
+ # @return [String] the name of the source of 'locked' dependencies, i.e.
63
+ # those passed to {Resolver#resolve} directly as the `base`
64
+ def name_for_locking_dependency_source
65
+ 'Lockfile'
66
+ end
67
+
68
+ # Sort dependencies so that the ones that are easiest to resolve are first.
69
+ # Easiest to resolve is (usually) defined by:
70
+ # 1) Is this dependency already activated?
71
+ # 2) How relaxed are the requirements?
72
+ # 3) Are there any conflicts for this dependency?
73
+ # 4) How many possibilities are there to satisfy this dependency?
74
+ #
75
+ # @param [Array<Object>] dependencies
76
+ # @param [DependencyGraph] activated the current dependency graph in the
77
+ # resolution process.
78
+ # @param [{String => Array<Conflict>}] conflicts
79
+ # @return [Array<Object>] a sorted copy of `dependencies`.
80
+ def sort_dependencies(dependencies, activated, conflicts)
81
+ dependencies.sort_by do |dependency|
82
+ name = name_for(dependency)
83
+ [
84
+ activated.vertex_named(name).payload ? 0 : 1,
85
+ conflicts[name] ? 0 : 1,
86
+ ]
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,63 @@
1
+ module Bundler::Molinillo
2
+ # Conveys information about the resolution process to a user.
3
+ module UI
4
+ # The {IO} object that should be used to print output. `STDOUT`, by default.
5
+ #
6
+ # @return [IO]
7
+ def output
8
+ STDOUT
9
+ end
10
+
11
+ # Called roughly every {#progress_rate}, this method should convey progress
12
+ # to the user.
13
+ #
14
+ # @return [void]
15
+ def indicate_progress
16
+ output.print '.' unless debug?
17
+ end
18
+
19
+ # How often progress should be conveyed to the user via
20
+ # {#indicate_progress}, in seconds. A third of a second, by default.
21
+ #
22
+ # @return [Float]
23
+ def progress_rate
24
+ 0.33
25
+ end
26
+
27
+ # Called before resolution begins.
28
+ #
29
+ # @return [void]
30
+ def before_resolution
31
+ output.print 'Resolving dependencies...'
32
+ end
33
+
34
+ # Called after resolution ends (either successfully or with an error).
35
+ # By default, prints a newline.
36
+ #
37
+ # @return [void]
38
+ def after_resolution
39
+ output.puts
40
+ end
41
+
42
+ # Conveys debug information to the user.
43
+ #
44
+ # @param [Integer] depth the current depth of the resolution process.
45
+ # @return [void]
46
+ def debug(depth = 0)
47
+ if debug?
48
+ debug_info = yield
49
+ debug_info = debug_info.inspect unless debug_info.is_a?(String)
50
+ output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
51
+ end
52
+ end
53
+
54
+ # Whether or not debug messages should be printed.
55
+ # By default, whether or not the `MOLINILLO_DEBUG` environment variable is
56
+ # set.
57
+ #
58
+ # @return [Boolean]
59
+ def debug?
60
+ @debug_mode ||= ENV['MOLINILLO_DEBUG']
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,412 @@
1
+ module Bundler::Molinillo
2
+ class Resolver
3
+ # A specific resolution from a given {Resolver}
4
+ class Resolution
5
+ # A conflict that the resolution process encountered
6
+ # @attr [Object] requirement the requirement that immediately led to the conflict
7
+ # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
8
+ # @attr [Object, nil] existing the existing spec that was in conflict with
9
+ # the {#possibility}
10
+ # @attr [Object] possibility the spec that was unable to be activated due
11
+ # to a conflict
12
+ # @attr [Object] locked_requirement the relevant locking requirement.
13
+ # @attr [Array<Array<Object>>] requirement_trees the different requirement
14
+ # trees that led to every requirement for the conflicting name.
15
+ Conflict = Struct.new(
16
+ :requirement,
17
+ :requirements,
18
+ :existing,
19
+ :possibility,
20
+ :locked_requirement,
21
+ :requirement_trees
22
+ )
23
+
24
+ # @return [SpecificationProvider] the provider that knows about
25
+ # dependencies, requirements, specifications, versions, etc.
26
+ attr_reader :specification_provider
27
+
28
+ # @return [UI] the UI that knows how to communicate feedback about the
29
+ # resolution process back to the user
30
+ attr_reader :resolver_ui
31
+
32
+ # @return [DependencyGraph] the base dependency graph to which
33
+ # dependencies should be 'locked'
34
+ attr_reader :base
35
+
36
+ # @return [Array] the dependencies that were explicitly required
37
+ attr_reader :original_requested
38
+
39
+ # @param [SpecificationProvider] specification_provider
40
+ # see {#specification_provider}
41
+ # @param [UI] resolver_ui see {#resolver_ui}
42
+ # @param [Array] requested see {#original_requested}
43
+ # @param [DependencyGraph] base see {#base}
44
+ def initialize(specification_provider, resolver_ui, requested, base)
45
+ @specification_provider = specification_provider
46
+ @resolver_ui = resolver_ui
47
+ @original_requested = requested
48
+ @base = base
49
+ @states = []
50
+ @iteration_counter = 0
51
+ end
52
+
53
+ # Resolves the {#original_requested} dependencies into a full dependency
54
+ # graph
55
+ # @raise [ResolverError] if successful resolution is impossible
56
+ # @return [DependencyGraph] the dependency graph of successfully resolved
57
+ # dependencies
58
+ def resolve
59
+ start_resolution
60
+
61
+ while state
62
+ break unless state.requirements.any? || state.requirement
63
+ indicate_progress
64
+ if state.respond_to?(:pop_possibility_state) # DependencyState
65
+ debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
66
+ state.pop_possibility_state.tap { |s| states.push(s) if s }
67
+ end
68
+ process_topmost_state
69
+ end
70
+
71
+ activated.freeze
72
+ ensure
73
+ end_resolution
74
+ end
75
+
76
+ private
77
+
78
+ # Sets up the resolution process
79
+ # @return [void]
80
+ def start_resolution
81
+ @started_at = Time.now
82
+
83
+ states.push(initial_state)
84
+
85
+ debug { "Starting resolution (#{@started_at})" }
86
+ resolver_ui.before_resolution
87
+ end
88
+
89
+ # Ends the resolution process
90
+ # @return [void]
91
+ def end_resolution
92
+ resolver_ui.after_resolution
93
+ debug do
94
+ "Finished resolution (#{@iteration_counter} steps) " \
95
+ "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
96
+ end
97
+ debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
98
+ debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
99
+ end
100
+
101
+ require 'molinillo/state'
102
+ require 'molinillo/modules/specification_provider'
103
+
104
+ # @return [Integer] the number of resolver iterations in between calls to
105
+ # {#resolver_ui}'s {UI#indicate_progress} method
106
+ attr_accessor :iteration_rate
107
+
108
+ # @return [Time] the time at which resolution began
109
+ attr_accessor :started_at
110
+
111
+ # @return [Array<ResolutionState>] the stack of states for the resolution
112
+ attr_accessor :states
113
+
114
+ ResolutionState.new.members.each do |member|
115
+ define_method member do |*args, &block|
116
+ state.send(member, *args, &block)
117
+ end
118
+ end
119
+
120
+ SpecificationProvider.instance_methods(false).each do |instance_method|
121
+ define_method instance_method do |*args, &block|
122
+ begin
123
+ specification_provider.send(instance_method, *args, &block)
124
+ rescue NoSuchDependencyError => error
125
+ if state
126
+ vertex = activated.vertex_named(name_for error.dependency)
127
+ error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
128
+ error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
129
+ end
130
+ raise
131
+ end
132
+ end
133
+ end
134
+
135
+ # Processes the topmost available {RequirementState} on the stack
136
+ # @return [void]
137
+ def process_topmost_state
138
+ if possibility
139
+ attempt_to_activate
140
+ else
141
+ create_conflict if state.is_a? PossibilityState
142
+ unwind_for_conflict until possibility && state.is_a?(DependencyState)
143
+ end
144
+ end
145
+
146
+ # @return [Object] the current possibility that the resolution is trying
147
+ # to activate
148
+ def possibility
149
+ possibilities.last
150
+ end
151
+
152
+ # @return [RequirementState] the current state the resolution is
153
+ # operating upon
154
+ def state
155
+ states.last
156
+ end
157
+
158
+ # Creates the initial state for the resolution, based upon the
159
+ # {#requested} dependencies
160
+ # @return [DependencyState] the initial state for the resolution
161
+ def initial_state
162
+ graph = DependencyGraph.new.tap do |dg|
163
+ original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } }
164
+ end
165
+
166
+ requirements = sort_dependencies(original_requested, graph, {})
167
+ initial_requirement = requirements.shift
168
+ DependencyState.new(
169
+ initial_requirement && name_for(initial_requirement),
170
+ requirements,
171
+ graph,
172
+ initial_requirement,
173
+ initial_requirement && search_for(initial_requirement),
174
+ 0,
175
+ {}
176
+ )
177
+ end
178
+
179
+ # Unwinds the states stack because a conflict has been encountered
180
+ # @return [void]
181
+ def unwind_for_conflict
182
+ debug(depth) { "Unwinding for conflict: #{requirement}" }
183
+ conflicts.tap do |c|
184
+ states.slice!((state_index_for_unwind + 1)..-1)
185
+ raise VersionConflict.new(c) unless state
186
+ state.conflicts = c
187
+ end
188
+ end
189
+
190
+ # @return [Integer] The index to which the resolution should unwind in the
191
+ # case of conflict.
192
+ def state_index_for_unwind
193
+ current_requirement = requirement
194
+ existing_requirement = requirement_for_existing_name(name)
195
+ until current_requirement.nil?
196
+ current_state = find_state_for(current_requirement)
197
+ return states.index(current_state) if state_any?(current_state)
198
+ current_requirement = parent_of(current_requirement)
199
+ end
200
+
201
+ until existing_requirement.nil?
202
+ existing_state = find_state_for(existing_requirement)
203
+ return states.index(existing_state) if state_any?(existing_state)
204
+ existing_requirement = parent_of(existing_requirement)
205
+ end
206
+ -1
207
+ end
208
+
209
+ # @return [Object] the requirement that led to `requirement` being added
210
+ # to the list of requirements.
211
+ def parent_of(requirement)
212
+ return nil unless requirement
213
+ seen = false
214
+ state = states.reverse_each.find do |s|
215
+ seen ||= s.requirement == requirement
216
+ seen && s.requirement != requirement && !s.requirements.include?(requirement)
217
+ end
218
+ state && state.requirement
219
+ end
220
+
221
+ # @return [Object] the requirement that led to a version of a possibility
222
+ # with the given name being activated.
223
+ def requirement_for_existing_name(name)
224
+ return nil unless activated.vertex_named(name).payload
225
+ states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement
226
+ end
227
+
228
+ # @return [ResolutionState] the state whose `requirement` is the given
229
+ # `requirement`.
230
+ def find_state_for(requirement)
231
+ return nil unless requirement
232
+ states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) }
233
+ end
234
+
235
+ # @return [Boolean] whether or not the given state has any possibilities
236
+ # left.
237
+ def state_any?(state)
238
+ state && state.possibilities.any?
239
+ end
240
+
241
+ # @return [Conflict] a {Conflict} that reflects the failure to activate
242
+ # the {#possibility} in conjunction with the current {#state}
243
+ def create_conflict
244
+ vertex = activated.vertex_named(name)
245
+ requirements = {
246
+ name_for_explicit_dependency_source => vertex.explicit_requirements,
247
+ name_for_locking_dependency_source => Array(locked_requirement_named(name)),
248
+ }
249
+ vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) }
250
+ conflicts[name] = Conflict.new(
251
+ requirement,
252
+ Hash[requirements.select { |_, r| !r.empty? }],
253
+ vertex.payload,
254
+ possibility,
255
+ locked_requirement_named(name),
256
+ requirement_trees
257
+ )
258
+ end
259
+
260
+ # @return [Array<Array<Object>>] The different requirement
261
+ # trees that led to every requirement for the current spec.
262
+ def requirement_trees
263
+ activated.vertex_named(name).requirements.map { |r| requirement_tree_for(r) }
264
+ end
265
+
266
+ # @return [Array<Object>] the list of requirements that led to
267
+ # `requirement` being required.
268
+ def requirement_tree_for(requirement)
269
+ tree = []
270
+ while requirement
271
+ tree.unshift(requirement)
272
+ requirement = parent_of(requirement)
273
+ end
274
+ tree
275
+ end
276
+
277
+ # Indicates progress roughly once every second
278
+ # @return [void]
279
+ def indicate_progress
280
+ @iteration_counter += 1
281
+ @progress_rate ||= resolver_ui.progress_rate
282
+ if iteration_rate.nil?
283
+ if Time.now - started_at >= @progress_rate
284
+ self.iteration_rate = @iteration_counter
285
+ end
286
+ end
287
+
288
+ if iteration_rate && (@iteration_counter % iteration_rate) == 0
289
+ resolver_ui.indicate_progress
290
+ end
291
+ end
292
+
293
+ # Calls the {#resolver_ui}'s {UI#debug} method
294
+ # @param [Integer] depth the depth of the {#states} stack
295
+ # @param [Proc] block a block that yields a {#to_s}
296
+ # @return [void]
297
+ def debug(depth = 0, &block)
298
+ resolver_ui.debug(depth, &block)
299
+ end
300
+
301
+ # Attempts to activate the current {#possibility}
302
+ # @return [void]
303
+ def attempt_to_activate
304
+ debug(depth) { 'Attempting to activate ' + possibility.to_s }
305
+ existing_node = activated.vertex_named(name)
306
+ if existing_node.payload
307
+ debug(depth) { "Found existing spec (#{existing_node.payload})" }
308
+ attempt_to_activate_existing_spec(existing_node)
309
+ else
310
+ attempt_to_activate_new_spec
311
+ end
312
+ end
313
+
314
+ # Attempts to activate the current {#possibility} (given that it has
315
+ # already been activated)
316
+ # @return [void]
317
+ def attempt_to_activate_existing_spec(existing_node)
318
+ existing_spec = existing_node.payload
319
+ if requirement_satisfied_by?(requirement, activated, existing_spec)
320
+ new_requirements = requirements.dup
321
+ push_state_for_requirements(new_requirements)
322
+ else
323
+ return if attempt_to_swap_possibility
324
+ create_conflict
325
+ debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
326
+ unwind_for_conflict
327
+ end
328
+ end
329
+
330
+ # Attempts to swp the current {#possibility} with the already-activated
331
+ # spec with the given name
332
+ # @return [Boolean] Whether the possibility was swapped into {#activated}
333
+ def attempt_to_swap_possibility
334
+ swapped = activated.dup
335
+ swapped.vertex_named(name).payload = possibility
336
+ return unless swapped.vertex_named(name).requirements.
337
+ all? { |r| requirement_satisfied_by?(r, swapped, possibility) }
338
+ attempt_to_activate_new_spec
339
+ end
340
+
341
+ # Attempts to activate the current {#possibility} (given that it hasn't
342
+ # already been activated)
343
+ # @return [void]
344
+ def attempt_to_activate_new_spec
345
+ satisfied = begin
346
+ locked_requirement = locked_requirement_named(name)
347
+ requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility)
348
+ locked_spec_satisfied = !locked_requirement ||
349
+ requirement_satisfied_by?(locked_requirement, activated, possibility)
350
+ debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied
351
+ debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
352
+ requested_spec_satisfied && locked_spec_satisfied
353
+ end
354
+ if satisfied
355
+ activate_spec
356
+ else
357
+ create_conflict
358
+ unwind_for_conflict
359
+ end
360
+ end
361
+
362
+ # @param [String] requirement_name the spec name to search for
363
+ # @return [Object] the locked spec named `requirement_name`, if one
364
+ # is found on {#base}
365
+ def locked_requirement_named(requirement_name)
366
+ vertex = base.vertex_named(requirement_name)
367
+ vertex && vertex.payload
368
+ end
369
+
370
+ # Add the current {#possibility} to the dependency graph of the current
371
+ # {#state}
372
+ # @return [void]
373
+ def activate_spec
374
+ conflicts.delete(name)
375
+ debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
376
+ vertex = activated.vertex_named(name)
377
+ vertex.payload = possibility
378
+ require_nested_dependencies_for(possibility)
379
+ end
380
+
381
+ # Requires the dependencies that the recently activated spec has
382
+ # @param [Object] activated_spec the specification that has just been
383
+ # activated
384
+ # @return [void]
385
+ def require_nested_dependencies_for(activated_spec)
386
+ nested_dependencies = dependencies_for(activated_spec)
387
+ debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" }
388
+ nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d }
389
+
390
+ push_state_for_requirements(requirements + nested_dependencies)
391
+ end
392
+
393
+ # Pushes a new {DependencyState} that encapsulates both existing and new
394
+ # requirements
395
+ # @param [Array] new_requirements
396
+ # @return [void]
397
+ def push_state_for_requirements(new_requirements)
398
+ new_requirements = sort_dependencies(new_requirements.uniq, activated, conflicts)
399
+ new_requirement = new_requirements.shift
400
+ states.push DependencyState.new(
401
+ new_requirement ? name_for(new_requirement) : '',
402
+ new_requirements,
403
+ activated.dup,
404
+ new_requirement,
405
+ new_requirement ? search_for(new_requirement) : [],
406
+ depth,
407
+ conflicts.dup
408
+ )
409
+ end
410
+ end
411
+ end
412
+ end