Skip to content

Commit 2ea1b7c

Browse files
committed
feat: [resources] 与版本身份 —— 两处「模型比生态少一层」(#365, #363)
两个 issue 领域无关,失效形状同一条:生态已经产出的东西 mcpp 的模型 表达不了,于是走到一条「不报错但结果是错的」路径上。 #365 Windows 资源 - 新增 `[resources]`:icon 与版本信息一行搞定,元数据从 [package] 取默认值; 自写 .rc 走 files = [...],被当作构建输入跟踪。 - 只有 PE 目标消费;非 PE 上「不适用」(不做事、不警告、逐字节不变), 因此不需要也不能用 cfg(windows) —— 条件通道只载 BuildInputs。 - 声明了却不存在的文件是硬错误(对 issue 第 3 条的有意偏离)。 - 新增 BuildAction::Role::Object:产出接到链接输入,补齐角色表原本缺的 那一格;ldflags 塞路径不产生任何 implicit input,正是本 issue 的成因。 ⚠️ issue 结尾「llvm-rc 生成的 VERSIONINFO 不被解析」的归因是错的,已实测 证伪:VS_VERSION_INFO 是 <windows.h> 的宏,没有它时资源被存成字符串名而 非序号 1,而类型两种情况都是 RT_VERSION(16) —— 这正是它看着正常的原因。 合成脚本写字面 1,构造性正确;自写脚本命中这个形状时给出指名的警告。 #363 版本身份 - resolve_semver 返回索引的字面键,不再从解析出的数字重造地址。 真实索引里被这条修好的:1.92.8-docking(预发布塌成 1.92.8)、 25.0.4.7.1(五段截断成不存在的 25.0.4.7)、25.0.4={ref=…}(别名与 自己的目标构成平局)、pre-v0.0.5(报「no valid versions in index」)。 - 预发布按 SemVer 排序 + npm 预发布可见性规则;数值段任意长度; 别名不参与范围候选;不可排序键只精确匹配并给出可粘贴的 pin 行; 只差 build metadata 的真平局硬错,而精确形式按字面命中。 - mcpp.lock 记录解析结果并覆盖传递依赖,Compiling 行同源;两者原本读的 都是未解析的 m->dependencies,而 ResolvedRecord 早就覆盖整张图。 lock 本批不「权威化」,文件头自己声明这一点(e2e 断言,改时会红)。 实施中撞出来的(设计里没有):非 ASCII 元数据会让 rc 编译器拒绝整个脚本, 必须传 UTF-8 codepage,否则中文描述的项目根本构建不了。 设计与实测证据:.agents/docs/2026-08-07-windows-resources-and-version-identity-design.md
1 parent c9b561a commit 2ea1b7c

31 files changed

Lines changed: 3192 additions & 158 deletions

.agents/docs/2026-08-07-windows-resources-and-version-identity-design.md

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

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,16 @@ jobs:
296296
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
297297
bash tests/e2e/102_mingw_cross_wine.sh
298298
299+
# mcpp#365. This is the only job with a MinGW cross toolchain, so it is
300+
# the only place the GNU half of resource compilation (windres -O coff,
301+
# because GNU ld cannot consume a .res) can run at all — the Linux e2e
302+
# shards skip it for want of the `mingw-cross` capability. Named
303+
# explicitly for the same reason 102 is.
304+
- name: "e2e: windows resources (windres / COFF)"
305+
run: |
306+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
307+
bash tests/e2e/198_windows_resources_cross.sh
308+
299309
# ── windows → linux ───────────────────────────────────────────────────────
300310
# The mirror of mingw-cross-wine. Two jobs because a Windows runner cannot
301311
# execute the ELF it produces; the artefact is handed to a Linux job and

CHANGELOG.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,54 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6+
## [2026.8.7.1] — 2026-08-07
7+
8+
两处「模型比生态少一层」。设计与实测证据见 `.agents/docs/2026-08-07-windows-resources-and-version-identity-design.md`
9+
10+
### 新增
11+
12+
- **`[resources]`:exe 图标与版本信息现在是 `mcpp.toml` 里的一行(#365)。**
13+
14+
```toml
15+
[resources]
16+
icon = "assets/app.ico"
17+
```
18+
19+
`FILEVERSION` / `ProductName` / `FileDescription` / `CompanyName` / `LegalCopyright` 全部从 `[package]` 取默认值,资源脚本由 mcpp 生成。自写 `.rc``files = [...]`,mcpp 编译并**跟踪**它。
20+
21+
**只有 PE 目标消费这一节**;在 Linux/macOS 上它「不适用」——不是降级、不是带警告地跳过:没有消费者,构建逐字节不变,也不说话。所以**不需要(也不能)加 `cfg(windows)` 谓词**。节名不叫 `[windows]` 是因为「图标」作为概念不是 Windows 专有的,将来 macOS `.icns` 扩同一节而不是把这条轴按 OS 切三份。
22+
23+
**声明了却不存在的文件是硬错误**,这是对 issue 第 3 条请求的**有意偏离**:mcpp 里每个「声明过的输入」都是这个规则(`main = "…"` 必须匹配恰好一个文件、nasm 缺失是硬错误),而「缺失就跳过」会把这个 feature 要消灭的失效模式写成规定行为——一个没有图标、没有版本信息、且什么都没说的正式二进制。不要图标已经可表达:把那一行删掉。
24+
25+
- **`role = "object"`:build.mcpp 的 action 现在能把产物接到链接输入上。** 角色表原本三格接在「编译输入 / 无 / 链接输出」上,缺的正是「链接输入」——一个构建图显然有的接线点。后果不是理论上的:预编译对象只能塞进 `[build].ldflags`,而那是链接命令里的一串字符、不是图里的文件,于是改了图标得到 `ninja: no work to do`。可选 `.target("name")` 指定接哪条边,省略 = 声明包的全部镜像;未知名字报错而不是静默不接。
26+
27+
### 修复
28+
29+
- **解析出的版本现在是索引里的字面键,不是重新渲染的数字(#363)。** `resolve_semver` 一直把索引的字面版本键读到手里,然后 `return parsed[i].str()` —— 从解析出的数字重造一个地址。渲染器复现不了的东西就变成了不存在的地址:
30+
31+
| 上游键 | 旧行为 |
32+
|---|---|
33+
| `1.92.8-docking` | 截断成 `1.92.8`,与非 docking 那个**塌成同一个可比较版本**(两个不同 tarball) |
34+
| `25.0.4.7.1`(jdk-corretto,五段) | 截断成 `25.0.4.7` —— **索引里没有这个键** |
35+
| `pre-v0.0.5`(khistory,唯一的发布) | 静默跳过,然后报「no valid versions in index」——把责任推给一个发布得好好的包 |
36+
37+
现在字面键与序一起传递,`version_req` 只负责**排序**。连带修的:
38+
39+
- **预发布按 SemVer 排序**,且范围按 npm/Cargo 规则**看不见预发布**,除非约束自己在同一数值元组上带了预发布。`^1.92.8` 因此确定性地选 `1.92.8`,不再在两个 tarball 之间由一个看不见差别的序做取舍。同一条规则顺带修掉 `^1.2.3` 会漏进 `2.0.0-alpha`
40+
- **数值段不再截断在第四段**。真实索引里 `jdk-corretto` 发五段键;截断让 `25.0.4.7.1``25.0.4.7.2` 比较相等。
41+
- **别名条目(`{ ref = "…" }`)不再是范围候选**`jdk-temurin``["25.0.4"] = { ref = "25.0.4+7" }` 曾与它自己的目标构成一次「平局」。精确寻址不变。
42+
- **不可排序的键**(`b10069``latest``pre-v0.0.5`)成为一等公民的一类:只参与精确匹配,范围约束下报**指名的**错误并给出可粘贴的 pin 行。
43+
- **真平局硬错**。只差 build metadata 的两个键(`1.0.0+a` / `1.0.0+b`)是两个 tarball、两个 sha256,序说不出该要哪个;旧行为按描述符里的行序取第一个,意味着索引的一次排版调整会改变构建出来的东西。
44+
45+
- **mcpp.lock 记录解析结果,并覆盖传递依赖。** 它记的一直是**约束本身**(`version = "^1.92.8"`),而一个记录范围的 lock 不锁定任何东西;`Compiling compat.imgui v^1.92.8` 这行也一样。两者读的都是 `m->dependencies`(未解析的输入、且只有直接依赖),而解析结果 `ResolvedRecord` **早就覆盖整张图**——修法是把两个消费者都指过去,而不是补第三处回写。
46+
47+
lock 头部现在自己声明**它还不 pin 后续构建**(index 依赖仍每次从约束重新解析)。一个记着真实版本却不生效的文件,比一个明显记着范围的文件更容易被误当权威。
48+
49+
### 其他
50+
51+
- 版本号 2026.8.6.3 → **2026.8.7.1**
52+
- **行为变化(生态可见)**:`cc-connect` 这类「稳定版 + 预发布版」并存的包,`^1.3``1.3.3-beta.1` 改为解析到 `1.3.2`;`jdk-corretto`/`jdk-temurin` 这类带别名的包,范围解析改为选中真条目(`25.0.4.7.1` 而非别名 `25.0.4`),store 目录名随之变化。
53+
654
## [2026.8.5.4] — 2026-08-06
755

856
命令长度这一族缺陷的**第七次**,这次不再补洞。架构分析见 `.agents/docs/2026-08-06-command-length-architecture.md`

docs/05-mcpp-toml.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,100 @@ the other conditional dependency tables (§2.7.1). The **feature itself is
11511151
registered on every platform** — only what it pulls in is conditional — so
11521152
requesting it where no predicate matches is not an unknown-feature error.
11531153

1154+
### 2.15 `[resources]` — Metadata and Assets Embedded in the Artifact (2026.8.7.1+)
1155+
1156+
An exe icon and the version metadata Windows shows in a file's Properties dialog
1157+
are a path in `mcpp.toml`, nothing more:
1158+
1159+
```toml
1160+
[resources]
1161+
icon = "assets/app.ico"
1162+
```
1163+
1164+
That is the whole common case. `FILEVERSION`, `ProductName`, `FileDescription`,
1165+
`CompanyName` and `LegalCopyright` all default from `[package]`, and mcpp
1166+
generates the resource script for you.
1167+
1168+
| Key | Type | Meaning |
1169+
|---|---|---|
1170+
| `icon` | path | Embedded as the application icon (resource ordinal 1) |
1171+
| `files` | list of paths | Your own `.rc` scripts, compiled and **tracked** as build inputs |
1172+
| `extra-inputs` | list of paths | Inputs the `.rc` scanner could not see (see below) |
1173+
| `version-info` | bool | `false` opts out of the generated version resource |
1174+
| `[resources.version-info]` | table | `company`, `product`, `description`, `copyright`, `original-filename`, `internal-name` |
1175+
1176+
**Only PE targets consume this.** On Linux and macOS the section is
1177+
*inapplicable*: no work, no warning, byte-identical build. You do **not** need
1178+
(and cannot use) a `cfg(windows)` predicate — write it once, unconditionally.
1179+
1180+
**A declared file that does not exist fails the build.** A resource is a build
1181+
input like a source file; mcpp will not quietly ship a binary without it. If you
1182+
do not want an icon, delete the line.
1183+
1184+
**Version fields.** `FILEVERSION` takes the four numeric segments of
1185+
`[package].version`, each of which must fit in 16 bits; the string fields keep
1186+
the version verbatim, so a form the numeric fields cannot hold (`1.0.0-rc1`)
1187+
still shows up in the Properties dialog.
1188+
1189+
#### Writing your own `.rc`
1190+
1191+
```toml
1192+
[resources]
1193+
files = ["res/app.rc"]
1194+
```
1195+
1196+
With `files` set, mcpp stops generating a version resource — you own the
1197+
resource ID space. Set `version-info = true` alongside it if you want both (and
1198+
mind the collision: there can be only one `RT_VERSION` at ordinal 1).
1199+
1200+
To start from the generated script instead of a blank file, copy it out of the
1201+
build directory (`target/<triple>/<fp>/res/<target>.mcpp.rc`) and list it in
1202+
`files`. The result is byte-identical, so moving from generated to hand-written
1203+
never changes what ships.
1204+
1205+
> **`VS_VERSION_INFO` needs `<windows.h>`.** In a hand-written script,
1206+
> `VS_VERSION_INFO VERSIONINFO` without `#include <windows.h>` files the version
1207+
> resource under a *string* name instead of ordinal 1. Every tool still reports
1208+
> `Type: VERSIONINFO`, but `GetFileVersionInfo` looks up the ordinal, so
1209+
> PowerShell's `FileVersionInfo` shows every field as empty. Either include
1210+
> `<windows.h>` or write `1 VERSIONINFO`. mcpp warns when it sees this shape;
1211+
> the script it generates uses the literal `1`.
1212+
1213+
#### Tracked inputs
1214+
1215+
mcpp reads the `.rc` for quoted `#include`s and for the files named by resource
1216+
statements (`ICON`, `RCDATA`, `MANIFEST`, …), and makes them build inputs, so
1217+
editing your icon relinks. Angled includes (`<windows.h>`) are the toolchain's
1218+
and are covered by the toolchain fingerprint instead.
1219+
1220+
A file name reached through a macro (`1 ICON APP_ICON`) is invisible to that
1221+
scan. mcpp names what it could not resolve and asks you to declare it:
1222+
1223+
```toml
1224+
extra-inputs = ["assets/app.ico"]
1225+
```
1226+
1227+
#### Anything else: `role = "object"`
1228+
1229+
For inputs that are not resource scripts — a blob embedded with `objcopy`, a
1230+
generated `.def`, a pre-built object — a build program can declare a build-graph
1231+
node whose outputs join the link:
1232+
1233+
```cpp
1234+
mcpp::action o;
1235+
o.id = "blob"; o.role = "object";
1236+
o.arg("./mkblob.sh").arg("blob.bin").arg("${mcpp.out_dir}/blob.o")
1237+
.input("blob.bin")
1238+
.output("${mcpp.out_dir}/blob.o")
1239+
.target("myapp") // omit for every image this package produces
1240+
.submit();
1241+
```
1242+
1243+
See [07 — build.mcpp](07-build-mcpp.md). Naming such a file in
1244+
`[build].ldflags` also "works", but ldflags is a flat string in the link
1245+
command: nothing tracks it, and editing the file gives you `ninja: no work to
1246+
do`.
1247+
11541248
## Appendix A. Schema Ownership Principle (admission criteria for new fields)
11551249

11561250
> **Closed syntax, open vocabulary**: whoever owns the parsing semantics defines the keys; whoever owns the domain knowledge defines the values.

docs/07-build-mcpp.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ int main() {
170170
const std::string out = std::string(mcpp::out_dir()) + "/foo.pb.cc";
171171
mcpp::action a;
172172
a.id = "protoc:foo";
173-
a.role = "source"; // "source" | "check" | "artifact"
173+
a.role = "source"; // "source" | "check" | "object" | "artifact"
174174
a.arg(mcpp::dep_bin("protobuf", "protoc"))
175175
.arg("--cpp_out=...").arg("proto/foo.proto")
176176
.input("proto/foo.proto")
@@ -179,19 +179,33 @@ int main() {
179179
}
180180
```
181181

182-
Three roles, one primitive — `role` only decides where the edge's outputs
182+
Four roles, one primitive — `role` only decides where the edge's outputs
183183
attach:
184184

185185
| `role` | Outputs | Ordering | Typical |
186186
|---|---|---|---|
187187
| `source` | join the compile set | the compile edge consumes them | protoc, a transpiler |
188188
| `check` | a stamp file | runs **alongside** compilation (set `blocking = true` to gate it) | clang-tidy, a format or ABI check |
189+
| `object` | join the **link** set | the link edge consumes them | a resource compiler, `objcopy` embedding a blob, a generated `.def`, a pre-built `.o` |
189190
| `artifact` | a new file | its *inputs* are link outputs, so it runs after the link | codesign, packaging, size budgets |
190191

191192
No phase machinery is involved: ninja's own file dependencies do the
192193
sequencing, which is also why an `artifact` action cannot double-apply itself
193194
the way a naive "post-build hook" would.
194195

196+
`object` (2026.8.7.1+) takes an optional `.target("name")`, repeatable; omit it
197+
and the outputs attach to every image (binary / shared library) the declaring
198+
package produces. It needs the name because, unlike `artifact`, it runs *before*
199+
the link and so has no `${mcpp.target_file:…}` to infer one from — an unknown
200+
name is an error rather than an edge that quietly attaches to nothing.
201+
202+
> Naming a pre-built object in `[build].ldflags` also reaches the linker, and
203+
> should not be used for anything the build produces: ldflags is a flat string
204+
> in the link command, not a file in the graph, so nothing tracks it and editing
205+
> it gives you `ninja: no work to do`. For Windows resources specifically, use
206+
> [`[resources]`](05-mcpp-toml.md)
207+
> `object` is the escape hatch for everything else.
208+
195209
**You must name the output files.** mcpp fixes the source set, the fingerprint
196210
and the module graph during prepare, so an output whose *name* is unknown
197211
cannot be built. Content may arrive later; names may not. A malformed action is

docs/zh/05-mcpp-toml.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,89 @@ mcpp 写的包仍然能加载,这个读取器认识的部分照常生效。在
884884
—— 只有它拉进来的东西是条件性的 —— 因此在没有任何谓词匹配的平台上请求它,不是
885885
「未知 feature」错误。
886886

887+
### 2.15 `[resources]` —— 编译进产物的元数据与资产(2026.8.7.1+)
888+
889+
exe 图标,以及 Windows 在文件「属性」里显示的版本信息,就是 `mcpp.toml` 里的一个路径:
890+
891+
```toml
892+
[resources]
893+
icon = "assets/app.ico"
894+
```
895+
896+
常见场景到此为止。`FILEVERSION``ProductName``FileDescription``CompanyName`
897+
`LegalCopyright` 全部从 `[package]` 取默认值,资源脚本由 mcpp 生成。
898+
899+
|| 类型 | 含义 |
900+
|---|---|---|
901+
| `icon` | 路径 | 作为应用图标嵌入(资源序号 1) |
902+
| `files` | 路径列表 | 你自己的 `.rc` 脚本,mcpp 编译并**跟踪**为构建输入 |
903+
| `extra-inputs` | 路径列表 | `.rc` 扫描器看不见的输入(见下) |
904+
| `version-info` | 布尔 | `false` 表示不要生成版本资源 |
905+
| `[resources.version-info]` || `company``product``description``copyright``original-filename``internal-name` |
906+
907+
**只有 PE 目标消费这一节。** 在 Linux/macOS 上它**不适用**:不做事、不警告、
908+
构建逐字节不变。你**不需要**(也不能)加 `cfg(windows)` 谓词 —— 无条件写一次即可。
909+
910+
**声明了却不存在的文件会让构建失败。** 资源和源码一样是构建输入;mcpp 不会
911+
悄悄产出一个缺了它的二进制。不想要图标,把那一行删掉。
912+
913+
**版本字段。** `FILEVERSION``[package].version` 的四段数值,每段必须放得进
914+
16 位;字符串字段保留版本原文,所以数值字段装不下的形态(`1.0.0-rc1`)在属性
915+
对话框里照样看得到。
916+
917+
#### 自写 `.rc`
918+
919+
```toml
920+
[resources]
921+
files = ["res/app.rc"]
922+
```
923+
924+
写了 `files`,mcpp 就不再生成版本资源 —— 资源 ID 空间归你。想两者都要就同时写
925+
`version-info = true`(注意冲突:序号 1 的 `RT_VERSION` 只能有一个)。
926+
927+
想从生成的脚本起步而不是从空文件起步:把它从构建目录里拷出来
928+
(`target/<triple>/<fp>/res/<target>.mcpp.rc`)填进 `files`。结果**字节相同**,
929+
所以从「生成」走到「手写」不会改变产物。
930+
931+
> **`VS_VERSION_INFO` 需要 `<windows.h>`** 手写脚本里如果写
932+
> `VS_VERSION_INFO VERSIONINFO` 而没有 `#include <windows.h>`,版本资源会被存成
933+
> **字符串名**而不是序号 1。所有工具依然报告 `Type: VERSIONINFO`,但
934+
> `GetFileVersionInfo` 查的是序号,于是 PowerShell 的 `FileVersionInfo` 里每个字段
935+
> 都是空的。要么 include `<windows.h>`,要么直接写 `1 VERSIONINFO`。mcpp 见到这个
936+
> 形状会警告;它自己生成的脚本用的是字面 `1`
937+
938+
#### 被跟踪的输入
939+
940+
mcpp 会读 `.rc`,把引号形式的 `#include` 和资源语句(`ICON``RCDATA`
941+
`MANIFEST` …)点名的文件都变成构建输入,所以改图标会重链。尖括号形式
942+
(`<windows.h>`)属于工具链,由工具链 fingerprint 覆盖。
943+
944+
通过宏间接引用的文件名(`1 ICON APP_ICON`)扫描看不见。mcpp 会**指名**它没能解析
945+
的东西,并要求你显式声明:
946+
947+
```toml
948+
extra-inputs = ["assets/app.ico"]
949+
```
950+
951+
#### 其余一切:`role = "object"`
952+
953+
不是资源脚本的输入 —— `objcopy` 嵌入的 blob、生成的 `.def`、预编译对象 ——
954+
可以由构建程序声明一个产出接到链接的图节点:
955+
956+
```cpp
957+
mcpp::action o;
958+
o.id = "blob"; o.role = "object";
959+
o.arg("./mkblob.sh").arg("blob.bin").arg("${mcpp.out_dir}/blob.o")
960+
.input("blob.bin")
961+
.output("${mcpp.out_dir}/blob.o")
962+
.target("myapp") // 省略则接到本包产出的每个镜像
963+
.submit();
964+
```
965+
966+
[07 — build.mcpp](07-build-mcpp.md)。把这类文件写进 `[build].ldflags` 也「能用」,
967+
但 ldflags 是链接命令里的一串字符:没有任何东西跟踪它,改了它得到的是
968+
`ninja: no work to do`
969+
887970
## 附录 A. Schema 所有权原则(新字段准入标准)
888971

889972
> **语法封闭,词汇开放**:谁拥有解析语义谁定义键;谁拥有领域知识谁定义值。

docs/zh/07-build-mcpp.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ int main() {
156156
const std::string out = std::string(mcpp::out_dir()) + "/foo.pb.cc";
157157
mcpp::action a;
158158
a.id = "protoc:foo";
159-
a.role = "source"; // "source" | "check" | "artifact"
159+
a.role = "source"; // "source" | "check" | "object" | "artifact"
160160
a.arg(mcpp::dep_bin("protobuf", "protoc"))
161161
.arg("--cpp_out=...").arg("proto/foo.proto")
162162
.input("proto/foo.proto")
@@ -165,17 +165,27 @@ int main() {
165165
}
166166
```
167167

168-
三种 role,一个原语 —— `role` 只决定这条边的输出接到哪:
168+
四种 role,一个原语 —— `role` 只决定这条边的输出接到哪:
169169

170170
| `role` | 输出 | 顺序 | 典型 |
171171
|---|---|---|---|
172172
| `source` | 进编译集 | 编译边消费它们 | protoc、转译器 |
173173
| `check` | 一个 stamp 文件 | **与编译并行**(`blocking = true` 才前置) | clang-tidy、格式/ABI 检查 |
174+
| `object` |**链接**| 链接边消费它们 | 资源编译器、`objcopy` 嵌 blob、生成的 `.def`、预编译 `.o` |
174175
| `artifact` | 一个新文件 | 它的**输入**是链接产物,所以在链接之后跑 | 签名、打包、size budget |
175176

176177
全程不涉及任何 phase 机制:顺序由 ninja 自己的文件依赖决定 —— 这也是为什么
177178
`artifact` 不会像朴素的「post 构建钩子」那样把自己重复施加一遍。
178179

180+
`object`(2026.8.7.1+)可选 `.target("name")`,可重复;省略则接到声明包产出的
181+
每个镜像(可执行 / 动态库)。它必须写名字:与 `artifact` 不同,它跑在链接**之前**,
182+
没有 `${mcpp.target_file:…}` 可以反推 —— 未知名字是错误,而不是一条静默不接的边。
183+
184+
> 把预编译对象写进 `[build].ldflags` 同样能到达链接器,但**不要**用它承载构建产物:
185+
> ldflags 是链接命令里的一串字符、不是图里的文件,没有任何东西跟踪它,改了它得到的是
186+
> `ninja: no work to do`。Windows 资源请用 [`[resources]`](05-mcpp-toml.md);
187+
> `object` 是其余一切的出口。
188+
179189
**必须写出输出文件名。** mcpp 在 prepare 期就定死源码集、fingerprint 与模块图,
180190
所以名字未知的产物无法构建。内容可以晚到,名字不行。畸形 action 是**硬错误**,
181191
绝不静默跳过。

mcpp.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcpp"
3-
version = "2026.8.6.3"
3+
version = "2026.8.7.1"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

0 commit comments

Comments
 (0)