English | 简体中文
绝大多数工程只需要 mcpp.toml。当你需要构建期逻辑——探测主机、生成源码、依据环境
决定某个编译开关——就在工程根目录放一个 build.mcpp。它是 mcpp 版的 Zig build.zig
/ Cargo build.rs,但用 C++ 编写:不引入第二种语言,而且 mcpp 自己吃自己的狗粮。
mcpp 用你的工具链编译 build.mcpp,并在主构建之前运行它。程序通过向 stdout 打印
mcpp: 指令与 mcpp 通信,这些指令会增补本次构建。
// build.mcpp
#include <cstdio>
#include <fstream>
int main() {
// 生成一份源码,主构建会编译 + 链接它。
std::ofstream("src/generated.cpp") << "const char* banner() { return \"hi\"; }\n";
std::puts("mcpp:generated=src/generated.cpp"); // 加入构建
std::puts("mcpp:cxxflag=-DHAVE_BANNER=1"); // 为所有 C++ TU 定义宏
if (std::getenv("USE_FAST")) std::puts("mcpp:cxxflag=-DFAST_PATH=1");
std::puts("mcpp:rerun-if-env-changed=USE_FAST"); // USE_FAST 变化时重跑我
return 0;
}mcpp build # 编译 + 运行 build.mcpp,然后构建工程把这些打印到 stdout(每行一条)。任何不以 mcpp: 开头的行都会被忽略,因此你可以
自由打印诊断日志。
| 指令 | 作用 |
|---|---|
mcpp:cxxflag=<flag> |
给 C++ 编译追加 <flag> |
mcpp:cflag=<flag> |
给 C 编译追加 <flag> |
mcpp:link-lib=<name> |
链接 -l<name> |
mcpp:link-search=<dir> |
增加库搜索目录(-L;相对路径按工程根目录解析) |
mcpp:cfg=<name> |
为 C 与 C++ 同时定义 -D<name> |
mcpp:generated=<path> |
把生成的源码(相对工程根目录)加入构建 |
mcpp:rerun-if-changed=<path> |
该文件变化时重跑 build.mcpp |
mcpp:rerun-if-env-changed=<VAR> |
该环境变量变化时重跑 build.mcpp |
程序请求构建边(开关、库、源码),它不能新增注册表依赖——请把依赖图保持在
mcpp.toml 里声明式管理(包括平台条件依赖 [target.windows.dependencies])。
build.mcpp 用于叶子决策:开关、代码生成、链接需求。
除了打印裸字符串,你还可以把 build.mcpp 写成模块优先——import mcpp;,无
#include、无 import std;。mcpp 模块内置在 mcpp 二进制里(因此永远和你这版 mcpp
的协议匹配),按需编译;它的函数只是 emit 上面那些指令:
// build.mcpp
import mcpp;
int main() {
mcpp::cxxflag("-DHAVE_BANNER=1");
mcpp::link_lib("m"); // -lm
mcpp::link_search("vendor/lib"); // -L…
mcpp::define("HAVE_FEATURE"); // == mcpp:cfg= → -DHAVE_FEATURE
mcpp::generated("src/gen.cpp");
mcpp::rerun_if_changed("config.h");
mcpp::rerun_if_env_changed("USE_FAST");
}| 函数 | emit |
|---|---|
mcpp::cxxflag(s) / mcpp::cflag(s) |
mcpp:cxxflag= / mcpp:cflag= |
mcpp::link_lib(s) / mcpp::link_search(s) |
mcpp:link-lib= / mcpp:link-search= |
mcpp::define(s) |
mcpp:cfg=(即 -D<s>) |
mcpp::generated(p) |
mcpp:generated= |
mcpp::rerun_if_changed(p) / mcpp::rerun_if_env_changed(v) |
对应的 rerun-* 指令 |
如果 build.mcpp 还需要写生成文件,混入一个文本 #include <fstream> 即可——这没问题,
只有 import std; 是不必要的。上面的裸 stdout 协议仍是底层基底;import mcpp; 是其上的
类型化层。
mcpp 不会每次构建都重跑 build.mcpp。它会缓存程序产出的指令,只有当它依赖的东西
变化时才重跑:
build.mcpp源码本身,- 工具链,
- 任何用
rerun-if-changed声明的文件, - 任何用
rerun-if-env-changed声明的环境变量, - (或某个
generated产物丢失了)。
所以请声明你的输入:如果程序读了 config.h 或 USE_FAST 变量,就分别 emit
mcpp:rerun-if-changed=config.h / mcpp:rerun-if-env-changed=USE_FAST。这用一份明确的
输入/输出契约取代了过去「进程退出码为 0 就当成功」的猜测——让增量构建保持正确。
无变化时你会看到 build.mcpp up to date (cached);否则是 build.mcpp compiling /
running。
- 在主机上运行。
build.mcpp用主机工具链编译并运行。在交叉构建 (mcpp build --target <triple>)下目前会跳过并给出警告(主机工具链交叉是计划中的 后续项)。要按目标平台门控依赖,请改用[target.'cfg(...)']表——它们按解析后的目标 求值。参见 05 - mcpp.toml 工程文件指南。 - 当前工作目录是工程根目录,因此相对路径(
src/generated.cpp)会落在你预期的位置。 build.mcpp非零退出会中止构建并打印其输出。