An agent working on a crafter-build project reads its AGENTS.md, and learns nothing there about the libraries the project builds against unless someone copied that in by hand. This repository shows the cost: the code style section of its AGENTS.md restates what Crafter.LintRules enforces, and goes stale whenever the rules change. A library now ships those notes itself, as AGENTS.consumer.md beside its project.cpp; nothing in its project.cpp changes. When a consumer has an AGENTS.md beside its own project.cpp, `crafter-build` and `crafter-build test` keep a section in it, between two marker comments, holding its dependencies' notes. The section is appended the first time and rewritten in place after that. The file is written only when the section changes, and an AGENTS.md is never created. lint, format, run and clean leave it alone. The update runs after loading, which has just fetched every dependency, and before compiling, so a build that fails still has the notes. cfg.dependencyNotes picks the dependencies. Transitive, the default, follows `dependencies` all the way down from the project's own configurations, its tests and its `#pragma crafter dependency` tooling; Direct stops at the first loaded project on each path; None removes the section. A dependency's own pragma tooling never counts: it is about how that dependency is built, not how it is used, and being no `dependencies` edge it is never reached. Nor are a dependency's tests: nothing builds them for the consumer, and their configurations can point at a local their project file has since moved out of. The walk has to know which Configurations belong to another project, and where that project lives, and cfg.path cannot say: only the Configuration GitProject and LocalProject return is rebased, so a dependency's internal configurations keep "./", which resolves to the consumer. The loader records it instead, LoadedProjectOf beside the project cache, and keeps what the root's pragmas resolved to past the frame LoadProject pops, PragmaDependencies. Neither is exported. The markers count only as whole lines outside fenced code blocks, so documentation can quote them. Unbalanced markers and a document ending inside a code block leave the file alone with a warning: guessing which part of someone's file is generated would overwrite their text, and missing the section would add another one on every build. A note that would break the section when it is read back, through a marker line of its own or a code block it never closes, is left out with a warning. The file is replaced through a temporary renamed onto the symlink's target, so a linked AGENTS.md stays a link and a write cut short leaves the old file rather than part of the new one. The code is a new partition, Crafter.Build:Agents, listed in build.sh, CrafterBuildModules, project.cpp and the umbrella. Co-Authored-By: Claude Opus 5.5 <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 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
|