Skip to content

Commit 9e8f283

Browse files
committed
docs: SPEC-007 v0.2 marks the implemented rules; docs/04 §2.11 states the DLL placement after a PE link
1 parent 78e772c commit 9e8f283

3 files changed

Lines changed: 50 additions & 24 deletions

File tree

‎docs/04-mcpp-toml.md‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ Link intent keeps discovery stages separate:
14581458
|---|---|---|---|
14591459
| `link_library_dirs` | `-L` | `-L` | `-L` or `/LIBPATH:` |
14601460
| `transitive_needed_dirs` | `-Wl,-rpath-link` | no flag | no flag |
1461-
| `runtime_search_dirs` | RUNPATH/rpath only, never `-L` | rpath only | no flag |
1461+
| `runtime_search_dirs` | RUNPATH/rpath only, never `-L` | rpath only | no flag; after the link, the DLLs the program imports from these directories are placed beside it *(2026.9.27.1+)* |
14621462
| `frameworks` | no flag | `-framework` | no flag |
14631463
| `deploy_files` | copy edge | copy edge | copy beside the output; never a linker flag |
14641464
| `deploy` *(2026.9.12.2+)* | copy edge into `bin/<to>/` | copy edge into `bin/<to>/` | copy edge into `bin/<to>/`; never a linker flag |
@@ -1492,6 +1492,19 @@ directly — not the legacy `library_dirs` above, which gains no directive of
14921492
its own. The directory need not exist when the program runs; a `prepare`
14931493
action may populate it later, at build time.
14941494

1495+
A PE image has no run path, so on Windows a runtime search directory serves
1496+
`mcpp run`, which puts it on `PATH`, and `mcpp pack`, which stages the closure.
1497+
From 2026.9.27.1 the link of a PE program whose plan has runtime search
1498+
directories is followed by one more edge, `mcpp place-dlls`, which reads the
1499+
program's import closure as `mcpp pack` does and places beside the program
1500+
every DLL it imports, directly or through another DLL, that resolves in one of
1501+
those directories. System DLLs and API sets are never copied, a copy is written
1502+
only when its bytes differ, and a DLL replaced in its directory is placed again
1503+
on the next build. A program started by hand from the build directory therefore
1504+
finds them, as it does after vcpkg's applocal step or CMake's
1505+
`$<TARGET_RUNTIME_DLLS>`. When two directories offer one name, the first in
1506+
search order is placed and a note names both.
1507+
14951508
`target/<triple>/<fp>/resolution.json` schema 2 stores the RuntimeBinding,
14961509
canonical requirements/providers/artifacts, LinkIntent, platform search
14971510
mechanism, and post-link verdict. `mcpp why runtime` is a pure interpreter of

‎docs/specs/build-plugins.md‎

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
|---|---|
55
| 规范编号 | SPEC-007 |
66
| 标题 | 构建插件:配置、施工与校验的分工,运行时与规划期的义务 |
7-
| 状态 | 草案 v0.1 |
8-
| 版本 | 0.1 |
7+
| 状态 | 草案 v0.2 |
8+
| 版本 | 0.2 |
99
| 最后修改 | 2026-09-26 |
10-
| 对应实现 | 逐条标注。标「已实现」的条款对应 mcpp >= 2026.9.26.1;标「未实现」的条款随 mcpp#702 落地 |
10+
| 对应实现 | 逐条标注。未注明版本的「已实现」条款对应 mcpp >= 2026.9.26.1;注明 mcpp#702 的条款对应 mcpp >= 2026.9.27.1 |
1111
| 相关设计文档 | `.agents/docs/2026-09-26-compile-database-and-issue-699-design.md`(§5) |
1212
| 相关 issue | mcpp#699、mcpp#701、mcpp#702、mcpp#703 |
1313
| 使用文档 | [docs/30 - build.mcpp](../30-build-mcpp.md)、[docs/31 - 编写规则包](../31-authoring-a-rule-package.md) |
@@ -57,7 +57,7 @@
5757
环境变量。配置**禁止**依赖施工的结果,例如枚举一个安装前缀中由 action 产生的文件:规划
5858
(`mcpp emit build-database`)与第一次构建都发生在施工之前,依赖施工结果的配置在第一次与
5959
第二次构建之间不同。(作者义务;构建程序的重新运行依据落在一个 `prepare` action 声明的目录内
60-
时,引擎给出一条警告,**未实现**,mcpp#702)
60+
时,引擎给出一条警告并点名两者,**已实现**,mcpp#702)
6161
- **R1.4** 构建程序在规划与构建中**必须**行为相同。引擎不提供「正在规划」的信号,因为规划
6262
所描述的计划与 `mcpp build --configure-only` 计算的计划相同(SPEC-005 R1.2)。(**已实现**)
6363

@@ -74,7 +74,7 @@
7474
| 宏 | `define` | `cfg` | 协议 1 |
7575
| 链接库与库目录 | `link_lib`、`link_search` | `link-lib`、`link-search` | 协议 1 |
7676
| 其他链接选项(含库的完整路径) | `link_flag` | `link-flag` | 协议 8 |
77-
| 运行时搜索目录 | `runtime_search_dir` | `runtime-search-dir` | 协议 12,**未实现**(mcpp#702) |
77+
| 运行时搜索目录 | `runtime_search_dir` | `runtime-search-dir` | 协议 12(mcpp#702) |
7878
| 放到程序旁的文件 | `deploy` | `deploy` | 协议 11 |
7979
| 生成的源 | `generated`、`source` 与 `role = "source"` 的 action | `generated`、`source`、`action` | 协议 1 |
8080
| 重新运行构建程序的依据 | `rerun_if_changed`、`rerun_if_changed_glob`、`rerun_if_env_changed` | 同名 | 协议 1、2 |
@@ -87,7 +87,7 @@
8787
- **R2.3** 插件**必须**使用现行字段的指令,**禁止**依赖只为兼容而保留的字段
8888
(`[runtime] library_dirs`,docs/04 §2.11)。(作者义务)
8989
- **R2.4** 链接选项的一个元素按 SPEC-004 §8 读成词,每个词原样到达链接器。插件**禁止**依赖
90-
引擎内部的转义拼写(例如 `'$$ORIGIN'`)。(**未实现**,mcpp#703:当前 `ldflags` 与
90+
引擎内部的转义拼写(例如 `'$$ORIGIN'`)。(**已实现**,mcpp#703;此前 `ldflags` 与
9191
`link_flag` 中的 `$` 会被宿主 shell 展开)
9292

9393
## 3. 施工:action
@@ -96,7 +96,7 @@
9696
自己发现的输入用 depfile 报告。(argv 与 depfile **已实现**;工具作为输入是作者义务)
9797
- **R3.2** action **必须**在提交时命名它的输出文件,因为引擎在规划期确定源集合、指纹与模块
9898
图。输出文件名在施工前无法得知的工作(安装一个前缀、解开一个 SDK)使用 `prepare` 角色。
99-
(输出命名 **已实现**;`prepare` **未实现**)
99+
(**已实现**;`prepare` 随 mcpp#702)
100100
- **R3.3** 角色:
101101

102102
| `role` | 输出 | 顺序 |
@@ -105,21 +105,23 @@
105105
| `object` | 加入链接集 | 链接边消费它 |
106106
| `artifact` | 新文件,输入是链接产物 | 链接之后 |
107107
| `check` | 引擎写的 stamp | 与编译并行;`blocking = true` 时声明包的编译边等待它 |
108-
| `prepare` | 引擎写的 stamp;命令填充它用 `output_dir` 声明的目录,构建按目录引用其内容 | 声明包的每条编译边与链接边等待它 |
109-
110-
`prepare`(**未实现**,mcpp#702)是施工,不是校验,任何只针对校验的策略都不作用于它。它的
111-
产物由配置以目录为单位引用(`include_dir`、`link_search`、`runtime_search_dir`),或以
112-
`link_flag` 中的完整路径引用;这些名字在配置时确定,内容在施工时到达。一个 `prepare` action
113-
**必须**用 `output_dir` 声明它填充的目录;命令成功而该目录不存在时,引擎不写 stamp,并以
114-
指出该目录的消息使这条边失败。
108+
| `prepare` | 引擎写的 stamp;命令填充它用 `output_dir` 声明的目录,构建按目录引用其内容 | 声明包的每条编译边与计划中的每条链接边(静态库归档除外)等待它 |
109+
110+
`prepare`(**已实现**,mcpp#702)是施工,不是校验,任何只针对校验的策略都不作用于它;构建
111+
输出以 `PREPARE` 标注它。它的产物由配置以目录为单位引用(`include_dir`、`link_search`、
112+
`runtime_search_dir`),或以 `link_flag` 中的完整路径引用;这些名字在配置时确定,内容在施工
113+
时到达。一个 `prepare` action **必须**用 `output_dir` 声明它填充的目录(缺少时引擎拒绝该
114+
action);命令成功而该目录不存在,或除该 action 的 stamp 之外不含任何文件时,引擎不写
115+
stamp,并以指出该目录的消息使这条边失败。链接边等待所有 `prepare`,因为每个包的链接全局
116+
指令并入整个计划共用的一份链接意图。
115117
- **R3.4** `check` 只用于校验,**禁止**用来表示施工。(作者义务)
116118
- **R3.5** `check` 与 `prepare` 的命令成功后,引擎创建或更新它们的 stamp,使 stamp 新于该
117119
action 的每个输入;命令失败时不写 stamp。命令无需自己写 stamp。(创建自 2026.8.29.1
118-
**已实现**;更新**未实现**,mcpp#702:当前一个已存在的 stamp 不被更新,输入改变一次后该
120+
**已实现**;更新 **已实现**,mcpp#702;此前一个已存在的 stamp 不被更新,输入改变一次后该
119121
action 在此后每次构建中都会重新运行)
120-
- **R3.6** 角色**应当**以常量书写(`mcpp::roles::prepare` 等),使不认识该角色的旧引擎在编译
121-
构建程序时拒绝它;引擎拒绝未知的角色字符串,并列出可用的角色。(**未实现**,mcpp#702:
122-
当前引擎把未知的角色字符串当作 `source`)
122+
- **R3.6** 角色**应当**以常量书写(`mcpp::roles::source`、`check`、`object`、`artifact`、
123+
`prepare`),使不认识该角色的旧引擎在编译构建程序时拒绝它;引擎拒绝未知的角色字符串,并列出
124+
五个角色。(**已实现**,mcpp#702;此前引擎把未知的角色字符串当作 `source`)
123125
- **R3.7** 构建期**不应**访问网络:下载属于安装期(载荷的安装、依赖的解析)。一个必须在构建期
124126
下载的 action(例如由包管理器取得源码)**必须**在其说明中写明,并在离线构建中
125127
(`--offline` 或 `MCPP_OFFLINE=1`;前者在进程环境中设置后者,action 继承之)不访问网络:
@@ -130,13 +132,14 @@
130132
- **R4.1** 一个目录中的共享库由施工产生(`prepare`)或文件名不定时,插件**必须**用
131133
`runtime_search_dir` 声明该目录。引擎把它用于 ELF 与 Mach-O 的运行路径(RUNPATH/rpath,
132134
从不作为 `-L`)、`mcpp run` 的加载路径、`mcpp pack` 的闭包搜索与运行时校验,并把依赖包的
133-
声明传到消费方的可执行文件。(**未实现**,mcpp#702)
135+
声明传到消费方的可执行文件。(**已实现**,mcpp#702)
134136
- **R4.2** 一个在配置时已知的文件需要位于程序旁的某个相对位置时(Qt 的平台插件、Vulkan 的
135137
ICD 清单),插件用 `deploy`。(**已实现**,协议 11)
136138
- **R4.3** Windows 的可执行文件没有运行路径。`mcpp run` 通过 `PATH` 使用运行时搜索目录,
137139
`mcpp pack` 把闭包需要的 DLL 放到程序旁(**已实现**)。链接之后,引擎把程序直接或间接导入的、
138140
位于其运行时搜索目录中的非系统 DLL 放到程序旁,使从构建目录直接启动的程序同样能找到它们;
139-
闭包的求解与 `mcpp pack` 相同。(**未实现**,mcpp#702)
141+
闭包的求解与 `mcpp pack` 相同,DLL 在其目录中被替换后下一次构建再次放置。(**已实现**,
142+
mcpp#702)
140143
- **R4.4** 插件**禁止**在 `link_flag` 中写运行路径(`-Wl,-rpath,...`),**必须**使用 R4.1。
141144
(作者义务)
142145

@@ -145,10 +148,10 @@
145148
- **R5.1** 规划运行构建程序,不运行任何 action(SPEC-005 R2.2)。插件遵守 R1.3 时,规划得出
146149
的配置与构建相同。(**已实现**)
147150
- **R5.2** 一个包的构建程序在规划中失败时,该包只按其清单描述,并得到一条错误诊断;成员的
148-
其余部分照常描述。插件遵守 R1.2 时,环境不完整不会使构建程序失败。(**未实现**,mcpp#702)
151+
其余部分照常描述。插件遵守 R1.2 时,环境不完整不会使构建程序失败。(**已实现**,mcpp#702)
149152
- **R5.3** 插件所需的宿主工具在规划中构建失败时,规划继续,构建程序收到该工具将被发布的路径,
150153
并产生一条警告。插件**应当**在 action 中运行宿主工具,而不是在构建程序中运行,使规划不依赖
151-
工具能否构建。(引擎部分 **未实现**,mcpp#702;「应当」为作者义务)
154+
工具能否构建。(引擎部分 **已实现**,mcpp#702;「应当」为作者义务)
152155

153156
## 6. 环境与载荷
154157

@@ -179,3 +182,4 @@
179182
| 版本 | 日期 | 变更 |
180183
|---|---|---|
181184
| 0.1 | 2026-09-26 | 首版草案(mcpp#699、#701、#702、#703)。 |
185+
| 0.2 | 2026-09-26 | 随 mcpp 2026.9.27.1 落地:R1.3 的警告、R2.1 的 `runtime_search_dir`、R2.4、R3.3 的 `prepare`(目录须含文件;链接边等待所有 `prepare`)、R3.5、R3.6、R4.1、R4.3、R5.2、R5.3 标为已实现。 |

‎docs/zh/04-mcpp-toml.md‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1389,7 +1389,7 @@ Link intent 把各个发现阶段分开处理:
13891389
|---|---|---|---|
13901390
| `link_library_dirs` | `-L` | `-L` | `-L` 或 `/LIBPATH:` |
13911391
| `transitive_needed_dirs` | `-Wl,-rpath-link` | 无旗标 | 无旗标 |
1392-
| `runtime_search_dirs` | 仅 RUNPATH/rpath,从不是 `-L` | 仅 rpath | 无旗标 |
1392+
| `runtime_search_dirs` | 仅 RUNPATH/rpath,从不是 `-L` | 仅 rpath | 无旗标;链接之后,程序从这些目录导入的 DLL 被放到程序旁 *(2026.9.27.1+)* |
13931393
| `frameworks` | 无旗标 | `-framework` | 无旗标 |
13941394
| `deploy_files` | 拷贝边 | 拷贝边 | 拷贝到输出旁边;从不是链接器旗标 |
13951395
| `deploy` *(2026.9.12.2+)* | 拷贝边,进 `bin/<to>/` | 拷贝边,进 `bin/<to>/` | 拷贝边,进 `bin/<to>/`;从不是链接器旗标 |
@@ -1420,6 +1420,15 @@ Link intent 把各个发现阶段分开处理:
14201420
那个不再获得新指令的遗留字段 `library_dirs`。这个目录在该程序运行时不必
14211421
存在;一个 `prepare` action 可能在构建期之后才把它填充起来。
14221422

1423+
PE 映像没有运行路径,所以在 Windows 上运行时搜索目录服务于 `mcpp run`(把它放进
1424+
`PATH`)与 `mcpp pack`(暂存闭包)。自 2026.9.27.1 起,计划中带运行时搜索目录的
1425+
PE 程序在链接之后多一条边 `mcpp place-dlls`:它按 `mcpp pack` 的方式读取程序的
1426+
导入闭包,把程序直接或经另一个 DLL 间接导入、且在这些目录之一中解析到的每个 DLL
1427+
放到程序旁。系统 DLL 与 API set 从不复制,副本只在字节不同时写入,某个 DLL 在
1428+
其目录中被替换后,下一次构建会再次放置它。于是从构建目录直接启动的程序能找到
1429+
它们,与 vcpkg 的 applocal 步骤或 CMake 的 `$<TARGET_RUNTIME_DLLS>` 之后相同。两个
1430+
目录提供同一个名字时,放置按搜索顺序的第一个,并以一条说明指出两者。
1431+
14231432
`target/<triple>/<fp>/resolution.json` schema 2 存储 RuntimeBinding、
14241433
规范化后的要求/提供者/产物、LinkIntent、平台发现机制与链接后判定。
14251434
`mcpp why runtime` 是对最新存储文件的一个纯粹解读器:它既不会重新

0 commit comments

Comments
 (0)