diff --git a/Dockerfile b/Dockerfile index b73eb6eae6..ede9668b37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,6 +45,7 @@ RUN apt-get update \ tzdata \ jq \ && if [ "$(. /etc/lsb-release; echo "${DISTRIB_RELEASE}" | cut -d. -f1)" -ge 22 ]; then apt-get install -y --no-install-recommends skopeo; fi \ + && if [ "$(. /etc/lsb-release; echo "${DISTRIB_RELEASE}" | cut -d. -f1)" -eq 22 ]; then apt-get install -y --no-install-recommends gcc-12 g++-12; fi \ && mkdir -p /etc/apt/keyrings \ && chmod 0755 /etc /etc/apt /etc/apt/keyrings \ && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null \ diff --git a/Library/Homebrew/extend/os/linux/development_tools.rb b/Library/Homebrew/extend/os/linux/development_tools.rb index 04827adee3..cb3e4f5898 100644 --- a/Library/Homebrew/extend/os/linux/development_tools.rb +++ b/Library/Homebrew/extend/os/linux/development_tools.rb @@ -43,13 +43,14 @@ module OS end # Keep this method around for now to make it easier to add this functionality later. - # rubocop:disable Lint/UselessMethodDefinition sig { returns(Pathname) } def host_gcc_path - # TODO: override this if/when we to pick the GCC based on e.g. the Ubuntu version. + # Prioritise versioned path if installed + path = Pathname.new("/usr/bin/#{OS::LINUX_PREFERRED_GCC_COMPILER_FORMULA.tr("@", "-")}") + return path if path.exist? + super end - # rubocop:enable Lint/UselessMethodDefinition sig { returns(T::Boolean) } def needs_compiler_formula? @@ -60,12 +61,7 @@ module OS # Undocumented environment variable to make it easier to test compiler # formula automatic installation. @needs_compiler_formula = true if ENV["HOMEBREW_FORCE_COMPILER_FORMULA"] - - @needs_compiler_formula ||= if host_gcc_path.exist? - ::DevelopmentTools.gcc_version(host_gcc_path.to_s) < OS::LINUX_GCC_CI_VERSION - else - true - end + @needs_compiler_formula ||= OS::Linux::Libstdcxx.below_ci_version? end sig { returns(T::Hash[String, T.nilable(String)]) } diff --git a/Library/Homebrew/extend/os/linux/install.rb b/Library/Homebrew/extend/os/linux/install.rb index cbfa62ece1..b55f33a271 100644 --- a/Library/Homebrew/extend/os/linux/install.rb +++ b/Library/Homebrew/extend/os/linux/install.rb @@ -1,6 +1,7 @@ # typed: strict # frozen_string_literal: true +require "os/linux/libstdcxx" require "utils/output" module OS @@ -25,12 +26,12 @@ module OS # 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 = %w[ + GCC_RUNTIME_LIBS = T.let(%W[ libatomic.so.1 libgcc_s.so.1 libgomp.so.1 - libstdc++.so.6 - ].freeze + #{OS::Linux::Libstdcxx::SONAME} + ].freeze, T::Array[String]) sig { params(all_fatal: T::Boolean).void } def perform_preinstall_checks(all_fatal: false) diff --git a/Library/Homebrew/extend/os/linux/linkage_checker.rb b/Library/Homebrew/extend/os/linux/linkage_checker.rb index 634bd5b71f..82a3d46d27 100644 --- a/Library/Homebrew/extend/os/linux/linkage_checker.rb +++ b/Library/Homebrew/extend/os/linux/linkage_checker.rb @@ -2,12 +2,13 @@ # frozen_string_literal: true require "compilers" +require "os/linux/libstdcxx" module OS module Linux module LinkageChecker # Libraries provided by glibc and gcc. - SYSTEM_LIBRARY_ALLOWLIST = %w[ + SYSTEM_LIBRARY_ALLOWLIST = %W[ ld-linux-x86-64.so.2 ld-linux-aarch64.so.1 libanl.so.1 @@ -24,7 +25,7 @@ module OS libutil.so.1 libgcc_s.so.1 libgomp.so.1 - libstdc++.so.6 + #{OS::Linux::Libstdcxx::SONAME} libquadmath.so.0 ].freeze diff --git a/Library/Homebrew/extend/os/linux/system_config.rb b/Library/Homebrew/extend/os/linux/system_config.rb index a9aa5213d4..dcf0fcb884 100644 --- a/Library/Homebrew/extend/os/linux/system_config.rb +++ b/Library/Homebrew/extend/os/linux/system_config.rb @@ -3,6 +3,7 @@ require "compilers" require "os/linux/glibc" +require "os/linux/libstdcxx" require "system_command" module OS @@ -20,6 +21,13 @@ module OS version end + def host_libstdcxx_version + version = OS::Linux::Libstdcxx.system_version + return "N/A" if version.null? + + version + end + def host_gcc_version gcc = ::DevelopmentTools.host_gcc_path return "N/A" unless gcc.executable? @@ -49,6 +57,7 @@ module OS out.puts "OS: #{OS::Linux.os_version}" out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl? out.puts "Host glibc: #{host_glibc_version}" + out.puts "Host libstdc++: #{host_libstdcxx_version}" out.puts "#{::DevelopmentTools.host_gcc_path}: #{host_gcc_version}" out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH ["glibc", ::CompilerSelector.preferred_gcc, OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA, "xorg"].each do |f| diff --git a/Library/Homebrew/os.rb b/Library/Homebrew/os.rb index 3cd2b7f6f1..53ce0c6fba 100644 --- a/Library/Homebrew/os.rb +++ b/Library/Homebrew/os.rb @@ -49,8 +49,10 @@ module OS LINUX_CI_OS_VERSION = "Ubuntu 22.04" LINUX_GLIBC_CI_VERSION = "2.35" LINUX_GLIBC_NEXT_CI_VERSION = "2.39" - LINUX_GCC_CI_VERSION = "11.0" - LINUX_PREFERRED_GCC_COMPILER_FORMULA = "gcc@11" # https://packages.ubuntu.com/jammy/gcc + LINUX_GCC_CI_VERSION = "12" # https://packages.ubuntu.com/jammy/gcc-12 + LINUX_LIBSTDCXX_CI_VERSION = "6.0.30" # https://packages.ubuntu.com/jammy/libstdc++6 + LINUX_LIBSTDCXX_NEXT_CI_VERSION = "6.0.33" # https://packages.ubuntu.com/noble/libstdc++6 + LINUX_PREFERRED_GCC_COMPILER_FORMULA = T.let("gcc@#{LINUX_GCC_CI_VERSION}".freeze, String) LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc" if OS.mac? diff --git a/Library/Homebrew/os/linux/libstdcxx.rb b/Library/Homebrew/os/linux/libstdcxx.rb new file mode 100644 index 0000000000..dddbc26082 --- /dev/null +++ b/Library/Homebrew/os/linux/libstdcxx.rb @@ -0,0 +1,47 @@ +# typed: strict +# frozen_string_literal: true + +module OS + module Linux + # Helper functions for querying libstdc++ information. + module Libstdcxx + SONAME = "libstdc++.so.6" + SYSTEM_LIBDIRS = %w[/lib64 /lib /usr/lib64 /usr/lib].freeze + private_constant :SYSTEM_LIBDIRS + + sig { returns(Version) } + def self.system_version + @system_version ||= T.let(nil, T.nilable(Version)) + @system_version ||= if (path = system_path) && + (version = File.realpath(path)[%r{/libstdc\+\+\.so\.(\d+(?:\.\d+)*)$}, 1]) + Version.new(version) + else + Version::NULL + end + end + + sig { returns(T::Boolean) } + def self.below_ci_version? + system_version < LINUX_LIBSTDCXX_CI_VERSION + end + + sig { returns(T.nilable(String)) } + private_class_method def self.system_path + if (ldconfig = which("ldconfig")) + path = Utils.popen_read(ldconfig, "-p")[%r{=> (/.*/#{Regexp.escape(SONAME)})$}o, 1] + return path if path.present? && File.file?(path) && Pathname(path).dylib? + end + if (gcc = ::DevelopmentTools.host_gcc_path).executable? + path = Utils.popen_read(gcc, "--print-file-name=#{SONAME}").strip + return path if path.start_with?("/") && File.file?(path) && Pathname(path).dylib? + end + libdirs = SYSTEM_LIBDIRS.filter_map { |path| File.realpath(path) if File.directory?(path) } + libdirs.uniq! + Find.find(*libdirs).find do |path| + path = Pathname(path) + path.basename.to_s == SONAME && path.file? && path.dylib? && path.arch_compatible?(::Hardware::CPU.arch) + end + end + end + end +end