diff --git a/Library/Homebrew/os/linux/ld.rb b/Library/Homebrew/os/linux/ld.rb new file mode 100644 index 0000000000..1ff2bd05f8 --- /dev/null +++ b/Library/Homebrew/os/linux/ld.rb @@ -0,0 +1,74 @@ +# typed: strict +# frozen_string_literal: true + +module OS + module Linux + # Helper functions for querying `ld` information. + # + # @api private + module Ld + sig { returns(String) } + def self.brewed_ld_so_diagnostics + brewed_ld_so = HOMEBREW_PREFIX/"lib/ld.so" + return "" unless brewed_ld_so.exist? + + ld_so_output = Utils.popen_read(brewed_ld_so, "--list-diagnostics") + return "" unless $CHILD_STATUS.success? + + ld_so_output + end + + sig { returns(String) } + def self.sysconfdir + fallback_sysconfdir = "/etc" + + match = brewed_ld_so_diagnostics.match(/path.sysconfdir="(.+)"/) + return fallback_sysconfdir unless match + + match.captures.compact.first || fallback_sysconfdir + end + + sig { returns(T::Array[String]) } + def self.system_dirs + dirs = [] + + brewed_ld_so_diagnostics.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)).returns(T::Array[String]) } + def self.library_paths(conf_path = Pathname(sysconfdir)/"ld.so.conf") + conf_file = Pathname(conf_path) + paths = Set.new + directory = conf_file.realpath.dirname + + conf_file.readlines.each do |line| + # Remove comments and leading/trailing whitespace + line.strip! + 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 + + Dir.glob(wildcard.to_s).each do |include_file| + paths += library_paths(include_file) + end + elsif line.empty? + next + else + paths << line + end + end + + paths.to_a + end + end + end +end diff --git a/Library/Homebrew/test/os/linux/ld_spec.rb b/Library/Homebrew/test/os/linux/ld_spec.rb new file mode 100644 index 0000000000..b738bbe253 --- /dev/null +++ b/Library/Homebrew/test/os/linux/ld_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "os/linux/ld" +require "tmpdir" + +RSpec.describe OS::Linux::Ld do + describe "::library_paths" do + ld_etc = Pathname("") + before do + ld_etc = Pathname(Dir.mktmpdir("homebrew-tests-ld-etc-", Dir.tmpdir)) + FileUtils.mkdir [ld_etc/"subdir1", ld_etc/"subdir2"] + (ld_etc/"ld.so.conf").write <<~EOS + # This line is a comment + + include #{ld_etc}/subdir1/*.conf # This is an end-of-line comment, should be ignored + + # subdir2 is an empty directory + include #{ld_etc}/subdir2/*.conf + + /a/b/c + /d/e/f # Indentation on this line should be ignored + /a/b/c # Duplicate entry should be ignored + EOS + + (ld_etc/"subdir1/1-1.conf").write <<~EOS + /foo/bar + /baz/qux + EOS + + (ld_etc/"subdir1/1-2.conf").write <<~EOS + /g/h/i + EOS + + # Empty files (or files containing only whitespace) should be ignored + (ld_etc/"subdir1/1-3.conf").write "\n\t\n\t\n" + (ld_etc/"subdir1/1-4.conf").write "" + end + + after do + FileUtils.rm_rf ld_etc + end + + it "parses library paths successfully" do + expect(described_class.library_paths(ld_etc/"ld.so.conf")).to eq(%w[/foo/bar /baz/qux /g/h/i /a/b/c /d/e/f]) + end + end +end