From 375d4015801024a0a5d46df72c22dacc1cd51030 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 16 Jul 2018 16:46:39 +0100 Subject: [PATCH] Handle missing formulae in runtime_dependencies Hopefully put this to bed for once and for all. Add a new method `runtime_formulae_dependencies` which returns the `runtime_dependencies` which have only the `Formula` objects that exist. I've checked all the `runtime_dependencies` callers and updated them accordingly. Also, fix a few cases where runtime dependencies were being read from the tab when this wasn't desirable. --- Library/Homebrew/formula.rb | 72 ++++++++++++++++----------- Library/Homebrew/formula_installer.rb | 10 ++-- Library/Homebrew/linkage_checker.rb | 10 +--- Library/Homebrew/tab.rb | 2 +- 4 files changed, 52 insertions(+), 42 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index f0203cad93..10b483562c 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1521,6 +1521,7 @@ class Formula # @private def runtime_dependencies(read_from_tab: true, undeclared: true) if read_from_tab && + undeclared && (keg = opt_or_installed_prefix_keg) && (tab_deps = keg.runtime_dependencies) return tab_deps.map do |d| @@ -1534,44 +1535,28 @@ class Formula declared_runtime_dependencies | undeclared_runtime_dependencies end - # Returns a list of Dependency objects that are declared in the formula. + # Returns a list of Formula objects that are required at runtime. # @private - def declared_runtime_dependencies - recursive_dependencies do |_, dependency| - Dependency.prune if dependency.build? - next if dependency.required? - if build.any_args_or_options? - Dependency.prune if build.without?(dependency) - elsif !dependency.recommended? - Dependency.prune + def runtime_formula_dependencies(read_from_tab: true, undeclared: true) + runtime_dependencies( + read_from_tab: read_from_tab, + undeclared: undeclared, + ).map do |d| + begin + d.to_formula + rescue FormulaUnavailableError + nil end - end - end - - # Returns a list of Dependency objects that are not declared in the formula - # but the formula links to. - # @private - def undeclared_runtime_dependencies - keg = opt_or_installed_prefix_keg - return [] unless keg - - undeclared_deps = CacheStoreDatabase.use(:linkage) do |db| - linkage_checker = LinkageChecker.new(keg, self, cache_db: db) - linkage_checker.undeclared_deps.map { |n| Dependency.new(n) } - end - - undeclared_deps + end.compact end # Returns a list of formulae depended on by this formula that aren't # installed def missing_dependencies(hide: nil) hide ||= [] - runtime_dependencies.map(&:to_formula).select do |d| - hide.include?(d.name) || d.installed_prefixes.empty? + runtime_formula_dependencies.select do |f| + hide.include?(f.name) || f.installed_prefixes.empty? end - rescue FormulaUnavailableError - [] end # @private @@ -1745,6 +1730,35 @@ class Formula PYTHON end + # Returns a list of Dependency objects that are declared in the formula. + # @private + def declared_runtime_dependencies + recursive_dependencies do |_, dependency| + Dependency.prune if dependency.build? + next if dependency.required? + if build.any_args_or_options? + Dependency.prune if build.without?(dependency) + elsif !dependency.recommended? + Dependency.prune + end + end + end + + # Returns a list of Dependency objects that are not declared in the formula + # but the formula links to. + # @private + def undeclared_runtime_dependencies + keg = opt_or_installed_prefix_keg + return [] unless keg + + undeclared_deps = CacheStoreDatabase.use(:linkage) do |db| + linkage_checker = LinkageChecker.new(keg, self, cache_db: db) + linkage_checker.undeclared_deps.map { |n| Dependency.new(n) } + end + + undeclared_deps + end + public # To call out to the system, we use the `system` method and we prefer diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 1494ffb74d..b2f9f60063 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -155,8 +155,8 @@ class FormulaInstaller recursive_deps = formula.recursive_dependencies recursive_formulae = recursive_deps.map(&:to_formula) - recursive_runtime_deps = formula.runtime_dependencies - recursive_runtime_formulae = recursive_runtime_deps.map(&:to_formula) + recursive_runtime_formulae = + formula.runtime_formula_dependencies(undeclared: false) recursive_dependencies = [] recursive_formulae.each do |dep| @@ -174,7 +174,9 @@ class FormulaInstaller EOS end - if recursive_formulae.flat_map(&:recursive_dependencies).map(&:to_s).include?(formula.name) + if recursive_formulae.flat_map(&:recursive_dependencies) + .map(&:to_s) + .include?(formula.name) raise CannotInstallFormulaError, <<~EOS #{formula.full_name} contains a recursive dependency on itself! EOS @@ -431,7 +433,7 @@ class FormulaInstaller end def runtime_requirements(formula) - runtime_deps = formula.runtime_dependencies.map(&:to_formula) + runtime_deps = formula.runtime_formula_dependencies(undeclared: false) recursive_requirements = formula.recursive_requirements do |dependent, _| Requirement.prune unless runtime_deps.include?(dependent) end diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index d297ac215f..31345ec610 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -149,14 +149,8 @@ class LinkageChecker declared_deps_names = declared_deps_full_names.map do |dep| dep.split("/").last end - recursive_deps = formula.runtime_dependencies(undeclared: false) - .map do |dep| - begin - dep.to_formula.name - rescue FormulaUnavailableError - nil - end - end.compact + recursive_deps = formula.runtime_formula_dependencies(undeclared: false) + .map(&:name) indirect_deps = [] undeclared_deps = [] diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index c0eccc6372..5c27ad1454 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -17,7 +17,7 @@ class Tab < OpenStruct # Instantiates a Tab for a new installation of a formula. def self.create(formula, compiler, stdlib) build = formula.build - runtime_deps = formula.declared_runtime_dependencies + runtime_deps = formula.runtime_dependencies(undeclared: false) attributes = { "homebrew_version" => HOMEBREW_VERSION, "used_options" => build.used_options.as_flags,