singe/build-all.sh
2026-09-10 20:17:28 -05:00

227 lines
6.6 KiB
Bash
Executable file

#!/bin/bash
#
# Singe 3
# Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
#
# 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