os/linux/ld: add support for using system ld.so
This commit is contained in:
parent
6e9da0ba4c
commit
896edb4451
@ -1,26 +1,13 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/ld"
|
||||
require "utils/output"
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module Install
|
||||
module ClassMethods
|
||||
# This is a list of known paths to the host dynamic linker on Linux if
|
||||
# the host glibc is new enough. The symlink_ld_so method will fail 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
|
||||
|
||||
# We link GCC runtime libraries that are not specifically used for Fortran,
|
||||
# which are linked by the GCC formula. We only use the versioned shared libraries
|
||||
# as the other shared and static libraries are only used at build time where
|
||||
@ -67,7 +54,7 @@ module OS
|
||||
|
||||
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
|
||||
unless ld_so.readable?
|
||||
ld_so = DYNAMIC_LINKERS.find { |s| File.executable? s }
|
||||
ld_so = OS::Linux::Ld.system_ld_so
|
||||
if ld_so.blank?
|
||||
::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?
|
||||
|
||||
|
@ -5,37 +5,69 @@ module OS
|
||||
module Linux
|
||||
# Helper functions for querying `ld` information.
|
||||
module Ld
|
||||
sig { returns(String) }
|
||||
def self.brewed_ld_so_diagnostics
|
||||
@brewed_ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
|
||||
# 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
|
||||
|
||||
brewed_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
|
||||
return "" unless brewed_ld_so.exist?
|
||||
# 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
|
||||
|
||||
brewed_ld_so_target = brewed_ld_so.readlink
|
||||
@brewed_ld_so_diagnostics[brewed_ld_so_target] ||= begin
|
||||
ld_so_output = Utils.popen_read(brewed_ld_so, "--list-diagnostics")
|
||||
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
|
||||
|
||||
@brewed_ld_so_diagnostics[brewed_ld_so_target].to_s
|
||||
@ld_so_diagnostics[ld_so_target].to_s
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def self.sysconfdir
|
||||
sig { params(brewed: T::Boolean).returns(String) }
|
||||
def self.sysconfdir(brewed: true)
|
||||
fallback_sysconfdir = "/etc"
|
||||
|
||||
match = brewed_ld_so_diagnostics.match(/path.sysconfdir="(.+)"/)
|
||||
match = ld_so_diagnostics(brewed:).match(/path.sysconfdir="(.+)"/)
|
||||
return fallback_sysconfdir unless match
|
||||
|
||||
match.captures.compact.first || fallback_sysconfdir
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def self.system_dirs
|
||||
sig { params(brewed: T::Boolean).returns(T::Array[String]) }
|
||||
def self.system_dirs(brewed: true)
|
||||
dirs = []
|
||||
|
||||
brewed_ld_so_diagnostics.split("\n").each do |line|
|
||||
ld_so_diagnostics(brewed:).split("\n").each do |line|
|
||||
match = line.match(/path.system_dirs\[0x.*\]="(.*)"/)
|
||||
next unless match
|
||||
|
||||
@ -45,9 +77,9 @@ module OS
|
||||
dirs
|
||||
end
|
||||
|
||||
sig { params(conf_path: T.any(Pathname, String)).returns(T::Array[String]) }
|
||||
def self.library_paths(conf_path = Pathname(sysconfdir)/"ld.so.conf")
|
||||
conf_file = Pathname(conf_path)
|
||||
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?
|
||||
@ -68,8 +100,7 @@ module OS
|
||||
line.sub!(/\s*#.*$/, "")
|
||||
|
||||
if line.start_with?(/\s*include\s+/)
|
||||
include_path = Pathname(line.sub(/^\s*include\s+/, "")).expand_path
|
||||
wildcard = include_path.absolute? ? include_path : directory/include_path
|
||||
wildcard = Pathname(line.sub(/^\s*include\s+/, "")).expand_path(directory)
|
||||
|
||||
Dir.glob(wildcard.to_s).each do |include_file|
|
||||
paths += library_paths(include_file)
|
||||
|
@ -4,6 +4,61 @@ require "os/linux/ld"
|
||||
require "tmpdir"
|
||||
|
||||
RSpec.describe OS::Linux::Ld do
|
||||
let(:diagnostics) do
|
||||
<<~EOS
|
||||
path.prefix="/usr"
|
||||
path.sysconfdir="/usr/local/etc"
|
||||
path.system_dirs[0x0]="/lib64"
|
||||
path.system_dirs[0x1]="/var/lib"
|
||||
EOS
|
||||
end
|
||||
|
||||
describe "::system_ld_so" do
|
||||
let(:ld_so) { "/lib/ld-linux.so.3" }
|
||||
|
||||
before do
|
||||
allow(File).to receive(:executable?).and_return(false)
|
||||
described_class.instance_variable_set(:@system_ld_so, nil)
|
||||
end
|
||||
|
||||
it "returns the path to a known dynamic linker" do
|
||||
allow(File).to receive(:executable?).with(ld_so).and_return(true)
|
||||
expect(described_class.system_ld_so).to eq(Pathname(ld_so))
|
||||
end
|
||||
|
||||
it "returns nil when there is no known dynamic linker" do
|
||||
expect(described_class.system_ld_so).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "::sysconfdir" do
|
||||
it "returns path.sysconfdir" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return(diagnostics)
|
||||
expect(described_class.sysconfdir).to eq("/usr/local/etc")
|
||||
expect(described_class.sysconfdir(brewed: false)).to eq("/usr/local/etc")
|
||||
end
|
||||
|
||||
it "returns fallback on blank diagnostics" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return("")
|
||||
expect(described_class.sysconfdir).to eq("/etc")
|
||||
expect(described_class.sysconfdir(brewed: false)).to eq("/etc")
|
||||
end
|
||||
end
|
||||
|
||||
describe "::system_dirs" do
|
||||
it "returns all path.system_dirs" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return(diagnostics)
|
||||
expect(described_class.system_dirs).to eq(["/lib64", "/var/lib"])
|
||||
expect(described_class.system_dirs(brewed: false)).to eq(["/lib64", "/var/lib"])
|
||||
end
|
||||
|
||||
it "returns an empty array on blank diagnostics" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return("")
|
||||
expect(described_class.system_dirs).to eq([])
|
||||
expect(described_class.system_dirs(brewed: false)).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "::library_paths" do
|
||||
ld_etc = Pathname("")
|
||||
before do
|
||||
|
Loading…
x
Reference in New Issue
Block a user