rubygems-update 3.1.2 → 3.2.6
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.
- checksums.yaml +4 -4
- data/{History.txt → CHANGELOG.md} +756 -489
- data/CODE_OF_CONDUCT.md +55 -19
- data/CONTRIBUTING.md +25 -11
- data/Manifest.txt +75 -104
- data/POLICIES.md +6 -12
- data/README.md +5 -5
- data/Rakefile +64 -92
- data/bin/update_rubygems +1 -1
- data/bundler/CHANGELOG.md +1636 -1422
- data/bundler/README.md +6 -8
- data/bundler/UPGRADING.md +18 -32
- data/bundler/bundler.gemspec +4 -4
- data/bundler/exe/bundle +3 -0
- data/bundler/lib/bundler.rb +32 -8
- data/bundler/lib/bundler/build_metadata.rb +4 -12
- data/bundler/lib/bundler/cli.rb +55 -21
- data/bundler/lib/bundler/cli/add.rb +1 -1
- data/bundler/lib/bundler/cli/binstubs.rb +6 -2
- data/bundler/lib/bundler/cli/cache.rb +1 -7
- data/bundler/lib/bundler/cli/clean.rb +1 -1
- data/bundler/lib/bundler/cli/common.rb +14 -0
- data/bundler/lib/bundler/cli/console.rb +1 -1
- data/bundler/lib/bundler/cli/doctor.rb +1 -1
- data/bundler/lib/bundler/cli/exec.rb +4 -4
- data/bundler/lib/bundler/cli/fund.rb +36 -0
- data/bundler/lib/bundler/cli/gem.rb +86 -11
- data/bundler/lib/bundler/cli/info.rb +15 -4
- data/bundler/lib/bundler/cli/init.rb +2 -2
- data/bundler/lib/bundler/cli/inject.rb +1 -1
- data/bundler/lib/bundler/cli/install.rb +16 -13
- data/bundler/lib/bundler/cli/issue.rb +2 -2
- data/bundler/lib/bundler/cli/list.rb +12 -10
- data/bundler/lib/bundler/cli/outdated.rb +87 -66
- data/bundler/lib/bundler/cli/plugin.rb +10 -0
- data/bundler/lib/bundler/cli/pristine.rb +5 -0
- data/bundler/lib/bundler/cli/show.rb +1 -1
- data/bundler/lib/bundler/cli/update.rb +3 -1
- data/bundler/lib/bundler/compact_index_client.rb +1 -1
- data/bundler/lib/bundler/compact_index_client/cache.rb +6 -14
- data/bundler/lib/bundler/compact_index_client/gem_parser.rb +28 -0
- data/bundler/lib/bundler/compact_index_client/updater.rb +5 -13
- data/bundler/lib/bundler/definition.rb +66 -82
- data/bundler/lib/bundler/dep_proxy.rb +16 -9
- data/bundler/lib/bundler/dependency.rb +3 -10
- data/bundler/lib/bundler/dsl.rb +5 -9
- data/bundler/lib/bundler/endpoint_specification.rb +1 -1
- data/bundler/lib/bundler/env.rb +1 -1
- data/bundler/lib/bundler/environment_preserver.rb +26 -2
- data/bundler/lib/bundler/errors.rb +1 -0
- data/bundler/lib/bundler/feature_flag.rb +0 -3
- data/bundler/lib/bundler/fetcher.rb +4 -3
- data/bundler/lib/bundler/fetcher/base.rb +1 -1
- data/bundler/lib/bundler/fetcher/compact_index.rb +1 -1
- data/bundler/lib/bundler/fetcher/downloader.rb +1 -1
- data/bundler/lib/bundler/fetcher/index.rb +3 -4
- data/bundler/lib/bundler/friendly_errors.rb +22 -13
- data/bundler/lib/bundler/gem_helper.rb +33 -19
- data/bundler/lib/bundler/gem_helpers.rb +36 -25
- data/bundler/lib/bundler/gem_version_promoter.rb +4 -4
- data/bundler/lib/bundler/graph.rb +1 -1
- data/bundler/lib/bundler/index.rb +6 -2
- data/bundler/lib/bundler/injector.rb +22 -4
- data/bundler/lib/bundler/inline.rb +2 -2
- data/bundler/lib/bundler/installer.rb +35 -32
- data/bundler/lib/bundler/installer/gem_installer.rb +3 -3
- data/bundler/lib/bundler/installer/parallel_installer.rb +10 -10
- data/bundler/lib/bundler/installer/standalone.rb +2 -2
- data/bundler/lib/bundler/lazy_specification.rb +35 -11
- data/bundler/lib/bundler/lockfile_generator.rb +1 -1
- data/bundler/lib/bundler/lockfile_parser.rb +1 -1
- data/bundler/lib/bundler/man/.document +1 -0
- data/bundler/{man → lib/bundler/man}/bundle-add.1 +1 -1
- data/bundler/{man/bundle-add.ronn → lib/bundler/man/bundle-add.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-binstubs.1 +5 -3
- data/bundler/{man/bundle-binstubs.ronn → lib/bundler/man/bundle-binstubs.1.ronn} +2 -4
- data/bundler/{man → lib/bundler/man}/bundle-cache.1 +1 -1
- data/bundler/{man/bundle-cache.ronn → lib/bundler/man/bundle-cache.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-check.1 +1 -1
- data/bundler/{man/bundle-check.ronn → lib/bundler/man/bundle-check.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-clean.1 +1 -1
- data/bundler/{man/bundle-clean.ronn → lib/bundler/man/bundle-clean.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-config.1 +16 -25
- data/bundler/{man/bundle-config.ronn → lib/bundler/man/bundle-config.1.ronn} +19 -30
- data/bundler/{man → lib/bundler/man}/bundle-doctor.1 +1 -1
- data/bundler/{man/bundle-doctor.ronn → lib/bundler/man/bundle-doctor.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-exec.1 +1 -1
- data/bundler/{man/bundle-exec.ronn → lib/bundler/man/bundle-exec.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-gem.1 +25 -3
- data/bundler/{man/bundle-gem.ronn → lib/bundler/man/bundle-gem.1.ronn} +30 -7
- data/bundler/{man → lib/bundler/man}/bundle-info.1 +1 -1
- data/bundler/{man/bundle-info.ronn → lib/bundler/man/bundle-info.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-init.1 +1 -1
- data/bundler/{man/bundle-init.ronn → lib/bundler/man/bundle-init.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-inject.1 +1 -1
- data/bundler/{man/bundle-inject.ronn → lib/bundler/man/bundle-inject.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-install.1 +30 -3
- data/bundler/{man/bundle-install.ronn → lib/bundler/man/bundle-install.1.ronn} +25 -3
- data/bundler/{man → lib/bundler/man}/bundle-list.1 +7 -7
- data/bundler/{man/bundle-list.ronn → lib/bundler/man/bundle-list.1.ronn} +6 -6
- data/bundler/{man → lib/bundler/man}/bundle-lock.1 +1 -1
- data/bundler/{man/bundle-lock.ronn → lib/bundler/man/bundle-lock.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-open.1 +1 -1
- data/bundler/{man/bundle-open.ronn → lib/bundler/man/bundle-open.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-outdated.1 +1 -1
- data/bundler/{man/bundle-outdated.ronn → lib/bundler/man/bundle-outdated.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-platform.1 +1 -1
- data/bundler/{man/bundle-platform.ronn → lib/bundler/man/bundle-platform.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-pristine.1 +1 -1
- data/bundler/{man/bundle-pristine.ronn → lib/bundler/man/bundle-pristine.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-remove.1 +1 -1
- data/bundler/{man/bundle-remove.ronn → lib/bundler/man/bundle-remove.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-show.1 +1 -1
- data/bundler/{man/bundle-show.ronn → lib/bundler/man/bundle-show.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-update.1 +1 -1
- data/bundler/{man/bundle-update.ronn → lib/bundler/man/bundle-update.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle-viz.1 +1 -1
- data/bundler/{man/bundle-viz.ronn → lib/bundler/man/bundle-viz.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/bundle.1 +1 -1
- data/bundler/{man/bundle.ronn → lib/bundler/man/bundle.1.ronn} +0 -0
- data/bundler/{man → lib/bundler/man}/gemfile.5 +4 -4
- data/bundler/{man → lib/bundler/man}/gemfile.5.ronn +4 -4
- data/bundler/{man → lib/bundler/man}/index.txt +0 -0
- data/bundler/lib/bundler/mirror.rb +2 -2
- data/bundler/lib/bundler/plugin.rb +30 -5
- data/bundler/lib/bundler/plugin/api/source.rb +1 -1
- data/bundler/lib/bundler/plugin/dsl.rb +1 -1
- data/bundler/lib/bundler/plugin/index.rb +10 -1
- data/bundler/lib/bundler/plugin/installer.rb +1 -1
- data/bundler/lib/bundler/plugin/installer/rubygems.rb +1 -1
- data/bundler/lib/bundler/plugin/source_list.rb +1 -1
- data/bundler/lib/bundler/psyched_yaml.rb +0 -15
- data/bundler/lib/bundler/remote_specification.rb +5 -2
- data/bundler/lib/bundler/resolver.rb +43 -19
- data/bundler/lib/bundler/resolver/spec_group.rb +39 -24
- data/bundler/lib/bundler/retry.rb +1 -1
- data/bundler/lib/bundler/ruby_version.rb +1 -1
- data/bundler/lib/bundler/rubygems_ext.rb +69 -9
- data/bundler/lib/bundler/rubygems_gem_installer.rb +3 -9
- data/bundler/lib/bundler/rubygems_integration.rb +26 -61
- data/bundler/lib/bundler/runtime.rb +4 -14
- data/bundler/lib/bundler/settings.rb +49 -46
- data/bundler/lib/bundler/shared_helpers.rb +2 -2
- data/bundler/lib/bundler/similarity_detector.rb +1 -1
- data/bundler/lib/bundler/source.rb +1 -1
- data/bundler/lib/bundler/source/git.rb +23 -21
- data/bundler/lib/bundler/source/git/git_proxy.rb +82 -80
- data/bundler/lib/bundler/source/path.rb +7 -3
- data/bundler/lib/bundler/source/path/installer.rb +10 -10
- data/bundler/lib/bundler/source/rubygems.rb +23 -17
- data/bundler/lib/bundler/source/rubygems/remote.rb +1 -1
- data/bundler/lib/bundler/source_list.rb +2 -2
- data/bundler/lib/bundler/spec_set.rb +8 -10
- data/bundler/lib/bundler/stub_specification.rb +17 -7
- data/bundler/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +57 -47
- data/bundler/lib/bundler/templates/newgem/Gemfile.tt +9 -1
- data/bundler/lib/bundler/templates/newgem/README.md.tt +1 -2
- data/bundler/lib/bundler/templates/newgem/Rakefile.tt +19 -5
- data/bundler/lib/bundler/templates/newgem/bin/console.tt +1 -0
- data/bundler/lib/bundler/templates/newgem/circleci/config.yml.tt +13 -0
- data/bundler/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +2 -0
- data/bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt +18 -0
- data/bundler/lib/bundler/templates/newgem/gitlab-ci.yml.tt +9 -0
- data/bundler/lib/bundler/templates/newgem/lib/newgem.rb.tt +4 -2
- data/bundler/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +2 -0
- data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +15 -7
- data/bundler/lib/bundler/templates/newgem/rubocop.yml.tt +13 -0
- data/bundler/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +2 -0
- data/bundler/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -1
- data/bundler/lib/bundler/templates/newgem/test/{newgem_test.rb.tt → minitest/newgem_test.rb.tt} +2 -0
- data/bundler/lib/bundler/templates/newgem/test/{test_helper.rb.tt → minitest/test_helper.rb.tt} +2 -0
- data/bundler/lib/bundler/templates/newgem/test/test-unit/newgem_test.rb.tt +15 -0
- data/bundler/lib/bundler/templates/newgem/test/test-unit/test_helper.rb.tt +6 -0
- data/bundler/lib/bundler/ui/shell.rb +5 -5
- data/bundler/lib/bundler/uri_credentials_filter.rb +3 -1
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo.rb +0 -1
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +34 -1
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +2 -2
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +1 -1
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +1 -1
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +48 -46
- data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +79 -208
- data/bundler/lib/bundler/vendor/thor/lib/thor.rb +0 -7
- data/bundler/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +2 -1
- data/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +9 -0
- data/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
- data/bundler/lib/bundler/vendor/tmpdir/lib/tmpdir.rb +154 -0
- data/bundler/lib/bundler/vendored_persistent.rb +0 -7
- data/bundler/lib/bundler/vendored_tmpdir.rb +4 -0
- data/bundler/lib/bundler/version.rb +1 -1
- data/bundler/lib/bundler/worker.rb +1 -1
- data/bundler/lib/bundler/yaml_serializer.rb +1 -1
- data/lib/rubygems.rb +138 -187
- data/lib/rubygems/available_set.rb +4 -6
- data/lib/rubygems/basic_specification.rb +12 -10
- data/lib/rubygems/bundler_version_finder.rb +14 -9
- data/lib/rubygems/command.rb +17 -17
- data/lib/rubygems/command_manager.rb +5 -6
- data/lib/rubygems/commands/build_command.rb +40 -20
- data/lib/rubygems/commands/cert_command.rb +2 -10
- data/lib/rubygems/commands/check_command.rb +0 -2
- data/lib/rubygems/commands/cleanup_command.rb +11 -7
- data/lib/rubygems/commands/contents_command.rb +4 -6
- data/lib/rubygems/commands/dependency_command.rb +6 -8
- data/lib/rubygems/commands/environment_command.rb +1 -3
- data/lib/rubygems/commands/fetch_command.rb +2 -4
- data/lib/rubygems/commands/generate_index_command.rb +0 -2
- data/lib/rubygems/commands/help_command.rb +4 -4
- data/lib/rubygems/commands/info_command.rb +8 -5
- data/lib/rubygems/commands/install_command.rb +3 -5
- data/lib/rubygems/commands/list_command.rb +8 -7
- data/lib/rubygems/commands/lock_command.rb +1 -3
- data/lib/rubygems/commands/mirror_command.rb +0 -2
- data/lib/rubygems/commands/open_command.rb +0 -4
- data/lib/rubygems/commands/outdated_command.rb +0 -2
- data/lib/rubygems/commands/owner_command.rb +9 -4
- data/lib/rubygems/commands/pristine_command.rb +11 -5
- data/lib/rubygems/commands/push_command.rb +10 -47
- data/lib/rubygems/commands/query_command.rb +14 -344
- data/lib/rubygems/commands/rdoc_command.rb +0 -2
- data/lib/rubygems/commands/search_command.rb +7 -7
- data/lib/rubygems/commands/server_command.rb +3 -1
- data/lib/rubygems/commands/setup_command.rb +101 -74
- data/lib/rubygems/commands/signin_command.rb +0 -2
- data/lib/rubygems/commands/signout_command.rb +0 -2
- data/lib/rubygems/commands/sources_command.rb +9 -7
- data/lib/rubygems/commands/specification_command.rb +8 -4
- data/lib/rubygems/commands/stale_command.rb +1 -3
- data/lib/rubygems/commands/uninstall_command.rb +2 -4
- data/lib/rubygems/commands/unpack_command.rb +1 -3
- data/lib/rubygems/commands/update_command.rb +59 -14
- data/lib/rubygems/commands/which_command.rb +0 -2
- data/lib/rubygems/commands/yank_command.rb +4 -7
- data/lib/rubygems/config_file.rb +11 -4
- data/lib/rubygems/core_ext/kernel_require.rb +29 -36
- data/lib/rubygems/core_ext/kernel_warn.rb +12 -13
- data/lib/rubygems/defaults.rb +101 -7
- data/lib/rubygems/dependency.rb +3 -8
- data/lib/rubygems/dependency_installer.rb +6 -78
- data/lib/rubygems/dependency_list.rb +7 -9
- data/lib/rubygems/deprecate.rb +46 -1
- data/lib/rubygems/doctor.rb +4 -4
- data/lib/rubygems/errors.rb +3 -14
- data/lib/rubygems/exceptions.rb +2 -33
- data/lib/rubygems/ext.rb +6 -6
- data/lib/rubygems/ext/build_error.rb +2 -0
- data/lib/rubygems/ext/builder.rb +21 -39
- data/lib/rubygems/ext/cmake_builder.rb +6 -9
- data/lib/rubygems/ext/configure_builder.rb +5 -8
- data/lib/rubygems/ext/ext_conf_builder.rb +21 -19
- data/lib/rubygems/ext/rake_builder.rb +4 -6
- data/lib/rubygems/gem_runner.rb +3 -10
- data/lib/rubygems/gemcutter_utilities.rb +102 -21
- data/lib/rubygems/indexer.rb +1 -22
- data/lib/rubygems/install_update_options.rb +7 -7
- data/lib/rubygems/installer.rb +59 -80
- data/lib/rubygems/installer_test_case.rb +25 -11
- data/lib/rubygems/installer_uninstaller_utils.rb +24 -0
- data/lib/rubygems/local_remote_options.rb +1 -1
- data/lib/rubygems/mock_gem_ui.rb +0 -6
- data/lib/rubygems/name_tuple.rb +3 -7
- data/lib/rubygems/openssl.rb +7 -0
- data/lib/rubygems/package.rb +14 -25
- data/lib/rubygems/package/digest_io.rb +0 -2
- data/lib/rubygems/package/file_source.rb +0 -2
- data/lib/rubygems/package/io_source.rb +0 -2
- data/lib/rubygems/package/old.rb +1 -3
- data/lib/rubygems/package/tar_header.rb +4 -6
- data/lib/rubygems/package/tar_reader.rb +0 -3
- data/lib/rubygems/package/tar_reader/entry.rb +0 -3
- data/lib/rubygems/package/tar_test_case.rb +2 -4
- data/lib/rubygems/package/tar_writer.rb +2 -12
- data/lib/rubygems/package_task.rb +1 -7
- data/lib/rubygems/path_support.rb +1 -3
- data/lib/rubygems/platform.rb +21 -16
- data/lib/rubygems/psych_tree.rb +0 -2
- data/lib/rubygems/query_utils.rb +353 -0
- data/lib/rubygems/rdoc.rb +0 -12
- data/lib/rubygems/remote_fetcher.rb +14 -29
- data/lib/rubygems/request.rb +4 -11
- data/lib/rubygems/request/connection_pools.rb +1 -5
- data/lib/rubygems/request/http_pool.rb +0 -2
- data/lib/rubygems/request/https_pool.rb +0 -2
- data/lib/rubygems/request_set.rb +7 -20
- data/lib/rubygems/request_set/gem_dependency_api.rb +6 -8
- data/lib/rubygems/request_set/lockfile.rb +8 -12
- data/lib/rubygems/request_set/lockfile/parser.rb +0 -2
- data/lib/rubygems/request_set/lockfile/tokenizer.rb +1 -3
- data/lib/rubygems/requirement.rb +21 -22
- data/lib/rubygems/resolver.rb +14 -12
- data/lib/rubygems/resolver/activation_request.rb +9 -3
- data/lib/rubygems/resolver/api_set.rb +31 -24
- data/lib/rubygems/resolver/api_set/gem_parser.rb +20 -0
- data/lib/rubygems/resolver/api_specification.rb +24 -10
- data/lib/rubygems/resolver/best_set.rb +2 -4
- data/lib/rubygems/resolver/composed_set.rb +3 -5
- data/lib/rubygems/resolver/conflict.rb +2 -4
- data/lib/rubygems/resolver/current_set.rb +0 -2
- data/lib/rubygems/resolver/dependency_request.rb +1 -3
- data/lib/rubygems/resolver/git_set.rb +0 -2
- data/lib/rubygems/resolver/git_specification.rb +0 -2
- data/lib/rubygems/resolver/index_set.rb +1 -3
- data/lib/rubygems/resolver/index_specification.rb +29 -2
- data/lib/rubygems/resolver/installed_specification.rb +0 -2
- data/lib/rubygems/resolver/installer_set.rb +60 -13
- data/lib/rubygems/resolver/local_specification.rb +0 -2
- data/lib/rubygems/resolver/lock_set.rb +2 -4
- data/lib/rubygems/resolver/lock_specification.rb +0 -2
- data/lib/rubygems/resolver/molinillo/lib/molinillo.rb +6 -5
- data/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb +7 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb +1 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +39 -5
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb +1 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +2 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +2 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +2 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +2 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb +7 -6
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb +2 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb +4 -3
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb +43 -10
- data/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +75 -7
- data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +2 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb +1 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb +3 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +506 -165
- data/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb +3 -2
- data/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb +8 -4
- data/lib/rubygems/resolver/requirement_list.rb +0 -2
- data/lib/rubygems/resolver/set.rb +0 -2
- data/lib/rubygems/resolver/source_set.rb +0 -2
- data/lib/rubygems/resolver/spec_specification.rb +14 -2
- data/lib/rubygems/resolver/specification.rb +13 -3
- data/lib/rubygems/resolver/stats.rb +0 -2
- data/lib/rubygems/resolver/vendor_set.rb +0 -2
- data/lib/rubygems/resolver/vendor_specification.rb +0 -2
- data/lib/rubygems/s3_uri_signer.rb +2 -8
- data/lib/rubygems/safe_yaml.rb +4 -4
- data/lib/rubygems/security.rb +27 -34
- data/lib/rubygems/security/policy.rb +4 -8
- data/lib/rubygems/security/signer.rb +5 -7
- data/lib/rubygems/security/trust_dir.rb +1 -3
- data/lib/rubygems/server.rb +16 -13
- data/lib/rubygems/source.rb +23 -12
- data/lib/rubygems/source/git.rb +7 -8
- data/lib/rubygems/source/installed.rb +0 -2
- data/lib/rubygems/source/local.rb +2 -4
- data/lib/rubygems/source/lock.rb +0 -2
- data/lib/rubygems/source/specific_file.rb +0 -2
- data/lib/rubygems/source/vendor.rb +0 -2
- data/lib/rubygems/source_list.rb +4 -7
- data/lib/rubygems/spec_fetcher.rb +19 -18
- data/lib/rubygems/specification.rb +122 -131
- data/lib/rubygems/specification_policy.rb +88 -30
- data/lib/rubygems/ssl_certs/{index.rubygems.org → rubygems.org}/GlobalSignRootCA.pem +0 -0
- data/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem +21 -0
- data/lib/rubygems/stub_specification.rb +1 -5
- data/lib/rubygems/syck_hack.rb +0 -2
- data/lib/rubygems/test_case.rb +120 -134
- data/lib/rubygems/test_utilities.rb +12 -19
- data/lib/rubygems/uninstaller.rb +35 -16
- data/lib/rubygems/uri_formatter.rb +2 -3
- data/lib/rubygems/uri_parser.rb +0 -2
- data/lib/rubygems/user_interaction.rb +1 -26
- data/lib/rubygems/util.rb +15 -3
- data/lib/rubygems/util/licenses.rb +4 -6
- data/lib/rubygems/util/list.rb +0 -2
- data/lib/rubygems/validator.rb +1 -3
- data/lib/rubygems/version.rb +5 -7
- data/lib/rubygems/version_option.rb +6 -0
- data/rubygems-update.gemspec +3 -3
- data/setup.rb +2 -7
- data/test/rubygems/data/null-required-rubygems-version.gemspec.rz +0 -0
- data/test/rubygems/plugin/load/rubygems_plugin.rb +0 -2
- data/test/rubygems/rubygems/commands/crash_command.rb +0 -2
- data/test/rubygems/rubygems_plugin.rb +0 -2
- data/test/rubygems/specifications/bar-0.0.2.gemspec +0 -2
- data/test/rubygems/specifications/rubyforge-0.0.1.gemspec +12 -0
- data/test/rubygems/test_bundled_ca.rb +42 -45
- data/test/rubygems/test_config.rb +0 -2
- data/test/rubygems/test_deprecate.rb +40 -7
- data/test/rubygems/test_gem.rb +156 -99
- data/test/rubygems/test_gem_available_set.rb +3 -5
- data/test/rubygems/test_gem_bundler_version_finder.rb +19 -3
- data/test/rubygems/test_gem_command.rb +24 -7
- data/test/rubygems/test_gem_command_manager.rb +36 -5
- data/test/rubygems/test_gem_commands_build_command.rb +250 -15
- data/test/rubygems/test_gem_commands_cert_command.rb +4 -6
- data/test/rubygems/test_gem_commands_check_command.rb +0 -2
- data/test/rubygems/test_gem_commands_cleanup_command.rb +14 -5
- data/test/rubygems/test_gem_commands_contents_command.rb +50 -19
- data/test/rubygems/test_gem_commands_dependency_command.rb +0 -2
- data/test/rubygems/test_gem_commands_environment_command.rb +21 -23
- data/test/rubygems/test_gem_commands_fetch_command.rb +0 -2
- data/test/rubygems/test_gem_commands_generate_index_command.rb +1 -7
- data/test/rubygems/test_gem_commands_help_command.rb +15 -4
- data/test/rubygems/test_gem_commands_info_command.rb +6 -8
- data/test/rubygems/test_gem_commands_install_command.rb +163 -34
- data/test/rubygems/test_gem_commands_list_command.rb +0 -2
- data/test/rubygems/test_gem_commands_lock_command.rb +0 -2
- data/test/rubygems/test_gem_commands_mirror.rb +1 -3
- data/test/rubygems/test_gem_commands_open_command.rb +4 -6
- data/test/rubygems/test_gem_commands_outdated_command.rb +0 -2
- data/test/rubygems/test_gem_commands_owner_command.rb +59 -5
- data/test/rubygems/test_gem_commands_pristine_command.rb +43 -12
- data/test/rubygems/test_gem_commands_push_command.rb +77 -9
- data/test/rubygems/test_gem_commands_query_command.rb +12 -12
- data/test/rubygems/test_gem_commands_search_command.rb +0 -2
- data/test/rubygems/test_gem_commands_server_command.rb +0 -2
- data/test/rubygems/test_gem_commands_setup_command.rb +160 -135
- data/test/rubygems/test_gem_commands_signin_command.rb +33 -9
- data/test/rubygems/test_gem_commands_signout_command.rb +0 -7
- data/test/rubygems/test_gem_commands_sources_command.rb +99 -3
- data/test/rubygems/test_gem_commands_specification_command.rb +46 -20
- data/test/rubygems/test_gem_commands_stale_command.rb +0 -2
- data/test/rubygems/test_gem_commands_uninstall_command.rb +2 -3
- data/test/rubygems/test_gem_commands_unpack_command.rb +0 -2
- data/test/rubygems/test_gem_commands_update_command.rb +116 -7
- data/test/rubygems/test_gem_commands_which_command.rb +3 -5
- data/test/rubygems/test_gem_commands_yank_command.rb +44 -8
- data/test/rubygems/test_gem_config_file.rb +7 -12
- data/test/rubygems/test_gem_dependency.rb +0 -2
- data/test/rubygems/test_gem_dependency_installer.rb +116 -239
- data/test/rubygems/test_gem_dependency_list.rb +10 -12
- data/test/rubygems/test_gem_dependency_resolution_error.rb +1 -3
- data/test/rubygems/test_gem_doctor.rb +28 -2
- data/test/rubygems/test_gem_ext_builder.rb +50 -47
- data/test/rubygems/test_gem_ext_cmake_builder.rb +16 -25
- data/test/rubygems/test_gem_ext_configure_builder.rb +6 -22
- data/test/rubygems/test_gem_ext_ext_conf_builder.rb +9 -29
- data/test/rubygems/test_gem_ext_rake_builder.rb +39 -24
- data/test/rubygems/test_gem_gem_runner.rb +44 -1
- data/test/rubygems/test_gem_gemcutter_utilities.rb +8 -5
- data/test/rubygems/test_gem_impossible_dependencies_error.rb +0 -2
- data/test/rubygems/test_gem_indexer.rb +9 -15
- data/test/rubygems/test_gem_install_update_options.rb +14 -4
- data/test/rubygems/test_gem_installer.rb +258 -115
- data/test/rubygems/test_gem_local_remote_options.rb +0 -2
- data/test/rubygems/test_gem_name_tuple.rb +0 -2
- data/test/rubygems/test_gem_package.rb +41 -39
- data/test/rubygems/test_gem_package_old.rb +4 -6
- data/test/rubygems/test_gem_package_tar_header.rb +18 -1
- data/test/rubygems/test_gem_package_tar_reader.rb +0 -2
- data/test/rubygems/test_gem_package_tar_reader_entry.rb +0 -2
- data/test/rubygems/test_gem_package_tar_writer.rb +9 -6
- data/test/rubygems/test_gem_package_task.rb +46 -13
- data/test/rubygems/test_gem_path_support.rb +0 -2
- data/test/rubygems/test_gem_platform.rb +71 -6
- data/test/rubygems/test_gem_rdoc.rb +0 -2
- data/test/rubygems/test_gem_remote_fetcher.rb +169 -212
- data/test/rubygems/test_gem_request.rb +13 -17
- data/test/rubygems/test_gem_request_connection_pools.rb +0 -4
- data/test/rubygems/test_gem_request_set.rb +72 -22
- data/test/rubygems/test_gem_request_set_gem_dependency_api.rb +3 -5
- data/test/rubygems/test_gem_request_set_lockfile.rb +4 -6
- data/test/rubygems/test_gem_request_set_lockfile_parser.rb +9 -11
- data/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb +118 -120
- data/test/rubygems/test_gem_requirement.rb +43 -3
- data/test/rubygems/test_gem_resolver.rb +6 -8
- data/test/rubygems/test_gem_resolver_activation_request.rb +0 -2
- data/test/rubygems/test_gem_resolver_api_set.rb +60 -59
- data/test/rubygems/test_gem_resolver_api_specification.rb +3 -5
- data/test/rubygems/test_gem_resolver_best_set.rb +27 -6
- data/test/rubygems/test_gem_resolver_composed_set.rb +0 -2
- data/test/rubygems/test_gem_resolver_conflict.rb +1 -3
- data/test/rubygems/test_gem_resolver_dependency_request.rb +0 -2
- data/test/rubygems/test_gem_resolver_git_set.rb +0 -2
- data/test/rubygems/test_gem_resolver_git_specification.rb +0 -2
- data/test/rubygems/test_gem_resolver_index_set.rb +2 -4
- data/test/rubygems/test_gem_resolver_index_specification.rb +0 -2
- data/test/rubygems/test_gem_resolver_installed_specification.rb +0 -2
- data/test/rubygems/test_gem_resolver_installer_set.rb +7 -9
- data/test/rubygems/test_gem_resolver_local_specification.rb +0 -2
- data/test/rubygems/test_gem_resolver_lock_set.rb +3 -5
- data/test/rubygems/test_gem_resolver_lock_specification.rb +0 -2
- data/test/rubygems/test_gem_resolver_requirement_list.rb +0 -2
- data/test/rubygems/test_gem_resolver_specification.rb +0 -4
- data/test/rubygems/test_gem_resolver_vendor_set.rb +1 -3
- data/test/rubygems/test_gem_resolver_vendor_specification.rb +0 -2
- data/test/rubygems/test_gem_security.rb +22 -24
- data/test/rubygems/test_gem_security_policy.rb +7 -12
- data/test/rubygems/test_gem_security_signer.rb +10 -12
- data/test/rubygems/test_gem_security_trust_dir.rb +4 -6
- data/test/rubygems/test_gem_server.rb +10 -14
- data/test/rubygems/test_gem_silent_ui.rb +0 -2
- data/test/rubygems/test_gem_source.rb +19 -18
- data/test/rubygems/test_gem_source_fetch_problem.rb +0 -2
- data/test/rubygems/test_gem_source_git.rb +12 -13
- data/test/rubygems/test_gem_source_installed.rb +7 -9
- data/test/rubygems/test_gem_source_list.rb +1 -2
- data/test/rubygems/test_gem_source_local.rb +8 -10
- data/test/rubygems/test_gem_source_lock.rb +10 -12
- data/test/rubygems/test_gem_source_specific_file.rb +7 -9
- data/test/rubygems/test_gem_source_subpath_problem.rb +49 -0
- data/test/rubygems/test_gem_source_vendor.rb +7 -9
- data/test/rubygems/test_gem_spec_fetcher.rb +11 -4
- data/test/rubygems/test_gem_specification.rb +188 -131
- data/test/rubygems/test_gem_stream_ui.rb +3 -3
- data/test/rubygems/test_gem_stub_specification.rb +4 -7
- data/test/rubygems/test_gem_text.rb +1 -3
- data/test/rubygems/test_gem_uninstaller.rb +134 -12
- data/test/rubygems/test_gem_unsatisfiable_dependency_error.rb +0 -2
- data/test/rubygems/test_gem_uri_formatter.rb +0 -2
- data/test/rubygems/test_gem_util.rb +7 -7
- data/test/rubygems/test_gem_validator.rb +1 -3
- data/test/rubygems/test_gem_version.rb +1 -3
- data/test/rubygems/test_gem_version_option.rb +1 -3
- data/test/rubygems/test_kernel.rb +25 -10
- data/test/rubygems/test_project_sanity.rb +7 -2
- data/test/rubygems/test_remote_fetch_error.rb +0 -2
- data/test/rubygems/test_require.rb +291 -56
- data/test/test_changelog_generator.rb +17 -0
- metadata +79 -133
- data/.bundle/config +0 -2
- data/.rubocop.yml +0 -91
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -43
- data/bundler/CODE_OF_CONDUCT.md +0 -136
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +0 -26
- data/bundler/man/bundle-add.1.txt +0 -58
- data/bundler/man/bundle-binstubs.1.txt +0 -48
- data/bundler/man/bundle-cache.1.txt +0 -78
- data/bundler/man/bundle-check.1.txt +0 -33
- data/bundler/man/bundle-clean.1.txt +0 -26
- data/bundler/man/bundle-config.1.txt +0 -528
- data/bundler/man/bundle-doctor.1.txt +0 -44
- data/bundler/man/bundle-exec.1.txt +0 -178
- data/bundler/man/bundle-gem.1.txt +0 -91
- data/bundler/man/bundle-info.1.txt +0 -21
- data/bundler/man/bundle-init.1.txt +0 -34
- data/bundler/man/bundle-inject.1.txt +0 -32
- data/bundler/man/bundle-install.1.txt +0 -401
- data/bundler/man/bundle-list.1.txt +0 -43
- data/bundler/man/bundle-lock.1.txt +0 -93
- data/bundler/man/bundle-open.1.txt +0 -29
- data/bundler/man/bundle-outdated.1.txt +0 -131
- data/bundler/man/bundle-platform.1.txt +0 -57
- data/bundler/man/bundle-pristine.1.txt +0 -44
- data/bundler/man/bundle-remove.1.txt +0 -34
- data/bundler/man/bundle-show.1.txt +0 -27
- data/bundler/man/bundle-update.1.txt +0 -390
- data/bundler/man/bundle-viz.1.txt +0 -39
- data/bundler/man/bundle.1.txt +0 -116
- data/bundler/man/gemfile.5.txt +0 -649
- data/lib/rubygems/source_local.rb +0 -7
- data/lib/rubygems/source_specific_file.rb +0 -6
- data/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +0 -23
- data/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +0 -25
- data/lib/ubygems.rb +0 -14
- data/tmp/.keep +0 -0
- data/util/CL2notes +0 -55
- data/util/bisect +0 -10
- data/util/ci.sh +0 -62
- data/util/cops/deprecations.rb +0 -52
- data/util/create_certs.rb +0 -171
- data/util/create_certs.sh +0 -27
- data/util/create_encrypted_key.rb +0 -16
- data/util/generate_spdx_license_list.rb +0 -63
- data/util/patch_with_prs.rb +0 -77
- data/util/rubocop +0 -8
- data/util/update_bundled_ca_certificates.rb +0 -139
- data/util/update_changelog.rb +0 -67
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'set'
|
3
4
|
require 'tsort'
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
require_relative 'dependency_graph/log'
|
7
|
+
require_relative 'dependency_graph/vertex'
|
7
8
|
|
8
9
|
module Gem::Resolver::Molinillo
|
9
10
|
# A directed acyclic graph that is tuned to hold named dependencies
|
@@ -123,6 +124,7 @@ module Gem::Resolver::Molinillo
|
|
123
124
|
dot.join("\n")
|
124
125
|
end
|
125
126
|
|
127
|
+
# @param [DependencyGraph] other
|
126
128
|
# @return [Boolean] whether the two dependency graphs are equal, determined
|
127
129
|
# by a recursive traversal of each {#root_vertices} and its
|
128
130
|
# {Vertex#successors}
|
@@ -147,8 +149,8 @@ module Gem::Resolver::Molinillo
|
|
147
149
|
vertex = add_vertex(name, payload, root)
|
148
150
|
vertex.explicit_requirements << requirement if root
|
149
151
|
parent_names.each do |parent_name|
|
150
|
-
|
151
|
-
add_edge(
|
152
|
+
parent_vertex = vertex_named(parent_name)
|
153
|
+
add_edge(parent_vertex, vertex, requirement)
|
152
154
|
end
|
153
155
|
vertex
|
154
156
|
end
|
@@ -189,7 +191,7 @@ module Gem::Resolver::Molinillo
|
|
189
191
|
# @return [Edge] the added edge
|
190
192
|
def add_edge(origin, destination, requirement)
|
191
193
|
if destination.path_to?(origin)
|
192
|
-
raise CircularDependencyError.new(
|
194
|
+
raise CircularDependencyError.new(path(destination, origin))
|
193
195
|
end
|
194
196
|
add_edge_no_circular(origin, destination, requirement)
|
195
197
|
end
|
@@ -218,5 +220,37 @@ module Gem::Resolver::Molinillo
|
|
218
220
|
def add_edge_no_circular(origin, destination, requirement)
|
219
221
|
log.add_edge_no_circular(self, origin.name, destination.name, requirement)
|
220
222
|
end
|
223
|
+
|
224
|
+
# Returns the path between two vertices
|
225
|
+
# @raise [ArgumentError] if there is no path between the vertices
|
226
|
+
# @param [Vertex] from
|
227
|
+
# @param [Vertex] to
|
228
|
+
# @return [Array<Vertex>] the shortest path from `from` to `to`
|
229
|
+
def path(from, to)
|
230
|
+
distances = Hash.new(vertices.size + 1)
|
231
|
+
distances[from.name] = 0
|
232
|
+
predecessors = {}
|
233
|
+
each do |vertex|
|
234
|
+
vertex.successors.each do |successor|
|
235
|
+
if distances[successor.name] > distances[vertex.name] + 1
|
236
|
+
distances[successor.name] = distances[vertex.name] + 1
|
237
|
+
predecessors[successor] = vertex
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
path = [to]
|
243
|
+
while before = predecessors[to]
|
244
|
+
path << before
|
245
|
+
to = before
|
246
|
+
break if to == from
|
247
|
+
end
|
248
|
+
|
249
|
+
unless path.last.equal?(from)
|
250
|
+
raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
|
251
|
+
end
|
252
|
+
|
253
|
+
path.reverse
|
254
|
+
end
|
221
255
|
end
|
222
256
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
|
3
|
+
require_relative 'add_edge_no_circular'
|
4
|
+
require_relative 'add_vertex'
|
5
|
+
require_relative 'delete_edge'
|
6
|
+
require_relative 'detach_vertex_named'
|
7
|
+
require_relative 'set_payload'
|
8
|
+
require_relative 'tag'
|
8
9
|
|
9
10
|
module Gem::Resolver::Molinillo
|
10
11
|
class DependencyGraph
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require_relative 'action'
|
3
4
|
module Gem::Resolver::Molinillo
|
4
5
|
class DependencyGraph
|
5
6
|
# @!visibility private
|
@@ -13,11 +14,11 @@ module Gem::Resolver::Molinillo
|
|
13
14
|
end
|
14
15
|
|
15
16
|
# (see Action#up)
|
16
|
-
def up(
|
17
|
+
def up(graph)
|
17
18
|
end
|
18
19
|
|
19
20
|
# (see Action#down)
|
20
|
-
def down(
|
21
|
+
def down(graph)
|
21
22
|
end
|
22
23
|
|
23
24
|
# @!group Tag
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Gem::Resolver::Molinillo
|
3
4
|
class DependencyGraph
|
4
5
|
# A vertex in a {DependencyGraph} that encapsulates a {#name} and a
|
@@ -32,7 +33,7 @@ module Gem::Resolver::Molinillo
|
|
32
33
|
# @return [Array<Object>] all of the requirements that required
|
33
34
|
# this vertex
|
34
35
|
def requirements
|
35
|
-
incoming_edges.map(&:requirement) + explicit_requirements
|
36
|
+
(incoming_edges.map(&:requirement) + explicit_requirements).uniq
|
36
37
|
end
|
37
38
|
|
38
39
|
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
@@ -49,14 +50,25 @@ module Gem::Resolver::Molinillo
|
|
49
50
|
incoming_edges.map(&:origin)
|
50
51
|
end
|
51
52
|
|
52
|
-
# @return [
|
53
|
+
# @return [Set<Vertex>] the vertices of {#graph} where `self` is a
|
53
54
|
# {#descendent?}
|
54
55
|
def recursive_predecessors
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
_recursive_predecessors
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param [Set<Vertex>] vertices the set to add the predecessors to
|
60
|
+
# @return [Set<Vertex>] the vertices of {#graph} where `self` is a
|
61
|
+
# {#descendent?}
|
62
|
+
def _recursive_predecessors(vertices = Set.new)
|
63
|
+
incoming_edges.each do |edge|
|
64
|
+
vertex = edge.origin
|
65
|
+
next unless vertices.add?(vertex)
|
66
|
+
vertex._recursive_predecessors(vertices)
|
67
|
+
end
|
68
|
+
|
58
69
|
vertices
|
59
70
|
end
|
71
|
+
protected :_recursive_predecessors
|
60
72
|
|
61
73
|
# @return [Array<Vertex>] the vertices of {#graph} that have an edge with
|
62
74
|
# `self` as their {Edge#origin}
|
@@ -64,14 +76,25 @@ module Gem::Resolver::Molinillo
|
|
64
76
|
outgoing_edges.map(&:destination)
|
65
77
|
end
|
66
78
|
|
67
|
-
# @return [
|
79
|
+
# @return [Set<Vertex>] the vertices of {#graph} where `self` is an
|
68
80
|
# {#ancestor?}
|
69
81
|
def recursive_successors
|
70
|
-
|
71
|
-
|
72
|
-
|
82
|
+
_recursive_successors
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [Set<Vertex>] vertices the set to add the successors to
|
86
|
+
# @return [Set<Vertex>] the vertices of {#graph} where `self` is an
|
87
|
+
# {#ancestor?}
|
88
|
+
def _recursive_successors(vertices = Set.new)
|
89
|
+
outgoing_edges.each do |edge|
|
90
|
+
vertex = edge.destination
|
91
|
+
next unless vertices.add?(vertex)
|
92
|
+
vertex._recursive_successors(vertices)
|
93
|
+
end
|
94
|
+
|
73
95
|
vertices
|
74
96
|
end
|
97
|
+
protected :_recursive_successors
|
75
98
|
|
76
99
|
# @return [String] a string suitable for debugging
|
77
100
|
def inspect
|
@@ -107,11 +130,21 @@ module Gem::Resolver::Molinillo
|
|
107
130
|
# dependency graph?
|
108
131
|
# @return true iff there is a path following edges within this {#graph}
|
109
132
|
def path_to?(other)
|
110
|
-
|
133
|
+
_path_to?(other)
|
111
134
|
end
|
112
135
|
|
113
136
|
alias descendent? path_to?
|
114
137
|
|
138
|
+
# @param [Vertex] other the vertex to check if there's a path to
|
139
|
+
# @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
|
140
|
+
# @return [Boolean] whether there is a path to `other` from `self`
|
141
|
+
def _path_to?(other, visited = Set.new)
|
142
|
+
return false unless visited.add?(self)
|
143
|
+
return true if equal?(other)
|
144
|
+
successors.any? { |v| v._path_to?(other, visited) }
|
145
|
+
end
|
146
|
+
protected :_path_to?
|
147
|
+
|
115
148
|
# Is there a path from `other` to `self` following edges in the
|
116
149
|
# dependency graph?
|
117
150
|
# @return true iff there is a path following edges within this {#graph}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Gem::Resolver::Molinillo
|
3
4
|
# An error that occurred during the resolution process
|
4
5
|
class ResolverError < StandardError; end
|
@@ -17,7 +18,7 @@ module Gem::Resolver::Molinillo
|
|
17
18
|
# @param [Array<Object>] required_by @see {#required_by}
|
18
19
|
def initialize(dependency, required_by = [])
|
19
20
|
@dependency = dependency
|
20
|
-
@required_by = required_by
|
21
|
+
@required_by = required_by.uniq
|
21
22
|
super()
|
22
23
|
end
|
23
24
|
|
@@ -41,11 +42,11 @@ module Gem::Resolver::Molinillo
|
|
41
42
|
attr_reader :dependencies
|
42
43
|
|
43
44
|
# Initializes a new error with the given circular vertices.
|
44
|
-
# @param [Array<DependencyGraph::Vertex>]
|
45
|
+
# @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
|
45
46
|
# that caused the error
|
46
|
-
def initialize(
|
47
|
-
super "There is a circular dependency between #{
|
48
|
-
@dependencies =
|
47
|
+
def initialize(vertices)
|
48
|
+
super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
|
49
|
+
@dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
@@ -55,11 +56,16 @@ module Gem::Resolver::Molinillo
|
|
55
56
|
# resolution to fail
|
56
57
|
attr_reader :conflicts
|
57
58
|
|
59
|
+
# @return [SpecificationProvider] the specification provider used during
|
60
|
+
# resolution
|
61
|
+
attr_reader :specification_provider
|
62
|
+
|
58
63
|
# Initializes a new error with the given version conflicts.
|
59
64
|
# @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
|
60
|
-
|
65
|
+
# @param [SpecificationProvider] specification_provider see {#specification_provider}
|
66
|
+
def initialize(conflicts, specification_provider)
|
61
67
|
pairs = []
|
62
|
-
conflicts.values.
|
68
|
+
conflicts.values.flat_map(&:requirements).each do |conflicting|
|
63
69
|
conflicting.each do |source, conflict_requirements|
|
64
70
|
conflict_requirements.each do |c|
|
65
71
|
pairs << [c, source]
|
@@ -69,7 +75,69 @@ module Gem::Resolver::Molinillo
|
|
69
75
|
|
70
76
|
super "Unable to satisfy the following requirements:\n\n" \
|
71
77
|
"#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
|
78
|
+
|
72
79
|
@conflicts = conflicts
|
80
|
+
@specification_provider = specification_provider
|
81
|
+
end
|
82
|
+
|
83
|
+
require_relative 'delegates/specification_provider'
|
84
|
+
include Delegates::SpecificationProvider
|
85
|
+
|
86
|
+
# @return [String] An error message that includes requirement trees,
|
87
|
+
# which is much more detailed & customizable than the default message
|
88
|
+
# @param [Hash] opts the options to create a message with.
|
89
|
+
# @option opts [String] :solver_name The user-facing name of the solver
|
90
|
+
# @option opts [String] :possibility_type The generic name of a possibility
|
91
|
+
# @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
|
92
|
+
# @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
|
93
|
+
# @option opts [Proc] :additional_message_for_conflict A proc that appends additional
|
94
|
+
# messages for each conflict
|
95
|
+
# @option opts [Proc] :version_for_spec A proc that returns the version number for a
|
96
|
+
# possibility
|
97
|
+
def message_with_trees(opts = {})
|
98
|
+
solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
|
99
|
+
possibility_type = opts.delete(:possibility_type) { 'possibility named' }
|
100
|
+
reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
|
101
|
+
printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
|
102
|
+
additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
|
103
|
+
version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
|
104
|
+
incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
|
105
|
+
proc do |name, _conflict|
|
106
|
+
%(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
|
111
|
+
o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
|
112
|
+
if conflict.locked_requirement
|
113
|
+
o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
|
114
|
+
o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
|
115
|
+
o << %(\n)
|
116
|
+
end
|
117
|
+
o << %( In #{name_for_explicit_dependency_source}:\n)
|
118
|
+
trees = reduce_trees.call(conflict.requirement_trees)
|
119
|
+
|
120
|
+
o << trees.map do |tree|
|
121
|
+
t = ''.dup
|
122
|
+
depth = 2
|
123
|
+
tree.each do |req|
|
124
|
+
t << ' ' * depth << req.to_s
|
125
|
+
unless tree.last == req
|
126
|
+
if spec = conflict.activated_by_name[name_for(req)]
|
127
|
+
t << %( was resolved to #{version_for_spec.call(spec)}, which)
|
128
|
+
end
|
129
|
+
t << %( depends on)
|
130
|
+
end
|
131
|
+
t << %(\n)
|
132
|
+
depth += 1
|
133
|
+
end
|
134
|
+
t
|
135
|
+
end.join("\n")
|
136
|
+
|
137
|
+
additional_message_for_conflict.call(o, name, conflict)
|
138
|
+
|
139
|
+
o
|
140
|
+
end.strip
|
73
141
|
end
|
74
142
|
end
|
75
143
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Gem::Resolver::Molinillo
|
3
4
|
# Conveys information about the resolution process to a user.
|
4
5
|
module UI
|
@@ -48,7 +49,8 @@ module Gem::Resolver::Molinillo
|
|
48
49
|
if debug?
|
49
50
|
debug_info = yield
|
50
51
|
debug_info = debug_info.inspect unless debug_info.is_a?(String)
|
51
|
-
|
52
|
+
debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
|
53
|
+
output.puts debug_info
|
52
54
|
end
|
53
55
|
end
|
54
56
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Gem::Resolver::Molinillo
|
3
4
|
class Resolver
|
4
5
|
# A specific resolution from a given {Resolver}
|
@@ -8,22 +9,125 @@ module Gem::Resolver::Molinillo
|
|
8
9
|
# @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
|
9
10
|
# @attr [Object, nil] existing the existing spec that was in conflict with
|
10
11
|
# the {#possibility}
|
11
|
-
# @attr [Object]
|
12
|
-
# to a conflict
|
12
|
+
# @attr [Object] possibility_set the set of specs that was unable to be
|
13
|
+
# activated due to a conflict.
|
13
14
|
# @attr [Object] locked_requirement the relevant locking requirement.
|
14
15
|
# @attr [Array<Array<Object>>] requirement_trees the different requirement
|
15
16
|
# trees that led to every requirement for the conflicting name.
|
16
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.
|
17
20
|
Conflict = Struct.new(
|
18
21
|
:requirement,
|
19
22
|
:requirements,
|
20
23
|
:existing,
|
21
|
-
:
|
24
|
+
:possibility_set,
|
22
25
|
:locked_requirement,
|
23
26
|
:requirement_trees,
|
24
|
-
:activated_by_name
|
27
|
+
:activated_by_name,
|
28
|
+
:underlying_error
|
25
29
|
)
|
26
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
|
+
|
27
131
|
# @return [SpecificationProvider] the provider that knows about
|
28
132
|
# dependencies, requirements, specifications, versions, etc.
|
29
133
|
attr_reader :specification_provider
|
@@ -64,7 +168,7 @@ module Gem::Resolver::Molinillo
|
|
64
168
|
start_resolution
|
65
169
|
|
66
170
|
while state
|
67
|
-
break
|
171
|
+
break if !state.requirement && state.requirements.empty?
|
68
172
|
indicate_progress
|
69
173
|
if state.respond_to?(:pop_possibility_state) # DependencyState
|
70
174
|
debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
|
@@ -78,7 +182,7 @@ module Gem::Resolver::Molinillo
|
|
78
182
|
process_topmost_state
|
79
183
|
end
|
80
184
|
|
81
|
-
|
185
|
+
resolve_activated_specs
|
82
186
|
ensure
|
83
187
|
end_resolution
|
84
188
|
end
|
@@ -103,12 +207,25 @@ module Gem::Resolver::Molinillo
|
|
103
207
|
def start_resolution
|
104
208
|
@started_at = Time.now
|
105
209
|
|
106
|
-
|
210
|
+
push_initial_state
|
107
211
|
|
108
212
|
debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
|
109
213
|
resolver_ui.before_resolution
|
110
214
|
end
|
111
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
|
+
|
112
229
|
# Ends the resolution process
|
113
230
|
# @return [void]
|
114
231
|
def end_resolution
|
@@ -121,11 +238,11 @@ module Gem::Resolver::Molinillo
|
|
121
238
|
debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
|
122
239
|
end
|
123
240
|
|
124
|
-
|
125
|
-
|
241
|
+
require_relative 'state'
|
242
|
+
require_relative 'modules/specification_provider'
|
126
243
|
|
127
|
-
|
128
|
-
|
244
|
+
require_relative 'delegates/resolution_state'
|
245
|
+
require_relative 'delegates/specification_provider'
|
129
246
|
|
130
247
|
include Gem::Resolver::Molinillo::Delegates::ResolutionState
|
131
248
|
include Gem::Resolver::Molinillo::Delegates::SpecificationProvider
|
@@ -136,9 +253,12 @@ module Gem::Resolver::Molinillo
|
|
136
253
|
if possibility
|
137
254
|
attempt_to_activate
|
138
255
|
else
|
139
|
-
create_conflict
|
140
|
-
unwind_for_conflict
|
256
|
+
create_conflict
|
257
|
+
unwind_for_conflict
|
141
258
|
end
|
259
|
+
rescue CircularDependencyError => underlying_error
|
260
|
+
create_conflict(underlying_error)
|
261
|
+
unwind_for_conflict
|
142
262
|
end
|
143
263
|
|
144
264
|
# @return [Object] the current possibility that the resolution is trying
|
@@ -153,63 +273,292 @@ module Gem::Resolver::Molinillo
|
|
153
273
|
states.last
|
154
274
|
end
|
155
275
|
|
156
|
-
# Creates the initial state for the resolution, based upon the
|
276
|
+
# Creates and pushes the initial state for the resolution, based upon the
|
157
277
|
# {#requested} dependencies
|
158
|
-
# @return [
|
159
|
-
def
|
278
|
+
# @return [void]
|
279
|
+
def push_initial_state
|
160
280
|
graph = DependencyGraph.new.tap do |dg|
|
161
|
-
original_requested.each
|
281
|
+
original_requested.each do |requested|
|
282
|
+
vertex = dg.add_vertex(name_for(requested), nil, true)
|
283
|
+
vertex.explicit_requirements << requested
|
284
|
+
end
|
162
285
|
dg.tag(:initial_state)
|
163
286
|
end
|
164
287
|
|
165
|
-
|
166
|
-
initial_requirement = requirements.shift
|
167
|
-
DependencyState.new(
|
168
|
-
initial_requirement && name_for(initial_requirement),
|
169
|
-
requirements,
|
170
|
-
graph,
|
171
|
-
initial_requirement,
|
172
|
-
initial_requirement && search_for(initial_requirement),
|
173
|
-
0,
|
174
|
-
{}
|
175
|
-
)
|
288
|
+
push_state_for_requirements(original_requested, true, graph)
|
176
289
|
end
|
177
290
|
|
178
291
|
# Unwinds the states stack because a conflict has been encountered
|
179
292
|
# @return [void]
|
180
293
|
def unwind_for_conflict
|
181
|
-
|
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}" }
|
182
297
|
conflicts.tap do |c|
|
183
|
-
sliced_states = states.slice!((
|
184
|
-
|
298
|
+
sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
|
299
|
+
raise_error_unless_state(c)
|
185
300
|
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
|
186
301
|
state.conflicts = c
|
302
|
+
state.unused_unwind_options = unwind_options
|
303
|
+
filter_possibilities_after_unwind(details_for_unwind)
|
187
304
|
index = states.size - 1
|
188
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
|
+
relevant_unused_unwinds = unused_unwind_options.select do |alternative|
|
333
|
+
intersecting_requirements =
|
334
|
+
last_detail_for_current_unwind.all_requirements &
|
335
|
+
alternative.requirements_unwound_to_instead
|
336
|
+
next if intersecting_requirements.empty?
|
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 { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
|
348
|
+
unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
|
349
|
+
|
350
|
+
current_detail
|
351
|
+
end
|
352
|
+
|
353
|
+
# @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
|
354
|
+
# @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
|
355
|
+
# of resolving the passed requirements
|
356
|
+
def unwind_options_for_requirements(binding_requirements)
|
357
|
+
unwind_details = []
|
358
|
+
|
359
|
+
trees = []
|
360
|
+
binding_requirements.reverse_each do |r|
|
361
|
+
partial_tree = [r]
|
362
|
+
trees << partial_tree
|
363
|
+
unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
|
364
|
+
|
365
|
+
# If this requirement has alternative possibilities, check if any would
|
366
|
+
# satisfy the other requirements that created this conflict
|
367
|
+
requirement_state = find_state_for(r)
|
368
|
+
if conflict_fixing_possibilities?(requirement_state, binding_requirements)
|
369
|
+
unwind_details << UnwindDetails.new(
|
370
|
+
states.index(requirement_state),
|
371
|
+
r,
|
372
|
+
partial_tree,
|
373
|
+
binding_requirements,
|
374
|
+
trees,
|
375
|
+
[]
|
376
|
+
)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Next, look at the parent of this requirement, and check if the requirement
|
380
|
+
# could have been avoided if an alternative PossibilitySet had been chosen
|
381
|
+
parent_r = parent_of(r)
|
382
|
+
next if parent_r.nil?
|
383
|
+
partial_tree.unshift(parent_r)
|
384
|
+
requirement_state = find_state_for(parent_r)
|
385
|
+
if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
|
386
|
+
unwind_details << UnwindDetails.new(
|
387
|
+
states.index(requirement_state),
|
388
|
+
parent_r,
|
389
|
+
partial_tree,
|
390
|
+
binding_requirements,
|
391
|
+
trees,
|
392
|
+
[]
|
393
|
+
)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Finally, look at the grandparent and up of this requirement, looking
|
397
|
+
# for any possibilities that wouldn't create their parent requirement
|
398
|
+
grandparent_r = parent_of(parent_r)
|
399
|
+
until grandparent_r.nil?
|
400
|
+
partial_tree.unshift(grandparent_r)
|
401
|
+
requirement_state = find_state_for(grandparent_r)
|
402
|
+
if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
|
403
|
+
unwind_details << UnwindDetails.new(
|
404
|
+
states.index(requirement_state),
|
405
|
+
grandparent_r,
|
406
|
+
partial_tree,
|
407
|
+
binding_requirements,
|
408
|
+
trees,
|
409
|
+
[]
|
410
|
+
)
|
411
|
+
end
|
412
|
+
parent_r = grandparent_r
|
413
|
+
grandparent_r = parent_of(parent_r)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
unwind_details
|
418
|
+
end
|
419
|
+
|
420
|
+
# @param [DependencyState] state
|
421
|
+
# @param [Array] binding_requirements array of requirements
|
422
|
+
# @return [Boolean] whether or not the given state has any possibilities
|
423
|
+
# that could satisfy the given requirements
|
424
|
+
def conflict_fixing_possibilities?(state, binding_requirements)
|
425
|
+
return false unless state
|
426
|
+
|
427
|
+
state.possibilities.any? do |possibility_set|
|
428
|
+
possibility_set.possibilities.any? do |poss|
|
429
|
+
possibility_satisfies_requirements?(poss, binding_requirements)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# Filter's a state's possibilities to remove any that would not fix the
|
435
|
+
# conflict we've just rewound from
|
436
|
+
# @param [UnwindDetails] unwind_details details of the conflict just
|
437
|
+
# unwound from
|
438
|
+
# @return [void]
|
439
|
+
def filter_possibilities_after_unwind(unwind_details)
|
440
|
+
return unless state && !state.possibilities.empty?
|
441
|
+
|
442
|
+
if unwind_details.unwinding_to_primary_requirement?
|
443
|
+
filter_possibilities_for_primary_unwind(unwind_details)
|
444
|
+
else
|
445
|
+
filter_possibilities_for_parent_unwind(unwind_details)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
# Filter's a state's possibilities to remove any that would not satisfy
|
450
|
+
# the requirements in the conflict we've just rewound from
|
451
|
+
# @param [UnwindDetails] unwind_details details of the conflict just unwound from
|
452
|
+
# @return [void]
|
453
|
+
def filter_possibilities_for_primary_unwind(unwind_details)
|
454
|
+
unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
|
455
|
+
unwinds_to_state << unwind_details
|
456
|
+
unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
|
457
|
+
|
458
|
+
state.possibilities.reject! do |possibility_set|
|
459
|
+
possibility_set.possibilities.none? do |poss|
|
460
|
+
unwind_requirement_sets.any? do |requirements|
|
461
|
+
possibility_satisfies_requirements?(poss, requirements)
|
462
|
+
end
|
463
|
+
end
|
189
464
|
end
|
190
465
|
end
|
191
466
|
|
192
|
-
# @
|
193
|
-
#
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
467
|
+
# @param [Object] possibility a single possibility
|
468
|
+
# @param [Array] requirements an array of requirements
|
469
|
+
# @return [Boolean] whether the possibility satisfies all of the
|
470
|
+
# given requirements
|
471
|
+
def possibility_satisfies_requirements?(possibility, requirements)
|
472
|
+
name = name_for(possibility)
|
473
|
+
|
474
|
+
activated.tag(:swap)
|
475
|
+
activated.set_payload(name, possibility) if activated.vertex_named(name)
|
476
|
+
satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
|
477
|
+
activated.rewind_to(:swap)
|
478
|
+
|
479
|
+
satisfied
|
480
|
+
end
|
481
|
+
|
482
|
+
# Filter's a state's possibilities to remove any that would (eventually)
|
483
|
+
# create a requirement in the conflict we've just rewound from
|
484
|
+
# @param [UnwindDetails] unwind_details details of the conflict just unwound from
|
485
|
+
# @return [void]
|
486
|
+
def filter_possibilities_for_parent_unwind(unwind_details)
|
487
|
+
unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
|
488
|
+
unwinds_to_state << unwind_details
|
489
|
+
|
490
|
+
primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
|
491
|
+
parent_unwinds = unwinds_to_state.uniq - primary_unwinds
|
492
|
+
|
493
|
+
allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
|
494
|
+
states[unwind.state_index].possibilities.select do |possibility_set|
|
495
|
+
possibility_set.possibilities.any? do |poss|
|
496
|
+
possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
|
205
497
|
end
|
206
|
-
r = parent_of(r)
|
207
498
|
end
|
208
499
|
end
|
209
500
|
|
210
|
-
|
501
|
+
requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
|
502
|
+
|
503
|
+
state.possibilities.reject! do |possibility_set|
|
504
|
+
!allowed_possibility_sets.include?(possibility_set) &&
|
505
|
+
(requirements_to_avoid - possibility_set.dependencies).empty?
|
506
|
+
end
|
211
507
|
end
|
212
508
|
|
509
|
+
# @param [Conflict] conflict
|
510
|
+
# @return [Array] minimal array of requirements that would cause the passed
|
511
|
+
# conflict to occur.
|
512
|
+
def binding_requirements_for_conflict(conflict)
|
513
|
+
return [conflict.requirement] if conflict.possibility.nil?
|
514
|
+
|
515
|
+
possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
|
516
|
+
|
517
|
+
# When there's a `CircularDependency` error the conflicting requirement
|
518
|
+
# (the one causing the circular) won't be `conflict.requirement`
|
519
|
+
# (which won't be for the right state, because we won't have created it,
|
520
|
+
# because it's circular).
|
521
|
+
# We need to make sure we have that requirement in the conflict's list,
|
522
|
+
# otherwise we won't be able to unwind properly, so we just return all
|
523
|
+
# the requirements for the conflict.
|
524
|
+
return possible_binding_requirements if conflict.underlying_error
|
525
|
+
|
526
|
+
possibilities = search_for(conflict.requirement)
|
527
|
+
|
528
|
+
# If all the requirements together don't filter out all possibilities,
|
529
|
+
# then the only two requirements we need to consider are the initial one
|
530
|
+
# (where the dependency's version was first chosen) and the last
|
531
|
+
if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
|
532
|
+
return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
|
533
|
+
end
|
534
|
+
|
535
|
+
# Loop through the possible binding requirements, removing each one
|
536
|
+
# that doesn't bind. Use a `reverse_each` as we want the earliest set of
|
537
|
+
# binding requirements, and don't use `reject!` as we wish to refine the
|
538
|
+
# array *on each iteration*.
|
539
|
+
binding_requirements = possible_binding_requirements.dup
|
540
|
+
possible_binding_requirements.reverse_each do |req|
|
541
|
+
next if req == conflict.requirement
|
542
|
+
unless binding_requirement_in_set?(req, binding_requirements, possibilities)
|
543
|
+
binding_requirements -= [req]
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
binding_requirements
|
548
|
+
end
|
549
|
+
|
550
|
+
# @param [Object] requirement we wish to check
|
551
|
+
# @param [Array] possible_binding_requirements array of requirements
|
552
|
+
# @param [Array] possibilities array of possibilities the requirements will be used to filter
|
553
|
+
# @return [Boolean] whether or not the given requirement is required to filter
|
554
|
+
# out all elements of the array of possibilities.
|
555
|
+
def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
|
556
|
+
possibilities.any? do |poss|
|
557
|
+
possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
# @param [Object] requirement
|
213
562
|
# @return [Object] the requirement that led to `requirement` being added
|
214
563
|
# to the list of requirements.
|
215
564
|
def parent_of(requirement)
|
@@ -219,29 +568,27 @@ module Gem::Resolver::Molinillo
|
|
219
568
|
parent_state.requirement
|
220
569
|
end
|
221
570
|
|
571
|
+
# @param [String] name
|
222
572
|
# @return [Object] the requirement that led to a version of a possibility
|
223
573
|
# with the given name being activated.
|
224
574
|
def requirement_for_existing_name(name)
|
225
|
-
return nil unless activated.vertex_named(name)
|
575
|
+
return nil unless vertex = activated.vertex_named(name)
|
576
|
+
return nil unless vertex.payload
|
226
577
|
states.find { |s| s.name == name }.requirement
|
227
578
|
end
|
228
579
|
|
580
|
+
# @param [Object] requirement
|
229
581
|
# @return [ResolutionState] the state whose `requirement` is the given
|
230
582
|
# `requirement`.
|
231
583
|
def find_state_for(requirement)
|
232
584
|
return nil unless requirement
|
233
|
-
states.
|
234
|
-
end
|
235
|
-
|
236
|
-
# @return [Boolean] whether or not the given state has any possibilities
|
237
|
-
# left.
|
238
|
-
def state_any?(state)
|
239
|
-
state && state.possibilities.any?
|
585
|
+
states.find { |i| requirement == i.requirement }
|
240
586
|
end
|
241
587
|
|
588
|
+
# @param [Object] underlying_error
|
242
589
|
# @return [Conflict] a {Conflict} that reflects the failure to activate
|
243
590
|
# the {#possibility} in conjunction with the current {#state}
|
244
|
-
def create_conflict
|
591
|
+
def create_conflict(underlying_error = nil)
|
245
592
|
vertex = activated.vertex_named(name)
|
246
593
|
locked_requirement = locked_requirement_named(name)
|
247
594
|
|
@@ -250,18 +597,21 @@ module Gem::Resolver::Molinillo
|
|
250
597
|
requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
|
251
598
|
end
|
252
599
|
requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
|
253
|
-
vertex.incoming_edges.each
|
600
|
+
vertex.incoming_edges.each do |edge|
|
601
|
+
(requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
|
602
|
+
end
|
254
603
|
|
255
604
|
activated_by_name = {}
|
256
|
-
activated.each { |v| activated_by_name[v.name] = v.payload if v.payload }
|
605
|
+
activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
|
257
606
|
conflicts[name] = Conflict.new(
|
258
607
|
requirement,
|
259
608
|
requirements,
|
260
|
-
vertex.payload,
|
609
|
+
vertex.payload && vertex.payload.latest_version,
|
261
610
|
possibility,
|
262
611
|
locked_requirement,
|
263
612
|
requirement_trees,
|
264
|
-
activated_by_name
|
613
|
+
activated_by_name,
|
614
|
+
underlying_error
|
265
615
|
)
|
266
616
|
end
|
267
617
|
|
@@ -272,6 +622,7 @@ module Gem::Resolver::Molinillo
|
|
272
622
|
vertex.requirements.map { |r| requirement_tree_for(r) }
|
273
623
|
end
|
274
624
|
|
625
|
+
# @param [Object] requirement
|
275
626
|
# @return [Array<Object>] the list of requirements that led to
|
276
627
|
# `requirement` being required.
|
277
628
|
def requirement_tree_for(requirement)
|
@@ -311,116 +662,47 @@ module Gem::Resolver::Molinillo
|
|
311
662
|
# @return [void]
|
312
663
|
def attempt_to_activate
|
313
664
|
debug(depth) { 'Attempting to activate ' + possibility.to_s }
|
314
|
-
|
315
|
-
if
|
316
|
-
debug(depth) { "Found existing spec (#{
|
317
|
-
|
665
|
+
existing_vertex = activated.vertex_named(name)
|
666
|
+
if existing_vertex.payload
|
667
|
+
debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
|
668
|
+
attempt_to_filter_existing_spec(existing_vertex)
|
318
669
|
else
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
# Attempts to activate the current {#possibility} (given that it has
|
324
|
-
# already been activated)
|
325
|
-
# @return [void]
|
326
|
-
def attempt_to_activate_existing_spec(existing_node)
|
327
|
-
existing_spec = existing_node.payload
|
328
|
-
if requirement_satisfied_by?(requirement, activated, existing_spec)
|
329
|
-
new_requirements = requirements.dup
|
330
|
-
push_state_for_requirements(new_requirements, false)
|
331
|
-
else
|
332
|
-
return if attempt_to_swap_possibility
|
333
|
-
create_conflict
|
334
|
-
debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
|
335
|
-
unwind_for_conflict
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
# Attempts to swp the current {#possibility} with the already-activated
|
340
|
-
# spec with the given name
|
341
|
-
# @return [Boolean] Whether the possibility was swapped into {#activated}
|
342
|
-
def attempt_to_swap_possibility
|
343
|
-
activated.tag(:swap)
|
344
|
-
vertex = activated.vertex_named(name)
|
345
|
-
activated.set_payload(name, possibility)
|
346
|
-
if !vertex.requirements.
|
347
|
-
all? { |r| requirement_satisfied_by?(r, activated, possibility) } ||
|
348
|
-
!new_spec_satisfied?
|
349
|
-
activated.rewind_to(:swap)
|
350
|
-
return
|
351
|
-
end
|
352
|
-
fixup_swapped_children(vertex)
|
353
|
-
activate_spec
|
354
|
-
end
|
355
|
-
|
356
|
-
# Ensures there are no orphaned successors to the given {vertex}.
|
357
|
-
# @param [DependencyGraph::Vertex] vertex the vertex to fix up.
|
358
|
-
# @return [void]
|
359
|
-
def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity
|
360
|
-
payload = vertex.payload
|
361
|
-
deps = dependencies_for(payload).group_by(&method(:name_for))
|
362
|
-
vertex.outgoing_edges.each do |outgoing_edge|
|
363
|
-
requirement = outgoing_edge.requirement
|
364
|
-
parent_index = @parents_of[requirement].last
|
365
|
-
succ = outgoing_edge.destination
|
366
|
-
matching_deps = Array(deps[succ.name])
|
367
|
-
dep_matched = matching_deps.include?(requirement)
|
368
|
-
|
369
|
-
# only push the current index when it was originally required by the
|
370
|
-
# same named spec
|
371
|
-
if parent_index && states[parent_index].name == name
|
372
|
-
@parents_of[requirement].push(states.size - 1)
|
670
|
+
latest = possibility.latest_version
|
671
|
+
possibility.possibilities.select! do |possibility|
|
672
|
+
requirement_satisfied_by?(requirement, activated, possibility)
|
373
673
|
end
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
# the only removed vertices are those with no other requirements,
|
382
|
-
# so it's safe to delete only based upon name here
|
383
|
-
removed_names.include?(name_for(r))
|
384
|
-
end
|
385
|
-
elsif !dep_matched
|
386
|
-
debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" }
|
387
|
-
# also reset if we're removing the edge, but only if its parent has
|
388
|
-
# already been fixed up
|
389
|
-
@parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty?
|
390
|
-
|
391
|
-
activated.delete_edge(outgoing_edge)
|
392
|
-
requirements.delete(requirement)
|
674
|
+
if possibility.latest_version.nil?
|
675
|
+
# ensure there's a possibility for better error messages
|
676
|
+
possibility.possibilities << latest if latest
|
677
|
+
create_conflict
|
678
|
+
unwind_for_conflict
|
679
|
+
else
|
680
|
+
activate_new_spec
|
393
681
|
end
|
394
682
|
end
|
395
683
|
end
|
396
684
|
|
397
|
-
# Attempts to
|
398
|
-
# already been activated)
|
685
|
+
# Attempts to update the existing vertex's `PossibilitySet` with a filtered version
|
399
686
|
# @return [void]
|
400
|
-
def
|
401
|
-
|
402
|
-
|
687
|
+
def attempt_to_filter_existing_spec(vertex)
|
688
|
+
filtered_set = filtered_possibility_set(vertex)
|
689
|
+
if !filtered_set.possibilities.empty?
|
690
|
+
activated.set_payload(name, filtered_set)
|
691
|
+
new_requirements = requirements.dup
|
692
|
+
push_state_for_requirements(new_requirements, false)
|
403
693
|
else
|
404
694
|
create_conflict
|
695
|
+
debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
|
405
696
|
unwind_for_conflict
|
406
697
|
end
|
407
698
|
end
|
408
699
|
|
409
|
-
#
|
410
|
-
#
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
end
|
416
|
-
|
417
|
-
locked_requirement = locked_requirement_named(name)
|
418
|
-
|
419
|
-
locked_spec_satisfied = !locked_requirement ||
|
420
|
-
requirement_satisfied_by?(locked_requirement, activated, possibility)
|
421
|
-
debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
|
422
|
-
|
423
|
-
locked_spec_satisfied
|
700
|
+
# Generates a filtered version of the existing vertex's `PossibilitySet` using the
|
701
|
+
# current state's `requirement`
|
702
|
+
# @param [Object] vertex existing vertex
|
703
|
+
# @return [PossibilitySet] filtered possibility set
|
704
|
+
def filtered_possibility_set(vertex)
|
705
|
+
PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
|
424
706
|
end
|
425
707
|
|
426
708
|
# @param [String] requirement_name the spec name to search for
|
@@ -434,7 +716,7 @@ module Gem::Resolver::Molinillo
|
|
434
716
|
# Add the current {#possibility} to the dependency graph of the current
|
435
717
|
# {#state}
|
436
718
|
# @return [void]
|
437
|
-
def
|
719
|
+
def activate_new_spec
|
438
720
|
conflicts.delete(name)
|
439
721
|
debug(depth) { "Activated #{name} at #{possibility}" }
|
440
722
|
activated.set_payload(name, possibility)
|
@@ -442,14 +724,14 @@ module Gem::Resolver::Molinillo
|
|
442
724
|
end
|
443
725
|
|
444
726
|
# Requires the dependencies that the recently activated spec has
|
445
|
-
# @param [Object]
|
727
|
+
# @param [Object] possibility_set the PossibilitySet that has just been
|
446
728
|
# activated
|
447
729
|
# @return [void]
|
448
|
-
def require_nested_dependencies_for(
|
449
|
-
nested_dependencies = dependencies_for(
|
730
|
+
def require_nested_dependencies_for(possibility_set)
|
731
|
+
nested_dependencies = dependencies_for(possibility_set.latest_version)
|
450
732
|
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
|
451
733
|
nested_dependencies.each do |d|
|
452
|
-
activated.add_child_vertex(name_for(d), nil, [name_for(
|
734
|
+
activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
|
453
735
|
parent_index = states.size - 1
|
454
736
|
parents = @parents_of[d]
|
455
737
|
parents << parent_index if parents.empty?
|
@@ -461,23 +743,82 @@ module Gem::Resolver::Molinillo
|
|
461
743
|
# Pushes a new {DependencyState} that encapsulates both existing and new
|
462
744
|
# requirements
|
463
745
|
# @param [Array] new_requirements
|
746
|
+
# @param [Boolean] requires_sort
|
747
|
+
# @param [Object] new_activated
|
464
748
|
# @return [void]
|
465
749
|
def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
|
466
750
|
new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
|
467
|
-
new_requirement =
|
751
|
+
new_requirement = nil
|
752
|
+
loop do
|
753
|
+
new_requirement = new_requirements.shift
|
754
|
+
break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
|
755
|
+
end
|
468
756
|
new_name = new_requirement ? name_for(new_requirement) : ''.freeze
|
469
|
-
possibilities =
|
757
|
+
possibilities = possibilities_for_requirement(new_requirement)
|
470
758
|
handle_missing_or_push_dependency_state DependencyState.new(
|
471
759
|
new_name, new_requirements, new_activated,
|
472
|
-
new_requirement, possibilities, depth, conflicts.dup
|
760
|
+
new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
|
473
761
|
)
|
474
762
|
end
|
475
763
|
|
764
|
+
# Checks a proposed requirement with any existing locked requirement
|
765
|
+
# before generating an array of possibilities for it.
|
766
|
+
# @param [Object] requirement the proposed requirement
|
767
|
+
# @param [Object] activated
|
768
|
+
# @return [Array] possibilities
|
769
|
+
def possibilities_for_requirement(requirement, activated = self.activated)
|
770
|
+
return [] unless requirement
|
771
|
+
if locked_requirement_named(name_for(requirement))
|
772
|
+
return locked_requirement_possibility_set(requirement, activated)
|
773
|
+
end
|
774
|
+
|
775
|
+
group_possibilities(search_for(requirement))
|
776
|
+
end
|
777
|
+
|
778
|
+
# @param [Object] requirement the proposed requirement
|
779
|
+
# @param [Object] activated
|
780
|
+
# @return [Array] possibility set containing only the locked requirement, if any
|
781
|
+
def locked_requirement_possibility_set(requirement, activated = self.activated)
|
782
|
+
all_possibilities = search_for(requirement)
|
783
|
+
locked_requirement = locked_requirement_named(name_for(requirement))
|
784
|
+
|
785
|
+
# Longwinded way to build a possibilities array with either the locked
|
786
|
+
# requirement or nothing in it. Required, since the API for
|
787
|
+
# locked_requirement isn't guaranteed.
|
788
|
+
locked_possibilities = all_possibilities.select do |possibility|
|
789
|
+
requirement_satisfied_by?(locked_requirement, activated, possibility)
|
790
|
+
end
|
791
|
+
|
792
|
+
group_possibilities(locked_possibilities)
|
793
|
+
end
|
794
|
+
|
795
|
+
# Build an array of PossibilitySets, with each element representing a group of
|
796
|
+
# dependency versions that all have the same sub-dependency version constraints
|
797
|
+
# and are contiguous.
|
798
|
+
# @param [Array] possibilities an array of possibilities
|
799
|
+
# @return [Array<PossibilitySet>] an array of possibility sets
|
800
|
+
def group_possibilities(possibilities)
|
801
|
+
possibility_sets = []
|
802
|
+
current_possibility_set = nil
|
803
|
+
|
804
|
+
possibilities.reverse_each do |possibility|
|
805
|
+
dependencies = dependencies_for(possibility)
|
806
|
+
if current_possibility_set && current_possibility_set.dependencies == dependencies
|
807
|
+
current_possibility_set.possibilities.unshift(possibility)
|
808
|
+
else
|
809
|
+
possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
|
810
|
+
current_possibility_set = possibility_sets.first
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
possibility_sets
|
815
|
+
end
|
816
|
+
|
476
817
|
# Pushes a new {DependencyState}.
|
477
818
|
# If the {#specification_provider} says to
|
478
819
|
# {SpecificationProvider#allow_missing?} that particular requirement, and
|
479
820
|
# there are no possibilities for that requirement, then `state` is not
|
480
|
-
# pushed, and the
|
821
|
+
# pushed, and the vertex in {#activated} is removed, and we continue
|
481
822
|
# resolving the remaining requirements.
|
482
823
|
# @param [DependencyState] state
|
483
824
|
# @return [void]
|