linkage: fix --test exit code.

Ensure that a non-zero exit code is set both for missing random dylibs
and random missing dependencies.

Additionally, while we are here, drastically trim down the public
interface for this class to the bare minimum and allow getting the
output from `display_test_output` as a variable.

Fixes issue mentioned by @ilovezfs in:
https://github.com/Homebrew/brew/pull/3940#issuecomment-383794520
This commit is contained in:
Mike McQuaid 2018-04-24 09:52:51 +01:00
parent 648a7c7b1e
commit 4a03145c1c
3 changed files with 47 additions and 56 deletions

View File

@ -21,7 +21,7 @@ module Homebrew
result = LinkageChecker.new(keg) result = LinkageChecker.new(keg)
if ARGV.include?("--test") if ARGV.include?("--test")
result.display_test_output result.display_test_output
Homebrew.failed = true if result.broken_dylibs? Homebrew.failed = true if result.broken_library_linkage?
elsif ARGV.include?("--reverse") elsif ARGV.include?("--reverse")
result.display_reverse_output result.display_reverse_output
else else

View File

@ -66,10 +66,10 @@ module FormulaCellarChecks
keg = Keg.new(formula.prefix) keg = Keg.new(formula.prefix)
checker = LinkageChecker.new(keg, formula) checker = LinkageChecker.new(keg, formula)
return unless checker.broken_dylibs? return unless checker.broken_library_linkage?
output = <<~EOS output = <<~EOS
#{formula} has broken dynamic library links: #{formula} has broken dynamic library links:
#{checker.broken_dylibs.to_a * "\n "} #{checker.display_test_output}
EOS EOS
tab = Tab.for_keg(keg) tab = Tab.for_keg(keg)
if tab.poured_from_bottle if tab.poured_from_bottle

View File

@ -2,10 +2,6 @@ require "keg"
require "formula" require "formula"
class LinkageChecker class LinkageChecker
attr_reader :keg, :formula
attr_reader :brewed_dylibs, :system_dylibs, :broken_dylibs, :broken_deps, :variable_dylibs
attr_reader :undeclared_deps, :unnecessary_deps, :reverse_links
def initialize(keg, formula = nil) def initialize(keg, formula = nil)
@keg = keg @keg = keg
@formula = formula || resolve_formula(keg) @formula = formula || resolve_formula(keg)
@ -21,6 +17,44 @@ class LinkageChecker
check_dylibs check_dylibs
end end
def display_normal_output
display_items "System libraries", @system_dylibs
display_items "Homebrew libraries", @brewed_dylibs
display_items "Indirect dependencies with linkage", @indirect_deps
display_items "Variable-referenced libraries", @variable_dylibs
display_items "Missing libraries", @broken_dylibs
display_items "Broken dependencies", @broken_deps
display_items "Undeclared dependencies with linkage", @undeclared_deps
display_items "Dependencies with no linkage", @unnecessary_deps
end
def display_reverse_output
return if @reverse_links.empty?
sorted = @reverse_links.sort
sorted.each do |dylib, files|
puts dylib
files.each do |f|
unprefixed = f.to_s.strip_prefix "#{@keg}/"
puts " #{unprefixed}"
end
puts unless dylib == sorted.last[0]
end
end
def display_test_output(puts_output: true)
display_items "Missing libraries", @broken_dylibs, puts_output: puts_output
display_items "Broken dependencies", @broken_deps, puts_output: puts_output
puts "No broken library linkage" unless broken_library_linkage?
end
def broken_library_linkage?
!@broken_dylibs.empty? || !@broken_deps.empty?
end
private
attr_reader :keg, :formula
def dylib_to_dep(dylib) def dylib_to_dep(dylib)
dylib =~ %r{#{Regexp.escape(HOMEBREW_PREFIX)}/(opt|Cellar)/([\w+-.@]+)/} dylib =~ %r{#{Regexp.escape(HOMEBREW_PREFIX)}/(opt|Cellar)/([\w+-.@]+)/}
Regexp.last_match(2) Regexp.last_match(2)
@ -113,51 +147,6 @@ class LinkageChecker
end end
end end
def display_normal_output
display_items "System libraries", @system_dylibs
display_items "Homebrew libraries", @brewed_dylibs
display_items "Indirect dependencies with linkage", @indirect_deps
display_items "Variable-referenced libraries", @variable_dylibs
display_items "Missing libraries", @broken_dylibs
display_items "Broken dependencies", @broken_deps
display_items "Undeclared dependencies with linkage", @undeclared_deps
display_items "Dependencies with no linkage", @unnecessary_deps
end
def display_reverse_output
return if @reverse_links.empty?
sorted = @reverse_links.sort
sorted.each do |dylib, files|
puts dylib
files.each do |f|
unprefixed = f.to_s.strip_prefix "#{@keg}/"
puts " #{unprefixed}"
end
puts unless dylib == sorted.last[0]
end
end
def display_test_output
display_items "Missing libraries", @broken_dylibs
display_items "Broken dependencies", @broken_deps
display_items "Dependencies with no linkage", @unnecessary_deps
puts "No broken dylib links" if @broken_dylibs.empty?
end
def broken_dylibs?
!@broken_dylibs.empty?
end
def undeclared_deps?
!@undeclared_deps.empty?
end
def unnecessary_deps?
!@unnecessary_deps.empty?
end
private
# Whether or not dylib is a harmless broken link, meaning that it's # Whether or not dylib is a harmless broken link, meaning that it's
# okay to skip (and not report) as broken. # okay to skip (and not report) as broken.
def harmless_broken_link?(dylib) def harmless_broken_link?(dylib)
@ -171,20 +160,22 @@ class LinkageChecker
# Display a list of things. # Display a list of things.
# Things may either be an array, or a hash of (label -> array) # Things may either be an array, or a hash of (label -> array)
def display_items(label, things) def display_items(label, things, puts_output: true)
return if things.empty? return if things.empty?
puts "#{label}:" output = "#{label}:"
if things.is_a? Hash if things.is_a? Hash
things.keys.sort.each do |list_label| things.keys.sort.each do |list_label|
things[list_label].sort.each do |item| things[list_label].sort.each do |item|
puts " #{item} (#{list_label})" output += "\n #{item} (#{list_label})"
end end
end end
else else
things.sort.each do |item| things.sort.each do |item|
puts " #{item}" output += "\n #{item}"
end end
end end
puts output if puts_output
output
end end
def resolve_formula(keg) def resolve_formula(keg)