-
-
Notifications
You must be signed in to change notification settings - Fork 11.3k
test-bot: check all dependents for broken dylibs #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally yes. But most other |
||
| 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 | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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.rbas well, i.e. checking linkage of the formula itself other than the dependents.There was a problem hiding this comment.
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 missingthere. Mind if I put it off to a later PR, though?There was a problem hiding this comment.
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.