Merge pull request #3789 from alyssais/undeclared_runtime_dependencies
Undeclared dependencies in runtime_dependencies
This commit is contained in:
commit
0fd2db347b
@ -472,9 +472,17 @@ class Pathname
|
||||
}
|
||||
end
|
||||
|
||||
def binary_executable?
|
||||
false
|
||||
end
|
||||
|
||||
def mach_o_bundle?
|
||||
false
|
||||
end
|
||||
|
||||
def dylib?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
require "extend/os/pathname"
|
||||
|
||||
@ -12,6 +12,7 @@ require "install_renamed"
|
||||
require "pkg_version"
|
||||
require "keg"
|
||||
require "migrator"
|
||||
require "linkage_checker"
|
||||
require "extend/ENV"
|
||||
require "language/python"
|
||||
require "tab"
|
||||
@ -1486,22 +1487,50 @@ class Formula
|
||||
Requirement.expand(self, &block)
|
||||
end
|
||||
|
||||
# Returns a Keg for the opt_prefix or installed_prefix if they exist.
|
||||
# If not, return nil.
|
||||
# @private
|
||||
def opt_or_installed_prefix_keg
|
||||
if optlinked? && opt_prefix.exist?
|
||||
Keg.new(opt_prefix)
|
||||
elsif installed_prefix.directory?
|
||||
Keg.new(installed_prefix)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a list of Dependency objects that are required at runtime.
|
||||
# @private
|
||||
def runtime_dependencies(read_from_tab: true)
|
||||
if read_from_tab &&
|
||||
installed_prefix.directory? &&
|
||||
(keg = Keg.new(installed_prefix)) &&
|
||||
(keg = opt_or_installed_prefix_keg) &&
|
||||
(tab_deps = keg.runtime_dependencies)
|
||||
return tab_deps.map { |d| Dependency.new d["full_name"] }.compact
|
||||
end
|
||||
|
||||
declared_runtime_dependencies | undeclared_runtime_dependencies
|
||||
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?
|
||||
Dependency.prune if !dependency.required? && build.without?(dependency)
|
||||
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
|
||||
|
||||
linkage_checker = LinkageChecker.new(keg, self)
|
||||
linkage_checker.undeclared_deps.map { |n| Dependency.new(n) }
|
||||
end
|
||||
|
||||
# Returns a list of formulae depended on by this formula that aren't
|
||||
# installed
|
||||
def missing_dependencies(hide: nil)
|
||||
|
||||
@ -2,6 +2,8 @@ require "keg"
|
||||
require "formula"
|
||||
|
||||
class LinkageChecker
|
||||
attr_reader :undeclared_deps
|
||||
|
||||
def initialize(keg, formula = nil)
|
||||
@keg = keg
|
||||
@formula = formula || resolve_formula(keg)
|
||||
@ -64,7 +66,7 @@ class LinkageChecker
|
||||
checked_dylibs = Set.new
|
||||
@keg.find do |file|
|
||||
next if file.symlink? || file.directory?
|
||||
next unless file.dylib? || file.binary_executable? || file.mach_o_bundle?
|
||||
next if !file.dylib? && !file.binary_executable? && !file.mach_o_bundle?
|
||||
|
||||
# weakly loaded dylibs may not actually exist on disk, so skip them
|
||||
# when checking for broken linkage
|
||||
@ -108,10 +110,17 @@ class LinkageChecker
|
||||
next false unless dep.optional? || dep.recommended?
|
||||
formula.build.without?(dep)
|
||||
end
|
||||
|
||||
declared_deps = formula.deps.reject { |dep| filter_out.call(dep) }.map(&:name)
|
||||
runtime_deps = keg.to_formula.runtime_dependencies(read_from_tab: false)
|
||||
recursive_deps = runtime_deps.map { |dep| dep.to_formula.name }
|
||||
declared_dep_names = declared_deps.map { |dep| dep.split("/").last }
|
||||
recursive_deps = formula.declared_runtime_dependencies.map do |dep|
|
||||
begin
|
||||
dep.to_formula.name
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
end
|
||||
end.compact
|
||||
|
||||
indirect_deps = []
|
||||
undeclared_deps = []
|
||||
@brewed_dylibs.each_key do |full_name|
|
||||
@ -123,15 +132,19 @@ class LinkageChecker
|
||||
undeclared_deps << full_name
|
||||
end
|
||||
end
|
||||
|
||||
sort_by_formula_full_name!(indirect_deps)
|
||||
sort_by_formula_full_name!(undeclared_deps)
|
||||
|
||||
unnecessary_deps = declared_dep_names.reject do |full_name|
|
||||
name = full_name.split("/").last
|
||||
next true if Formula[name].bin.directory?
|
||||
@brewed_dylibs.keys.map { |x| x.split("/").last }.include?(name)
|
||||
end
|
||||
|
||||
missing_deps = @broken_deps.values.flatten.map { |d| dylib_to_dep(d) }
|
||||
unnecessary_deps -= missing_deps
|
||||
|
||||
[indirect_deps, undeclared_deps, unnecessary_deps]
|
||||
end
|
||||
|
||||
|
||||
@ -685,6 +685,7 @@ describe Formula do
|
||||
expect(f5.runtime_dependencies.map(&:name)).to eq(["f1", "f4"])
|
||||
end
|
||||
|
||||
describe "#runtime_dependencies" do
|
||||
specify "runtime dependencies with optional deps from tap" do
|
||||
tap_loader = double
|
||||
|
||||
@ -707,6 +708,21 @@ describe Formula do
|
||||
expect(f3.runtime_dependencies.map(&:name)).to eq(["foo/bar/f1", "baz/qux/f2"])
|
||||
end
|
||||
|
||||
it "includes non-declared direct dependencies", :focus do
|
||||
formula = Class.new(Testball).new
|
||||
dependency = formula("dependency") { url "f-1.0" }
|
||||
|
||||
formula.brew { formula.install }
|
||||
keg = Keg.for(formula.installed_prefix)
|
||||
keg.link
|
||||
|
||||
linkage_checker = double("linkage checker", undeclared_deps: [dependency.name])
|
||||
allow(LinkageChecker).to receive(:new).and_return(linkage_checker)
|
||||
|
||||
expect(formula.runtime_dependencies.map(&:name)).to eq [dependency.name]
|
||||
end
|
||||
end
|
||||
|
||||
specify "requirements" do
|
||||
f1 = formula "f1" do
|
||||
url "f1-1"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user