move LinkageChecker to standalone file
This commit is contained in:
parent
28861ac92d
commit
13730a9dad
@ -12,126 +12,21 @@
|
||||
# --reverse - For each dylib the keg references, print the dylib followed by the
|
||||
# binaries which link to it.
|
||||
|
||||
require "set"
|
||||
require "keg"
|
||||
require "formula"
|
||||
require "os/mac/linkage_checker"
|
||||
|
||||
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
|
||||
Homebrew.failed = true if result.broken_dylibs?
|
||||
elsif ARGV.include?("--reverse")
|
||||
result.display_reverse_output
|
||||
else
|
||||
result.display_normal_output
|
||||
end
|
||||
found_broken_dylibs = true unless result.broken_dylibs.empty?
|
||||
end
|
||||
if ARGV.include?("--test") && found_broken_dylibs
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
class LinkageChecker
|
||||
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
|
||||
@reverse_links = Hash.new { |h, k| h[k] = Set.new }
|
||||
check_dylibs
|
||||
end
|
||||
|
||||
def check_dylibs
|
||||
@keg.find do |file|
|
||||
next if file.symlink? || file.directory?
|
||||
next unless file.dylib? || file.mach_o_executable? || file.mach_o_bundle?
|
||||
file.dynamically_linked_libraries.each do |dylib|
|
||||
@reverse_links[dylib] << file
|
||||
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 = Formulary.from_rack(keg.rack)
|
||||
f.build = Tab.for_keg(keg)
|
||||
filter_out = proc do |dep|
|
||||
dep.build? || (dep.optional? && !dep.option_names.any? { |n| f.build.with?(n) })
|
||||
end
|
||||
declared_deps = f.deps.reject { |dep| filter_out.call(dep) }.map(&:name) +
|
||||
f.requirements.reject { |req| filter_out.call(req) }.map(&:default_formula).compact
|
||||
@undeclared_deps = @brewed_dylibs.keys - declared_deps.map { |dep| dep.split("/").last }
|
||||
@undeclared_deps -= [f.name]
|
||||
rescue FormulaUnavailableError
|
||||
opoo "Formula unavailable: #{keg.name}"
|
||||
@undeclared_deps = []
|
||||
end
|
||||
end
|
||||
|
||||
def display_normal_output
|
||||
display_items "System libraries", @system_dylibs
|
||||
display_items "Homebrew libraries", @brewed_dylibs
|
||||
display_items "Variable-referenced libraries", @variable_dylibs
|
||||
display_items "Missing libraries", @broken_dylibs
|
||||
display_items "Possible undeclared dependencies", @undeclared_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.to_s}/"
|
||||
puts " #{unprefixed}"
|
||||
end
|
||||
puts unless dylib == sorted.last[0]
|
||||
end
|
||||
end
|
||||
|
||||
def display_test_output
|
||||
display_items "Missing libraries", @broken_dylibs
|
||||
puts "No broken dylib links" if @broken_dylibs.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Display a list of things.
|
||||
# Things may either be an array, or a hash of (label -> array)
|
||||
def display_items(label, things)
|
||||
return if things.empty?
|
||||
puts "#{label}:"
|
||||
if things.is_a? Hash
|
||||
things.sort.each do |list_label, list|
|
||||
list.sort.each do |item|
|
||||
puts " #{item} (#{list_label})"
|
||||
end
|
||||
end
|
||||
else
|
||||
things.sort.each do |item|
|
||||
puts " #{item}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
113
Library/Homebrew/os/mac/linkage_checker.rb
Normal file
113
Library/Homebrew/os/mac/linkage_checker.rb
Normal file
@ -0,0 +1,113 @@
|
||||
require "set"
|
||||
require "keg"
|
||||
require "formula"
|
||||
|
||||
class LinkageChecker
|
||||
attr_reader :keg
|
||||
attr_reader :brewed_dylibs, :system_dylibs, :broken_dylibs, :variable_dylibs
|
||||
attr_reader :undeclared_deps, :reverse_links
|
||||
|
||||
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
|
||||
@undeclared_deps = []
|
||||
@reverse_links = Hash.new { |h, k| h[k] = Set.new }
|
||||
check_dylibs
|
||||
end
|
||||
|
||||
def check_dylibs
|
||||
@keg.find do |file|
|
||||
next if file.symlink? || file.directory?
|
||||
next unless file.dylib? || file.mach_o_executable? || file.mach_o_bundle?
|
||||
file.dynamically_linked_libraries.each do |dylib|
|
||||
@reverse_links[dylib] << file
|
||||
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 = Formulary.from_rack(keg.rack)
|
||||
f.build = Tab.for_keg(keg)
|
||||
filter_out = proc do |dep|
|
||||
dep.build? || (dep.optional? && !dep.option_names.any? { |n| f.build.with?(n) })
|
||||
end
|
||||
declared_deps = f.deps.reject { |dep| filter_out.call(dep) }.map(&:name) +
|
||||
f.requirements.reject { |req| filter_out.call(req) }.map(&:default_formula).compact
|
||||
@undeclared_deps = @brewed_dylibs.keys - declared_deps.map { |dep| dep.split("/").last }
|
||||
@undeclared_deps -= [f.name]
|
||||
rescue FormulaUnavailableError
|
||||
opoo "Formula unavailable: #{keg.name}"
|
||||
end
|
||||
end
|
||||
|
||||
def display_normal_output
|
||||
display_items "System libraries", @system_dylibs
|
||||
display_items "Homebrew libraries", @brewed_dylibs
|
||||
display_items "Variable-referenced libraries", @variable_dylibs
|
||||
display_items "Missing libraries", @broken_dylibs
|
||||
display_items "Possible undeclared dependencies", @undeclared_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
|
||||
puts "No broken dylib links" if @broken_dylibs.empty?
|
||||
display_items "Possible undeclared dependencies", @undeclared_deps
|
||||
puts "No undeclared dependencies" if @undeclared_deps.empty?
|
||||
end
|
||||
|
||||
def broken_dylibs?
|
||||
!@broken_dylibs.empty?
|
||||
end
|
||||
|
||||
def undeclared_deps?
|
||||
!@undeclared_deps.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Display a list of things.
|
||||
# Things may either be an array, or a hash of (label -> array)
|
||||
def display_items(label, things)
|
||||
return if things.empty?
|
||||
puts "#{label}:"
|
||||
if things.is_a? Hash
|
||||
things.sort.each do |list_label, list|
|
||||
list.sort.each do |item|
|
||||
puts " #{item} (#{list_label})"
|
||||
end
|
||||
end
|
||||
else
|
||||
things.sort.each do |item|
|
||||
puts " #{item}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user