rubygems-update 3.3.26 → 3.4.1

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 (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/CONTRIBUTING.md +24 -1
  4. data/Manifest.txt +30 -27
  5. data/POLICIES.md +10 -8
  6. data/README.md +2 -2
  7. data/bin/gem +1 -4
  8. data/bin/update_rubygems +1 -1
  9. data/bundler/CHANGELOG.md +59 -0
  10. data/bundler/README.md +2 -2
  11. data/bundler/bundler.gemspec +2 -2
  12. data/bundler/exe/bundle +1 -4
  13. data/bundler/lib/bundler/build_metadata.rb +2 -2
  14. data/bundler/lib/bundler/cli/add.rb +1 -1
  15. data/bundler/lib/bundler/cli/check.rb +1 -1
  16. data/bundler/lib/bundler/cli/common.rb +1 -0
  17. data/bundler/lib/bundler/cli/console.rb +2 -2
  18. data/bundler/lib/bundler/cli/doctor.rb +4 -6
  19. data/bundler/lib/bundler/cli/gem.rb +62 -40
  20. data/bundler/lib/bundler/cli/install.rb +2 -3
  21. data/bundler/lib/bundler/cli/lock.rb +8 -5
  22. data/bundler/lib/bundler/cli/outdated.rb +1 -3
  23. data/bundler/lib/bundler/cli/viz.rb +1 -1
  24. data/bundler/lib/bundler/cli.rb +43 -2
  25. data/bundler/lib/bundler/compact_index_client/cache.rb +1 -1
  26. data/bundler/lib/bundler/compact_index_client/updater.rb +40 -39
  27. data/bundler/lib/bundler/constants.rb +1 -1
  28. data/bundler/lib/bundler/definition.rb +61 -31
  29. data/bundler/lib/bundler/dependency.rb +12 -11
  30. data/bundler/lib/bundler/digest.rb +1 -1
  31. data/bundler/lib/bundler/dsl.rb +1 -1
  32. data/bundler/lib/bundler/env.rb +1 -1
  33. data/bundler/lib/bundler/environment_preserver.rb +1 -0
  34. data/bundler/lib/bundler/errors.rb +1 -11
  35. data/bundler/lib/bundler/fetcher/compact_index.rb +9 -11
  36. data/bundler/lib/bundler/fetcher/dependency.rb +1 -1
  37. data/bundler/lib/bundler/fetcher/downloader.rb +2 -5
  38. data/bundler/lib/bundler/fetcher.rb +2 -6
  39. data/bundler/lib/bundler/force_platform.rb +18 -0
  40. data/bundler/lib/bundler/friendly_errors.rb +0 -3
  41. data/bundler/lib/bundler/gem_version_promoter.rb +52 -86
  42. data/bundler/lib/bundler/graph.rb +3 -3
  43. data/bundler/lib/bundler/index.rb +5 -13
  44. data/bundler/lib/bundler/injector.rb +1 -1
  45. data/bundler/lib/bundler/inline.rb +2 -2
  46. data/bundler/lib/bundler/installer/parallel_installer.rb +0 -31
  47. data/bundler/lib/bundler/installer.rb +6 -16
  48. data/bundler/lib/bundler/lazy_specification.rb +5 -1
  49. data/bundler/lib/bundler/lockfile_parser.rb +5 -5
  50. data/bundler/lib/bundler/man/bundle-add.1 +1 -1
  51. data/bundler/lib/bundler/man/bundle-binstubs.1 +1 -1
  52. data/bundler/lib/bundler/man/bundle-cache.1 +1 -1
  53. data/bundler/lib/bundler/man/bundle-check.1 +1 -1
  54. data/bundler/lib/bundler/man/bundle-clean.1 +1 -1
  55. data/bundler/lib/bundler/man/bundle-config.1 +1 -1
  56. data/bundler/lib/bundler/man/bundle-console.1 +1 -1
  57. data/bundler/lib/bundler/man/bundle-doctor.1 +1 -1
  58. data/bundler/lib/bundler/man/bundle-exec.1 +1 -1
  59. data/bundler/lib/bundler/man/bundle-gem.1 +27 -37
  60. data/bundler/lib/bundler/man/bundle-gem.1.ronn +5 -5
  61. data/bundler/lib/bundler/man/bundle-help.1 +1 -1
  62. data/bundler/lib/bundler/man/bundle-info.1 +1 -1
  63. data/bundler/lib/bundler/man/bundle-init.1 +1 -1
  64. data/bundler/lib/bundler/man/bundle-inject.1 +1 -1
  65. data/bundler/lib/bundler/man/bundle-install.1 +1 -30
  66. data/bundler/lib/bundler/man/bundle-install.1.ronn +0 -29
  67. data/bundler/lib/bundler/man/bundle-list.1 +1 -1
  68. data/bundler/lib/bundler/man/bundle-lock.1 +1 -1
  69. data/bundler/lib/bundler/man/bundle-open.1 +1 -1
  70. data/bundler/lib/bundler/man/bundle-outdated.1 +1 -1
  71. data/bundler/lib/bundler/man/bundle-platform.1 +2 -2
  72. data/bundler/lib/bundler/man/bundle-platform.1.ronn +1 -1
  73. data/bundler/lib/bundler/man/bundle-plugin.1 +1 -1
  74. data/bundler/lib/bundler/man/bundle-pristine.1 +1 -1
  75. data/bundler/lib/bundler/man/bundle-remove.1 +1 -1
  76. data/bundler/lib/bundler/man/bundle-show.1 +1 -1
  77. data/bundler/lib/bundler/man/bundle-update.1 +1 -1
  78. data/bundler/lib/bundler/man/bundle-version.1 +1 -1
  79. data/bundler/lib/bundler/man/bundle-viz.1 +1 -1
  80. data/bundler/lib/bundler/man/bundle.1 +1 -1
  81. data/bundler/lib/bundler/man/gemfile.5 +1 -1
  82. data/bundler/lib/bundler/mirror.rb +5 -7
  83. data/bundler/lib/bundler/plugin/index.rb +4 -4
  84. data/bundler/lib/bundler/plugin/installer/rubygems.rb +0 -4
  85. data/bundler/lib/bundler/resolver/base.rb +7 -11
  86. data/bundler/lib/bundler/resolver/candidate.rb +92 -0
  87. data/bundler/lib/bundler/resolver/incompatibility.rb +15 -0
  88. data/bundler/lib/bundler/resolver/package.rb +63 -0
  89. data/bundler/lib/bundler/resolver/root.rb +25 -0
  90. data/bundler/lib/bundler/resolver/spec_group.rb +26 -36
  91. data/bundler/lib/bundler/resolver.rb +294 -277
  92. data/bundler/lib/bundler/rubygems_ext.rb +11 -6
  93. data/bundler/lib/bundler/rubygems_gem_installer.rb +4 -2
  94. data/bundler/lib/bundler/rubygems_integration.rb +1 -9
  95. data/bundler/lib/bundler/runtime.rb +1 -5
  96. data/bundler/lib/bundler/settings.rb +0 -6
  97. data/bundler/lib/bundler/shared_helpers.rb +1 -0
  98. data/bundler/lib/bundler/source/git/git_proxy.rb +193 -67
  99. data/bundler/lib/bundler/source/git.rb +15 -17
  100. data/bundler/lib/bundler/source/metadata.rb +0 -1
  101. data/bundler/lib/bundler/source/path/installer.rb +1 -22
  102. data/bundler/lib/bundler/source/path.rb +5 -5
  103. data/bundler/lib/bundler/source/rubygems.rb +13 -67
  104. data/bundler/lib/bundler/source_list.rb +8 -2
  105. data/bundler/lib/bundler/spec_set.rb +7 -9
  106. data/bundler/lib/bundler/templates/Executable +1 -1
  107. data/bundler/lib/bundler/templates/Executable.bundler +4 -9
  108. data/bundler/lib/bundler/templates/Executable.standalone +2 -0
  109. data/bundler/lib/bundler/templates/newgem/Cargo.toml.tt +7 -0
  110. data/bundler/lib/bundler/templates/newgem/Gemfile.tt +3 -0
  111. data/bundler/lib/bundler/templates/newgem/README.md.tt +6 -4
  112. data/bundler/lib/bundler/templates/newgem/Rakefile.tt +2 -1
  113. data/bundler/lib/bundler/templates/newgem/circleci/config.yml.tt +12 -0
  114. data/bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +15 -0
  115. data/bundler/lib/bundler/templates/newgem/ext/newgem/{extconf.rb.tt → extconf-c.rb.tt} +0 -0
  116. data/bundler/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt +6 -0
  117. data/bundler/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +12 -0
  118. data/bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt +10 -0
  119. data/bundler/lib/bundler/templates/newgem/gitignore.tt +3 -0
  120. data/bundler/lib/bundler/templates/newgem/gitlab-ci.yml.tt +8 -0
  121. data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +8 -2
  122. data/bundler/lib/bundler/ui/shell.rb +35 -12
  123. data/bundler/lib/bundler/ui/silent.rb +21 -5
  124. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +3 -3
  125. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +0 -1
  126. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +3 -1
  127. data/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +1350 -408
  128. data/bundler/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  129. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1 -1
  130. data/bundler/lib/bundler/vendor/pub_grub/LICENSE.txt +21 -0
  131. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  132. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +189 -0
  133. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  134. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +151 -0
  135. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb +43 -0
  136. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  137. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  138. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb +19 -0
  139. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +53 -0
  140. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  141. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  142. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb +124 -0
  143. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +409 -0
  144. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +240 -0
  145. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb +178 -0
  146. data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +31 -0
  147. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +1 -1
  148. data/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +64 -16
  149. data/bundler/lib/bundler/vendor/uri/lib/uri/file.rb +7 -1
  150. data/bundler/lib/bundler/vendor/uri/lib/uri/ftp.rb +2 -1
  151. data/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +27 -7
  152. data/bundler/lib/bundler/vendor/uri/lib/uri/http.rb +40 -2
  153. data/bundler/lib/bundler/vendor/uri/lib/uri/https.rb +2 -1
  154. data/bundler/lib/bundler/vendor/uri/lib/uri/ldap.rb +1 -1
  155. data/bundler/lib/bundler/vendor/uri/lib/uri/ldaps.rb +2 -1
  156. data/bundler/lib/bundler/vendor/uri/lib/uri/mailto.rb +2 -2
  157. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +13 -7
  158. data/bundler/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +10 -5
  159. data/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  160. data/bundler/lib/bundler/vendor/uri/lib/uri/ws.rb +1 -2
  161. data/bundler/lib/bundler/vendor/uri/lib/uri/wss.rb +2 -1
  162. data/bundler/lib/bundler/vendor/uri/lib/uri.rb +3 -2
  163. data/bundler/lib/bundler/vendored_persistent.rb +1 -33
  164. data/bundler/lib/bundler/{vendored_tmpdir.rb → vendored_pub_grub.rb} +1 -1
  165. data/bundler/lib/bundler/version.rb +5 -1
  166. data/bundler/lib/bundler/worker.rb +5 -7
  167. data/bundler/lib/bundler.rb +20 -64
  168. data/lib/rubygems/command_manager.rb +2 -2
  169. data/lib/rubygems/commands/fetch_command.rb +1 -1
  170. data/lib/rubygems/commands/install_command.rb +7 -3
  171. data/lib/rubygems/commands/rdoc_command.rb +3 -2
  172. data/lib/rubygems/commands/setup_command.rb +2 -2
  173. data/lib/rubygems/commands/unpack_command.rb +1 -1
  174. data/lib/rubygems/commands/update_command.rb +1 -7
  175. data/lib/rubygems/config_file.rb +33 -0
  176. data/lib/rubygems/core_ext/kernel_warn.rb +1 -2
  177. data/lib/rubygems/defaults.rb +15 -1
  178. data/lib/rubygems/dependency.rb +4 -1
  179. data/lib/rubygems/dependency_installer.rb +24 -24
  180. data/lib/rubygems/exceptions.rb +1 -3
  181. data/lib/rubygems/ext/builder.rb +3 -3
  182. data/lib/rubygems/ext/cargo_builder/link_flag_converter.rb +9 -5
  183. data/lib/rubygems/ext/cargo_builder.rb +15 -20
  184. data/lib/rubygems/ext/ext_conf_builder.rb +2 -0
  185. data/lib/rubygems/indexer.rb +1 -1
  186. data/lib/rubygems/installer.rb +5 -5
  187. data/lib/rubygems/optparse/lib/optparse.rb +20 -15
  188. data/lib/rubygems/package/tar_header.rb +11 -11
  189. data/lib/rubygems/platform.rb +0 -2
  190. data/lib/rubygems/request_set/gem_dependency_api.rb +104 -104
  191. data/lib/rubygems/requirement.rb +7 -7
  192. data/lib/rubygems/resolver/installer_set.rb +1 -1
  193. data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +1 -1
  194. data/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +32 -26
  195. data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +1 -1
  196. data/lib/rubygems/security/policies.rb +40 -40
  197. data/lib/rubygems/security/trust_dir.rb +1 -1
  198. data/lib/rubygems/security.rb +3 -16
  199. data/lib/rubygems/source.rb +2 -2
  200. data/lib/rubygems/specification.rb +37 -49
  201. data/lib/rubygems/specification_policy.rb +14 -0
  202. data/lib/rubygems/stub_specification.rb +2 -2
  203. data/lib/rubygems/text.rb +1 -1
  204. data/lib/rubygems/tsort/lib/tsort.rb +308 -310
  205. data/lib/rubygems/update_suggestion.rb +69 -0
  206. data/lib/rubygems/util.rb +1 -5
  207. data/lib/rubygems/validator.rb +1 -1
  208. data/lib/rubygems.rb +8 -3
  209. data/rubygems-update.gemspec +2 -2
  210. data/test/rubygems/helper.rb +7 -3
  211. data/test/rubygems/test_bundled_ca.rb +1 -1
  212. data/test/rubygems/test_exit.rb +6 -0
  213. data/test/rubygems/test_gem.rb +4 -9
  214. data/test/rubygems/test_gem_bundler_version_finder.rb +2 -1
  215. data/test/rubygems/test_gem_command_manager.rb +1 -1
  216. data/test/rubygems/test_gem_commands_install_command.rb +19 -0
  217. data/test/rubygems/test_gem_commands_setup_command.rb +1 -8
  218. data/test/rubygems/test_gem_commands_update_command.rb +6 -6
  219. data/test/rubygems/test_gem_config_file.rb +1 -1
  220. data/test/rubygems/test_gem_dependency.rb +2 -0
  221. data/test/rubygems/test_gem_ext_builder.rb +3 -3
  222. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.lock +22 -32
  223. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/Cargo.toml +1 -1
  224. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +22 -32
  225. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +1 -1
  226. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/src/lib.rs +12 -0
  227. data/test/rubygems/test_gem_ext_cargo_builder.rb +22 -27
  228. data/test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb +16 -16
  229. data/test/rubygems/test_gem_ext_cargo_builder_unit.rb +0 -10
  230. data/test/rubygems/test_gem_indexer.rb +39 -20
  231. data/test/rubygems/test_gem_installer.rb +68 -2
  232. data/test/rubygems/test_gem_package_tar_header.rb +13 -13
  233. data/test/rubygems/test_gem_platform.rb +59 -60
  234. data/test/rubygems/test_gem_remote_fetcher.rb +4 -4
  235. data/test/rubygems/test_gem_request_set.rb +2 -2
  236. data/test/rubygems/test_gem_requirement.rb +1 -1
  237. data/test/rubygems/test_gem_resolver_api_set.rb +12 -12
  238. data/test/rubygems/test_gem_resolver_api_specification.rb +19 -19
  239. data/test/rubygems/test_gem_resolver_git_specification.rb +1 -1
  240. data/test/rubygems/test_gem_security_policy.rb +10 -10
  241. data/test/rubygems/test_gem_security_trust_dir.rb +2 -2
  242. data/test/rubygems/test_gem_specification.rb +50 -37
  243. data/test/rubygems/test_gem_uninstaller.rb +1 -1
  244. data/test/rubygems/test_gem_update_suggestion.rb +208 -0
  245. data/test/rubygems/test_kernel.rb +10 -8
  246. data/test/rubygems/test_require.rb +70 -55
  247. metadata +34 -31
  248. data/bundler/lib/bundler/templates/newgem/travis.yml.tt +0 -6
  249. data/bundler/lib/bundler/vendor/molinillo/LICENSE +0 -9
  250. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +0 -57
  251. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +0 -88
  252. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +0 -36
  253. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +0 -66
  254. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +0 -62
  255. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +0 -63
  256. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +0 -61
  257. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +0 -126
  258. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +0 -46
  259. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +0 -36
  260. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +0 -164
  261. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +0 -255
  262. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +0 -149
  263. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +0 -6
  264. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +0 -112
  265. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +0 -67
  266. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +0 -839
  267. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +0 -46
  268. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +0 -58
  269. data/bundler/lib/bundler/vendor/molinillo/lib/molinillo.rb +0 -11
  270. data/bundler/lib/bundler/vendor/tmpdir/lib/tmpdir.rb +0 -154
  271. data/bundler/lib/bundler/vendored_molinillo.rb +0 -4
  272. data/bundler/lib/bundler/version_ranges.rb +0 -122
@@ -1,839 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bundler::Molinillo
4
- class Resolver
5
- # A specific resolution from a given {Resolver}
6
- class Resolution
7
- # A conflict that the resolution process encountered
8
- # @attr [Object] requirement the requirement that immediately led to the conflict
9
- # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
10
- # @attr [Object, nil] existing the existing spec that was in conflict with
11
- # the {#possibility}
12
- # @attr [Object] possibility_set the set of specs that was unable to be
13
- # activated due to a conflict.
14
- # @attr [Object] locked_requirement the relevant locking requirement.
15
- # @attr [Array<Array<Object>>] requirement_trees the different requirement
16
- # trees that led to every requirement for the conflicting name.
17
- # @attr [{String=>Object}] activated_by_name the already-activated specs.
18
- # @attr [Object] underlying_error an error that has occurred during resolution, and
19
- # will be raised at the end of it if no resolution is found.
20
- Conflict = Struct.new(
21
- :requirement,
22
- :requirements,
23
- :existing,
24
- :possibility_set,
25
- :locked_requirement,
26
- :requirement_trees,
27
- :activated_by_name,
28
- :underlying_error
29
- )
30
-
31
- class Conflict
32
- # @return [Object] a spec that was unable to be activated due to a conflict
33
- def possibility
34
- possibility_set && possibility_set.latest_version
35
- end
36
- end
37
-
38
- # A collection of possibility states that share the same dependencies
39
- # @attr [Array] dependencies the dependencies for this set of possibilities
40
- # @attr [Array] possibilities the possibilities
41
- PossibilitySet = Struct.new(:dependencies, :possibilities)
42
-
43
- class PossibilitySet
44
- # String representation of the possibility set, for debugging
45
- def to_s
46
- "[#{possibilities.join(', ')}]"
47
- end
48
-
49
- # @return [Object] most up-to-date dependency in the possibility set
50
- def latest_version
51
- possibilities.last
52
- end
53
- end
54
-
55
- # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
56
- # @attr [Integer] state_index the index of the state to unwind to
57
- # @attr [Object] state_requirement the requirement of the state we're unwinding to
58
- # @attr [Array] requirement_tree for the requirement we're relaxing
59
- # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
60
- # @attr [Array] requirement_trees for the conflict
61
- # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
62
- UnwindDetails = Struct.new(
63
- :state_index,
64
- :state_requirement,
65
- :requirement_tree,
66
- :conflicting_requirements,
67
- :requirement_trees,
68
- :requirements_unwound_to_instead
69
- )
70
-
71
- class UnwindDetails
72
- include Comparable
73
-
74
- # We compare UnwindDetails when choosing which state to unwind to. If
75
- # two options have the same state_index we prefer the one most
76
- # removed from a requirement that caused the conflict. Both options
77
- # would unwind to the same state, but a `grandparent` option will
78
- # filter out fewer of its possibilities after doing so - where a state
79
- # is both a `parent` and a `grandparent` to requirements that have
80
- # caused a conflict this is the correct behaviour.
81
- # @param [UnwindDetail] other UnwindDetail to be compared
82
- # @return [Integer] integer specifying ordering
83
- def <=>(other)
84
- if state_index > other.state_index
85
- 1
86
- elsif state_index == other.state_index
87
- reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
88
- else
89
- -1
90
- end
91
- end
92
-
93
- # @return [Integer] index of state requirement in reversed requirement tree
94
- # (the conflicting requirement itself will be at position 0)
95
- def reversed_requirement_tree_index
96
- @reversed_requirement_tree_index ||=
97
- if state_requirement
98
- requirement_tree.reverse.index(state_requirement)
99
- else
100
- 999_999
101
- end
102
- end
103
-
104
- # @return [Boolean] where the requirement of the state we're unwinding
105
- # to directly caused the conflict. Note: in this case, it is
106
- # impossible for the state we're unwinding to to be a parent of
107
- # any of the other conflicting requirements (or we would have
108
- # circularity)
109
- def unwinding_to_primary_requirement?
110
- requirement_tree.last == state_requirement
111
- end
112
-
113
- # @return [Array] array of sub-dependencies to avoid when choosing a
114
- # new possibility for the state we've unwound to. Only relevant for
115
- # non-primary unwinds
116
- def sub_dependencies_to_avoid
117
- @requirements_to_avoid ||=
118
- requirement_trees.map do |tree|
119
- index = tree.index(state_requirement)
120
- tree[index + 1] if index
121
- end.compact
122
- end
123
-
124
- # @return [Array] array of all the requirements that led to the need for
125
- # this unwind
126
- def all_requirements
127
- @all_requirements ||= requirement_trees.flatten(1)
128
- end
129
- end
130
-
131
- # @return [SpecificationProvider] the provider that knows about
132
- # dependencies, requirements, specifications, versions, etc.
133
- attr_reader :specification_provider
134
-
135
- # @return [UI] the UI that knows how to communicate feedback about the
136
- # resolution process back to the user
137
- attr_reader :resolver_ui
138
-
139
- # @return [DependencyGraph] the base dependency graph to which
140
- # dependencies should be 'locked'
141
- attr_reader :base
142
-
143
- # @return [Array] the dependencies that were explicitly required
144
- attr_reader :original_requested
145
-
146
- # Initializes a new resolution.
147
- # @param [SpecificationProvider] specification_provider
148
- # see {#specification_provider}
149
- # @param [UI] resolver_ui see {#resolver_ui}
150
- # @param [Array] requested see {#original_requested}
151
- # @param [DependencyGraph] base see {#base}
152
- def initialize(specification_provider, resolver_ui, requested, base)
153
- @specification_provider = specification_provider
154
- @resolver_ui = resolver_ui
155
- @original_requested = requested
156
- @base = base
157
- @states = []
158
- @iteration_counter = 0
159
- @parents_of = Hash.new { |h, k| h[k] = [] }
160
- end
161
-
162
- # Resolves the {#original_requested} dependencies into a full dependency
163
- # graph
164
- # @raise [ResolverError] if successful resolution is impossible
165
- # @return [DependencyGraph] the dependency graph of successfully resolved
166
- # dependencies
167
- def resolve
168
- start_resolution
169
-
170
- while state
171
- break if !state.requirement && state.requirements.empty?
172
- indicate_progress
173
- if state.respond_to?(:pop_possibility_state) # DependencyState
174
- debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
175
- state.pop_possibility_state.tap do |s|
176
- if s
177
- states.push(s)
178
- activated.tag(s)
179
- end
180
- end
181
- end
182
- process_topmost_state
183
- end
184
-
185
- resolve_activated_specs
186
- ensure
187
- end_resolution
188
- end
189
-
190
- # @return [Integer] the number of resolver iterations in between calls to
191
- # {#resolver_ui}'s {UI#indicate_progress} method
192
- attr_accessor :iteration_rate
193
- private :iteration_rate
194
-
195
- # @return [Time] the time at which resolution began
196
- attr_accessor :started_at
197
- private :started_at
198
-
199
- # @return [Array<ResolutionState>] the stack of states for the resolution
200
- attr_accessor :states
201
- private :states
202
-
203
- private
204
-
205
- # Sets up the resolution process
206
- # @return [void]
207
- def start_resolution
208
- @started_at = Time.now
209
-
210
- push_initial_state
211
-
212
- debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
213
- resolver_ui.before_resolution
214
- end
215
-
216
- def resolve_activated_specs
217
- activated.vertices.each do |_, vertex|
218
- next unless vertex.payload
219
-
220
- latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
221
- vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
222
- end
223
-
224
- activated.set_payload(vertex.name, latest_version)
225
- end
226
- activated.freeze
227
- end
228
-
229
- # Ends the resolution process
230
- # @return [void]
231
- def end_resolution
232
- resolver_ui.after_resolution
233
- debug do
234
- "Finished resolution (#{@iteration_counter} steps) " \
235
- "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
236
- end
237
- debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
238
- debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
239
- end
240
-
241
- require_relative 'state'
242
- require_relative 'modules/specification_provider'
243
-
244
- require_relative 'delegates/resolution_state'
245
- require_relative 'delegates/specification_provider'
246
-
247
- include Bundler::Molinillo::Delegates::ResolutionState
248
- include Bundler::Molinillo::Delegates::SpecificationProvider
249
-
250
- # Processes the topmost available {RequirementState} on the stack
251
- # @return [void]
252
- def process_topmost_state
253
- if possibility
254
- attempt_to_activate
255
- else
256
- create_conflict
257
- unwind_for_conflict
258
- end
259
- rescue CircularDependencyError => underlying_error
260
- create_conflict(underlying_error)
261
- unwind_for_conflict
262
- end
263
-
264
- # @return [Object] the current possibility that the resolution is trying
265
- # to activate
266
- def possibility
267
- possibilities.last
268
- end
269
-
270
- # @return [RequirementState] the current state the resolution is
271
- # operating upon
272
- def state
273
- states.last
274
- end
275
-
276
- # Creates and pushes the initial state for the resolution, based upon the
277
- # {#requested} dependencies
278
- # @return [void]
279
- def push_initial_state
280
- graph = DependencyGraph.new.tap do |dg|
281
- original_requested.each do |requested|
282
- vertex = dg.add_vertex(name_for(requested), nil, true)
283
- vertex.explicit_requirements << requested
284
- end
285
- dg.tag(:initial_state)
286
- end
287
-
288
- push_state_for_requirements(original_requested, true, graph)
289
- end
290
-
291
- # Unwinds the states stack because a conflict has been encountered
292
- # @return [void]
293
- def unwind_for_conflict
294
- details_for_unwind = build_details_for_unwind
295
- unwind_options = unused_unwind_options
296
- debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
297
- conflicts.tap do |c|
298
- sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
299
- raise_error_unless_state(c)
300
- activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
301
- state.conflicts = c
302
- state.unused_unwind_options = unwind_options
303
- filter_possibilities_after_unwind(details_for_unwind)
304
- index = states.size - 1
305
- @parents_of.each { |_, a| a.reject! { |i| i >= index } }
306
- state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
307
- end
308
- end
309
-
310
- # Raises a VersionConflict error, or any underlying error, if there is no
311
- # current state
312
- # @return [void]
313
- def raise_error_unless_state(conflicts)
314
- return if state
315
-
316
- error = conflicts.values.map(&:underlying_error).compact.first
317
- raise error || VersionConflict.new(conflicts, specification_provider)
318
- end
319
-
320
- # @return [UnwindDetails] Details of the nearest index to which we could unwind
321
- def build_details_for_unwind
322
- # Get the possible unwinds for the current conflict
323
- current_conflict = conflicts[name]
324
- binding_requirements = binding_requirements_for_conflict(current_conflict)
325
- unwind_details = unwind_options_for_requirements(binding_requirements)
326
-
327
- last_detail_for_current_unwind = unwind_details.sort.last
328
- current_detail = last_detail_for_current_unwind
329
-
330
- # Look for past conflicts that could be unwound to affect the
331
- # requirement tree for the current conflict
332
- all_reqs = last_detail_for_current_unwind.all_requirements
333
- all_reqs_size = all_reqs.size
334
- relevant_unused_unwinds = unused_unwind_options.select do |alternative|
335
- diff_reqs = all_reqs - alternative.requirements_unwound_to_instead
336
- next if diff_reqs.size == all_reqs_size
337
- # Find the highest index unwind whilst looping through
338
- current_detail = alternative if alternative > current_detail
339
- alternative
340
- end
341
-
342
- # Add the current unwind options to the `unused_unwind_options` array.
343
- # The "used" option will be filtered out during `unwind_for_conflict`.
344
- state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
345
-
346
- # Update the requirements_unwound_to_instead on any relevant unused unwinds
347
- relevant_unused_unwinds.each do |d|
348
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
349
- end
350
- unwind_details.each do |d|
351
- (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq!
352
- end
353
-
354
- current_detail
355
- end
356
-
357
- # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
358
- # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
359
- # of resolving the passed requirements
360
- def unwind_options_for_requirements(binding_requirements)
361
- unwind_details = []
362
-
363
- trees = []
364
- binding_requirements.reverse_each do |r|
365
- partial_tree = [r]
366
- trees << partial_tree
367
- unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
368
-
369
- # If this requirement has alternative possibilities, check if any would
370
- # satisfy the other requirements that created this conflict
371
- requirement_state = find_state_for(r)
372
- if conflict_fixing_possibilities?(requirement_state, binding_requirements)
373
- unwind_details << UnwindDetails.new(
374
- states.index(requirement_state),
375
- r,
376
- partial_tree,
377
- binding_requirements,
378
- trees,
379
- []
380
- )
381
- end
382
-
383
- # Next, look at the parent of this requirement, and check if the requirement
384
- # could have been avoided if an alternative PossibilitySet had been chosen
385
- parent_r = parent_of(r)
386
- next if parent_r.nil?
387
- partial_tree.unshift(parent_r)
388
- requirement_state = find_state_for(parent_r)
389
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
390
- unwind_details << UnwindDetails.new(
391
- states.index(requirement_state),
392
- parent_r,
393
- partial_tree,
394
- binding_requirements,
395
- trees,
396
- []
397
- )
398
- end
399
-
400
- # Finally, look at the grandparent and up of this requirement, looking
401
- # for any possibilities that wouldn't create their parent requirement
402
- grandparent_r = parent_of(parent_r)
403
- until grandparent_r.nil?
404
- partial_tree.unshift(grandparent_r)
405
- requirement_state = find_state_for(grandparent_r)
406
- if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
407
- unwind_details << UnwindDetails.new(
408
- states.index(requirement_state),
409
- grandparent_r,
410
- partial_tree,
411
- binding_requirements,
412
- trees,
413
- []
414
- )
415
- end
416
- parent_r = grandparent_r
417
- grandparent_r = parent_of(parent_r)
418
- end
419
- end
420
-
421
- unwind_details
422
- end
423
-
424
- # @param [DependencyState] state
425
- # @param [Array] binding_requirements array of requirements
426
- # @return [Boolean] whether or not the given state has any possibilities
427
- # that could satisfy the given requirements
428
- def conflict_fixing_possibilities?(state, binding_requirements)
429
- return false unless state
430
-
431
- state.possibilities.any? do |possibility_set|
432
- possibility_set.possibilities.any? do |poss|
433
- possibility_satisfies_requirements?(poss, binding_requirements)
434
- end
435
- end
436
- end
437
-
438
- # Filter's a state's possibilities to remove any that would not fix the
439
- # conflict we've just rewound from
440
- # @param [UnwindDetails] unwind_details details of the conflict just
441
- # unwound from
442
- # @return [void]
443
- def filter_possibilities_after_unwind(unwind_details)
444
- return unless state && !state.possibilities.empty?
445
-
446
- if unwind_details.unwinding_to_primary_requirement?
447
- filter_possibilities_for_primary_unwind(unwind_details)
448
- else
449
- filter_possibilities_for_parent_unwind(unwind_details)
450
- end
451
- end
452
-
453
- # Filter's a state's possibilities to remove any that would not satisfy
454
- # the requirements in the conflict we've just rewound from
455
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
456
- # @return [void]
457
- def filter_possibilities_for_primary_unwind(unwind_details)
458
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
459
- unwinds_to_state << unwind_details
460
- unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
461
-
462
- state.possibilities.reject! do |possibility_set|
463
- possibility_set.possibilities.none? do |poss|
464
- unwind_requirement_sets.any? do |requirements|
465
- possibility_satisfies_requirements?(poss, requirements)
466
- end
467
- end
468
- end
469
- end
470
-
471
- # @param [Object] possibility a single possibility
472
- # @param [Array] requirements an array of requirements
473
- # @return [Boolean] whether the possibility satisfies all of the
474
- # given requirements
475
- def possibility_satisfies_requirements?(possibility, requirements)
476
- name = name_for(possibility)
477
-
478
- activated.tag(:swap)
479
- activated.set_payload(name, possibility) if activated.vertex_named(name)
480
- satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
481
- activated.rewind_to(:swap)
482
-
483
- satisfied
484
- end
485
-
486
- # Filter's a state's possibilities to remove any that would (eventually)
487
- # create a requirement in the conflict we've just rewound from
488
- # @param [UnwindDetails] unwind_details details of the conflict just unwound from
489
- # @return [void]
490
- def filter_possibilities_for_parent_unwind(unwind_details)
491
- unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
492
- unwinds_to_state << unwind_details
493
-
494
- primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
495
- parent_unwinds = unwinds_to_state.uniq - primary_unwinds
496
-
497
- allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
498
- states[unwind.state_index].possibilities.select do |possibility_set|
499
- possibility_set.possibilities.any? do |poss|
500
- possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
501
- end
502
- end
503
- end
504
-
505
- requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
506
-
507
- state.possibilities.reject! do |possibility_set|
508
- !allowed_possibility_sets.include?(possibility_set) &&
509
- (requirements_to_avoid - possibility_set.dependencies).empty?
510
- end
511
- end
512
-
513
- # @param [Conflict] conflict
514
- # @return [Array] minimal array of requirements that would cause the passed
515
- # conflict to occur.
516
- def binding_requirements_for_conflict(conflict)
517
- return [conflict.requirement] if conflict.possibility.nil?
518
-
519
- possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
520
-
521
- # When there's a `CircularDependency` error the conflicting requirement
522
- # (the one causing the circular) won't be `conflict.requirement`
523
- # (which won't be for the right state, because we won't have created it,
524
- # because it's circular).
525
- # We need to make sure we have that requirement in the conflict's list,
526
- # otherwise we won't be able to unwind properly, so we just return all
527
- # the requirements for the conflict.
528
- return possible_binding_requirements if conflict.underlying_error
529
-
530
- possibilities = search_for(conflict.requirement)
531
-
532
- # If all the requirements together don't filter out all possibilities,
533
- # then the only two requirements we need to consider are the initial one
534
- # (where the dependency's version was first chosen) and the last
535
- if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
536
- return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
537
- end
538
-
539
- # Loop through the possible binding requirements, removing each one
540
- # that doesn't bind. Use a `reverse_each` as we want the earliest set of
541
- # binding requirements, and don't use `reject!` as we wish to refine the
542
- # array *on each iteration*.
543
- binding_requirements = possible_binding_requirements.dup
544
- possible_binding_requirements.reverse_each do |req|
545
- next if req == conflict.requirement
546
- unless binding_requirement_in_set?(req, binding_requirements, possibilities)
547
- binding_requirements -= [req]
548
- end
549
- end
550
-
551
- binding_requirements
552
- end
553
-
554
- # @param [Object] requirement we wish to check
555
- # @param [Array] possible_binding_requirements array of requirements
556
- # @param [Array] possibilities array of possibilities the requirements will be used to filter
557
- # @return [Boolean] whether or not the given requirement is required to filter
558
- # out all elements of the array of possibilities.
559
- def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
560
- possibilities.any? do |poss|
561
- possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
562
- end
563
- end
564
-
565
- # @param [Object] requirement
566
- # @return [Object] the requirement that led to `requirement` being added
567
- # to the list of requirements.
568
- def parent_of(requirement)
569
- return unless requirement
570
- return unless index = @parents_of[requirement].last
571
- return unless parent_state = @states[index]
572
- parent_state.requirement
573
- end
574
-
575
- # @param [String] name
576
- # @return [Object] the requirement that led to a version of a possibility
577
- # with the given name being activated.
578
- def requirement_for_existing_name(name)
579
- return nil unless vertex = activated.vertex_named(name)
580
- return nil unless vertex.payload
581
- states.find { |s| s.name == name }.requirement
582
- end
583
-
584
- # @param [Object] requirement
585
- # @return [ResolutionState] the state whose `requirement` is the given
586
- # `requirement`.
587
- def find_state_for(requirement)
588
- return nil unless requirement
589
- states.find { |i| requirement == i.requirement }
590
- end
591
-
592
- # @param [Object] underlying_error
593
- # @return [Conflict] a {Conflict} that reflects the failure to activate
594
- # the {#possibility} in conjunction with the current {#state}
595
- def create_conflict(underlying_error = nil)
596
- vertex = activated.vertex_named(name)
597
- locked_requirement = locked_requirement_named(name)
598
-
599
- requirements = {}
600
- unless vertex.explicit_requirements.empty?
601
- requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
602
- end
603
- requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
604
- vertex.incoming_edges.each do |edge|
605
- (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
606
- end
607
-
608
- activated_by_name = {}
609
- activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
610
- conflicts[name] = Conflict.new(
611
- requirement,
612
- requirements,
613
- vertex.payload && vertex.payload.latest_version,
614
- possibility,
615
- locked_requirement,
616
- requirement_trees,
617
- activated_by_name,
618
- underlying_error
619
- )
620
- end
621
-
622
- # @return [Array<Array<Object>>] The different requirement
623
- # trees that led to every requirement for the current spec.
624
- def requirement_trees
625
- vertex = activated.vertex_named(name)
626
- vertex.requirements.map { |r| requirement_tree_for(r) }
627
- end
628
-
629
- # @param [Object] requirement
630
- # @return [Array<Object>] the list of requirements that led to
631
- # `requirement` being required.
632
- def requirement_tree_for(requirement)
633
- tree = []
634
- while requirement
635
- tree.unshift(requirement)
636
- requirement = parent_of(requirement)
637
- end
638
- tree
639
- end
640
-
641
- # Indicates progress roughly once every second
642
- # @return [void]
643
- def indicate_progress
644
- @iteration_counter += 1
645
- @progress_rate ||= resolver_ui.progress_rate
646
- if iteration_rate.nil?
647
- if Time.now - started_at >= @progress_rate
648
- self.iteration_rate = @iteration_counter
649
- end
650
- end
651
-
652
- if iteration_rate && (@iteration_counter % iteration_rate) == 0
653
- resolver_ui.indicate_progress
654
- end
655
- end
656
-
657
- # Calls the {#resolver_ui}'s {UI#debug} method
658
- # @param [Integer] depth the depth of the {#states} stack
659
- # @param [Proc] block a block that yields a {#to_s}
660
- # @return [void]
661
- def debug(depth = 0, &block)
662
- resolver_ui.debug(depth, &block)
663
- end
664
-
665
- # Attempts to activate the current {#possibility}
666
- # @return [void]
667
- def attempt_to_activate
668
- debug(depth) { 'Attempting to activate ' + possibility.to_s }
669
- existing_vertex = activated.vertex_named(name)
670
- if existing_vertex.payload
671
- debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
672
- attempt_to_filter_existing_spec(existing_vertex)
673
- else
674
- latest = possibility.latest_version
675
- possibility.possibilities.select! do |possibility|
676
- requirement_satisfied_by?(requirement, activated, possibility)
677
- end
678
- if possibility.latest_version.nil?
679
- # ensure there's a possibility for better error messages
680
- possibility.possibilities << latest if latest
681
- create_conflict
682
- unwind_for_conflict
683
- else
684
- activate_new_spec
685
- end
686
- end
687
- end
688
-
689
- # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
690
- # @return [void]
691
- def attempt_to_filter_existing_spec(vertex)
692
- filtered_set = filtered_possibility_set(vertex)
693
- if !filtered_set.possibilities.empty?
694
- activated.set_payload(name, filtered_set)
695
- new_requirements = requirements.dup
696
- push_state_for_requirements(new_requirements, false)
697
- else
698
- create_conflict
699
- debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
700
- unwind_for_conflict
701
- end
702
- end
703
-
704
- # Generates a filtered version of the existing vertex's `PossibilitySet` using the
705
- # current state's `requirement`
706
- # @param [Object] vertex existing vertex
707
- # @return [PossibilitySet] filtered possibility set
708
- def filtered_possibility_set(vertex)
709
- PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
710
- end
711
-
712
- # @param [String] requirement_name the spec name to search for
713
- # @return [Object] the locked spec named `requirement_name`, if one
714
- # is found on {#base}
715
- def locked_requirement_named(requirement_name)
716
- vertex = base.vertex_named(requirement_name)
717
- vertex && vertex.payload
718
- end
719
-
720
- # Add the current {#possibility} to the dependency graph of the current
721
- # {#state}
722
- # @return [void]
723
- def activate_new_spec
724
- conflicts.delete(name)
725
- debug(depth) { "Activated #{name} at #{possibility}" }
726
- activated.set_payload(name, possibility)
727
- require_nested_dependencies_for(possibility)
728
- end
729
-
730
- # Requires the dependencies that the recently activated spec has
731
- # @param [Object] possibility_set the PossibilitySet that has just been
732
- # activated
733
- # @return [void]
734
- def require_nested_dependencies_for(possibility_set)
735
- nested_dependencies = dependencies_for(possibility_set.latest_version)
736
- debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
737
- nested_dependencies.each do |d|
738
- activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
739
- parent_index = states.size - 1
740
- parents = @parents_of[d]
741
- parents << parent_index if parents.empty?
742
- end
743
-
744
- push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
745
- end
746
-
747
- # Pushes a new {DependencyState} that encapsulates both existing and new
748
- # requirements
749
- # @param [Array] new_requirements
750
- # @param [Boolean] requires_sort
751
- # @param [Object] new_activated
752
- # @return [void]
753
- def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
754
- new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
755
- new_requirement = nil
756
- loop do
757
- new_requirement = new_requirements.shift
758
- break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
759
- end
760
- new_name = new_requirement ? name_for(new_requirement) : ''.freeze
761
- possibilities = possibilities_for_requirement(new_requirement)
762
- handle_missing_or_push_dependency_state DependencyState.new(
763
- new_name, new_requirements, new_activated,
764
- new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
765
- )
766
- end
767
-
768
- # Checks a proposed requirement with any existing locked requirement
769
- # before generating an array of possibilities for it.
770
- # @param [Object] requirement the proposed requirement
771
- # @param [Object] activated
772
- # @return [Array] possibilities
773
- def possibilities_for_requirement(requirement, activated = self.activated)
774
- return [] unless requirement
775
- if locked_requirement_named(name_for(requirement))
776
- return locked_requirement_possibility_set(requirement, activated)
777
- end
778
-
779
- group_possibilities(search_for(requirement))
780
- end
781
-
782
- # @param [Object] requirement the proposed requirement
783
- # @param [Object] activated
784
- # @return [Array] possibility set containing only the locked requirement, if any
785
- def locked_requirement_possibility_set(requirement, activated = self.activated)
786
- all_possibilities = search_for(requirement)
787
- locked_requirement = locked_requirement_named(name_for(requirement))
788
-
789
- # Longwinded way to build a possibilities array with either the locked
790
- # requirement or nothing in it. Required, since the API for
791
- # locked_requirement isn't guaranteed.
792
- locked_possibilities = all_possibilities.select do |possibility|
793
- requirement_satisfied_by?(locked_requirement, activated, possibility)
794
- end
795
-
796
- group_possibilities(locked_possibilities)
797
- end
798
-
799
- # Build an array of PossibilitySets, with each element representing a group of
800
- # dependency versions that all have the same sub-dependency version constraints
801
- # and are contiguous.
802
- # @param [Array] possibilities an array of possibilities
803
- # @return [Array<PossibilitySet>] an array of possibility sets
804
- def group_possibilities(possibilities)
805
- possibility_sets = []
806
- current_possibility_set = nil
807
-
808
- possibilities.reverse_each do |possibility|
809
- dependencies = dependencies_for(possibility)
810
- if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies)
811
- current_possibility_set.possibilities.unshift(possibility)
812
- else
813
- possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
814
- current_possibility_set = possibility_sets.first
815
- end
816
- end
817
-
818
- possibility_sets
819
- end
820
-
821
- # Pushes a new {DependencyState}.
822
- # If the {#specification_provider} says to
823
- # {SpecificationProvider#allow_missing?} that particular requirement, and
824
- # there are no possibilities for that requirement, then `state` is not
825
- # pushed, and the vertex in {#activated} is removed, and we continue
826
- # resolving the remaining requirements.
827
- # @param [DependencyState] state
828
- # @return [void]
829
- def handle_missing_or_push_dependency_state(state)
830
- if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
831
- state.activated.detach_vertex_named(state.name)
832
- push_state_for_requirements(state.requirements.dup, false, state.activated)
833
- else
834
- states.push(state).tap { activated.tag(state) }
835
- end
836
- end
837
- end
838
- end
839
- end