现象
Windows 上构建大型源码包(ffmpeg 8.1.2,2283 个 TU / .o)时,编译全部通过,但最终链接步骤直接失败:
ninja: fatal: CreateProcess: The parameter is incorrect.
失败的正是那条 driver-style 链接命令(clang-MSVC 工具链,clang++.exe 作为链接驱动):
clang++.exe obj\ffmpegwin.o obj\...\libswscale\yuv2rgb.o obj\yuv_2_rgb.asm.o obj\scale_avx2.asm.o ... (2283+ 个对象内联)
根因
src/build/ninja_backend.cppm 的链接/归档/动态库规则按 separateLinker 分两支(L564–598):
separateLinker == true(MSVC dialect,link.exe/lib.exe):走 response file(@$out.rsp + rspfile_content = $in)——已正确规避命令行长度限制。
separateLinker == false(driver-style,g++/clang++ 既是编译器又是链接驱动):
rule cxx_link
command = $cxx $in -o $out $ldflags $unit_ldflags # $in = 全部对象,内联
rule cxx_archive
command = $ar rcs $out $in # 同样内联
rule cxx_shared
command = $cxx -shared $in -o $out ...
没有 rspfile。
Windows 的 mcpp 托管工具链是 clang-MSVC-ABI(x86_64-pc-windows-msvc),clang++ 是 driver,故 dialect 判定为 driver-style(separateLinker=false、.archiveCmd = "$ar rcs $out $in",dialect.cppm L86),命中 else 分支。而 Windows 上无论驱动是不是 cl.exe,ninja 都经 CreateProcess 拉起,受 32 KiB(historically 8191)命令行上限约束。2283 个对象路径内联 → 直接溢出 → CreateProcess: The parameter is incorrect。
Linux/macOS 因 ARG_MAX(~256 KiB–2 MiB)容得下同样的对象列表,所以同一份 multi-platform compat.ffmpeg 描述符在 linux + macosx 两条腿全绿,唯独 windows 挂在链接这一步。
影响
挡死 Windows 上一切大型全源码构建包——不止 ffmpeg,opencv(compat.opencv,同样数千 TU)必然撞同一堵墙。这是 mcpp 生态"全平台"目标在 Windows 的最后一道系统性障碍;编译侧(clang-MSVC 吃下 2283 个 ffmpeg C 源 + 158 个 win64 NASM)已验证通过,只差链接命令行走 rspfile。
建议修复
driver-style 分支在 Windows 上也走 response file(clang++ 与 GNU/llvm-ar 均支持 @file 语法)。else 分支改为:
} else if constexpr (mcpp::platform::is_windows) {
// Driver-style toolchains (clang++/gcc) still spawn through CreateProcess
// on Windows (32 KiB cmdline limit); large object lists overflow. Both
// clang and GNU/llvm-ar accept @rspfile, so route $in through one.
append("rule cxx_link\n");
append(" command = $cxx @$out.rsp -o $out $ldflags $unit_ldflags\n");
append(" rspfile = $out.rsp\n");
append(" rspfile_content = $in\n");
append(" description = LINK $out\n\n");
append("rule cxx_archive\n");
append(" command = $ar rcs $out @$out.rsp\n");
append(" rspfile = $out.rsp\n");
append(" rspfile_content = $in\n");
append(" description = AR $out\n\n");
append("rule cxx_shared\n");
append(" command = $cxx -shared @$out.rsp -o $out $ldflags $soname_flag $unit_ldflags\n");
append(" rspfile = $out.rsp\n");
append(" rspfile_content = $in\n");
append(" description = SHARED $out\n\n");
} else {
// ...现有 POSIX driver-style 三条规则不变...
}
(POSIX 分支保持内联即可,ARG_MAX 足够;只在 is_windows 上加 rspfile,避免影响 linux/macOS 现有行为。归档命令这里硬编码了 $ar rcs,与 dialect.cppm L86 的 .archiveCmd 一致;若想更稳妥可让 dialect 暴露一个 archiveRspCmd。)
复现
任意 >~500 个 TU 的源码包 kind=lib,在 windows-latest 上 mcpp test。手边可复现的最小对象:multi-platform compat.ffmpeg(mcpp-index 临时 spike 分支)。
现象
Windows 上构建大型源码包(ffmpeg 8.1.2,2283 个 TU / .o)时,编译全部通过,但最终链接步骤直接失败:
失败的正是那条 driver-style 链接命令(clang-MSVC 工具链,
clang++.exe作为链接驱动):根因
src/build/ninja_backend.cppm的链接/归档/动态库规则按separateLinker分两支(L564–598):separateLinker == true(MSVC dialect,link.exe/lib.exe):走 response file(@$out.rsp+rspfile_content = $in)——已正确规避命令行长度限制。separateLinker == false(driver-style,g++/clang++ 既是编译器又是链接驱动):Windows 的 mcpp 托管工具链是 clang-MSVC-ABI(
x86_64-pc-windows-msvc),clang++是 driver,故 dialect 判定为 driver-style(separateLinker=false、.archiveCmd = "$ar rcs $out $in",dialect.cppm L86),命中 else 分支。而 Windows 上无论驱动是不是 cl.exe,ninja 都经CreateProcess拉起,受 32 KiB(historically 8191)命令行上限约束。2283 个对象路径内联 → 直接溢出 →CreateProcess: The parameter is incorrect。Linux/macOS 因
ARG_MAX(~256 KiB–2 MiB)容得下同样的对象列表,所以同一份 multi-platform compat.ffmpeg 描述符在 linux + macosx 两条腿全绿,唯独 windows 挂在链接这一步。影响
挡死 Windows 上一切大型全源码构建包——不止 ffmpeg,opencv(compat.opencv,同样数千 TU)必然撞同一堵墙。这是 mcpp 生态"全平台"目标在 Windows 的最后一道系统性障碍;编译侧(clang-MSVC 吃下 2283 个 ffmpeg C 源 + 158 个 win64 NASM)已验证通过,只差链接命令行走 rspfile。
建议修复
driver-style 分支在 Windows 上也走 response file(clang++ 与 GNU/llvm-ar 均支持
@file语法)。else 分支改为:(POSIX 分支保持内联即可,
ARG_MAX足够;只在is_windows上加 rspfile,避免影响 linux/macOS 现有行为。归档命令这里硬编码了$ar rcs,与 dialect.cppm L86 的.archiveCmd一致;若想更稳妥可让 dialect 暴露一个archiveRspCmd。)复现
任意 >~500 个 TU 的源码包
kind=lib,在 windows-latest 上mcpp test。手边可复现的最小对象:multi-platform compat.ffmpeg(mcpp-index 临时 spike 分支)。