0.2.5: 着色器阶段的拒绝、三平台各自的着色器编译器,以及 CUDA/SYCL 的 Windows 形态 - #9
Merged
Conversation
输出名是着色器的 stem 加阶段,所以 `ui/text.vert` 与 `world/text.vert` 都产出 `text_vert.h`、都声明 `text_vert_spv`。 实测:在这条检查之前 ninja 会抓到重复输出 —— **所以它从来不是静默的**。但那条消息 点的是生成文件而不是两个着色器,以图加载失败的形式出现而不是这条规则的拒绝,而且 没有出路。每个阶段只有一个着色器的工程永远碰不到它,这是它活下来的原因;而按用途 分目录的图形工程是第一个会有两个的。 把目录并进名字不是修法:两个头文件只要进到同一个翻译单元,符号照样撞。
`xim:shaderc` 补齐 macOS arm64 与 Windows x86_64 之后,这条规则可以在三个平台上
都自带它驱动的编译器。而它带的不是同一个:
[target.'cfg(all(accelerator = "vulkan", linux))'.feature-xlings.rules-spirv]
"xim:glslang" = ">=15.1.0"
[target.'cfg(all(accelerator = "vulkan", macos))'.feature-xlings.rules-spirv]
"xim:shaderc" = ">=2026.3"
[target.'cfg(all(accelerator = "vulkan", windows))'.feature-xlings.rules-spirv]
"xim:shaderc" = ">=2026.3"
这条规则自 0.2.0 起同时驱动两个参考编译器,并在 glslc 那条路线上自己写出 C 声明,
所以「哪一个在场」不是消费者看得见的差别。**跨平台一致性由规则的选择能力提供,不由
把同一个编译器发三遍提供。** Linux 保持 glslang,既有构建的读数一个字都不变。
新增的 CI job 在 macOS 与 Windows 上跑同一个 spirv 夹具,并施加与 Linux 同一条
来源判据:载荷必须来自**图**那一遍,不是夹具自己声明的。只跑 spirv 一个夹具 ——
CUDA/HIP/SYCL 的载荷上游只有 Linux,把每个夹具都摆到每个平台上会因为不是缺陷的
理由变红。
`MCPP_HOME` 从 workflow 级挪到 job 级:那是一条 Linux 路径,留在上面会在另外两个
runner 上悄悄地错 —— 而且是「看起来还能跑」的那种错。
两处宿主假设:载荷里的编译器按 `bin/glslc` 查(Windows 上是 `glslc.exe`), 而 PATH 按 ':' 切(Windows 上是 ';')。于是刚加的 Windows job 会以「找不到着色器 编译器」失败,而载荷就在那里。 `program_in()` 在每个宿主上都同时试裸名与 `.exe`,代价是一次 stat;换来的是一个 用另一种约定重打包的载荷会被找到而不是被静默错过。后缀与分隔符按**宿主**决定 —— 构建程序跑在做构建的那台机器上,交叉构建到 Windows 时要找的仍然是 `glslc`。 `classify` 用的是 stem,`glslc.exe` 本来就分类正确;版本串里那个冒号与平台无关。
xim-pkgindex 26f682fc 之后,CUDA 的四个组件与 dpcpp 在 Windows 上装得上了。 规则这一侧还是按 Linux 写的:程序名不带 `.exe`,导入库不在 `lib/x64`,而 SYCL 那条把宿主 C/C++ 库挡在外面的三个载荷在那个平台上既不存在也不需要。 ## rules-cuda `nvcc()`、clang++、g++、ptxas/fatbinary 的存在性检查都按宿主取后缀;`lib_dirs()` 把 `/lib/x64` 一并搜索(不存在的目录不贡献任何东西,按宿主分表反而多一处要同步)。 **Windows 上默认走 clang 路线,与项目的编译器无关。** 两条路线的区别是谁编译设备 单元的宿主那一半:nvcc 路线交给 `-ccbin` 命名的编译器,而在这个平台上那只能是 MSVC 的 `cl.exe` —— 找到它要问机器的 Visual Studio 安装,正是这个生态在消除的那 种宿主依赖。clang 路线不驱动第二个编译器,它自己就会定位 MSVC 的头与库,和它编译 任何一个普通翻译单元时一样。显式要 nvcc 路线的项目会在 Windows 上被拒绝,并说明 这一点,而不是拿两个没有意义的答案拼出一条命令行。 `-fPIC` 只在非 Windows 上加:在那个平台上它命名的是无条件成立的性质,每个设备 单元会多一条 unused argument 警告。 ## rules-sycl `clang++.exe`;`--gcc-install-dir`、`-isystem <glibc>`、`-isystem <uapi>`、 `-fPIC` 与 `-l:libstdc++.so.6` 都只在 Linux 上出现 —— 它们存在的理由是"两个 C++ 运行时不能共处一个进程"和"dpcpp 的 clang 不是 mcpp 解析的那个 clang,没有被配置 过这个生态的 glibc",而 Windows 上只有一个 C++ 运行时(MSVC 的),两边用的是同 一个。要求那三个载荷会让 Windows 的构建因为三个本生态不为它发布、而那个平台的 编译器也不需要的包而失败 —— 一个补救措施不存在的错误。 `accel` 里点名 NVIDIA 架构的构建在 Windows 上被拒绝并说明:上游不为 Windows 构建 CUDA 与 HIP 插件,已发布的资产也确实只带 Level Zero 与 OpenCL 两个适配器。 mcpp.toml 里那条 `sycl + cuda` 的声明因此加上 `linux` —— 供给发生在规则之前, 一个在几个 GB 下载之后才到达的拒绝是更坏的拒绝。 ## tests/all-rules-compile 上面每一条改动都写在 `#if defined(_WIN32)` 里,而本仓库此前没有任何东西在 Windows 上编译过 cuda.cppm 或 sycl.cppm。 其余每个 consumer 都端到端地跑一条规则,因此都需要那条规则的载荷,而载荷只为一个、 两个或三个平台发布 —— **于是一条规则里为某个宿主写的那半段,恰好是那个宿主从不 编译的那半段**。语法错了也能一直绿,直到第一个在 Windows 上构建的人从一个他只想 用的包里拿到一个编译错误。 这个夹具不点名任何 accelerator。每条规则的 `compile()` 在那个状态下立刻返回,所以 一个字节都不下载、也不需要第二个编译器;它断言的是六个模块都为**本宿主**编译过。 足够便宜,矩阵里每个平台都跑。 实测:往 `rules/cuda.cppm` 末尾加一条 `static_assert(false)`,这个夹具当场报 `host module 'mcpp.rules.cuda' compile failed`。 `spirv-cross-platform` 因此更名为 `rules-cross-platform`。
`rules (macos arm64)` 与 `rules (windows x86_64)` 是这条分支新加的两个 job,
第一次跑就红,而两条红各自指向一个真实的缺口。
## macOS 14:std::println 不是 header-only
ld64.lld: error: undefined symbol: std::__1::__is_posix_terminal(__sFILE*)
ld64.lld: error: undefined symbol: std::__1::__get_ostream_file(...)
`std::print`/`std::println` 的两个重载都要到 libc++ **dylib** 里取符号,而这两个
符号是在 macOS 14 不带的那一版里加进去的。构建程序的链接在那台机器上把 `-lc++`
解析到系统那份,于是规则**编译通过、链接失败**,报的两个符号既不说是哪一次调用要
的,也不说原因。
`std::format` 是 header-only。三十七处消息改成先 format 再 stream,六个模块各留
一段说明为什么。
⭐ **为什么此前没人看见**:mcpp 自己的 macOS CI 跑在 `macos-15` 上,那一版系统
libc++ 有这两个符号;而唯一在 build.mcpp 里用 `std::println` 的 e2e(110)带
`# requires: gcc`,macOS 上直接跳过。这个矩阵钉的是 `macos-14` —— 两个受支持
版本里更低的那个 —— 所以它看见了。钉子保留。
## Windows:MCPP_VENDORED_XLINGS 指向一个不存在的文件
`registry/bin/xlings` 在那个平台上是 `xlings.exe`。报出来的是
error: xlings binary not found
后面跟着三条补救建议,没有一条是实际适用的那条。改成按宿主选后缀,并在导出前
断言那个文件确实存在 —— 一个指向不存在文件的变量不该以"没配置"的形态报出来。
两条红都不是规则的问题。 Windows:这个 job 跑在 Git Bash 下,$PWD 是 `/d/a/...`,而 mcpp 是原生 Windows 程序,把 MCPP_VENDORED_XLINGS 当 Windows 路径读。后缀与路径形状两个差异报出来 是同一句 `xlings binary not found` 加同样三条建议,没有一条是「你给的路径是另一 种语法」。改成 cygpath -m。 macOS:macos-14 上构建程序链接失败于 `std::__1::__is_posix_terminal` —— 它由 `import std` 自己引到,规则改掉 std::println 并不足够。引擎那一半是 host_link_tokens 在 macOS 上提前返回、从不加载荷的运行时目录。矩阵暂钉 macos-15(与 mcpp 自己的 macOS CI 一致),等带修复的发布出来再移回 macos-14 —— 那是 README 声明的下限。
Windows:MCPP_HOME 也要用宿主的路径语法。Git Bash 下 $HOME 是 `/c/Users/runneradmin`,而 mcpp 把这个变量交给 xlings —— 一个原生 Windows 程序,它答 `The filename, directory name, or volume label syntax is incorrect.` 然后退 1,而报出来的那一层说的是包名不是路径。 macOS:`mcpp self env | awk '...; exit'` 会在第一次匹配后关掉管道读端,mcpp 随后死于 `internal: unhandled exception: failed to write formatted output` (exit 70)。改成先写文件再读。两个 job 写成同一形状,免得再分叉。 这一轮 macOS 的读数:`rules-spirv through a consumer` 与 `every rule module compiles for this host` 都过了 —— 六个规则模块(含各自的 Windows/macOS 分支)在 macOS arm64 上编译通过,上一条提交换掉 std::println 的 修复成立。
windows-2022 上,声明 `xim:shaderc@>=2026.3` 的构建死在
Provisioning [xlings.workspace] entries declared by dependencies (xim:shaderc@>=2026.3)
The filename, directory name, or volume label syntax is incorrect.
error: provisioning ... failed: xlings exited 1
那句话是 cmd.exe 说的,说的是一个用不了的重定向目标。mcpp 把供给请求作为一个 JSON
参数放在 shell 命令行上;Windows 上解析它的是 cmd.exe,而 JSON 用的是 MSVCRT 的
`\"` 转义 —— cmd 不认这个转义,每个 `"` 都在切换它的引用状态,数到版本约束里
那个 `>` 的时候状态恰好是「未引用」,于是 `>` 成了重定向。
**在这之前没有任何一条能在 Windows 上生效的声明带过 `>`**,所以整个 `>=` 形态在
那个平台上从没被走到过。
精确版本不是伪装的绕过:mcpp 把裸版本读成**选择**,项目写一个不同的仍然赢并被报告。
macOS 取同一个值,让用这个编译器的两个平台一致。引擎侧的转义修好并发布之后,两处都
回到 `>=2026.3`。
windows-2022 上 `mcpp.rules.spirv` 的宿主模块编译失败:
rules/spirv.cppm:277:17: error: no member named 'popen' in the global namespace
rules/spirv.cppm:282:7: error: no type named 'pclose' in the global namespace
同一形状此前在 sycl 里已经处理过。空设备也一样(`/dev/null` / `NUL`)。两者都命名
成常量,让调用点在每个宿主上读起来一样 —— 另一种写法是每个调用点套一个 `#if`,而
被忘掉的那一个恰好是没人编译的那一个。
hip 那条 lane 的 clang++ 也补上后缀:它今天只到得了 Linux(NVIDIA 平台的头文件包
只为它发布),所以这段 Windows 拼写没有任何东西在走。仍然写下来,因为错误的路径会
把自己报成「工具链缺 clang」。
CI:跨平台 job 里把「每条规则都为本宿主编译过」挪到端到端那一步之前。它更便宜、答案
更有信息量;放在后面时,它的失败以别人的构建错误形态到达 —— 一个与它无关的 consumer
报 `no member named 'popen'`。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
四个提交,一条线索:这套规则此前只在一个平台上被编译过,也只在一个平台上被驱动过。
rules-spirv:两个只差目录的着色器映射到同一个输出
shaders/a/blur.comp与shaders/b/blur.comp生成同一个blur_comp.h。这从来不是静默的 —— ninja 在加载图时就报
multiple rules generate ...。但它报的是生成的文件,不是那两个源;它以图加载失败的形态到达,而那一步之前没有任何一条属于规则
的诊断;它也不说出路。
现在规则在编译循环之前自己查一遍,点名两个源、那个头、那个符号,并给出办法。
rules-spirv:三个平台各自带对的那个编译器
xim:glslang只有 Linux。macOS 与 Windows 改为xim:shaderc(xim-pkgindex #778已发布)。规则会选,所以消费者看不到这个差异 —— 跨平台的一致由规则的选择能力
提供,不是靠把一个编译器发布三遍。
find_compiler与first_on_path此前会在 Windows 上失败:它按bin/glslc找一个叫
glslc.exe的文件,并用:切PATH。rules-cuda / rules-sycl:Windows 形态
xim-pkgindex #779 之后,CUDA 的四个组件与 dpcpp 在 Windows 上装得上。规则这一侧:
.exe,lib_dirs()一并搜索/lib/x64。-ccbin指向 MSVC 的cl.exe,而找到它要问机器的 Visual Studio 安装 —— 正是这个生态在消除的那种宿主依赖。显式
要 nvcc 路线会被拒绝并说明。
C++ 运行时,两边用的是同一个。要求它们会让构建因为三个不为那个平台发布、编译器
也不需要的包而失败。
accel点名 NVIDIA 架构的 SYCL 构建在 Windows 上被拒绝:上游不为 Windows 构建CUDA/HIP 插件,资产也只带 Level Zero 与 OpenCL 适配器。
tests/all-rules-compile
以上每一条都写在
#if defined(_WIN32)里,而本仓库此前没有任何东西在 Windows 上编译过
cuda.cppm或sycl.cppm。其余每个 consumer 都端到端跑一条规则,因此都需要那条规则的载荷,而载荷只为一到三个
平台发布 —— 于是一条规则里为某个宿主写的那半段,恰好是那个宿主从不编译的那半段。
新夹具不点名任何 accelerator:每条规则的
compile()立刻返回,一个字节都不下载,断言的是六个模块都为本宿主编译过。Linux 与跨平台矩阵都跑。
实测:往
rules/cuda.cppm末尾加一条static_assert(false),这个夹具当场报host module 'mcpp.rules.cuda' compile failed。spirv-cross-platform更名为rules-cross-platform。