Merge pull request #3789 from alyssais/undeclared_runtime_dependencies

Undeclared dependencies in runtime_dependencies
This commit is contained in:
Mike McQuaid 2018-04-25 13:10:07 +01:00 committed by GitHub
commit 0fd2db347b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 17 deletions

View File

@ -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"

View File

@ -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)

View File

@ -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

View File

@ -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"