Build FEX for the runtime

The x86-64 Steam client experiment needs FEX inside the guest, and neither
Arch Linux ARM nor FEX publishes a Linux arm64 binary. Built in the same
Arch Linux ARM container as gamescope, without thunks, tuned for generic
armv8-a, with the runtime's own unsquashfs alongside to unpack FEX's x86
rootfs image.
This commit is contained in:
The412Banner committed 2026-10-02 13:51:26 -04:00
1 parent ed2160c137
commit 61ea96a9e8
2 files changed
+84

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
name: Build FEX (x86-64 Steam client experiment)
# FEX built for the runtime in an Arch Linux ARM container on an arm64 runner (tools/fex), for the
# opt-in x86-64 Steam client (bannerlator-steam-x64). Published as a release asset that script
# fetches; the runtime image and the default arm64 client are untouched.
on:
push:
paths:
- 'tools/fex/**'
- '.github/workflows/build-fex.yml'
workflow_dispatch:
inputs:
tag:
description: 'Release tag to publish as (e.g. fex-2609-r1); empty = artifact only'
required: false
default: ''
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-24.04-arm
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: Build in Arch Linux ARM
run: |
set -euo pipefail
docker run --rm -v "$PWD:/work" -w /work menci/archlinuxarm:base-devel bash tools/fex/build-in-arch.sh
sudo chown -R "$(id -u):$(id -g)" .
- uses: actions/upload-artifact@v4
with:
name: fex
path: |
fex.tzst
fex.tzst.sha256
fex-needed.txt
fex-files.txt
- name: Publish
if: github.event_name == 'workflow_dispatch' && inputs.tag != ''
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh release create "${{ inputs.tag }}" --latest=false --prerelease=false --title "${{ inputs.tag }}" --notes "FEX for the runtime (tools/fex/build-in-arch.sh); fetched by bannerlator-steam-x64. sha256 $(cut -d' ' -f1 fex.tzst.sha256)" fex.tzst fex.tzst.sha256 || gh release upload "${{ inputs.tag }}" fex.tzst fex.tzst.sha256 --clobber
# A component the apk fetches by tag, never something users install: it must not
# become the repo's Latest release or appear as a pre-release, also when re-uploaded.
gh release edit "${{ inputs.tag }}" --latest=false --prerelease=false
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/bash
# Runs INSIDE an Arch Linux ARM container (menci/archlinuxarm:base-devel) on an arm64 runner.
# Builds FEX for the runtime (Arch Linux ARM ships no package and FEX publishes no Linux
# binaries), plus the runtime's own unsquashfs to unpack FEX's x86 rootfs image, and packs both
# as fex.tzst (usr/local/...) for bannerlator-steam-x64 to stage. No thunks: they need an x86
# cross toolchain, and the x86-64 Steam client only needs FEX to run, not host GPU drivers.
set -euxo pipefail
VERSION=${FEX_VERSION:-FEX-2609}
WORK=/work
cd "$WORK"
# Same pacman workarounds as tools/gamescope/build-in-arch.sh.
grep -q '^DisableSandbox' /etc/pacman.conf || sed -i 's/^\[options\]/[options]\nDisableSandbox/' /etc/pacman.conf
{ for m in https://ca.us.mirror.archlinuxarm.org https://fl.us.mirror.archlinuxarm.org https://de3.mirror.archlinuxarm.org https://nl.mirror.archlinuxarm.org; do echo "Server = $m/\$arch/\$repo"; done; cat /etc/pacman.d/mirrorlist; } > /etc/pacman.d/mirrorlist.new
mv /etc/pacman.d/mirrorlist.new /etc/pacman.d/mirrorlist
pacman -Syu --noconfirm --needed git zstd binutils cmake ninja clang lld llvm python squashfs-tools file
rm -rf fex-src out && git clone -q --depth 1 --branch "$VERSION" --recurse-submodules --shallow-submodules https://github.com/FEX-Emu/FEX.git fex-src
# TUNE_CPU none: the default (native) would tune for the runner's Neoverse cores, not the device.
cmake -S fex-src -B fex-build -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUSE_LINKER=lld \
-DTUNE_CPU=none -DTUNE_ARCH=armv8-a \
-DBUILD_TESTING=OFF -DBUILD_THUNKS=OFF -DBUILD_FEXCONFIG=OFF -DENABLE_ASSERTIONS=OFF \
-DENABLE_CCACHE=OFF -DENABLE_OFFLINE_TELEMETRY=OFF
ninja -C fex-build
DESTDIR="$WORK/out" ninja -C fex-build install
install -Dm755 /usr/bin/unsquashfs out/usr/local/bin/unsquashfs
# Nothing outside the runtime's own libraries: every needed soname must resolve in a stock guest.
for f in out/usr/local/bin/*; do
file "$f" | grep -q ELF || continue
readelf -d "$f" | awk '/NEEDED/ { gsub(/[\[\]]/, "", $5); print $5 }'
done | sort -u | tee fex-needed.txt
(cd out && find . -type f -o -type l | sort) | tee fex-files.txt
tar -C out -c . | zstd -19 -T0 -o fex.tzst
sha256sum fex.tzst | tee fex.tzst.sha256