1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#
# 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 libraries and possibly
# undeclared dependencies; exit non-zero if any breakage was
# found (i.e. any of these yield a nonempty result).
# --reverse - For each dylib the keg references, print the dylib followed by the
# binaries which link to it.
require "set"
require "keg"
require "formula"
module Homebrew
def linkage
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
if result.broken_dylibs? || result.undeclared_deps?
Homebrew.failed = true
end
elsif ARGV.include?("--reverse")
result.display_reverse_output
else
result.display_normal_output
end
end
end
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.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?
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
end
|