You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(build.mcpp): typed import mcpp; build module bundled in the binary (v0.0.81) (#193)
* feat(build.mcpp): typed import mcpp; build module bundled in the binary (v0.0.81)
build.mcpp can be written modules-first — import mcpp; (no #include, no import
std;) — calling a typed API (mcpp::cxxflag/define/link_lib/generated/…) that emits
the same mcpp: wire protocol the engine already parses.
Architecture (see .agents/docs/2026-06-30-build-mcpp-module-library-design.md):
the helper IS part of the engine's ABI (it speaks this mcpp's protocol), so it
ships WITH the engine, not as a versioned package — the Zig std.Build model, not
Cargo's build-dep model. Embed the module SOURCE (constexpr string), not a BMI
(BMIs are compiler-version-locked); compile on demand against the resolved host
toolchain into target/.build-mcpp/ (GCC: -fmodules gcm.cache; Clang: --precompile
.pcm). I/O is C-level so the module needs no import std.
Gated on actual use: mcpp only builds/links the module when build.mcpp contains
'import mcpp' — a #include-based program compiles byte-identically to before (zero
blast radius). Uses the 0.0.79 capture_exec cwd to let GCC find gcm.cache/.
- src/build/build_program.cppm: kMcppModuleSource + build_mcpp_module + use-gating
- tests/e2e/92_build_mcpp_import.sh (GCC path); docs/07-build-mcpp.md (+zh)
- design doc; version -> 0.0.81. Clang path covered by the mcpp-index build-mcpp
member's workspace job on macOS/Windows.
* fix(build.mcpp): placeholder the embedded module decl so the regex scanner doesn't misread it
The Windows self-host build uses the default regex module scanner, which read the
'export module mcpp;' line inside kMcppModuleSource (a raw string literal) as
build_program.cppm exporting a second module -> 'file already exports module
... cannot export mcpp'. Use a @module@ placeholder in the embedded source,
substituted with 'export module' when written. No behavior change; the generated
mcpp.cppm is identical.
* docs: record the regex-scanner gotcha in the build-module design
The helper's job is to emit the *exact*`mcpp:` wire protocol **this** mcpp parses.
11
+
So it is **not a third-party library — it is part of the engine's ABI.** Any design
12
+
that lets the helper drift from the engine's protocol version (a separately
13
+
released package, a pinned dependency) introduces skew. This single fact rules out
14
+
most of the "obvious" options and points straight at "ship it with the engine."
15
+
16
+
## Options considered
17
+
18
+
| Option | What | Verdict |
19
+
|---|---|---|
20
+
|**A. Ship a prebuilt BMI** (`mcpp.gcm`/`.pcm` in the release) | precompiled module interface | ✗ BMIs are **not portable** across compiler vendor/version/flags (GCC gcm is locked to the exact GCC build). Would need a combinatorial matrix of BMIs. Fragile. |
21
+
|**B. Header-only** (`#include <mcpp_build.h>`) | a shipped header | ✗ contradicts modules-first ("no headers"). (Most portable, but off-brand; kept as a mental fallback only.) |
22
+
|**C. Cargo model** — helper is a normal `[build-dependencies]` package in the index |`build.mcpp` depends on a published `mcpp` package, resolved + compiled like any dep | △ composable, but adds a **resolution step** for a leaf script and reintroduces **version skew** (the package version vs the engine's protocol). Cargo's `build-rs` crate works this way — but Cargo's protocol is far more stable than a young tool's. |
23
+
|**D. Zig model** — helper is part of the tool, always present, version-matched | embed the module **source** in the binary; compile on demand against the host toolchain | ✓ **chosen.** Zig's `std.Build` ships with the compiler; the build API and the engine are one artifact, so they can never disagree. |
24
+
25
+
### Why "embed the **source**, not the BMI"
26
+
27
+
Source is the only **toolchain-portable** form. A BMI is compiler-version-locked;
28
+
source compiles against *whatever* host toolchain resolved for this build (gcc on
29
+
Linux, clang on macOS/Windows), at whatever version, with the same sysroot flags
30
+
the build already computes. So one embedded `constexpr std::string_view` adapts to
31
+
every toolchain — no matrix, no skew. This is the crux of **适配 + 稳定**.
32
+
33
+
## Chosen design
34
+
35
+
```
36
+
mcpp binary
37
+
└── constexpr std::string_view kMcppModuleSource // the `mcpp` module, embedded
0 commit comments