Skip to content

Commit 5db679f

Browse files
committed
docs(plan): stages one and two landed, and three judgements they corrected
Records what implementation established, including where it contradicted the design. * link-flag reaches the consumer. The doc ruled it private by analogy with include-dir; the code refutes the analogy. linkUsage.ldflags is a copy of buildConfig.ldflags and propagateLinkFlags pushes every dependency ldflag to the consumer, so a private link flag is not a policy the engine can express. include-dir is private because a compile interface HAS a declarative public counterpart; link flags do not, so the computed form must behave like its declarative twin. C4 is rewritten accordingly. * exports does not imply hidden visibility. The narrowing is link-time on all three formats, so implying a compile-time effect gives one key two, and the second changes how the library's own TUs see each other -- a separate decision. C3 is retired because it asserted exactly that coupling. * Section 4.3's open question is answered. manifest_emit builds [[runtime.artifacts]] from doc.legs plus one interface entry and does NOT carry the author's declared artifacts; role is a free string in the parser and the white list is an effect of its readers. So stage three is a new path through the packer -- carry a declared file into the artifact -- not a new role value. Staging is unchanged; its rationale is.
1 parent 44cd2b3 commit 5db679f

1 file changed

Lines changed: 52 additions & 16 deletions

File tree

.agents/docs/2026-09-07-general-build-infrastructure-gaps-design.md

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,19 @@ vk_icdNegotiateLoaderICDInterfaceVersion
102102
引擎按方言渲染",而不是让作者写三份平台专用文件。导出面是同一形状的第二个实例,
103103
因此它不是一个新概念,是一条既有原则的应用。
104104

105-
### 2.3 声明 `exports` 隐含编译期默认 hidden
105+
### 2.3 声明 `exports` 不隐含编译期 hidden(实现时更正)
106+
107+
初稿写的是"声明 `exports` 时引擎同时把编译期默认置为隐藏"。**实现时更正为不隐含。**
108+
109+
三种格式上的收窄都是**链接期**属性:version script 限制的是动态符号表,
110+
`-exported_symbols_list``.def` 同理。因此隐含一个编译期效果会让一个键有两个效果,
111+
而第二个效果还改变**本库各翻译单元之间**如何看见彼此 —— 那是一个有独立理由的独立决定。
112+
113+
`-fvisibility=hidden` 仍可经 `[build] cxxflags` 使用以取得代码生成收益。相应地,初稿的
114+
判据 C3(声明后夹具应链接失败)**作废**,因为它断言的正是这个被取消的耦合。
115+
116+
<details>
117+
<summary>初稿原文(已作废)</summary>
106118

107119
仅有 version script 会收窄动态符号表,但对象里的符号仍是默认可见性,链接期优化拿
108120
不到收益,而且 Mach-O 与 PE 的渲染需要编译期配合。因此:**声明 `exports` 时,引擎
@@ -112,6 +124,8 @@ vk_icdNegotiateLoaderICDInterfaceVersion
112124
在声明 `exports` 之后会链接失败。这正是作者声明 `exports` 时所要求的语义,失败点
113125
也在链接期而非运行期,因此是可接受的。
114126

127+
</details>
128+
115129
### 2.4 不做什么
116130

117131
- **不做符号版本的完整语法。** `foo@@LIB_1.0``foo@LIB_0.9` 并存是 ELF 独有的
@@ -142,17 +156,23 @@ mcpp:link-flag=<flag> mcpp::link_flag(s)
142156

143157
按发出顺序追加,位置在 manifest 的 `[build] ldflags` 之后。
144158

145-
### 3.3 传播性:私有
159+
### 3.3 传播性:到达消费者(实现时更正)
160+
161+
初稿判它私有,与 `include-dir` 同规。**实现时更正:这个类比是假的,而且代码就是证据。**
146162

147-
`link-flag` **只作用于本包的链接,不到达消费者**,与 `include-dir` 同规。理由相同:
148-
一个依赖发出的任意标志落到消费者的链接行上,正是 `include-dir` 的私有性所要避免的
149-
耦合。
163+
`linkUsage.ldflags``buildConfig.ldflags` 的一份拷贝,`propagateLinkFlags` 把依赖的
164+
每一条 ldflag 推到消费者 —— **引擎今天没有"私有链接标志"这个策略可表达**
150165

151-
`link-script` 是既有的例外,而它的例外理由在文档里写得很清楚 —— 板级内存布局是
152-
消费者无法自行写出的东西。任意标志不具备这条性质,因此不继承这个例外。
166+
更要紧的是类比本身错在哪:`include-dir` 私有,是因为编译接口有一个声明式的公开对应物
167+
(`[build] include_dirs`),构建期程序若能加宽它就是绕过 manifest。链接标志没有这个
168+
分裂 —— `[build] ldflags` 本来就传播。让"算出来"的形态与它自己的声明式孪生行为不同,
169+
才是不一致,而不是防护。
153170

154-
依赖确实需要改变消费者链接方式的情形,已有 `[runtime]` 的 link intent 承担,且那条
155-
路径是中立的、可按方言渲染的。
171+
**后果写明而不藏起来**:依赖发出的 `--version-script` 也会落到消费者链接行上,而这通常
172+
不是它的本意。这个隐患**不是新的** —— 依赖在 `[build] ldflags` 里写同一条标志一直如此
173+
—— 所以这条指令加宽的是**谁能算出这个值**,不是**这个值能到达哪里**
174+
175+
相应地,C4 的反向断言("不出现在消费者的链接行上")作废。
156176

157177
## 4. 缺口三:包内布局
158178

@@ -208,8 +228,23 @@ ICD JSON 的内容里要写 `.so` 的位置,而那是构建期才知道的。两
208228
mcpp:artifact=<role>=<relpath> mcpp::artifact(role, relpath)
209229
```
210230

211-
**开放问题**:`mcpp pack` 目前决定哪些文件进入产物。构建程序贡献的 artifact 条目
212-
与 pack 的选择规则如何合并,需要在实现前读 `src/pack` 确定,本文不预设答案。
231+
**开放问题已查清**(2026-09-07 读 `src/pack/manifest_emit.cppm`):
232+
233+
`[[runtime.artifacts]]` 的发出**完全由 packer 自己决定**,它从 `doc.legs`(构建出来的
234+
库)加一条 `interface` 条目生成,**不携带作者在源 manifest 里写的 `[runtime].artifacts`**
235+
`role` 在 manifest 解析侧是自由字符串,没有白名单;白名单效应来自读者:`prebuilt.cppm`
236+
`static-library` / `shared-library` / `interface`,`prepare.cppm` 认前两个。
237+
238+
因此本项的工作量比初估大,且落在 packer 而非 manifest 解析:
239+
240+
1. `mcpp:artifact=<role>=<relpath>` 指令(与 §3 的 `link-flag` 同形,一行表项);
241+
2. packer 要把该文件**拷进产物**并把条目**写进描述符** —— 这是新行为,今天的 packer
242+
只发它自己产出的东西;
243+
3. 该文件的内容通常由构建程序生成(ICD JSON 里要写 `.so` 的位置),所以第 2 步接收的
244+
是构建目录里的一个路径,而落点是包内相对路径。
245+
246+
结论:**这一项不是"加一个 role",是给 packer 增加一条"携带被声明的文件"的通路。**
247+
分期不变(三期),但依据从"小改动"改为"边界清楚、工作量中等,且不阻塞其他各项"。
213248

214249
## 5. 缺口四:探测库(是包,不是引擎)
215250

@@ -687,8 +722,8 @@ Triton IR → Linalg IR → AscendNPU IR → 算子二进制,配套 `triton-asce
687722
|---|---|
688723
| C1 | 声明 `exports` 的共享库,`nm -D --defined-only` 只列出声明的符号;不声明时列出全部。两侧都断言,否则"少了几个"与"根本没链上"读数相同 |
689724
| C2 | 同一份 `exports` 在 ELF 与 PE 上各渲染一次,两边导出集合**相同**。跨平台是这条设计的全部理由,单平台绿零信息量 |
690-
| C3 | 声明 `exports` 后编译期默认为 hidden:一个依赖默认可见性做跨 DSO 内部调用的夹具**链接失败**,且失败点在链接期 |
691-
| C4 | 构建程序发出的 `link-flag` 出现在链接命令行上,顺序在 `ldflags` 之后;**且不出现在消费者的链接行上**(私有性的反向断言) |
725+
| ~~C3~~ | **作废**(§2.3):它断言的隐含 hidden 在实现时被取消,一个键一个效果 |
726+
| C4 | 构建程序**算出来的** `link-flag` 到达链接器。判据是**链接器的行为**而非命令行文本:e2e 620 让程序算出 `-Wl,--defsym=…=42`,产物打印那个符号的地址。grep build.ninja 会对"写下了但没交给链接器"同样成立 |
692727
| C5 | `role = "manifest"` 的文件在打包后位于声明的相对路径上,内容里的路径为包内相对路径。判据读**打包后的产物**,不读构建目录 |
693728
| C6 | 探测库在一台**没有宿主编译器**的机器上仍能完成探测。这是 §5.2 唯一能证伪的判据 |
694729
| C7 | 声明 `cfg(accelerator = "none")` 的回退源码,在 `accel` 为空时编译、在任意后端被命名时不编译;**且新增一个后端后该谓词的行为不变** —— 这条才是本项的理由,单后端下绿零信息量 |
@@ -706,9 +741,9 @@ C6 值得单独说明:它是这批里唯一无法在开发机上验证的判据
706741

707742
|| 内容 | 依据 |
708743
|---|---|---|
709-
|| `accelerator = "none"` | 最小,且它修的是一条**会随生态增长而静默失效**的写法 —— 越晚落地,要改的既有 manifest 越多 |
710-
|| `link-flag` | 同样最小:一条指令 + 一处透传。且它是 §2.4 的逃生口,应先于 `exports` 落地 |
711-
| | `exports` + 隐含 hidden | 打开"可发布稳定 ABI 的 `.so`"这一档,同时惠及运行时与驱动 |
744+
|(**已实现** 2026.9.6.5) | `accelerator = "none"` | 最小,且它修的是一条**会随生态增长而静默失效**的写法 —— 越晚落地,要改的既有 manifest 越多 |
745+
|(**已实现** 2026.9.6.5) | `link-flag` | 同样最小:一条指令 + 一处透传。且它是 §2.4 的逃生口,应先于 `exports` 落地 |
746+
|(**已实现** 2026.9.6.5,不含隐含 hidden) | `exports` | 打开"可发布稳定 ABI 的 `.so`"这一档,同时惠及运行时与驱动 |
712747
|| `role = "manifest"` + `mcpp:artifact` | 只有驱动这一档需要;且受 §4.2 的发布顺序约束,越早落地引擎越好 |
713748
|| `mcpplibs:probe` | 与引擎正交,任何时候可做;但 C6 要求它一开始就跑在 hermetic job 里 |
714749
|| 生成输入粒度 | 触发条件明确(§6.2),未达到该规模前不做 |
@@ -742,5 +777,6 @@ C6 值得单独说明:它是这批里唯一无法在开发机上验证的判据
742777
| 修订一 | 两处判断被推翻并就地更正:**RDC 归插件侧,引擎零改动**(§0.2);**stdpar 可岛化,初稿的"互斥"说法过头**(§8.1)。新增三处缺口(§6)、归属规则(§7)、编程模型三分法(§8)、术语对齐建议(§9)、以及用陌生厂商证伪的实验(§10) |
743778
| 修订二(第三处结论已被修订三推翻) | §10 的三处不确定实地调研(§10.3),两处证实一处未证实。第三处更正:**"有硬件或模拟器"不是开工硬闸**,架构验证完全在构建期(§10.4);真实前置只有毕昇的合规取得。新增两条实测结论:`cpu` 模式**不走岛**因而不能充当设备判据(§10.3.1),以及 CMake 把 Ascend C 当作一门语言 —— 同一条轴、不同归属的第三方佐证(§10.3.4) |
744779
| 修订三 | §10.3.3 的结论被**推翻**:工具包官方镜像 `swr.cn-south-1.myhuaweicloud.com/ascendhub/cann` **可匿名拉取**(走完 token 握手实测),且从厂商自有 registry 取正落在不变量允许的一档。同时纠正一个框架错误:毕昇与模拟器**在同一个包里**,是一个获取问题不是两个。**至此该实验没有阻碍项。** 另记 AscendNPU IR 已开放,规则包存在第二个切入高度(§10.3.3b) |
780+
| 修订六 | 一、二期实现落地(mcpp 2026.9.6.5),实现过程中三处判断被更正:**`link-flag` 到达消费者**(§3.3,`linkUsage.ldflags``buildConfig.ldflags` 的拷贝,私有形态引擎无从表达);**`exports` 不隐含 hidden**(§2.3,一个键一个效果);**C3 作废、C4 改写**。另查清 §4.3 的开放问题:packer 不携带作者声明的 artifacts,所以三期是给 packer 增加一条通路而非加一个 role |
745781
| 修订五 | 综合复核。两处补齐:验证项此前**既无判据也无期次**,现补为 C9 与 §13.1;`kind = "device"` 此前是孤儿条目,现按"两把尺子"说明它落在引擎侧但属领域,归 docs/20。一处更正:C9 的严判据 `git diff src/` 为空**分不开两种失败**(吸收了厂商知识 vs 缺一项通用能力),改为两级判据,主判据用既有的 vendor-probe 测试(§10.5.1) |
746782
| 修订四 | 最后一条待确认项闭合:`sim` **确实调用 bisheng**,由 `asc-devkit` 内 vendored 的 `ASC_CMake` 证实 —— ASC 是一门 CMake 语言,其编译器就是 `bisheng`,且**全仓 `RUN_MODE` 判断只区分 `cpu` 与非 `cpu`,不存在 `sim` 分支**。至此 §10 无待确认项 |

0 commit comments

Comments
 (0)