Class: LinkageChecker

Inherits:
Object
  • Object
show all
Defined in:
brew/Library/Homebrew/linkage_checker.rb,
brew/Library/Homebrew/extend/os/linux/linkage_checker.rb

Constant Summary collapse

SYSTEM_LIBRARY_WHITELIST =

Libraries provided by glibc and gcc.

%w[
  ld-linux-x86-64.so.2
  libanl.so.1
  libc.so.6
  libcrypt.so.1
  libdl.so.2
  libm.so.6
  libmvec.so.1
  libnsl.so.1
  libpthread.so.0
  libresolv.so.2
  librt.so.1
  libutil.so.1

  libgcc_s.so.1
  libgomp.so.1
  libstdc++.so.6
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(keg, formula = nil, cache_db:, rebuild_cache: false) ⇒ LinkageChecker

Returns a new instance of LinkageChecker



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'brew/Library/Homebrew/linkage_checker.rb', line 10

def initialize(keg, formula = nil, cache_db:, rebuild_cache: false)
  @keg = keg
  @formula = formula || resolve_formula(keg)
  @store = LinkageCacheStore.new(keg.to_s, cache_db)

  @system_dylibs    = Set.new
  @broken_dylibs    = Set.new
  @variable_dylibs  = Set.new
  @brewed_dylibs    = Hash.new { |h, k| h[k] = Set.new }
  @reverse_links    = Hash.new { |h, k| h[k] = Set.new }
  @broken_deps      = Hash.new { |h, k| h[k] = [] }
  @indirect_deps    = []
  @undeclared_deps  = []
  @unnecessary_deps = []
  @unwanted_system_dylibs = []
  @version_conflict_deps = []

  check_dylibs(rebuild_cache: rebuild_cache)
end

Instance Attribute Details

#undeclared_depsObject (readonly)

Returns the value of attribute undeclared_deps



8
9
10
# File 'brew/Library/Homebrew/linkage_checker.rb', line 8

def undeclared_deps
  @undeclared_deps
end

Instance Method Details

#broken_library_linkage?Boolean

Returns:

  • (Boolean)


64
65
66
67
68
69
# File 'brew/Library/Homebrew/linkage_checker.rb', line 64

def broken_library_linkage?
  !@broken_dylibs.empty? ||
    !@broken_deps.empty? ||
    !@unwanted_system_dylibs.empty? ||
    !@version_conflict_deps.empty?
end

#check_dylibs(rebuild_cache:) ⇒ Object



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
148
149
150
151
# File 'brew/Library/Homebrew/linkage_checker.rb', line 80

def check_dylibs(rebuild_cache:)
  keg_files_dylibs = nil

  if rebuild_cache
    store&.delete!
  else
    keg_files_dylibs = store&.fetch(:keg_files_dylibs)
  end

  keg_files_dylibs_was_empty = false
  keg_files_dylibs ||= {}
  if keg_files_dylibs.empty?
    keg_files_dylibs_was_empty = true
    @keg.find do |file|
      next if file.symlink? || file.directory?
      next if !file.dylib? && !file.binary_executable? && !file.mach_o_bundle?

      # weakly loaded dylibs may not actually exist on disk, so skip them
      # when checking for broken linkage
      keg_files_dylibs[file] =
        file.dynamically_linked_libraries(except: :LC_LOAD_WEAK_DYLIB)
    end
  end

  checked_dylibs = Set.new

  keg_files_dylibs.each do |file, dylibs|
    dylibs.each do |dylib|
      @reverse_links[dylib] << file

      next if checked_dylibs.include? dylib

      checked_dylibs << dylib

      if dylib.start_with? "@"
        @variable_dylibs << dylib
        next
      end

      begin
        owner = Keg.for Pathname.new(dylib)
      rescue NotAKegError
        @system_dylibs << dylib
      rescue Errno::ENOENT
        next if harmless_broken_link?(dylib)

        if (dep = dylib_to_dep(dylib))
          @broken_deps[dep] |= [dylib]
        else
          @broken_dylibs << dylib
        end
      else
        tap = Tab.for_keg(owner).tap
        f = if tap.nil? || tap.core_tap?
          owner.name
        else
          "#{tap}/#{owner.name}"
        end
        @brewed_dylibs[f] << dylib
      end
    end
  end

  if formula
    @indirect_deps, @undeclared_deps, @unnecessary_deps,
      @version_conflict_deps = check_formula_deps
  end

  return unless keg_files_dylibs_was_empty

  store&.update!(keg_files_dylibs: keg_files_dylibs)
end

#display_normal_outputObject



30
31
32
33
34
35
36
37
38
39
40
# File 'brew/Library/Homebrew/linkage_checker.rb', line 30

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
  display_items "Unwanted system libraries", @unwanted_system_dylibs
end

#display_reverse_outputObject



42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'brew/Library/Homebrew/linkage_checker.rb', line 42

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.delete_prefix "#{keg}/"
      puts "  #{unprefixed}"
    end
    puts if dylib != sorted.last.first
  end
end

#display_test_output(puts_output: true) ⇒ Object



56
57
58
59
60
61
62
# File 'brew/Library/Homebrew/linkage_checker.rb', line 56

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
  display_items "Unwanted system libraries", @unwanted_system_dylibs, puts_output: puts_output
  display_items "Conflicting libraries", @version_conflict_deps, puts_output: puts_output
  puts "No broken library linkage" unless broken_library_linkage?
end