From ab8c9669060f466eaf4c3dfa9358dcd22bfadec8 Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Tue, 4 Aug 2026 15:26:58 -0700 Subject: [PATCH] Fix cufftMp soname priority: tabulate linux_sonames oldest-first `load_dl_linux._candidate_sonames()` reverses `desc.linux_sonames` "to achieve new -> old search order", so the catalog must list the oldest version first. `cufftMp` is the only entry that lists them newest-first (`libcufftMp.so.12`, `libcufftMp.so.11`), which inverts the intent: when both versions are reachable, the already-loaded check and the system search both pick `libcufftMp.so.11` over `libcufftMp.so.12`. The entry started as `("libcufftMp.so.11",)` and `.so.12` was prepended rather than appended in #1194. Swap the two and add a catalog invariant test so the next entry cannot regress the same way. --- .../_dynamic_libs/descriptor_catalog.py | 4 +++- cuda_pathfinder/tests/test_descriptor_catalog.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/descriptor_catalog.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/descriptor_catalog.py index e39046eec70..a6ccfc44997 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/descriptor_catalog.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/descriptor_catalog.py @@ -59,6 +59,8 @@ def _ctk_windows_wheel_dirs(cuda13_bin_dir: str, cuda12_dir: str) -> WindowsSear class DescriptorSpec: name: str packaged_with: PackagedWith + # Tabulate oldest version first: the loader reverses these to search + # newest -> oldest (see load_dl_linux._candidate_sonames). linux_sonames: tuple[str, ...] = () windows_dlls: tuple[str, ...] = () supported_windows_arch: tuple[WindowsArch, ...] = () @@ -406,7 +408,7 @@ class DescriptorSpec: DescriptorSpec( name="cufftMp", packaged_with="other", - linux_sonames=("libcufftMp.so.12", "libcufftMp.so.11"), + linux_sonames=("libcufftMp.so.11", "libcufftMp.so.12"), site_packages_linux=("nvidia/cufftmp/cu13/lib", "nvidia/cufftmp/cu12/lib"), dependencies=("nvshmem_host",), requires_rtld_deepbind=True, diff --git a/cuda_pathfinder/tests/test_descriptor_catalog.py b/cuda_pathfinder/tests/test_descriptor_catalog.py index 3b643aa2e77..7f810e7b86d 100644 --- a/cuda_pathfinder/tests/test_descriptor_catalog.py +++ b/cuda_pathfinder/tests/test_descriptor_catalog.py @@ -80,6 +80,22 @@ def test_linux_sonames_look_like_sonames(spec: DescriptorSpec): assert ".so" in soname, f"Unexpected Linux soname format: {soname}" +def _linux_soname_version_key(soname: str) -> tuple[int, ...]: + """Version components of a ``lib.so[.[....]]`` file name.""" + _, _, version_suffix = soname.partition(".so") + return tuple(int(part) for part in version_suffix.split(".") if part) + + +@pytest.mark.parametrize("spec", DESCRIPTOR_CATALOG, ids=lambda s: s.name) +@pytest.mark.agent_authored(model="claude-opus-5") +def test_linux_sonames_are_tabulated_oldest_first(spec: DescriptorSpec): + """``load_dl_linux._candidate_sonames()`` reverses this tuple to search + newest -> oldest, so a newest-first table silently inverts the priority. + """ + versions = [_linux_soname_version_key(soname) for soname in spec.linux_sonames] + assert versions == sorted(versions), f"{spec.name} linux_sonames are not oldest-first: {spec.linux_sonames}" + + @pytest.mark.parametrize("spec", DESCRIPTOR_CATALOG, ids=lambda s: s.name) def test_windows_dlls_look_like_dlls(spec: DescriptorSpec): for dll in spec.windows_dlls: