Skip to content

Commit 184059b

Browse files
committed
feat(build): make C++ standard first-class
1 parent 035e983 commit 184059b

18 files changed

Lines changed: 669 additions & 78 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6+
## [0.0.42] — 2026-06-01
7+
8+
### 新增
9+
10+
-`[package].standard` 打通为一等 C++ 标准配置,默认仍为 `c++23`,
11+
并支持 `c++26` / `c++2c` 等写法。
12+
13+
### 修复
14+
15+
- 编译 flags、`compile_commands.json`、fingerprint 与 `import std` 标准库
16+
BMI 预构建命令现在使用同一个 active C++ 标准。
17+
- `std.gcm` / `std.pcm` cache 增加元数据校验,只有 compiler、stdlib、target、
18+
standard、source 与 build command 匹配时才复用。
19+
- `build.cxxflags` 回归附加 C++ flags 语义,若写入 `-std=` 会提示迁移到
20+
`[package].standard`
21+
622
## [0.0.41] — 2026-06-01
723

824
### 修复

docs/05-mcpp-toml.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,21 @@ lib-root 约定:主模块接口默认在 `src/mylib.cppm`(包名的最后一段)
4141
[package]
4242
name = "myapp" # 包名(必填)
4343
version = "0.1.0" # 语义化版本(必填)
44+
standard = "c++23" # C++ 标准(默认 c++23; 可设 c++26)
4445
description = "My awesome app" # 简介(可选)
4546
license = "MIT" # 许可证(可选)
4647
authors = ["Alice", "Bob"] # 作者列表(可选)
4748
repo = "https://github.com/user/myapp" # 仓库地址(可选)
4849
```
4950

51+
`standard` 是 C++ 语言标准的一等配置。推荐值:
52+
53+
- `c++23`:默认值,适合当前模块化默认模板。
54+
- `c++26`:需要 C++26 语言特性时使用。
55+
- `c++2c`:兼容别名,解析后归一为 `c++26`
56+
- `gnu++23` / `gnu++26`:需要 GNU dialect 时使用,会进入 fingerprint 和 std BMI cache key。
57+
- `c++latest`:跟随当前 mcpp 支持的最新标准,适合本地试验,不推荐要求可复现的发布包使用。
58+
5059
### 2.2 `[targets.<name>]` — 构建目标
5160

5261
```toml
@@ -72,11 +81,20 @@ sources = ["src/**/*.cppm", "src/**/*.cpp"] # 源文件 glob(默认: src/*
7281
include_dirs = ["include", "third_party/include"] # 头文件搜索路径
7382
c_standard = "c11" # C 源文件的标准(默认 c11)
7483
cflags = ["-DFOO=1"] # 额外 C 编译参数
75-
cxxflags = ["-DBAR=2"] # 额外 C++ 编译参数
84+
cxxflags = ["-DBAR=2"] # 额外 C++ 编译参数(不要放 -std=...)
7685
ldflags = ["-lfoo"] # 额外链接参数
7786
static_stdlib = true # 静态链接 libstdc++(默认 true)
7887
```
7988

89+
C++ 标准不要通过 `build.cxxflags = ["-std=..."]` 配置。请使用:
90+
91+
```toml
92+
[package]
93+
standard = "c++26"
94+
```
95+
96+
mcpp 会把同一个标准用于普通 C++ 编译、模块扫描、`compile_commands.json``import std` 的标准库 BMI 构建。
97+
8098
**glob 排除**(`!` 前缀,mcpp 0.0.4+):
8199

82100
```toml
@@ -285,9 +303,20 @@ mcpp build --target x86_64-linux-musl
285303
| 源文件 | `src/**/*.{cppm,cpp,cc,c}` | 自动递归扫描 |
286304
| 入口 | `src/main.cpp` | 有这个文件就推断为 `bin` 目标 |
287305
| 库根 | `src/<pkg-tail>.cppm` | 可用 `[lib].path` 覆盖 |
288-
| 标准 | `c++23` | C++23 模块是一等公民 |
306+
| C++ 标准 | `c++23` | `[package].standard` 配置; 支持 `c++26` / `c++2c` |
289307
| C 标准 | `c11` | `.c` 文件自动走 C 编译器 |
290308
| 静态 stdlib | `true` | 便携二进制 |
291309
| 头文件 | `include/`(如果存在) | 自动加到 `-I` |
292310
| 测试 | `tests/**/*.cpp` | `mcpp test` 自动发现 |
293311
| 依赖命名空间 | `mcpp`(默认) | 平铺写法走默认 ns |
312+
313+
### 4.1 旧 `[language]` 兼容层
314+
315+
旧配置仍可读取:
316+
317+
```toml
318+
[language]
319+
standard = "c++26"
320+
```
321+
322+
新项目请使用 `[package].standard`。如果两个位置都出现,`[package].standard` 是权威配置。

mcpp.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcpp"
3-
version = "0.0.41"
3+
version = "0.0.42"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

src/build/flags.cppm

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,7 @@ CompileFlags compute_flags(const BuildPlan& plan) {
201201
// Opt level (musl ICE workaround)
202202
std::string opt_flag = isMuslTc ? " -Og" : " -O2";
203203

204-
// User flags
205-
auto join = [](const std::vector<std::string>& v) {
206-
std::string s;
207-
for (auto& f : v) {
208-
s += ' ';
209-
s += f;
210-
}
211-
return s;
212-
};
213-
std::string user_cxxflags = join(plan.manifest.buildConfig.cxxflags);
214-
std::string user_cflags = join(plan.manifest.buildConfig.cflags);
204+
// User link flags
215205
std::string user_ldflags;
216206
for (auto const& flag : plan.manifest.buildConfig.ldflags) {
217207
user_ldflags += ' ';
@@ -251,11 +241,13 @@ CompileFlags compute_flags(const BuildPlan& plan) {
251241
prebuilt_module_flag = std::format(" -fprebuilt-module-path={}",
252242
escape_path(plan.outputDir / traits.bmiDir));
253243
}
254-
f.cxx = std::format("-std=c++23{}{}{}{}{}{}{}{}{}{}", module_flag, std_module_flag,
244+
std::string cxx_std_flag =
245+
plan.cppStandardFlag.empty() ? std::string("-std=c++23") : plan.cppStandardFlag;
246+
f.cxx = std::format("{}{}{}{}{}{}{}{}{}", cxx_std_flag, module_flag, std_module_flag,
255247
std_compat_module_flag, prebuilt_module_flag,
256-
opt_flag, pic_flag, compile_toolchain_flags, b_flag, include_flags, user_cxxflags);
257-
f.cc = std::format("-std={}{}{}{}{}{}{}", c_std, opt_flag, pic_flag, compile_toolchain_flags,
258-
b_flag, include_flags, user_cflags);
248+
opt_flag, pic_flag, compile_toolchain_flags, b_flag, include_flags);
249+
f.cc = std::format("-std={}{}{}{}{}{}", c_std, opt_flag, pic_flag, compile_toolchain_flags,
250+
b_flag, include_flags);
259251

260252
// Link flags
261253
f.staticStdlib = plan.manifest.buildConfig.staticStdlib;

src/build/plan.cppm

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ struct BuildPlan {
4040
mcpp::manifest::Manifest manifest;
4141
mcpp::toolchain::Toolchain toolchain;
4242
mcpp::toolchain::Fingerprint fingerprint;
43+
std::string cppStandard = "c++23";
44+
std::string cppStandardFlag = "-std=c++23";
4345

4446
std::filesystem::path projectRoot; // where mcpp.toml lives
4547
std::filesystem::path outputDir; // target/<triple>/<fp>/
@@ -153,6 +155,17 @@ std::vector<std::string> shared_library_link_flags(const mcpp::manifest::Target&
153155
return flags;
154156
}
155157

158+
std::vector<std::filesystem::path>
159+
local_include_dirs_for_manifest(const std::filesystem::path& root,
160+
const mcpp::manifest::Manifest& manifest)
161+
{
162+
std::vector<std::filesystem::path> dirs;
163+
for (auto const& inc : manifest.buildConfig.includeDirs) {
164+
dirs.push_back(inc.is_absolute() ? inc : root / inc);
165+
}
166+
return dirs;
167+
}
168+
156169
} // namespace
157170

158171
BuildPlan make_plan(const mcpp::manifest::Manifest& manifest,
@@ -170,6 +183,10 @@ BuildPlan make_plan(const mcpp::manifest::Manifest& manifest,
170183
plan.manifest = manifest;
171184
plan.toolchain = tc;
172185
plan.fingerprint = fp;
186+
if (auto stdCfg = mcpp::manifest::normalize_cpp_standard(manifest.package.standard)) {
187+
plan.cppStandard = stdCfg->canonical;
188+
plan.cppStandardFlag = stdCfg->flag;
189+
}
173190
plan.projectRoot = projectRoot;
174191
plan.outputDir = outputDir;
175192
plan.stdBmiPath = stdBmiPath;
@@ -391,6 +408,9 @@ BuildPlan make_plan(const mcpp::manifest::Manifest& manifest,
391408
main_cu.source = *lu.entryMain;
392409
main_cu.object = std::filesystem::path("obj") / object_filename_for(*lu.entryMain);
393410
main_cu.packageName = qualified_package_name(manifest);
411+
main_cu.localIncludeDirs = local_include_dirs_for_manifest(projectRoot, manifest);
412+
main_cu.packageCflags = manifest.buildConfig.cflags;
413+
main_cu.packageCxxflags = manifest.buildConfig.cxxflags;
394414

395415
// We didn't scan main.cpp earlier (it's not in scanner output unless globbed in).
396416
// Best-effort: scan its imports here.

src/cli.cppm

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ struct CliInstallProgress : mcpp::fetcher::EventHandler {
583583
// Compose a stable canonical compile-flags string for fingerprinting.
584584
std::string canonical_compile_flags(const mcpp::manifest::Manifest& m) {
585585
std::string s;
586-
s += "-std="; s += m.language.standard;
586+
s += "-std="; s += m.package.standard;
587587
s += " -fmodules";
588588
if (!m.buildConfig.cStandard.empty()) {
589589
s += " c_standard=";
@@ -2467,7 +2467,7 @@ prepare_build(bool print_fingerprint,
24672467
auto tmp = std::filesystem::temp_directory_path()
24682468
/ std::format("mcpp_p1689_{}", std::random_device{}());
24692469
std::filesystem::create_directories(tmp);
2470-
return mcpp::modgraph::scan_packages_p1689(packages, *tc, tmp);
2470+
return mcpp::modgraph::scan_packages_p1689(packages, *tc, tmp, m->cppStandard.flag);
24712471
}
24722472
return mcpp::modgraph::scan_packages(packages);
24732473
}();
@@ -2504,7 +2504,7 @@ prepare_build(bool print_fingerprint,
25042504
// Compute fingerprint (no lockfile in M1 → empty hash)
25052505
mcpp::toolchain::FingerprintInputs fpi;
25062506
fpi.toolchain = *tc;
2507-
fpi.cppStandard = m->language.standard;
2507+
fpi.cppStandard = m->package.standard;
25082508
fpi.compileFlags = canonical_compile_flags(*m)
25092509
+ canonical_package_build_metadata(packages);
25102510
fpi.dependencyLockHash = ""; // M2
@@ -2517,7 +2517,8 @@ prepare_build(bool print_fingerprint,
25172517
std::filesystem::path stdCompatBmiPath;
25182518
std::filesystem::path stdCompatObjectPath;
25192519
if (needsStdModule) {
2520-
auto sm = mcpp::toolchain::ensure_built(*tc, fp.hex);
2520+
auto sm = mcpp::toolchain::ensure_built(
2521+
*tc, fp.hex, m->package.standard, m->cppStandard.flag);
25212522
if (!sm) return std::unexpected(sm.error().message);
25222523
stdBmiPath = sm->bmiPath;
25232524
stdObjectPath = sm->objectPath;

0 commit comments

Comments
 (0)