aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDavid Rodríguez <deivid.rodriguez@riseup.net>2023-03-16 20:11:18 +0100
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2023-03-23 17:18:49 +0900
commitebebc90ec297c945cdf94c90f8db85dd7ddbcb7b (patch)
treeb9ae53f98da645ebab294491c994746da8f2306e /lib
parent8e6bbc032c1bde617a45e418af697831df471083 (diff)
downloadruby-ebebc90ec297c945cdf94c90f8db85dd7ddbcb7b.tar.gz
Refactor incomplete specs handling
Recent bugs fixed made me realize we were relying on state too much here. We only need to keep incomplete specs to be able to expire them and retry resolution without them locked. If we use a separate class, we can do that more transparently and handle them just like we handle "missing specs".
Diffstat (limited to 'lib')
-rw-r--r--lib/bundler.rb1
-rw-r--r--lib/bundler/incomplete_specification.rb24
-rw-r--r--lib/bundler/resolver/base.rb8
-rw-r--r--lib/bundler/spec_set.rb19
4 files changed, 39 insertions, 13 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 3f7fc8f5f4..71e16b1959 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -62,6 +62,7 @@ module Bundler
autoload :GemHelpers, File.expand_path("bundler/gem_helpers", __dir__)
autoload :GemVersionPromoter, File.expand_path("bundler/gem_version_promoter", __dir__)
autoload :Graph, File.expand_path("bundler/graph", __dir__)
+ autoload :IncompleteSpecification, File.expand_path("bundler/incomplete_specification", __dir__)
autoload :Index, File.expand_path("bundler/index", __dir__)
autoload :Injector, File.expand_path("bundler/injector", __dir__)
autoload :Installer, File.expand_path("bundler/installer", __dir__)
diff --git a/lib/bundler/incomplete_specification.rb b/lib/bundler/incomplete_specification.rb
new file mode 100644
index 0000000000..addf7554d8
--- /dev/null
+++ b/lib/bundler/incomplete_specification.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Bundler
+ #
+ # Represents a package name that was found to be incomplete when trying to
+ # materialize a fresh resolution or the lockfile.
+ #
+ # Holds the actual partially complete set of specifications for the name.
+ # These are used so that they can be unlocked in a future resolution, and fix
+ # the situation.
+ #
+ class IncompleteSpecification
+ attr_reader :name, :partially_complete_specs
+
+ def initialize(name, partially_complete_specs = [])
+ @name = name
+ @partially_complete_specs = partially_complete_specs
+ end
+
+ def ==(other)
+ partially_complete_specs == other.partially_complete_specs
+ end
+ end
+end
diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb
index 6921c047a7..ad19577971 100644
--- a/lib/bundler/resolver/base.rb
+++ b/lib/bundler/resolver/base.rb
@@ -34,9 +34,11 @@ module Bundler
@base[name]
end
- def delete(specs)
- specs.each do |spec|
- @base.delete(spec)
+ def delete(incomplete_specs)
+ incomplete_specs.each do |incomplete_spec|
+ incomplete_spec.partially_complete_specs.each do |spec|
+ @base.delete(spec)
+ end
end
end
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index cf63c16a70..2361fc356c 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -7,11 +7,8 @@ module Bundler
include Enumerable
include TSort
- attr_reader :incomplete_specs
-
- def initialize(specs, incomplete_specs = [])
+ def initialize(specs)
@specs = specs
- @incomplete_specs = incomplete_specs
end
def for(dependencies, check = false, platforms = [nil])
@@ -45,7 +42,7 @@ module Bundler
end
if incomplete && check
- @incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)]
+ specs << IncompleteSpecification.new(name, lookup[name])
end
end
@@ -84,7 +81,7 @@ module Bundler
def materialize(deps)
materialized = self.for(deps, true)
- SpecSet.new(materialized, incomplete_specs)
+ SpecSet.new(materialized)
end
# Materialize for all the specs in the spec set, regardless of what platform they're for
@@ -103,17 +100,19 @@ module Bundler
def incomplete_ruby_specs?(deps)
return false if @specs.empty?
- @incomplete_specs = []
-
- self.for(deps, true, [Gem::Platform::RUBY])
+ materialized = self.for(deps, true, [Gem::Platform::RUBY])
- @incomplete_specs.any?
+ SpecSet.new(materialized).incomplete_specs.any?
end
def missing_specs
@specs.select {|s| s.is_a?(LazySpecification) }
end
+ def incomplete_specs
+ @specs.select {|s| s.is_a?(IncompleteSpecification) }
+ end
+
def merge(set)
arr = sorted.dup
set.each do |set_spec|