aboutsummaryrefslogtreecommitdiff
path: root/provision-static.sh
blob: c2125ee2fb5f252acff98f21eb8af4e0a49a6156 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/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"