#!/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"