carat 1.9.9.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +24 -0
  5. data/CHANGELOG.md +2006 -0
  6. data/CODE_OF_CONDUCT.md +40 -0
  7. data/CONTRIBUTING.md +23 -0
  8. data/DEVELOPMENT.md +119 -0
  9. data/ISSUES.md +96 -0
  10. data/LICENSE.md +23 -0
  11. data/README.md +32 -0
  12. data/Rakefile +308 -0
  13. data/bin/carat +21 -0
  14. data/bin/carat_ruby +56 -0
  15. data/carat.gemspec +32 -0
  16. data/lib/carat.rb +446 -0
  17. data/lib/carat/anonymizable_uri.rb +32 -0
  18. data/lib/carat/capistrano.rb +16 -0
  19. data/lib/carat/cli.rb +407 -0
  20. data/lib/carat/cli/binstubs.rb +38 -0
  21. data/lib/carat/cli/cache.rb +35 -0
  22. data/lib/carat/cli/check.rb +35 -0
  23. data/lib/carat/cli/clean.rb +26 -0
  24. data/lib/carat/cli/common.rb +56 -0
  25. data/lib/carat/cli/config.rb +84 -0
  26. data/lib/carat/cli/console.rb +38 -0
  27. data/lib/carat/cli/exec.rb +44 -0
  28. data/lib/carat/cli/gem.rb +195 -0
  29. data/lib/carat/cli/init.rb +33 -0
  30. data/lib/carat/cli/inject.rb +33 -0
  31. data/lib/carat/cli/install.rb +156 -0
  32. data/lib/carat/cli/open.rb +23 -0
  33. data/lib/carat/cli/outdated.rb +80 -0
  34. data/lib/carat/cli/package.rb +45 -0
  35. data/lib/carat/cli/platform.rb +43 -0
  36. data/lib/carat/cli/show.rb +74 -0
  37. data/lib/carat/cli/update.rb +73 -0
  38. data/lib/carat/cli/viz.rb +27 -0
  39. data/lib/carat/constants.rb +5 -0
  40. data/lib/carat/current_ruby.rb +183 -0
  41. data/lib/carat/definition.rb +628 -0
  42. data/lib/carat/dep_proxy.rb +43 -0
  43. data/lib/carat/dependency.rb +110 -0
  44. data/lib/carat/deployment.rb +59 -0
  45. data/lib/carat/deprecate.rb +15 -0
  46. data/lib/carat/dsl.rb +331 -0
  47. data/lib/carat/endpoint_specification.rb +76 -0
  48. data/lib/carat/env.rb +75 -0
  49. data/lib/carat/environment.rb +42 -0
  50. data/lib/carat/fetcher.rb +423 -0
  51. data/lib/carat/friendly_errors.rb +85 -0
  52. data/lib/carat/gem_helper.rb +180 -0
  53. data/lib/carat/gem_helpers.rb +26 -0
  54. data/lib/carat/gem_installer.rb +9 -0
  55. data/lib/carat/gem_path_manipulation.rb +8 -0
  56. data/lib/carat/gem_tasks.rb +2 -0
  57. data/lib/carat/graph.rb +169 -0
  58. data/lib/carat/index.rb +197 -0
  59. data/lib/carat/injector.rb +64 -0
  60. data/lib/carat/installer.rb +339 -0
  61. data/lib/carat/lazy_specification.rb +83 -0
  62. data/lib/carat/lockfile_parser.rb +167 -0
  63. data/lib/carat/match_platform.rb +13 -0
  64. data/lib/carat/psyched_yaml.rb +26 -0
  65. data/lib/carat/remote_specification.rb +57 -0
  66. data/lib/carat/resolver.rb +334 -0
  67. data/lib/carat/retry.rb +60 -0
  68. data/lib/carat/ruby_dsl.rb +11 -0
  69. data/lib/carat/ruby_version.rb +117 -0
  70. data/lib/carat/rubygems_ext.rb +170 -0
  71. data/lib/carat/rubygems_integration.rb +619 -0
  72. data/lib/carat/runtime.rb +289 -0
  73. data/lib/carat/settings.rb +208 -0
  74. data/lib/carat/setup.rb +24 -0
  75. data/lib/carat/shared_helpers.rb +149 -0
  76. data/lib/carat/similarity_detector.rb +63 -0
  77. data/lib/carat/source.rb +46 -0
  78. data/lib/carat/source/git.rb +294 -0
  79. data/lib/carat/source/git/git_proxy.rb +162 -0
  80. data/lib/carat/source/path.rb +226 -0
  81. data/lib/carat/source/path/installer.rb +43 -0
  82. data/lib/carat/source/rubygems.rb +381 -0
  83. data/lib/carat/source_list.rb +101 -0
  84. data/lib/carat/spec_set.rb +154 -0
  85. data/lib/carat/ssl_certs/.document +1 -0
  86. data/lib/carat/ssl_certs/AddTrustExternalCARoot-2048.pem +25 -0
  87. data/lib/carat/ssl_certs/AddTrustExternalCARoot.pem +32 -0
  88. data/lib/carat/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +14 -0
  89. data/lib/carat/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +23 -0
  90. data/lib/carat/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +28 -0
  91. data/lib/carat/ssl_certs/GeoTrustGlobalCA.pem +20 -0
  92. data/lib/carat/ssl_certs/certificate_manager.rb +66 -0
  93. data/lib/carat/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
  94. data/lib/carat/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
  95. data/lib/carat/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
  96. data/lib/carat/templates/Executable +16 -0
  97. data/lib/carat/templates/Executable.standalone +12 -0
  98. data/lib/carat/templates/Gemfile +4 -0
  99. data/lib/carat/templates/newgem/.travis.yml.tt +3 -0
  100. data/lib/carat/templates/newgem/CODE_OF_CONDUCT.md.tt +13 -0
  101. data/lib/carat/templates/newgem/Gemfile.tt +4 -0
  102. data/lib/carat/templates/newgem/LICENSE.txt.tt +21 -0
  103. data/lib/carat/templates/newgem/README.md.tt +39 -0
  104. data/lib/carat/templates/newgem/Rakefile.tt +25 -0
  105. data/lib/carat/templates/newgem/bin/console.tt +14 -0
  106. data/lib/carat/templates/newgem/bin/setup.tt +7 -0
  107. data/lib/carat/templates/newgem/exe/newgem.tt +3 -0
  108. data/lib/carat/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
  109. data/lib/carat/templates/newgem/ext/newgem/newgem.c.tt +9 -0
  110. data/lib/carat/templates/newgem/ext/newgem/newgem.h.tt +6 -0
  111. data/lib/carat/templates/newgem/gitignore.tt +16 -0
  112. data/lib/carat/templates/newgem/lib/newgem.rb.tt +12 -0
  113. data/lib/carat/templates/newgem/lib/newgem/version.rb.tt +7 -0
  114. data/lib/carat/templates/newgem/newgem.gemspec.tt +43 -0
  115. data/lib/carat/templates/newgem/rspec.tt +2 -0
  116. data/lib/carat/templates/newgem/spec/newgem_spec.rb.tt +11 -0
  117. data/lib/carat/templates/newgem/spec/spec_helper.rb.tt +2 -0
  118. data/lib/carat/templates/newgem/test/minitest_helper.rb.tt +4 -0
  119. data/lib/carat/templates/newgem/test/test_newgem.rb.tt +11 -0
  120. data/lib/carat/ui.rb +7 -0
  121. data/lib/carat/ui/rg_proxy.rb +21 -0
  122. data/lib/carat/ui/shell.rb +103 -0
  123. data/lib/carat/ui/silent.rb +44 -0
  124. data/lib/carat/vendor/molinillo/lib/molinillo.rb +5 -0
  125. data/lib/carat/vendor/molinillo/lib/molinillo/dependency_graph.rb +266 -0
  126. data/lib/carat/vendor/molinillo/lib/molinillo/errors.rb +69 -0
  127. data/lib/carat/vendor/molinillo/lib/molinillo/gem_metadata.rb +3 -0
  128. data/lib/carat/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +90 -0
  129. data/lib/carat/vendor/molinillo/lib/molinillo/modules/ui.rb +63 -0
  130. data/lib/carat/vendor/molinillo/lib/molinillo/resolution.rb +415 -0
  131. data/lib/carat/vendor/molinillo/lib/molinillo/resolver.rb +43 -0
  132. data/lib/carat/vendor/molinillo/lib/molinillo/state.rb +43 -0
  133. data/lib/carat/vendor/net/http/faster.rb +26 -0
  134. data/lib/carat/vendor/net/http/persistent.rb +1230 -0
  135. data/lib/carat/vendor/net/http/persistent/ssl_reuse.rb +128 -0
  136. data/lib/carat/vendor/thor/lib/thor.rb +484 -0
  137. data/lib/carat/vendor/thor/lib/thor/actions.rb +319 -0
  138. data/lib/carat/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  139. data/lib/carat/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  140. data/lib/carat/vendor/thor/lib/thor/actions/directory.rb +118 -0
  141. data/lib/carat/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  142. data/lib/carat/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
  143. data/lib/carat/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
  144. data/lib/carat/vendor/thor/lib/thor/base.rb +656 -0
  145. data/lib/carat/vendor/thor/lib/thor/command.rb +133 -0
  146. data/lib/carat/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  147. data/lib/carat/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
  148. data/lib/carat/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
  149. data/lib/carat/vendor/thor/lib/thor/error.rb +32 -0
  150. data/lib/carat/vendor/thor/lib/thor/group.rb +281 -0
  151. data/lib/carat/vendor/thor/lib/thor/invocation.rb +178 -0
  152. data/lib/carat/vendor/thor/lib/thor/line_editor.rb +17 -0
  153. data/lib/carat/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  154. data/lib/carat/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  155. data/lib/carat/vendor/thor/lib/thor/parser.rb +4 -0
  156. data/lib/carat/vendor/thor/lib/thor/parser/argument.rb +73 -0
  157. data/lib/carat/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  158. data/lib/carat/vendor/thor/lib/thor/parser/option.rb +125 -0
  159. data/lib/carat/vendor/thor/lib/thor/parser/options.rb +218 -0
  160. data/lib/carat/vendor/thor/lib/thor/rake_compat.rb +71 -0
  161. data/lib/carat/vendor/thor/lib/thor/runner.rb +322 -0
  162. data/lib/carat/vendor/thor/lib/thor/shell.rb +81 -0
  163. data/lib/carat/vendor/thor/lib/thor/shell/basic.rb +421 -0
  164. data/lib/carat/vendor/thor/lib/thor/shell/color.rb +149 -0
  165. data/lib/carat/vendor/thor/lib/thor/shell/html.rb +126 -0
  166. data/lib/carat/vendor/thor/lib/thor/util.rb +267 -0
  167. data/lib/carat/vendor/thor/lib/thor/version.rb +3 -0
  168. data/lib/carat/vendored_fileutils.rb +9 -0
  169. data/lib/carat/vendored_molinillo.rb +2 -0
  170. data/lib/carat/vendored_persistent.rb +11 -0
  171. data/lib/carat/vendored_thor.rb +3 -0
  172. data/lib/carat/version.rb +6 -0
  173. data/lib/carat/vlad.rb +11 -0
  174. data/lib/carat/worker.rb +73 -0
  175. data/man/carat-config.ronn +178 -0
  176. data/man/carat-exec.ronn +136 -0
  177. data/man/carat-install.ronn +383 -0
  178. data/man/carat-package.ronn +66 -0
  179. data/man/carat-platform.ronn +42 -0
  180. data/man/carat-update.ronn +188 -0
  181. data/man/carat.ronn +98 -0
  182. data/man/gemfile.5.ronn +473 -0
  183. data/man/index.txt +7 -0
  184. metadata +321 -0
@@ -0,0 +1,63 @@
1
+ module Carat::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,415 @@
1
+ module Carat::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
+ # @return [Integer] the number of resolver iterations in between calls to
77
+ # {#resolver_ui}'s {UI#indicate_progress} method
78
+ attr_accessor :iteration_rate
79
+ private :iteration_rate
80
+
81
+ # @return [Time] the time at which resolution began
82
+ attr_accessor :started_at
83
+ private :started_at
84
+
85
+ # @return [Array<ResolutionState>] the stack of states for the resolution
86
+ attr_accessor :states
87
+ private :states
88
+
89
+ private
90
+
91
+ # Sets up the resolution process
92
+ # @return [void]
93
+ def start_resolution
94
+ @started_at = Time.now
95
+
96
+ states.push(initial_state)
97
+
98
+ debug { "Starting resolution (#{@started_at})" }
99
+ resolver_ui.before_resolution
100
+ end
101
+
102
+ # Ends the resolution process
103
+ # @return [void]
104
+ def end_resolution
105
+ resolver_ui.after_resolution
106
+ debug do
107
+ "Finished resolution (#{@iteration_counter} steps) " \
108
+ "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
109
+ end
110
+ debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
111
+ debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
112
+ end
113
+
114
+ require 'carat/vendor/molinillo/lib/molinillo/state'
115
+ require 'carat/vendor/molinillo/lib/molinillo/modules/specification_provider'
116
+
117
+ ResolutionState.new.members.each do |member|
118
+ define_method member do |*args, &block|
119
+ state.send(member, *args, &block)
120
+ end
121
+ end
122
+
123
+ SpecificationProvider.instance_methods(false).each do |instance_method|
124
+ define_method instance_method do |*args, &block|
125
+ begin
126
+ specification_provider.send(instance_method, *args, &block)
127
+ rescue NoSuchDependencyError => error
128
+ if state
129
+ vertex = activated.vertex_named(name_for error.dependency)
130
+ error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
131
+ error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
132
+ end
133
+ raise
134
+ end
135
+ end
136
+ end
137
+
138
+ # Processes the topmost available {RequirementState} on the stack
139
+ # @return [void]
140
+ def process_topmost_state
141
+ if possibility
142
+ attempt_to_activate
143
+ else
144
+ create_conflict if state.is_a? PossibilityState
145
+ unwind_for_conflict until possibility && state.is_a?(DependencyState)
146
+ end
147
+ end
148
+
149
+ # @return [Object] the current possibility that the resolution is trying
150
+ # to activate
151
+ def possibility
152
+ possibilities.last
153
+ end
154
+
155
+ # @return [RequirementState] the current state the resolution is
156
+ # operating upon
157
+ def state
158
+ states.last
159
+ end
160
+
161
+ # Creates the initial state for the resolution, based upon the
162
+ # {#requested} dependencies
163
+ # @return [DependencyState] the initial state for the resolution
164
+ def initial_state
165
+ graph = DependencyGraph.new.tap do |dg|
166
+ original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } }
167
+ end
168
+
169
+ requirements = sort_dependencies(original_requested, graph, {})
170
+ initial_requirement = requirements.shift
171
+ DependencyState.new(
172
+ initial_requirement && name_for(initial_requirement),
173
+ requirements,
174
+ graph,
175
+ initial_requirement,
176
+ initial_requirement && search_for(initial_requirement),
177
+ 0,
178
+ {}
179
+ )
180
+ end
181
+
182
+ # Unwinds the states stack because a conflict has been encountered
183
+ # @return [void]
184
+ def unwind_for_conflict
185
+ debug(depth) { "Unwinding for conflict: #{requirement}" }
186
+ conflicts.tap do |c|
187
+ states.slice!((state_index_for_unwind + 1)..-1)
188
+ raise VersionConflict.new(c) unless state
189
+ state.conflicts = c
190
+ end
191
+ end
192
+
193
+ # @return [Integer] The index to which the resolution should unwind in the
194
+ # case of conflict.
195
+ def state_index_for_unwind
196
+ current_requirement = requirement
197
+ existing_requirement = requirement_for_existing_name(name)
198
+ until current_requirement.nil?
199
+ current_state = find_state_for(current_requirement)
200
+ return states.index(current_state) if state_any?(current_state)
201
+ current_requirement = parent_of(current_requirement)
202
+ end
203
+
204
+ until existing_requirement.nil?
205
+ existing_state = find_state_for(existing_requirement)
206
+ return states.index(existing_state) if state_any?(existing_state)
207
+ existing_requirement = parent_of(existing_requirement)
208
+ end
209
+ -1
210
+ end
211
+
212
+ # @return [Object] the requirement that led to `requirement` being added
213
+ # to the list of requirements.
214
+ def parent_of(requirement)
215
+ return nil unless requirement
216
+ seen = false
217
+ state = states.reverse_each.find do |s|
218
+ seen ||= s.requirement == requirement
219
+ seen && s.requirement != requirement && !s.requirements.include?(requirement)
220
+ end
221
+ state && state.requirement
222
+ end
223
+
224
+ # @return [Object] the requirement that led to a version of a possibility
225
+ # with the given name being activated.
226
+ def requirement_for_existing_name(name)
227
+ return nil unless activated.vertex_named(name).payload
228
+ states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement
229
+ end
230
+
231
+ # @return [ResolutionState] the state whose `requirement` is the given
232
+ # `requirement`.
233
+ def find_state_for(requirement)
234
+ return nil unless requirement
235
+ states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) }
236
+ end
237
+
238
+ # @return [Boolean] whether or not the given state has any possibilities
239
+ # left.
240
+ def state_any?(state)
241
+ state && state.possibilities.any?
242
+ end
243
+
244
+ # @return [Conflict] a {Conflict} that reflects the failure to activate
245
+ # the {#possibility} in conjunction with the current {#state}
246
+ def create_conflict
247
+ vertex = activated.vertex_named(name)
248
+ requirements = {
249
+ name_for_explicit_dependency_source => vertex.explicit_requirements,
250
+ name_for_locking_dependency_source => Array(locked_requirement_named(name)),
251
+ }
252
+ vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) }
253
+ conflicts[name] = Conflict.new(
254
+ requirement,
255
+ Hash[requirements.select { |_, r| !r.empty? }],
256
+ vertex.payload,
257
+ possibility,
258
+ locked_requirement_named(name),
259
+ requirement_trees
260
+ )
261
+ end
262
+
263
+ # @return [Array<Array<Object>>] The different requirement
264
+ # trees that led to every requirement for the current spec.
265
+ def requirement_trees
266
+ activated.vertex_named(name).requirements.map { |r| requirement_tree_for(r) }
267
+ end
268
+
269
+ # @return [Array<Object>] the list of requirements that led to
270
+ # `requirement` being required.
271
+ def requirement_tree_for(requirement)
272
+ tree = []
273
+ while requirement
274
+ tree.unshift(requirement)
275
+ requirement = parent_of(requirement)
276
+ end
277
+ tree
278
+ end
279
+
280
+ # Indicates progress roughly once every second
281
+ # @return [void]
282
+ def indicate_progress
283
+ @iteration_counter += 1
284
+ @progress_rate ||= resolver_ui.progress_rate
285
+ if iteration_rate.nil?
286
+ if Time.now - started_at >= @progress_rate
287
+ self.iteration_rate = @iteration_counter
288
+ end
289
+ end
290
+
291
+ if iteration_rate && (@iteration_counter % iteration_rate) == 0
292
+ resolver_ui.indicate_progress
293
+ end
294
+ end
295
+
296
+ # Calls the {#resolver_ui}'s {UI#debug} method
297
+ # @param [Integer] depth the depth of the {#states} stack
298
+ # @param [Proc] block a block that yields a {#to_s}
299
+ # @return [void]
300
+ def debug(depth = 0, &block)
301
+ resolver_ui.debug(depth, &block)
302
+ end
303
+
304
+ # Attempts to activate the current {#possibility}
305
+ # @return [void]
306
+ def attempt_to_activate
307
+ debug(depth) { 'Attempting to activate ' + possibility.to_s }
308
+ existing_node = activated.vertex_named(name)
309
+ if existing_node.payload
310
+ debug(depth) { "Found existing spec (#{existing_node.payload})" }
311
+ attempt_to_activate_existing_spec(existing_node)
312
+ else
313
+ attempt_to_activate_new_spec
314
+ end
315
+ end
316
+
317
+ # Attempts to activate the current {#possibility} (given that it has
318
+ # already been activated)
319
+ # @return [void]
320
+ def attempt_to_activate_existing_spec(existing_node)
321
+ existing_spec = existing_node.payload
322
+ if requirement_satisfied_by?(requirement, activated, existing_spec)
323
+ new_requirements = requirements.dup
324
+ push_state_for_requirements(new_requirements)
325
+ else
326
+ return if attempt_to_swap_possibility
327
+ create_conflict
328
+ debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
329
+ unwind_for_conflict
330
+ end
331
+ end
332
+
333
+ # Attempts to swp the current {#possibility} with the already-activated
334
+ # spec with the given name
335
+ # @return [Boolean] Whether the possibility was swapped into {#activated}
336
+ def attempt_to_swap_possibility
337
+ swapped = activated.dup
338
+ swapped.vertex_named(name).payload = possibility
339
+ return unless swapped.vertex_named(name).requirements.
340
+ all? { |r| requirement_satisfied_by?(r, swapped, possibility) }
341
+ attempt_to_activate_new_spec
342
+ end
343
+
344
+ # Attempts to activate the current {#possibility} (given that it hasn't
345
+ # already been activated)
346
+ # @return [void]
347
+ def attempt_to_activate_new_spec
348
+ satisfied = begin
349
+ locked_requirement = locked_requirement_named(name)
350
+ requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility)
351
+ locked_spec_satisfied = !locked_requirement ||
352
+ requirement_satisfied_by?(locked_requirement, activated, possibility)
353
+ debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied
354
+ debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
355
+ requested_spec_satisfied && locked_spec_satisfied
356
+ end
357
+ if satisfied
358
+ activate_spec
359
+ else
360
+ create_conflict
361
+ unwind_for_conflict
362
+ end
363
+ end
364
+
365
+ # @param [String] requirement_name the spec name to search for
366
+ # @return [Object] the locked spec named `requirement_name`, if one
367
+ # is found on {#base}
368
+ def locked_requirement_named(requirement_name)
369
+ vertex = base.vertex_named(requirement_name)
370
+ vertex && vertex.payload
371
+ end
372
+
373
+ # Add the current {#possibility} to the dependency graph of the current
374
+ # {#state}
375
+ # @return [void]
376
+ def activate_spec
377
+ conflicts.delete(name)
378
+ debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
379
+ vertex = activated.vertex_named(name)
380
+ vertex.payload = possibility
381
+ require_nested_dependencies_for(possibility)
382
+ end
383
+
384
+ # Requires the dependencies that the recently activated spec has
385
+ # @param [Object] activated_spec the specification that has just been
386
+ # activated
387
+ # @return [void]
388
+ def require_nested_dependencies_for(activated_spec)
389
+ nested_dependencies = dependencies_for(activated_spec)
390
+ debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" }
391
+ nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d }
392
+
393
+ push_state_for_requirements(requirements + nested_dependencies)
394
+ end
395
+
396
+ # Pushes a new {DependencyState} that encapsulates both existing and new
397
+ # requirements
398
+ # @param [Array] new_requirements
399
+ # @return [void]
400
+ def push_state_for_requirements(new_requirements)
401
+ new_requirements = sort_dependencies(new_requirements.uniq, activated, conflicts)
402
+ new_requirement = new_requirements.shift
403
+ states.push DependencyState.new(
404
+ new_requirement ? name_for(new_requirement) : '',
405
+ new_requirements,
406
+ activated.dup,
407
+ new_requirement,
408
+ new_requirement ? search_for(new_requirement) : [],
409
+ depth,
410
+ conflicts.dup
411
+ )
412
+ end
413
+ end
414
+ end
415
+ end