## 症状 每个 protobuf 消费者的每次构建都会刷一条: ``` warning: src/protobuf.cppm: lib target without conventional lib root 'src/protobuf.cppm' (create the file or set [lib].path) ``` 来源:`compat.protobuf` 描述符声明 `["protobuf"] = { kind = "lib" }`,mcpp 于是按约定推出模块根 `src/<name>.cppm`,发现文件不存在。 ## 为什么这条建议不成立 protobuf 是**纯 `.cc` / `.h` 的非模块 C++ 库**——它没有模块接口,也不会有。`src/protobuf.cppm` 不是「作者忘了建」的文件,而是一个**在这个包里不存在的概念**。于是这条 warning 给出的两个出路都不通: - "create the file" —— 要求作者为一个非模块库凭空造一个模块接口; - "set [lib].path" —— 没有可以指的东西。 `src/modgraph/validate.cppm:145` 的判据只有「`has_lib_target(manifest)` 且约定路径不存在」,它把**命名约定**当成了**要求**。约定的正确含义是「如果你有模块根,默认叫这个名字」,而不是「lib 就必须有模块根」。 ## 可用的判据 同一个函数往下几行就在做这件事:它遍历 `g.units` 找 lib root 单元,以便检查它导出的是主模块而不是分区。所以图里**有没有任何模块接口单元**这件事是现成的—— - 该 target 有模块接口、但约定根缺失 ⇒ 当前的 warning 是对的,保留; - 该 target **一个模块接口都没有** ⇒ 它是非模块库,约定根不适用,不该告警。 (也可以让描述符显式声明,但那会要求所有既有的非模块 compat 包都改一遍;从图上判定不需要生态动一根手指。) ## 影响面 仅告警。但它落在 protobuf / gRPC 这条最常用的链路上,每次构建都出现;与 #373 那条一样,长期刷屏会把诊断输出训练成噪声,而「lib root 缺失」在真正的模块库上是有意义的信号。 ## 复现 任意工程依赖 `compat.protobuf`(或直接 `mcpp new` 一个 grpc 模板工程)后 `mcpp build`。
Activity
speak-agent commentedon Aug 31, 2026
已修复,随 2026.8.30.1(commit
0117a9f,PR #536)发布。按 issue 给的判据实施,一字不差:图上有没有任何模块接口单元这件事是现成的,所以用它。
src/modgraph/validate.cppm:按 issue 的另一条建议,没有要求描述符显式声明 —— 从图上判定不需要生态动一根手指。
补两点实施时才看清的:
这条 warning 的谓词一直是
has_lib_target(「这会不会产出一个库」),而它要表达的性质是「这是不是一个 C++ 模块库」。 两者在索引长出源码构建的 C 包那一刻就分开了(#533):[targets.x] kind = "shared"铺在一棵.c树上,同样会说src/<name>.cppm不见了,而一个 C 库根本没有 lib root 可缺。protobuf 是这个形状里最常见的那一个。更重的一半是它出现在依赖的校验里 —— 所以一个这样的包会把这条 warning 打进每一个消费者的每次构建。这正是 issue 说的「把诊断输出训练成噪声」,而噪声的产地不在消费者自己的工程里。
#374 修之前必须先看懂 #397 C-4那条提醒也照办了:新谓词读的是u.provides,不是u.packageName == manifest.package.name,所以不会对设了namespace的包判成「没有模块接口」而把 warning 全局静默。覆盖的实话:
tests/e2e/263_lib_root_follows_the_extension.sh的noroot用例守住了否定方向 —— 一个有模块接口、约定根确实缺失的工程仍然必须被告警,所以这条检查不能靠「干脆不看了」通过。但肯定方向(一棵.c树上的kind = "shared"不告警)目前只有那一行谓词和它的注释,没有自己的 e2e。记在这里,别让「修好了」和「有判据」被读成同一件事。