概述
本 Issue 与架构改进提案(RFC)包含以下内容:
致命缺陷 (Bug 1) :在配置 MCPP_TOOLCHAIN=system 时,由于传入空编译器路径导致编译 build.mcpp 时崩溃(posix_spawnp('') failed)。
主要缺陷 (Bug 2) :工作区(Workspace)根目录中的 [build] 配置项未透传给成员包(Member)。
架构提案 (RFC 1) :引入原生宿主支持(toolchain = "system" 与 sysroot = "system"),明确区分“第三方依赖包 RUNPATH”与“系统基础 Libc 寻址”。
架构提案 (RFC 2) :将核心语言方言(如 -fno-exceptions)自动同步至 import std; 标准库 BMI 预编译流程。
架构提案 (RFC 3) :提供完备的 [workspace.*] 继承规范(特别是统一声明 standard = 26 / [workspace.build]),彻底杜绝 C++ 模块化工程因标准版本不一致引发的跨包 BMI 解析崩溃。
架构提案 (RFC 4) :结合现代 C++ Language Server(如 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
复现环境 :Linux / macOS,配置 [toolchain] default = "system" 或环境变量 MCPP_TOOLCHAIN=system,且工程中包含 build.mcpp 构建脚本。
最小复现步骤
创建一个包含 build.mcpp 的最小工程:
# mcpp.toml
[package ]
name = " demo"
version = " 0.1.0"
standard = 26
[toolchain ]
linux = " system"
// build.mcpp
#include < iostream>
int main () { return 0 ; }
执行构建命令:mcpp build。
实际报错
在编译主程序前构建立即中断:
mcpp module compile failed (exit 127): posix_spawnp('') failed (error 2)
build.mcpp failed to compile (exit 127)
源码调用链与根因溯源
未赋值的 explicit_compiler :
在 src/build/prepare.cppm:1705-1706 中,当 *tcSpec == "system" 时,进入了空分支:
// 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.
}
局部变量 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:
// src/build/prepare.cppm:2205-2208
auto host_tc_for_build_program = [&]() -> std::expected<
std::pair<std::filesystem::path, mcpp::toolchain::Toolchain>, std::string> {
if (overrides.target_triple .empty ())
return std::pair{explicit_compiler, *tc}; // ⚠️ 致命 Bug:explicit_compiler 为空!
posix_spawnp 执行空路径 :
该空路径作为 hostCompiler 传递给 src/build/build_program.cppm:573-580。底层 posix_spawnp("") 尝试执行空文件名,操作系统立即返回 ENOENT(error 2),子进程以 127 退出码异常终止。
建议修复补丁 (Proposed Patch)
--- a/src/build/prepare.cppm
+++ b/src/build/prepare.cppm
@@ -2205,8 +2205,10 @@ export std::expected<BuildContext, std::string> prepare_build(
auto host_tc_for_build_program = [&]() -> std::expected<
std::pair<std::filesystem::path, mcpp::toolchain::Toolchain>, std::string> {
if (overrides.target_triple.empty())
- return std::pair{explicit_compiler, *tc};
+ return std::pair{explicit_compiler.empty() ? tc->binaryPath : explicit_compiler, *tc};
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 寻址的分层解耦
动态链接中必须清晰区分两类不同的动态库 及其寻址诉求:
第三方业务依赖动态库(Package .so) :由 mcpp 托管下载或本地编译输出。必须注入 DT_RUNPATH (本地开发期注入缓存绝对路径,发布期通过 mcpp pack 重写为 $ORIGIN/../lib)。
基础 C 运行时(glibc / libc.so.6 / 加载器 ld.so) :
托管沙盒模式(Managed) :适合独立 CLI / 跨发行版免安装工具,保持现有的 xim:glibc 并注入沙盒 RUNPATH;
原生宿主模式(Host-Native) :直接使用系统 /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]
Loading
提案配置语法
在 mcpp.toml 中支持显式声明宿主原生模式:
[target .x86_64-linux-gnu ]
toolchain = " system" # 使用宿主 /usr/bin/g++ 或 clang++
sysroot = " system" # 使用宿主 glibc、头文件与 /lib64/ld-linux (不注入 libc RUNPATH)
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) 统一声明 :
[workspace ]
members = [" apps/*" , " packages/*" ]
# ① 全局 C++ 语言版本与包元信息继承 (统一 C++26,杜绝模块 BMI 标准漂移)
[workspace .package ]
standard = 26 # 统一 C++26,所有成员包自动继承
edition = " 2026"
version = " 0.1.0"
license = " Apache-2.0"
# ② 全局构建与方言选项继承 (彻底解决 Bug 2)
[workspace .build ]
cxxflags = [" -fno-exceptions" , " -Wall" ]
dialect_cxxflags = [" -fno-exceptions" ]
bmi_schedule = " on"
jobs = " auto"
# ③ 全局目标平台与原生系统模式继承
[workspace .target .x86_64-linux-gnu ]
toolchain = " system"
sysroot = " system"
# ④ 统一依赖版本表
[workspace .dependencies ]
wayland-client = { version = " 1.23.0" }
成员包 (apps/compositor/mcpp.toml) 轻量继承 :
[package ]
name = " compositor"
standard.workspace = true # 自动继承全局 C++26 (或未显式声明时默认继承)
version.workspace = true
[build ]
inherit.workspace = true # 自动继承根目录 [workspace.build]
[dependencies ]
wayland-client.workspace = true
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 开关:
mcpp build --workspace --configure-only --cdb-aggregate
在 Workspace 根目录下输出一份汇聚了所有 Member 编译单元的完整全局 compile_commands.json,使 clice 与 clangd 等现代 LSP 能够无缝接入并完成全工作区的跨包符号分析与模块导航。
概述
本 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 能够无缝接入并完成全工作区的跨包符号分析与模块导航。