mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Implement caching for dependency expansion
This commit is contained in:
parent
3c3bf1c74d
commit
e49a338896
9
Library/Homebrew/dependencies.rbi
Normal file
9
Library/Homebrew/dependencies.rbi
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# typed: strict
|
||||||
|
|
||||||
|
class Dependencies < SimpleDelegator
|
||||||
|
include Kernel
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requirements < SimpleDelegator
|
||||||
|
include Kernel
|
||||||
|
end
|
@ -35,15 +35,11 @@ module DependenciesHelpers
|
|||||||
end
|
end
|
||||||
|
|
||||||
def recursive_includes(klass, root_dependent, includes, ignores)
|
def recursive_includes(klass, root_dependent, includes, ignores)
|
||||||
type = if klass == Dependency
|
raise ArgumentError, "Invalid class argument: #{klass}" if klass != Dependency && klass != Requirement
|
||||||
:dependencies
|
|
||||||
elsif klass == Requirement
|
|
||||||
:requirements
|
|
||||||
else
|
|
||||||
raise ArgumentError, "Invalid class argument: #{klass}"
|
|
||||||
end
|
|
||||||
|
|
||||||
root_dependent.send("recursive_#{type}") do |dependent, dep|
|
cache_key = "recursive_includes_#{includes}_#{ignores}"
|
||||||
|
|
||||||
|
klass.expand(root_dependent, cache_key: cache_key) do |dependent, dep|
|
||||||
if dep.recommended?
|
if dep.recommended?
|
||||||
klass.prune if ignores.include?("recommended?") || dependent.build.without?(dep)
|
klass.prune if ignores.include?("recommended?") || dependent.build.without?(dep)
|
||||||
elsif dep.optional?
|
elsif dep.optional?
|
||||||
@ -57,7 +53,7 @@ module DependenciesHelpers
|
|||||||
|
|
||||||
# If a tap isn't installed, we can't find the dependencies of one of
|
# If a tap isn't installed, we can't find the dependencies of one of
|
||||||
# its formulae, and an exception will be thrown if we try.
|
# its formulae, and an exception will be thrown if we try.
|
||||||
if type == :dependencies &&
|
if klass == Dependency &&
|
||||||
dep.is_a?(TapDependency) &&
|
dep.is_a?(TapDependency) &&
|
||||||
!dep.tap.installed?
|
!dep.tap.installed?
|
||||||
Dependency.keep_but_prune_recursive_deps
|
Dependency.keep_but_prune_recursive_deps
|
||||||
|
@ -11,6 +11,7 @@ class Dependency
|
|||||||
|
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
include Dependable
|
include Dependable
|
||||||
|
extend Cachable
|
||||||
|
|
||||||
attr_reader :name, :tags, :env_proc, :option_names
|
attr_reader :name, :tags, :env_proc, :option_names
|
||||||
|
|
||||||
@ -87,12 +88,17 @@ class Dependency
|
|||||||
# `[dependent, dep]` pairs to allow callers to apply arbitrary filters to
|
# `[dependent, dep]` pairs to allow callers to apply arbitrary filters to
|
||||||
# the list.
|
# the list.
|
||||||
# The default filter, which is applied when a block is not given, omits
|
# The default filter, which is applied when a block is not given, omits
|
||||||
# optionals and recommendeds based on what the dependent has asked for.
|
# optionals and recommendeds based on what the dependent has asked for
|
||||||
def expand(dependent, deps = dependent.deps, &block)
|
def expand(dependent, deps = dependent.deps, cache_key: nil, &block)
|
||||||
# Keep track dependencies to avoid infinite cyclic dependency recursion.
|
# Keep track dependencies to avoid infinite cyclic dependency recursion.
|
||||||
@expand_stack ||= []
|
@expand_stack ||= []
|
||||||
@expand_stack.push dependent.name
|
@expand_stack.push dependent.name
|
||||||
|
|
||||||
|
if cache_key.present?
|
||||||
|
cache[cache_key] ||= {}
|
||||||
|
return cache[cache_key][dependent.full_name].dup if cache[cache_key][dependent.full_name]
|
||||||
|
end
|
||||||
|
|
||||||
expanded_deps = []
|
expanded_deps = []
|
||||||
|
|
||||||
deps.each do |dep|
|
deps.each do |dep|
|
||||||
@ -104,18 +110,20 @@ class Dependency
|
|||||||
when :skip
|
when :skip
|
||||||
next if @expand_stack.include? dep.name
|
next if @expand_stack.include? dep.name
|
||||||
|
|
||||||
expanded_deps.concat(expand(dep.to_formula, &block))
|
expanded_deps.concat(expand(dep.to_formula, cache_key: cache_key, &block))
|
||||||
when :keep_but_prune_recursive_deps
|
when :keep_but_prune_recursive_deps
|
||||||
expanded_deps << dep
|
expanded_deps << dep
|
||||||
else
|
else
|
||||||
next if @expand_stack.include? dep.name
|
next if @expand_stack.include? dep.name
|
||||||
|
|
||||||
expanded_deps.concat(expand(dep.to_formula, &block))
|
expanded_deps.concat(expand(dep.to_formula, cache_key: cache_key, &block))
|
||||||
expanded_deps << dep
|
expanded_deps << dep
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
merge_repeats(expanded_deps)
|
expanded_deps = merge_repeats(expanded_deps)
|
||||||
|
cache[cache_key][dependent.full_name] = expanded_deps.dup if cache_key.present?
|
||||||
|
expanded_deps
|
||||||
ensure
|
ensure
|
||||||
@expand_stack.pop
|
@expand_stack.pop
|
||||||
end
|
end
|
||||||
|
@ -310,6 +310,8 @@ module Homebrew
|
|||||||
Formula.clear_cache
|
Formula.clear_cache
|
||||||
Keg.clear_cache
|
Keg.clear_cache
|
||||||
Tab.clear_cache
|
Tab.clear_cache
|
||||||
|
Dependency.clear_cache
|
||||||
|
Requirement.clear_cache
|
||||||
tab = Tab.for_keg(keg)
|
tab = Tab.for_keg(keg)
|
||||||
original_tab = tab.dup
|
original_tab = tab.dup
|
||||||
tab.poured_from_bottle = false
|
tab.poured_from_bottle = false
|
||||||
|
@ -165,7 +165,7 @@ class Formula
|
|||||||
# during the installation of a {Formula}. This is annoying but the result of
|
# during the installation of a {Formula}. This is annoying but the result of
|
||||||
# state that we're trying to eliminate.
|
# state that we're trying to eliminate.
|
||||||
# @return [BuildOptions]
|
# @return [BuildOptions]
|
||||||
attr_accessor :build
|
attr_reader :build
|
||||||
|
|
||||||
# Whether this formula should be considered outdated
|
# Whether this formula should be considered outdated
|
||||||
# if the target of the alias it was installed with has since changed.
|
# if the target of the alias it was installed with has since changed.
|
||||||
@ -222,10 +222,28 @@ class Formula
|
|||||||
spec = send(spec_sym)
|
spec = send(spec_sym)
|
||||||
raise FormulaSpecificationError, "#{spec_sym} spec is not available for #{full_name}" unless spec
|
raise FormulaSpecificationError, "#{spec_sym} spec is not available for #{full_name}" unless spec
|
||||||
|
|
||||||
|
old_spec_sym = @active_spec_sym
|
||||||
@active_spec = spec
|
@active_spec = spec
|
||||||
@active_spec_sym = spec_sym
|
@active_spec_sym = spec_sym
|
||||||
validate_attributes!
|
validate_attributes!
|
||||||
@build = active_spec.build
|
@build = active_spec.build
|
||||||
|
|
||||||
|
return if spec_sym == old_spec_sym
|
||||||
|
|
||||||
|
Dependency.clear_cache
|
||||||
|
Requirement.clear_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
# @private
|
||||||
|
def build=(build_options)
|
||||||
|
old_options = @build
|
||||||
|
@build = build_options
|
||||||
|
|
||||||
|
return if old_options.used_options == build_options.used_options &&
|
||||||
|
old_options.unused_options == build_options.unused_options
|
||||||
|
|
||||||
|
Dependency.clear_cache
|
||||||
|
Requirement.clear_cache
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -1657,13 +1675,15 @@ class Formula
|
|||||||
# means if a depends on b then b will be ordered before a in this list
|
# means if a depends on b then b will be ordered before a in this list
|
||||||
# @private
|
# @private
|
||||||
def recursive_dependencies(&block)
|
def recursive_dependencies(&block)
|
||||||
Dependency.expand(self, &block)
|
cache_key = "Formula#recursive_dependencies" unless block
|
||||||
|
Dependency.expand(self, cache_key: cache_key, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# The full set of Requirements for this formula's dependency tree.
|
# The full set of Requirements for this formula's dependency tree.
|
||||||
# @private
|
# @private
|
||||||
def recursive_requirements(&block)
|
def recursive_requirements(&block)
|
||||||
Requirement.expand(self, &block)
|
cache_key = "Formula#recursive_requirements" unless block
|
||||||
|
Requirement.expand(self, cache_key: cache_key, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a Keg for the opt_prefix or installed_prefix if they exist.
|
# Returns a Keg for the opt_prefix or installed_prefix if they exist.
|
||||||
|
@ -572,8 +572,8 @@ class FormulaInstaller
|
|||||||
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
|
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
|
||||||
req_deps = []
|
req_deps = []
|
||||||
formulae = [formula]
|
formulae = [formula]
|
||||||
formula_deps_map = Dependency.expand(formula)
|
formula_deps_map = formula.recursive_dependencies
|
||||||
.index_by(&:name)
|
.index_by(&:name)
|
||||||
|
|
||||||
while (f = formulae.pop)
|
while (f = formulae.pop)
|
||||||
runtime_requirements = runtime_requirements(f)
|
runtime_requirements = runtime_requirements(f)
|
||||||
|
@ -114,6 +114,12 @@ class Options
|
|||||||
@options.to_a * other
|
@options.to_a * other
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
instance_of?(other.class) &&
|
||||||
|
to_a == other.to_a
|
||||||
|
end
|
||||||
|
alias eql? ==
|
||||||
|
|
||||||
def empty?
|
def empty?
|
||||||
@options.empty?
|
@options.empty?
|
||||||
end
|
end
|
||||||
|
@ -15,6 +15,7 @@ class Requirement
|
|||||||
extend T::Sig
|
extend T::Sig
|
||||||
|
|
||||||
include Dependable
|
include Dependable
|
||||||
|
extend Cachable
|
||||||
|
|
||||||
attr_reader :tags, :name, :cask, :download
|
attr_reader :tags, :name, :cask, :download
|
||||||
|
|
||||||
@ -219,7 +220,12 @@ class Requirement
|
|||||||
# the list.
|
# the list.
|
||||||
# The default filter, which is applied when a block is not given, omits
|
# The default filter, which is applied when a block is not given, omits
|
||||||
# optionals and recommendeds based on what the dependent has asked for.
|
# optionals and recommendeds based on what the dependent has asked for.
|
||||||
def expand(dependent, &block)
|
def expand(dependent, cache_key: nil, &block)
|
||||||
|
if cache_key.present?
|
||||||
|
cache[cache_key] ||= {}
|
||||||
|
return cache[cache_key][dependent.full_name].dup if cache[cache_key][dependent.full_name]
|
||||||
|
end
|
||||||
|
|
||||||
reqs = Requirements.new
|
reqs = Requirements.new
|
||||||
|
|
||||||
formulae = dependent.recursive_dependencies.map(&:to_formula)
|
formulae = dependent.recursive_dependencies.map(&:to_formula)
|
||||||
@ -233,6 +239,7 @@ class Requirement
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
cache[cache_key][dependent.full_name] = reqs.dup if cache_key.present?
|
||||||
reqs
|
reqs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -166,8 +166,7 @@ describe Formula do
|
|||||||
end
|
end
|
||||||
|
|
||||||
build_values_with_no_installed_alias = [
|
build_values_with_no_installed_alias = [
|
||||||
nil,
|
BuildOptions.new(Options.new, f.options),
|
||||||
BuildOptions.new({}, {}),
|
|
||||||
Tab.new(source: { "path" => f.path.to_s }),
|
Tab.new(source: { "path" => f.path.to_s }),
|
||||||
]
|
]
|
||||||
build_values_with_no_installed_alias.each do |build|
|
build_values_with_no_installed_alias.each do |build|
|
||||||
@ -201,7 +200,10 @@ describe Formula do
|
|||||||
url "foo-1.0"
|
url "foo-1.0"
|
||||||
end
|
end
|
||||||
|
|
||||||
build_values_with_no_installed_alias = [nil, BuildOptions.new({}, {}), Tab.new(source: { "path" => f.path })]
|
build_values_with_no_installed_alias = [
|
||||||
|
BuildOptions.new(Options.new, f.options),
|
||||||
|
Tab.new(source: { "path" => f.path }),
|
||||||
|
]
|
||||||
build_values_with_no_installed_alias.each do |build|
|
build_values_with_no_installed_alias.each do |build|
|
||||||
f.build = build
|
f.build = build
|
||||||
expect(f.installed_alias_path).to be nil
|
expect(f.installed_alias_path).to be nil
|
||||||
@ -405,7 +407,7 @@ describe Formula do
|
|||||||
f = formula alias_path: alias_path do
|
f = formula alias_path: alias_path do
|
||||||
url "foo-1.0"
|
url "foo-1.0"
|
||||||
end
|
end
|
||||||
f.build = BuildOptions.new({}, {})
|
f.build = BuildOptions.new(Options.new, f.options)
|
||||||
|
|
||||||
expect(f.alias_path).to eq(alias_path)
|
expect(f.alias_path).to eq(alias_path)
|
||||||
expect(f.installed_alias_path).to be nil
|
expect(f.installed_alias_path).to be nil
|
||||||
@ -802,7 +804,7 @@ describe Formula do
|
|||||||
|
|
||||||
expect(Set.new(f1.recursive_requirements)).to eq(Set[])
|
expect(Set.new(f1.recursive_requirements)).to eq(Set[])
|
||||||
|
|
||||||
f1.build = BuildOptions.new(["--with-xcode"], f1.options)
|
f1.build = BuildOptions.new(Options.create(["--with-xcode"]), f1.options)
|
||||||
|
|
||||||
expect(Set.new(f1.recursive_requirements)).to eq(Set[xcode])
|
expect(Set.new(f1.recursive_requirements)).to eq(Set[xcode])
|
||||||
|
|
||||||
|
@ -185,6 +185,8 @@ RSpec.configure do |config|
|
|||||||
Formula.clear_cache
|
Formula.clear_cache
|
||||||
Keg.clear_cache
|
Keg.clear_cache
|
||||||
Tab.clear_cache
|
Tab.clear_cache
|
||||||
|
Dependency.clear_cache
|
||||||
|
Requirement.clear_cache
|
||||||
FormulaInstaller.clear_attempted
|
FormulaInstaller.clear_attempted
|
||||||
FormulaInstaller.clear_installed
|
FormulaInstaller.clear_installed
|
||||||
|
|
||||||
@ -229,6 +231,8 @@ RSpec.configure do |config|
|
|||||||
Formula.clear_cache
|
Formula.clear_cache
|
||||||
Keg.clear_cache
|
Keg.clear_cache
|
||||||
Tab.clear_cache
|
Tab.clear_cache
|
||||||
|
Dependency.clear_cache
|
||||||
|
Requirement.clear_cache
|
||||||
|
|
||||||
FileUtils.rm_rf [
|
FileUtils.rm_rf [
|
||||||
*TEST_DIRECTORIES,
|
*TEST_DIRECTORIES,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user