Skip to content

[Bug] host-module 的 lib root 未参与同包模块依赖排序,导入同包模块时构建失败(附最小示例) #720

Description

@julixian

问题

当一个 host-module = true 包的 lib root 导入同包的其他模块时,即使所有模块接口都已列入 build.sources,mcpp 仍先编译 lib root,导致:

fatal error: module 'repro.helper' not found

仅将 lib.path 从上层模块改为它依赖的底层模块,保持模块源码和消费方完全不变,构建就能成功。

显式指定 lib root 和省略 lib.path、使用约定入口两种形式都会复现。因此问题是宿主模块的编译顺序,不是默认路径找不到,也不是要求取消入口约定。

环境

  • Windows
  • mcpp 2026.9.26.2
  • LLVM 22.1.8,普通 clang++
  • 系统 MSVC 14.51.36231(Visual Studio Community 2026)
  • Windows SDK 10.0.26100.0
  • 下面的最小示例在临时目录独立测试,不依赖 GalTranslPP、Qt、vcpkg 或 mcpp.plugins。

完整最小示例

目录结构:

repro/
├── app/
│   ├── mcpp.toml
│   ├── build.mcpp
│   └── main.cpp
└── rules/
    ├── mcpp.toml
    └── src/
        ├── rules.cppm
        └── helper.cppm

app/mcpp.toml

[package]
namespace = "repro"
name = "app"
version = "0.1.0"
standard = "c++23"

[toolchain]
windows = "[email protected]"

[build-dependencies]
"repro.rules" = { path = "../rules", host-module = true }

[build]
sources = ["main.cpp"]

[targets.app]
kind = "bin"
main = "main.cpp"

app/build.mcpp

import repro.rules;
int main() { return answer() == 42 ? 0 : 1; }

app/main.cpp

int main() { return 0; }

rules/mcpp.toml

[package]
namespace = "repro"
name = "rules"
version = "0.1.0"
standard = "c++23"

[lib]
path = "src/rules.cppm"

[build]
sources = ["src/*.cppm"]

[targets.rules]
kind = "lib"

rules/src/rules.cppm

export module repro.rules;
import repro.helper;
export int answer() { return helper_answer(); }

rules/src/helper.cppm

export module repro.helper;
export int helper_answer() { return 42; }

复现命令与实际结果

cd repro/app
mcpp --version
mcpp build

mcpp --version 输出 mcpp 2026.9.26.2。构建退出码为 1,关键输出如下(临时目录路径缩写为 <repro>,省略无关的 -L 未使用参数警告):

   Resolving toolchain
    Resolved [email protected] → @mcpp/registry/data/xpkgs/xim-x-llvm/22.1.8/bin/clang++.exe
    Resolved sysroot msvc@system → MSVC 14.51.36231 (system: Visual Studio Community 2026) · Windows SDK 10.0.26100.0
      Target x86_64-pc-windows-msvc
error: host module 'repro.rules' precompile failed (exit 1):
<repro>\rules\src/rules.cppm:2:8: fatal error: module 'repro.helper' not found
    2 | import repro.helper;
      | ~~~~~~~^~~~~
1 error generated.

对照实验(均已实际运行)

配置 结果
lib.path = "src/rules.cppm",rules 导入 helper 失败,helper 模块找不到
省略整个 [lib] 表,使用约定入口 src/rules.cppm,rules 导入 helper 同样失败
仅将 lib.path 改为 "src/helper.cppm",两个模块源码与 app 都不变 成功,build.mcpp 执行成功,应用构建退出码 0
省略 [lib],将 rules 改为不导入 helper、直接返回 42 成功

最后还把无显式 lib.path 的入口恢复为导入 helper,再次得到相同失败,排除了第一次准备环境才失败的情况。

成功时的关键输出:

  build.mcpp compiling
  build.mcpp running
   Compiling app v0.1.0 (.)
    Finished dev [unoptimized + debuginfo]

期望行为

lib root 可以导入同包其他模块。对于这个无环依赖图,宿主模块应按以下顺序构建:

repro.helper → repro.rules → app/build.mcpp

入口身份不应强制它早于自身依赖编译;显式入口和约定入口应有相同的依赖排序行为。

源码定位

检查的源码版本:52549fbb2d17939e89efb1e05e0c2717bf30d96c。

在 prepare.cppm 的宿主模块收集逻辑 中:

  1. resolve_lib_root_path(...) 找到入口后,立即 push(iface, ...),先将入口放入输出队列。
  2. 遍历 build.sources 时,跳过与入口相同的文件。
  3. 只对其余 pending 模块做依赖排序,再追加到入口后面。

所以目前支持“其他模块导入入口”,却无法处理“入口导入同包其他模块”。

建议让入口也参与同包模块的依赖排序,保留入口识别/诊断语义,并在编译每个模块时传入它依赖的 BMI。建议回归测试覆盖显式入口、约定入口以及入口依赖同包模块的情况。

实际项目中的影响

GalTranslPP 的 gpp.build 是供多个 build.mcpp 导入的构建辅助模块,它导入同包的 gpp.deps.vcpkg、gpp.deps.cmake。期望可以将 lib.path 指向实际的 gpp-build.ixx;目前需要把入口改指向底层的 deps/tools.ixx 才能绕过顺序问题。

这不是说所有宿主模块依赖都会失败,范围是 lib root 导入同包其他模块 的情况。

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions