Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Library/Homebrew/cmd/test-bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,8 @@ def formula(formula_name)
dependents -= @formulae
dependents = dependents.map { |d| Formulary.factory(d) }

testable_dependents = dependents.select { |d| d.test_defined? && d.bottled? }
bottled_dependents = dependents.select { |d| d.bottled? }
testable_dependents = dependents.select { |d| d.bottled? && d.test_defined? }

if (deps | reqs).any? { |d| d.name == "mercurial" && d.build? }
run_as_not_developer { test "brew", "install", "mercurial" }
Expand Down Expand Up @@ -592,7 +593,7 @@ def formula(formula_name)
shared_test_args = ["--verbose"]
shared_test_args << "--keep-tmp" if ARGV.keep_tmp?
test "brew", "test", formula_name, *shared_test_args if formula.test_defined?
testable_dependents.each do |dependent|
bottled_dependents.each do |dependent|
unless dependent.installed?
test "brew", "fetch", "--retry", dependent.name
next if steps.last.failed?
Expand All @@ -606,7 +607,10 @@ def formula(formula_name)
end
end
if dependent.installed?
test "brew", "test", "--verbose", dependent.name
test "brew", "linkage", "--test", dependent.name
if testable_dependents.include? dependent
test "brew", "test", "--verbose", dependent.name
end
end
end
test "brew", "uninstall", "--force", formula_name
Expand Down
125 changes: 125 additions & 0 deletions Library/Homebrew/dev-cmd/linkage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#
# Description: check linkage of installed keg
# Usage:
# brew linkage <formulae>
#
# Only works on installed formulae. An error is raised if it is run on uninstalled
# formulae.
#
# Options:
# --test - testing version: only display broken libs; exit non-zero if any
# breakage was found.

require "set"
require "keg"
require "formula"

module Homebrew

def linkage
found_broken_dylibs = false
ARGV.kegs.each do |keg|
ohai "Checking #{keg.name} linkage" if ARGV.kegs.size > 1
result = LinkageChecker.new(keg)
if ARGV.include?("--test")
result.display_test_output
else
result.display_normal_output
end
found_broken_dylibs = true if !result.broken_dylibs.empty?
end
if ARGV.include?("--test") && found_broken_dylibs
exit 1
end
end

class LinkageChecker

@xu-cheng xu-cheng Apr 18, 2016

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth to add this checker to formula_cellar_checks.rb as well, i.e. checking linkage of the formula itself other than the dependents.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, similar to how we do a check like brew missing there. Mind if I put it off to a later PR, though?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem. But you may want to put this class in a standalone file if it will be used in other part of core in the future.

attr_reader :keg
attr_reader :broken_dylibs

def initialize(keg)
@keg = keg
@brewed_dylibs = Hash.new { |h, k| h[k] = Set.new }
@system_dylibs = Set.new
@broken_dylibs = Set.new
@variable_dylibs = Set.new
check_dylibs
end

def check_dylibs
@keg.find do |file|
next unless file.dylib? || file.mach_o_executable? || file.mach_o_bundle?
file.dynamically_linked_libraries.each do |dylib|
if dylib.start_with? "@"
@variable_dylibs << dylib
else
begin
owner = Keg.for Pathname.new(dylib)
rescue NotAKegError
@system_dylibs << dylib
rescue Errno::ENOENT
@broken_dylibs << dylib
else
@brewed_dylibs[owner.name] << dylib
end
end
end
end

begin
f = Formula[keg.name]
@undeclared_deps = @brewed_dylibs.keys - f.deps.map(&:name)
@undeclared_deps -= [f.name]
rescue FormulaUnavailableError
opoo "Formula unavailable: #{keg.name}"
@undeclared_deps = []
end

end

def display_normal_output
unless @system_dylibs.empty?
display_items "System libraries", @system_dylibs
end
unless @brewed_dylibs.empty?
display_items "Homebrew libraries", @brewed_dylibs
end
unless @variable_dylibs.empty?
display_items "Variable-referenced libraries", @variable_dylibs
end
unless @broken_dylibs.empty?
display_items "Missing libraries", @broken_dylibs
end
unless @undeclared_deps.empty?
display_items "Possible undeclared dependencies", @undeclared_deps
end
end

def display_test_output
if @broken_dylibs.empty?
puts "No broken dylib links"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This information is already encoded in the exit status and it seems to be customary for most CLI programs to output nothing in this case. I think it is sufficient to output only when there is something to report on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally yes. But most other brew subcommands, including diagnostic ones like brew test, brew style, and brew doctor, include some output even in the normal "no problems" case. Did this to be consistent.

else
display_items "Missing libraries", @broken_dylibs
end
end

private

# Display a list of things.
# Things may either be an array, or a hash of (label -> array)
def display_items(label, things)
puts "#{label}:"
if things.is_a? Hash
things.sort.each do |label, list|
list.sort.each do |item|
puts " #{item} (#{label})"
end
end
else
things.sort.each do |item|
puts " #{item}"
end
end
end
end
end