#!/bin/sh
# provision-static.sh - build the static libraries mouseboard needs.
#
# Usage: ./provision-static.sh
#
# Debian (and friends) ship no static libwayland-client or libxkbcommon, so
# this fetches and builds them (plus libffi) with a musl toolchain into
# musl-sysroot/, ready for `make static`. Run it once per machine; an unchanged
# completed sysroot is reused, while changed inputs trigger a clean rebuild.
#
# Prerequisites (Debian):
#   apt install musl-tools meson ninja-build bison pkgconf curl libexpat1-dev
set -eu

ROOT="$(cd "$(dirname "$0")" && pwd)"
SYSROOT="$ROOT/musl-sysroot"
WORK="$ROOT/musl-build"
HOSTCC="${HOSTCC:-cc}"

# Pick the musl target compiler. An explicit CC always wins. Otherwise, if the
# default cc already targets musl (Alpine, Void-musl) use it directly; on a
# glibc host (Debian, Arch) fall back to the musl-gcc wrapper from musl-tools.
if [ -n "${CC:-}" ]; then
	BASECC="$CC"
elif command -v cc >/dev/null 2>&1 && cc -dumpmachine 2>/dev/null | grep -q musl; then
	BASECC="cc"
else
	BASECC="musl-gcc"
fi

# Pinned versions and checksums. Override a version only together with its sha.
FFI_VER="${FFI_VER:-3.4.6}"
FFI_SHA="${FFI_SHA:-b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e}"
WL_VER="${WL_VER:-1.23.1}"
WL_SHA="${WL_SHA:-864fb2a8399e2d0ec39d56e9d9b753c093775beadc6022ce81f441929a81e5ed}"
XKB_VER="${XKB_VER:-1.7.0}"
XKB_SHA="${XKB_SHA:-65782f0a10a4b455af9c6baab7040e2f537520caa2ec2092805cdfd36863b247}"

command -v "$BASECC" >/dev/null 2>&1 || {
	echo "missing musl compiler: $BASECC" >&2
	echo "  glibc host (Debian, Arch): install musl-tools for musl-gcc" >&2
	echo "  or point CC at your musl compiler: CC=... $0" >&2
	exit 1
}
for t in "$HOSTCC" meson ninja bison curl pkg-config sha256sum \
	tar make nproc sed; do
	command -v "$t" >/dev/null 2>&1 || { echo "missing tool: $t" >&2; exit 1; }
done
JOBS="$(nproc)"

# wayland-scanner is built from source below and parses XML with expat.
pkg-config --exists expat || {
	echo "missing dev library: expat (Debian: libexpat1-dev)" >&2; exit 1; }

BASECC_PATH="$(command -v "$BASECC")"
HOSTCC_PATH="$(command -v "$HOSTCC")"
BASECC_VERSION="$("$BASECC" --version | sed -n '1p')"
HOSTCC_VERSION="$("$HOSTCC" --version | sed -n '1p')"
MULTIARCH="$("$BASECC" -print-multiarch 2>/dev/null || echo x86_64-linux-gnu)"
SCRIPT_SHA="$(sha256sum "$0" | sed 's/ .*//')"
STAMP="$SYSROOT/.provisioned"
INPUTS="recipe=$SCRIPT_SHA
ffi=$FFI_VER $FFI_SHA
wayland=$WL_VER $WL_SHA
xkbcommon=$XKB_VER $XKB_SHA
target-cc=$BASECC_PATH
target-cc-version=$BASECC_VERSION
host-cc=$HOSTCC_PATH
host-cc-version=$HOSTCC_VERSION
multiarch=$MULTIARCH
cflags=${CFLAGS:-}
cppflags=${CPPFLAGS:-}
ldflags=${LDFLAGS:-}"

sysroot_complete() {
	[ -x "$SYSROOT/bin/cc" ] &&
	[ -x "$SYSROOT/bin/wayland-scanner" ] &&
	[ -f "$SYSROOT/lib/libffi.a" ] &&
	[ -f "$SYSROOT/lib/libwayland-client.a" ] &&
	[ -f "$SYSROOT/lib/libxkbcommon.a" ] &&
	[ -f "$SYSROOT/lib/pkgconfig/libffi.pc" ] &&
	[ -f "$SYSROOT/lib/pkgconfig/wayland-client.pc" ] &&
	[ -f "$SYSROOT/lib/pkgconfig/xkbcommon.pc" ] &&
	[ -f "$SYSROOT/include/wayland-client.h" ] &&
	[ -f "$SYSROOT/include/xkbcommon/xkbcommon.h" ]
}

if [ -d "$SYSROOT" ]; then
	OLD_INPUTS=""
	[ ! -f "$STAMP" ] || OLD_INPUTS="$(cat "$STAMP")"
	if [ "$OLD_INPUTS" != "$INPUTS" ] || ! sysroot_complete; then
		echo "static sysroot stale or incomplete; rebuilding $SYSROOT"
		rm -rf "$SYSROOT"
	fi
fi

# Fetch a file if absent, then verify its checksum (fails loud on mismatch).
fetch() {	# url sha
	file="$(basename "$1")"
	[ -f "$file" ] || curl -fsSLO "$1"
	echo "$2  $file" | sha256sum -c - || { rm -f "$file"; exit 1; }
}

# Sysroot libs resolve first while building the deps themselves (wayland
# needs the sysroot's libffi.pc).
export PKG_CONFIG_PATH="$SYSROOT/lib/pkgconfig:$SYSROOT/share/pkgconfig:${PKG_CONFIG_PATH:-}"

mkdir -p "$WORK" "$SYSROOT/bin"

# musl-gcc only exposes musl's libc headers, not the Linux kernel uapi headers
# (linux/*, asm/*) from linux-libc-dev. Wrap it to add them at lowest priority
# so musl's libc headers still win. The wrapper lands in musl-sysroot/bin/cc;
# `make static` compiles with it too. On musl-native distros the extra
# -idirafter flags are harmless.
MCC="$SYSROOT/bin/cc"
printf '#!/bin/sh\nexec "%s" -idirafter /usr/include -idirafter /usr/include/%s "$@"\n' \
	"$BASECC_PATH" "$MULTIARCH" > "$MCC"
chmod +x "$MCC"

cd "$WORK"

# ---- libffi (autotools): wayland's marshalling needs it ----
if [ ! -f "$SYSROOT/lib/pkgconfig/libffi.pc" ]; then
	fetch "https://github.com/libffi/libffi/releases/download/v$FFI_VER/libffi-$FFI_VER.tar.gz" "$FFI_SHA"
	rm -rf "libffi-$FFI_VER"
	tar xf "libffi-$FFI_VER.tar.gz"
	cd "libffi-$FFI_VER"
	./configure CC="$MCC" --prefix="$SYSROOT" --libdir="$SYSROOT/lib" \
	  --disable-shared --enable-static --disable-docs
	make -j"$JOBS"
	make install
	cd "$WORK"
fi

# ---- wayland-scanner (native host tool, pinned to WL_VER) ----
# wayland's library build needs a wayland-scanner at least as new as the
# library, but distros ship older ones (CI had 1.22.0 vs 1.23.1). Build it
# from the pinned source with the host compiler (glibc + host expat) into the
# sysroot, so the version always matches and nothing depends on the host copy.
# The static mouseboard build uses this scanner too (see Makefile).
if [ ! -x "$SYSROOT/bin/wayland-scanner" ]; then
	fetch "https://gitlab.freedesktop.org/wayland/wayland/-/releases/$WL_VER/downloads/wayland-$WL_VER.tar.xz" "$WL_SHA"
	rm -rf "wayland-$WL_VER" wl-scanner-build
	tar xf "wayland-$WL_VER.tar.xz"
	CC="$HOSTCC_PATH" meson setup "wayland-$WL_VER" wl-scanner-build \
	  --prefix="$SYSROOT" --libdir=lib --buildtype=release \
	  -Dscanner=true -Dlibraries=false -Dtests=false \
	  -Ddocumentation=false -Ddtd_validation=false
	ninja -C wl-scanner-build
	ninja -C wl-scanner-build install
fi

# ---- wayland (libraries, static/musl; uses the scanner built above) ----
if [ ! -f "$SYSROOT/lib/pkgconfig/wayland-client.pc" ]; then
	fetch "https://gitlab.freedesktop.org/wayland/wayland/-/releases/$WL_VER/downloads/wayland-$WL_VER.tar.xz" "$WL_SHA"
	rm -rf "wayland-$WL_VER" wl-build
	tar xf "wayland-$WL_VER.tar.xz"
	CC="$MCC" meson setup "wayland-$WL_VER" wl-build \
	  --prefix="$SYSROOT" --libdir=lib -Ddefault_library=static \
	  --buildtype=release \
	  -Dscanner=false -Dlibraries=true -Dtests=false \
	  -Ddocumentation=false -Ddtd_validation=false
	ninja -C wl-build
	ninja -C wl-build install
fi

# ---- libxkbcommon (no x11, registry, tools, docs -> no extra deps) ----
if [ ! -f "$SYSROOT/lib/pkgconfig/xkbcommon.pc" ]; then
	fetch "https://xkbcommon.org/download/libxkbcommon-$XKB_VER.tar.xz" "$XKB_SHA"
	rm -rf "libxkbcommon-$XKB_VER" xkb-build
	tar xf "libxkbcommon-$XKB_VER.tar.xz"
	CC="$MCC" meson setup "libxkbcommon-$XKB_VER" xkb-build \
	  --prefix="$SYSROOT" --libdir=lib -Ddefault_library=static \
	  --buildtype=release \
	  -Denable-x11=false -Denable-docs=false -Denable-xkbregistry=false \
	  -Denable-tools=false -Denable-bash-completion=false
	ninja -C xkb-build
	ninja -C xkb-build install
fi

if ! sysroot_complete; then
	echo "static sysroot incomplete after provisioning" >&2
	exit 1
fi
printf '%s\n' "$INPUTS" >"$STAMP.tmp"
mv "$STAMP.tmp" "$STAMP"

echo
echo "sysroot ready: $SYSROOT"
echo "build the static binary with: make static"
