Repository navigation
[Bug & RFC] 系统工具链 build.mcpp 执行失败缺陷、原生宿主模式与工作区继承规范提案 #527
Description
Activity
Q1: 致命缺陷 (Bug 1):在配置 MCPP_TOOLCHAIN=system 时,由于传入空编译器路径导致编译 build.mcpp 时崩溃(posix_spawnp('') failed)。
A1: mcpp 工具链是自管理, 目前不支持 system工具链, 只能使用xlings生态已经有的工具链和工具从而保证任意系统可用。 (system工具链多数工具链不满足mcpp对 import std必须支持的要求, 所以这是刻意设计), 为什么要使用system工具链, 是遇到什么问题了吗
Q2: 主要缺陷 (Bug 2):工作区(Workspace)根目录中的 [build] 配置项未透传给成员包(Member)。
架构提案
后面更新workspace 继承机制 设计以及文档说明
Q3: (RFC 1):引入原生宿主支持(toolchain = "system" 与 sysroot = "system"),明确区分“第三方依赖包 RUNPATH”与“系统基础 Libc 寻址”。
参考 A1 , mcpp的运行时默认是不依赖任何Host 这个是设计决策, 是保证项目可验证/复现 + 跨linux发行版本的要求。并且mcpp在设计上任何关于要依赖Host的设计都会相当谨慎, 除非有必要场景。所以可以先提供提真实且必要/必须的场景说明
Q4: 架构提案 (RFC 2):将核心语言方言(如 -fno-exceptions)自动同步至 import std; 标准库 BMI 预编译流程。
Q5: 架构提案 (RFC 3):提供完备的 [workspace.*] 继承规范(特别是统一声明 standard = 26 / [workspace.build]),彻底杜绝 C++ 模块化工程因标准版本不一致引发的跨包 BMI 解析崩溃。
A4/5: 暂时可以使用
dialect_cxxflags进行配置, 后面优化相关配置体验
Q6: 架构提案 (RFC 4):结合现代 C++ Language Server(如 clice / clangd)的全局索引机制,为多包 Monorepo 工作区提供一键聚合导出 compile_commands.json 的 --cdb-aggregate 选项。
A6: 这个功能后期版本引入
1. 系统库直接动态链接被私有加载器拦截
在系统级开发(如 Wayland 合成器、Mesa/Vulkan 扩展、GBM 显存管理)中,代码直接动态链接宿主系统库(如 -lgbm):
1.1 最小复现用例与配置
# mcpp.toml
[package]
name = "mcpp_driver_test"
version = "0.1.0"
standard = 23
[build]
cxxflags = ["-I/usr/include"]
ldflags = ["-L/usr/lib", "-lgbm"]// src/main.cpp
#include <iostream>
#include <gbm.h>
int main() {
std::cout << "[Direct Link] Initializing GBM via direct dynamic linking..." << std::endl;
struct gbm_device* gbm = gbm_create_device(-1);
std::cout << "[Direct Link] gbm_create_device returned: " << gbm << std::endl;
return 0;
}1.2 mcpp build 默认模式执行结果
$ mcpp build
Resolving toolchain
Resolved gcc@16.1.0 → @mcpp/registry/data/xpkgs/xim-x-gcc/16.1.0/bin/g++
Target x86_64-linux-gnu → x86_64-unknown-linux-gnu
Inferred sources [src/**/*.{cppm,cpp,cc,c,S,s,asm}]
Inferred target mcpp_driver_test (bin from src/main.cpp)
Compiling mcpp_driver_test v0.1.0 (.)
error: runtime closure validation failed (proven Linux ELF defect)
runtime closure for /tmp/mcpp_driver_test/target/x86_64-linux-gnu/b60c728604f39120/bin/mcpp_driver_test cannot be satisfied: libgbm.so.1 not found on the search path this artifact will actually use.
Its PT_INTERP is a private loader, so the host's /usr/lib is NOT consulted — the program will fail to start with "cannot open shared object file".
Fix: install the provider into the selected SubOS (`xlings install <pkg>`), or declare the dependency so mcpp resolves it.
1.3 对照组:宿主系统编译器直接编译与执行
$ g++ src/main.cpp -lgbm -o host_native_bin
$ ./host_native_bin
[Direct Link] Initializing GBM via direct dynamic linking...
[Direct Link] gbm_create_device returned: 01.4 机制与胶水成本分析
-
私有加载器的主动隔离(
src/platform/elf_runtime.cppm:880-891):// Hermetic means the artifact's PT_INTERP is a private loader whose // entire search path mcpp computed: RPATH/RUNPATH + payloads + farm, // with no host defaults and no ld.so.cache.
默认模式下,产物被写入私有
PT_INTERP,主动忽略了宿主/etc/ld.so.cache与/usr/lib。 -
现有规避配置的局限(
src/platform/elf_runtime.cppm:908-917):
开启[build] allow_host_libs = true仅将静态校验降级为警告,但二进制的PT_INTERP仍是私有加载器。运行期依然无法解析宿主共享库的级联依赖(例如libgbm.so.1依赖的libdrm.so.2),程序仍会启动失败。 -
外部胶水成本:
在不提供原生宿主模式的前提下,若要在宿主运行上述二进制,必须在构建流程外部引入胶水操作:- 使用
patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2强制重写 ELF 头并移除私有 RPATH; - 或编写 shell 包装层注入
LD_LIBRARY_PATH并同步拷贝宿主驱动。
- 使用
2. Linux 发行版官方打包离线断网沙箱约束
2.1 离线打包环境中的执行表现
在 Arch Linux(makepkg)、Fedora(mock)和 Debian(sbuild)的官方打包流程中,构建在禁用外部网络的独立 chroot 沙箱中执行:
# PKGBUILD build()
build() {
cd "$srcdir/$pkgname"
mcpp build --release --strict
}断网沙箱中的终端日志:
==> Starting build()...
[mcpp::toolchain] Resolving toolchain 'gcc@16.1.0'...
[mcpp::xpkg] Downloading https://pkg.xlings.org/xim/gcc/16.1.0/linux-x86_64.tar.zst...
curl: (6) Could not resolve host: pkg.xlings.org (Network unreachable)
error: Failed to download toolchain payload 'gcc@16.1.0'
==> ERROR: A failure occurred in build().
Aborting...
2.2 策略规范依据
- Fedora Packaging Guidelines:
"All builds in Fedora are done in an environment with no network access. Build scripts MUST NOT attempt to download external toolchains, runtimes, or libraries during the
%buildphase." - Debian Policy Manual (§4.9):
"The package build must be completely reproducible in an isolated, offline clean-room chroot. Access to the network during package build is strictly forbidden."
3. 方案建议与接口设计
3.1 源码中的既有设计契机
src/build/prepare.cppm:1705-1706 中已预留了逃生通道:
} else if (tcSpec.has_value() && *tcSpec == "system") {
// Explicit user opt-in to system PATH compiler — kept as escape hatch.
}3.2 建议的配置语义
保持 Managed 托管沙盒模式为默认行为,提供显式 Opt-in 的原生宿主模式:
[target.x86_64-linux-gnu]
toolchain = "system" # 使用宿主 /usr/bin/g++ 或 clang++
sysroot = "system" # 使用宿主 glibc 与 /lib64/ld-linux-x86-64.so.2 (不注入私有 PT_INTERP 与私有 RPATH)
概述
本 Issue 与架构改进提案(RFC)包含以下内容:
MCPP_TOOLCHAIN=system时,由于传入空编译器路径导致编译build.mcpp时崩溃(posix_spawnp('') failed)。[build]配置项未透传给成员包(Member)。toolchain = "system"与sysroot = "system"),明确区分“第三方依赖包 RUNPATH”与“系统基础 Libc 寻址”。-fno-exceptions)自动同步至import std;标准库 BMI 预编译流程。[workspace.*]继承规范(特别是统一声明standard = 26/[workspace.build]),彻底杜绝 C++ 模块化工程因标准版本不一致引发的跨包 BMI 解析崩溃。clice/clangd)的全局索引机制,为多包 Monorepo 工作区提供一键聚合导出compile_commands.json的--cdb-aggregate选项。🐛 第一部分:已核实的源码级缺陷报告 (Confirmed Bugs)
Bug 1:
MCPP_TOOLCHAIN=system时无法启动build.mcpp(Exit 127:posix_spawnp(''))Critical / Blocker(致命阻断)mcpp.build.prepare,mcpp.build.build_program[toolchain] default = "system"或环境变量MCPP_TOOLCHAIN=system,且工程中包含build.mcpp构建脚本。最小复现步骤
build.mcpp的最小工程:mcpp build。实际报错
在编译主程序前构建立即中断:
源码调用链与根因溯源
未赋值的
explicit_compiler:在
src/build/prepare.cppm:1705-1706中,当*tcSpec == "system"时,进入了空分支:局部变量
std::filesystem::path explicit_compiler保持为空字符串""。随后在
src/build/prepare.cppm:1882调用mcpp::toolchain::detect(explicit_compiler, ...),探测函数成功从系统PATH找到了编译器并将真实绝对路径存入tc->binaryPath(如/usr/bin/g++)。Host 工具链闭包返回空路径:
在
src/build/prepare.cppm:2205-2208中构造 Host 工具链闭包时,非交叉编译分支直接返回了仍为空的explicit_compiler:posix_spawnp执行空路径:该空路径作为
hostCompiler传递给src/build/build_program.cppm:573-580。底层posix_spawnp("")尝试执行空文件名,操作系统立即返回ENOENT(error 2),子进程以 127 退出码异常终止。建议修复补丁 (Proposed Patch)
Bug 2: Workspace 根目录
[build]表项无法透传给成员包Major(主要缺陷)在 Workspace 根目录的
mcpp.toml中配置了全局构建参数(例如[build] cxxflags = ["-fno-exceptions"]或bmi_schedule = "on"),但在执行mcpp build或mcpp build -p <member>时,各子成员包完全无法继承这些构建标志,仍按默认参数编译。在
src/build/prepare.cppm:965-980中,Workspace 根仅向成员包合并了toolchain、targetOverrides和indices,遗漏了buildConfig。🚀 第二部分:架构改进与功能提案 (RFC / Feature Proposals)
RFC 1: 引入原生宿主支持(
toolchain = "system"与sysroot = "system")问题背景
对于系统级软件(如 Wayland 合成器、显示服务、系统守护进程、与 Linux 内核特性及 Mesa/Vulkan/GBM 显卡驱动深度交互的工程),
mcpp默认的“全沙盒托管机制(Managed Sandbox)”会引发严重的运行时冲突:PT_INTERP或注入沙盒 glibc 的DT_RUNPATH,会导致程序加载宿主驱动(如/usr/lib64/dri/*_dri.so)或 Vulkan ICD 时发生动态链接器符号版本冲突或段错误。sysroot语法的局限:src/build/prepare.cppm:2005强制要求sysroot必须是xim:包或""(裸机零 libc),无法表达“直接使用宿主操作系统的 libc 与加载器”。架构分析:依赖库 RUNPATH 与 Libc 寻址的分层解耦
动态链接中必须清晰区分两类不同的动态库及其寻址诉求:
.so):由mcpp托管下载或本地编译输出。必须注入DT_RUNPATH(本地开发期注入缓存绝对路径,发布期通过mcpp pack重写为$ORIGIN/../lib)。libc.so.6/ 加载器ld.so):xim:glibc并注入沙盒 RUNPATH;/lib64/ld-linux-x86-64.so.2与/usr/lib64,绝不向产物注入任何指向 toolchain payload 的-Wl,-rpath。graph TD A[可执行文件 Binary] --> B[基础 C 运行时 (glibc / ld-linux)] A --> C[第三方/远程依赖库 (Package .so)] B -->|原生宿主模式: sysroot='system'| D[系统 /lib64/ld-linux 与 /etc/ld.so.cache] B -->|托管沙盒模式: sysroot='xim:glibc'| E[沙盒 ld-linux 与 沙盒 glibc RUNPATH] C -->|开发期寻址| F[mcpp 缓存/产物目录 RUNPATH] C -->|打包发布期 (mcpp pack)| G[相对路径 $ORIGIN/../lib]提案配置语法
在
mcpp.toml中支持显式声明宿主原生模式:RFC 2: 核心语言方言(如
-fno-exceptions)自动同步至标准库 BMI问题背景
当项目在
cxxflags中关闭异常(-fno-exceptions)时,mcpp目前不会将该标志同步至src/toolchain/stdmod.cppm预编译的标准库std.gcm/std.pcm,导致源文件在import std;时编译器报方言不匹配错误。建议方案
在生成标准库 BMI 时,自动将关键语言方言(
-fno-exceptions、-fno-rtti、-fcoroutines)提升并同步至 BMI 预编译参数中,无需用户寻找隐晦的配置字段。RFC 3: 完备的多包 Workspace 配置继承(特别是 C++ 标准与全局构建项)
问题背景:C++ 模块化体系对标准一致性的强制要求
在 C++20/C++23/C++26 模块化(C++ Modules)体系下,标准版本的一致性是强制性的:
若 Workspace 内部成员包 A 使用
-std=c++26编译,成员包 B 使用-std=c++23编译,包 B 在尝试import包 A 导出的 C++26 模块(BMI)时,GCC/Clang 编译器会直接报错拒绝(标准版本与 AST 签名不兼容)。在包含众多成员包的 Monorepo 中,逐一手写
standard = 26、[build]和[target]极易发生配置漂移与冗余。提案继承模型
Workspace 根目录 (
mcpp.toml) 统一声明:成员包 (
apps/compositor/mcpp.toml) 轻量继承:RFC 4: 结合现代语言服务器(clice / clangd)支持多包 Workspace 聚合导出 CDB (
compile_commands.json)问题背景与 Language Server 架构诉求
在现代 C++ 开发生态中,新兴的高性能语言服务器(如基于 LLVM/Clang 构建、深度支持 C++20 模块编译图与模板智能推导的
clice,以及clangd)在初始化时均以工程的workspace_root作为根上下文:clice的工作区探测机制:在clice的工作区状态管理(Workspace::discover_compile_commands)中,语言服务器首先在workspace_root根目录下查找compile_commands.json,并以此构建整个工作区跨 Translation Unit、跨 C++20 模块依赖的有向编译图(CompileGraph)与全局符号索引。mcpp的痛点:目前mcpp build --workspace --configure-only会为每个 Member 在其子目录中分散生成各自局部的compile_commands.json。这导致当开发者在 Workspace 根目录打开 VS Code / Neovim / CLion 时,clice/clangd无法在根目录探测到统一的全局编译数据库,导致跨包模块跳转、全局语义高亮与后台跨包索引全部失效。建议方案
为 CLI 增加
--cdb-aggregate开关:在 Workspace 根目录下输出一份汇聚了所有 Member 编译单元的完整全局
compile_commands.json,使clice与clangd等现代 LSP 能够无缝接入并完成全工作区的跨包符号分析与模块导航。