gcc: port GCC 16.1.0 to the Redox target and install the toolchain

The Redox target port applier claimed validation on 16.1.0 but its
idempotency probe used each block's longest line, which for three blocks
is generic upstream boilerplate. Against a pristine 16.1.0 tree that
silently skipped the libgcc target arms and BOTH crossconfig.m4 arms
(libgcc/config.host and crossconfig.m4 contain zero redox references, yet
the applier reported 'already present'), producing a half-ported tree --
the exact failure the script documents itself as preventing. Probe on the
longest redox-bearing line instead, matched whole.

Two port requirements the original extraction missed, both fatal:

- gcc/config/redox.opt.urls. GCC 16 requires a .opt.urls companion for
  every .opt; s-options fails without it. Contents match what
  regenerate-opt-urls.py emits for these two options, cross-checked
  against the six upstream .opt.urls declaring the same pthread/rdynamic.

- libtool has no redox host. Upstream Redox gets shared libraries from
  recipes/dev/libtool (a Redox-patched libtool 2.5.4-redox-9510) via
  libtoolize during autoreconf, not from GCC's bundled libtool.m4. That
  route does not apply to GCC 16, which bundles 2.2.7-era macros plus its
  own ltgcc.m4. Without redox arms _LT_SYS_DYNAMIC_LINKER leaves
  dynamic_linker=no, libstdc++ builds static-only, and the desktop stack
  cannot link -- libQt6Core.so and every KF6 library carry DT_NEEDED
  libstdc++.so.6. apply-libtool-redox.py registers the four arms that
  matter, verbatim from the Redox libtool macros already in prefix/.

Result: x86_64-unknown-redox-gcc 16.1.0 with libstdc++.so.6.0.35 (SONAME
libstdc++.so.6, NEEDED libc.so.6 + libgcc_s.so.1 -- identical to the
13.2.0 library it replaces, exports a superset up to GLIBCXX_3.4.35).
std::ranges::to now compiles for the Redox target; GCC 13.2.0 fails the
same test, which is what blocked kwin's 16 affected files.

install-gcc16-toolchain.sh installs into all three locations a recipe can
resolve a compiler from -- including ~/.redoxer, which src/cook/script.rs
puts highest on PATH -- and is reversible with --restore. Its cstdlib
strtold patch is guarded; the mk/prefix.mk sed is not, and had applied
that block 17 times to the GCC 13 toolchain.
This commit is contained in:
2026-08-03 12:40:15 +03:00
parent 3a32edf18d
commit f2b6c1e0ed
7 changed files with 499 additions and 21 deletions
+7
View File
@@ -88,3 +88,10 @@ Packages/*.pkgar
# from build-redbear.sh — never commit them. The toolchain is rebuilt by
# `make prefix` after fork (relibc/kernel/base) changes.
/prefix/**/*.tar.gz
# GCC 16 cross-toolchain build artifacts (reproducible via
# local/scripts/build-gcc16-cross.sh from local/recipes/dev/gcc16/source.tar)
/build/gcc16-build/
/build/gcc16-install/
/build/gcc13-backup/
/build/host-tools/
+79 -14
View File
@@ -97,19 +97,84 @@ offline-first build model and matches how other external recipes vendor sources
`gitlab.redox-os.org/redox-os/gcc` carries **no** upstream release tags, so the
pristine base must come from ftp.gnu.org or gcc-mirror, not from the Redox fork.
## Next steps (in order)
## Two things the extraction missed
1. Vendor `gcc-16.1.0.tar.xz`, record its blake3, create
`local/recipes/dev/gcc16/recipe.toml` (model on `recipes/dev/gcc13`).
2. Apply `files/` + the arms in `registration-hunks.txt`; regenerate `configure`
from `crossconfig.m4` with autoconf.
3. Parameterize the hardcoded `13.2.0` in `mk/prefix.mk` (at minimum the
`lib/gcc/$(GNU_TARGET)/13.2.0/include/limits.h` path).
4. Build the cross compiler; stage into `prefix/`. Expect relibc header
fallout (see risks above) before anything else compiles.
5. Full C++ tree rebuild for the libstdc++ ABI change — Qt6, all KF6, Plasma,
Mesa C++. Budget this as the dominant cost, not the port itself.
6. Only then retry `kwin`, whose 16 `std::ranges::to` files are the reason for
the whole exercise.
The "13 files" count above was derived by grepping the `redox-13.2.0` fork for
"redox". Two requirements do not show up that way and both are fatal if skipped.
**Not started.** Steps 16 are unvalidated; nothing below step 0 has been run.
### 1. `gcc/config/redox.opt.urls` (new in GCC 16)
GCC 16 requires a `.opt.urls` companion for every `.opt` file; `s-options`
fails with *"No rule to make target ... redox.opt.urls"*. GCC 13 had no such
rule. The file is now carried in `files/` and matches what
`gcc/regenerate-opt-urls.py` produces: `rdynamic` gets the standard
`gcc/Link-Options.html#index-rdynamic` suffix, and `pthread` is skipped with the
duplicate-URL comment — identical to the six other upstream `.opt.urls` that
declare the same two options.
### 2. libtool has no `redox` host — `apply-libtool-redox.py`
Upstream Redox's GCC fork does not carry redox arms in GCC's bundled
`libtool.m4` either. Their toolchain gets shared libraries because
`recipes/dev/libtool` is a **Redox-patched GNU libtool (2.5.4-redox-9510)**
whose `libtoolize` overwrites the tree's `libtool.m4` during `autoreconf -fvi`.
That route is not usable here: GCC 16 bundles 2.2.7-era macros plus its own
`ltgcc.m4`, and gcc13's recipe only regenerates when an `autoreconf2.69`
happens to be on `PATH`.
Without the arms, `_LT_SYS_DYNAMIC_LINKER` leaves `dynamic_linker=no` for
redox, so configure reports *"libtool supports shared libraries... no"* and
libstdc++ builds **static only**. That is fatal: `libQt6Core.so` and every KF6
library carry `DT_NEEDED libstdc++.so.6` (`readelf -d`), so the desktop stack
cannot link against a static-only toolchain. `apply-libtool-redox.py` registers
the four arms that matter, copied verbatim from the Redox libtool macros in
`prefix/<target>/gcc-install/share/aclocal/libtool.m4`.
## Applying to a new GCC (validated sequence)
```bash
tar xf source.tar -C local/recipes/dev/gcc16/source --strip-components=1
python3 local/patches/gcc-redox-port/apply-redox-port.py local/recipes/dev/gcc16/source
python3 local/patches/gcc-redox-port/apply-libtool-redox.py local/recipes/dev/gcc16/source
# configure is GENERATED -- regenerate with autoconf 2.69 (GCC pins that exact
# version via config/override.m4; the host's 2.73 is rejected).
(cd local/recipes/dev/gcc16/source/libstdc++-v3 && autoconf)
./local/scripts/build-gcc16-cross.sh # cross compiler + libgcc + libstdc++
./local/scripts/install-gcc16-toolchain.sh # into prefix + ~/.redoxer (reversible)
```
Both appliers are idempotent and report an unmatched anchor rather than
silently producing a half-ported tree.
## Status — GCC 16.1.0 ported and installed
| Step | State |
|---|---|
| `gcc-16.1.0.tar.xz` vendored (`local/recipes/dev/gcc16/source.tar`) | done |
| Redox target port applied (10 edits + 6 files) | done |
| `libstdc++-v3/configure` regenerated with autoconf 2.69 | done — purely additive, 3240 lines, no drift |
| Cross compiler built | done — `x86_64-unknown-redox-gcc (GCC) 16.1.0` |
| libgcc + shared libstdc++ | done — `libstdc++.so.6.0.35`, SONAME `libstdc++.so.6`, `NEEDED libc.so.6 libgcc_s.so.1` (identical to the 13.2.0 library it replaces) |
| **`std::ranges::to`** | **works**`__cpp_lib_ranges_to_container` defined; GCC 13 fails the same test with *"'to' is not a member of 'std::ranges'"* |
| Installed into prefix + `~/.redoxer` | done, reversible via `install-gcc16-toolchain.sh --restore` |
ABI note: `libstdc++.so.6.0.35` exports up to `GLIBCXX_3.4.35` where 13.2.0
reached `3.4.32`, and keeps SONAME `libstdc++.so.6` — a backward-compatible
superset, so already-linked binaries still resolve. Recompilation is still
required to *use* the new headers.
### Still open
- `mk/prefix.mk` hardcodes `13.2.0` (line 354, the `limits.h` removal) and
`GCC_TARGET=recipes/dev/gcc13/...` (line 10). Neither is on the path this
port takes, but both must be parameterized before the toolchain can be
rebuilt through `make prefix`.
- `local/recipes/dev/gcc16/recipe.toml` does not exist. The cross compiler is
currently produced by `local/scripts/build-gcc16-cross.sh` instead, because
the cookbook's from-source toolchain path is dead: `mk/prefix.mk:315` copies
`$(GCC_TARGET)/stage.cxx/`, and nothing in `src/` produces a `stage.cxx`.
- Full C++ tree rebuild against the new toolchain — the dominant cost, and the
reason the whole exercise exists (`kwin`'s 16 `std::ranges::to` files).
- `riscv64*-*-redox*` is recorded in `registration-points.txt` for
`libgcc/config.host` but is not in the applier's block. Irrelevant for
amd64-only Red Bear; noted so the gap is not mistaken for completeness.
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""Register the Redox host OS in GCC's bundled libtool.m4.
Usage: apply-libtool-redox.py <gcc-source-root> [--check]
Why this is separate from apply-redox-port.py
---------------------------------------------
The original port extraction (README.md, registration-points.txt) measured
the Redox port by grepping redox-os/gcc for "redox" and found 13 files. It
missed libtool: upstream Redox does not carry redox arms in GCC's bundled
libtool.m4 either. Their toolchain gets shared-library support because
recipes/dev/libtool is a Redox-patched GNU libtool (2.5.4-redox-9510) whose
libtoolize overwrites the tree's libtool.m4 during `autoreconf -fvi`.
That path is not usable for GCC 16: GCC bundles libtool 2.2.7-era macros
plus its own ltgcc.m4 overrides, and gcc13's recipe only regenerates when
an autoreconf2.69 happens to be on PATH. Splicing 2.5.4 wholesale into a
2.2.7 tree risks a macro-version mismatch across the whole GCC build.
Without these arms libtool's _LT_SYS_DYNAMIC_LINKER leaves dynamic_linker=no
for redox, so "libtool supports shared libraries... no" and libstdc++ builds
static-only. That is fatal here: libQt6Core.so and every KF6 library carry
DT_NEEDED libstdc++.so.6, so the desktop stack cannot link without a shared
libstdc++ matching the compiler's headers.
The arm contents below are copied verbatim from the Redox libtool 2.5.4
macros already present in this repo at
prefix/<target>/gcc-install/share/aclocal/libtool.m4
so the behaviour matches the toolchain they replace. They are plain shell
case arms and are version-agnostic; only the ANCHORS are GCC-version
sensitive, and a missing anchor is reported rather than silently skipped.
"""
import sys
from pathlib import Path
MAX_CMD_LEN = """ redox*)
# Under Redox, this test is not required because there is
# no limit to the length of command line arguments.
# Libtool will interpret -1 as no limit whatsoever
lt_cv_sys_max_cmd_len=-1;
;;
"""
DYNAMIC_LINKER = """redox*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
dynamic_linker='relibc ld64.so'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
"""
DEPLIBS = """redox*)
lt_cv_deplibs_check_method=pass_all
;;
"""
# C++ tag: no special PIC handling needed, -fPIC comes from the generic path.
PIC_CXX = """ redox*)
\t;;
"""
PIC_C = """ redox*)
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
;;
"""
ARCHIVE_C = """ redox*)
\t_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
\t_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
"""
LD_SHLIBS_CXX = """ redox*)
_LT_TAGVAR(ld_shlibs, $1)=yes
\t;;
"""
# (anchor, block, description)
EDITS = [
(" gnu*)\n # Under GNU Hurd, this test is not required because there is",
MAX_CMD_LEN, "max_cmd_len"),
("newsos6)\n version_type=linux",
DYNAMIC_LINKER, "dynamic_linker (enables shared libs)"),
("newos6*)\n lt_cv_deplibs_check_method=",
DEPLIBS, "deplibs_check_method"),
(" netbsd*)\n if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then\n\t _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects",
LD_SHLIBS_CXX, "ld_shlibs (C++ tag)"),
]
def probe_present(text: str, block: str) -> bool:
"""A block counts as present when its longest redox-bearing line already
exists as a complete line. Same rule as apply-redox-port.py: a plain
longest-line probe matches generic libtool boilerplate."""
redox_lines = [l.strip() for l in block.strip().splitlines()
if "redox" in l.lower()]
if not redox_lines:
return False
probe = max(redox_lines, key=len)
return any(l.strip() == probe for l in text.splitlines())
def main() -> int:
if len(sys.argv) < 2:
print(__doc__)
return 2
root = Path(sys.argv[1]).resolve()
check = "--check" in sys.argv
p = root / "libtool.m4"
if not p.is_file():
print(f"ERROR: {p} not found", file=sys.stderr)
return 2
text = p.read_text(encoding="utf-8", errors="replace")
rc = 0
pending = []
for anchor, block, desc in EDITS:
if probe_present(text, block):
print(f"skip {desc} (already present)")
continue
idx = text.find(anchor)
if idx == -1:
print(f"ANCHOR-FAIL {desc} :: anchor not found -- re-derive for "
f"this GCC version", file=sys.stderr)
rc = 1
continue
if text.count(anchor) != 1:
print(f"AMBIGUOUS {desc} :: anchor occurs {text.count(anchor)}x",
file=sys.stderr)
rc = 1
continue
print(f"{'would-apply' if check else 'applied '} {desc}")
pending.append((idx, block))
if not check and pending:
# Apply back-to-front so earlier offsets stay valid.
for idx, block in sorted(pending, key=lambda t: -t[0]):
text = text[:idx] + block + text[idx:]
p.write_text(text, encoding="utf-8")
if rc:
print("libtool.m4 is NOT fully registered for redox.", file=sys.stderr)
else:
print("\nNEXT: regenerate libstdc++-v3/configure with autoconf 2.69.")
return rc
if __name__ == "__main__":
sys.exit(main())
@@ -174,12 +174,29 @@ def apply_edit(root: Path, rel: str, anchor: str, block: str, check: bool) -> st
# the generic block starts "*-redox*)", which occurs inside the already
# inserted "aarch64-*-redox* | riscv64*-*-redox*)" and made the second
# crossconfig.m4 block silently skip.
# Probe on the block's LONGEST line, matched whole. The first line is not
# distinctive enough: CONFIG_GCC_TARGETS starts "aarch64-*-redox*)", which
# also appears (indented) inside CONFIG_GCC_OS, so a first-line probe made
# the tm_file arms silently skip.
probe = max((l.strip() for l in block.strip().splitlines()), key=len)
if probe and any(l.strip() == probe for l in text.splitlines()):
# Probe on the block's longest REDOX-BEARING line, matched whole. Both
# halves of that rule are load-bearing:
# - "redox-bearing": a plain longest-line probe picks generic upstream
# boilerplate. On GCC 16.1.0 that made three blocks silently skip
# against a pristine tree -- LIBGCC_TARGETS probed
# 'tmake_file="${tmake_file} ${cpu_type}/t-lse t-slibgcc-libgcc"'
# (3 upstream hits) and both crossconfig.m4 blocks probed
# "SECTION_FLAGS='-ffunction-sections -fdata-sections'" (7 hits) and
# "AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign
# _aligned_malloc)" (4 hits), yielding a half-ported tree with no
# libgcc target arms and no libstdc++ redox support.
# - "whole line": CROSSCONFIG_GENERIC's marker is "*-redox*)", a
# substring of the already-inserted
# "aarch64-*-redox* | riscv64*-*-redox*)".
# Longest (not first) still matters: CONFIG_GCC_TARGETS starts
# "aarch64-*-redox*)", which also appears indented inside CONFIG_GCC_OS.
redox_lines = [l.strip() for l in block.strip().splitlines()
if "redox" in l.lower()]
if not redox_lines:
return (f"NO-PROBE {rel} :: block has no redox-bearing line; "
"cannot test idempotency safely")
probe = max(redox_lines, key=len)
if any(l.strip() == probe for l in text.splitlines()):
return f"skip {rel} (block already present)"
idx = text.find(anchor)
if idx == -1:
@@ -250,7 +267,7 @@ def main() -> int:
for rel, anchor, block, _ in EDITS:
msg = apply_edit(root, rel, anchor, block, check)
print(msg)
if "ANCHOR-FAIL" in msg or "MISSING" in msg:
if any(k in msg for k in ("ANCHOR-FAIL", "MISSING", "NO-PROBE")):
rc = 1
print()
@@ -0,0 +1,8 @@
; Autogenerated by regenerate-opt-urls.py from gcc/config/redox.opt and generated HTML
; skipping UrlSuffix for 'pthread' due to multiple URLs:
; duplicate: 'gcc/Link-Options.html#index-pthread-1'
; duplicate: 'gcc/Preprocessor-Options.html#index-pthread'
rdynamic
UrlSuffix(gcc/Link-Options.html#index-rdynamic)
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env bash
# Build the GCC 16 cross-compiler (x86_64-linux -> x86_64-unknown-redox).
#
# Why this exists
# ---------------
# mk/prefix.mk has two toolchain paths:
# PREFIX_BINARY=1 (the default, mk/config.mk:13) downloads a prebuilt
# GCC 13.2.0 from static.redox-os.org.
# PREFIX_BINARY=0 cooks host:gcc13 through the cookbook.
# The second path is dead in this fork: it copies
# "$(GCC_TARGET)/stage.cxx/usr/" (mk/prefix.mk:315) and nothing in src/
# ever produces a stage.cxx directory, so it cannot have run. Repairing
# the cookbook's from-source path is a separate piece of work and is not
# required to obtain a newer GCC.
#
# This script therefore builds the cross compiler directly against the
# artifacts the prefix already provides: binutils 2.43.1 and the relibc
# sysroot (libc.a, crt*.o, headers). Configure flags mirror the cross arm
# of recipes/dev/gcc13/recipe.toml so the resulting compiler is
# configured like the one it replaces.
#
# The GCC 16 source must already carry the Redox target port:
# python3 local/patches/gcc-redox-port/apply-redox-port.py \
# local/recipes/dev/gcc16/source
# (then regenerate libstdc++-v3/configure with autoconf 2.69)
# See local/patches/gcc-redox-port/README.md.
set -euo pipefail
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
TARGET=x86_64-unknown-redox
PREFIX_DIR="$ROOT/prefix/$TARGET"
SYSROOT="$PREFIX_DIR/sysroot/$TARGET"
SRC="$ROOT/local/recipes/dev/gcc16/source"
BUILD="$ROOT/build/gcc16-build"
INSTALL="$ROOT/build/gcc16-install"
JOBS="${JOBS:-$(nproc)}"
for req in "$SRC/gcc/BASE-VER" "$SYSROOT/lib/libc.a" "$PREFIX_DIR/gcc-install/bin/$TARGET-as"; do
[ -e "$req" ] || { echo "FATAL: missing prerequisite: $req" >&2; exit 1; }
done
# The port must be present, or we would silently build a compiler with no
# Redox target and only discover it at the end of a ~40 minute build.
grep -q 'redox' "$SRC/gcc/config.gcc" || {
echo "FATAL: $SRC is not Redox-ported (gcc/config.gcc has no redox arm)." >&2
echo " Run local/patches/gcc-redox-port/apply-redox-port.py first." >&2
exit 1
}
grep -q 'redox' "$SRC/libstdc++-v3/configure" || {
echo "FATAL: libstdc++-v3/configure has no redox arm -- it was not" >&2
echo " regenerated from the patched crossconfig.m4 (autoconf 2.69)." >&2
exit 1
}
echo "GCC version : $(cat "$SRC/gcc/BASE-VER")"
echo "target : $TARGET"
echo "sysroot : $SYSROOT"
echo "install : $INSTALL"
echo "jobs : $JOBS"
export PATH="$PREFIX_DIR/gcc-install/bin:$PATH"
# Redox has no SystemTap probes, but the configure probe can see the host's
# sys/sdt.h and then libgcc fails including a header the target lacks.
# Same reasoning as recipes/dev/gcc13/recipe.toml.
export gcc_cv_sys_sdt_h=no
rm -rf "$BUILD" "$INSTALL"
mkdir -p "$BUILD" "$INSTALL"
cd "$BUILD"
"$SRC/configure" \
--build=x86_64-pc-linux-gnu \
--host=x86_64-pc-linux-gnu \
--target="$TARGET" \
--prefix="$INSTALL" \
--with-sysroot="$SYSROOT" \
--with-native-system-header-dir="/include" \
--with-linker-hash-style=gnu \
--enable-languages=c,c++,lto \
--enable-initfini-array \
--enable-threads=posix \
--enable-libstdcxx-threads \
--enable-frame-pointer \
--disable-nls \
--disable-multilib \
--disable-bootstrap \
--with-bugurl="https://gitea.redbearos.org/vasilito/RedBear-OS/-/issues"
"${MAKE:-make}" -j "$JOBS" all-gcc
"${MAKE:-make}" -j "$JOBS" all-target-libgcc
"${MAKE:-make}" -j "$JOBS" all-target-libstdc++-v3
"${MAKE:-make}" install-gcc install-target-libgcc install-target-libstdc++-v3
echo
echo "BUILD OK"
"$INSTALL/bin/$TARGET-gcc" --version | head -1
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env bash
# Install the GCC 16 cross compiler over the GCC 13.2.0 one, in every
# location the build actually reads a compiler from.
#
# There are three such locations, and all three must agree or the build
# picks up a mixed toolchain:
#
# 1. prefix/<target>/gcc-install/ - the GCC half of the prefix
# 2. prefix/<target>/sysroot/ - the merged prefix (gcc+rust+clang)
# 3. ~/.redoxer/<target>/toolchain/ - HIGHEST PRIORITY. src/cook/script.rs
# prepends this to PATH last, so it wins over both prefix paths for
# every recipe. Missing this one silently leaves recipes on GCC 13.
#
# Reversible: the GCC 13 components are moved aside to a timestamped
# backup dir rather than deleted, and --restore puts them back.
#
# Run local/scripts/build-gcc16-cross.sh first.
set -euo pipefail
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
TARGET=x86_64-unknown-redox
NEWGCC=16.1.0
OLDGCC=13.2.0
SRC="$ROOT/build/gcc16-install"
PREFIX_GCC="$ROOT/prefix/$TARGET/gcc-install"
PREFIX_SYSROOT="$ROOT/prefix/$TARGET/sysroot"
REDOXER="$HOME/.redoxer/$TARGET/toolchain"
BACKUP="$ROOT/build/gcc13-backup"
# GCC's <cstdlib> does `using ::strtold` before relibc's cbindgen trailer has
# declared it, so the C++ wrapper must pull in <stdlib.h> explicitly. mk/prefix.mk
# does this with a sed, but that sed is NOT idempotent -- the current GCC 13
# toolchain has the comment block appended 17 times from repeated `make prefix`
# runs. The guard below makes it apply exactly once.
patch_cstdlib() {
local f="$1"
[ -f "$f" ] || return 0
grep -q "Red Bear: relibc declares strtold" "$f" && return 0
sed -i '/^#include_next <stdlib.h>$/a\\n// Red Bear: relibc declares strtold via cbindgen trailer.\n// The explicit include ensures ::strtold is visible when the\n// C++ wrapper processes its using-directives below.\n#include <stdlib.h>' "$f"
echo " patched strtold: $f"
}
# Move GCC's own components out of a tree without disturbing the rust/clang/
# relibc files that share it.
evict_old() {
local dst="$1" tag="$2"
local bk="$BACKUP/$tag"
mkdir -p "$bk"
for p in "bin/$TARGET-gcc-$OLDGCC" \
"lib/gcc/$TARGET/$OLDGCC" \
"libexec/gcc/$TARGET/$OLDGCC" \
"$TARGET/include/c++/$OLDGCC"; do
if [ -e "$dst/$p" ]; then
mkdir -p "$bk/$(dirname "$p")"
mv "$dst/$p" "$bk/$p"
fi
done
for so in "$dst/$TARGET/lib/"libstdc++.so.6.0.* ; do
[ -e "$so" ] || continue
mkdir -p "$bk/$TARGET/lib"
mv "$so" "$bk/$TARGET/lib/"
done
}
install_into() {
local dst="$1" tag="$2"
[ -d "$dst" ] || { echo " skip $tag (not present)"; return 0; }
echo " -> $tag: $dst"
evict_old "$dst" "$tag"
# Driver, cc1/cc1plus, target headers and libs.
cp -a "$SRC/bin/." "$dst/bin/"
mkdir -p "$dst/lib/gcc/$TARGET" "$dst/libexec/gcc/$TARGET"
cp -a "$SRC/lib/gcc/$TARGET/$NEWGCC" "$dst/lib/gcc/$TARGET/"
cp -a "$SRC/libexec/gcc/$TARGET/$NEWGCC" "$dst/libexec/gcc/$TARGET/"
mkdir -p "$dst/$TARGET/include/c++"
cp -a "$SRC/$TARGET/include/c++/$NEWGCC" "$dst/$TARGET/include/c++/"
cp -a "$SRC/$TARGET/lib/." "$dst/$TARGET/lib/"
for c in "$dst/$TARGET/include/c++/$NEWGCC/cstdlib"; do patch_cstdlib "$c"; done
}
if [ "${1:-}" = "--restore" ]; then
[ -d "$BACKUP" ] || { echo "no backup at $BACKUP" >&2; exit 1; }
for tag in gcc-install sysroot redoxer; do
case $tag in
gcc-install) dst="$PREFIX_GCC" ;;
sysroot) dst="$PREFIX_SYSROOT" ;;
redoxer) dst="$REDOXER" ;;
esac
[ -d "$BACKUP/$tag" ] || continue
echo "restoring $tag"
cp -a "$BACKUP/$tag/." "$dst/"
done
echo "GCC $OLDGCC restored. Re-run without --restore to go back to $NEWGCC."
exit 0
fi
[ -x "$SRC/bin/$TARGET-gcc" ] || {
echo "FATAL: $SRC not built. Run local/scripts/build-gcc16-cross.sh" >&2; exit 1; }
[ -e "$SRC/$TARGET/lib/libstdc++.so.6" ] || {
echo "FATAL: $SRC has no shared libstdc++. The desktop stack links" >&2
echo " libstdc++.so.6 (readelf -d libQt6Core.so), so a static-only" >&2
echo " toolchain cannot build it." >&2; exit 1; }
echo "Installing GCC $NEWGCC (backup of $OLDGCC -> $BACKUP)"
rm -rf "$BACKUP"
install_into "$PREFIX_GCC" gcc-install
install_into "$PREFIX_SYSROOT" sysroot
install_into "$REDOXER" redoxer
echo
echo "Verifying the compiler each recipe will actually invoke:"
PATH="$REDOXER/bin:$PREFIX_SYSROOT/bin:$PATH" "$TARGET-gcc" --version | head -1
PATH="$REDOXER/bin:$PREFIX_SYSROOT/bin:$PATH" "$TARGET-g++" -std=c++23 -x c++ -c - -o /dev/null <<'EOF'
#include <version>
#ifndef __cpp_lib_ranges_to_container
#error ranges::to missing
#endif
int main(){}
EOF
echo "C++23 ranges::to: OK"
echo "Revert with: $0 --restore"