Skip to content

Commit c109fdd

Browse files
authored
2026.9.26.2: one compile database per configuration, emit plans every member, prepare actions and runtime search directories, DLLs beside a Windows program, link flags as words (#702)
Closes #699, #701, #703. Design record: .agents/docs/2026-09-26-compile-database-and-issue-699-design.md.
1 parent 3a6ac7f commit c109fdd

65 files changed

Lines changed: 6378 additions & 600 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/docs/2026-09-26-compile-database-and-issue-699-design.md‎

Lines changed: 948 additions & 0 deletions
Large diffs are not rendered by default.

‎.agents/docs/README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
1818
---
1919
```
2020

21-
310 records.
21+
311 records.
2222

2323
## By subject
2424

@@ -30,6 +30,7 @@ Records that declare one. Everything else is listed by date below.
3030

3131
### design
3232

33+
- [The compile database, `emit build-database`, and #701/#702: triage against the specifications, and one design](2026-09-26-compile-database-and-issue-699-design.md) — landed
3334
- [Issues #693 to #696: triage against mcpp's contracts, and one repair plan](2026-09-25-issues-693-696-triage-and-repair-plan.md) — landed
3435
- [Workspace inheritance, flag scoping and the published form: a unified repair plan (#690)](2026-09-25-issue-690-workspace-build-inheritance-consistency.md) — landed
3536
- [MSVC toolset 的选择、#685、#687 与工具链管理规范:总体设计](2026-09-24-toolchain-selection-and-payload-trust-design.md) — active
@@ -101,6 +102,7 @@ Records that declare one. Everything else is listed by date below.
101102

102103
### 2026-09
103104

105+
- [The compile database, `emit build-database`, and #701/#702: triage against the specifications, and one design](2026-09-26-compile-database-and-issue-699-design.md) — landed
104106
- [Issues #693 to #696: triage against mcpp's contracts, and one repair plan](2026-09-25-issues-693-696-triage-and-repair-plan.md) — landed
105107
- [Workspace inheritance, flag scoping and the published form: a unified repair plan (#690)](2026-09-25-issue-690-workspace-build-inheritance-consistency.md) — landed
106108
- [#690: self-review before release, engine and ecosystem](2026-09-25-issue-690-self-review.md) — landed

‎.github/workflows/ci-linux-e2e.yml‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,49 @@ jobs:
449449
"ok: a program over a C++ shared library runs on one C++ runtime" \
450450
"ok: a stated self-contained program over a coupled C++ shared library is refused" \
451451
"PASS: 700 a program over a C++ shared library has one C++ runtime"
452+
453+
# The compile database (#699 report, design 2026-09-26 §3): the scripts
454+
# whose rows need llvm declare `# requires: llvm`, which no shard has, so
455+
# they run here and each is held to the lines it prints only when the
456+
# property it names was checked.
457+
- name: "Compile database e2e that needs llvm (783, 784, 785, 786)"
458+
run: |
459+
set -o pipefail
460+
# The scripts read the database with python3, which this container
461+
# lacks. It is not a toolchain: the job's property (no host compiler,
462+
# no host C runtime) was asserted before the checkout and still holds.
463+
if ! command -v python3 >/dev/null 2>&1; then
464+
apt-get install -y -qq python3 >/dev/null
465+
if command -v gcc >/dev/null 2>&1; then
466+
echo "::error::installing python3 brought a host compiler"; exit 1
467+
fi
468+
fi
469+
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
470+
export MCPP
471+
run_and_assert() { # $1 = script; the remaining arguments are lines it must print
472+
local script="$1"; shift
473+
local log="$RUNNER_TEMP/$(basename "$script").log"
474+
bash "$script" 2>&1 | tee "$log"
475+
for line in "$@"; do
476+
grep -qxF "$line" "$log" || { echo "::error::$script did not print: $line"; exit 1; }
477+
done
478+
}
479+
run_and_assert tests/e2e/783_cdb_switches_whole_with_the_configuration.sh \
480+
"ok: mcpp test then mcpp build keep the test entry in one configuration" \
481+
"ok: switching toolchain switches the whole root file" \
482+
"ok: switching back restores that configuration's entries, test units included" \
483+
"PASS: 783 one compile database per configuration"
484+
run_and_assert tests/e2e/784_cdb_replays_from_its_directory.sh \
485+
"ok: gcc row replays every entry from its directory, no gcm.cache/ in the project root" \
486+
"ok: llvm row replays every entry from its directory" \
487+
"PASS: 784 the compile database replays from its own directory"
488+
run_and_assert tests/e2e/785_cdb_interface_flag_module_extensions.sh \
489+
"ok: the interface entry carries -x c++-module immediately before -c, and replays" \
490+
"ok: the non-module unit carries no language flag" \
491+
"PASS: 785 the compile database states a module interface's language explicitly"
492+
run_and_assert tests/e2e/786_std_unit_in_the_database_and_build_id.sh \
493+
"ok: the std unit's directory is the shared std cache, not the project's output directory" \
494+
"ok: emit --spec compile-commands renders the same std entry as the build's database" \
495+
"ok: provides['std'] names the std-cache BMI" \
496+
"ok: build-id is present and stable across two runs" \
497+
"PASS: 786 the std unit in the database, emit/build agreement, provides and build-id"

‎.github/workflows/cross-build-test.yml‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,38 @@ jobs:
370370
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
371371
bash tests/e2e/248_pack_library_fat_pe_leg.sh
372372
373+
# SPEC-007 R4.3: the engine places a Windows program's runtime DLLs
374+
# beside it after its link, so a program started by hand finds them.
375+
# Named here for the reason the steps above are: the Linux shards skip
376+
# `# requires: mingw-cross wine`. Each property is held to the line the
377+
# script prints only when that property was checked.
378+
# The runtime_search_dir directive on PE (#701): `mcpp run` finds a DLL
379+
# through PATH and `mcpp pack` places it. The native leg is e2e 794 on
380+
# the Windows shards; this is its Linux-hosted counterpart.
381+
- name: "e2e: runtime_search_dir on PE under wine"
382+
run: |
383+
set -o pipefail
384+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
385+
log="$RUNNER_TEMP/796.log"
386+
bash tests/e2e/796_runtime_search_dir_on_pe_under_wine.sh 2>&1 | tee "$log"
387+
grep -qxF "PASS: 796_runtime_search_dir_on_pe_under_wine" "$log" \
388+
|| { echo "::error::796 did not print its PASS line"; exit 1; }
389+
390+
- name: "e2e: a Windows program finds its runtime DLLs beside it"
391+
run: |
392+
set -o pipefail
393+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
394+
log="$RUNNER_TEMP/797.log"
395+
bash tests/e2e/797_a_windows_program_finds_its_dlls_beside_it.sh 2>&1 | tee "$log"
396+
for line in \
397+
" ok: after the first build the program started by hand finds its DLL" \
398+
" ok: a build with nothing changed neither relinks nor places again" \
399+
" ok: a DLL replaced in its directory replaces the copy" \
400+
" ok: no system DLL is copied" \
401+
"PASS: a Windows program finds its runtime DLLs beside it"; do
402+
grep -qxF "$line" "$log" || { echo "::error::797 did not print: $line"; exit 1; }
403+
done
404+
373405
# ── windows → linux ───────────────────────────────────────────────────────
374406
# The mirror of mingw-cross-wine. Two jobs because a Windows runner cannot
375407
# execute the ELF it produces; the artefact is handed to a Linux job and

‎CHANGELOG.md‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,85 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。
55

6+
## [2026.9.26.2] - 2026-09-26
7+
8+
### 编译数据库:一个配置一个数据库(#699 的报告;#397 C-1、#677 B1)
9+
10+
`compile_commands.json` 此前是一份跨命令、跨工具链合并的文件:删除后的下一次构建不再写回它,
11+
另一个工具写入的条目与上一个工具链的条目都被保留,`directory` 写的是工程根目录而编译器在输出目录
12+
中运行。现在:
13+
14+
- 每个配置一份数据库,位于 `target/<triple>/<fingerprint>/compile_commands.json`,只在本配置之内
15+
合并,因此 `mcpp test` 之后的 `mcpp build` 保留测试单元的条目。工程根目录的文件是当前配置数据库
16+
的副本,整体替换、从不合并,内容相同时不写;切换工具链或 profile 就切换整个文件,切回时恢复该
17+
配置的条目。根目录的符号链接照旧按其指向写入。
18+
- 根文件被删除后,下一次构建从配置数据库恢复它,快路径也是如此,不需要规划。
19+
- 被替换的根文件含有 mcpp 没有写的条目时(条目的 `output` 不在本工程的 `target/` 或 mcpp 主目录
20+
之下),构建以一条警告说明其数量。
21+
- `directory` 与 S1 的 `work-directory` 是编译器运行的输出目录,与 JSON Compilation Database 格式、
22+
S1-8-2 以及 CMake、ninja 的写法一致;从 `directory` 重放 GCC 的条目不再在源码树中写出
23+
`gcm.cache/`。
24+
- 提供模块的单元在 `-c` 之前带上接口语言 flag(clang 为 `-x c++-module`,GCC 为 `-x c++`),
25+
clangd 因此能处理 `.ixx` 接口;MSVC 的写法待 Windows 实测后再加。
26+
- 导入 `std` 的构建在 `compile_commands.json` 与 `emit --spec compile-commands` 中列出标准库单元
27+
(S1-12-1),另一个版本的 clangd 因此能自己构建 `std`。S1 文档中标准库单元的 `provides` 指向
28+
共享 std 缓存中的 BMI,工具链带 `build-id`,版本与构建身份完全一致的读者可以直接复用。
29+
- `mcpp new` 写出的 `.gitignore` 包含 `compile_commands.json`。
30+
31+
### `emit build-database` 按成员规划(#699)
32+
33+
`emit build-database --workspace` 此前在第一个规划失败的成员处停止,已规划成员的集合全部丢失。
34+
现在每个选中的成员独立规划:规划失败的成员不贡献集合,贡献一条 `error` 诊断,`path` 为它的
35+
`mcpp.toml`;至少一个成员规划成功时文档带 `data`,`watch` 也列出失败成员的 `mcpp.toml` 与
36+
`build.mcpp`;有任何错误时退出码为 1。在 `emit` 下,构建失败的宿主工具是警告
37+
`MCPP_BUILD_DATABASE_HOST_TOOL_UNBUILT`,规划继续,构建程序得到该工具将被发布的路径;构建程序失败
38+
的包不带该程序的指令地被描述,并得到一条 `MCPP_BUILD_DATABASE_PROGRAM_FAILED` 错误,`path` 为它的
39+
`build.mcpp`。`mcpp build` 的行为不变。带错误诊断的 `data` 是 S2 0.3.0 的部分回答
40+
(Sunrisepeak/mcpp-language-server#25)。
41+
42+
### 构建程序:`runtime_search_dir`、`prepare` 角色与 stamp 规则(#701、#702)
43+
44+
- `mcpp::runtime_search_dir(dir)`(`mcpp:runtime-search-dir=`,协议 12)是 `[runtime]
45+
runtime_search_dirs` 的构建程序形态,并入同一个字段:ELF 与 Mach-O 的运行路径、`mcpp run` 的加载
46+
路径、`mcpp pack` 的闭包搜索与运行时校验都照常读取它,依赖的声明到达消费方的可执行文件。目录在
47+
构建程序运行时不必存在。保留字段 `[runtime] library_dirs` 不获得指令。
48+
- 新角色 `prepare` 用于文件名在构建程序运行时未知的施工(安装一个前缀、解开一个 SDK):命令填充
49+
用 `a.output_dir(dir)` 声明的目录,引擎写 stamp。命令成功而目录不存在或除 stamp 外不含文件时,
50+
这条边失败并点名目录。声明包的每条编译边与计划中的每条链接边等待它,构建输出以 `PREPARE`
51+
标注。构建程序的重新运行依据落在某个 `prepare` 目录内时,构建给出警告。
52+
- 角色以常量书写:`mcpp::roles::{source, check, object, artifact, prepare}`。引擎拒绝这五个之外的
53+
角色字符串并列出它们;此前未知的字符串被静默读作 `source`。
54+
- `check` 与 `prepare` 的命令成功后,引擎创建缺失的 stamp,并把已存在的 stamp 更新到当前时间;
55+
此前已存在的 stamp 不被更新,一个输入改变一次之后,该 action 在此后每次构建中都会重新运行。
56+
57+
### Windows 程序的 DLL 在链接后放到程序旁
58+
59+
PE 映像没有运行路径,运行时搜索目录中的 DLL 此前只服务于 `mcpp run` 与 `mcpp pack`,从构建目录直接
60+
启动的程序找不到它。现在计划中带运行时搜索目录的 PE 程序在链接后多一条边 `mcpp place-dlls`:按
61+
`mcpp pack` 的闭包求解与系统规则,把程序直接或间接导入、且在这些目录中解析到的 DLL 放到程序旁,
62+
字节不同时才写;DLL 在其目录中被替换(包括被 `prepare` action 替换)后,同一次构建会再次放置它。
63+
部署到程序旁的 DLL 从链接边的隐式输入改为 order-only 输入,DLL 变化不再使程序重新链接。
64+
65+
### 链接 flag 按词读取(#703)
66+
67+
`[build] ldflags`、`mcpp::link_flag` 与依赖传播的链接 flag 此前只为 ninja 转义,Linux 与 macOS 的
68+
`sh` 展开其中的 `$ORIGIN`,程序的运行路径因此含有 `/../lib`,即宿主的 `/lib`。SPEC-004 §8 的词读法
69+
现在同样适用于链接 flag:每个词原样到达链接器;依赖的链接 flag 按词传播;`link_search`、`link_lib`、
70+
`link_script` 由路径构成的值是一个词,含空格的目录不再被拆开。为 shell 或 ninja 手工转义的写法
71+
(`\$ORIGIN`、`'$$ORIGIN'`)现在按写法读取,首次规划以 `build/flag-words` 指出这样的元素;
72+
索引中没有这样的写法。
73+
74+
### 构建插件规范 SPEC-007
75+
76+
新增 `docs/specs/build-plugins.md`(草案 0.2):插件的配置、施工与校验各用一种机制,环境不完整时
77+
警告而不失败,配置不依赖施工结果,运行时库以 `runtime_search_dir` 声明,构建期的网络访问在离线
78+
构建中不发生。mcpp 只提供通用机制,某一个工具的知识只属于它的插件。
79+
80+
### 兼容性
81+
82+
- 构建程序缓存的 epoch 升到 3,升级后每个构建程序重新运行一次。
83+
- 构建程序协议升到 12;使用新指令、新角色或角色常量的构建程序在旧引擎上编译失败并指出名字。
84+
685
## [2026.9.26.1] - 2026-09-26
786

887
### 路径与文本统一为 UTF-8(#693)

‎docs/01-getting-started.md‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,31 @@ Run it only in a trusted workspace. The process exit code and the resulting
152152
`compile_commands.json` are the stable integration contract; stdout remains
153153
human-readable.
154154

155+
`compile_commands.json` at the project root is a copy of the current
156+
configuration's own database, written at
157+
`target/<triple>/<fingerprint>/compile_commands.json` — the directory the
158+
toolchain, target, profile and features together name, and the directory the
159+
compiler actually runs in. Every command that plans in that configuration
160+
(`build`, `test`, `run`, `--configure-only`) merges its fresh plan into that
161+
one file, so a unit `mcpp test` added stays listed after a plain `mcpp
162+
build`. The root copy is never merged: it is replaced whole with the
163+
configuration's file, and left untouched when the two already agree, so
164+
switching `--toolchain` or `--profile` switches the whole root file and
165+
switching back restores that configuration's entries, test units included. A
166+
symlink at the project root is followed, and the copy lands at its target.
167+
When the replaced root file held entries mcpp did not write, one warning
168+
states how many; the file then holds this project's own configuration.
169+
Deleting the root file is not a way to reset it: the next build (even one
170+
that finds nothing to recompile) restores it from the configuration's
171+
database.
172+
173+
A project that imports `std` also finds an entry for the toolchain's
174+
standard-library units, in both `compile_commands.json` and the S1 document
175+
`emit build-database` prints — see below. Because a BMI is readable only by
176+
the compiler that built it, listing the unit lets a reader whose toolchain
177+
differs from the one mcpp built with compile its own copy of `std` instead of
178+
failing on a version mismatch.
179+
155180
An editor that must not write into the project asks for the same plan on stdout
156181
*(mcpp 2026.9.15.1+)*:
157182

0 commit comments

Comments
 (0)