bundler 2.3.26 → 2.4.5

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