diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index 28abd8b0a0..7b6cb276c1 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -451,6 +451,11 @@ class Pathname def dylib? false end + + sig { returns(T::Array[String]) } + def rpaths + [] + end end require "extend/os/pathname" diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index 116abd76b1..db753fc9b0 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -32,6 +32,7 @@ class LinkageChecker @unnecessary_deps = [] @unwanted_system_dylibs = [] @version_conflict_deps = [] + @files_missing_rpaths = [] check_dylibs(rebuild_cache: rebuild_cache) end @@ -46,6 +47,7 @@ class LinkageChecker display_items "Undeclared dependencies with linkage", @undeclared_deps display_items "Dependencies with no linkage", @unnecessary_deps display_items "Unwanted system libraries", @unwanted_system_dylibs + display_items "Files with missing rpath", @files_missing_rpaths end def display_reverse_output @@ -69,11 +71,12 @@ class LinkageChecker display_items "Unwanted system libraries", @unwanted_system_dylibs, puts_output: puts_output display_items "Conflicting libraries", @version_conflict_deps, puts_output: puts_output display_items "Undeclared dependencies with linkage", @undeclared_deps, puts_output: puts_output if strict + display_items "Files with missing rpath", @files_missing_rpaths, puts_output: puts_output end sig { params(strict: T::Boolean).returns(T::Boolean) } def broken_library_linkage?(strict: false) - issues = [@broken_deps, @unwanted_system_dylibs, @version_conflict_deps] + issues = [@broken_deps, @unwanted_system_dylibs, @version_conflict_deps, @files_missing_rpaths] issues << @undeclared_deps if strict [issues, unexpected_broken_dylibs, unexpected_present_dylibs].flatten.any?(&:present?) end @@ -152,9 +155,18 @@ class LinkageChecker checked_dylibs = Set.new keg_files_dylibs.each do |file, dylibs| + file_has_any_rpath_dylibs = T.let(false, T::Boolean) dylibs.each do |dylib| @reverse_links[dylib] << file + # Files that link @rpath-prefixed dylibs must include at + # least one rpath in order to resolve it. + if !file_has_any_rpath_dylibs && (dylib.start_with? "@rpath/") + file_has_any_rpath_dylibs = true + pathname = Pathname(file) + @files_missing_rpaths << file if pathname.rpaths.empty? + end + next if checked_dylibs.include? dylib checked_dylibs << dylib diff --git a/Library/Homebrew/os/linux/elf.rb b/Library/Homebrew/os/linux/elf.rb index f151d23b57..73dd1b016e 100644 --- a/Library/Homebrew/os/linux/elf.rb +++ b/Library/Homebrew/os/linux/elf.rb @@ -86,12 +86,20 @@ module ELFShim elf_type == :executable end + # The runtime search path, such as: + # "/lib:/usr/lib:/usr/local/lib" def rpath return @rpath if defined? @rpath @rpath = rpath_using_patchelf_rb end + # An array of runtime search path entries, such as: + # ["/lib", "/usr/lib", "/usr/local/lib"] + def rpaths + rpath.split(":") + end + def interpreter return @interpreter if defined? @interpreter