Crafter.Build/build.sh
Jorijn van der Graaf be81fc098e
feat(ci): drop beta and latest, release signed tags on master
A push to master published the `beta` branch and moved the rolling
`latest` tag and its "Latest master build" prerelease, and a pushed
X.Y.Z tag made the release and published `stable`. The owner wants a
release for every push to master, made by the signed tag that goes with
it, and nothing for a push without one. So `beta`, `latest` and the
branch trigger are gone: the workflow runs for pushed tags, pull
requests and manual runs only, and the `latest` tag and its prerelease
have been deleted from the forge.

`beta` gets no more builds, and its installations would not hear of it:
the release notes ship in stable builds only. The Publish step marks
beta's last build end-of-life, once, with a reason that names stable,
and the next `flatpak update` of a beta installation prints it. The
reason also names the branch, because a non-interactive update prints
only the app's name with it.

Before building, a tag run now checks the tag: it has to be annotated
and signed by the owner as Forgejo verifies it, its commit has to be on
master, its version has to be X.Y.Z and newer than every release the
forge has published, its own included (a later tag whose run still
queues does not count), and the metainfo's newest <release> entry has to
be that version. The tag run checked only the last of these. It reads
the tag from the forge's API, because the checkout fetches a pushed tag
again as a plain ref to its commit, so the job's clone never has the
annotated tag, and Forgejo verifies a signed tag's own signature but
falls back to the commit's for an unsigned one, so the step also
requires that the verdict covers the tag's payload. The metainfo's entry
is read once, as the first <release> element: the Version step takes its
version and renders its notes into release-notes.md with the renderer
the Release notes step had, and stops the run when they come out empty,
so a comment or another attribute order cannot give a release one
entry's version and another's notes. It does so on every run, pull
requests and manual runs included, so a broken entry shows before a
release.

Release runs share a concurrency group, so they queue one behind
another; a pull request still cancels its own outdated run. A failed
release run can be re-run while no newer release exists, since its tag
is still on its commit; a release that an attempt left half made has to
be deleted first. The release step runs without `override`, which would
delete a tag that has moved off the run's commit since the Version step
and tag the old commit instead.

A manual run no longer publishes anything; it used to publish `beta`
and move `latest` from whichever branch it was started on. No step
pushes to git, so the checkout keeps no credentials. git describe in
build.sh, project.cpp and the workflow has no `latest` tag left to
exclude.
2026-11-02 17:17:02 +01:00

126 lines
5.5 KiB
Shell
Executable file

#!/bin/sh
#SPDX-License-Identifier: LGPL-3.0-only
#SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
# Bootstrap: compile crafter-build by hand, without crafter-build. The
# self-host build through project.cpp follows (`./bin/crafter-build`).
set -eu
# The toolchain. CRAFTER_BUILD_LLVM_DIR names an LLVM prefix (the Flatpak
# sets /app); otherwise the tools come from PATH. The same rule the built
# binary applies at run time (Crafter.Build:Toolchain).
if [ -n "${CRAFTER_BUILD_LLVM_DIR:-}" ]; then
CXX="$CRAFTER_BUILD_LLVM_DIR/bin/clang++"
CC="$CRAFTER_BUILD_LLVM_DIR/bin/clang"
AR="$CRAFTER_BUILD_LLVM_DIR/bin/llvm-ar"
LLVM_CONFIG="$CRAFTER_BUILD_LLVM_DIR/bin/llvm-config"
# A prefix other than the system one keeps its libc++ off both the
# linker's search path (clang looks in lib/<triple> and the system dirs,
# not <prefix>/lib) and the loader's; -L and an rpath cover the two.
if [ "$CRAFTER_BUILD_LLVM_DIR" != /usr ]; then
LIBCXX_LINK_FLAGS="-L$CRAFTER_BUILD_LLVM_DIR/lib -Wl,-rpath,$CRAFTER_BUILD_LLVM_DIR/lib"
else
LIBCXX_LINK_FLAGS=""
fi
if [ -f "$CRAFTER_BUILD_LLVM_DIR/share/libc++/v1/std.cppm" ]; then
STD_CPPM="$CRAFTER_BUILD_LLVM_DIR/share/libc++/v1/std.cppm"
else
STD_CPPM=/usr/share/libc++/v1/std.cppm
fi
else
CXX=clang++
CC=clang
AR=llvm-ar
LLVM_CONFIG=llvm-config
LIBCXX_LINK_FLAGS=""
STD_CPPM=/usr/share/libc++/v1/std.cppm
fi
# The version baked into the binary: the environment (CI sets it), else the
# nearest tag, else a development marker.
VERSION="${CRAFTER_BUILD_VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo 0.0.0-dev)}"
mkdir -p build bin lib share/crafter-build share/crafter-build/wasi-runtime
cp wasi-runtime/runtime.js share/crafter-build/wasi-runtime/
cp wasi-runtime/index.html.in share/crafter-build/wasi-runtime/
# Partition order is the import order: every unit is listed before the
# units that import it, and the umbrella last.
PARTITIONS="Toolchain Shader Platform Interface Implementation External Agents Clang Test Lint Script Progress Asset"
cp interfaces/Crafter.Build.cppm share/crafter-build/
for p in $PARTITIONS; do
cp "interfaces/Crafter.Build-$p.cppm" share/crafter-build/
done
if [ ! -d build/glslang/.git ]; then
git clone --depth 1 https://github.com/KhronosGroup/glslang.git build/glslang
fi
current_dir=$(pwd)
cd build/glslang
cmake -B build \
-DCMAKE_C_COMPILER="$CC" \
-DCMAKE_CXX_COMPILER="$CXX" \
-DCMAKE_CXX_FLAGS="-stdlib=libc++" \
-DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ $LIBCXX_LINK_FLAGS" \
-DCMAKE_SHARED_LINKER_FLAGS="-stdlib=libc++ $LIBCXX_LINK_FLAGS" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$current_dir/build" \
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$current_dir/build" \
-DENABLE_OPT=OFF
cmake --build build --config Release
cd ../../
MARCH="${CRAFTER_BUILD_MARCH:-native}"
MTUNE="${CRAFTER_BUILD_MTUNE:-native}"
# The bootstrap targets the toolchain's own triple: x86_64-pc-linux-gnu on
# glibc distros, x86_64-unknown-linux-gnu for the bundled LLVM,
# x86_64-alpine-linux-musl on Alpine. Override with CRAFTER_BUILD_TARGET.
TARGET="${CRAFTER_BUILD_TARGET:-$("$CXX" -print-target-triple)}"
TARGET_DEFINE="CRAFTER_BUILD_CONFIGURATION_TARGET_$(printf '%s' "$TARGET" | tr - _)"
# Crafter.Build-Lint includes libclang's clang-c/Index.h. glibc distros keep
# it in /usr/include; other prefixes keep it under their own include dir, so
# ask llvm-config and add the dir when it is not already on the default path
# (never add /usr/include itself: that breaks libc++'s #include_next).
LLVM_INCDIR="$("$LLVM_CONFIG" --includedir 2>/dev/null || true)"
case "$LLVM_INCDIR" in ""|/usr/include) LLVM_INCFLAG="" ;; *) LLVM_INCFLAG="-I$LLVM_INCDIR" ;; esac
common_options="--target=$TARGET -stdlib=libc++ -I./build/glslang $LLVM_INCFLAG -std=c++26 -O3 -march=$MARCH -mtune=$MTUNE -fprebuilt-module-path=./build -D $TARGET_DEFINE -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE -D CRAFTER_BUILD_VERSION=\"$VERSION\" -c"
"$CXX" --target="$TARGET" -std=c++26 -stdlib=libc++ -O3 -march="$MARCH" -mtune="$MTUNE" -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile "$STD_CPPM" -o ./build/std.pcm
# Asset partition: bootstrap compiles it WITHOUT CRAFTER_BUILD_HAS_ASSET so
# CompressAsset takes the stub branch and the impl unit doesn't try to
# `import Crafter.Asset` (no Crafter.Asset PCM exists at this stage — the
# self-host pass that follows wires in the real dep).
OBJECTS=""
for p in $PARTITIONS; do
"$CXX" $common_options -fmodule-output "interfaces/Crafter.Build-$p.cppm" -o "./build/Crafter.Build-$p.o"
OBJECTS="$OBJECTS ./build/Crafter.Build-$p.o"
done
"$CXX" $common_options -fmodule-output interfaces/Crafter.Build.cppm -o ./build/Crafter.Build.o
OBJECTS="$OBJECTS ./build/Crafter.Build.o"
for p in $PARTITIONS; do
"$CXX" $common_options "./implementations/Crafter.Build-$p.cpp" -o "./build/Crafter.Build-${p}_impl.o"
OBJECTS="$OBJECTS ./build/Crafter.Build-${p}_impl.o"
done
"$CXX" $common_options ./implementations/main.cpp -o ./build/main.o
# A fresh archive each time: `ar rcs` into an existing one keeps members a
# previous layout left behind.
rm -f ./lib/libcrafter-build.a
"$AR" rcs ./lib/libcrafter-build.a $OBJECTS
"$CXX" --target="$TARGET" -std=c++26 -stdlib=libc++ -O3 -march="$MARCH" -mtune="$MTUNE" -fuse-ld=lld \
-Wl,--export-dynamic $LIBCXX_LINK_FLAGS \
-L./build \
$OBJECTS \
./build/main.o \
-lSPIRV -lGenericCodeGen -lglslang -lOSDependent -lMachineIndependent -lglslang-default-resource-limits \
-ldl \
-o ./bin/crafter-build