From 13e8b39e0b186739fa1b048fa35ec7efabe88975 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 31 Oct 2026 20:38:49 +0100 Subject: [PATCH] Build Ruby < 3.2 with GCC 15 everywhere, not only on Fedora GCC 15 defaults to C23, which Ruby < 3.2 can't compile as. Detect the GCC version instead of the OS, so that e.g. Ubuntu 26.04 is handled too. The flag keeps going through the cflags configure variable, except for Ruby 2.6 where it goes on CC, because its MJIT header check runs a bare $(CC) and would otherwise fail. Ported from https://github.com/ruby/ruby-builder/pull/31 Co-Authored-By: Claude Fable 5.1 --- bin/ruby-build | 58 ++++++++++----- test/compiler.bats | 176 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 19 deletions(-) diff --git a/bin/ruby-build b/bin/ruby-build index 800564c7be..be666f98b1 100755 --- a/bin/ruby-build +++ b/bin/ruby-build @@ -208,16 +208,24 @@ is_freebsd() { [ "$(uname -s)" = "FreeBSD" ] } -is_fedora() { - [ -r /etc/os-release ] || return 1 - # shellcheck disable=SC1091 - source /etc/os-release - # Treat Aurora, Bazzite, BlueFin, uCore, and other Fedora variants as "fedora". - if [[ "${ID_LIKE:-$ID}" != "fedora" || ( $# -gt 0 && "${VERSION_ID%%.*}" -lt "$1") ]]; then - return 1 +# The C compiler that Ruby's configure picks: $CC, otherwise gcc if available, otherwise cc. +c_compiler() { + if [ -n "$CC" ]; then + echo "$CC" + elif command -v gcc >/dev/null; then + echo gcc + else + echo cc fi } +# Prints the major version of GCC, or nothing if the given C compiler is not GCC (e.g. Clang). +gcc_major_version() { + # shellcheck disable=SC2086 + $1 -E -dM - /dev/null | + awk '/^#define __clang__ /{clang=1} /^#define __GNUC__ /{gnuc=$3} END{if (!clang) print gnuc}' +} + freebsd_package_prefix() { local package="$1" pkg info --prefix "$package" 2>/dev/null | cut -wf2 @@ -735,24 +743,36 @@ build_package_standard() { fi fi + local cc_override extra_cflags + if [ -z "$CC" ] && is_mac 1010; then + cc_override=clang + elif [[ "$package_var_name" = "RUBY" && "$ruby_semver" -lt 30200 ]]; then + local cc gcc_version + cc="$(c_compiler)" + gcc_version="$(gcc_major_version "$cc")" + if [ "${gcc_version:-0}" -ge 15 ]; then + # GCC 15 changed the default C mode from gnu17 to gnu23: https://gcc.gnu.org/gcc-15/changes.html#c + # Ruby < 3.2 can't compile as C23, where `()` in a declaration means `(void)`. + extra_cflags="-std=gnu17" + fi + if [[ -n "$extra_cflags" && "$ruby_semver" -ge 20600 && "$ruby_semver" -lt 20700 ]]; then + # Ruby 2.6 checks its MJIT header with a bare $(CC), so the flags must be part of CC there. + cc_override="$cc $extra_cflags" + extra_cflags= + fi + fi + ( if [ "${CFLAGS+defined}" ] || [ "${!PACKAGE_CFLAGS+defined}" ]; then export CFLAGS="$CFLAGS ${!PACKAGE_CFLAGS}" fi - if [ -z "$CC" ] && is_mac 1010; then - export CC=clang - elif [[ "$package_var_name" = "RUBY" && "$ruby_semver" -lt 30200 ]] && is_fedora 42; then - # Fedora 42+ has updated to GCC v15. GCC v15 changed the default - # mode from gnu17 to gnu23: https://gcc.gnu.org/gcc-15/changes.html#c - # - # Ruby < 3.2 can't compile on GCC v15 in the default mode of gnu23. - # - # TODO: Consider changing this to check for GCC v15 specifically - # rather than inspecting the OS itself. - + if [ -n "$cc_override" ]; then + export CC="$cc_override" + fi + if [ -n "$extra_cflags" ]; then # Use the cflags configure variable, which Ruby's configure appends to # its default optflags/debugflags/warnflags, unlike CFLAGS which would # override them and notably lose the -O3, producing a -O0 Ruby. - export cflags="-std=gnu17 $cflags" + export cflags="$extra_cflags${cflags:+ $cflags}" fi if [ "$package_var_name" = "RUBY" ] && is_mac && ! is_mac 2700; then # The macOS 27 SDK declares dup3() and pipe2() as available since macOS 27.0. diff --git a/test/compiler.bats b/test/compiler.bats index 14ac4daa7e..94995b27c2 100755 --- a/test/compiler.bats +++ b/test/compiler.bats @@ -117,3 +117,179 @@ OUT unstub brew unstub make } + +# Stubs a Linux host with a ./configure that logs CC +stub_linux_build() { + mkdir -p "$INSTALL_ROOT" + cd "$INSTALL_ROOT" || return 1 + + stub_repeated uname '-s : echo Linux' + stub_repeated brew 'false' + # shellcheck disable=SC2016 + stub_repeated make 'echo "make $(inspect_args "$@")" >> build.log' + + cat > ./configure < build.log +echo CC=\${CC-unset} >> build.log +echo cflags=\${cflags-unset} >> build.log +CON + chmod +x ./configure +} + +unstub_linux_build() { + unstub uname + unstub brew + unstub make +} + +# Stubs the given C compiler (default: gcc) to report the given preprocessor defines +stub_cc() { + stub_repeated "${2:-gcc}" "-E -dM - : printf '%s\n' $1" +} + +@test "GCC 15 compiles Ruby < 3.2 as gnu17" { + stub_linux_build + stub_cc '"#define __GNUC__ 15"' + + run_inline_definition <= 2.3" { + stub_linux_build + stub_cc '"#define __GNUC__ 14"' + + run_inline_definition <