Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@

### Changed

- `compat.sycl-runtime` 2026.09.10 与 `compat.cuda-driver` 2026.09.10:驱动一侧
的 farm 由**枚举哨兵目录**得到,不再手写 `libcuda.so.1` 一个名字,并把哨兵
依赖钉到 `xim:libcuda-host-link@0.0.2`。手写的那一半正是错的那一半:
`libur_adapter_cuda.so.0` 的 DT_NEEDED 里除 `libcuda.so.1` 之外还有
`libnvidia-ml.so.1`,farm 没有携带,适配器加载失败,CUDA 后端整个消失,
程序以退出码 134 终止且不打印任何异常文本(mcpp#596)。哪些驱动库存在是
哨兵包的问题;读它的目录得到的 farm 无法与它不一致,而写死文件名的 farm 已经
不一致了。旧版本键保留:本文件只有一个 `install()` 且不读 `pkginfo.version()`,
所以旧钉今天安装得到的仍是当前 farm,新版本键的作用是让已经装过该目录的机器
重新安装。
- `compat.sycl-runtime` 明确声明**不服务** `libOpenCL.so.1`。载荷自带 OpenCL
适配器,而 `compat:opencl` 会连带引入宿主专有 OpenCL 驱动的 farm,把一份
随机器而变的厂商面塞进每一个 SYCL 工程;实测中它还提供了 `libnvidia-ml.so.1`,
从而遮住上一条正在修的缺口。需要该后端的工程在自己的 manifest 里声明
`compat:opencl`。

- `compat.cuda-runtime` 改名为 `compat.cuda-driver`,并改正 `repo` 字段。
NVIDIA 词汇里 "CUDA Runtime" 专指 `libcudart`,而本包 farm 的是驱动的
`libcuda.so.1`;它的 `capabilities` / `provides` 从第一版起就写作 `cuda.driver`,
Expand Down Expand Up @@ -172,6 +188,15 @@

### Fixed

- `tests/examples/sycl-runtime` 的判据由**三个手写 soname 的 dlopen**改为
**对 farm 全体成员做 DT_NEEDED 闭包走查**。farm 有 26 个成员而断言只有三个
名字,坏掉的两个不在其中,所以这条判据在缺陷存在期间一直是绿的。改用 dlopen
全体成员仍然不够:dlopen 量的是**进程**,而进程的搜索路径上不止本包放的东西
—— 实测中另一个 farm 提供了 `libnvidia-ml.so.1`,把本包的缺口遮成通过。
现在的走查只看 farm 自己,并区分三种读数:解析到、farm 里存在但悬空(无驱动的
机器,记 note)、farm 根本没有(打包缺口,失败)。三条腿都实测过:补全后 26/26
通过;拿掉 NVML 后点名成员与 soname 失败;把驱动链改为悬空后 24/26 通过。

- 跟进 Galay 5.0.1 对 C++23 module prelude 的跨平台 intrinsic 头文件守卫修复,
避免 Clang 在 Linux/macOS 上错误转发 `intrin.h`。
- 跟进 Galay 5.0.2 将 `AioCommitAwaitable::await_suspend` 的类外模板定义放回
Expand Down
99 changes: 81 additions & 18 deletions pkgs/c/compat.cuda-driver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,21 @@
-- package's own `[xlings]`, because mcpp materialises `[xlings] deps` for the
-- ROOT project only and this must resolve when the package itself installs.
--
-- ⭐ ONLY libcuda.so.1 IS LINKED, and that is a measured claim rather than a
-- minimal-effort one. A draft also harvested libnvidia-ptxjitcompiler on the
-- theory that PTX JIT would otherwise fail. Measured on a machine with driver
-- 550.144.03: a binary built for `compute_80` alone, run with only this one
-- symlink reachable, JITs and produces the right answer on an sm_89 device.
-- The driver loads its own siblings through its own paths, which the private
-- loader does not interfere with. The extra patterns were unnecessary.
-- ⭐ THE FARM MIRRORS THE SENTINEL; IT DOES NOT HOLD AN OPINION.
--
-- This package used to name `libcuda.so.1` and link that one file. It now
-- links every versioned soname the sentinel publishes, which is the same
-- delegation the paragraph above describes carried one step further: "where is
-- the driver" and "which driver libraries are there" are one question, and a
-- farm that answers the second one itself can disagree with the package that
-- answers the first. It did: the sentinel gained NVML for mcpp#596 and a
-- hand-written farm would not have noticed.
--
-- The negative result that used to live here has moved to the sentinel, where
-- it decides the set: a draft harvested libnvidia-ptxjitcompiler on the theory
-- that PTX JIT would otherwise fail, and measurement on driver 550.144.03
-- showed it unnecessary. That is a fact about which libraries a client needs,
-- so it belongs beside the list rather than beside one consumer of it.
--
-- NOTHING IS REQUIRED. A machine with no NVIDIA driver is a legitimate
-- configuration -- every runner in this repository is one. The sentinel's
Expand All @@ -80,15 +88,30 @@ package = {
-- The install-time edge. Materialised when THIS package installs,
-- which is what makes the sentinel's directory exist by the time
-- install() below reads it.
deps = { "xim:libcuda-host-link@0.0.1" },
-- 0.0.2 is the version at which the sentinel answers for a SET of
-- driver sonames rather than for `libcuda.so.1` alone. This pin is
-- the only place that decides which of them a consumer gets: the
-- farm below links whatever the pinned sentinel published.
deps = { "xim:libcuda-host-link@0.0.2" },
["2026.09.10"] = {
url = "https://raw.githubusercontent.com/NVIDIA/cuda-samples/v12.5/LICENSE",
sha256 = "b3e40c5bfed1fca5c62d2c1f2208bf51f8d2c910219f94c443f657ace9001be3",
},
["2026.09.05"] = {
-- Nothing downloaded matters: the content is the symlink this
-- install() creates. A stable, tiny anchor keeps the xpm entry
-- well-formed, the same trick compat.vulkan-runtime uses.
url = "https://raw.githubusercontent.com/NVIDIA/cuda-samples/v12.5/LICENSE",
sha256 = "b3e40c5bfed1fca5c62d2c1f2208bf51f8d2c910219f94c443f657ace9001be3",
},
["latest"] = { ref = "2026.09.05" },
-- 2026.09.05 is kept so a consumer already pinning it keeps
-- resolving. WHAT IS FROZEN IS THE ENTRY, NOT THE BEHAVIOUR: there
-- is one install() here and it never reads pkginfo.version(), so an
-- old pin installed today builds the current farm. The new key
-- exists for the machine that already holds the directory and
-- would otherwise never reinstall -- which is the whole of what a
-- version buys for a package whose content is generated.
["latest"] = { ref = "2026.09.10" },
},
},

Expand Down Expand Up @@ -116,22 +139,62 @@ import("xim.libxpkg.log")
-- installed into the shared registry cache is invisible to it and comes back
-- nil, so the known roots are tried before giving up. This is the same fallback
-- compat.mysql-connector-cpp needs for the same reason.
-- The version this package asks the sentinel for. One spelling, because the
-- xpm dependency edge and the directory read back must name the same thing or
-- the farm silently mirrors an older sentinel than the one that was installed.
local SENTINEL_VERSION = "0.0.2"

local function sentinel_dir()
local dir = pkginfo.install_dir("xim:libcuda-host-link", "0.0.1")
local dir = pkginfo.install_dir("xim:libcuda-host-link", SENTINEL_VERSION)
if dir then return dir end
local roots = {}
local pfx = pkginfo.install_dir()
if pfx then roots[#roots + 1] = path.directory(path.directory(pfx)) end
local home = (os.getenv and os.getenv("XLINGS_HOME")) or ""
if home == "" then home = ((os.getenv and os.getenv("HOME")) or "") .. "/.xlings" end
roots[#roots + 1] = path.join(home, "data/xpkgs")
roots[#roots + 1] = path.join((os.getenv and os.getenv("HOME")) or "",
".mcpp/registry/data/xpkgs")
for _, root in ipairs(roots) do
local cand = path.join(root, "xim-x-libcuda-host-link", "0.0.1")
local cand = path.join(root, "xim-x-libcuda-host-link", SENTINEL_VERSION)
if os.isdir(cand) then return cand end
end
return nil
end

-- Link every versioned soname the sentinel publishes into DST.
--
-- ENUMERATED, NOT NAMED, for the reason the header records. Returns the count,
-- which the caller logs: a farm of zero and a farm that was never built read
-- the same in a scrolled log otherwise.
--
-- ONLY VERSIONED SONAMES. mcpp puts runtime.library_dirs on the LINK line as
-- well as the runtime path, so an unversioned `libcuda.so` here would be found
-- by `-lcuda` and would bind the build to one machine's driver. A versioned
-- soname is invisible to the linker and is exactly what dlopen asks for. The
-- sentinel publishes only versioned names today; the filter states the
-- requirement rather than trusting that it stays true.
--
-- `io.popen` rather than `os.files`: the latter is not available in the recipe
-- sandbox, which the llvm and cuda-cccl recipes record the same way.
local function farm_sentinel(dst, src)
local n = 0
local p = io.popen(string.format([[ls -1 "%s" 2>/dev/null]],
path.join(src, "lib")))
if not p then return 0 end
for line in p:lines() do
local name = line:gsub("[\r\n]+$", "")
if name:match("%.so%.%d") then
os.exec(string.format([[ln -sfn "%s" "%s"]],
path.join(src, "lib", name),
path.join(dst, name)))
n = n + 1
end
end
p:close()
return n
end

function install()
os.tryrm(pkginfo.install_dir())
os.mkdir(pkginfo.install_dir())
Expand All @@ -154,12 +217,12 @@ function install()
return true
end

-- Only the versioned soname. mcpp puts runtime.library_dirs on the LINK
-- line as well as the runtime path, so an unversioned libcuda.so here would
-- be picked up by -lcuda and bind a build to one machine's driver. A
-- versioned soname is invisible to the linker and is exactly what dlopen
-- asks for.
os.exec("ln -sf " .. path.join(src, "lib", "libcuda.so.1") .. " "
.. path.join(outdir, "libcuda.so.1"))
local n = farm_sentinel(outdir, src)
if n == 0 then
log.warn("compat.cuda-driver: the sentinel at %s published no versioned "
.. "soname; the runtime library directory is empty", src)
return true
end
log.info("compat.cuda-driver: %d driver soname(s) from %s", n, src)
return true
end
Loading
Loading