126 lines
5.8 KiB
Markdown
126 lines
5.8 KiB
Markdown
# Crafter Build
|
|
|
|
A C++26 modules build system. Project descriptions are written in C++ — no JSON, no Lua, no embedded DSL. You write a `project.cpp` that constructs a `Configuration` and returns it; Crafter Build compiles, loads, and executes it.
|
|
|
|
## Status
|
|
|
|
- **Linux**: working end-to-end, packageable as a distro package. Verified on Arch Linux via [PKGBUILD](PKGBUILD) in a fresh container.
|
|
- **Windows**: bootstrap builds and runs; cross-EXE/DLL symbol-resolution work in progress.
|
|
|
|
## Quick start (Linux)
|
|
|
|
Bootstrap requires `clang`, `cmake`, `git`, `lld`, and `libc++`.
|
|
|
|
```bash
|
|
./build.sh # produces bin/crafter-build
|
|
CRAFTER_BUILD_HOME=$PWD/build ./bin/crafter-build # rebuild via project.cpp
|
|
```
|
|
|
|
For distro-packaged installs, `crafter-build` finds its modules at `<prefix>/share/crafter-build/` automatically — no env var required.
|
|
|
|
To build the system as a distro package on Arch:
|
|
|
|
```bash
|
|
makepkg -si # uses CRAFTER_BUILD_MARCH=x86-64-v3 by default for portability
|
|
```
|
|
|
|
## Writing a project.cpp
|
|
|
|
Crafter Build loads a `project.cpp` from the current directory. The file exports one function that returns a populated `Configuration`:
|
|
|
|
```cpp
|
|
import std;
|
|
import Crafter.Build;
|
|
namespace fs = std::filesystem;
|
|
using namespace Crafter;
|
|
|
|
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
|
Configuration cfg;
|
|
cfg.path = "./";
|
|
cfg.name = "myapp";
|
|
cfg.outputName = "myapp";
|
|
cfg.target = "x86_64-pc-linux-gnu";
|
|
cfg.type = ConfigurationType::Executable;
|
|
|
|
std::array<fs::path, 1> ifaces = { "interfaces/Hello" };
|
|
std::array<fs::path, 2> impls = { "implementations/Hello", "implementations/main" };
|
|
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
|
|
|
return cfg;
|
|
}
|
|
```
|
|
|
|
Run `crafter-build` in that directory; outputs land at `bin/myapp-<target>-<march>/myapp` and intermediates at `build/myapp-<target>-<march>/`.
|
|
|
|
## Dependencies
|
|
|
|
Three kinds, all fetched/built incrementally:
|
|
|
|
**Cross-project Crafter sub-projects** — point `cfg.dependencies` at another `Configuration*`. Cross-project module imports (`import OtherProject;`) are tracked per-import: only the modules that actually import a changed dep rebuild.
|
|
|
|
**External git+cmake deps** — for things like glslang. Declare in `cfg.externalDependencies`:
|
|
|
|
```cpp
|
|
ExternalDependency& glslang = cfg.externalDependencies.emplace_back();
|
|
glslang.name = "glslang";
|
|
glslang.source.url = "https://github.com/KhronosGroup/glslang.git";
|
|
glslang.source.branch = "main";
|
|
glslang.builder = ExternalBuilder::CMake;
|
|
glslang.options = { "-DENABLE_OPT=OFF" };
|
|
glslang.includeDirs = { "" };
|
|
glslang.libs = { "SPIRV", "glslang", "OSDependent" };
|
|
```
|
|
|
|
Clones live at `~/.cache/crafter.build/external/<name>-<hash>/`, keyed by `(url, branch, commit, options)` so different projects sharing the same dep with the same spec reuse the clone and cmake build, while projects with diverging specs each get their own cache entry. The clone is resilient to partial-clone recovery and CMake reconfigure is triggered automatically on `options` changes.
|
|
|
|
**Header-only git deps** — same as cmake deps but `builder = ExternalBuilder::None`. Just clones and propagates `-I` flags.
|
|
|
|
## Install layout
|
|
|
|
```
|
|
<prefix>/bin/crafter-build # the executable
|
|
<prefix>/share/crafter-build/*.cppm # module sources (rebuilt on user machine on first run)
|
|
~/.cache/crafter.build/<target>-<march>/ # std.pcm + Crafter.Build*.pcm, built locally
|
|
```
|
|
|
|
Sources ship instead of PCMs because libc++ ABI varies between machines — the user's machine builds its own PCMs against its own libc++ on first run (~6-8s one-time cost), making the install resilient to libc++ version differences across distros.
|
|
|
|
## Build incrementality
|
|
|
|
Per-import precise tracking for both within-project and cross-project module dependencies:
|
|
|
|
- Touch `lib/Hello.cppm` → only consumers of `Hello` rebuild.
|
|
- Touch `lib/Other.cppm` → only consumers of `Other` rebuild.
|
|
- External CMake dep produces fresh `.a` files → whole project rebuilds (deliberately coarse — cmake-dep changes are rare).
|
|
|
|
Diamond deps (`A → {B, C}; B → X; C → X`) build `X` exactly once via a `std::shared_future<BuildResult>` cache.
|
|
|
|
## Architecture
|
|
|
|
- **Modules**: `Crafter.Build:Shader` / `:Platform` / `:Interface` / `:Implementation` / `:External` / `:Clang` partitions, re-exported by the `Crafter.Build` umbrella.
|
|
- **Build process**: parallel — interface PCMs, implementation `.o` files, external dep clones, dep-config recursive builds, and shader compilations all spawn threads, sync at well-defined join points.
|
|
- **`LoadProject`** compiles `project.cpp` to `<project>/build/project.so` and `dlopen`s it. The host exe is linked with `-Wl,--export-dynamic` so the project's undefined symbols resolve against the running binary.
|
|
|
|
## Compatibility / portability
|
|
|
|
- Linux: x86_64. Tested on Arch in a clean container.
|
|
- Windows: bootstrap works (build.cmd produces working exe); the `LoadProject` path needs the in-progress DLL+launcher refactor to support cross-EXE/DLL symbol resolution.
|
|
|
|
The `CRAFTER_BUILD_MARCH` and `CRAFTER_BUILD_MTUNE` env vars override the default `-march=native` / `-mtune=native` for distro CI builds:
|
|
|
|
```bash
|
|
CRAFTER_BUILD_MARCH=x86-64-v3 CRAFTER_BUILD_MTUNE=generic ./build.sh
|
|
```
|
|
|
|
## Roadmap
|
|
|
|
- [ ] Windows DLL+launcher refactor (in progress)
|
|
- [ ] Configuration inheritance (`extends:`-style merging from base configs)
|
|
- [ ] Test runner (dlopen-based test loading)
|
|
- [ ] Target-triple auto-detection (`clang -print-target-triple`)
|
|
- [ ] Prefer system glslang when available, fall back to git+cmake
|
|
- [ ] Order-preserving deduplication of `BuildResult.libs` (current `unordered_set` doesn't preserve link order — only matters for cyclic static libs)
|
|
|
|
## License
|
|
|
|
LGPL-3.0-only. See [LICENSE](LICENSE).
|