Michael Cho f5c11fa342
Check host libstdc++ for brew gcc dependency
For most formulae, the bottles need a minimum libstdc++ rather than a
minimum GCC version. This is particularly important when building on
Ubuntu where the default compiler version is older than libstdc++.

So, checking the host libstdc++ version is a more accurate way to
determine whether brew GCC is needed at runtime. This can be improved in
the future to check symbol versions (e.g. GLIBCXX, CXXABI, GLIBC) which
can allow some bottles to be installed even with older glibc/libstdc++.
2025-09-12 14:15:56 -04:00

128 lines
4.2 KiB
Ruby

# typed: strict
# frozen_string_literal: true
require "os/linux/ld"
require "os/linux/libstdcxx"
require "utils/output"
module OS
module Linux
module Install
module ClassMethods
# 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
# GCC can find its own libraries.
GCC_RUNTIME_LIBS = T.let(%W[
libatomic.so.1
libgcc_s.so.1
libgomp.so.1
#{OS::Linux::Libstdcxx::SONAME}
].freeze, T::Array[String])
sig { params(all_fatal: T::Boolean).void }
def perform_preinstall_checks(all_fatal: false)
super
symlink_ld_so
setup_preferred_gcc_libs
end
sig { void }
def global_post_install
super
symlink_ld_so
setup_preferred_gcc_libs
end
sig { void }
def check_cpu
return if ::Hardware::CPU.intel? && ::Hardware::CPU.is_64_bit?
return if ::Hardware::CPU.arm?
message = "Sorry, Homebrew does not support your computer's CPU architecture!"
if ::Hardware::CPU.ppc64le?
message += <<~EOS
For OpenPOWER Linux (PPC64LE) support, see:
#{Formatter.url("https://github.com/homebrew-ppc64le/brew")}
EOS
end
::Kernel.abort message
end
sig { void }
def symlink_ld_so
brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
unless ld_so.readable?
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?
return
end
end
return if brew_ld_so.readable? && (brew_ld_so.readlink == ld_so)
FileUtils.mkdir_p HOMEBREW_PREFIX/"lib"
FileUtils.ln_sf ld_so, brew_ld_so
end
sig { void }
def setup_preferred_gcc_libs
gcc_opt_prefix = HOMEBREW_PREFIX/"opt/#{OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA}"
glibc_installed = (HOMEBREW_PREFIX/"opt/glibc/bin/ld.so").readable?
return unless gcc_opt_prefix.readable?
if glibc_installed
ld_so_conf_d = HOMEBREW_PREFIX/"etc/ld.so.conf.d"
unless ld_so_conf_d.exist?
ld_so_conf_d.mkpath
FileUtils.chmod "go-w", ld_so_conf_d
end
# Add gcc to ld search paths
ld_gcc_conf = ld_so_conf_d/"50-homebrew-preferred-gcc.conf"
ld_gcc_conf_content = <<~EOS
# This file is generated by Homebrew. Do not modify.
#{gcc_opt_prefix}/lib/gcc/current
EOS
if !ld_gcc_conf.exist? || (ld_gcc_conf.read != ld_gcc_conf_content)
ld_gcc_conf.atomic_write ld_gcc_conf_content
FileUtils.chmod "u=rw,go-wx", ld_gcc_conf
FileUtils.rm_f HOMEBREW_PREFIX/"etc/ld.so.cache"
::Kernel.system HOMEBREW_PREFIX/"opt/glibc/sbin/ldconfig"
end
else
Utils::Output.odie "#{HOMEBREW_PREFIX}/lib does not exist!" unless (HOMEBREW_PREFIX/"lib").readable?
end
GCC_RUNTIME_LIBS.each do |library|
gcc_library_symlink = HOMEBREW_PREFIX/"lib/#{library}"
if glibc_installed
# Remove legacy symlinks
FileUtils.rm gcc_library_symlink if gcc_library_symlink.symlink?
else
gcc_library = gcc_opt_prefix/"lib/gcc/current/#{library}"
# Skip if the link target doesn't exist.
next unless gcc_library.readable?
# Also skip if the symlink already exists.
next if gcc_library_symlink.readable? && (gcc_library_symlink.readlink == gcc_library)
FileUtils.ln_sf gcc_library, gcc_library_symlink
end
end
end
end
end
end
end
Homebrew::Install.singleton_class.prepend(OS::Linux::Install::ClassMethods)