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
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "os/linux/ld"
|
||||||
require "utils/output"
|
require "utils/output"
|
||||||
|
|
||||||
module OS
|
module OS
|
||||||
module Linux
|
module Linux
|
||||||
module Install
|
module Install
|
||||||
module ClassMethods
|
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,
|
# 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
|
# 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
|
# 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"
|
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
|
||||||
unless ld_so.readable?
|
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?
|
if ld_so.blank?
|
||||||
::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?
|
::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?
|
||||||
|
|
||||||
|
@ -5,37 +5,69 @@ module OS
|
|||||||
module Linux
|
module Linux
|
||||||
# Helper functions for querying `ld` information.
|
# Helper functions for querying `ld` information.
|
||||||
module Ld
|
module Ld
|
||||||
sig { returns(String) }
|
# This is a list of known paths to the host dynamic linker on Linux if
|
||||||
def self.brewed_ld_so_diagnostics
|
# the host glibc is new enough. Brew will fail to create a symlink for
|
||||||
@brewed_ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
|
# 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"
|
# The path to the system's dynamic linker or `nil` if not found
|
||||||
return "" unless brewed_ld_so.exist?
|
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
|
sig { params(brewed: T::Boolean).returns(String) }
|
||||||
@brewed_ld_so_diagnostics[brewed_ld_so_target] ||= begin
|
def self.ld_so_diagnostics(brewed: true)
|
||||||
ld_so_output = Utils.popen_read(brewed_ld_so, "--list-diagnostics")
|
@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?
|
ld_so_output if $CHILD_STATUS.success?
|
||||||
end
|
end
|
||||||
|
|
||||||
@brewed_ld_so_diagnostics[brewed_ld_so_target].to_s
|
@ld_so_diagnostics[ld_so_target].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(String) }
|
sig { params(brewed: T::Boolean).returns(String) }
|
||||||
def self.sysconfdir
|
def self.sysconfdir(brewed: true)
|
||||||
fallback_sysconfdir = "/etc"
|
fallback_sysconfdir = "/etc"
|
||||||
|
|
||||||
match = brewed_ld_so_diagnostics.match(/path.sysconfdir="(.+)"/)
|
match = ld_so_diagnostics(brewed:).match(/path.sysconfdir="(.+)"/)
|
||||||
return fallback_sysconfdir unless match
|
return fallback_sysconfdir unless match
|
||||||
|
|
||||||
match.captures.compact.first || fallback_sysconfdir
|
match.captures.compact.first || fallback_sysconfdir
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(T::Array[String]) }
|
sig { params(brewed: T::Boolean).returns(T::Array[String]) }
|
||||||
def self.system_dirs
|
def self.system_dirs(brewed: true)
|
||||||
dirs = []
|
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.*\]="(.*)"/)
|
match = line.match(/path.system_dirs\[0x.*\]="(.*)"/)
|
||||||
next unless match
|
next unless match
|
||||||
|
|
||||||
@ -45,9 +77,9 @@ module OS
|
|||||||
dirs
|
dirs
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(conf_path: T.any(Pathname, String)).returns(T::Array[String]) }
|
sig { params(conf_path: T.any(Pathname, String), brewed: T::Boolean).returns(T::Array[String]) }
|
||||||
def self.library_paths(conf_path = Pathname(sysconfdir)/"ld.so.conf")
|
def self.library_paths(conf_path = "ld.so.conf", brewed: true)
|
||||||
conf_file = Pathname(conf_path)
|
conf_file = Pathname(sysconfdir(brewed:))/conf_path
|
||||||
return [] unless conf_file.exist?
|
return [] unless conf_file.exist?
|
||||||
return [] unless conf_file.file?
|
return [] unless conf_file.file?
|
||||||
return [] unless conf_file.readable?
|
return [] unless conf_file.readable?
|
||||||
@ -68,8 +100,7 @@ module OS
|
|||||||
line.sub!(/\s*#.*$/, "")
|
line.sub!(/\s*#.*$/, "")
|
||||||
|
|
||||||
if line.start_with?(/\s*include\s+/)
|
if line.start_with?(/\s*include\s+/)
|
||||||
include_path = Pathname(line.sub(/^\s*include\s+/, "")).expand_path
|
wildcard = Pathname(line.sub(/^\s*include\s+/, "")).expand_path(directory)
|
||||||
wildcard = include_path.absolute? ? include_path : directory/include_path
|
|
||||||
|
|
||||||
Dir.glob(wildcard.to_s).each do |include_file|
|
Dir.glob(wildcard.to_s).each do |include_file|
|
||||||
paths += library_paths(include_file)
|
paths += library_paths(include_file)
|
||||||
|
@ -4,6 +4,61 @@ require "os/linux/ld"
|
|||||||
require "tmpdir"
|
require "tmpdir"
|
||||||
|
|
||||||
RSpec.describe OS::Linux::Ld do
|
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
|
describe "::library_paths" do
|
||||||
ld_etc = Pathname("")
|
ld_etc = Pathname("")
|
||||||
before do
|
before do
|
||||||
|
Loading…
x
Reference in New Issue
Block a user