Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions bin/ruby-build
Original file line number Diff line number Diff line change
Expand Up @@ -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 2>/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
Expand Down Expand Up @@ -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.
Expand Down
176 changes: 176 additions & 0 deletions test/compiler.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<CON
#!${BASH}
echo ./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 <<DEF
build_package_standard ruby-3.1.7
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT --with-ext=openssl,psych,+
CC=unset
cflags=-std=gnu17
make -j 2
make install
OUT

unstub gcc
unstub_linux_build
}

@test "GCC 15 compiles Ruby 2.6 as gnu17 via CC" {
stub_linux_build
stub_cc '"#define __GNUC__ 15"'

run_inline_definition <<DEF
build_package_standard ruby-2.6.10
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT --with-ext=openssl,psych,+
CC=gcc -std=gnu17
cflags=unset
make -j 2
make install
OUT

unstub gcc
unstub_linux_build
}

@test "GCC 15 compiles Ruby <= 2.2 as gnu17" {
stub_linux_build
stub_cc '"#define __GNUC__ 15"'

run_inline_definition <<DEF
build_package_standard ruby-2.2.10
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT
CC=unset
cflags=-std=gnu17
make -j 2
make install
OUT

unstub gcc
unstub_linux_build
}

@test "GCC 15 adds -std=gnu17 to a custom CC for Ruby 2.6" {
stub_linux_build
stub_cc '"#define __GNUC__ 15"' gcc-15
export CC=gcc-15

run_inline_definition <<DEF
build_package_standard ruby-2.6.10
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT --with-ext=openssl,psych,+
CC=gcc-15 -std=gnu17
cflags=unset
make -j 2
make install
OUT

unstub gcc-15
unstub_linux_build
}

@test "the C compiler is not probed for Ruby 3.2+" {
stub_linux_build

run_inline_definition <<DEF
build_package_standard ruby-3.2.0
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT --with-ext=openssl,psych,+
CC=unset
cflags=unset
make -j 2
make install
OUT

unstub_linux_build
}

@test "GCC 14 adds no flags for Ruby >= 2.3" {
stub_linux_build
stub_cc '"#define __GNUC__ 14"'

run_inline_definition <<DEF
build_package_standard ruby-3.1.7
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT --with-ext=openssl,psych,+
CC=unset
cflags=unset
make -j 2
make install
OUT

unstub gcc
unstub_linux_build
}

@test "Clang masquerading as gcc gets no flags" {
stub_linux_build
stub_cc '"#define __clang__ 1" "#define __GNUC__ 15"'

run_inline_definition <<DEF
build_package_standard ruby-3.1.7
DEF
assert_success
run cat build.log
assert_output <<OUT
./configure --prefix=$INSTALL_ROOT --with-ext=openssl,psych,+
CC=unset
cflags=unset
make -j 2
make install
OUT

unstub gcc
unstub_linux_build
}
Loading