121 lines
3.7 KiB
Ruby
121 lines
3.7 KiB
Ruby
# typed: strict
|
|
# frozen_string_literal: true
|
|
|
|
module OS
|
|
module Linux
|
|
# Helper functions for querying `ld` information.
|
|
module Ld
|
|
# This is a list of known paths to the host dynamic linker on Linux if
|
|
# the host glibc is new enough. Brew will fail to create a symlink for
|
|
# ld.so if the host linker cannot be found in this list.
|
|
DYNAMIC_LINKERS = %w[
|
|
/lib64/ld-linux-x86-64.so.2
|
|
/lib64/ld64.so.2
|
|
/lib/ld-linux.so.3
|
|
/lib/ld-linux.so.2
|
|
/lib/ld-linux-aarch64.so.1
|
|
/lib/ld-linux-armhf.so.3
|
|
/system/bin/linker64
|
|
/system/bin/linker
|
|
].freeze
|
|
|
|
# The path to the system's dynamic linker or `nil` if not found
|
|
sig { returns(T.nilable(Pathname)) }
|
|
def self.system_ld_so
|
|
@system_ld_so ||= T.let(nil, T.nilable(Pathname))
|
|
@system_ld_so ||= begin
|
|
linker = DYNAMIC_LINKERS.find { |s| File.executable? s }
|
|
Pathname(linker) if linker
|
|
end
|
|
end
|
|
|
|
sig { params(brewed: T::Boolean).returns(String) }
|
|
def self.ld_so_diagnostics(brewed: true)
|
|
@ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
|
|
|
|
ld_so_target = if brewed
|
|
ld_so = HOMEBREW_PREFIX/"lib/ld.so"
|
|
return "" unless ld_so.exist?
|
|
|
|
ld_so.readlink
|
|
else
|
|
ld_so = system_ld_so
|
|
return "" unless ld_so&.exist?
|
|
|
|
ld_so
|
|
end
|
|
|
|
@ld_so_diagnostics[ld_so_target] ||= begin
|
|
ld_so_output = Utils.popen_read(ld_so, "--list-diagnostics")
|
|
ld_so_output if $CHILD_STATUS.success?
|
|
end
|
|
|
|
@ld_so_diagnostics[ld_so_target].to_s
|
|
end
|
|
|
|
sig { params(brewed: T::Boolean).returns(String) }
|
|
def self.sysconfdir(brewed: true)
|
|
fallback_sysconfdir = "/etc"
|
|
|
|
match = ld_so_diagnostics(brewed:).match(/path.sysconfdir="(.+)"/)
|
|
return fallback_sysconfdir unless match
|
|
|
|
match.captures.compact.first || fallback_sysconfdir
|
|
end
|
|
|
|
sig { params(brewed: T::Boolean).returns(T::Array[String]) }
|
|
def self.system_dirs(brewed: true)
|
|
dirs = []
|
|
|
|
ld_so_diagnostics(brewed:).split("\n").each do |line|
|
|
match = line.match(/path.system_dirs\[0x.*\]="(.*)"/)
|
|
next unless match
|
|
|
|
dirs << match.captures.compact.first
|
|
end
|
|
|
|
dirs
|
|
end
|
|
|
|
sig { params(conf_path: T.any(Pathname, String), brewed: T::Boolean).returns(T::Array[String]) }
|
|
def self.library_paths(conf_path = "ld.so.conf", brewed: true)
|
|
conf_file = Pathname(sysconfdir(brewed:))/conf_path
|
|
return [] unless conf_file.exist?
|
|
return [] unless conf_file.file?
|
|
return [] unless conf_file.readable?
|
|
|
|
@library_paths_cache ||= T.let({}, T.nilable(T::Hash[String, T::Array[String]]))
|
|
cache_key = conf_file.to_s
|
|
if (cached_library_path_contents = @library_paths_cache[cache_key])
|
|
return cached_library_path_contents
|
|
end
|
|
|
|
paths = Set.new
|
|
directory = conf_file.realpath.dirname
|
|
|
|
conf_file.open("r") do |file|
|
|
file.each_line do |line|
|
|
# Remove comments and leading/trailing whitespace
|
|
line.strip!
|
|
line.sub!(/\s*#.*$/, "")
|
|
|
|
if line.start_with?(/\s*include\s+/)
|
|
wildcard = Pathname(line.sub(/^\s*include\s+/, "")).expand_path(directory)
|
|
|
|
Dir.glob(wildcard.to_s).each do |include_file|
|
|
paths += library_paths(include_file)
|
|
end
|
|
elsif line.empty?
|
|
next
|
|
else
|
|
paths << line
|
|
end
|
|
end
|
|
end
|
|
|
|
@library_paths_cache[cache_key] = paths.to_a
|
|
end
|
|
end
|
|
end
|
|
end
|