A build system for C++26 modules. Project descriptions are C++: a project.cpp constructs a Configuration and returns it, and crafter-build compiles, loads and executes it. The Flatpak carries its own LLVM, so every machine builds with the same clang, lld, libc++ and WASI sysroot.
New
wasm32-wasip1 unit and the std module are compiled with WebAssembly exception handling in its standardized form (exnref), against the exception-enabled libc++ the sysroot ships under lib/<triple>/eh/, and -lunwind and -lsetjmp are linked automatically, so throw and catch, libc++'s own throws, setjmp and longjmp all work. The Flatpak's WASI sysroot is built with those libraries. A page from EnableWasiBrowserRuntime probes for the feature and names what an older browser lacks instead of failing with a CompileError.cfg.scripts; crafter-build run <name> [args...] runs it with everything after the name handed over untouched, a bare run lists the scripts, and the callback's return value is the exit status. ScriptContext gives it the project, the project directory, the arguments and Run(command), which runs a shell command with the terminal attached. Crafter::ShellQuote is exported. Only the scripts of the project crafter-build was pointed at can be run, never a dependency's.AGENTS.consumer.md beside its project.cpp; crafter-build and crafter-build test keep its dependencies' notes in a marked section of a consumer's AGENTS.md, when the consumer has one, and never create the file. cfg.dependencyNotes picks the dependencies: Transitive (the default), Direct or None, which also removes the section.EnableWasiBrowserRuntime, exit and _Exit end the program without running any of its code on the way out, no destructor and no catch (...), with the status in window.crafter_wasi.exitCode. A program that hands the thread to the browser for good and goes on serving calls imports crafter_runtime.park instead, which never returns and leaves main's locals valid for the calls that follow.x86_64-w64-mingw32-gcc on PATH, through CRAFTER_MINGW_DIR naming another prefix, and inside the Flatpak through the host's /usr when it has mingw-w64. Its std module comes from the toolchain's own libstdc++.modules.json, and a toolchain that is not there is a build error saying where it looked, not std::terminate.Fixes
#pragma crafter dependency tools, with the same args, got the tool's copy back: host tooling with -march=native and -fPIC, whose BMI clang then refused. Host tooling and everything it loads are now kept apart in the project cache.crafter-build format clean formats the rules matching "clean"; it used to delete bin/ and build/ and exit 0.-- ends crafter-build's options, as the help has advertised: every later word is an operand of the verb. It used to be forwarded as a project arg and change the variant hash, and the words after it were still read as options.ProjectDependency() works from a static initialiser of a project file; it used to find nothing there and terminate the process.Breaking
-fno-exceptions, -flto in compileFlags or linkFlags, and shared libraries, each with a message that says why; there is no switch back to building without exceptions. The result runs on engines with exnref: Chrome 137, Firefox 131, Safari 18.4 and wasmtime 47 or newer (the bundled one qualifies; an older wasmtime needs -W exceptions=y). An exception that escapes main ends the program as an uncaught WebAssembly exception without calling std::terminate, so catch it in main to report it. The flags are part of the variant hash, so the first wasm build after updating starts in a fresh directory.GitProject or LocalProject without .args now receives the args its loading project.cpp was given, so --target=, --march=, --debug and the project's own flags reach every sibling down the chain; releases up to 2.2.0 forwarded nothing. Set .args = {} to keep the old behaviour, and give every sibling the same list: a project that two paths load with different lists now stops the build. The default is read when the spec is made, so make it inside CrafterBuildProject; a spec made at namespace scope forwards nothing. A #pragma crafter dependency still gets none of the project's args.A Shader entry can carry defines, spelled like the compiler's -D (NAME is defined to 1, NAME=VALUE to VALUE), and a variant that names its output, so one source compiles into several outputs: ui.comp.glsl with variant sets becomes ui.comp.sets.spv. A configuration in which two entries share an output name is refused with both named, as are a malformed define and a variant with a path separator.
Compiles with a sysroot now use the sysroot's own libc++ headers instead of the bundled toolchain's, which fixes cross builds against a musl sysroot that include a standard header textually and stops glibc sysroot builds from silently compiling against the wrong libc++ release. The project.so stamp hashes every part of the host build, so a changed declaration recompiles it. The vendored json.hpp is gone.
The bundled toolchain is now LLVM 23.1.2. Build outputs are kept per compiler, so the first build after updating recompiles each project into a fresh directory and nothing has to be cleaned by hand.
Cross builds pick the std module source that matches the sysroot's libc++ release. The source for libc++ 22 ships beside the bundled 23 for sysroots that carry libc++ without one, such as Alpine, and a sysroot nothing matches fails with one line naming both releases. crafter-build --version prints the libc++ release in use.
First release distributed as a Flatpak, with a bundled LLVM 22 toolchain and WASI sysroot. Windows host support is removed; crafter-build gains --version and resolves its compiler from one place.