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
45 changes: 25 additions & 20 deletions pkgs/c/compat.sycl-runtime.lua
Original file line number Diff line number Diff line change
Expand Up @@ -128,29 +128,34 @@ package = {
c_standard = "c11",
sources = { "mcpp_generated/sycl_runtime_empty.c" },
targets = { ["sycl_runtime"] = { kind = "lib" } },
-- THE OTHER BACK END THE PAYLOAD SHIPS AN ADAPTER FOR IS DELIBERATELY
-- NOT SERVED HERE, and saying so is the point: it was unstated before,
-- which is the same condition that let the CUDA one break silently.
-- THE OTHER BACK END THE PAYLOAD SHIPS AN ADAPTER FOR.
--
-- `libur_adapter_opencl.so.0` has `libOpenCL.so.1` in its DT_NEEDED and
-- nothing on an mcpp artifact's search path provides it, so the OpenCL
-- back end of a SYCL program does not load. `compat:opencl` builds that
-- loader and would fix it in one line here.
-- `libur_adapter_opencl.so.0` has `libOpenCL.so.1` in its DT_NEEDED,
-- and nothing on an mcpp artifact's search path provided it, so the
-- OpenCL back end of a SYCL program did not load. That is the same
-- defect as the missing driver soname one API over, and it was
-- invisible for the same reason: an adapter that fails to load is
-- reported by nothing.
--
-- MEASURED REASON FOR NOT WRITING THAT LINE. `compat:opencl` depends in
-- turn on `compat:opencl-runtime`, a farm of the HOST's proprietary
-- OpenCL driver family, so the edge would put a machine-specific vendor
-- surface into every SYCL project. It also carries `libnvidia-ml.so.1`,
-- and with the edge declared it satisfied the CUDA adapter's need for
-- NVML -- which made the farm below look correct while it was not. A
-- dependency that hides the defect the package next to it is fixing is
-- the wrong dependency.
-- A DEPENDENCY RATHER THAN A FARM ENTRY. Unlike the driver, this one is
-- not a host file that may not be redistributed: `compat:opencl` builds
-- the Khronos ICD loader from source with the canonical soname, and it
-- reaches the machine's own drivers through `compat:opencl-runtime`.
-- Farming a copy here would be a second answer to a question this index
-- already answers.
--
-- A project that wants OpenCL devices from SYCL writes
-- `[dependencies.compat] opencl = "2026.05.29"` in its own manifest,
-- where the vendor surface is its choice. `tests/farm.cpp` records the
-- adapter as expected-unserved so the omission stays visible.
deps = {},
-- WHAT WAS BRIEFLY WRITTEN HERE INSTEAD, AND WHY IT WAS WRONG. A draft
-- declared the adapter unserved, on the ground that `compat:opencl`
-- drags a vendor surface in and that its farm -- which carries
-- `libnvidia-ml.so.1` -- made this package's own farm look correct
-- while it was missing NVML. The second half was true of a criterion
-- that measured the PROCESS: `tests/farm.cpp` now reads each member's
-- DT_NEEDED against this farm alone, so nothing else on the search path
-- can hide a gap in it. With that fixed, the remaining objection was
-- only the size of the surface, and a back end the payload ships an
-- adapter for is not something a runtime adapter should leave
-- unreachable.
deps = { ["compat.opencl"] = "2026.05.29" },
runtime = {
library_dirs = { "mcpp_generated/sycl_runtime/lib" },
capabilities = { "sycl.runtime" },
Expand Down
33 changes: 21 additions & 12 deletions tests/examples/sycl-runtime/tests/farm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@
// consumer. The entry is present, so the package did its part; the target
// is the machine's answer.
//
// * A SONAME this package states it does not serve. Today that is
// `libOpenCL.so.1`: the payload ships an OpenCL adapter, and serving it
// would mean depending on `compat:opencl`, which drags the host's
// proprietary OpenCL driver farm into every SYCL project. The recipe
// records that decision; this list is the half that makes it visible, and
// a project that wants the back end declares `compat:opencl` itself.
// * A SONAME this package gets from a DECLARED DEPENDENCY rather than from
// its own farm. Today that is `libOpenCL.so.1`: `compat:sycl-runtime`
// depends on `compat:opencl`, whose shared library is deployed beside the
// consumer's executable and is found there through `$ORIGIN`. The name is
// listed here rather than resolved through the process, because resolving
// through the process is what let another farm answer for
// `libnvidia-ml.so.1` and hide the gap this test exists to catch. A short
// list that a reader can check is the price of a criterion that cannot be
// masked.
//
// Which devices exist is deliberately NOT asserted: that is the machine's
// answer, not this package's.
Expand Down Expand Up @@ -83,9 +86,14 @@ bool provided_by_the_artifact(const std::string& soname) {
|| soname.rfind("ld-linux", 0) == 0;
}

// Stated, not served. See the header for the reason and for what a project
// that wants it writes instead.
bool declared_unserved(const std::string& soname) {
// Served by a declared dependency of this package, not by its farm.
//
// `compat:opencl` builds the Khronos ICD loader with the canonical soname and
// mcpp deploys it beside the consumer's executable, where `$ORIGIN` finds it.
// Naming it here keeps the farm's self-sufficiency assertion exact: everything
// NOT on this list must be in the farm, and no other directory on the search
// path can answer for it.
bool served_by_a_declared_dependency(const std::string& soname) {
return soname == "libOpenCL.so.1";
}

Expand Down Expand Up @@ -258,9 +266,10 @@ int main() {
name.c_str(), soname.c_str());
continue;
}
if (declared_unserved(soname)) {
std::printf("note %s needs %s, which this package states it "
"does not serve\n", name.c_str(), soname.c_str());
if (served_by_a_declared_dependency(soname)) {
std::printf("note %s needs %s, which a declared dependency of "
"this package provides\n",
name.c_str(), soname.c_str());
continue;
}
std::printf("FAIL %s needs %s, which the farm does not carry\n",
Expand Down
Loading