diff --git a/pkgs/c/compat.sycl-runtime.lua b/pkgs/c/compat.sycl-runtime.lua index 9f42de1..b1bef00 100644 --- a/pkgs/c/compat.sycl-runtime.lua +++ b/pkgs/c/compat.sycl-runtime.lua @@ -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" }, diff --git a/tests/examples/sycl-runtime/tests/farm.cpp b/tests/examples/sycl-runtime/tests/farm.cpp index 147d371..29105bc 100644 --- a/tests/examples/sycl-runtime/tests/farm.cpp +++ b/tests/examples/sycl-runtime/tests/farm.cpp @@ -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. @@ -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"; } @@ -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",