bundler 2.0.2
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3111 -0
- data/LICENSE.md +23 -0
- data/README.md +63 -0
- data/bundler.gemspec +65 -0
- data/exe/bundle +31 -0
- data/exe/bundle_ruby +60 -0
- data/exe/bundler +4 -0
- data/lib/bundler.rb +567 -0
- data/lib/bundler/build_metadata.rb +53 -0
- data/lib/bundler/capistrano.rb +22 -0
- data/lib/bundler/cli.rb +792 -0
- data/lib/bundler/cli/add.rb +35 -0
- data/lib/bundler/cli/binstubs.rb +49 -0
- data/lib/bundler/cli/cache.rb +36 -0
- data/lib/bundler/cli/check.rb +38 -0
- data/lib/bundler/cli/clean.rb +25 -0
- data/lib/bundler/cli/common.rb +102 -0
- data/lib/bundler/cli/config.rb +119 -0
- data/lib/bundler/cli/console.rb +43 -0
- data/lib/bundler/cli/doctor.rb +140 -0
- data/lib/bundler/cli/exec.rb +105 -0
- data/lib/bundler/cli/gem.rb +252 -0
- data/lib/bundler/cli/info.rb +50 -0
- data/lib/bundler/cli/init.rb +47 -0
- data/lib/bundler/cli/inject.rb +60 -0
- data/lib/bundler/cli/install.rb +218 -0
- data/lib/bundler/cli/issue.rb +40 -0
- data/lib/bundler/cli/list.rb +58 -0
- data/lib/bundler/cli/lock.rb +63 -0
- data/lib/bundler/cli/open.rb +26 -0
- data/lib/bundler/cli/outdated.rb +266 -0
- data/lib/bundler/cli/package.rb +49 -0
- data/lib/bundler/cli/platform.rb +46 -0
- data/lib/bundler/cli/plugin.rb +24 -0
- data/lib/bundler/cli/pristine.rb +47 -0
- data/lib/bundler/cli/remove.rb +18 -0
- data/lib/bundler/cli/show.rb +75 -0
- data/lib/bundler/cli/update.rb +91 -0
- data/lib/bundler/cli/viz.rb +31 -0
- data/lib/bundler/compact_index_client.rb +109 -0
- data/lib/bundler/compact_index_client/cache.rb +118 -0
- data/lib/bundler/compact_index_client/updater.rb +116 -0
- data/lib/bundler/compatibility_guard.rb +13 -0
- data/lib/bundler/constants.rb +7 -0
- data/lib/bundler/current_ruby.rb +94 -0
- data/lib/bundler/definition.rb +995 -0
- data/lib/bundler/dep_proxy.rb +48 -0
- data/lib/bundler/dependency.rb +139 -0
- data/lib/bundler/deployment.rb +69 -0
- data/lib/bundler/deprecate.rb +44 -0
- data/lib/bundler/dsl.rb +615 -0
- data/lib/bundler/endpoint_specification.rb +141 -0
- data/lib/bundler/env.rb +149 -0
- data/lib/bundler/environment_preserver.rb +59 -0
- data/lib/bundler/errors.rb +158 -0
- data/lib/bundler/feature_flag.rb +75 -0
- data/lib/bundler/fetcher.rb +312 -0
- data/lib/bundler/fetcher/base.rb +52 -0
- data/lib/bundler/fetcher/compact_index.rb +126 -0
- data/lib/bundler/fetcher/dependency.rb +82 -0
- data/lib/bundler/fetcher/downloader.rb +84 -0
- data/lib/bundler/fetcher/index.rb +52 -0
- data/lib/bundler/friendly_errors.rb +131 -0
- data/lib/bundler/gem_helper.rb +217 -0
- data/lib/bundler/gem_helpers.rb +101 -0
- data/lib/bundler/gem_remote_fetcher.rb +43 -0
- data/lib/bundler/gem_tasks.rb +7 -0
- data/lib/bundler/gem_version_promoter.rb +190 -0
- data/lib/bundler/gemdeps.rb +29 -0
- data/lib/bundler/graph.rb +152 -0
- data/lib/bundler/index.rb +213 -0
- data/lib/bundler/injector.rb +253 -0
- data/lib/bundler/inline.rb +74 -0
- data/lib/bundler/installer.rb +318 -0
- data/lib/bundler/installer/gem_installer.rb +85 -0
- data/lib/bundler/installer/parallel_installer.rb +229 -0
- data/lib/bundler/installer/standalone.rb +53 -0
- data/lib/bundler/lazy_specification.rb +123 -0
- data/lib/bundler/lockfile_generator.rb +95 -0
- data/lib/bundler/lockfile_parser.rb +256 -0
- data/lib/bundler/match_platform.rb +24 -0
- data/lib/bundler/mirror.rb +223 -0
- data/lib/bundler/plugin.rb +294 -0
- data/lib/bundler/plugin/api.rb +81 -0
- data/lib/bundler/plugin/api/source.rb +306 -0
- data/lib/bundler/plugin/dsl.rb +53 -0
- data/lib/bundler/plugin/events.rb +61 -0
- data/lib/bundler/plugin/index.rb +165 -0
- data/lib/bundler/plugin/installer.rb +96 -0
- data/lib/bundler/plugin/installer/git.rb +38 -0
- data/lib/bundler/plugin/installer/rubygems.rb +27 -0
- data/lib/bundler/plugin/source_list.rb +27 -0
- data/lib/bundler/process_lock.rb +24 -0
- data/lib/bundler/psyched_yaml.rb +37 -0
- data/lib/bundler/remote_specification.rb +114 -0
- data/lib/bundler/resolver.rb +373 -0
- data/lib/bundler/resolver/spec_group.rb +106 -0
- data/lib/bundler/retry.rb +66 -0
- data/lib/bundler/ruby_dsl.rb +18 -0
- data/lib/bundler/ruby_version.rb +152 -0
- data/lib/bundler/rubygems_ext.rb +209 -0
- data/lib/bundler/rubygems_gem_installer.rb +99 -0
- data/lib/bundler/rubygems_integration.rb +915 -0
- data/lib/bundler/runtime.rb +322 -0
- data/lib/bundler/settings.rb +464 -0
- data/lib/bundler/settings/validator.rb +102 -0
- data/lib/bundler/setup.rb +28 -0
- data/lib/bundler/shared_helpers.rb +386 -0
- data/lib/bundler/similarity_detector.rb +63 -0
- data/lib/bundler/source.rb +94 -0
- data/lib/bundler/source/gemspec.rb +18 -0
- data/lib/bundler/source/git.rb +329 -0
- data/lib/bundler/source/git/git_proxy.rb +262 -0
- data/lib/bundler/source/metadata.rb +62 -0
- data/lib/bundler/source/path.rb +249 -0
- data/lib/bundler/source/path/installer.rb +74 -0
- data/lib/bundler/source/rubygems.rb +539 -0
- data/lib/bundler/source/rubygems/remote.rb +69 -0
- data/lib/bundler/source_list.rb +186 -0
- data/lib/bundler/spec_set.rb +208 -0
- data/lib/bundler/ssl_certs/.document +1 -0
- data/lib/bundler/ssl_certs/certificate_manager.rb +66 -0
- data/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
- data/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
- data/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
- data/lib/bundler/stub_specification.rb +108 -0
- data/lib/bundler/templates/.document +1 -0
- data/lib/bundler/templates/Executable +29 -0
- data/lib/bundler/templates/Executable.bundler +105 -0
- data/lib/bundler/templates/Executable.standalone +14 -0
- data/lib/bundler/templates/Gemfile +7 -0
- data/lib/bundler/templates/gems.rb +8 -0
- data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +74 -0
- data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
- data/lib/bundler/templates/newgem/LICENSE.txt.tt +21 -0
- data/lib/bundler/templates/newgem/README.md.tt +47 -0
- data/lib/bundler/templates/newgem/Rakefile.tt +29 -0
- data/lib/bundler/templates/newgem/bin/console.tt +14 -0
- data/lib/bundler/templates/newgem/bin/setup.tt +8 -0
- data/lib/bundler/templates/newgem/exe/newgem.tt +3 -0
- data/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
- data/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt +9 -0
- data/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt +6 -0
- data/lib/bundler/templates/newgem/gitignore.tt +20 -0
- data/lib/bundler/templates/newgem/lib/newgem.rb.tt +13 -0
- data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +50 -0
- data/lib/bundler/templates/newgem/rspec.tt +3 -0
- data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +9 -0
- data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +14 -0
- data/lib/bundler/templates/newgem/test/newgem_test.rb.tt +11 -0
- data/lib/bundler/templates/newgem/test/test_helper.rb.tt +8 -0
- data/lib/bundler/templates/newgem/travis.yml.tt +7 -0
- data/lib/bundler/ui.rb +9 -0
- data/lib/bundler/ui/rg_proxy.rb +19 -0
- data/lib/bundler/ui/shell.rb +146 -0
- data/lib/bundler/ui/silent.rb +69 -0
- data/lib/bundler/uri_credentials_filter.rb +37 -0
- data/lib/bundler/vendor/fileutils/lib/fileutils.rb +1741 -0
- data/lib/bundler/vendor/fileutils/lib/fileutils/version.rb +5 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo.rb +12 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +26 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +57 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +81 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +223 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +36 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +66 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +62 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +63 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +61 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +126 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +46 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +36 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +136 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +143 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +6 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +101 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +67 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +837 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +46 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +58 -0
- data/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb +27 -0
- data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1233 -0
- data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb +129 -0
- data/lib/bundler/vendor/thor/lib/thor.rb +509 -0
- data/lib/bundler/vendor/thor/lib/thor/actions.rb +331 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +104 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +60 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +118 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +143 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +373 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +109 -0
- data/lib/bundler/vendor/thor/lib/thor/base.rb +678 -0
- data/lib/bundler/vendor/thor/lib/thor/command.rb +135 -0
- data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +97 -0
- data/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb +129 -0
- data/lib/bundler/vendor/thor/lib/thor/error.rb +114 -0
- data/lib/bundler/vendor/thor/lib/thor/group.rb +281 -0
- data/lib/bundler/vendor/thor/lib/thor/invocation.rb +177 -0
- data/lib/bundler/vendor/thor/lib/thor/line_editor.rb +17 -0
- data/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb +37 -0
- data/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
- data/lib/bundler/vendor/thor/lib/thor/parser.rb +4 -0
- data/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +70 -0
- data/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +175 -0
- data/lib/bundler/vendor/thor/lib/thor/parser/option.rb +146 -0
- data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +226 -0
- data/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +71 -0
- data/lib/bundler/vendor/thor/lib/thor/runner.rb +324 -0
- data/lib/bundler/vendor/thor/lib/thor/shell.rb +81 -0
- data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +482 -0
- data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +149 -0
- data/lib/bundler/vendor/thor/lib/thor/shell/html.rb +126 -0
- data/lib/bundler/vendor/thor/lib/thor/util.rb +268 -0
- data/lib/bundler/vendor/thor/lib/thor/version.rb +3 -0
- data/lib/bundler/vendored_fileutils.rb +9 -0
- data/lib/bundler/vendored_molinillo.rb +4 -0
- data/lib/bundler/vendored_persistent.rb +52 -0
- data/lib/bundler/vendored_thor.rb +8 -0
- data/lib/bundler/version.rb +28 -0
- data/lib/bundler/version_ranges.rb +76 -0
- data/lib/bundler/vlad.rb +17 -0
- data/lib/bundler/worker.rb +106 -0
- data/lib/bundler/yaml_serializer.rb +90 -0
- data/man/bundle-add.1 +58 -0
- data/man/bundle-add.1.txt +52 -0
- data/man/bundle-add.ronn +40 -0
- data/man/bundle-binstubs.1 +40 -0
- data/man/bundle-binstubs.1.txt +48 -0
- data/man/bundle-binstubs.ronn +43 -0
- data/man/bundle-check.1 +31 -0
- data/man/bundle-check.1.txt +33 -0
- data/man/bundle-check.ronn +26 -0
- data/man/bundle-clean.1 +24 -0
- data/man/bundle-clean.1.txt +26 -0
- data/man/bundle-clean.ronn +18 -0
- data/man/bundle-config.1 +497 -0
- data/man/bundle-config.1.txt +529 -0
- data/man/bundle-config.ronn +397 -0
- data/man/bundle-doctor.1 +44 -0
- data/man/bundle-doctor.1.txt +44 -0
- data/man/bundle-doctor.ronn +33 -0
- data/man/bundle-exec.1 +165 -0
- data/man/bundle-exec.1.txt +178 -0
- data/man/bundle-exec.ronn +152 -0
- data/man/bundle-gem.1 +80 -0
- data/man/bundle-gem.1.txt +91 -0
- data/man/bundle-gem.ronn +78 -0
- data/man/bundle-info.1 +20 -0
- data/man/bundle-info.1.txt +21 -0
- data/man/bundle-info.ronn +17 -0
- data/man/bundle-init.1 +25 -0
- data/man/bundle-init.1.txt +34 -0
- data/man/bundle-init.ronn +29 -0
- data/man/bundle-inject.1 +33 -0
- data/man/bundle-inject.1.txt +32 -0
- data/man/bundle-inject.ronn +22 -0
- data/man/bundle-install.1 +308 -0
- data/man/bundle-install.1.txt +396 -0
- data/man/bundle-install.ronn +378 -0
- data/man/bundle-list.1 +50 -0
- data/man/bundle-list.1.txt +43 -0
- data/man/bundle-list.ronn +33 -0
- data/man/bundle-lock.1 +84 -0
- data/man/bundle-lock.1.txt +93 -0
- data/man/bundle-lock.ronn +94 -0
- data/man/bundle-open.1 +32 -0
- data/man/bundle-open.1.txt +29 -0
- data/man/bundle-open.ronn +19 -0
- data/man/bundle-outdated.1 +155 -0
- data/man/bundle-outdated.1.txt +131 -0
- data/man/bundle-outdated.ronn +111 -0
- data/man/bundle-package.1 +55 -0
- data/man/bundle-package.1.txt +79 -0
- data/man/bundle-package.ronn +72 -0
- data/man/bundle-platform.1 +61 -0
- data/man/bundle-platform.1.txt +57 -0
- data/man/bundle-platform.ronn +42 -0
- data/man/bundle-pristine.1 +34 -0
- data/man/bundle-pristine.1.txt +44 -0
- data/man/bundle-pristine.ronn +34 -0
- data/man/bundle-remove.1 +31 -0
- data/man/bundle-remove.1.txt +34 -0
- data/man/bundle-remove.ronn +23 -0
- data/man/bundle-show.1 +23 -0
- data/man/bundle-show.1.txt +27 -0
- data/man/bundle-show.ronn +21 -0
- data/man/bundle-update.1 +394 -0
- data/man/bundle-update.1.txt +391 -0
- data/man/bundle-update.ronn +350 -0
- data/man/bundle-viz.1 +39 -0
- data/man/bundle-viz.1.txt +39 -0
- data/man/bundle-viz.ronn +30 -0
- data/man/bundle.1 +136 -0
- data/man/bundle.1.txt +116 -0
- data/man/bundle.ronn +111 -0
- data/man/gemfile.5 +689 -0
- data/man/gemfile.5.ronn +521 -0
- data/man/gemfile.5.txt +653 -0
- data/man/index.txt +25 -0
- metadata +463 -0
@@ -0,0 +1,837 @@
|
|
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
|
+
handle_missing_or_push_dependency_state(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 'bundler/vendor/molinillo/lib/molinillo/state'
|
242
|
+
require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider'
|
243
|
+
|
244
|
+
require 'bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state'
|
245
|
+
require 'bundler/vendor/molinillo/lib/molinillo/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 the initial state for the resolution, based upon the
|
277
|
+
# {#requested} dependencies
|
278
|
+
# @return [DependencyState] the initial state for the resolution
|
279
|
+
def 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
|
+
requirements = sort_dependencies(original_requested, graph, {})
|
289
|
+
initial_requirement = requirements.shift
|
290
|
+
DependencyState.new(
|
291
|
+
initial_requirement && name_for(initial_requirement),
|
292
|
+
requirements,
|
293
|
+
graph,
|
294
|
+
initial_requirement,
|
295
|
+
possibilities_for_requirement(initial_requirement, graph),
|
296
|
+
0,
|
297
|
+
{},
|
298
|
+
[]
|
299
|
+
)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Unwinds the states stack because a conflict has been encountered
|
303
|
+
# @return [void]
|
304
|
+
def unwind_for_conflict
|
305
|
+
details_for_unwind = build_details_for_unwind
|
306
|
+
unwind_options = unused_unwind_options
|
307
|
+
debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
|
308
|
+
conflicts.tap do |c|
|
309
|
+
sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
|
310
|
+
raise_error_unless_state(c)
|
311
|
+
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
|
312
|
+
state.conflicts = c
|
313
|
+
state.unused_unwind_options = unwind_options
|
314
|
+
filter_possibilities_after_unwind(details_for_unwind)
|
315
|
+
index = states.size - 1
|
316
|
+
@parents_of.each { |_, a| a.reject! { |i| i >= index } }
|
317
|
+
state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Raises a VersionConflict error, or any underlying error, if there is no
|
322
|
+
# current state
|
323
|
+
# @return [void]
|
324
|
+
def raise_error_unless_state(conflicts)
|
325
|
+
return if state
|
326
|
+
|
327
|
+
error = conflicts.values.map(&:underlying_error).compact.first
|
328
|
+
raise error || VersionConflict.new(conflicts, specification_provider)
|
329
|
+
end
|
330
|
+
|
331
|
+
# @return [UnwindDetails] Details of the nearest index to which we could unwind
|
332
|
+
def build_details_for_unwind
|
333
|
+
# Get the possible unwinds for the current conflict
|
334
|
+
current_conflict = conflicts[name]
|
335
|
+
binding_requirements = binding_requirements_for_conflict(current_conflict)
|
336
|
+
unwind_details = unwind_options_for_requirements(binding_requirements)
|
337
|
+
|
338
|
+
last_detail_for_current_unwind = unwind_details.sort.last
|
339
|
+
current_detail = last_detail_for_current_unwind
|
340
|
+
|
341
|
+
# Look for past conflicts that could be unwound to affect the
|
342
|
+
# requirement tree for the current conflict
|
343
|
+
relevant_unused_unwinds = unused_unwind_options.select do |alternative|
|
344
|
+
intersecting_requirements =
|
345
|
+
last_detail_for_current_unwind.all_requirements &
|
346
|
+
alternative.requirements_unwound_to_instead
|
347
|
+
next if intersecting_requirements.empty?
|
348
|
+
# Find the highest index unwind whilst looping through
|
349
|
+
current_detail = alternative if alternative > current_detail
|
350
|
+
alternative
|
351
|
+
end
|
352
|
+
|
353
|
+
# Add the current unwind options to the `unused_unwind_options` array.
|
354
|
+
# The "used" option will be filtered out during `unwind_for_conflict`.
|
355
|
+
state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
|
356
|
+
|
357
|
+
# Update the requirements_unwound_to_instead on any relevant unused unwinds
|
358
|
+
relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
|
359
|
+
unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
|
360
|
+
|
361
|
+
current_detail
|
362
|
+
end
|
363
|
+
|
364
|
+
# @param [Array<Object>] array of requirements that combine to create a conflict
|
365
|
+
# @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
|
366
|
+
# of resolving the passed requirements
|
367
|
+
def unwind_options_for_requirements(binding_requirements)
|
368
|
+
unwind_details = []
|
369
|
+
|
370
|
+
trees = []
|
371
|
+
binding_requirements.reverse_each do |r|
|
372
|
+
partial_tree = [r]
|
373
|
+
trees << partial_tree
|
374
|
+
unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
|
375
|
+
|
376
|
+
# If this requirement has alternative possibilities, check if any would
|
377
|
+
# satisfy the other requirements that created this conflict
|
378
|
+
requirement_state = find_state_for(r)
|
379
|
+
if conflict_fixing_possibilities?(requirement_state, binding_requirements)
|
380
|
+
unwind_details << UnwindDetails.new(
|
381
|
+
states.index(requirement_state),
|
382
|
+
r,
|
383
|
+
partial_tree,
|
384
|
+
binding_requirements,
|
385
|
+
trees,
|
386
|
+
[]
|
387
|
+
)
|
388
|
+
end
|
389
|
+
|
390
|
+
# Next, look at the parent of this requirement, and check if the requirement
|
391
|
+
# could have been avoided if an alternative PossibilitySet had been chosen
|
392
|
+
parent_r = parent_of(r)
|
393
|
+
next if parent_r.nil?
|
394
|
+
partial_tree.unshift(parent_r)
|
395
|
+
requirement_state = find_state_for(parent_r)
|
396
|
+
if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
|
397
|
+
unwind_details << UnwindDetails.new(
|
398
|
+
states.index(requirement_state),
|
399
|
+
parent_r,
|
400
|
+
partial_tree,
|
401
|
+
binding_requirements,
|
402
|
+
trees,
|
403
|
+
[]
|
404
|
+
)
|
405
|
+
end
|
406
|
+
|
407
|
+
# Finally, look at the grandparent and up of this requirement, looking
|
408
|
+
# for any possibilities that wouldn't create their parent requirement
|
409
|
+
grandparent_r = parent_of(parent_r)
|
410
|
+
until grandparent_r.nil?
|
411
|
+
partial_tree.unshift(grandparent_r)
|
412
|
+
requirement_state = find_state_for(grandparent_r)
|
413
|
+
if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
|
414
|
+
unwind_details << UnwindDetails.new(
|
415
|
+
states.index(requirement_state),
|
416
|
+
grandparent_r,
|
417
|
+
partial_tree,
|
418
|
+
binding_requirements,
|
419
|
+
trees,
|
420
|
+
[]
|
421
|
+
)
|
422
|
+
end
|
423
|
+
parent_r = grandparent_r
|
424
|
+
grandparent_r = parent_of(parent_r)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
unwind_details
|
429
|
+
end
|
430
|
+
|
431
|
+
# @param [DependencyState] state
|
432
|
+
# @param [Array] array of requirements
|
433
|
+
# @return [Boolean] whether or not the given state has any possibilities
|
434
|
+
# that could satisfy the given requirements
|
435
|
+
def conflict_fixing_possibilities?(state, binding_requirements)
|
436
|
+
return false unless state
|
437
|
+
|
438
|
+
state.possibilities.any? do |possibility_set|
|
439
|
+
possibility_set.possibilities.any? do |poss|
|
440
|
+
possibility_satisfies_requirements?(poss, binding_requirements)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# Filter's a state's possibilities to remove any that would not fix the
|
446
|
+
# conflict we've just rewound from
|
447
|
+
# @param [UnwindDetails] details of the conflict just unwound from
|
448
|
+
# @return [void]
|
449
|
+
def filter_possibilities_after_unwind(unwind_details)
|
450
|
+
return unless state && !state.possibilities.empty?
|
451
|
+
|
452
|
+
if unwind_details.unwinding_to_primary_requirement?
|
453
|
+
filter_possibilities_for_primary_unwind(unwind_details)
|
454
|
+
else
|
455
|
+
filter_possibilities_for_parent_unwind(unwind_details)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
# Filter's a state's possibilities to remove any that would not satisfy
|
460
|
+
# the requirements in the conflict we've just rewound from
|
461
|
+
# @param [UnwindDetails] details of the conflict just unwound from
|
462
|
+
# @return [void]
|
463
|
+
def filter_possibilities_for_primary_unwind(unwind_details)
|
464
|
+
unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
|
465
|
+
unwinds_to_state << unwind_details
|
466
|
+
unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
|
467
|
+
|
468
|
+
state.possibilities.reject! do |possibility_set|
|
469
|
+
possibility_set.possibilities.none? do |poss|
|
470
|
+
unwind_requirement_sets.any? do |requirements|
|
471
|
+
possibility_satisfies_requirements?(poss, requirements)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
# @param [Object] possibility a single possibility
|
478
|
+
# @param [Array] requirements an array of requirements
|
479
|
+
# @return [Boolean] whether the possibility satisfies all of the
|
480
|
+
# given requirements
|
481
|
+
def possibility_satisfies_requirements?(possibility, requirements)
|
482
|
+
name = name_for(possibility)
|
483
|
+
|
484
|
+
activated.tag(:swap)
|
485
|
+
activated.set_payload(name, possibility) if activated.vertex_named(name)
|
486
|
+
satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
|
487
|
+
activated.rewind_to(:swap)
|
488
|
+
|
489
|
+
satisfied
|
490
|
+
end
|
491
|
+
|
492
|
+
# Filter's a state's possibilities to remove any that would (eventually)
|
493
|
+
# create a requirement in the conflict we've just rewound from
|
494
|
+
# @param [UnwindDetails] details of the conflict just unwound from
|
495
|
+
# @return [void]
|
496
|
+
def filter_possibilities_for_parent_unwind(unwind_details)
|
497
|
+
unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
|
498
|
+
unwinds_to_state << unwind_details
|
499
|
+
|
500
|
+
primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
|
501
|
+
parent_unwinds = unwinds_to_state.uniq - primary_unwinds
|
502
|
+
|
503
|
+
allowed_possibility_sets = Compatibility.flat_map(primary_unwinds) do |unwind|
|
504
|
+
states[unwind.state_index].possibilities.select do |possibility_set|
|
505
|
+
possibility_set.possibilities.any? do |poss|
|
506
|
+
possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
requirements_to_avoid = Compatibility.flat_map(parent_unwinds, &:sub_dependencies_to_avoid)
|
512
|
+
|
513
|
+
state.possibilities.reject! do |possibility_set|
|
514
|
+
!allowed_possibility_sets.include?(possibility_set) &&
|
515
|
+
(requirements_to_avoid - possibility_set.dependencies).empty?
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
# @param [Conflict] conflict
|
520
|
+
# @return [Array] minimal array of requirements that would cause the passed
|
521
|
+
# conflict to occur.
|
522
|
+
def binding_requirements_for_conflict(conflict)
|
523
|
+
return [conflict.requirement] if conflict.possibility.nil?
|
524
|
+
|
525
|
+
possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
|
526
|
+
|
527
|
+
# When there’s a `CircularDependency` error the conflicting requirement
|
528
|
+
# (the one causing the circular) won’t be `conflict.requirement`
|
529
|
+
# (which won’t be for the right state, because we won’t have created it,
|
530
|
+
# because it’s circular).
|
531
|
+
# We need to make sure we have that requirement in the conflict’s list,
|
532
|
+
# otherwise we won’t be able to unwind properly, so we just return all
|
533
|
+
# the requirements for the conflict.
|
534
|
+
return possible_binding_requirements if conflict.underlying_error
|
535
|
+
|
536
|
+
possibilities = search_for(conflict.requirement)
|
537
|
+
|
538
|
+
# If all the requirements together don't filter out all possibilities,
|
539
|
+
# then the only two requirements we need to consider are the initial one
|
540
|
+
# (where the dependency's version was first chosen) and the last
|
541
|
+
if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
|
542
|
+
return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
|
543
|
+
end
|
544
|
+
|
545
|
+
# Loop through the possible binding requirements, removing each one
|
546
|
+
# that doesn't bind. Use a `reverse_each` as we want the earliest set of
|
547
|
+
# binding requirements, and don't use `reject!` as we wish to refine the
|
548
|
+
# array *on each iteration*.
|
549
|
+
binding_requirements = possible_binding_requirements.dup
|
550
|
+
possible_binding_requirements.reverse_each do |req|
|
551
|
+
next if req == conflict.requirement
|
552
|
+
unless binding_requirement_in_set?(req, binding_requirements, possibilities)
|
553
|
+
binding_requirements -= [req]
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
binding_requirements
|
558
|
+
end
|
559
|
+
|
560
|
+
# @param [Object] requirement we wish to check
|
561
|
+
# @param [Array] array of requirements
|
562
|
+
# @param [Array] array of possibilities the requirements will be used to filter
|
563
|
+
# @return [Boolean] whether or not the given requirement is required to filter
|
564
|
+
# out all elements of the array of possibilities.
|
565
|
+
def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
|
566
|
+
possibilities.any? do |poss|
|
567
|
+
possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# @return [Object] the requirement that led to `requirement` being added
|
572
|
+
# to the list of requirements.
|
573
|
+
def parent_of(requirement)
|
574
|
+
return unless requirement
|
575
|
+
return unless index = @parents_of[requirement].last
|
576
|
+
return unless parent_state = @states[index]
|
577
|
+
parent_state.requirement
|
578
|
+
end
|
579
|
+
|
580
|
+
# @return [Object] the requirement that led to a version of a possibility
|
581
|
+
# with the given name being activated.
|
582
|
+
def requirement_for_existing_name(name)
|
583
|
+
return nil unless vertex = activated.vertex_named(name)
|
584
|
+
return nil unless vertex.payload
|
585
|
+
states.find { |s| s.name == name }.requirement
|
586
|
+
end
|
587
|
+
|
588
|
+
# @return [ResolutionState] the state whose `requirement` is the given
|
589
|
+
# `requirement`.
|
590
|
+
def find_state_for(requirement)
|
591
|
+
return nil unless requirement
|
592
|
+
states.find { |i| requirement == i.requirement }
|
593
|
+
end
|
594
|
+
|
595
|
+
# @return [Conflict] a {Conflict} that reflects the failure to activate
|
596
|
+
# the {#possibility} in conjunction with the current {#state}
|
597
|
+
def create_conflict(underlying_error = nil)
|
598
|
+
vertex = activated.vertex_named(name)
|
599
|
+
locked_requirement = locked_requirement_named(name)
|
600
|
+
|
601
|
+
requirements = {}
|
602
|
+
unless vertex.explicit_requirements.empty?
|
603
|
+
requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
|
604
|
+
end
|
605
|
+
requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
|
606
|
+
vertex.incoming_edges.each do |edge|
|
607
|
+
(requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
|
608
|
+
end
|
609
|
+
|
610
|
+
activated_by_name = {}
|
611
|
+
activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
|
612
|
+
conflicts[name] = Conflict.new(
|
613
|
+
requirement,
|
614
|
+
requirements,
|
615
|
+
vertex.payload && vertex.payload.latest_version,
|
616
|
+
possibility,
|
617
|
+
locked_requirement,
|
618
|
+
requirement_trees,
|
619
|
+
activated_by_name,
|
620
|
+
underlying_error
|
621
|
+
)
|
622
|
+
end
|
623
|
+
|
624
|
+
# @return [Array<Array<Object>>] The different requirement
|
625
|
+
# trees that led to every requirement for the current spec.
|
626
|
+
def requirement_trees
|
627
|
+
vertex = activated.vertex_named(name)
|
628
|
+
vertex.requirements.map { |r| requirement_tree_for(r) }
|
629
|
+
end
|
630
|
+
|
631
|
+
# @return [Array<Object>] the list of requirements that led to
|
632
|
+
# `requirement` being required.
|
633
|
+
def requirement_tree_for(requirement)
|
634
|
+
tree = []
|
635
|
+
while requirement
|
636
|
+
tree.unshift(requirement)
|
637
|
+
requirement = parent_of(requirement)
|
638
|
+
end
|
639
|
+
tree
|
640
|
+
end
|
641
|
+
|
642
|
+
# Indicates progress roughly once every second
|
643
|
+
# @return [void]
|
644
|
+
def indicate_progress
|
645
|
+
@iteration_counter += 1
|
646
|
+
@progress_rate ||= resolver_ui.progress_rate
|
647
|
+
if iteration_rate.nil?
|
648
|
+
if Time.now - started_at >= @progress_rate
|
649
|
+
self.iteration_rate = @iteration_counter
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
if iteration_rate && (@iteration_counter % iteration_rate) == 0
|
654
|
+
resolver_ui.indicate_progress
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
# Calls the {#resolver_ui}'s {UI#debug} method
|
659
|
+
# @param [Integer] depth the depth of the {#states} stack
|
660
|
+
# @param [Proc] block a block that yields a {#to_s}
|
661
|
+
# @return [void]
|
662
|
+
def debug(depth = 0, &block)
|
663
|
+
resolver_ui.debug(depth, &block)
|
664
|
+
end
|
665
|
+
|
666
|
+
# Attempts to activate the current {#possibility}
|
667
|
+
# @return [void]
|
668
|
+
def attempt_to_activate
|
669
|
+
debug(depth) { 'Attempting to activate ' + possibility.to_s }
|
670
|
+
existing_vertex = activated.vertex_named(name)
|
671
|
+
if existing_vertex.payload
|
672
|
+
debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
|
673
|
+
attempt_to_filter_existing_spec(existing_vertex)
|
674
|
+
else
|
675
|
+
latest = possibility.latest_version
|
676
|
+
# use reject!(!satisfied) for 1.8.7 compatibility
|
677
|
+
possibility.possibilities.reject! do |possibility|
|
678
|
+
!requirement_satisfied_by?(requirement, activated, possibility)
|
679
|
+
end
|
680
|
+
if possibility.latest_version.nil?
|
681
|
+
# ensure there's a possibility for better error messages
|
682
|
+
possibility.possibilities << latest if latest
|
683
|
+
create_conflict
|
684
|
+
unwind_for_conflict
|
685
|
+
else
|
686
|
+
activate_new_spec
|
687
|
+
end
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
# Attempts to update the existing vertex's `PossibilitySet` with a filtered version
|
692
|
+
# @return [void]
|
693
|
+
def attempt_to_filter_existing_spec(vertex)
|
694
|
+
filtered_set = filtered_possibility_set(vertex)
|
695
|
+
if !filtered_set.possibilities.empty?
|
696
|
+
activated.set_payload(name, filtered_set)
|
697
|
+
new_requirements = requirements.dup
|
698
|
+
push_state_for_requirements(new_requirements, false)
|
699
|
+
else
|
700
|
+
create_conflict
|
701
|
+
debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
|
702
|
+
unwind_for_conflict
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
# Generates a filtered version of the existing vertex's `PossibilitySet` using the
|
707
|
+
# current state's `requirement`
|
708
|
+
# @param [Object] existing vertex
|
709
|
+
# @return [PossibilitySet] filtered possibility set
|
710
|
+
def filtered_possibility_set(vertex)
|
711
|
+
PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
|
712
|
+
end
|
713
|
+
|
714
|
+
# @param [String] requirement_name the spec name to search for
|
715
|
+
# @return [Object] the locked spec named `requirement_name`, if one
|
716
|
+
# is found on {#base}
|
717
|
+
def locked_requirement_named(requirement_name)
|
718
|
+
vertex = base.vertex_named(requirement_name)
|
719
|
+
vertex && vertex.payload
|
720
|
+
end
|
721
|
+
|
722
|
+
# Add the current {#possibility} to the dependency graph of the current
|
723
|
+
# {#state}
|
724
|
+
# @return [void]
|
725
|
+
def activate_new_spec
|
726
|
+
conflicts.delete(name)
|
727
|
+
debug(depth) { "Activated #{name} at #{possibility}" }
|
728
|
+
activated.set_payload(name, possibility)
|
729
|
+
require_nested_dependencies_for(possibility)
|
730
|
+
end
|
731
|
+
|
732
|
+
# Requires the dependencies that the recently activated spec has
|
733
|
+
# @param [Object] activated_possibility the PossibilitySet that has just been
|
734
|
+
# activated
|
735
|
+
# @return [void]
|
736
|
+
def require_nested_dependencies_for(possibility_set)
|
737
|
+
nested_dependencies = dependencies_for(possibility_set.latest_version)
|
738
|
+
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
|
739
|
+
nested_dependencies.each do |d|
|
740
|
+
activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
|
741
|
+
parent_index = states.size - 1
|
742
|
+
parents = @parents_of[d]
|
743
|
+
parents << parent_index if parents.empty?
|
744
|
+
end
|
745
|
+
|
746
|
+
push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?)
|
747
|
+
end
|
748
|
+
|
749
|
+
# Pushes a new {DependencyState} that encapsulates both existing and new
|
750
|
+
# requirements
|
751
|
+
# @param [Array] new_requirements
|
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] the proposed requirement
|
771
|
+
# @return [Array] possibilities
|
772
|
+
def possibilities_for_requirement(requirement, activated = self.activated)
|
773
|
+
return [] unless requirement
|
774
|
+
if locked_requirement_named(name_for(requirement))
|
775
|
+
return locked_requirement_possibility_set(requirement, activated)
|
776
|
+
end
|
777
|
+
|
778
|
+
group_possibilities(search_for(requirement))
|
779
|
+
end
|
780
|
+
|
781
|
+
# @param [Object] the proposed requirement
|
782
|
+
# @return [Array] possibility set containing only the locked requirement, if any
|
783
|
+
def locked_requirement_possibility_set(requirement, activated = self.activated)
|
784
|
+
all_possibilities = search_for(requirement)
|
785
|
+
locked_requirement = locked_requirement_named(name_for(requirement))
|
786
|
+
|
787
|
+
# Longwinded way to build a possibilities array with either the locked
|
788
|
+
# requirement or nothing in it. Required, since the API for
|
789
|
+
# locked_requirement isn't guaranteed.
|
790
|
+
locked_possibilities = all_possibilities.select do |possibility|
|
791
|
+
requirement_satisfied_by?(locked_requirement, activated, possibility)
|
792
|
+
end
|
793
|
+
|
794
|
+
group_possibilities(locked_possibilities)
|
795
|
+
end
|
796
|
+
|
797
|
+
# Build an array of PossibilitySets, with each element representing a group of
|
798
|
+
# dependency versions that all have the same sub-dependency version constraints
|
799
|
+
# and are contiguous.
|
800
|
+
# @param [Array] an array of possibilities
|
801
|
+
# @return [Array] an array of possibility sets
|
802
|
+
def group_possibilities(possibilities)
|
803
|
+
possibility_sets = []
|
804
|
+
current_possibility_set = nil
|
805
|
+
|
806
|
+
possibilities.reverse_each do |possibility|
|
807
|
+
dependencies = dependencies_for(possibility)
|
808
|
+
if current_possibility_set && current_possibility_set.dependencies == dependencies
|
809
|
+
current_possibility_set.possibilities.unshift(possibility)
|
810
|
+
else
|
811
|
+
possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
|
812
|
+
current_possibility_set = possibility_sets.first
|
813
|
+
end
|
814
|
+
end
|
815
|
+
|
816
|
+
possibility_sets
|
817
|
+
end
|
818
|
+
|
819
|
+
# Pushes a new {DependencyState}.
|
820
|
+
# If the {#specification_provider} says to
|
821
|
+
# {SpecificationProvider#allow_missing?} that particular requirement, and
|
822
|
+
# there are no possibilities for that requirement, then `state` is not
|
823
|
+
# pushed, and the vertex in {#activated} is removed, and we continue
|
824
|
+
# resolving the remaining requirements.
|
825
|
+
# @param [DependencyState] state
|
826
|
+
# @return [void]
|
827
|
+
def handle_missing_or_push_dependency_state(state)
|
828
|
+
if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement)
|
829
|
+
state.activated.detach_vertex_named(state.name)
|
830
|
+
push_state_for_requirements(state.requirements.dup, false, state.activated)
|
831
|
+
else
|
832
|
+
states.push(state).tap { activated.tag(state) }
|
833
|
+
end
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|