问题
当一个 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 = "llvm@22.1.8"
[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
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 llvm@22.1.8 → @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 的宿主模块收集逻辑 中:
resolve_lib_root_path(...) 找到入口后,立即 push(iface, ...),先将入口放入输出队列。
- 遍历
build.sources 时,跳过与入口相同的文件。
- 只对其余
pending 模块做依赖排序,再追加到入口后面。
所以目前支持“其他模块导入入口”,却无法处理“入口导入同包其他模块”。
建议让入口也参与同包模块的依赖排序,保留入口识别/诊断语义,并在编译每个模块时传入它依赖的 BMI。建议回归测试覆盖显式入口、约定入口以及入口依赖同包模块的情况。
实际项目中的影响
GalTranslPP 的 gpp.build 是供多个 build.mcpp 导入的构建辅助模块,它导入同包的 gpp.deps.vcpkg、gpp.deps.cmake。期望可以将 lib.path 指向实际的 gpp-build.ixx;目前需要把入口改指向底层的 deps/tools.ixx 才能绕过顺序问题。
这不是说所有宿主模块依赖都会失败,范围是 lib root 导入同包其他模块 的情况。
问题
当一个
host-module = true包的 lib root 导入同包的其他模块时,即使所有模块接口都已列入build.sources,mcpp 仍先编译 lib root,导致:仅将
lib.path从上层模块改为它依赖的底层模块,保持模块源码和消费方完全不变,构建就能成功。显式指定 lib root 和省略
lib.path、使用约定入口两种形式都会复现。因此问题是宿主模块的编译顺序,不是默认路径找不到,也不是要求取消入口约定。环境
2026.9.26.222.1.8,普通clang++14.51.36231(Visual Studio Community 2026)10.0.26100.0完整最小示例
目录结构:
app/mcpp.toml
app/build.mcpp
app/main.cpp
rules/mcpp.toml
rules/src/rules.cppm
rules/src/helper.cppm
复现命令与实际结果
mcpp --version输出mcpp 2026.9.26.2。构建退出码为 1,关键输出如下(临时目录路径缩写为<repro>,省略无关的-L未使用参数警告):对照实验(均已实际运行)
lib.path = "src/rules.cppm",rules 导入 helper[lib]表,使用约定入口src/rules.cppm,rules 导入 helperlib.path改为"src/helper.cppm",两个模块源码与 app 都不变[lib],将 rules 改为不导入 helper、直接返回 42最后还把无显式
lib.path的入口恢复为导入 helper,再次得到相同失败,排除了第一次准备环境才失败的情况。成功时的关键输出:
期望行为
lib root 可以导入同包其他模块。对于这个无环依赖图,宿主模块应按以下顺序构建:
入口身份不应强制它早于自身依赖编译;显式入口和约定入口应有相同的依赖排序行为。
源码定位
检查的源码版本:
52549fbb2d17939e89efb1e05e0c2717bf30d96c。在 prepare.cppm 的宿主模块收集逻辑 中:
resolve_lib_root_path(...)找到入口后,立即push(iface, ...),先将入口放入输出队列。build.sources时,跳过与入口相同的文件。pending模块做依赖排序,再追加到入口后面。所以目前支持“其他模块导入入口”,却无法处理“入口导入同包其他模块”。
建议让入口也参与同包模块的依赖排序,保留入口识别/诊断语义,并在编译每个模块时传入它依赖的 BMI。建议回归测试覆盖显式入口、约定入口以及入口依赖同包模块的情况。
实际项目中的影响
GalTranslPP 的
gpp.build是供多个build.mcpp导入的构建辅助模块,它导入同包的gpp.deps.vcpkg、gpp.deps.cmake。期望可以将lib.path指向实际的gpp-build.ixx;目前需要把入口改指向底层的deps/tools.ixx才能绕过顺序问题。这不是说所有宿主模块依赖都会失败,范围是 lib root 导入同包其他模块 的情况。