aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/os/mac
diff options
context:
space:
mode:
authorXu Cheng2016-07-07 20:41:14 +0800
committerXu Cheng2016-07-14 15:23:33 +0800
commit13730a9dadde8570e41cf5599b4f5c940014f190 (patch)
tree0a8b35c23598f34c8002305e26375c8b1b42c0df /Library/Homebrew/os/mac
parent28861ac92d910abbe014a05c4418ee077371d9ca (diff)
downloadbrew-13730a9dadde8570e41cf5599b4f5c940014f190.tar.bz2
move LinkageChecker to standalone file
Diffstat (limited to 'Library/Homebrew/os/mac')
-rw-r--r--Library/Homebrew/os/mac/linkage_checker.rb113
1 files changed, 113 insertions, 0 deletions
diff --git a/Library/Homebrew/os/mac/linkage_checker.rb b/Library/Homebrew/os/mac/linkage_checker.rb
new file mode 100644
index 000000000..c33b296c7
--- /dev/null
+++ b/Library/Homebrew/os/mac/linkage_checker.rb
@@ -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