bundler 2.3.26 → 2.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +228 -1
  3. data/README.md +3 -6
  4. data/bundler.gemspec +2 -2
  5. data/exe/bundle +1 -4
  6. data/lib/bundler/build_metadata.rb +2 -2
  7. data/lib/bundler/cli/add.rb +1 -1
  8. data/lib/bundler/cli/binstubs.rb +5 -1
  9. data/lib/bundler/cli/check.rb +1 -1
  10. data/lib/bundler/cli/common.rb +1 -0
  11. data/lib/bundler/cli/console.rb +2 -2
  12. data/lib/bundler/cli/doctor.rb +4 -6
  13. data/lib/bundler/cli/gem.rb +62 -40
  14. data/lib/bundler/cli/init.rb +2 -2
  15. data/lib/bundler/cli/install.rb +2 -3
  16. data/lib/bundler/cli/lock.rb +8 -5
  17. data/lib/bundler/cli/open.rb +6 -4
  18. data/lib/bundler/cli/outdated.rb +1 -3
  19. data/lib/bundler/cli/viz.rb +1 -1
  20. data/lib/bundler/cli.rb +45 -2
  21. data/lib/bundler/compact_index_client/cache.rb +1 -1
  22. data/lib/bundler/compact_index_client/updater.rb +40 -39
  23. data/lib/bundler/constants.rb +1 -1
  24. data/lib/bundler/current_ruby.rb +2 -0
  25. data/lib/bundler/definition.rb +99 -51
  26. data/lib/bundler/dependency.rb +13 -12
  27. data/lib/bundler/digest.rb +1 -1
  28. data/lib/bundler/dsl.rb +3 -3
  29. data/lib/bundler/endpoint_specification.rb +0 -4
  30. data/lib/bundler/env.rb +1 -1
  31. data/lib/bundler/environment_preserver.rb +3 -2
  32. data/lib/bundler/errors.rb +1 -11
  33. data/lib/bundler/fetcher/compact_index.rb +9 -11
  34. data/lib/bundler/fetcher/dependency.rb +2 -6
  35. data/lib/bundler/fetcher/downloader.rb +2 -5
  36. data/lib/bundler/fetcher.rb +4 -8
  37. data/lib/bundler/force_platform.rb +18 -0
  38. data/lib/bundler/friendly_errors.rb +0 -3
  39. data/lib/bundler/gem_version_promoter.rb +52 -86
  40. data/lib/bundler/graph.rb +3 -3
  41. data/lib/bundler/index.rb +7 -15
  42. data/lib/bundler/injector.rb +2 -2
  43. data/lib/bundler/inline.rb +8 -10
  44. data/lib/bundler/installer/parallel_installer.rb +3 -33
  45. data/lib/bundler/installer/standalone.rb +12 -8
  46. data/lib/bundler/installer.rb +9 -23
  47. data/lib/bundler/lazy_specification.rb +42 -42
  48. data/lib/bundler/lockfile_generator.rb +1 -1
  49. data/lib/bundler/lockfile_parser.rb +16 -16
  50. data/lib/bundler/man/bundle-add.1 +1 -1
  51. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  52. data/lib/bundler/man/bundle-cache.1 +3 -3
  53. data/lib/bundler/man/bundle-cache.1.ronn +2 -2
  54. data/lib/bundler/man/bundle-check.1 +1 -1
  55. data/lib/bundler/man/bundle-clean.1 +1 -1
  56. data/lib/bundler/man/bundle-config.1 +2 -2
  57. data/lib/bundler/man/bundle-config.1.ronn +1 -1
  58. data/lib/bundler/man/bundle-console.1 +1 -1
  59. data/lib/bundler/man/bundle-doctor.1 +1 -1
  60. data/lib/bundler/man/bundle-exec.1 +5 -5
  61. data/lib/bundler/man/bundle-exec.1.ronn +5 -5
  62. data/lib/bundler/man/bundle-gem.1 +27 -37
  63. data/lib/bundler/man/bundle-gem.1.ronn +5 -5
  64. data/lib/bundler/man/bundle-help.1 +1 -1
  65. data/lib/bundler/man/bundle-info.1 +1 -1
  66. data/lib/bundler/man/bundle-init.1 +5 -1
  67. data/lib/bundler/man/bundle-init.1.ronn +2 -0
  68. data/lib/bundler/man/bundle-inject.1 +1 -1
  69. data/lib/bundler/man/bundle-install.1 +1 -30
  70. data/lib/bundler/man/bundle-install.1.ronn +0 -29
  71. data/lib/bundler/man/bundle-list.1 +1 -1
  72. data/lib/bundler/man/bundle-lock.1 +1 -1
  73. data/lib/bundler/man/bundle-open.1 +22 -2
  74. data/lib/bundler/man/bundle-open.1.ronn +9 -1
  75. data/lib/bundler/man/bundle-outdated.1 +1 -1
  76. data/lib/bundler/man/bundle-platform.1 +2 -2
  77. data/lib/bundler/man/bundle-platform.1.ronn +1 -1
  78. data/lib/bundler/man/bundle-plugin.1 +1 -1
  79. data/lib/bundler/man/bundle-pristine.1 +1 -1
  80. data/lib/bundler/man/bundle-remove.1 +1 -1
  81. data/lib/bundler/man/bundle-show.1 +1 -1
  82. data/lib/bundler/man/bundle-update.1 +1 -1
  83. data/lib/bundler/man/bundle-version.1 +1 -1
  84. data/lib/bundler/man/bundle-viz.1 +1 -1
  85. data/lib/bundler/man/bundle.1 +1 -1
  86. data/lib/bundler/man/gemfile.5 +1 -1
  87. data/lib/bundler/mirror.rb +5 -7
  88. data/lib/bundler/plugin/index.rb +4 -4
  89. data/lib/bundler/plugin/installer/rubygems.rb +0 -4
  90. data/lib/bundler/plugin/installer.rb +5 -2
  91. data/lib/bundler/plugin.rb +1 -1
  92. data/lib/bundler/remote_specification.rb +2 -6
  93. data/lib/bundler/resolver/base.rb +72 -15
  94. data/lib/bundler/resolver/candidate.rb +94 -0
  95. data/lib/bundler/resolver/incompatibility.rb +15 -0
  96. data/lib/bundler/resolver/package.rb +72 -0
  97. data/lib/bundler/resolver/root.rb +25 -0
  98. data/lib/bundler/resolver/spec_group.rb +26 -36
  99. data/lib/bundler/resolver.rb +311 -278
  100. data/lib/bundler/ruby_version.rb +1 -1
  101. data/lib/bundler/rubygems_ext.rb +16 -9
  102. data/lib/bundler/rubygems_gem_installer.rb +4 -2
  103. data/lib/bundler/rubygems_integration.rb +10 -14
  104. data/lib/bundler/runtime.rb +1 -5
  105. data/lib/bundler/safe_marshal.rb +31 -0
  106. data/lib/bundler/settings.rb +1 -7
  107. data/lib/bundler/setup.rb +4 -1
  108. data/lib/bundler/shared_helpers.rb +2 -1
  109. data/lib/bundler/source/git/git_proxy.rb +214 -69
  110. data/lib/bundler/source/git.rb +17 -18
  111. data/lib/bundler/source/metadata.rb +0 -1
  112. data/lib/bundler/source/path/installer.rb +1 -22
  113. data/lib/bundler/source/path.rb +6 -6
  114. data/lib/bundler/source/rubygems.rb +19 -77
  115. data/lib/bundler/source_list.rb +8 -2
  116. data/lib/bundler/spec_set.rb +22 -14
  117. data/lib/bundler/templates/Executable +1 -1
  118. data/lib/bundler/templates/Executable.bundler +5 -10
  119. data/lib/bundler/templates/Executable.standalone +2 -0
  120. data/lib/bundler/templates/newgem/Cargo.toml.tt +7 -0
  121. data/lib/bundler/templates/newgem/Gemfile.tt +3 -0
  122. data/lib/bundler/templates/newgem/README.md.tt +6 -4
  123. data/lib/bundler/templates/newgem/Rakefile.tt +12 -1
  124. data/lib/bundler/templates/newgem/bin/console.tt +0 -4
  125. data/lib/bundler/templates/newgem/circleci/config.yml.tt +12 -0
  126. data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +15 -0
  127. data/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt +10 -0
  128. data/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt +6 -0
  129. data/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt +1 -1
  130. data/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +12 -0
  131. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +10 -0
  132. data/lib/bundler/templates/newgem/gitignore.tt +3 -0
  133. data/lib/bundler/templates/newgem/gitlab-ci.yml.tt +8 -0
  134. data/lib/bundler/templates/newgem/newgem.gemspec.tt +8 -2
  135. data/lib/bundler/ui/shell.rb +35 -12
  136. data/lib/bundler/ui/silent.rb +21 -5
  137. data/lib/bundler/uri_normalizer.rb +23 -0
  138. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +3 -3
  139. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +0 -1
  140. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +3 -1
  141. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +1350 -408
  142. data/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  143. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1 -1
  144. data/lib/bundler/vendor/pub_grub/LICENSE.txt +21 -0
  145. data/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  146. data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +189 -0
  147. data/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  148. data/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +150 -0
  149. data/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb +43 -0
  150. data/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  151. data/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  152. data/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb +19 -0
  153. data/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +60 -0
  154. data/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  155. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  156. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb +129 -0
  157. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +411 -0
  158. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +243 -0
  159. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb +178 -0
  160. data/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +31 -0
  161. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +1 -1
  162. data/lib/bundler/vendor/uri/lib/uri/common.rb +64 -16
  163. data/lib/bundler/vendor/uri/lib/uri/file.rb +7 -1
  164. data/lib/bundler/vendor/uri/lib/uri/ftp.rb +2 -1
  165. data/lib/bundler/vendor/uri/lib/uri/generic.rb +27 -7
  166. data/lib/bundler/vendor/uri/lib/uri/http.rb +40 -2
  167. data/lib/bundler/vendor/uri/lib/uri/https.rb +2 -1
  168. data/lib/bundler/vendor/uri/lib/uri/ldap.rb +1 -1
  169. data/lib/bundler/vendor/uri/lib/uri/ldaps.rb +2 -1
  170. data/lib/bundler/vendor/uri/lib/uri/mailto.rb +2 -2
  171. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +13 -7
  172. data/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +10 -5
  173. data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  174. data/lib/bundler/vendor/uri/lib/uri/ws.rb +1 -2
  175. data/lib/bundler/vendor/uri/lib/uri/wss.rb +2 -1
  176. data/lib/bundler/vendor/uri/lib/uri.rb +3 -2
  177. data/lib/bundler/vendored_persistent.rb +1 -33
  178. data/lib/bundler/{vendored_tmpdir.rb → vendored_pub_grub.rb} +1 -1
  179. data/lib/bundler/version.rb +5 -1
  180. data/lib/bundler/worker.rb +5 -7
  181. data/lib/bundler.rb +17 -69
  182. metadata +35 -33
  183. data/lib/bundler/templates/gems.rb +0 -5
  184. data/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +0 -5
  185. data/lib/bundler/templates/newgem/travis.yml.tt +0 -6
  186. data/lib/bundler/vendor/molinillo/LICENSE +0 -9
  187. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +0 -57
  188. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +0 -88
  189. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +0 -36
  190. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +0 -66
  191. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +0 -62
  192. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +0 -63
  193. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +0 -61
  194. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +0 -126
  195. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +0 -46
  196. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +0 -36
  197. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +0 -164
  198. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +0 -255
  199. data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +0 -149
  200. data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +0 -6
  201. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +0 -112
  202. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +0 -67
  203. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +0 -839
  204. data/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +0 -46
  205. data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +0 -58
  206. data/lib/bundler/vendor/molinillo/lib/molinillo.rb +0 -11
  207. data/lib/bundler/vendor/tmpdir/lib/tmpdir.rb +0 -154
  208. data/lib/bundler/vendored_molinillo.rb +0 -4
  209. data/lib/bundler/version_ranges.rb +0 -122
@@ -1,380 +1,413 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bundler
4
+ #
5
+ # This class implements the interface needed by PubGrub for resolution. It is
6
+ # equivalent to the `PubGrub::BasicPackageSource` class provided by PubGrub by
7
+ # default and used by the most simple PubGrub consumers.
8
+ #
4
9
  class Resolver
5
- require_relative "vendored_molinillo"
10
+ require_relative "vendored_pub_grub"
6
11
  require_relative "resolver/base"
7
- require_relative "resolver/spec_group"
12
+ require_relative "resolver/candidate"
13
+ require_relative "resolver/incompatibility"
14
+ require_relative "resolver/root"
8
15
 
9
16
  include GemHelpers
10
17
 
11
- def initialize(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
12
- @source_requirements = source_requirements
13
- @base = Resolver::Base.new(base, additional_base_requirements)
14
- @resolver = Molinillo::Resolver.new(self, self)
15
- @results_for = {}
16
- @search_for = {}
17
- @platforms = platforms
18
- @resolving_only_for_ruby = platforms == [Gem::Platform::RUBY]
18
+ def initialize(base, gem_version_promoter)
19
+ @source_requirements = base.source_requirements
20
+ @base = base
19
21
  @gem_version_promoter = gem_version_promoter
20
22
  end
21
23
 
22
- def start(requirements, exclude_specs: [])
23
- @metadata_requirements, regular_requirements = requirements.partition {|dep| dep.name.end_with?("\0") }
24
+ def start
25
+ @requirements = @base.requirements
26
+ @packages = @base.packages
24
27
 
25
- exclude_specs.each do |spec|
26
- remove_from_candidates(spec)
27
- end
28
+ root, logger = setup_solver
28
29
 
29
- requirements.each {|dep| prerelease_specified[dep.name] ||= dep.prerelease? }
30
+ Bundler.ui.info "Resolving dependencies...", true
30
31
 
31
- verify_gemfile_dependencies_are_found!(requirements)
32
- result = @resolver.resolve(requirements).
33
- map(&:payload).
34
- reject {|sg| sg.name.end_with?("\0") }.
35
- map(&:to_specs).
36
- flatten
32
+ solve_versions(:root => root, :logger => logger)
33
+ end
37
34
 
38
- SpecSet.new(SpecSet.new(result).for(regular_requirements, false, @platforms))
39
- rescue Molinillo::VersionConflict => e
40
- conflicts = e.conflicts
35
+ def setup_solver
36
+ root = Resolver::Root.new(name_for_explicit_dependency_source)
37
+ root_version = Resolver::Candidate.new(0)
41
38
 
42
- deps_to_unlock = conflicts.values.inject([]) do |deps, conflict|
43
- deps |= conflict.requirement_trees.flatten.map {|req| base_requirements[req.name] }.compact
39
+ @all_specs = Hash.new do |specs, name|
40
+ specs[name] = source_for(name).specs.search(name).reject do |s|
41
+ s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } # ignore versions that depend on themselves incorrectly
42
+ end.sort_by {|s| [s.version, s.platform.to_s] }
44
43
  end
45
44
 
46
- if deps_to_unlock.any?
47
- @base.unlock_deps(deps_to_unlock)
48
- reset_spec_cache
49
- retry
45
+ @sorted_versions = Hash.new do |candidates, package|
46
+ candidates[package] = if package.root?
47
+ [root_version]
48
+ else
49
+ all_versions_for(package).sort
50
+ end
50
51
  end
51
52
 
52
- message = version_conflict_message(e)
53
- raise VersionConflict.new(conflicts.keys.uniq, message)
54
- rescue Molinillo::CircularDependencyError => e
55
- names = e.dependencies.sort_by(&:name).map {|d| "gem '#{d.name}'" }
56
- raise CyclicDependencyError, "Your bundle requires gems that depend" \
57
- " on each other, creating an infinite loop. Please remove" \
58
- " #{names.count > 1 ? "either " : ""}#{names.join(" or ")}" \
59
- " and try again."
60
- end
53
+ root_dependencies = prepare_dependencies(@requirements, @packages)
61
54
 
62
- include Molinillo::UI
63
-
64
- # Conveys debug information to the user.
65
- #
66
- # @param [Integer] depth the current depth of the resolution process.
67
- # @return [void]
68
- def debug(depth = 0)
69
- return unless debug?
70
- debug_info = yield
71
- debug_info = debug_info.inspect unless debug_info.is_a?(String)
72
- puts debug_info.split("\n").map {|s| depth == 0 ? "BUNDLER: #{s}" : "BUNDLER(#{depth}): #{s}" }
73
- end
55
+ @cached_dependencies = Hash.new do |dependencies, package|
56
+ dependencies[package] = if package.root?
57
+ { root_version => root_dependencies }
58
+ else
59
+ Hash.new do |versions, version|
60
+ versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages)
61
+ end
62
+ end
63
+ end
74
64
 
75
- def debug?
76
- return @debug_mode if defined?(@debug_mode)
77
- @debug_mode =
78
- ENV["BUNDLER_DEBUG_RESOLVER"] ||
79
- ENV["BUNDLER_DEBUG_RESOLVER_TREE"] ||
80
- ENV["DEBUG_RESOLVER"] ||
81
- ENV["DEBUG_RESOLVER_TREE"] ||
82
- false
83
- end
65
+ logger = Bundler::UI::Shell.new
66
+ logger.level = debug? ? "debug" : "warn"
84
67
 
85
- def before_resolution
86
- Bundler.ui.info "Resolving dependencies...", debug?
68
+ [root, logger]
87
69
  end
88
70
 
89
- def after_resolution
90
- Bundler.ui.info ""
91
- end
71
+ def solve_versions(root:, logger:)
72
+ solver = PubGrub::VersionSolver.new(:source => self, :root => root, :logger => logger)
73
+ result = solver.solve
74
+ result.map {|package, version| version.to_specs(package) }.flatten.uniq
75
+ rescue PubGrub::SolveFailure => e
76
+ incompatibility = e.incompatibility
92
77
 
93
- def indicate_progress
94
- Bundler.ui.info ".", false unless debug?
95
- end
78
+ names_to_unlock, names_to_allow_prereleases_for, extended_explanation = find_names_to_relax(incompatibility)
96
79
 
97
- include Molinillo::SpecificationProvider
80
+ names_to_relax = names_to_unlock + names_to_allow_prereleases_for
98
81
 
99
- def dependencies_for(specification)
100
- specification.dependencies_for_activated_platforms
101
- end
82
+ if names_to_relax.any?
83
+ if names_to_unlock.any?
84
+ Bundler.ui.debug "Found conflicts with locked dependencies. Will retry with #{names_to_unlock.join(", ")} unlocked...", true
102
85
 
103
- def search_for(dependency)
104
- @search_for[dependency] ||= begin
105
- name = dependency.name
106
- locked_results = @base[name].select {|spec| requirement_satisfied_by?(dependency, nil, spec) }
107
- locked_requirement = base_requirements[name]
108
- results = results_for(dependency) + locked_results
109
- results = results.select {|spec| requirement_satisfied_by?(locked_requirement, nil, spec) } if locked_requirement
110
- dep_platforms = dependency.gem_platforms(@platforms)
111
-
112
- @gem_version_promoter.sort_versions(dependency, results).group_by(&:version).reduce([]) do |groups, (_, specs)|
113
- relevant_platforms = dep_platforms.select {|platform| specs.any? {|spec| spec.match_platform(platform) } }
114
- next groups unless relevant_platforms.any?
115
-
116
- ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY)
117
- if ruby_specs.any?
118
- spec_group_ruby = SpecGroup.new(ruby_specs, [Gem::Platform::RUBY])
119
- spec_group_ruby.force_ruby_platform = dependency.force_ruby_platform
120
- groups << spec_group_ruby
121
- end
86
+ @base.unlock_names(names_to_unlock)
87
+ end
122
88
 
123
- next groups if @resolving_only_for_ruby || dependency.force_ruby_platform
89
+ if names_to_allow_prereleases_for.any?
90
+ Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retrying considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true
124
91
 
125
- platform_specs = relevant_platforms.flat_map {|platform| select_best_platform_match(specs, platform) }
126
- next groups if platform_specs == ruby_specs
92
+ @base.include_prereleases(names_to_allow_prereleases_for)
93
+ end
127
94
 
128
- spec_group = SpecGroup.new(platform_specs, relevant_platforms)
129
- groups << spec_group
95
+ root, logger = setup_solver
130
96
 
131
- groups
132
- end
97
+ Bundler.ui.debug "Retrying resolution...", true
98
+ retry
133
99
  end
134
- end
135
100
 
136
- def index_for(dependency)
137
- source_for(dependency.name).specs
138
- end
101
+ explanation = e.message
139
102
 
140
- def source_for(name)
141
- @source_requirements[name] || @source_requirements[:default]
142
- end
103
+ if extended_explanation
104
+ explanation << "\n\n"
105
+ explanation << extended_explanation
106
+ end
143
107
 
144
- def results_for(dependency)
145
- @results_for[dependency] ||= index_for(dependency).search(dependency)
108
+ raise SolveFailure.new(explanation)
146
109
  end
147
110
 
148
- def name_for(dependency)
149
- dependency.name
150
- end
111
+ def find_names_to_relax(incompatibility)
112
+ names_to_unlock = []
113
+ names_to_allow_prereleases_for = []
114
+ extended_explanation = nil
151
115
 
152
- def name_for_explicit_dependency_source
153
- Bundler.default_gemfile.basename.to_s
154
- rescue StandardError
155
- "Gemfile"
156
- end
116
+ while incompatibility.conflict?
117
+ cause = incompatibility.cause
118
+ incompatibility = cause.incompatibility
157
119
 
158
- def requirement_satisfied_by?(requirement, activated, spec)
159
- requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec)
160
- end
120
+ incompatibility.terms.each do |term|
121
+ package = term.package
122
+ name = package.name
123
+
124
+ if base_requirements[name]
125
+ names_to_unlock << name
126
+ elsif package.ignores_prereleases?
127
+ names_to_allow_prereleases_for << name
128
+ end
161
129
 
162
- def sort_dependencies(dependencies, activated, conflicts)
163
- dependencies.sort_by do |dependency|
164
- name = name_for(dependency)
165
- vertex = activated.vertex_named(name)
166
- [
167
- @base[name].any? ? 0 : 1,
168
- vertex.payload ? 0 : 1,
169
- vertex.root? ? 0 : 1,
170
- amount_constrained(dependency),
171
- conflicts[name] ? 0 : 1,
172
- vertex.payload ? 0 : search_for(dependency).count,
173
- ]
130
+ no_versions_incompat = [cause.incompatibility, cause.satisfier].find {|incompat| incompat.cause.is_a?(PubGrub::Incompatibility::NoVersions) }
131
+ next unless no_versions_incompat
132
+
133
+ extended_explanation = no_versions_incompat.extended_explanation
134
+ end
174
135
  end
136
+
137
+ [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, extended_explanation]
175
138
  end
176
139
 
177
- private
140
+ def parse_dependency(package, dependency)
141
+ range = if repository_for(package).is_a?(Source::Gemspec)
142
+ PubGrub::VersionRange.any
143
+ else
144
+ requirement_to_range(dependency)
145
+ end
178
146
 
179
- def base_requirements
180
- @base.base_requirements
147
+ PubGrub::VersionConstraint.new(package, :range => range)
181
148
  end
182
149
 
183
- def prerelease_specified
184
- @gem_version_promoter.prerelease_specified
150
+ def versions_for(package, range=VersionRange.any)
151
+ versions = range.select_versions(@sorted_versions[package])
152
+
153
+ sort_versions(package, versions)
185
154
  end
186
155
 
187
- def remove_from_candidates(spec)
188
- @base.delete(spec)
156
+ def no_versions_incompatibility_for(package, unsatisfied_term)
157
+ cause = PubGrub::Incompatibility::NoVersions.new(unsatisfied_term)
158
+ name = package.name
159
+ constraint = unsatisfied_term.constraint
160
+ constraint_string = constraint.constraint_string
161
+ requirements = constraint_string.split(" OR ").map {|req| Gem::Requirement.new(req.split(",")) }
162
+
163
+ if name == "bundler"
164
+ custom_explanation = "the current Bundler version (#{Bundler::VERSION}) does not satisfy #{constraint}"
165
+ extended_explanation = bundler_not_found_message(requirements)
166
+ else
167
+ specs_matching_other_platforms = filter_matching_specs(@all_specs[name], requirements)
189
168
 
190
- @results_for.keys.each do |dep|
191
- next unless dep.name == spec.name
169
+ platforms_explanation = specs_matching_other_platforms.any? ? " for any resolution platforms (#{package.platforms.join(", ")})" : ""
170
+ custom_explanation = "#{constraint} could not be found in #{repository_for(package)}#{platforms_explanation}"
192
171
 
193
- @results_for[dep].reject {|s| s.name == spec.name && s.version == spec.version }
172
+ label = "#{name} (#{constraint_string})"
173
+ extended_explanation = other_specs_matching_message(specs_matching_other_platforms, label) if specs_matching_other_platforms.any?
194
174
  end
195
175
 
196
- reset_spec_cache
176
+ Incompatibility.new([unsatisfied_term], :cause => cause, :custom_explanation => custom_explanation, :extended_explanation => extended_explanation)
197
177
  end
198
178
 
199
- def reset_spec_cache
200
- @search_for = {}
201
- @gem_version_promoter.reset
179
+ def debug?
180
+ ENV["BUNDLER_DEBUG_RESOLVER"] ||
181
+ ENV["BUNDLER_DEBUG_RESOLVER_TREE"] ||
182
+ ENV["DEBUG_RESOLVER"] ||
183
+ ENV["DEBUG_RESOLVER_TREE"] ||
184
+ false
202
185
  end
203
186
 
204
- # returns an integer \in (-\infty, 0]
205
- # a number closer to 0 means the dependency is less constraining
206
- #
207
- # dependencies w/ 0 or 1 possibilities (ignoring version requirements)
208
- # are given very negative values, so they _always_ sort first,
209
- # before dependencies that are unconstrained
210
- def amount_constrained(dependency)
211
- @amount_constrained ||= {}
212
- @amount_constrained[dependency.name] ||= if (base = @base[dependency.name]) && !base.empty?
213
- dependency.requirement.satisfied_by?(base.first.version) ? 0 : 1
214
- else
215
- all = index_for(dependency).search(dependency.name).size
187
+ def incompatibilities_for(package, version)
188
+ package_deps = @cached_dependencies[package]
189
+ sorted_versions = @sorted_versions[package]
190
+ package_deps[version].map do |dep_package, dep_constraint|
191
+ low = high = sorted_versions.index(version)
216
192
 
217
- if all <= 1
218
- all - 1_000_000
219
- else
220
- search = search_for(dependency)
221
- search = prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? }
222
- search - all
193
+ # find version low such that all >= low share the same dep
194
+ while low > 0 && package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint
195
+ low -= 1
196
+ end
197
+ low =
198
+ if low == 0
199
+ nil
200
+ else
201
+ sorted_versions[low]
202
+ end
203
+
204
+ # find version high such that all < high share the same dep
205
+ while high < sorted_versions.length && package_deps[sorted_versions[high]][dep_package] == dep_constraint
206
+ high += 1
207
+ end
208
+ high =
209
+ if high == sorted_versions.length
210
+ nil
211
+ else
212
+ sorted_versions[high]
213
+ end
214
+
215
+ range = PubGrub::VersionRange.new(:min => low, :max => high, :include_min => true)
216
+
217
+ self_constraint = PubGrub::VersionConstraint.new(package, :range => range)
218
+
219
+ dep_term = PubGrub::Term.new(dep_constraint, false)
220
+ self_term = PubGrub::Term.new(self_constraint, true)
221
+
222
+ custom_explanation = if dep_package.meta? && package.root?
223
+ "current #{dep_package} version is #{dep_constraint.constraint_string}"
223
224
  end
225
+
226
+ PubGrub::Incompatibility.new([self_term, dep_term], :cause => :dependency, :custom_explanation => custom_explanation)
227
+ end
228
+ end
229
+
230
+ def all_versions_for(package)
231
+ name = package.name
232
+ results = (@base[name] + filter_prereleases(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] }
233
+ locked_requirement = base_requirements[name]
234
+ results = filter_matching_specs(results, locked_requirement) if locked_requirement
235
+
236
+ versions = results.group_by(&:version).reduce([]) do |groups, (version, specs)|
237
+ platform_specs = package.platforms.flat_map {|platform| select_best_platform_match(specs, platform) }
238
+ next groups if platform_specs.empty?
239
+
240
+ ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY)
241
+ groups << Resolver::Candidate.new(version, :specs => ruby_specs) if ruby_specs.any?
242
+
243
+ next groups if platform_specs == ruby_specs || package.force_ruby_platform?
244
+
245
+ groups << Resolver::Candidate.new(version, :specs => platform_specs)
246
+
247
+ groups
224
248
  end
249
+
250
+ sort_versions(package, versions)
225
251
  end
226
252
 
227
- def verify_gemfile_dependencies_are_found!(requirements)
228
- requirements.map! do |requirement|
229
- name = requirement.name
230
- next requirement if name == "bundler"
231
- next if requirement.gem_platforms(@platforms).empty?
232
- next requirement unless search_for(requirement).empty?
233
- next unless requirement.current_platform?
253
+ def source_for(name)
254
+ @source_requirements[name] || @source_requirements[:default]
255
+ end
234
256
 
235
- raise GemNotFound, gem_not_found_message(name, requirement, source_for(name))
236
- end.compact!
257
+ def name_for_explicit_dependency_source
258
+ Bundler.default_gemfile.basename.to_s
259
+ rescue StandardError
260
+ "Gemfile"
237
261
  end
238
262
 
239
- def gem_not_found_message(name, requirement, source, extra_message = "")
240
- specs = source.specs.search(name).sort_by {|s| [s.version, s.platform.to_s] }
263
+ def raise_not_found!(package)
264
+ name = package.name
265
+ source = source_for(name)
266
+ specs = @all_specs[name]
241
267
  matching_part = name
242
- requirement_label = SharedHelpers.pretty_dependency(requirement)
268
+ requirement_label = SharedHelpers.pretty_dependency(package.dependency)
243
269
  cache_message = begin
244
270
  " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
245
271
  rescue GemfileNotFound
246
272
  nil
247
273
  end
248
- specs_matching_requirement = specs.select {| spec| requirement.matches_spec?(spec) }
274
+ specs_matching_requirement = filter_matching_specs(specs, package.dependency.requirement)
249
275
 
250
276
  if specs_matching_requirement.any?
251
277
  specs = specs_matching_requirement
252
278
  matching_part = requirement_label
253
- platforms = requirement.gem_platforms(@platforms)
279
+ platforms = package.platforms
254
280
  platform_label = platforms.size == 1 ? "platform '#{platforms.first}" : "platforms '#{platforms.join("', '")}"
255
281
  requirement_label = "#{requirement_label}' with #{platform_label}"
256
282
  end
257
283
 
258
- message = String.new("Could not find gem '#{requirement_label}'#{extra_message} in #{source}#{cache_message}.\n")
284
+ message = String.new("Could not find gem '#{requirement_label}' in #{source}#{cache_message}.\n")
259
285
 
260
286
  if specs.any?
261
- message << "\nThe source contains the following gems matching '#{matching_part}':\n"
262
- message << specs.map {|s| " * #{s.full_name}" }.join("\n")
287
+ message << "\n#{other_specs_matching_message(specs, matching_part)}"
263
288
  end
264
289
 
265
- message
290
+ raise GemNotFound, message
266
291
  end
267
292
 
268
- def version_conflict_message(e)
269
- # only show essential conflicts, if possible
270
- conflicts = e.conflicts.dup
293
+ private
294
+
295
+ def filter_matching_specs(specs, requirements)
296
+ Array(requirements).flat_map do |requirement|
297
+ specs.select {| spec| requirement_satisfied_by?(requirement, spec) }
298
+ end
299
+ end
271
300
 
272
- if conflicts["bundler"]
273
- conflicts.replace("bundler" => conflicts["bundler"])
301
+ def filter_prereleases(specs, package)
302
+ return specs unless package.ignores_prereleases? && specs.size > 1
303
+
304
+ specs.reject {|s| s.version.prerelease? }
305
+ end
306
+
307
+ def requirement_satisfied_by?(requirement, spec)
308
+ requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
309
+ end
310
+
311
+ def sort_versions(package, versions)
312
+ if versions.size > 1
313
+ @gem_version_promoter.sort_versions(package, versions).reverse
274
314
  else
275
- conflicts.delete_if do |_name, conflict|
276
- deps = conflict.requirement_trees.map(&:last).flatten(1)
277
- !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
278
- end
315
+ versions
279
316
  end
317
+ end
280
318
 
281
- e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty?
319
+ def repository_for(package)
320
+ source_for(package.name)
321
+ end
282
322
 
283
- e.message_with_trees(
284
- :full_message_for_conflict => lambda do |name, conflict|
285
- trees = conflict.requirement_trees
323
+ def base_requirements
324
+ @base.base_requirements
325
+ end
286
326
 
287
- # called first, because we want to reduce the amount of work required to find maximal empty sets
288
- trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } }
327
+ def prepare_dependencies(requirements, packages)
328
+ to_dependency_hash(requirements, packages).map do |dep_package, dep_constraint|
329
+ name = dep_package.name
289
330
 
290
- # bail out if tree size is too big for Array#combination to make any sense
291
- if trees.size <= 15
292
- maximal = 1.upto(trees.size).map do |size|
293
- trees.map(&:last).flatten(1).combination(size).to_a
294
- end.flatten(1).select do |deps|
295
- Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
296
- end.min_by(&:size)
331
+ next [dep_package, dep_constraint] if name == "bundler"
297
332
 
298
- trees.reject! {|t| !maximal.include?(t.last) } if maximal
333
+ versions = versions_for(dep_package, dep_constraint.range)
334
+ if versions.empty? && dep_package.ignores_prereleases?
335
+ @sorted_versions.delete(dep_package)
336
+ dep_package.consider_prereleases!
337
+ versions = versions_for(dep_package, dep_constraint.range)
338
+ end
339
+ next [dep_package, dep_constraint] unless versions.empty?
299
340
 
300
- trees.sort_by! {|t| t.reverse.map(&:name) }
301
- end
341
+ next unless dep_package.current_platform?
302
342
 
303
- if trees.size > 1 || name == "bundler"
304
- o = if name.end_with?("\0")
305
- String.new("Bundler found conflicting requirements for the #{name} version:")
306
- else
307
- String.new("Bundler could not find compatible versions for gem \"#{name}\":")
308
- end
309
- o << %(\n)
310
- o << %( In #{name_for_explicit_dependency_source}:\n)
311
- o << trees.map do |tree|
312
- t = "".dup
313
- depth = 2
314
-
315
- base_tree = tree.first
316
- base_tree_name = base_tree.name
317
-
318
- if base_tree_name.end_with?("\0")
319
- t = nil
320
- else
321
- tree.each do |req|
322
- t << " " * depth << SharedHelpers.pretty_dependency(req)
323
- unless tree.last == req
324
- if spec = conflict.activated_by_name[req.name]
325
- t << %( was resolved to #{spec.version}, which)
326
- end
327
- t << %( depends on)
328
- end
329
- t << %(\n)
330
- depth += 1
331
- end
332
- end
333
- t
334
- end.compact.join("\n")
335
- else
336
- o = String.new
337
- end
343
+ raise_not_found!(dep_package)
344
+ end.compact.to_h
345
+ end
338
346
 
339
- if name == "bundler"
340
- o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
341
-
342
- conflict_dependency = conflict.requirement
343
- conflict_requirement = conflict_dependency.requirement
344
- other_bundler_required = !conflict_requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION))
345
-
346
- if other_bundler_required
347
- o << "\n\n"
348
-
349
- candidate_specs = source_for(:default_bundler).specs.search(conflict_dependency)
350
- if candidate_specs.any?
351
- target_version = candidate_specs.last.version
352
- new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
353
- o << "Your bundle requires a different version of Bundler than the one you're running.\n"
354
- o << "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
355
- else
356
- o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
357
- end
358
- end
359
- elsif name.end_with?("\0")
360
- o << %(\n Current #{name} version:\n #{SharedHelpers.pretty_dependency(@metadata_requirements.find {|req| req.name == name })}\n\n)
361
- elsif !conflict.existing
362
- o << "\n"
363
-
364
- relevant_source = conflict.requirement.source || source_for(name)
365
-
366
- extra_message = if trees.first.size > 1
367
- ", which is required by gem '#{SharedHelpers.pretty_dependency(trees.first[-2])}',"
368
- else
369
- ""
370
- end
371
-
372
- o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message)
373
- end
347
+ def other_specs_matching_message(specs, requirement)
348
+ message = String.new("The source contains the following gems matching '#{requirement}':\n")
349
+ message << specs.map {|s| " * #{s.full_name}" }.join("\n")
350
+ message
351
+ end
374
352
 
375
- o
353
+ def requirement_to_range(requirement)
354
+ ranges = requirement.requirements.map do |(op, version)|
355
+ ver = Resolver::Candidate.new(version).generic!
356
+ platform_ver = Resolver::Candidate.new(version).platform_specific!
357
+
358
+ case op
359
+ when "~>"
360
+ name = "~> #{ver}"
361
+ bump = Resolver::Candidate.new(version.bump.to_s + ".A")
362
+ PubGrub::VersionRange.new(:name => name, :min => ver, :max => bump, :include_min => true)
363
+ when ">"
364
+ PubGrub::VersionRange.new(:min => platform_ver)
365
+ when ">="
366
+ PubGrub::VersionRange.new(:min => ver, :include_min => true)
367
+ when "<"
368
+ PubGrub::VersionRange.new(:max => ver)
369
+ when "<="
370
+ PubGrub::VersionRange.new(:max => platform_ver, :include_max => true)
371
+ when "="
372
+ PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true)
373
+ when "!="
374
+ PubGrub::VersionRange.new(:min => ver, :max => platform_ver, :include_min => true, :include_max => true).invert
375
+ else
376
+ raise "bad version specifier: #{op}"
376
377
  end
377
- )
378
+ end
379
+
380
+ ranges.inject(&:intersect)
381
+ end
382
+
383
+ def to_dependency_hash(dependencies, packages)
384
+ dependencies.inject({}) do |deps, dep|
385
+ package = packages[dep.name]
386
+
387
+ current_req = deps[package]
388
+ new_req = parse_dependency(package, dep.requirement)
389
+
390
+ deps[package] = if current_req
391
+ current_req.intersect(new_req)
392
+ else
393
+ new_req
394
+ end
395
+
396
+ deps
397
+ end
398
+ end
399
+
400
+ def bundler_not_found_message(conflict_dependencies)
401
+ candidate_specs = filter_matching_specs(source_for(:default_bundler).specs.search("bundler"), conflict_dependencies)
402
+
403
+ if candidate_specs.any?
404
+ target_version = candidate_specs.last.version
405
+ new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
406
+ "Your bundle requires a different version of Bundler than the one you're running.\n" \
407
+ "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n"
408
+ else
409
+ "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
410
+ end
378
411
  end
379
412
  end
380
413
  end