A project.cpp could describe how a project is built, tested and linted, but not the chores around it: packaging, installing, publishing. Those stayed shell lines in a README or in someone's history. Configuration gains `scripts`, a plain list like `defines`. A Script is a name and a C++ callback, as a lint rule is; there is no script language and no config file. The callback gets a ScriptContext (the project, the project file's directory, the arguments) and returns the exit status. ctx.Run runs a shell command with the terminal attached and returns its status. It is only stored when project.cpp loads, which happens for every verb and for every project that depends on this one. `crafter-build run <name> [args...]` runs one, a bare `run` lists them. Only the scripts of the project crafter-build was pointed at are runnable, never a dependency's. Everything after the script's name is handed to the script untouched. The two branches that do this sit ahead of every other branch of the parse loop, --version and --help included. Below the verbs, `run package clean` would delete build/, and any other word would land in the project's args and so in the variant hash, moving the build to another directory. Options before the name keep their meaning, so project args go there. The driver is its own partition, :Script. It refuses a project with no scripts, a name that is empty or starts with '-' (it could never be typed), a name declared twice (running either would be a guess) and an unknown name, each with exit 1 and without running anything. A script that throws exits 1. A return value outside 0..255 becomes 1: an exit status is eight bits, and a raw wait status such as 256 would otherwise wrap to success. Two helpers in :Platform: - RunCommandAttached runs a command with inherited stdio. Every helper so far captured through popen, which is right for a compile step and useless for a command that runs for half an hour or asks a question. It reads the wait status through DecodeWaitStatus, which RunCommandChecked and RunCommandWithTimeout now share. - ShellQuote is exported. It existed twice, as ShellQuote in :External and ShellQuoteSh in :Test, with the same behaviour; both are gone. A script that puts a path into a command needs it too. Not included: a string shorthand for one-command scripts (a one-line lambda covers it) and a ctx.Build() helper (Build() is already exported). A new partition and new exported symbols: run ./build.sh. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
127 lines
5.6 KiB
Shell
Executable file
127 lines
5.6 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 from the
|
|
# tag), else the nearest tag, else a development marker. `latest` is the
|
|
# rolling CI tag and never a version.
|
|
VERSION="${CRAFTER_BUILD_VERSION:-$(git describe --tags --always --dirty --exclude=latest 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 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
|