Skip to content

Commit b8e326e

Browse files
authored
2026.9.28.3: the build-plugin architecture -- mcpp.core, build information, members built once, batched placement, pack -p, the library interface, and the fast path on every platform (#734)
The engine half of #734 (.agents/docs/2026-09-28-build-cost-foreign-toolsets-and-library-surface-design.md; section 13.5 records the tasks and the departures). - E8: `mcpp.core` names the build-program interface; `mcpp` is its permanent equivalent. SPEC-007 section 9 states the three layers, the protocol table and the stability policy. - E2 (protocol 14): build information for build programs -- tool, abi_tool, tool_env, toolset_identity, msvc_instance_dir, ninja_program, cxx_runtime, msvc_crt_linkage -- read from the producers the engine's own command lines read. - E11: mcpp::report({severity, message, impact, hint}), rendered by mcpp.diag and replayed on a cache hit; written through printf only, so GCC accepts a build program that includes <cstdio> after `import mcpp;`. - E7: a build-program import of a module behind a disabled feature names the package and the feature. E10: `mcpp.<own namespace>.*` is accepted; the reserved second segments belong to namespace `mcpp`. - E1: a workspace member used as a path dependency is built once, in its own directory, and consumers take its objects through content-verified stage edges. - E4: two or more placements are one `stage_list` edge reading placements.list (generic spellings); a refusal names the list's entries. - E3: `mcpp pack -p <member>`. E6 phase 1 (SPEC-008): W1 to W3 and a complete "Withheld" row, warnings only; W3 leaves members of the package's own workspace alone. - E9: `[package] mcpp = ">=V"` and `[workspace.package] mcpp`. E12: `[lib]` reports unknown keys. - E5: the fast path serves on macOS, Windows and SDK-sysroot targets (the runtime validation now records a snapshot where no ELF rule applies); a path dependency's whole tree is swept; a confirmed graph refreshes build.ninja's time, so the fast path resumes after an edit; each refusal is a sentence under -v. - One line per download, with size and time, in both output modes. - clang on the MSVC ABI warns when msvc@system finds no toolset. - Tests: e2e 822-832, unit tests for the directives, provisions, the placement list and the progress output, and the protocol-table check. - Release 2026.9.28.3; mcpp-plugins 0.17.0 takes it as its floor.
1 parent dbf7194 commit b8e326e

71 files changed

Lines changed: 4221 additions & 298 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-28-build-cost-foreign-toolsets-and-library-surface-design.md‎

Lines changed: 1291 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-
315 records.
21+
316 records.
2222

2323
## By subject
2424

@@ -31,6 +31,7 @@ Records that declare one. Everything else is listed by date below.
3131
### design
3232

3333
- [An ecosystem design for mcpp and xlings: one authority per fact, and the work that follows from it](2026-09-28-ecosystem-design-and-optimisation-plan.md) — landed
34+
- [Build cost, foreign toolsets, the build-plugin architecture and the library surface: engine, plugin and index design (#734)](2026-09-28-build-cost-foreign-toolsets-and-library-surface-design.md) — active
3435
- [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
3536
- [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
3637
- [Workspace inheritance, flag scoping and the published form: a unified repair plan (#690)](2026-09-25-issue-690-workspace-build-inheritance-consistency.md) — landed
@@ -108,6 +109,7 @@ Records that declare one. Everything else is listed by date below.
108109

109110
- [Two days of mcpp and xlings: a review of what merged, what is known, and what is open](2026-09-28-ecosystem-review-of-two-days-of-mcpp-and-xlings.md) — active
110111
- [An ecosystem design for mcpp and xlings: one authority per fact, and the work that follows from it](2026-09-28-ecosystem-design-and-optimisation-plan.md) — landed
112+
- [Build cost, foreign toolsets, the build-plugin architecture and the library surface: engine, plugin and index design (#734)](2026-09-28-build-cost-foreign-toolsets-and-library-surface-design.md) — active
111113
- [Eight reports after 2026.9.27.1: implementation plan](2026-09-27-eight-reports-implementation-plan.md) — active
112114
- [Eight reports after 2026.9.27.1: what each one is, where it belongs, and one optimisation plan](2026-09-27-eight-reports-by-home-and-one-optimisation-plan.md) — active
113115
- [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

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ jobs:
110110
- name: The release canary runner runs each command under the named bash
111111
run: python3 tests/scripts/test_release_canaries.py
112112

113+
- name: The protocol table of SPEC-007 names the engine's protocol
114+
run: python3 tests/scripts/test_protocol_table.py
115+
113116
# Text-only, like the two steps around it, and it belongs here rather
114117
# than in the target-matrix workflow: that workflow runs the matrix, and
115118
# this asserts a property of the TABLE, which is readable without a

‎.github/workflows/measure-windows-tool-crt.yml‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ jobs:
113113
"$XLINGS_BIN" install qt-base@6.11.1 -y
114114
QT="$(cygpath -u "$USERPROFILE")/.xlings/data/xpkgs/xim-x-qt-base/6.11.1"
115115
test -x "$QT/bin/moc.exe" || { echo "::error::no moc.exe in $QT/bin"; exit 1; }
116-
# What revision 1 of the recipe removes (task I2).
117-
( cd "$QT/bin" && ls vcruntime140*.dll msvcp140*.dll concrt140.dll vccorlib140.dll 2>/dev/null; \
116+
# What revision 1 of the recipe removes (task I2). The published
117+
# revision 1 no longer carries them, and `ls` of absent files exits 2,
118+
# which `bash -e` would read as a failed install.
119+
( cd "$QT/bin" && { ls vcruntime140*.dll msvcp140*.dll concrt140.dll vccorlib140.dll 2>/dev/null || true; }; \
118120
rm -f vcruntime140*.dll msvcp140*.dll concrt140.dll vccorlib140.dll )
119121
echo "QT=$QT" >> "$GITHUB_ENV"
120122

‎CHANGELOG.md‎

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

6+
## [2026.9.28.3] - 2026-09-28
7+
8+
本版本实施 #734 设计中 mcpp 的部分:构建插件体系的三层(`mcpp.core`、官方通用库、插件)、
9+
构建信息、工作区成员只构建一次、批量放置、`pack -p`、库接口规范的第一阶段与包的版本下限。
10+
设计、测量、任务划分与实施记录见
11+
`.agents/docs/2026-09-28-build-cost-foreign-toolsets-and-library-surface-design.md`,规范见
12+
SPEC-007 §9 与新的 SPEC-008。配套的 mcpp-plugins 0.17.0 以本版本为下限。
13+
14+
### 行为变化
15+
16+
- **被其他成员以 path 依赖使用的工作区成员只构建一次(E1)。** 消费方构建之前,成员在自己的目录中
17+
作为自身构建的根构建一次,由它的 ninja 判断过期(包括成员根目录之外的头文件);消费方经 stage
18+
边取得其对象与 BMI,按内容比较。条件是成员在消费方图中的构建键输入与它作为根时相等
19+
(`package.index` 除外,它只记来源);不相等时照旧在消费方图中编译,`-v` 写出不同的输入。同时
20+
构建的两个消费方以文件锁轮流使用成员目录。一个五成员 Windows 工作区的核心库此前被编译三次。
21+
- **程序旁的文件由一个进程放置(E4)。** 程序的 deploy 条目在两条及以上时成为一条 `stage_list`
22+
边,读取规划写出的 `placements.list`;`mcpp stage --list` 对每个目的地保持单文件语义。在
23+
Windows 首次构建上,1270 条单文件放置耗时 4.5 s,一个进程复制同样的文件耗时 0.5 s。
24+
- **库接口的第一阶段(E6,SPEC-008)。** `mcpp pack` 写出未进入发布闭包的导出模块(W2),"Withheld"
25+
一行列出每个未发布的单元(此前没有接口根时显示 "(nothing)",而两个导出模块既未发布也未列出);
26+
`mcpp build` 在包导入依赖(本工作区成员除外)的非公开模块时警告(W3);缺少接口根的警告写明对 `mcpp pack` 的后果
27+
(W1)。全部为警告。
28+
- **快路径在一次确认之后恢复。** 编辑源码后的那次构建经完整路径确认了图,却不重写内容未变的
29+
build.ninja,而快路径以 build.ninja 的时间比较每个源码;此前编辑之后的每次构建都被拒绝,直到图的
30+
文本改变。现在确认时移动 build.ninja 的时间(e2e 832,2026.9.28.2 在 Linux 上同样复现)。
31+
- **快路径看见 path 依赖的整棵源码树。** 此前只扫描依赖的 `src/`;依赖在别处的 host module
32+
(例如 mcpp-plugins 的 `deps/vcpkg.cppm`)编入消费方的构建程序,不在任何 ninja 边上,被编辑后
33+
构建报告"无事可做"。现在扫描依赖的整棵树,跳过隐藏目录、`target` 与嵌套的包(e2e 831)。
34+
- **每个下载只占一行。** 非终端输出此前在开始时写一行 `Downloading <item> (<size>)`,完成时再写一行
35+
`... done, <size> in <time>`;现在只写完成的一行,失败时写 `did not complete` 的一行。终端上进度条
36+
原地刷新,结束时换成同样带大小与耗时的完成行。
37+
- **MSVC ABI 上的 clang 找不到工具集时说明原因。** 默认的 `msvc@system` 在没有带 C++ 工具的
38+
Visual Studio 实例时,此前静默继续,随后在预编译 `mcpp` 模块时以 `'cstdio' file not found` 失败;
39+
现在解析时给出警告,写出安装与指定托管工具集的命令(在屏蔽 Visual Studio 的 runner 上测得)。
40+
- **`mcpp.core` 的输出不再把 `FILE` 带入模块接口。** `mcpp::report` 只经 `printf` 输出;此前 GCC
41+
拒绝在 `import mcpp;` 之后 `#include <cstdio>` 的构建程序(e2e 651)。
42+
43+
### 特性
44+
45+
- **`mcpp.core`(E8,协议 14)。** 引擎接口以 `mcpp.core` 为名,`mcpp` 是永久等价的写法。
46+
- **构建信息(E2,协议 14)。** `mcpp::tool(role)`、`abi_tool(role)`、`tool_env()`、
47+
`toolset_identity()`、`msvc_instance_dir()`、`ninja_program()`、`cxx_runtime()`、
48+
`msvc_crt_linkage()` 以事实陈述解析出的工具链与程序的 C++ 运行时契约,取自引擎自身命令行所读的
49+
同一来源。
50+
- **结构化诊断(E11,协议 14)。** `mcpp::report({severity, message, impact, hint})` 以引擎的形式
51+
呈现,进入 JSON 输出,缓存命中时重放。
52+
- **缺失的构建程序模块指出 feature(E7)。** 模块只在依赖未启用的 feature 之后提供时,错误写出包名、
53+
feature 名与应加的一行。
54+
- **插件模块的名字(E10)。** `mcpp.<自己的命名空间>.*` 不再被警告;保留的第二段(`core`、
55+
`plugins`、`deps`、`rules`、`dist`、`tools`)只属于命名空间 `mcpp` 的包。
56+
- **`mcpp pack -p <member>`(E3)。** 在工作区根目录打包某个成员,结果与在成员目录中打包相同。
57+
- **包的版本下限(E9)。** `[package] mcpp = ">=<release>"` 与 `[workspace.package] mcpp`;
58+
低于下限的引擎在其他工作之前停止,写出升级命令;只接受 `>=`。
59+
- **`[lib]` 报告未知键(E12)。** 此前拼错的 `path` 被静默接受。
60+
- **快路径在 macOS、Windows 与自带 sysroot 的目标上生效(E5)。** 运行期校验只含 ELF/glibc 规则,在这些目标上
61+
提前返回而不写校验记录,快路径因此找不到已校验的产物快照,每次构建都走完整路径(macOS 上 e2e 645、831、
62+
832 读出 `no validated artifact snapshot is recorded`)。现在这些目标记录产物的戳记,判定为通过(没有适用的
63+
规则);产物被重新链接时快路径仍交回完整路径。`-v` 下每个拒绝点以一句话写出其条件。
64+
65+
### 兼容性
66+
67+
- 协议升至 14:使用 §9 新接口的构建程序在旧引擎上编译失败并指出缺少的名字;以
68+
`[package] mcpp` 声明下限的包在旧引擎上得到一条"不支持的键"警告。
69+
- 升级后的第一次构建各付一次代价:每个构建程序因上下文新增变量而重新运行一次;被共享的工作区
70+
成员在消费方中改为暂存,其对象在成员目录中编译一次;程序旁的放置边形状改变而运行一次(内容相同
71+
的文件不重写)。
72+
673
## [2026.9.28.2] - 2026-09-28
774

875
本版本实施 2026-09-28 生态设计中 mcpp 的部分(WS1、WS2、WS3、WS7、WS8、WS10 与决定 D7),关闭

‎docs/04-mcpp-toml.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ mcpp does not read is reported, as in `[build]`: a warning, and an error under
103103
resources = "res"
104104
```
105105

106+
`mcpp = ">=<release>"` (mcpp 2026.9.28.3+) states the oldest mcpp release the
107+
package supports. It is a floor, not a pin: the release a project installs is
108+
the one `.xlings.json` names. Only the `>=` form is accepted, because a bare
109+
release means "exactly" elsewhere in mcpp; a bare release and a value that is
110+
not a release are refused with the spelling that is meant. An engine below the
111+
floor stops before any other work, naming the package, the floor, its own
112+
release and the command that installs a newer one. `[workspace.package] mcpp`
113+
states it once for every member. An engine older than 2026.9.28.3 ignores the
114+
key with a warning.
115+
116+
```toml
117+
[package]
118+
name = "myplugin"
119+
mcpp = ">=2026.9.28.3"
120+
```
121+
106122
#### Dialect flags and the `import std` BMI
107123

108124
Some flags change what the standard library's headers declare, so the precompiled `import std`
@@ -1022,6 +1038,10 @@ path = "src/capi/lua.cppm" # Override the default lib-root location
10221038
```
10231039

10241040
Default convention: `src/<last segment of package name>.cppm` (e.g. package name `mcpplibs.cmdline` → `src/cmdline.cppm`).
1041+
`path` is the only key of the table; any other key is reported, as in `[build]`
1042+
(mcpp 2026.9.28.3+). The lib root is the interface root of SPEC-008: the module a
1043+
packed library publishes, with what it re-exports, and the module a build
1044+
program imports from a host-module dependency.
10251045
### 2.5 `[dependencies]`, `[dev-dependencies]`, `[build-dependencies]`
10261046

10271047
Moved to [05 — Dependencies and Resolution](05-dependencies.md).

‎docs/07-workspace.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,28 @@ fan-out continues; a timed-out build fails that member; `--workspace-timeout` st
413413
the fan-out and lists what did not run instead of leaving the CI job to kill the
414414
process (which discards everything it had to say).
415415

416+
### 5.4 A member used by other members is built once (mcpp 2026.9.28.3+)
417+
418+
A member that other members use as a path dependency is built once, in its own
419+
directory, and every member that uses it takes its objects and module
420+
interfaces from there. A `--workspace` build and separate `-p` builds of two
421+
programs therefore compile a shared library member once, where each program
422+
used to compile it again in its own directory.
423+
424+
- **Staleness is the member's own.** Before a consumer builds, the member's
425+
own build runs, and its ninja decides what is stale, including an input
426+
outside the member's root (a header under `../3rdParty`).
427+
- **Equal build keys are the condition.** The member's build key in the
428+
consumer's graph must equal its key as the root of its own build; the key
429+
covers the toolchain, the flags, the profile and the features. A member that
430+
a consumer builds differently (another feature set, for example) is compiled
431+
in that consumer's graph, as before; `-v` states the input that differs.
432+
- **One build at a time.** Two consumers built at once take turns on the
433+
member's directory.
434+
- **Scope.** The rule applies in the default cache mode (`--cache off` compiles
435+
everything in the graph that asks for it) and to members only; a path
436+
dependency outside the workspace has one consumer and keeps its behaviour.
437+
416438
## 6. Directory Layout
417439

418440
The recommended directory layout for a workspace:

‎docs/10-pack-and-release.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ mcpp pack --profile dev # build with a different profile (default
147147
mcpp pack --dev # the same, as `build` and `run` spell it; --profile wins over it
148148
mcpp pack --message-format json # one mcpp.pack envelope on stdout (mcpp 2026.9.16.1+)
149149
mcpp pack --no-strip # ship the artifacts as built
150+
mcpp pack -p app --format release # a workspace member, as if run in its directory
150151
mcpp pack --debug-symbols dbg/ # write the separated *.debug files under dbg/
151152
mcpp pack --format msi --features installer # activate root-package features for the pack
152153
```
@@ -158,6 +159,11 @@ one distribution is declared under `[feature-deps.<f>]` with `tools = [...]` and
158159
only by the pack that names `<f>`. `mcpp run --format <name> --features <LIST>` hands
159160
the same features to the pack it performs.
160161

162+
`-p <member>` (mcpp 2026.9.28.3+) packs a workspace member from the workspace
163+
root: the member is resolved as every other `-p` resolves it, and the pack runs in
164+
its directory, so the result is the one `mcpp pack` in that directory produces. A
165+
relative `-o` keeps meaning the directory the command was typed in.
166+
161167
`--release` and `--dev` (mcpp 2026.9.16.1+) are the shorthands `build` and `run`
162168
take, with the same precedence: `--profile` wins over either, on all three
163169
commands.

‎docs/12-binary-distribution.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,23 @@ error: the published interface imports mathkit:secret , which no unit in this
137137
Restructure so the interface does not reach it, or make it an `export module`
138138
partition and accept that its source is published.
139139

140+
### Public modules, and what `mcpp pack` and `mcpp build` report (mcpp 2026.9.28.3+)
141+
142+
A package's public modules are the lib root's module and what it re-exports
143+
with `export import`, transitively (SPEC-008). They are what a consumer may
144+
import; the published closure also carries what they import without
145+
re-exporting, because a consumer needs it to build their interfaces. The
146+
"Withheld" row lists every unit that is not published, including when the
147+
package has no lib root. The three conditions below are warnings: a library may
148+
implement itself in modules and publish only headers, and only its author can
149+
state which it means.
150+
151+
| Condition | Reported by | Consequence stated |
152+
|---|---|---|
153+
| a `lib` target exports modules and has no lib root | `mcpp build`, for the package being built | `mcpp pack` publishes the library without a module interface |
154+
| exported modules that the packed form does not ship | `mcpp pack`, naming each module | a consumer of the packed form cannot import them |
155+
| the package being built imports a module of a dependency that has a lib root, outside its public modules; the dependency is not a member of the package's own workspace, whose members are built from source with it | `mcpp build`, naming the module and the public ones | the build succeeds from source and fails against the packed form |
156+
140157
## The compatibility tag
141158

142159
Every artifact records the toolchain it was built for:

0 commit comments

Comments
 (0)