# Template file for 'libstdc++'
pkgname=libstdc++
version=14.2.0
revision=0
short_desc="GNU Standard C++ library and GCC unwinder runtime"
maintainer="finwo <finwo@pm.me>"
license="GPL-3.0-or-later WITH GCC-exception-3.1"
homepage="https://gcc.gnu.org/"
# GCC plus its pinned in-tree prerequisites. Only the first distfile is
# extracted by the driver; do_configure places the rest (see below).
# Prerequisite versions are GCC 14.2.0's own pins, taken verbatim from
# contrib/download_prerequisites -- not chosen by us.
distfiles="https://ftp.gnu.org/gnu/gcc/gcc-${version}/gcc-${version}.tar.xz
 https://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.2.1.tar.bz2
 https://gcc.gnu.org/pub/gcc/infrastructure/mpfr-4.1.0.tar.bz2
 https://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.2.1.tar.gz"
checksum="a7b39bc69cbf9e25826c5a60ab26477001f7c08d85cec04bc0e29cabed6f3cc9
 eae9326beb4158c386e39a356818031bd28f3124cf915f8c5b1dc4c7a36b4d7c
 feced2d430dd5a97805fa289fed3fc8ff2b094c02d05287fd6133e7f1f0ec926
 17503d2c395dfcf106b622dc142683c1199431d095367c6aacba6eec30340459"
depends="glibc busybox"

# Notes:
# - Why: sgdisk (gptfdisk) and smartctl (smartmontools) are C++, and UNOS
#   shipped no C++ runtime at all. Static-linking the runtime into each binary
#   was the alternative; packaging it keeps packages/README.md's shared-only
#   rule intact and amortises across any future C++ package.
# - We build GCC, but we ship only libstdc++.so and libgcc_s.so. No compiler,
#   no headers, no libsupc++.a. GCC is a means to the runtime here, nothing
#   more; do_install takes exactly two .so trees out of the tree.
# - ABI: host g++ 14.2.1 emits at most GLIBCXX_3.4.33, which GCC 14.2.0's
#   libstdc++ provides. Bumping this package below the host compiler version
#   would break C++ packages at link time -- the assert in do_install guards it.
# - --disable-bootstrap: a 3-stage bootstrap builds the compiler three times to
#   prove it can rebuild itself. We are not shipping the compiler, so stage1 is
#   sufficient and roughly a third of the build time.
# - gmp/mpfr/mpc are build-time only and never packaged. GCC builds them
#   in-tree when their source dirs are present, which is the upstream-supported
#   way and avoids three packages we would otherwise never install.

do_configure() {
	cd "${WRKSRC}"

	# In-tree prerequisites: GCC's build system picks these up automatically
	# when unpacked under the source root with their version suffix stripped.
	for dep in gmp-6.2.1 mpfr-4.1.0 mpc-1.2.1; do
		name=${dep%%-*}
		[ -d "${name}" ] && continue
		case "${dep}" in
			*.gz|mpc-*) tar -xf "${SRCDEST}/${dep}.tar.gz" -C . ;;
			*)          tar -xf "${SRCDEST}/${dep}.tar.bz2" -C . ;;
		esac
		mv "${dep}" "${name}" || die "libstdc++: could not stage ${name}"
	done
	for name in gmp mpfr mpc; do
		[ -d "${name}" ] || die "libstdc++: missing in-tree ${name}"
	done

	# Target libraries ignore --libdir: their location comes from the multilib
	# osdir, which on x86_64 defaults to ../lib64 even with --disable-multilib.
	# UNOS is /usr/lib throughout (glibc is built --libdir=/usr/lib), so
	# retarget m64 the way distros do rather than shipping a stray /usr/lib64.
	sed -i 's|m64=../lib64|m64=../lib|' gcc/config/i386/t-linux64 \
		|| die "libstdc++: could not retarget MULTILIB_OSDIRNAMES"
	grep -q 'm64=../lib$' gcc/config/i386/t-linux64 \
		|| grep -q 'm64=../lib[^6]' gcc/config/i386/t-linux64 \
		|| die "libstdc++: MULTILIB_OSDIRNAMES retarget did not apply"

	# GCC refuses to configure in its own source directory.
	mkdir -p "${WORK}/gccbuild"
	cd "${WORK}/gccbuild"
	"${WRKSRC}/configure" \
		--prefix=/usr \
		--libdir=/usr/lib \
		--disable-bootstrap \
		--enable-languages=c++ \
		--enable-shared \
		--enable-threads=posix \
		--enable-__cxa_atexit \
		--enable-clocale=gnu \
		--disable-multilib \
		--disable-nls \
		--disable-libstdcxx-pch \
		--disable-werror \
		--disable-libsanitizer \
		--disable-libssp \
		--disable-libquadmath \
		--disable-libvtv
}

do_build() {
	cd "${WORK}/gccbuild"
	# Only the two target runtimes. Building `all` would produce a full
	# compiler we then throw away.
	make -j"$(nproc)" all-target-libgcc
	make -j"$(nproc)" all-target-libstdc++-v3
}

do_install() {
	cd "${WORK}/gccbuild"
	make install-target-libgcc      DESTDIR="${DESTDIR}"
	make install-target-libstdc++-v3 DESTDIR="${DESTDIR}"

	# Sysroot seed so gptfdisk/smartmontools link against our runtime.
	make install-target-libgcc       DESTDIR="${SYSROOT}"
	make install-target-libstdc++-v3 DESTDIR="${SYSROOT}"

	# Ship the runtime only. Everything below is compiler-side: static
	# archives, linker scripts, headers and GCC's private lib tree.
	rm -rf "${DESTDIR}/usr/lib/gcc" "${DESTDIR}/usr/include"
	rm -rf "${DESTDIR}/usr/share"
	rm -f  "${DESTDIR}"/usr/lib/*.a "${DESTDIR}"/usr/lib/*.la
	rm -f  "${DESTDIR}"/usr/lib/*.py
	# libstdc++ ships GDB pretty-printers as .py; Python is banned in the
	# rootfs and they are debugger-side anyway.
	find "${DESTDIR}" -name '*-gdb.py' -delete 2>/dev/null || true

	# GCC builds target libs with -g; unstripped libstdc++.so is ~20MB against
	# ~2.5MB stripped, which is not a reasonable thing to put on a switch.
	# --strip-debug (not --strip-unneeded) is deliberate: it drops debug info
	# only and cannot touch the dynamic symbol table the loader resolves.
	for so in "${DESTDIR}"/usr/lib/*.so.*; do
		[ -f "${so}" ] || continue
		strip --strip-debug "${so}" || die "libstdc++: strip failed on ${so}"
	done

	[ -e "${DESTDIR}/usr/lib/libstdc++.so.6" ] || die "libstdc++: no libstdc++.so.6"
	[ -e "${DESTDIR}/usr/lib/libgcc_s.so.1" ]  || die "libstdc++: no libgcc_s.so.1"
	# Catch a silent regression of the MULTILIB_OSDIRNAMES retarget above.
	[ ! -d "${DESTDIR}/usr/lib64" ] || die "libstdc++: stray /usr/lib64 (multilib osdir not retargeted)"

	# ABI floor: our runtime must be at least what the host compiler emits,
	# or every C++ binary we build will fail to resolve symbols on the switch.
	have=$(strings "${DESTDIR}/usr/lib/libstdc++.so.6"* 2>/dev/null \
		| grep -oE 'GLIBCXX_3\.4\.[0-9]+' | sort -V | tail -1)
	need=$(strings /usr/lib/libstdc++.so.6 2>/dev/null \
		| grep -oE 'GLIBCXX_3\.4\.[0-9]+' | sort -V | tail -1)
	[ -n "${have}" ] || die "libstdc++: could not read our GLIBCXX versions"
	if [ -n "${need}" ] && [ "$(printf '%s\n%s\n' "${need}" "${have}" | sort -V | tail -1)" != "${have}" ]; then
		die "libstdc++: ours (${have}) is older than the host compiler needs (${need})"
	fi
	msg "libstdc++: ABI ${have} (host compiler needs ${need:-unknown})"
}
