Skip to content

Commit d727a08

Browse files
committed
feat(bench): cmake and xmake now build mcpp itself; move foreign build files under bench/
—— 真实工程那条臂终于有基准了,外加一处被自己实测推翻的结论 **cmake 现在能构建 mcpp。** 之前 cmake 只能建合成 fixture,而真正有意义的负载是 mcpp 自己:138 个接口单元、57k 行、每一个都 `import std;`。没有这份 CMakeLists, 「以 cmake 为基准」在真实工程上根本无从谈起。 冷构建(mcpp 源码,gcc@16.1.0,release,同一编译器二进制): mcpp 2026.8.12.1 80.0s 0.85x cmake 4.0.2+ninja 94.0s 1.00x (基准) xmake v3.0.7 91.6s 0.97x **注意这和 fixture 上的 0.26x 相差极远** —— 合成负载上的四倍优势在真实工程上只剩 15%。 **构建文件挪到 bench/projects/mcpp/。** mcpp 由 mcpp 构建,仓库根上再放一份 CMakeLists 和 xmake.lua 是每个贡献者都要学会忽略的东西。为此给 harness 加了 `Job::buildfile_dir` 与 `--buildfiles DIR`:cmake 用 `-S`、xmake 用 `-P` 指向它, mcpp 仍读工程自己的 manifest。另一个选项是运行期把它们拷进被测树, 但那会往用户仓库里写东西,而这个 harness 明确拒绝这么做。 踩到的两个真问题: * FILE_SET 要求文件位于 base 目录下,而 cmdline 依赖在工程外 ⇒ 单独一个 file set。 * **`add_compile_options()` 到不了 CMake 自己生成的 `std` 模块目标** ⇒ std 用默认 libc 头、mcpp 单元用 `--sysroot` 的头,构建死在 `_IO_FILE` 类型冲突上, 而报错既不点名那个 flag 也不点名那个目标。改用 `CMAKE_CXX_FLAGS`。 **⚠️ 纠正:bazel 能构建 `import std;`,我先前写的「没有等价物」是错的。** bazel 的 modmap 生成器确实会报 `Module not found: std`,但 libc++ 把 std 模块 以**普通源码**形式发布,可以当作任意接口单元来建 —— 实测在 bazel 9.2.0 上 构建并运行成功(配方记在 MODULE.bazel 里)。真正决定 bazel 不进这张表的是别的: 它的模块只能配 clang(解析不了 GCC 的 P1689),而这张表是 gcc 的, 放进来就违反「同一编译器二进制」这条不变式 —— 它属于另一张 clang 基准的表。 **冷构建性能分析(.agents/docs,附录 A)。** `bench --analyze`: 关键路径 79.73s = makespan 的 **100%**,32 线程上平均并行度仅 3.94 —— 加核与分布式全部无效。关键链 26 跳,`mcpp.build.prepare` 单文件 16.1s 占 20%。 其中我先用 `-fmodule-only` 判定「codegen 只占 1%,提前释放没空间」,**这是错的**: GCC 的 `-fmodule-only` 不跳过后端,只是不写目标文件。正确判据是三步 —— BMI 何时**写完**(轮询到大小稳定)、是否与成品**逐字节相同**、以及**下游能否用它编译**。 三步全过:`prepare` 的 BMI 在 2.50s / 16.20s = **15%** 处即完成且可用。 关键链最重的 8 个模块采样,中位约 **22%** —— 下游在等的 78% 是它不需要的代码生成。 据此头寸为 **80s → 25–35s(2.3–3.2×)**,实施形状与三个已知坑一并记录。
1 parent eb166a8 commit d727a08

11 files changed

Lines changed: 394 additions & 11 deletions

File tree

.agents/docs/2026-08-12-cold-build-optimization-plan.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,102 @@ codegen 占全部工作量的 **77%**。`bmi-equal` 让 BMI 稳定之后,`.o`
233233
## 8. 与其他构建系统的对照(同一台机器、同一编译器)
234234

235235
`bench/results/`。要点:xmake**同样的图形状**下也是延迟瓶颈(它同样走 GCC 单阶段),所以 A 不是「追平 xmake」,而是**两者都还没做的事**
236+
237+
---
238+
239+
# 附录:2026-08-13 复测 —— 优化 A 的依据被重新确立,并纠正一处错误推理
240+
241+
上文写 A(BMI 提前释放)时,依据是一次原型测量。这次在 **mcpp 自身 80s 冷构建**
242+
重新逐条量过,结论是 **A 成立,而且比原来写的更硬**;但中间我先得出过一个**相反且错误**
243+
的结论,过程值得记下来。
244+
245+
## A.1 现状:100% 延迟受限
246+
247+
`bench --analyze` 于 mcpp 的 release 构建目录:
248+
249+
```
250+
edges : 426
251+
makespan : 79.79 s
252+
work (sum dur) : 314.08 s
253+
avg parallelism: 3.94 x (of 32 hw threads)
254+
critical path : 79.73 s = 100% of makespan
255+
```
256+
257+
**关键路径就是墙钟本身。** 32 个硬件线程上平均并行度只有 3.94 —— 加核、加机器、
258+
分布式编译全部无效。关键链 26 跳,几乎全是 `cxx_module`,其中
259+
`mcpp.build.prepare` 单个模块 **16.1s**,占整个构建的 20%。
260+
261+
## A.2 ⚠️ 错误推理:用 `-fmodule-only` 判定「codegen 占多少」
262+
263+
第一反应是量「只产 BMI」要多久:
264+
265+
```
266+
prepare BMI-only 16.18s full 16.27s → 99%
267+
cli BMI-only 5.50s full 5.59s → 98%
268+
plan BMI-only 5.36s full 5.43s → 99%
269+
```
270+
271+
据此我一度判定 **A 不成立**:BMI 几乎就是全部成本,代码生成只有 1%,提前释放没有空间。
272+
273+
**这是错的。** GCC 的 `-fmodule-only` 并不跳过后端,它跑完整条流水线、只是不写目标文件。
274+
用它测「BMI 什么时候好」等于什么都没测。
275+
276+
## A.3 正确判据:BMI 文件何时**写完**,以及下游能否用
277+
278+
三步,缺一不可:
279+
280+
1. **何时出现** —— 轮询 `.gcm`:`prepare` 的 BMI 在 **2.31s / 16.19s = 14%** 处出现。
281+
但「出现」不等于「写完」(GCC 早创建、可能持续写)。
282+
2. **何时写完** —— 轮询到大小连续 150ms 不变。快照 785488 字节,与编译结束后的成品
283+
**逐字节相同**
284+
3. **是否可用** —— 把早期快照放回 `gcm.cache/`,编译一个真实下游导入者
285+
(`mcpp.build.execute`):**exit 0**
286+
287+
采样关键链上最重的 8 个模块:
288+
289+
| 模块 | full | BMI 写完 | 占比 |
290+
|---|---|---|---|
291+
| mcpp.build.prepare | 16.20s | 2.50s | **15%** |
292+
| mcpp.cli | 5.67s | 2.22s | 39% |
293+
| mcpp.build.plan | 5.47s | 1.07s | 20% |
294+
| mcpp.build.compile_commands | 4.74s | 1.03s | 22% |
295+
| mcpp.build.execute | 4.52s | 1.19s | 26% |
296+
| mcpp.libs.toml | 2.28s | 0.53s | 23% |
297+
| mcpp.modgraph.scanner | 3.23s | 0.66s | 20% |
298+
| mcpp.build.ninja | 3.65s | 1.00s | 27% |
299+
300+
**中位约 22%。下游在等的 78% 是它根本不需要的代码生成。**
301+
302+
## A.4 头寸
303+
304+
关键链 24 个模块节点合计 ~74.7s。若在 BMI 写完即解锁:
305+
`74.7 × 0.22 ≈ 16.4s` + `obj/main.o` 4.78s + link 0.18s ≈ **21s**
306+
此后构建转为吞吐受限,下限是 `work / 线程数 = 314 / 32 ≈ 9.8s`,按 60–70% 并行效率
307+
落在 15–20s。**综合预期 80s → 25–35s(2.3–3.2×)。**
308+
309+
## A.5 实施形状(未实施)
310+
311+
ninja 认为一条边完成 = 进程退出,所以必须让「BMI 好了」成为一个可观测事件:
312+
313+
* **信号**:GCC`-fmodule-mapper`(P1184)在 BMI 落盘时发 `MODULE-COMPILED`
314+
这是设计好的机制,不需要轮询文件大小(轮询只适合做上面这种一次性测量)。
315+
* **边的形状**:`cxx_module` 改为跑一个 mcpp 助手,它代管 mapper 协议,收到
316+
`MODULE-COMPILED`**把余下的 codegen 甩到后台并退出 0**
317+
* **收口**:`cxx_link` 前置一条 `await-objects` 边,等所有后台 codegen 结束。
318+
链接本来就在最后,目标文件是并行完成的,所以这条边通常不阻塞。
319+
320+
⚠️ 三个已知坑:
321+
322+
1. **甩到后台的子进程会继承 ninja 的管道** —— 上一次原型就栽在这里:BMI 边的耗时
323+
被记成整条编译的耗时,数字变成 78.99s,看起来像「这个想法不成立」。
324+
子进程的 stdio 必须重定向到文件。
325+
2. **失败会迟到** —— 后台 codegen 失败时,`cxx_module` 边已经报成功了。
326+
`await-objects` 必须收集并复现每个失败,否则会变成链接期的一堆未定义符号。
327+
3. **作业槽会超订** —— ninja 以为边结束了,后台进程仍在吃 CPU。这在当前
328+
3.94× 的并行度下是**想要**的,但在 `--jobs` 很大时需要重新标定。
329+
330+
## A.6 顺带:两条不需要改引擎的路
331+
332+
* **`mcpp.build.prepare` 一个文件 16.2s,占 20%。** 拆开它直接缩短关键链,
333+
且不引入任何调度复杂度。
334+
* **换编译器。** clang 在同类工程上整体快约 2.4×(此前测量),而关键链的形状不变。

bench/projects/mcpp/BUILD.bazel

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# See MODULE.bazel: this cannot build mcpp today. `import std;` has no bazel
2+
# equivalent, and bazel will not glob sources from outside its workspace.
3+
#
4+
# The shape a working version would take is kept here so the gap is legible:
5+
#
6+
# cc_binary(
7+
# name = "mcpp",
8+
# srcs = ["src/main.cpp"],
9+
# module_interfaces = glob(["src/**/*.cppm"]),
10+
# includes = ["src/libs/json"],
11+
# copts = ["-std=c++23"],
12+
# # ...plus whatever declares `import std;`, which does not exist yet.
13+
# )
14+
#
15+
# built with:
16+
# bazel build //:mcpp --experimental_cpp_modules --features=cpp_modules --force_pic

bench/projects/mcpp/CMakeLists.txt

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# CMake build description for mcpp — a like-for-like counterpart to mcpp.toml
2+
# and to the xmake.lua beside it.
3+
#
4+
# WHY THIS FILE EXISTS. The build-engine benchmark in bench/ uses cmake as its
5+
# performance baseline, and until now cmake could only build the synthetic
6+
# fixture. The interesting workload is mcpp itself: 139 module interface units,
7+
# 57k lines, every one of them `import std;`. Without this file the real-project
8+
# arm had no baseline to be measured against.
9+
#
10+
# FAIRNESS CONTRACT — all five must hold or the comparison means nothing:
11+
# 1. same compiler binary — the harness passes -DCMAKE_CXX_COMPILER, and the
12+
# payload's binutils + sysroot are added below
13+
# 2. same language flags — -std=c++23, -O2 in release
14+
# 3. same source set — src/**.cppm + src/main.cpp + the pinned
15+
# mcpplibs.cmdline units
16+
# 4. same link output kind — one binary, -static-libstdc++
17+
# 5. same standard library — `import std;`, not a header shim
18+
#
19+
# Usage (benchmark):
20+
# cmake -G Ninja -S bench/projects/mcpp -B build-cmake \
21+
# -DCMAKE_BUILD_TYPE=Release \
22+
# -DCMAKE_CXX_COMPILER=$HOME/.mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
23+
# cmake --build build-cmake
24+
25+
cmake_minimum_required(VERSION 3.30)
26+
27+
# `import std;` is still behind an experimental gate whose key changes with the
28+
# CMake version — this is the CMake 4.0 key. Set BEFORE project(), because the
29+
# compiler-support probe that reads it runs during project().
30+
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457")
31+
32+
project(mcpp CXX)
33+
34+
set(CMAKE_CXX_STANDARD 23)
35+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
36+
set(CMAKE_CXX_EXTENSIONS OFF)
37+
# Every mcpp module says `import std;`. This asks CMake to build the standard
38+
# library module from the compiler's own libstdc++.modules.json, which the
39+
# hermetic gcc payload ships.
40+
set(CMAKE_CXX_MODULE_STD 1)
41+
42+
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
43+
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
44+
endif()
45+
46+
# ---------------------------------------------------------------------------
47+
# The hermetic payload.
48+
#
49+
# mcpp always passes an explicit -B<binutils> and --sysroot; a bare g++ from the
50+
# payload otherwise falls back to PATH for `as`/`ld` and picks up whatever shim
51+
# is there — on a machine with xlings installed, a stale one. The two arms must
52+
# drive an identical process tree, so reproduce the full triple here rather than
53+
# hoping the environment matches.
54+
#
55+
# -B and --sysroot must reach BOTH compile and link: the driver spawns `as` from
56+
# it at compile time and `ld` from it at link time. Adding it on one side only
57+
# silently falls through to PATH.
58+
# ---------------------------------------------------------------------------
59+
if(DEFINED ENV{MCPP_HOME})
60+
set(MCPP_HOME "$ENV{MCPP_HOME}")
61+
else()
62+
set(MCPP_HOME "$ENV{HOME}/.mcpp")
63+
endif()
64+
# This file lives in bench/projects/mcpp/, so the tree it builds is three up.
65+
# Resolved to an absolute path once, because a FILE_SET's base directory and a
66+
# relative glob disagree about what "here" means.
67+
get_filename_component(MCPP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOLUTE)
68+
if(NOT EXISTS "${MCPP_ROOT}/mcpp.toml")
69+
message(FATAL_ERROR "expected mcpp's tree at ${MCPP_ROOT} (no mcpp.toml there)")
70+
endif()
71+
72+
set(MCPP_XPKGS "${MCPP_HOME}/registry/data/xpkgs")
73+
set(MCPP_SYSROOT "${MCPP_HOME}/registry/subos/default")
74+
75+
file(GLOB MCPP_BINUTILS_DIRS "${MCPP_XPKGS}/xim-x-binutils/*")
76+
# CMAKE_CXX_FLAGS, not add_compile_options(): CMake generates the `std` module
77+
# target ITSELF, and directory-scope options do not reach it. Without this the
78+
# std module compiles against whatever libc headers the compiler defaults to
79+
# while every mcpp unit compiles against the sysroot, and the build dies on a
80+
# type that exists in both:
81+
#
82+
# error: conflicting type for imported declaration 'char _IO_FILE::_unused2 [20]'
83+
# .../xlings/.../glibc-2.39/include/bits/types/struct_FILE.h:98
84+
# note: existing declaration 'char _IO_FILE::_unused2 [8]'
85+
# .../mcpp/registry/subos/default/usr/include/bits/types/struct_FILE.h:109
86+
#
87+
# Two glibcs in one link, and the error names neither the flag nor the target
88+
# that is wrong.
89+
if(MCPP_BINUTILS_DIRS)
90+
list(SORT MCPP_BINUTILS_DIRS)
91+
list(GET MCPP_BINUTILS_DIRS -1 MCPP_BINUTILS)
92+
string(APPEND CMAKE_CXX_FLAGS " -B${MCPP_BINUTILS}/bin")
93+
string(APPEND CMAKE_EXE_LINKER_FLAGS " -B${MCPP_BINUTILS}/bin")
94+
endif()
95+
if(IS_DIRECTORY "${MCPP_SYSROOT}")
96+
string(APPEND CMAKE_CXX_FLAGS " --sysroot=${MCPP_SYSROOT}")
97+
string(APPEND CMAKE_EXE_LINKER_FLAGS " --sysroot=${MCPP_SYSROOT}")
98+
endif()
99+
100+
# ---------------------------------------------------------------------------
101+
# Source set — mcpp.toml's inferred glob `src/**/*.{cppm,cpp}`. mcpp infers
102+
# kind=bin from src/main.cpp; CMake needs it spelled out.
103+
#
104+
# GLOB, not a hand-written list: the two arms must compile the same files even
105+
# as the tree changes, and a list that drifts silently measures two different
106+
# projects. CONFIGURE_DEPENDS re-globs on build so an added module is not missed.
107+
# ---------------------------------------------------------------------------
108+
file(GLOB_RECURSE MCPP_MODULES CONFIGURE_DEPENDS "${MCPP_ROOT}/src/*.cppm")
109+
list(LENGTH MCPP_MODULES MCPP_MODULE_COUNT)
110+
if(MCPP_MODULE_COUNT EQUAL 0)
111+
message(FATAL_ERROR "no module interface units found under src/ — refusing to "
112+
"build a project that is not mcpp")
113+
endif()
114+
115+
# mcpp.toml pins `mcpplibs.cmdline = "0.0.1"` EXACTLY. Newer versions are often
116+
# also unpacked in the registry, so pin rather than take the newest: otherwise
117+
# the two arms are not compiling the same code.
118+
#
119+
# mcpp stages prebuilt objects for this dependency out of its global build cache
120+
# and cmake has no such cache, so cmake compiles the 3 units from source. That is
121+
# a small handicap on cmake's cold build, and it is declared in the benchmark
122+
# report rather than hidden.
123+
set(MCPP_CMDLINE_VERSION "0.0.1")
124+
set(MCPP_CMDLINE_SRC
125+
"${MCPP_XPKGS}/mcpplibs-x-cmdline/${MCPP_CMDLINE_VERSION}/cmdline-${MCPP_CMDLINE_VERSION}/src")
126+
if(IS_DIRECTORY "${MCPP_CMDLINE_SRC}")
127+
file(GLOB MCPP_CMDLINE_MODULES CONFIGURE_DEPENDS "${MCPP_CMDLINE_SRC}/*.cppm")
128+
else()
129+
message(WARNING "mcpplibs.cmdline ${MCPP_CMDLINE_VERSION} not unpacked at "
130+
"${MCPP_CMDLINE_SRC}; this build will not match mcpp's own")
131+
set(MCPP_CMDLINE_MODULES "")
132+
endif()
133+
134+
add_executable(mcpp "${MCPP_ROOT}/src/main.cpp")
135+
136+
# FILE_SET CXX_MODULES is the only way CMake learns these are interface units.
137+
# Listing them as ordinary sources compiles them as plain TUs and the link fails
138+
# with missing module symbols.
139+
target_sources(mcpp
140+
PRIVATE
141+
FILE_SET CXX_MODULES BASE_DIRS "${MCPP_ROOT}/src" FILES ${MCPP_MODULES}
142+
)
143+
144+
# The dependency's units need their OWN file set: a CXX_MODULES set requires
145+
# every file to live under one of its base directories, which defaults to the
146+
# project source dir, and these live in the registry outside the tree.
147+
if(MCPP_CMDLINE_MODULES)
148+
target_sources(mcpp
149+
PRIVATE
150+
FILE_SET mcpp_cmdline_modules
151+
TYPE CXX_MODULES
152+
BASE_DIRS "${MCPP_CMDLINE_SRC}"
153+
FILES ${MCPP_CMDLINE_MODULES}
154+
)
155+
endif()
156+
157+
# mcpp.toml: include_dirs = ["src/libs/json"] — src/libs/json.cppm reaches for
158+
# <json.hpp> from its global module fragment.
159+
target_include_directories(mcpp PRIVATE "${MCPP_ROOT}/src/libs/json")
160+
161+
# mcpp.toml default: static_stdlib = true, so the binary is portable.
162+
target_link_options(mcpp PRIVATE -static-libstdc++)
163+
164+
message(STATUS "mcpp: ${MCPP_MODULE_COUNT} module interface units + src/main.cpp")

bench/projects/mcpp/MODULE.bazel

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# bazel module for mcpp — BEST EFFORT, AND IT DOES NOT BUILD.
2+
#
3+
# bazel 9.2.0 + rules_cc 0.2.22 CAN build C++20 named modules — measured, with
4+
# `module_interfaces` plus --experimental_cpp_modules --features=cpp_modules,
5+
# and clang (its ddi aggregator cannot parse GCC's P1689 output). For the
6+
# synthetic fixture that is enough. For mcpp it is not, for two reasons:
7+
#
8+
# 1. `import std;` works, but only by hand — CORRECTED, an earlier version of
9+
# this comment claimed it was impossible. bazel has no counterpart to
10+
# CMake's CXX_MODULE_STD, and its modmap generator fails with
11+
# ERROR: Module not found: std
12+
# but libc++ ships the std module as ORDINARY SOURCE, so it can be built
13+
# like any other interface unit. Measured working on bazel 9.2.0:
14+
#
15+
# cp $LLVM/share/libc++/v1/std.cppm .
16+
# cp -r $LLVM/share/libc++/v1/std . # 110 .inc files it includes
17+
# cc_binary(
18+
# srcs = ["main.cpp"] + glob(["std/**"]),
19+
# module_interfaces = ["std.cppm", "m.cppm"], # std FIRST
20+
# copts = ["-std=c++23", "-Wno-reserved-module-identifier"],
21+
# )
22+
# bazel build //:t --experimental_cpp_modules --features=cpp_modules --force_pic
23+
#
24+
# (No `includes` attribute: "." is rejected as the workspace root, and the
25+
# .inc files sit beside std.cppm in the sandbox anyway.)
26+
#
27+
# 2. Workspace boundary. mcpp's sources live three directories up from here.
28+
# bazel will not glob outside its workspace, so a working setup would have
29+
# to put MODULE.bazel at the repository root — exactly what these files
30+
# were moved out of the root to avoid.
31+
#
32+
# 3. THE ONE THAT DECIDES IT: bazel builds C++20 modules only with clang (its
33+
# ddi aggregator cannot parse GCC's P1689 output). mcpp, cmake and xmake
34+
# are measured here against gcc@16.1.0. A bazel column in a gcc table would
35+
# violate fairness invariant I1 — same compiler binary for every engine —
36+
# so bazel belongs in a separate clang-baselined table, not this one.
37+
module(name = "mcpp", version = "2026.8.12.1")
38+
bazel_dep(name = "rules_cc", version = "0.2.22")

bench/projects/mcpp/meson.build

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# meson description for mcpp — BEST EFFORT, AND IT DOES NOT BUILD.
2+
#
3+
# Kept so the directory answers "what about meson?" with a measurement instead
4+
# of silence, and so the day meson grows the feature this file is the diff.
5+
#
6+
# Two independent blockers, both measured on meson 1.10.2:
7+
#
8+
# 1. No named modules. meson has no attribute that declares a translation unit
9+
# to be a module INTERFACE. Listing .cppm files as ordinary sources compiles
10+
# them as plain TUs, and the first importer fails with
11+
# fatal error: module 'mcpp.log' not found
12+
# This is the same failure the synthetic fixture hits; see
13+
# bench/README.md §5.
14+
#
15+
# 2. No `import std;`. Every one of mcpp's 138 interface units imports the
16+
# standard library module. CMake needs an experimental UUID plus the
17+
# compiler's libstdc++.modules.json for this; meson has no equivalent.
18+
#
19+
# Blocker 1 alone is fatal, so the harness reports meson as `unavailable` with
20+
# the reason rather than running this and reporting a failed build.
21+
22+
project('mcpp', 'cpp',
23+
version: '2026.8.12.1',
24+
default_options: ['cpp_std=c++23', 'buildtype=release'])
25+
26+
fs = import('fs')
27+
root = meson.current_source_dir() / '..' / '..' / '..'
28+
29+
# Enumerated rather than globbed: meson deliberately has no glob, and hard-coding
30+
# 138 paths in a file that cannot build anyway would be noise. run_command with
31+
# `find` would work and is left out for the same reason.
32+
error('meson 1.10.2 cannot build C++20 named modules (no interface-unit '
33+
+ 'declaration) and has no `import std;` support; see the comment above. '
34+
+ 'This file exists to record that, not to build mcpp.')

0 commit comments

Comments
 (0)