#!/bin/bash # # Singe 3 # Copyright (C) 2006-2026 Scott Duensing # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public Licens # as published by the Free Software Foundation; either version 3 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # # Builds Singe and every vendored library for one platform, or all of them. The work is done # by the CMake superbuild (cmake/Superbuild.cmake, one preset per platform in # CMakePresets.json); this script installs the host packages and calls it: # # ./build-all.sh every supported platform # ./build-all.sh linux x86_64 one platform # # Anything cmake --build accepts can follow the platform, so a single library can be redone: # # ./build-all.sh linux x86_64 --target rebuild-ffmpeg G_BUILDDIR=.builddir G_INSTALL="$(dirname "$0")/INSTALL" # The one copy of the host package list. Everything else that names these packages derives from it. # Grouped by what wants them, because the SDL3 half is long and a bare list gives no clue why a # package is here: SDL3 compiles a backend whenever it finds the headers, so a package missing from # this list does not fail the build, it quietly drops a backend from the binary. G_HOSTPACKAGES=( # Toolchain build-essential cmake git-lfs pkg-config perl nasm llvm autoconf automake libtool # Content the build generates: embedded images, the menu video, the LuaSec table, the manual imagemagick ffmpeg lua5.4 asciidoctor ruby-asciidoctor-pdf # Video decode and display libva-dev libvdpau-dev libdrm-dev libgl-dev libegl-dev libgles-dev libgbm-dev # SDL3 audio backends libasound2-dev libpulse-dev libpipewire-0.3-dev libjack-jackd2-dev libsndio-dev # SDL3 device hotplug, desktop integration and input methods libudev-dev libdbus-1-dev libibus-1.0-dev libxkbcommon-dev # SDL3 X11 video backend and the extensions it uses libx11-dev libxext-dev libxfixes-dev libxi-dev libxcursor-dev libxrandr-dev libxss-dev libxtst-dev # SDL3 Wayland video backend libwayland-dev wayland-protocols libdecor-0-dev ) G_INSTALLLEAD="Host packages (what build-all.sh installs):" G_INSTALLWIDTH=72 function buildAll() { local OS=$1 local ARCH=$2 shift 2 # Every platform builds with the zig the build fetches itself; the Pi preset also fetches its # platform packages and macOS needs the Apple SDK tarball (see INSTALL). # "macos universal" builds both macOS presets and joins them with llvm-lipo. if [[ "${OS}" == "macos" && "${ARCH}" == "universal" ]]; then buildAll macos aarch64 "$@" buildAll macos x86_64 "$@" cmake -P cmake/zig/macosUniversal.cmake return fi cmake --preset ${OS}-${ARCH} cmake --build --preset ${OS}-${ARCH} "$@" } # assets/ holds the artwork, the font and the menu video in Git LFS. A clone made without git-lfs # leaves pointer stubs -- a hundred and thirty bytes of text where a video should be -- and the # resource generators embed them without complaining, so the failure only shows up at run time as a # menu with no background. One sentinel is enough to tell the two apart. function checkoutHydrated() { local SENTINEL="$(dirname "$0")/assets/180503_01_PurpleGrid.mp4" # A sentinel that is not there at all is not a hydrated checkout either, and saying so beats # passing quietly because the path was wrong. [[ -f "${SENTINEL}" ]] || return 1 ! head -c 64 "${SENTINEL}" | grep -q "git-lfs.github.com/spec" } # The package list as INSTALL wants to read it: one sentence, wrapped, ending in a full stop. function installParagraph() { local LINE="${G_INSTALLLEAD}" local SEP=" " local WORD for WORD in "${G_HOSTPACKAGES[@]}"; do if (( ${#LINE} + ${#SEP} + ${#WORD} > G_INSTALLWIDTH )); then printf '%s\n' "${LINE}" LINE="${WORD}" else LINE="${LINE}${SEP}${WORD}" fi SEP=" " done printf '%s.\n' "${LINE}" } # What INSTALL says today: from the lead line to the first line that ends the sentence. function installCurrent() { awk -v lead="${G_INSTALLLEAD}" ' index($0, lead) == 1 { found = 1 } found { print; if (/\.$/) exit } ' "${G_INSTALL}" } function installMatches() { # A checkout without INSTALL is not worth nagging about; there is nothing to disagree with. [[ ! -f "${G_INSTALL}" ]] || [[ "$(installParagraph)" == "$(installCurrent)" ]] } # Writes the paragraph back into INSTALL, replacing whatever sentence is there now. function installSync() { local TEMP TEMP=$(mktemp) awk -v lead="${G_INSTALLLEAD}" -v replacement="$(installParagraph)" ' index($0, lead) == 1 && !done { print replacement skip = 1 } skip { if (/\.$/) { skip = 0; done = 1 } next } { print } ' "${G_INSTALL}" > "${TEMP}" mv "${TEMP}" "${G_INSTALL}" } # -e = stop script on errors # -u = stop script on undefined variable # -o pipefail = stop pipeline if any step fails set -euo pipefail case "${1:-}" in --packages) printf '%s\n' "${G_HOSTPACKAGES[@]}" exit 0 ;; --sync-install) installSync echo "INSTALL rewritten from build-all.sh." exit 0 ;; esac mkdir -p ${G_BUILDDIR} # Host packages the build needs on a Debian based system. INSTALL prints the same list for a # reader who has not run anything yet, so it is written from this one: --sync-install rewrites # that paragraph and every build checks it, which is why there is no second list to keep in step. if ! installMatches; then echo "warning: INSTALL's host package list no longer matches this script." echo " Run ./build-all.sh --sync-install to write it from here." fi sudo apt-get install -y "${G_HOSTPACKAGES[@]}" if ! checkoutHydrated; then echo "error: assets/ holds git-lfs pointer files, not the real artwork, font and video." echo " The build would embed those stubs and produce a broken binary." echo " Fix the checkout with: git lfs install && git lfs pull" exit 1 fi if [[ $# -ge 2 ]]; then buildAll "$@" 2>&1 | tee ${G_BUILDDIR}/$1-$2.log else buildAll linux x86_64 2>&1 | tee ${G_BUILDDIR}/linux-x86_64.log buildAll macos universal 2>&1 | tee ${G_BUILDDIR}/macos-universal.log buildAll linux aarch64 2>&1 | tee ${G_BUILDDIR}/linux-aarch64.log buildAll windows x86_64 2>&1 | tee ${G_BUILDDIR}/windows-x86_64.log fi