Skip to content

非模块 lib 报「缺少约定的 .cppm 根」:把命名约定当成了要求 #374

Description

@speak-agent

症状

每个 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

  1. speak-agent commented on Aug 31, 2026

    @speak-agent
    MemberAuthor

    已修复,随 2026.8.30.1(commit 0117a9f,PR #536)发布。

    按 issue 给的判据实施,一字不差:图上有没有任何模块接口单元这件事是现成的,所以用它。src/modgraph/validate.cppm:

    } else if (std::ranges::any_of(g.units, [](auto const& u) {
                   return u.provides.has_value();
               })) {
        r.warnings.push_back({lib_root_rel, ...});
    }
    • 该 target 有模块接口、但约定根缺失 ⇒ warning 保留(这是它存在的理由);
    • 一个模块接口都没有 ⇒ 非模块库,约定根不适用,不告警。

    按 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。记在这里,别让「修好了」和「有判据」被读成同一件事。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions