From bc169d9264bfc12b5b914b8d3a0085318a358331 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 10 Apr 2025 16:21:40 -0700 Subject: [PATCH 01/41] Revert "Reapply "Revert debug changes under .github/workflows"" This reverts commit 8f69f832af51c393601b09c2fe29d874e9abb057. --- .github/workflows/build-and-test.yml | 10 ---------- .github/workflows/test-wheel-linux.yml | 8 ++++---- .github/workflows/test-wheel-windows.yml | 4 ++-- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 288a5624bc..b33bbcf65a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -21,14 +21,9 @@ jobs: matrix: host-platform: - linux-64 - - linux-aarch64 - win-64 python-version: - - "3.13" - "3.12" - - "3.11" - - "3.10" - - "3.9" cuda-version: # Note: this is for build-time only. - "12.8.0" @@ -211,13 +206,8 @@ jobs: matrix: host-platform: - linux-64 - - linux-aarch64 python-version: - - "3.13" - "3.12" - - "3.11" - - "3.10" - - "3.9" cuda-version: # Note: this is for test-time only. - "12.8.0" diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 322f859e3d..19c78c8cca 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -194,7 +194,7 @@ jobs: pushd ./cuda_bindings pip install -r requirements.txt - pytest -rxXs -v tests/ + pytest -ra -s -v tests/ # It is a bit convoluted to run the Cython tests against CTK wheels, # so let's just skip them. @@ -205,7 +205,7 @@ jobs: # TODO: enable this once win-64 runners are up exit 1 fi - pytest -rxXs -v tests/cython + pytest -ra -s -v tests/cython fi popd @@ -229,7 +229,7 @@ jobs: pushd ./cuda_core pip install -r "tests/requirements-cu${TEST_CUDA_MAJOR}.txt" - pytest -rxXs -v tests/ + pytest -ra -s -v tests/ # It is a bit convoluted to run the Cython tests against CTK wheels, # so let's just skip them. Also, currently our CI always installs the @@ -243,7 +243,7 @@ jobs: # TODO: enable this once win-64 runners are up exit 1 fi - pytest -rxXs -v tests/cython + pytest -ra -s -v tests/cython fi popd diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 948d2fae6b..5bfa9bdf05 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -179,7 +179,7 @@ jobs: Push-Location ./cuda_bindings pip install -r requirements.txt - pytest -rxXs -v tests/ + pytest -ra -s -v tests/ # skip Cython tests for now (NVIDIA/cuda-python#466) Pop-Location @@ -203,7 +203,7 @@ jobs: Push-Location ./cuda_core pip install -r "tests/requirements-cu${TEST_CUDA_MAJOR}.txt" - pytest -rxXs -v tests/ + pytest -ra -s -v tests/ Pop-Location - name: Ensure cuda-python installable From 7be2b78e23fb857d799f42983ef05b3c5c4b87bb Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 11 Apr 2025 09:23:23 -0700 Subject: [PATCH 02/41] Add names of all CTK 12.8.1 x86_64-linux libraries (.so) as `path_finder.SUPPORTED_LIBNAMES` https://chatgpt.com/share/67f98d0b-148c-8008-9951-9995cf5d860c --- cuda_bindings/cuda/bindings/path_finder.py | 39 ++++++++++++++++++++++ cuda_bindings/tests/path_finder.py | 15 ++++----- cuda_bindings/tests/test_path_finder.py | 15 +++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 cuda_bindings/tests/test_path_finder.py diff --git a/cuda_bindings/cuda/bindings/path_finder.py b/cuda_bindings/cuda/bindings/path_finder.py index 21aeb4b368..0764d41acc 100644 --- a/cuda_bindings/cuda/bindings/path_finder.py +++ b/cuda_bindings/cuda/bindings/path_finder.py @@ -34,4 +34,43 @@ "get_nvidia_nvvm_ctk", "get_nvidia_static_cudalib_ctk", "get_system_ctk", + "SUPPORTED_LIBNAMES", ] + +SUPPORTED_LIBNAMES = ( + # Core CUDA Runtime and Compiler + "cudart", + "nvfatbin", + "nvJitLink", + "nvrtc", + "nvvm", + # Math Libraries + "cublas", + "cufft", + "curand", + "cusolver", + "cusparse", + "nppc", + "nppial", + "nppicc", + "nppidei", + "nppif", + "nppig", + "nppim", + "nppist", + "nppisu", + "nppitc", + "npps", + "nvblas", + # Profiling and Developer Tools + "cupti", + "nvperf_host", + "nvperf_target", + "nvToolsExt", + # Debugging and Introspection + "accinj64", + "cuinj64", + "checkpoint", + # Miscellaneous + "OpenCL", +) diff --git a/cuda_bindings/tests/path_finder.py b/cuda_bindings/tests/path_finder.py index 9b7dd23a3a..8dfd91c8cc 100644 --- a/cuda_bindings/tests/path_finder.py +++ b/cuda_bindings/tests/path_finder.py @@ -6,13 +6,12 @@ print(f"{k}: {v}", flush=True) print() -libnames = ("nvJitLink", "nvrtc", "nvvm") - -for libname in libnames: - print(path_finder.find_nvidia_dynamic_library(libname)) - print() - -for libname in libnames: +for libname in path_finder.SUPPORTED_LIBNAMES: print(libname) - print(path_finder.load_nvidia_dynamic_library(libname)) + for fun in (path_finder.find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): + try: + out = fun(libname) + except Exception as e: + out = f"EXCEPTION: {type(e)} {str(e)}" + print(out) print() diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py new file mode 100644 index 0000000000..e6c5b50808 --- /dev/null +++ b/cuda_bindings/tests/test_path_finder.py @@ -0,0 +1,15 @@ +import pytest + +from cuda.bindings import path_finder + + +@pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) +def test_find_and_load(libname): + print(f"\n{libname}") + for fun in (path_finder.find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): + try: + out = fun(libname) + except Exception as e: + out = f"EXCEPTION: {type(e)} {str(e)}" + print(out) + print() From 808074d5e14d9630ba241680b297373d1e69f187 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 14 Apr 2025 13:34:31 -0700 Subject: [PATCH 03/41] Add `SUPPORTED_WINDOWS_DLLS` --- cuda_bindings/cuda/bindings/path_finder.py | 48 +++++++++++++++++----- cuda_bindings/tests/path_finder.py | 41 ++++++++++++------ cuda_bindings/tests/test_path_finder.py | 4 ++ 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/cuda_bindings/cuda/bindings/path_finder.py b/cuda_bindings/cuda/bindings/path_finder.py index 0764d41acc..a769009203 100644 --- a/cuda_bindings/cuda/bindings/path_finder.py +++ b/cuda_bindings/cuda/bindings/path_finder.py @@ -35,6 +35,7 @@ "get_nvidia_static_cudalib_ctk", "get_system_ctk", "SUPPORTED_LIBNAMES", + "SUPPORTED_WINDOWS_DLLS", ] SUPPORTED_LIBNAMES = ( @@ -47,8 +48,10 @@ # Math Libraries "cublas", "cufft", + "cufftw", "curand", "cusolver", + "cusolverMg", "cusparse", "nppc", "nppial", @@ -62,15 +65,38 @@ "nppitc", "npps", "nvblas", - # Profiling and Developer Tools - "cupti", - "nvperf_host", - "nvperf_target", - "nvToolsExt", - # Debugging and Introspection - "accinj64", - "cuinj64", - "checkpoint", - # Miscellaneous - "OpenCL", + # Other + "cufile", + "nvjpeg", ) + +# Based on https://developer.download.nvidia.com/compute/cuda/redist/ +# as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). +SUPPORTED_WINDOWS_DLLS = { + "cublas": ("cublas64_12.dll", "cublas64_11.dll"), + "cudart": ("cudart64_12.dll", "cudart64_110.dll", "cudart32_110.dll"), + "cufft": ("cufft64_11.dll", "cufft64_10.dll"), + "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), + "cufile": (), + "curand": ("curand64_10.dll",), + "cusolver": ("cusolver64_11.dll",), + "cusolverMg": ("cusolverMg64_11.dll",), + "cusparse": ("cusparse64_12.dll", "cusparse64_11.dll"), + "nppc": ("nppc64_12.dll", "nppc64_11.dll"), + "nppial": ("nppial64_12.dll", "nppial64_11.dll"), + "nppicc": ("nppicc64_12.dll", "nppicc64_11.dll"), + "nppidei": ("nppidei64_12.dll", "nppidei64_11.dll"), + "nppif": ("nppif64_12.dll", "nppif64_11.dll"), + "nppig": ("nppig64_12.dll", "nppig64_11.dll"), + "nppim": ("nppim64_12.dll", "nppim64_11.dll"), + "nppist": ("nppist64_12.dll", "nppist64_11.dll"), + "nppisu": ("nppisu64_12.dll", "nppisu64_11.dll"), + "nppitc": ("nppitc64_12.dll", "nppitc64_11.dll"), + "npps": ("npps64_12.dll", "npps64_11.dll"), + "nvblas": ("nvblas64_12.dll", "nvblas64_11.dll"), + "nvfatbin": ("nvfatbin_120_0.dll",), + "nvJitLink": ("nvJitLink_120_0.dll",), + "nvjpeg": ("nvjpeg64_12.dll", "nvjpeg64_11.dll"), + "nvrtc": ("nvrtc64_120_0.dll", "nvrtc64_112_0.dll"), + "nvvm": ("nvvm64_40_0.dll",), +} diff --git a/cuda_bindings/tests/path_finder.py b/cuda_bindings/tests/path_finder.py index 8dfd91c8cc..ffa04088a8 100644 --- a/cuda_bindings/tests/path_finder.py +++ b/cuda_bindings/tests/path_finder.py @@ -1,17 +1,34 @@ +import sys + from cuda.bindings import path_finder -paths = path_finder.get_cuda_paths() -for k, v in paths.items(): - print(f"{k}: {v}", flush=True) -print() +def run(args): + assert len(args) == 0 + + paths = path_finder.get_cuda_paths() -for libname in path_finder.SUPPORTED_LIBNAMES: - print(libname) - for fun in (path_finder.find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): - try: - out = fun(libname) - except Exception as e: - out = f"EXCEPTION: {type(e)} {str(e)}" - print(out) + for k, v in paths.items(): + print(f"{k}: {v}", flush=True) print() + + for libname in path_finder.SUPPORTED_WINDOWS_DLLS: + if libname not in path_finder.SUPPORTED_LIBNAMES: + print(f"MISSING IN SUPPORTED_LIBNAMES: {libname}") + + for libname in path_finder.SUPPORTED_LIBNAMES: + print(libname) + dlls = path_finder.SUPPORTED_WINDOWS_DLLS.get(libname) + if dlls is None: + print(f"MISSING IN SUPPORTED_WINDOWS_DLLS: {libname}") + for fun in (path_finder.find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): + try: + out = fun(libname) + except Exception as e: + out = f"EXCEPTION: {type(e)} {str(e)}" + print(out) + print() + + +if __name__ == "__main__": + run(args=sys.argv[1:]) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index e6c5b50808..8c88370088 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -3,6 +3,10 @@ from cuda.bindings import path_finder +def test_supported_libnames_windows_dlls_consistency(): + assert list(sorted(path_finder.SUPPORTED_LIBNAMES)) == list(sorted(path_finder.SUPPORTED_WINDOWS_DLLS.keys())) + + @pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) def test_find_and_load(libname): print(f"\n{libname}") From 832fcc64c65af4a6b590f352ef52cf53b6de889c Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 10:39:45 -0700 Subject: [PATCH 04/41] Add copyright notice --- .../cuda/bindings/_path_finder/load_nvidia_dynamic_library.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index 1a52bf0dde..e16aac097c 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -1,3 +1,7 @@ +# Copyright 2025 NVIDIA Corporation. All rights reserved. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + import functools import sys From 3c0a84d1838cc2fe8cae49242ed5177fdb01c55c Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 10:40:48 -0700 Subject: [PATCH 05/41] Move SUPPORTED_LIBNAMES, SUPPORTED_WINDOWS_DLLS to _path_finder/supported_libs.py --- .../bindings/_path_finder/supported_libs.py | 66 +++++++++++++++++++ cuda_bindings/cuda/bindings/path_finder.py | 64 +----------------- 2 files changed, 67 insertions(+), 63 deletions(-) create mode 100644 cuda_bindings/cuda/bindings/_path_finder/supported_libs.py diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py new file mode 100644 index 0000000000..b6c7fd14bf --- /dev/null +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -0,0 +1,66 @@ +# Copyright 2025 NVIDIA Corporation. All rights reserved. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +SUPPORTED_LIBNAMES = ( + # Core CUDA Runtime and Compiler + "cudart", + "nvfatbin", + "nvJitLink", + "nvrtc", + "nvvm", + # Math Libraries + "cublas", + "cufft", + "cufftw", + "curand", + "cusolver", + "cusolverMg", + "cusparse", + "nppc", + "nppial", + "nppicc", + "nppidei", + "nppif", + "nppig", + "nppim", + "nppist", + "nppisu", + "nppitc", + "npps", + "nvblas", + # Other + "cufile", + "nvjpeg", +) + +# Based on https://developer.download.nvidia.com/compute/cuda/redist/ +# as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). +SUPPORTED_WINDOWS_DLLS = { + "cublas": ("cublas64_12.dll", "cublas64_11.dll"), + "cudart": ("cudart64_12.dll", "cudart64_110.dll", "cudart32_110.dll"), + "cufft": ("cufft64_11.dll", "cufft64_10.dll"), + "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), + "cufile": (), + "curand": ("curand64_10.dll",), + "cusolver": ("cusolver64_11.dll",), + "cusolverMg": ("cusolverMg64_11.dll",), + "cusparse": ("cusparse64_12.dll", "cusparse64_11.dll"), + "nppc": ("nppc64_12.dll", "nppc64_11.dll"), + "nppial": ("nppial64_12.dll", "nppial64_11.dll"), + "nppicc": ("nppicc64_12.dll", "nppicc64_11.dll"), + "nppidei": ("nppidei64_12.dll", "nppidei64_11.dll"), + "nppif": ("nppif64_12.dll", "nppif64_11.dll"), + "nppig": ("nppig64_12.dll", "nppig64_11.dll"), + "nppim": ("nppim64_12.dll", "nppim64_11.dll"), + "nppist": ("nppist64_12.dll", "nppist64_11.dll"), + "nppisu": ("nppisu64_12.dll", "nppisu64_11.dll"), + "nppitc": ("nppitc64_12.dll", "nppitc64_11.dll"), + "npps": ("npps64_12.dll", "npps64_11.dll"), + "nvblas": ("nvblas64_12.dll", "nvblas64_11.dll"), + "nvfatbin": ("nvfatbin_120_0.dll",), + "nvJitLink": ("nvJitLink_120_0.dll",), + "nvjpeg": ("nvjpeg64_12.dll", "nvjpeg64_11.dll"), + "nvrtc": ("nvrtc64_120_0.dll", "nvrtc64_112_0.dll"), + "nvvm": ("nvvm64_40_0.dll",), +} diff --git a/cuda_bindings/cuda/bindings/path_finder.py b/cuda_bindings/cuda/bindings/path_finder.py index a769009203..b36f069ee7 100644 --- a/cuda_bindings/cuda/bindings/path_finder.py +++ b/cuda_bindings/cuda/bindings/path_finder.py @@ -18,6 +18,7 @@ ) from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library from cuda.bindings._path_finder.load_nvidia_dynamic_library import load_nvidia_dynamic_library +from cuda.bindings._path_finder.supported_libs import SUPPORTED_LIBNAMES, SUPPORTED_WINDOWS_DLLS __all__ = [ "find_nvidia_dynamic_library", @@ -37,66 +38,3 @@ "SUPPORTED_LIBNAMES", "SUPPORTED_WINDOWS_DLLS", ] - -SUPPORTED_LIBNAMES = ( - # Core CUDA Runtime and Compiler - "cudart", - "nvfatbin", - "nvJitLink", - "nvrtc", - "nvvm", - # Math Libraries - "cublas", - "cufft", - "cufftw", - "curand", - "cusolver", - "cusolverMg", - "cusparse", - "nppc", - "nppial", - "nppicc", - "nppidei", - "nppif", - "nppig", - "nppim", - "nppist", - "nppisu", - "nppitc", - "npps", - "nvblas", - # Other - "cufile", - "nvjpeg", -) - -# Based on https://developer.download.nvidia.com/compute/cuda/redist/ -# as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). -SUPPORTED_WINDOWS_DLLS = { - "cublas": ("cublas64_12.dll", "cublas64_11.dll"), - "cudart": ("cudart64_12.dll", "cudart64_110.dll", "cudart32_110.dll"), - "cufft": ("cufft64_11.dll", "cufft64_10.dll"), - "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), - "cufile": (), - "curand": ("curand64_10.dll",), - "cusolver": ("cusolver64_11.dll",), - "cusolverMg": ("cusolverMg64_11.dll",), - "cusparse": ("cusparse64_12.dll", "cusparse64_11.dll"), - "nppc": ("nppc64_12.dll", "nppc64_11.dll"), - "nppial": ("nppial64_12.dll", "nppial64_11.dll"), - "nppicc": ("nppicc64_12.dll", "nppicc64_11.dll"), - "nppidei": ("nppidei64_12.dll", "nppidei64_11.dll"), - "nppif": ("nppif64_12.dll", "nppif64_11.dll"), - "nppig": ("nppig64_12.dll", "nppig64_11.dll"), - "nppim": ("nppim64_12.dll", "nppim64_11.dll"), - "nppist": ("nppist64_12.dll", "nppist64_11.dll"), - "nppisu": ("nppisu64_12.dll", "nppisu64_11.dll"), - "nppitc": ("nppitc64_12.dll", "nppitc64_11.dll"), - "npps": ("npps64_12.dll", "npps64_11.dll"), - "nvblas": ("nvblas64_12.dll", "nvblas64_11.dll"), - "nvfatbin": ("nvfatbin_120_0.dll",), - "nvJitLink": ("nvJitLink_120_0.dll",), - "nvjpeg": ("nvjpeg64_12.dll", "nvjpeg64_11.dll"), - "nvrtc": ("nvrtc64_120_0.dll", "nvrtc64_112_0.dll"), - "nvvm": ("nvvm64_40_0.dll",), -} From 31fe75652762238994c900a90d27ff230e25ea43 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 10:46:42 -0700 Subject: [PATCH 06/41] Use SUPPORTED_WINDOWS_DLLS in _windows_load_with_dll_basename() --- .../load_nvidia_dynamic_library.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index e16aac097c..3f3f553d03 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -22,6 +22,7 @@ _LINUX_CDLL_MODE = os.RTLD_NOW | os.RTLD_GLOBAL from .find_nvidia_dynamic_library import find_nvidia_dynamic_library +from .supported_libs import SUPPORTED_WINDOWS_DLLS @functools.cache @@ -48,17 +49,15 @@ def _windows_load_with_dll_basename(name: str) -> int: driver_ver = _windows_cuDriverGetVersion() del driver_ver # Keeping this here because it will probably be needed in the future. - if name == "nvJitLink": - dll_name = "nvJitLink_120_0.dll" - elif name == "nvrtc": - dll_name = "nvrtc64_120_0.dll" - elif name == "nvvm": - dll_name = "nvvm64_40_0.dll" - - try: - return win32api.LoadLibrary(dll_name) - except pywintypes.error: - pass + dll_names = SUPPORTED_WINDOWS_DLLS.get(name) + if dll_names is None: + return None + + for dll_name in dll_names: + try: + return win32api.LoadLibrary(dll_name) + except pywintypes.error: + pass return None From f002d9a8d36246d7daf32d5ce5f736183467d8aa Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 12:30:36 -0700 Subject: [PATCH 07/41] Change "Set up mini CTK" to use `method: local`, remove `sub-packages` line. --- .github/workflows/test-wheel-windows.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 5bfa9bdf05..4d5e542a74 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -161,8 +161,7 @@ jobs: uses: Jimver/cuda-toolkit@v0.2.21 with: cuda: ${{ inputs.cuda-version }} - method: 'network' - sub-packages: ${{ env.MINI_CTK_DEPS }} + method: 'local' - name: Run cuda.bindings tests if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0' }} From d49980665ac484626cd0ad9e7f727d5761f34da5 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 12:34:11 -0700 Subject: [PATCH 08/41] Use Jimver/cuda-toolkit@v0.2.21 also under Linux, `method: local`, no `sub-packages`. --- .github/workflows/test-wheel-linux.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 19c78c8cca..c946f8b3e6 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -174,11 +174,10 @@ jobs: - name: Set up mini CTK if: ${{ inputs.local-ctk == '1' }} - uses: ./.github/actions/fetch_ctk - continue-on-error: false + uses: Jimver/cuda-toolkit@v0.2.21 with: - host-platform: ${{ inputs.host-platform }} - cuda-version: ${{ inputs.cuda-version }} + cuda: ${{ inputs.cuda-version }} + method: 'local' - name: Run cuda.bindings tests if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0' }} From 741bd8d854f127bc42535d7844a8b7cfd012b88a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 13:06:54 -0700 Subject: [PATCH 09/41] Add more `nvidia-*-cu12` wheels to get as many of the supported shared libraries as possible. --- cuda_bindings/pyproject.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cuda_bindings/pyproject.toml b/cuda_bindings/pyproject.toml index 8921cc5a21..8f28e9d033 100644 --- a/cuda_bindings/pyproject.toml +++ b/cuda_bindings/pyproject.toml @@ -40,6 +40,15 @@ all = [ "nvidia-cuda-nvcc-cu12", "nvidia-cuda-nvrtc-cu12", "nvidia-nvjitlink-cu12>=12.3", + "nvidia-cublas-cu12", + "nvidia-cufft-cu12", + "nvidia-curand-cu12", + "nvidia-cusolver-cu12", + "nvidia-cusparse-cu12", + "nvidia-npp-cu12", + "nvidia-nvjpeg-cu12", + "nvidia-nvfatbin-cu12", + "nvidia-cufile-cu12; sys_platform != 'win32'", ] [project.urls] From 3acc5b7d984543fa3969aa7eb7c64007f16b3afe Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 19:50:39 -0700 Subject: [PATCH 10/41] Revert "Use Jimver/cuda-toolkit@v0.2.21 also under Linux, `method: local`, no `sub-packages`." This reverts commit d49980665ac484626cd0ad9e7f727d5761f34da5. Problem observed: ``` /usr/bin/docker exec 1b42cd4ea3149ac3f2448eae830190ee62289b7304a73f8001e90cead5005102 sh -c "cat /etc/*release | grep ^ID" Warning: Failed to restore: Cache service responded with 422 /usr/bin/tar --posix -cf cache.tgz --exclude cache.tgz -P -C /__w/cuda-python/cuda-python --files-from manifest.txt -z Failed to save: Unable to reserve cache with key cuda_installer-linux-5.15.0-135-generic-x64-12.8.0, another job may be creating this cache. More details: This legacy service is shutting down, effective April 15, 2025. Migrate to the new service ASAP. For more information: https://gh.io/gha-cache-sunset Warning: Error during installation: Error: Unable to locate executable file: sudo. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable. Error: Error: Unable to locate executable file: sudo. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable. ``` --- .github/workflows/test-wheel-linux.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index c946f8b3e6..19c78c8cca 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -174,10 +174,11 @@ jobs: - name: Set up mini CTK if: ${{ inputs.local-ctk == '1' }} - uses: Jimver/cuda-toolkit@v0.2.21 + uses: ./.github/actions/fetch_ctk + continue-on-error: false with: - cuda: ${{ inputs.cuda-version }} - method: 'local' + host-platform: ${{ inputs.host-platform }} + cuda-version: ${{ inputs.cuda-version }} - name: Run cuda.bindings tests if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0' }} From cdf8f9333d34fa3482c7a81452e09a7e1f77ba3a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Apr 2025 20:14:09 -0700 Subject: [PATCH 11/41] Change test_path_finder::test_find_and_load() to skip cufile on Windows, and report exceptions as failures, except for cudart --- cuda_bindings/tests/test_path_finder.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 8c88370088..24fc867e31 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -1,3 +1,5 @@ +import sys + import pytest from cuda.bindings import path_finder @@ -9,11 +11,20 @@ def test_supported_libnames_windows_dlls_consistency(): @pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) def test_find_and_load(libname): - print(f"\n{libname}") - for fun in (path_finder.find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): + if sys.platform == "win32" and libname == "cufile": + pytest.skip(f'test_find_and_load("{libname}") not supported on this platform') + print(f'\ntest_find_and_load("{libname}")') + failures = [] + for algo, func in ( + ("find", path_finder.find_nvidia_dynamic_library), + ("load", path_finder.load_nvidia_dynamic_library), + ): try: - out = fun(libname) + out = func(libname) except Exception as e: out = f"EXCEPTION: {type(e)} {str(e)}" + if libname != "cudart": + failures.append(algo) print(out) print() + assert not failures From ae2fa4476a89db2da84b4944d2a7d1c952ea1271 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 16 Apr 2025 08:51:30 -0700 Subject: [PATCH 12/41] Add nvidia-cuda-runtime-cu12 to pyproject.toml (for libname cudart) --- cuda_bindings/pyproject.toml | 1 + cuda_bindings/tests/test_path_finder.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cuda_bindings/pyproject.toml b/cuda_bindings/pyproject.toml index 8f28e9d033..09b252c531 100644 --- a/cuda_bindings/pyproject.toml +++ b/cuda_bindings/pyproject.toml @@ -40,6 +40,7 @@ all = [ "nvidia-cuda-nvcc-cu12", "nvidia-cuda-nvrtc-cu12", "nvidia-nvjitlink-cu12>=12.3", + "nvidia-cuda-runtime-cu12", "nvidia-cublas-cu12", "nvidia-cufft-cu12", "nvidia-curand-cu12", diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 24fc867e31..f8aec4c39e 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -23,8 +23,7 @@ def test_find_and_load(libname): out = func(libname) except Exception as e: out = f"EXCEPTION: {type(e)} {str(e)}" - if libname != "cudart": - failures.append(algo) + failures.append(algo) print(out) print() assert not failures From e7e12c1b4906a534edeeef75ff9ac63b18e1cdb5 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 16 Apr 2025 20:06:33 -0700 Subject: [PATCH 13/41] test_path_finder.py: before loading cusolver, load nvJitLink, cusparse, cublas (experiment to see if that resolves the only Windows failure) Test (win-64, Python 3.12, CUDA 12.8.0, Runner default, CTK wheels) / test ``` ================================== FAILURES =================================== ________________________ test_find_and_load[cusolver] _________________________ libname = 'cusolver' @pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) def test_find_and_load(libname): if sys.platform == "win32" and libname == "cufile": pytest.skip(f'test_find_and_load("{libname}") not supported on this platform') print(f'\ntest_find_and_load("{libname}")') failures = [] for algo, func in ( ("find", path_finder.find_nvidia_dynamic_library), ("load", path_finder.load_nvidia_dynamic_library), ): try: out = func(libname) except Exception as e: out = f"EXCEPTION: {type(e)} {str(e)}" failures.append(algo) print(out) print() > assert not failures E AssertionError: assert not ['load'] tests\test_path_finder.py:29: AssertionError ``` --- cuda_bindings/tests/test_path_finder.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index f8aec4c39e..dece01aa77 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -19,6 +19,10 @@ def test_find_and_load(libname): ("find", path_finder.find_nvidia_dynamic_library), ("load", path_finder.load_nvidia_dynamic_library), ): + if libname == "cusolver" and algo == "load": + func("nvJitLink") + func("cusparse") + func("cublas") try: out = func(libname) except Exception as e: From e47652d575c1ceea68543a164398951bcf076929 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 17 Apr 2025 11:01:03 -0700 Subject: [PATCH 14/41] test_path_finder.py: load *only* nvJitLink before loading cusolver --- cuda_bindings/tests/test_path_finder.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index dece01aa77..acbedd94d5 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -20,9 +20,8 @@ def test_find_and_load(libname): ("load", path_finder.load_nvidia_dynamic_library), ): if libname == "cusolver" and algo == "load": + # Missing in cusolver_windows.pyx (ba9d40222af16c5fa808f0bfa1ca73f185860e12): func("nvJitLink") - func("cusparse") - func("cublas") try: out = func(libname) except Exception as e: From 556a2b4c00f5c612a3b86219955cf26ca15f2e0d Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 17 Apr 2025 16:40:42 -0700 Subject: [PATCH 15/41] Run each test_find_or_load_nvidia_dynamic_library() subtest in a subprocess --- cuda_bindings/tests/test_path_finder.py | 47 +++++++++++++++---------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index acbedd94d5..c522f7e27d 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -1,3 +1,4 @@ +import subprocess import sys import pytest @@ -9,24 +10,34 @@ def test_supported_libnames_windows_dlls_consistency(): assert list(sorted(path_finder.SUPPORTED_LIBNAMES)) == list(sorted(path_finder.SUPPORTED_WINDOWS_DLLS.keys())) +@pytest.mark.parametrize("algo", ("find", "load")) @pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) -def test_find_and_load(libname): +def test_find_or_load_nvidia_dynamic_library(algo, libname): if sys.platform == "win32" and libname == "cufile": pytest.skip(f'test_find_and_load("{libname}") not supported on this platform') - print(f'\ntest_find_and_load("{libname}")') - failures = [] - for algo, func in ( - ("find", path_finder.find_nvidia_dynamic_library), - ("load", path_finder.load_nvidia_dynamic_library), - ): - if libname == "cusolver" and algo == "load": - # Missing in cusolver_windows.pyx (ba9d40222af16c5fa808f0bfa1ca73f185860e12): - func("nvJitLink") - try: - out = func(libname) - except Exception as e: - out = f"EXCEPTION: {type(e)} {str(e)}" - failures.append(algo) - print(out) - print() - assert not failures + + code = """\ +from cuda.bindings import path_finder +""" + if algo == "load" and libname == "cusolver": + code += """\ +path_finder.load_nvidia_dynamic_library("nvJitLink") +path_finder.load_nvidia_dynamic_library("cusparse") +path_finder.load_nvidia_dynamic_library("cublas") +""" + code += f"""\ +path_finder.load_nvidia_dynamic_library({libname!r}) +""" + + result = subprocess.run( + [sys.executable, "-c", code], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + if result.returncode != 0: + raise RuntimeError( + f"Subprocess failed for libname={libname!r} with exit code {result.returncode}\\n" + f"--- stdout ---\\n{result.stdout}\\n" + f"--- stderr ---\\n{result.stderr}" + ) From ffc33a799be560e560c21b9539ca978e6aa96d06 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 17 Apr 2025 20:36:03 -0700 Subject: [PATCH 16/41] Add cublasLt to supported_libs.py and load deps for cusolver, cusolverMg, cusparse in test_path_finder.py. Also restrict test_path_finder.py to test load only for now. --- .../bindings/_path_finder/supported_libs.py | 2 ++ cuda_bindings/tests/test_path_finder.py | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index b6c7fd14bf..c1dd738a42 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -11,6 +11,7 @@ "nvvm", # Math Libraries "cublas", + "cublasLt", "cufft", "cufftw", "curand", @@ -38,6 +39,7 @@ # as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). SUPPORTED_WINDOWS_DLLS = { "cublas": ("cublas64_12.dll", "cublas64_11.dll"), + "cublasLt": ("cublasLt64_12.dll", "cublasLt64_11.dll"), "cudart": ("cudart64_12.dll", "cudart64_110.dll", "cudart32_110.dll"), "cufft": ("cufft64_11.dll", "cufft64_10.dll"), "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index c522f7e27d..18432162a5 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -10,7 +10,7 @@ def test_supported_libnames_windows_dlls_consistency(): assert list(sorted(path_finder.SUPPORTED_LIBNAMES)) == list(sorted(path_finder.SUPPORTED_WINDOWS_DLLS.keys())) -@pytest.mark.parametrize("algo", ("find", "load")) +@pytest.mark.parametrize("algo", ("find", "load")[1:]) @pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) def test_find_or_load_nvidia_dynamic_library(algo, libname): if sys.platform == "win32" and libname == "cufile": @@ -19,11 +19,23 @@ def test_find_or_load_nvidia_dynamic_library(algo, libname): code = """\ from cuda.bindings import path_finder """ - if algo == "load" and libname == "cusolver": - code += """\ + if algo == "load": + if libname == "cusolver": + code += """\ path_finder.load_nvidia_dynamic_library("nvJitLink") path_finder.load_nvidia_dynamic_library("cusparse") +path_finder.load_nvidia_dynamic_library("cublasLt") path_finder.load_nvidia_dynamic_library("cublas") +""" + elif libname == "cusolverMg": + code += """\ +path_finder.load_nvidia_dynamic_library("nvJitLink") +path_finder.load_nvidia_dynamic_library("cublasLt") +path_finder.load_nvidia_dynamic_library("cublas") +""" + elif libname == "cusparse": + code += """\ +path_finder.load_nvidia_dynamic_library("nvJitLink") """ code += f"""\ path_finder.load_nvidia_dynamic_library({libname!r}) From 5c54cbe0976be95e433e58617fde6a2e29aaba77 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 18 Apr 2025 20:50:30 -0700 Subject: [PATCH 17/41] Add supported_libs.DIRECT_DEPENDENCIES --- .../load_nvidia_dynamic_library.py | 5 +++- .../bindings/_path_finder/supported_libs.py | 23 ++++++++++++++++ cuda_bindings/tests/test_path_finder.py | 26 +++---------------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index 3f3f553d03..f320e8d53a 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -22,7 +22,7 @@ _LINUX_CDLL_MODE = os.RTLD_NOW | os.RTLD_GLOBAL from .find_nvidia_dynamic_library import find_nvidia_dynamic_library -from .supported_libs import SUPPORTED_WINDOWS_DLLS +from .supported_libs import DIRECT_DEPENDENCIES, SUPPORTED_WINDOWS_DLLS @functools.cache @@ -64,6 +64,9 @@ def _windows_load_with_dll_basename(name: str) -> int: @functools.cache def load_nvidia_dynamic_library(name: str) -> int: + for dep in DIRECT_DEPENDENCIES.get(name, ()): + load_nvidia_dynamic_library(dep) + # First try using the platform-specific dynamic loader search mechanisms if sys.platform == "win32": handle = _windows_load_with_dll_basename(name) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index c1dd738a42..1cf2bca8ec 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -32,9 +32,31 @@ "nvblas", # Other "cufile", + "cufile_rdma", "nvjpeg", ) +# Based on ldd output for Linux x86_64 nvidia-*-cu12 wheels (12.8.1) +DIRECT_DEPENDENCIES = { + "cublas": ("cublasLt",), + "cufftw": ("cufft",), + "cufile_rdma": ("cufile",), + "cusolver": ("nvJitLink", "cusparse", "cublasLt", "cublas"), + "cusolverMg": ("nvJitLink", "cublasLt", "cublas"), + "cusparse": ("nvJitLink",), + "nppial": ("nppc",), + "nppicc": ("nppc",), + "nppidei": ("nppc",), + "nppif": ("nppc",), + "nppig": ("nppc",), + "nppim": ("nppc",), + "nppist": ("nppc",), + "nppisu": ("nppc",), + "nppitc": ("nppc",), + "npps": ("nppc",), + "nvblas": ("cublas", "cublasLt"), +} + # Based on https://developer.download.nvidia.com/compute/cuda/redist/ # as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). SUPPORTED_WINDOWS_DLLS = { @@ -44,6 +66,7 @@ "cufft": ("cufft64_11.dll", "cufft64_10.dll"), "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), "cufile": (), + "cufile_rdma": (), "curand": ("curand64_10.dll",), "cusolver": ("cusolver64_11.dll",), "cusolverMg": ("cusolverMg64_11.dll",), diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 18432162a5..b9a8333c12 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -13,31 +13,11 @@ def test_supported_libnames_windows_dlls_consistency(): @pytest.mark.parametrize("algo", ("find", "load")[1:]) @pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) def test_find_or_load_nvidia_dynamic_library(algo, libname): - if sys.platform == "win32" and libname == "cufile": - pytest.skip(f'test_find_and_load("{libname}") not supported on this platform') + if sys.platform == "win32" and not path_finder.SUPPORTED_WINDOWS_DLLS[libname]: + pytest.skip(f'"{libname}" not supported on this platform') - code = """\ + code = f"""\ from cuda.bindings import path_finder -""" - if algo == "load": - if libname == "cusolver": - code += """\ -path_finder.load_nvidia_dynamic_library("nvJitLink") -path_finder.load_nvidia_dynamic_library("cusparse") -path_finder.load_nvidia_dynamic_library("cublasLt") -path_finder.load_nvidia_dynamic_library("cublas") -""" - elif libname == "cusolverMg": - code += """\ -path_finder.load_nvidia_dynamic_library("nvJitLink") -path_finder.load_nvidia_dynamic_library("cublasLt") -path_finder.load_nvidia_dynamic_library("cublas") -""" - elif libname == "cusparse": - code += """\ -path_finder.load_nvidia_dynamic_library("nvJitLink") -""" - code += f"""\ path_finder.load_nvidia_dynamic_library({libname!r}) """ From 9e8ee074796c55ec9b9be83090bc9b3134685552 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 18 Apr 2025 22:52:37 -0700 Subject: [PATCH 18/41] Remove cufile_rdma from supported libs (comment out). https://chatgpt.com/share/68033a33-385c-8008-a293-4c8cc3ea23ae --- cuda_bindings/cuda/bindings/_path_finder/supported_libs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index 1cf2bca8ec..da6580354d 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -32,7 +32,7 @@ "nvblas", # Other "cufile", - "cufile_rdma", + # "cufile_rdma", # Requires libmlx5.so "nvjpeg", ) @@ -40,7 +40,7 @@ DIRECT_DEPENDENCIES = { "cublas": ("cublasLt",), "cufftw": ("cufft",), - "cufile_rdma": ("cufile",), + # "cufile_rdma": ("cufile",), "cusolver": ("nvJitLink", "cusparse", "cublasLt", "cublas"), "cusolverMg": ("nvJitLink", "cublasLt", "cublas"), "cusparse": ("nvJitLink",), @@ -66,7 +66,7 @@ "cufft": ("cufft64_11.dll", "cufft64_10.dll"), "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), "cufile": (), - "cufile_rdma": (), + # "cufile_rdma": (), "curand": ("curand64_10.dll",), "cusolver": ("cusolver64_11.dll",), "cusolverMg": ("cusolverMg64_11.dll",), From 09e955a1326a58cd8189b23b8350e4b63e5166f3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 19 Apr 2025 11:24:13 -0700 Subject: [PATCH 19/41] Split out `PARTIALLY_SUPPORTED_LIBNAMES`. Fix up test code. --- .../bindings/_path_finder/supported_libs.py | 9 +++-- cuda_bindings/tests/path_finder.py | 7 ++-- cuda_bindings/tests/test_path_finder.py | 35 ++++++++++++------- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index da6580354d..8f93e0e796 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -4,11 +4,15 @@ SUPPORTED_LIBNAMES = ( # Core CUDA Runtime and Compiler - "cudart", - "nvfatbin", "nvJitLink", "nvrtc", "nvvm", +) + +PARTIALLY_SUPPORTED_LIBNAMES = ( + # Core CUDA Runtime and Compiler + "cudart", + "nvfatbin", # Math Libraries "cublas", "cublasLt", @@ -59,6 +63,7 @@ # Based on https://developer.download.nvidia.com/compute/cuda/redist/ # as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). +# Tuples of DLLs are sorted newest-to-oldest. SUPPORTED_WINDOWS_DLLS = { "cublas": ("cublas64_12.dll", "cublas64_11.dll"), "cublasLt": ("cublasLt64_12.dll", "cublasLt64_11.dll"), diff --git a/cuda_bindings/tests/path_finder.py b/cuda_bindings/tests/path_finder.py index ffa04088a8..ca9facd310 100644 --- a/cuda_bindings/tests/path_finder.py +++ b/cuda_bindings/tests/path_finder.py @@ -1,6 +1,9 @@ import sys from cuda.bindings import path_finder +from cuda.bindings._path_finder import supported_libs + +ALL_LIBNAMES = path_finder.SUPPORTED_LIBNAMES + supported_libs.PARTIALLY_SUPPORTED_LIBNAMES def run(args): @@ -13,10 +16,10 @@ def run(args): print() for libname in path_finder.SUPPORTED_WINDOWS_DLLS: - if libname not in path_finder.SUPPORTED_LIBNAMES: + if libname not in ALL_LIBNAMES: print(f"MISSING IN SUPPORTED_LIBNAMES: {libname}") - for libname in path_finder.SUPPORTED_LIBNAMES: + for libname in ALL_LIBNAMES: print(libname) dlls = path_finder.SUPPORTED_WINDOWS_DLLS.get(libname) if dlls is None: diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index b9a8333c12..46d4b4baac 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -1,26 +1,41 @@ +import os import subprocess import sys import pytest from cuda.bindings import path_finder +from cuda.bindings._path_finder import supported_libs +ALL_LIBNAMES = path_finder.SUPPORTED_LIBNAMES + supported_libs.PARTIALLY_SUPPORTED_LIBNAMES +if os.environ.get("CUDA_BINDINGS_PATH_FINDER_TEST_ALL_LIBNAMES", False): + TEST_LIBNAMES = ALL_LIBNAMES +else: + TEST_LIBNAMES = path_finder.SUPPORTED_LIBNAMES -def test_supported_libnames_windows_dlls_consistency(): - assert list(sorted(path_finder.SUPPORTED_LIBNAMES)) == list(sorted(path_finder.SUPPORTED_WINDOWS_DLLS.keys())) +def test_all_libnames_windows_dlls_consistency(): + assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(path_finder.SUPPORTED_WINDOWS_DLLS.keys())) -@pytest.mark.parametrize("algo", ("find", "load")[1:]) -@pytest.mark.parametrize("libname", path_finder.SUPPORTED_LIBNAMES) + +def _build_subprocess_failed_for_libname_message(libname, result): + return ( + f"Subprocess failed for {libname=!r} with exit code {result.returncode}\n" + f"--- stdout-from-subprocess ---\n{result.stdout}\n" + f"--- stderr-from-subprocess ---\n{result.stderr}\n" + ) + + +@pytest.mark.parametrize("algo", ("find", "load")) +@pytest.mark.parametrize("libname", TEST_LIBNAMES) def test_find_or_load_nvidia_dynamic_library(algo, libname): if sys.platform == "win32" and not path_finder.SUPPORTED_WINDOWS_DLLS[libname]: - pytest.skip(f'"{libname}" not supported on this platform') + pytest.skip(f"{libname=!r} not supported on {sys.platform=}") code = f"""\ from cuda.bindings import path_finder -path_finder.load_nvidia_dynamic_library({libname!r}) +path_finder.{algo}_nvidia_dynamic_library({libname!r}) """ - result = subprocess.run( [sys.executable, "-c", code], stdout=subprocess.PIPE, @@ -28,8 +43,4 @@ def test_find_or_load_nvidia_dynamic_library(algo, libname): encoding="utf-8", ) if result.returncode != 0: - raise RuntimeError( - f"Subprocess failed for libname={libname!r} with exit code {result.returncode}\\n" - f"--- stdout ---\\n{result.stdout}\\n" - f"--- stderr ---\\n{result.stderr}" - ) + raise RuntimeError(_build_subprocess_failed_for_libname_message(libname, result)) From a6b178a49868f3ec2afe21fe0c5ce46fbc249133 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 19 Apr 2025 11:53:44 -0700 Subject: [PATCH 20/41] Reduce public API to only load_nvidia_dynamic_library, SUPPORTED_LIBNAMES --- cuda_bindings/cuda/bindings/path_finder.py | 31 +--------------------- cuda_bindings/tests/path_finder.py | 11 ++++---- cuda_bindings/tests/test_path_finder.py | 20 +++++++++----- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/cuda_bindings/cuda/bindings/path_finder.py b/cuda_bindings/cuda/bindings/path_finder.py index b36f069ee7..9c08bdc258 100644 --- a/cuda_bindings/cuda/bindings/path_finder.py +++ b/cuda_bindings/cuda/bindings/path_finder.py @@ -2,39 +2,10 @@ # # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE -from cuda.bindings._path_finder.cuda_paths import ( - get_conda_ctk, - get_conda_include_dir, - get_cuda_home, - get_cuda_paths, - get_current_cuda_target_name, - get_debian_pkg_libdevice, - get_libdevice_wheel, - get_nvidia_cudalib_ctk, - get_nvidia_libdevice_ctk, - get_nvidia_nvvm_ctk, - get_nvidia_static_cudalib_ctk, - get_system_ctk, -) -from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library from cuda.bindings._path_finder.load_nvidia_dynamic_library import load_nvidia_dynamic_library -from cuda.bindings._path_finder.supported_libs import SUPPORTED_LIBNAMES, SUPPORTED_WINDOWS_DLLS +from cuda.bindings._path_finder.supported_libs import SUPPORTED_LIBNAMES __all__ = [ - "find_nvidia_dynamic_library", "load_nvidia_dynamic_library", - "get_conda_ctk", - "get_conda_include_dir", - "get_cuda_home", - "get_cuda_paths", - "get_current_cuda_target_name", - "get_debian_pkg_libdevice", - "get_libdevice_wheel", - "get_nvidia_cudalib_ctk", - "get_nvidia_libdevice_ctk", - "get_nvidia_nvvm_ctk", - "get_nvidia_static_cudalib_ctk", - "get_system_ctk", "SUPPORTED_LIBNAMES", - "SUPPORTED_WINDOWS_DLLS", ] diff --git a/cuda_bindings/tests/path_finder.py b/cuda_bindings/tests/path_finder.py index ca9facd310..b4ec22a37d 100644 --- a/cuda_bindings/tests/path_finder.py +++ b/cuda_bindings/tests/path_finder.py @@ -1,7 +1,8 @@ import sys from cuda.bindings import path_finder -from cuda.bindings._path_finder import supported_libs +from cuda.bindings._path_finder import cuda_paths, supported_libs +from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library ALL_LIBNAMES = path_finder.SUPPORTED_LIBNAMES + supported_libs.PARTIALLY_SUPPORTED_LIBNAMES @@ -9,22 +10,22 @@ def run(args): assert len(args) == 0 - paths = path_finder.get_cuda_paths() + paths = cuda_paths.get_cuda_paths() for k, v in paths.items(): print(f"{k}: {v}", flush=True) print() - for libname in path_finder.SUPPORTED_WINDOWS_DLLS: + for libname in supported_libs.SUPPORTED_WINDOWS_DLLS: if libname not in ALL_LIBNAMES: print(f"MISSING IN SUPPORTED_LIBNAMES: {libname}") for libname in ALL_LIBNAMES: print(libname) - dlls = path_finder.SUPPORTED_WINDOWS_DLLS.get(libname) + dlls = supported_libs.SUPPORTED_WINDOWS_DLLS.get(libname) if dlls is None: print(f"MISSING IN SUPPORTED_WINDOWS_DLLS: {libname}") - for fun in (path_finder.find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): + for fun in (find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): try: out = fun(libname) except Exception as e: diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 46d4b4baac..cec26fca88 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -15,7 +15,7 @@ def test_all_libnames_windows_dlls_consistency(): - assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(path_finder.SUPPORTED_WINDOWS_DLLS.keys())) + assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.SUPPORTED_WINDOWS_DLLS.keys())) def _build_subprocess_failed_for_libname_message(libname, result): @@ -26,15 +26,21 @@ def _build_subprocess_failed_for_libname_message(libname, result): ) -@pytest.mark.parametrize("algo", ("find", "load")) +@pytest.mark.parametrize("api", ("find", "load")) @pytest.mark.parametrize("libname", TEST_LIBNAMES) -def test_find_or_load_nvidia_dynamic_library(algo, libname): - if sys.platform == "win32" and not path_finder.SUPPORTED_WINDOWS_DLLS[libname]: +def test_find_or_load_nvidia_dynamic_library(api, libname): + if sys.platform == "win32" and not supported_libs.SUPPORTED_WINDOWS_DLLS[libname]: pytest.skip(f"{libname=!r} not supported on {sys.platform=}") - code = f"""\ -from cuda.bindings import path_finder -path_finder.{algo}_nvidia_dynamic_library({libname!r}) + if api == "find": + code = f"""\ +from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library +find_nvidia_dynamic_library({libname!r}) +""" + else: + code = f"""\ +from cuda.bindings.path_finder import load_nvidia_dynamic_library +load_nvidia_dynamic_library({libname!r}) """ result = subprocess.run( [sys.executable, "-c", code], From 5535f4e8d47d7545a927fb5a95a885ad5ab2d57c Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 19 Apr 2025 12:12:55 -0700 Subject: [PATCH 21/41] Set CUDA_BINDINGS_PATH_FINDER_TEST_ALL_LIBNAMES=1 to match expected availability of nvidia shared libraries. --- .github/workflows/test-wheel-linux.yml | 4 ++++ .github/workflows/test-wheel-windows.yml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 19c78c8cca..90709aa6c2 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -74,6 +74,10 @@ jobs: fi fi + if [[ "${{ inputs.local-ctk }}" != 1 ]]; then + echo "CUDA_BINDINGS_PATH_FINDER_TEST_ALL_LIBNAMES=1" >> $GITHUB_ENV + fi + # make outputs from the previous job as env vars CUDA_CORE_ARTIFACT_BASENAME="cuda-core-python${PYTHON_VERSION_FORMATTED}-${{ inputs.host-platform }}" echo "PYTHON_VERSION_FORMATTED=${PYTHON_VERSION_FORMATTED}" >> $GITHUB_ENV diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 4d5e542a74..36d1081fc1 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -61,6 +61,8 @@ jobs: } } + "CUDA_BINDINGS_PATH_FINDER_TEST_ALL_LIBNAMES=1" >> $env:GITHUB_ENV + # Make outputs from the previous job as env vars $CUDA_CORE_ARTIFACT_BASENAME = "cuda-core-python${PYTHON_VERSION_FORMATTED}-${{ inputs.host-platform }}" "PYTHON_VERSION_FORMATTED=${PYTHON_VERSION_FORMATTED}" >> $env:GITHUB_ENV From 97a95c481565dfff6ac810ebe3f32e1e82ad33eb Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 21 Apr 2025 08:24:54 -0700 Subject: [PATCH 22/41] Refactor as `class _find_nvidia_dynamic_library` --- .../find_nvidia_dynamic_library.py | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py index 3d6604f082..478b6b66d0 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py @@ -132,31 +132,42 @@ def _find_dll_using_cudalib_dir(libname, error_messages, attachments): return None -@functools.cache -def find_nvidia_dynamic_library(name: str) -> str: - error_messages = [] - attachments = [] - - if IS_WIN32: - dll_name = _find_dll_using_nvidia_bin_dirs(name, error_messages, attachments) - if dll_name is None: - if name == "nvvm": - dll_name = _get_cuda_paths_info("nvvm", error_messages) - else: - dll_name = _find_dll_using_cudalib_dir(name, error_messages, attachments) - if dll_name is None: - attachments = "\n".join(attachments) - raise RuntimeError(f'Failure finding "{name}*.dll": {", ".join(error_messages)}\n{attachments}') - return dll_name - - so_basename = f"lib{name}.so" - so_name = _find_so_using_nvidia_lib_dirs(name, so_basename, error_messages, attachments) - if so_name is None: - if name == "nvvm": - so_name = _get_cuda_paths_info("nvvm", error_messages) +class _find_nvidia_dynamic_library: + def __init__(self, libname: str): + self.libname = libname + self.error_messages = [] + self.attachments = [] + self.abs_path = None + + if IS_WIN32: + self.abs_path = _find_dll_using_nvidia_bin_dirs(libname, self.error_messages, self.attachments) + if self.abs_path is None: + if libname == "nvvm": + self.abs_path = _get_cuda_paths_info("nvvm", self.error_messages) + else: + self.abs_path = _find_dll_using_cudalib_dir(libname, self.error_messages, self.attachments) + self.lib_searched_for = f"{libname}*.dll" else: - so_name = _find_so_using_cudalib_dir(so_basename, error_messages, attachments) - if so_name is None: - attachments = "\n".join(attachments) - raise RuntimeError(f'Failure finding "{so_basename}": {", ".join(error_messages)}\n{attachments}') - return so_name + self.lib_searched_for = f"lib{libname}.so" + self.abs_path = _find_so_using_nvidia_lib_dirs( + libname, self.lib_searched_for, self.error_messages, self.attachments + ) + if self.abs_path is None: + if libname == "nvvm": + self.abs_path = _get_cuda_paths_info("nvvm", self.error_messages) + else: + self.abs_path = _find_so_using_cudalib_dir( + self.lib_searched_for, self.error_messages, self.attachments + ) + + def raise_if_abs_path_is_None(self): + if self.abs_path: + return self.abs_path + err = ", ".join(self.error_messages) + att = "\n".join(self.attachments) + raise RuntimeError(f'Failure finding "{self.lib_searched_for}": {err}\n{att}') + + +@functools.cache +def find_nvidia_dynamic_library(libname: str) -> str: + return _find_nvidia_dynamic_library(libname).raise_if_abs_path_is_None() From b9f66beebd853138c542a0158484495b0a29eeb8 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 21 Apr 2025 08:42:07 -0700 Subject: [PATCH 23/41] Strict wheel, conda, system rule: try using the platform-specific dynamic loader search mechanisms only last --- .../load_nvidia_dynamic_library.py | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index f320e8d53a..451c6c9d0e 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -21,7 +21,7 @@ _LINUX_CDLL_MODE = os.RTLD_NOW | os.RTLD_GLOBAL -from .find_nvidia_dynamic_library import find_nvidia_dynamic_library +from .find_nvidia_dynamic_library import _find_nvidia_dynamic_library from .supported_libs import DIRECT_DEPENDENCIES, SUPPORTED_WINDOWS_DLLS @@ -63,38 +63,39 @@ def _windows_load_with_dll_basename(name: str) -> int: @functools.cache -def load_nvidia_dynamic_library(name: str) -> int: - for dep in DIRECT_DEPENDENCIES.get(name, ()): +def load_nvidia_dynamic_library(libname: str) -> int: + for dep in DIRECT_DEPENDENCIES.get(libname, ()): load_nvidia_dynamic_library(dep) - # First try using the platform-specific dynamic loader search mechanisms - if sys.platform == "win32": - handle = _windows_load_with_dll_basename(name) - if handle: - return handle - else: - dl_path = f"lib{name}.so" # Version intentionally no specified. - try: - handle = ctypes.CDLL(dl_path, _LINUX_CDLL_MODE) - except OSError: - pass + found = _find_nvidia_dynamic_library(libname) + if found.abs_path is None: + if sys.platform == "win32": + handle = _windows_load_with_dll_basename(libname) + if handle: + # Use `cdef void* ptr = ` in cython to convert back to void* + return handle else: - # Use `cdef void* ptr = ` in cython to convert back to void* - return handle._handle # C unsigned int + try: + handle = ctypes.CDLL(found.lib_searched_for, _LINUX_CDLL_MODE) + except OSError: + pass + else: + # Use `cdef void* ptr = ` in cython to convert back to void* + return handle._handle # C unsigned int + found.raise_if_abs_path_is_None() - dl_path = find_nvidia_dynamic_library(name) if sys.platform == "win32": flags = _WINBASE_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | _WINBASE_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR try: - handle = win32api.LoadLibraryEx(dl_path, 0, flags) + handle = win32api.LoadLibraryEx(found.abs_path, 0, flags) except pywintypes.error as e: - raise RuntimeError(f"Failed to load DLL at {dl_path}: {e}") from e + raise RuntimeError(f"Failed to load DLL at {found.abs_path}: {e}") from e # Use `cdef void* ptr = ` in cython to convert back to void* return handle # C signed int, matches win32api.GetProcAddress else: try: - handle = ctypes.CDLL(dl_path, _LINUX_CDLL_MODE) + handle = ctypes.CDLL(found.abs_path, _LINUX_CDLL_MODE) except OSError as e: - raise RuntimeError(f"Failed to dlopen {dl_path}: {e}") from e + raise RuntimeError(f"Failed to dlopen {found.abs_path}: {e}") from e # Use `cdef void* ptr = ` in cython to convert back to void* return handle._handle # C unsigned int From 016a10377affa731b751ec505d15b6aa262f98d2 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 21 Apr 2025 14:21:14 -0700 Subject: [PATCH 24/41] Introduce _load_and_report_path_linux(), add supported_libs.EXPECTED_LIB_SYMBOLS --- .../load_nvidia_dynamic_library.py | 42 ++++++++++++++++--- .../bindings/_path_finder/supported_libs.py | 32 ++++++++++++++ cuda_bindings/tests/test_path_finder.py | 4 ++ 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index 451c6c9d0e..dac88af213 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +import ctypes import functools import sys @@ -16,13 +17,27 @@ _WINBASE_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 else: - import ctypes + import ctypes.util import os _LINUX_CDLL_MODE = os.RTLD_NOW | os.RTLD_GLOBAL + _LIBDL_PATH = ctypes.util.find_library("dl") or "libdl.so.2" + _LIBDL = ctypes.CDLL(_LIBDL_PATH) + _LIBDL.dladdr.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + _LIBDL.dladdr.restype = ctypes.c_int + + class Dl_info(ctypes.Structure): + _fields_ = [ + ("dli_fname", ctypes.c_char_p), # path to .so + ("dli_fbase", ctypes.c_void_p), + ("dli_sname", ctypes.c_char_p), + ("dli_saddr", ctypes.c_void_p), + ] + + from .find_nvidia_dynamic_library import _find_nvidia_dynamic_library -from .supported_libs import DIRECT_DEPENDENCIES, SUPPORTED_WINDOWS_DLLS +from .supported_libs import DIRECT_DEPENDENCIES, EXPECTED_LIB_SYMBOLS, SUPPORTED_WINDOWS_DLLS @functools.cache @@ -62,6 +77,21 @@ def _windows_load_with_dll_basename(name: str) -> int: return None +def _load_and_report_path_linux(libname, soname: str) -> (int, str): + handle = ctypes.CDLL(soname, _LINUX_CDLL_MODE) + for symbol_name in EXPECTED_LIB_SYMBOLS[libname]: + symbol = getattr(handle, symbol_name, None) + if symbol is not None: + break + else: + raise RuntimeError(f"No expected symbol for {libname=!r}") + addr = ctypes.cast(symbol, ctypes.c_void_p) + info = Dl_info() + if _LIBDL.dladdr(addr, ctypes.byref(info)) == 0: + raise OSError(f"dladdr failed for {soname}") + return handle, info.dli_fname.decode() + + @functools.cache def load_nvidia_dynamic_library(libname: str) -> int: for dep in DIRECT_DEPENDENCIES.get(libname, ()): @@ -76,11 +106,12 @@ def load_nvidia_dynamic_library(libname: str) -> int: return handle else: try: - handle = ctypes.CDLL(found.lib_searched_for, _LINUX_CDLL_MODE) - except OSError: - pass + handle, abs_path = _load_and_report_path_linux(libname, found.lib_searched_for) + except OSError as e: + print(f"SYSTEM OSError for {libname=!r}: {e!r}", flush=True) else: # Use `cdef void* ptr = ` in cython to convert back to void* + print(f"SYSTEM ABS_PATH for {libname=!r}: {abs_path}", flush=True) return handle._handle # C unsigned int found.raise_if_abs_path_is_None() @@ -98,4 +129,5 @@ def load_nvidia_dynamic_library(libname: str) -> int: except OSError as e: raise RuntimeError(f"Failed to dlopen {found.abs_path}: {e}") from e # Use `cdef void* ptr = ` in cython to convert back to void* + print(f"FOUND ABS_PATH for {libname=!r}: {found.abs_path}", flush=True) return handle._handle # C unsigned int diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index 8f93e0e796..2717af8829 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -94,3 +94,35 @@ "nvrtc": ("nvrtc64_120_0.dll", "nvrtc64_112_0.dll"), "nvvm": ("nvvm64_40_0.dll",), } + +# Based on nm output for Linux x86_64 /usr/local/cuda (12.8.1) +EXPECTED_LIB_SYMBOLS = { + "nvJitLink": ("nvJitLinkVersion",), + "nvrtc": ("nvrtcVersion",), + "nvvm": ("nvvmVersion",), + "cudart": ("cudaRuntimeGetVersion",), + "nvfatbin": ("nvFatbinVersion",), + "cublas": ("cublasGetVersion",), + "cublasLt": ("cublasLtGetVersion",), + "cufft": ("cufftGetVersion",), + "cufftw": ("fftwf_malloc",), + "curand": ("curandGetVersion",), + "cusolver": ("cusolverGetVersion",), + "cusolverMg": ("cusolverMgCreate",), + "cusparse": ("cusparseGetVersion",), + "nppc": ("nppGetLibVersion",), + "nppial": ("nppiAdd_32f_C1R",), + "nppicc": ("nppiColorToGray_8u_C3C1R",), + "nppidei": ("nppiCopy_8u_C1R",), + "nppif": ("nppiFilterSobelHorizBorder_8u_C1R",), + "nppig": ("nppiResize_8u_C1R",), + "nppim": ("nppiErode_8u_C1R",), + "nppist": ("nppiMean_8u_C1R",), + "nppisu": ("nppiFree",), + "nppitc": ("nppiThreshold_8u_C1R",), + "npps": ("nppsAdd_32f",), + "nvblas": ("dgemm",), + "cufile": ("cuFileGetVersion",), + # "cufile_rdma": ("rdma_buffer_reg",), + "nvjpeg": ("nvjpegCreate",), +} diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index cec26fca88..cba09cf897 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -18,6 +18,10 @@ def test_all_libnames_windows_dlls_consistency(): assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.SUPPORTED_WINDOWS_DLLS.keys())) +def test_all_libnames_expected_lib_symbols_consistency(): + assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.EXPECTED_LIB_SYMBOLS.keys())) + + def _build_subprocess_failed_for_libname_message(libname, result): return ( f"Subprocess failed for {libname=!r} with exit code {result.returncode}\n" From c813450e1ea01aefd80e0644e9a6ea833792df4d Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 21 Apr 2025 14:38:05 -0700 Subject: [PATCH 25/41] Plug in ctypes.windll.kernel32.GetModuleFileNameW() --- .../load_nvidia_dynamic_library.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index dac88af213..243ab8a2d6 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -5,6 +5,7 @@ import ctypes import functools import sys +from typing import Optional, Tuple if sys.platform == "win32": import ctypes.wintypes @@ -60,7 +61,7 @@ def _windows_cuDriverGetVersion() -> int: @functools.cache -def _windows_load_with_dll_basename(name: str) -> int: +def _windows_load_with_dll_basename(name: str) -> Tuple[Optional[int], Optional[str]]: driver_ver = _windows_cuDriverGetVersion() del driver_ver # Keeping this here because it will probably be needed in the future. @@ -68,16 +69,21 @@ def _windows_load_with_dll_basename(name: str) -> int: if dll_names is None: return None + kernel32 = ctypes.windll.kernel32 + for dll_name in dll_names: - try: - return win32api.LoadLibrary(dll_name) - except pywintypes.error: - pass + handle = kernel32.LoadLibraryW(ctypes.c_wchar_p(dll_name)) + if handle: + buf = ctypes.create_unicode_buffer(260) + n_chars = kernel32.GetModuleFileNameW(ctypes.wintypes.HMODULE(handle), buf, len(buf)) + if n_chars == 0: + raise OSError("GetModuleFileNameW failed") + return handle, buf.value - return None + return None, None -def _load_and_report_path_linux(libname, soname: str) -> (int, str): +def _load_and_report_path_linux(libname, soname: str) -> Tuple[int, str]: handle = ctypes.CDLL(soname, _LINUX_CDLL_MODE) for symbol_name in EXPECTED_LIB_SYMBOLS[libname]: symbol = getattr(handle, symbol_name, None) @@ -100,9 +106,10 @@ def load_nvidia_dynamic_library(libname: str) -> int: found = _find_nvidia_dynamic_library(libname) if found.abs_path is None: if sys.platform == "win32": - handle = _windows_load_with_dll_basename(libname) + handle, abs_path = _windows_load_with_dll_basename(libname) if handle: # Use `cdef void* ptr = ` in cython to convert back to void* + print(f"SYSTEM ABS_PATH for {libname=!r}: {abs_path}", flush=True) return handle else: try: @@ -122,6 +129,7 @@ def load_nvidia_dynamic_library(libname: str) -> int: except pywintypes.error as e: raise RuntimeError(f"Failed to load DLL at {found.abs_path}: {e}") from e # Use `cdef void* ptr = ` in cython to convert back to void* + print(f"FOUND ABS_PATH for {libname=!r}: {found.abs_path}", flush=True) return handle # C signed int, matches win32api.GetProcAddress else: try: From 1344621cf6b542df18eef8fede39b26b102e6449 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 22 Apr 2025 09:14:46 -0700 Subject: [PATCH 26/41] Keep track of nvrtc-related GitHub comment --- .../cuda/bindings/_path_finder/find_nvidia_dynamic_library.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py index 478b6b66d0..2d974f3360 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py @@ -58,6 +58,8 @@ def _find_dll_using_nvidia_bin_dirs(libname, error_messages, attachments): # nvrtc-builtins64_128.dll # nvrtc64_120_0.alt.dll # nvrtc64_120_0.dll + # See also: + # https://github.com/NVIDIA/cuda-python/pull/563#discussion_r2054427641 node = os.path.basename(path) if node.endswith(".alt.dll"): continue From a3ae3a3b49cd5a7f52bc173c133e453e5f791877 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 22 Apr 2025 14:24:43 -0700 Subject: [PATCH 27/41] Factor out `_find_dll_under_dir(dirpath, file_wild)` and reuse from `_find_dll_using_nvidia_bin_dirs()`, `_find_dll_using_cudalib_dir()` (to fix loading nvrtc64_120_0.dll from local CTK) --- .../find_nvidia_dynamic_library.py | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py index 2d974f3360..db81d0608e 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py @@ -43,6 +43,34 @@ def _append_to_os_environ_path(dirpath): os.environ["PATH"] = dirpath if curr_path is None else os.pathsep.join((curr_path, dirpath)) +def _find_dll_under_dir(dirpath, file_wild): + dll_name = None + have_builtins = False + for path in sorted(glob.glob(os.path.join(dirpath, file_wild))): + # nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl: + # nvidia\cuda_nvrtc\bin\ + # nvrtc-builtins64_128.dll + # nvrtc64_120_0.alt.dll + # nvrtc64_120_0.dll + node = os.path.basename(path) + if node.endswith(".alt.dll"): + continue + if "-builtins" in node: + have_builtins = True + continue + if dll_name is not None: + continue + if os.path.isfile(path): + dll_name = path + if dll_name is not None: + if have_builtins: + # Add the DLL directory to the search path + os.add_dll_directory(dirpath) + # Update PATH as a fallback for dependent DLL resolution + _append_to_os_environ_path(dirpath) + return dll_name + + def _find_dll_using_nvidia_bin_dirs(libname, error_messages, attachments): if libname == "nvvm": # noqa: SIM108 nvidia_sub_dirs = ("nvidia", "*", "nvvm", "bin") @@ -50,32 +78,8 @@ def _find_dll_using_nvidia_bin_dirs(libname, error_messages, attachments): nvidia_sub_dirs = ("nvidia", "*", "bin") file_wild = libname + "*.dll" for bin_dir in sys_path_find_sub_dirs(nvidia_sub_dirs): - dll_name = None - have_builtins = False - for path in sorted(glob.glob(os.path.join(bin_dir, file_wild))): - # nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl: - # nvidia\cuda_nvrtc\bin\ - # nvrtc-builtins64_128.dll - # nvrtc64_120_0.alt.dll - # nvrtc64_120_0.dll - # See also: - # https://github.com/NVIDIA/cuda-python/pull/563#discussion_r2054427641 - node = os.path.basename(path) - if node.endswith(".alt.dll"): - continue - if "-builtins" in node: - have_builtins = True - continue - if dll_name is not None: - continue - if os.path.isfile(path): - dll_name = path + dll_name = _find_dll_under_dir(bin_dir, file_wild) if dll_name is not None: - if have_builtins: - # Add the DLL directory to the search path - os.add_dll_directory(bin_dir) - # Update PATH as a fallback for dependent DLL resolution - _append_to_os_environ_path(bin_dir) return dll_name _no_such_file_in_sub_dirs(nvidia_sub_dirs, file_wild, error_messages, attachments) return None @@ -124,9 +128,9 @@ def _find_dll_using_cudalib_dir(libname, error_messages, attachments): if cudalib_dir is None: return None file_wild = libname + "*.dll" - for dll_name in sorted(glob.glob(os.path.join(cudalib_dir, file_wild))): - if os.path.isfile(dll_name): - return dll_name + dll_name = _find_dll_under_dir(cudalib_dir, file_wild) + if dll_name is not None: + return dll_name error_messages.append(f"No such file: {file_wild}") attachments.append(f' listdir("{cudalib_dir}"):') for node in sorted(os.listdir(cudalib_dir)): From d58535d3ab549795a3889abe4d18ec0dcdc13619 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 22 Apr 2025 22:22:19 -0700 Subject: [PATCH 28/41] Minimal "is already loaded" code. --- .../_path_finder/load_nvidia_dynamic_library.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index 243ab8a2d6..f745349845 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -100,6 +100,19 @@ def _load_and_report_path_linux(libname, soname: str) -> Tuple[int, str]: @functools.cache def load_nvidia_dynamic_library(libname: str) -> int: + if sys.platform == "win32": + for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname): + try: + return win32api.GetModuleHandle(dll_name) + except pywintypes.error: + pass + else: + try: + # TODO: This misses lib{libname}.so.N + return ctypes.CDLL(f"lib{libname}.so", mode=os.RTLD_NOLOAD) + except OSError: + pass + for dep in DIRECT_DEPENDENCIES.get(libname, ()): load_nvidia_dynamic_library(dep) From b98e1fa7640436018c67e939d73c72f0679bf754 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 23 Apr 2025 11:10:31 -0700 Subject: [PATCH 29/41] Add THIS FILE NEEDS TO BE REVIEWED/UPDATED FOR EACH CTK RELEASE comment in _path_finder/supported_libs.py --- cuda_bindings/cuda/bindings/_path_finder/supported_libs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index 2717af8829..bfa51ad50a 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# THIS FILE NEEDS TO BE REVIEWED/UPDATED FOR EACH CTK RELEASE + SUPPORTED_LIBNAMES = ( # Core CUDA Runtime and Compiler "nvJitLink", From ec89acc7cbac196f3b222fc6d4a3f7e88920babe Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 23 Apr 2025 11:45:55 -0700 Subject: [PATCH 30/41] Add SUPPORTED_LINUX_SONAMES in _path_finder/supported_libs.py --- .../load_nvidia_dynamic_library.py | 13 +- .../bindings/_path_finder/supported_libs.py | 121 ++++++++++++++++++ cuda_bindings/tests/test_path_finder.py | 4 + toolshed/build_path_finder_sonames.py | 68 ++++++++++ toolshed/find_sonames.sh | 6 + 5 files changed, 206 insertions(+), 6 deletions(-) create mode 100755 toolshed/build_path_finder_sonames.py create mode 100755 toolshed/find_sonames.sh diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index f745349845..b7b53afa07 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -38,7 +38,7 @@ class Dl_info(ctypes.Structure): from .find_nvidia_dynamic_library import _find_nvidia_dynamic_library -from .supported_libs import DIRECT_DEPENDENCIES, EXPECTED_LIB_SYMBOLS, SUPPORTED_WINDOWS_DLLS +from .supported_libs import DIRECT_DEPENDENCIES, EXPECTED_LIB_SYMBOLS, SUPPORTED_LINUX_SONAMES, SUPPORTED_WINDOWS_DLLS @functools.cache @@ -100,6 +100,7 @@ def _load_and_report_path_linux(libname, soname: str) -> Tuple[int, str]: @functools.cache def load_nvidia_dynamic_library(libname: str) -> int: + # Detect if the library was loaded already in some other way (i.e. not via this function). if sys.platform == "win32": for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname): try: @@ -107,11 +108,11 @@ def load_nvidia_dynamic_library(libname: str) -> int: except pywintypes.error: pass else: - try: - # TODO: This misses lib{libname}.so.N - return ctypes.CDLL(f"lib{libname}.so", mode=os.RTLD_NOLOAD) - except OSError: - pass + for soname in SUPPORTED_LINUX_SONAMES.get(libname): + try: + return ctypes.CDLL(soname, mode=os.RTLD_NOLOAD) + except OSError: + pass for dep in DIRECT_DEPENDENCIES.get(libname, ()): load_nvidia_dynamic_library(dep) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index bfa51ad50a..5c84f52199 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -63,6 +63,127 @@ "nvblas": ("cublas", "cublasLt"), } +# Based on these released files: +# cuda_11.0.3_450.51.06_linux.run +# cuda_11.1.1_455.32.00_linux.run +# cuda_11.2.2_460.32.03_linux.run +# cuda_11.3.1_465.19.01_linux.run +# cuda_11.4.4_470.82.01_linux.run +# cuda_11.5.1_495.29.05_linux.run +# cuda_11.6.2_510.47.03_linux.run +# cuda_11.7.1_515.65.01_linux.run +# cuda_11.8.0_520.61.05_linux.run +# cuda_12.0.1_525.85.12_linux.run +# cuda_12.1.1_530.30.02_linux.run +# cuda_12.2.2_535.104.05_linux.run +# cuda_12.3.2_545.23.08_linux.run +# cuda_12.4.1_550.54.15_linux.run +# cuda_12.5.1_555.42.06_linux.run +# cuda_12.6.2_560.35.03_linux.run +# cuda_12.8.0_570.86.10_linux.run +# Generated with toolshed/build_path_finder_sonames.py +SUPPORTED_LINUX_SONAMES = { + "cublas": ( + "libcublas.so.11", + "libcublas.so.12", + ), + "cublasLt": ( + "libcublasLt.so.11", + "libcublasLt.so.12", + ), + "cudart": ( + "libcudart.so.11.0", + "libcudart.so.12", + ), + "cufft": ( + "libcufft.so.10", + "libcufft.so.11", + ), + "cufftw": ( + "libcufftw.so.10", + "libcufftw.so.11", + ), + "cufile": ("libcufile.so.0",), + # "cufile_rdma": ("libcufile_rdma.so.1",), + "curand": ("libcurand.so.10",), + "cusolver": ( + "libcusolver.so.10", + "libcusolver.so.11", + ), + "cusolverMg": ( + "libcusolverMg.so.10", + "libcusolverMg.so.11", + ), + "cusparse": ( + "libcusparse.so.11", + "libcusparse.so.12", + ), + "nppc": ( + "libnppc.so.11", + "libnppc.so.12", + ), + "nppial": ( + "libnppial.so.11", + "libnppial.so.12", + ), + "nppicc": ( + "libnppicc.so.11", + "libnppicc.so.12", + ), + "nppidei": ( + "libnppidei.so.11", + "libnppidei.so.12", + ), + "nppif": ( + "libnppif.so.11", + "libnppif.so.12", + ), + "nppig": ( + "libnppig.so.11", + "libnppig.so.12", + ), + "nppim": ( + "libnppim.so.11", + "libnppim.so.12", + ), + "nppist": ( + "libnppist.so.11", + "libnppist.so.12", + ), + "nppisu": ( + "libnppisu.so.11", + "libnppisu.so.12", + ), + "nppitc": ( + "libnppitc.so.11", + "libnppitc.so.12", + ), + "npps": ( + "libnpps.so.11", + "libnpps.so.12", + ), + "nvJitLink": ("libnvJitLink.so.12",), + "nvblas": ( + "libnvblas.so.11", + "libnvblas.so.12", + ), + "nvfatbin": ("libnvfatbin.so.12",), + "nvjpeg": ( + "libnvjpeg.so.11", + "libnvjpeg.so.12", + ), + "nvrtc": ( + "libnvrtc.so.11.0", + "libnvrtc.so.11.1", + "libnvrtc.so.11.2", + "libnvrtc.so.12", + ), + "nvvm": ( + "libnvvm.so.3", + "libnvvm.so.4", + ), +} + # Based on https://developer.download.nvidia.com/compute/cuda/redist/ # as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). # Tuples of DLLs are sorted newest-to-oldest. diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index cba09cf897..c3844760e6 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -14,6 +14,10 @@ TEST_LIBNAMES = path_finder.SUPPORTED_LIBNAMES +def test_all_libnames_linux_sonames_consistency(): + assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.SUPPORTED_LINUX_SONAMES.keys())) + + def test_all_libnames_windows_dlls_consistency(): assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.SUPPORTED_WINDOWS_DLLS.keys())) diff --git a/toolshed/build_path_finder_sonames.py b/toolshed/build_path_finder_sonames.py new file mode 100755 index 0000000000..807b0a946b --- /dev/null +++ b/toolshed/build_path_finder_sonames.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +import sys + +LIBNAMES_IN_SCOPE_OF_CUDA_BINDINGS_PATH_FINDER = ( + "nvJitLink", + "nvrtc", + "nvvm", + "cudart", + "nvfatbin", + "cublas", + "cublasLt", + "cufft", + "cufftw", + "curand", + "cusolver", + "cusolverMg", + "cusparse", + "nppc", + "nppial", + "nppicc", + "nppidei", + "nppif", + "nppig", + "nppim", + "nppist", + "nppisu", + "nppitc", + "npps", + "nvblas", + "cufile", + "cufile_rdma", + "nvjpeg", +) + + +def run(args): + assert len(args) == 1, "output-of-find_sonames.sh" + + sonames_from_file = set() + for line in open(args[0]).read().splitlines(): + flds = line.split() + assert len(flds) == 3, flds + if flds[-1] != "SONAME_NOT_SET": + sonames_from_file.add(flds[-1]) + + print("SONAMEs in scope of cuda.bindings.path_finder") + print("=============================================") + sonames_in_scope = set() + for libname in sorted(LIBNAMES_IN_SCOPE_OF_CUDA_BINDINGS_PATH_FINDER): + print(f'"{libname}": (') + lib_so = "lib" + libname + ".so" + for soname in sorted(sonames_from_file): + if soname.startswith(lib_so): + sonames_in_scope.add(soname) + print(f' "{soname}",') + print("),") + print() + + print("SONAMEs out of scope") + print("====================") + for soname in sorted(sonames_from_file - sonames_in_scope): + print(soname) + print() + + +if __name__ == "__main__": + run(args=sys.argv[1:]) diff --git a/toolshed/find_sonames.sh b/toolshed/find_sonames.sh new file mode 100755 index 0000000000..79c2e89d5c --- /dev/null +++ b/toolshed/find_sonames.sh @@ -0,0 +1,6 @@ +#!/bin/bash +find "$@" -type f -name '*.so*' -print0 | while IFS= read -r -d '' f; do + type=$(test -L "$f" && echo SYMLINK || echo FILE) + soname=$(readelf -d "$f" 2>/dev/null | awk '/SONAME/ {gsub(/[][]/, "", $5); print $5; exit}') + echo "$f $type ${soname:-SONAME_NOT_SET}" +done From 38a1d6c55bf86d82a7a5f7cb67494c6e59796105 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 23 Apr 2025 22:17:50 -0700 Subject: [PATCH 31/41] Update SUPPORTED_WINDOWS_DLLS in _path_finder/supported_libs.py based on DLLs found in cuda_*win*.exe files. --- .../bindings/_path_finder/supported_libs.py | 150 ++++++++++++++---- toolshed/build_path_finder_dlls.py | 84 ++++++++++ toolshed/build_path_finder_sonames.py | 6 + 3 files changed, 213 insertions(+), 27 deletions(-) create mode 100755 toolshed/build_path_finder_dlls.py diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index 5c84f52199..02596508ff 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -184,38 +184,134 @@ ), } -# Based on https://developer.download.nvidia.com/compute/cuda/redist/ -# as of 2025-04-11 (redistrib_12.8.1.json was the newest .json file). -# Tuples of DLLs are sorted newest-to-oldest. +# Based on these released files: +# cuda_11.0.3_451.82_win10.exe +# cuda_11.1.1_456.81_win10.exe +# cuda_11.2.2_461.33_win10.exe +# cuda_11.3.1_465.89_win10.exe +# cuda_11.4.4_472.50_windows.exe +# cuda_11.5.1_496.13_windows.exe +# cuda_11.6.2_511.65_windows.exe +# cuda_11.7.1_516.94_windows.exe +# cuda_11.8.0_522.06_windows.exe +# cuda_12.0.1_528.33_windows.exe +# cuda_12.1.1_531.14_windows.exe +# cuda_12.2.2_537.13_windows.exe +# cuda_12.3.2_546.12_windows.exe +# cuda_12.4.1_551.78_windows.exe +# cuda_12.5.1_555.85_windows.exe +# cuda_12.6.2_560.94_windows.exe +# cuda_12.8.1_572.61_windows.exe +# Generated with toolshed/build_path_finder_dlls.py (WITH MANUAL EDITS) SUPPORTED_WINDOWS_DLLS = { - "cublas": ("cublas64_12.dll", "cublas64_11.dll"), - "cublasLt": ("cublasLt64_12.dll", "cublasLt64_11.dll"), - "cudart": ("cudart64_12.dll", "cudart64_110.dll", "cudart32_110.dll"), - "cufft": ("cufft64_11.dll", "cufft64_10.dll"), - "cufftw": ("cufftw64_10.dll", "cufftw64_11.dll"), + "cublas": ( + "cublas64_11.dll", + "cublas64_12.dll", + ), + "cublasLt": ( + "cublasLt64_11.dll", + "cublasLt64_12.dll", + ), + "cudart": ( + "cudart32_110.dll", + "cudart32_65.dll", + "cudart32_90.dll", + "cudart64_101.dll", + "cudart64_110.dll", + "cudart64_12.dll", + "cudart64_65.dll", + ), + "cufft": ( + "cufft64_10.dll", + "cufft64_11.dll", + "cufftw64_10.dll", + "cufftw64_11.dll", + ), + "cufftw": ( + "cufftw64_10.dll", + "cufftw64_11.dll", + ), "cufile": (), # "cufile_rdma": (), "curand": ("curand64_10.dll",), - "cusolver": ("cusolver64_11.dll",), - "cusolverMg": ("cusolverMg64_11.dll",), - "cusparse": ("cusparse64_12.dll", "cusparse64_11.dll"), - "nppc": ("nppc64_12.dll", "nppc64_11.dll"), - "nppial": ("nppial64_12.dll", "nppial64_11.dll"), - "nppicc": ("nppicc64_12.dll", "nppicc64_11.dll"), - "nppidei": ("nppidei64_12.dll", "nppidei64_11.dll"), - "nppif": ("nppif64_12.dll", "nppif64_11.dll"), - "nppig": ("nppig64_12.dll", "nppig64_11.dll"), - "nppim": ("nppim64_12.dll", "nppim64_11.dll"), - "nppist": ("nppist64_12.dll", "nppist64_11.dll"), - "nppisu": ("nppisu64_12.dll", "nppisu64_11.dll"), - "nppitc": ("nppitc64_12.dll", "nppitc64_11.dll"), - "npps": ("npps64_12.dll", "npps64_11.dll"), - "nvblas": ("nvblas64_12.dll", "nvblas64_11.dll"), - "nvfatbin": ("nvfatbin_120_0.dll",), + "cusolver": ( + "cusolver64_10.dll", + "cusolver64_11.dll", + ), + "cusolverMg": ( + "cusolverMg64_10.dll", + "cusolverMg64_11.dll", + ), + "cusparse": ( + "cusparse64_11.dll", + "cusparse64_12.dll", + ), + "nppc": ( + "nppc64_11.dll", + "nppc64_12.dll", + ), + "nppial": ( + "nppial64_11.dll", + "nppial64_12.dll", + ), + "nppicc": ( + "nppicc64_11.dll", + "nppicc64_12.dll", + ), + "nppidei": ( + "nppidei64_11.dll", + "nppidei64_12.dll", + ), + "nppif": ( + "nppif64_11.dll", + "nppif64_12.dll", + ), + "nppig": ( + "nppig64_11.dll", + "nppig64_12.dll", + ), + "nppim": ( + "nppim64_11.dll", + "nppim64_12.dll", + ), + "nppist": ( + "nppist64_11.dll", + "nppist64_12.dll", + ), + "nppisu": ( + "nppisu64_11.dll", + "nppisu64_12.dll", + ), + "nppitc": ( + "nppitc64_11.dll", + "nppitc64_12.dll", + ), + "npps": ( + "npps64_11.dll", + "npps64_12.dll", + ), "nvJitLink": ("nvJitLink_120_0.dll",), - "nvjpeg": ("nvjpeg64_12.dll", "nvjpeg64_11.dll"), - "nvrtc": ("nvrtc64_120_0.dll", "nvrtc64_112_0.dll"), - "nvvm": ("nvvm64_40_0.dll",), + "nvblas": ( + "nvblas64_11.dll", + "nvblas64_12.dll", + ), + "nvfatbin": ("nvfatbin_120_0.dll",), + "nvjpeg": ( + "nvjpeg64_11.dll", + "nvjpeg64_12.dll", + ), + "nvrtc": ( + "nvrtc64_110_0.dll", + "nvrtc64_111_0.dll", + "nvrtc64_112_0.dll", + "nvrtc64_120_0.dll", + ), + "nvvm": ( + "nvvm32.dll", + "nvvm64.dll", + "nvvm64_33_0.dll", + "nvvm64_40_0.dll", + ), } # Based on nm output for Linux x86_64 /usr/local/cuda (12.8.1) diff --git a/toolshed/build_path_finder_dlls.py b/toolshed/build_path_finder_dlls.py new file mode 100755 index 0000000000..c82dcd866d --- /dev/null +++ b/toolshed/build_path_finder_dlls.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# Input for this script: .txt files generated with: +# for exe in *.exe; do 7z l $exe > "${exe%.exe}.txt"; done + +# The output of this script +# requires obvious manual edits to remove duplicates and unwanted dlls. + +import sys + +LIBNAMES_IN_SCOPE_OF_CUDA_BINDINGS_PATH_FINDER = ( + "nvJitLink", + "nvrtc", + "nvvm", + "cudart", + "nvfatbin", + "cublas", + "cublasLt", + "cufft", + "cufftw", + "curand", + "cusolver", + "cusolverMg", + "cusparse", + "nppc", + "nppial", + "nppicc", + "nppidei", + "nppif", + "nppig", + "nppim", + "nppist", + "nppisu", + "nppitc", + "npps", + "nvblas", + "cufile", + "cufile_rdma", + "nvjpeg", +) + + +def run(args): + dlls_from_files = set() + for filename in args: + lines_iter = iter(open(filename).read().splitlines()) + for line in lines_iter: + if line.startswith("-------------------"): + break + else: + raise RuntimeError("------------------- NOT FOUND") + for line in lines_iter: + if line.startswith("-------------------"): + break + assert line[52] == " ", line + assert line[53] != " ", line + path = line[53:] + if path.endswith(".dll"): + dll = path.rsplit("/", 1)[1] + dlls_from_files.add(dll) + else: + raise RuntimeError("------------------- NOT FOUND") + + print("DLLs in scope of cuda.bindings.path_finder") + print("==========================================") + dlls_in_scope = set() + for libname in sorted(LIBNAMES_IN_SCOPE_OF_CUDA_BINDINGS_PATH_FINDER): + print(f'"{libname}": (') + for dll in sorted(dlls_from_files): + if dll.startswith(libname): + dlls_in_scope.add(dll) + print(f' "{dll}",') + print("),") + print() + + print("DLLs out of scope") + print("=================") + for dll in sorted(dlls_from_files - dlls_in_scope): + print(dll) + print() + + +if __name__ == "__main__": + run(args=sys.argv[1:]) diff --git a/toolshed/build_path_finder_sonames.py b/toolshed/build_path_finder_sonames.py index 807b0a946b..20e8ec6c7d 100755 --- a/toolshed/build_path_finder_sonames.py +++ b/toolshed/build_path_finder_sonames.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 +# Input for this script: +# output of toolshed/find_sonames.sh + +# The output of this script +# is expected to be usable as-is. + import sys LIBNAMES_IN_SCOPE_OF_CUDA_BINDINGS_PATH_FINDER = ( From 74d7230a6cd01a4cb219cb64a049d19220f2afa4 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 23 Apr 2025 23:01:41 -0700 Subject: [PATCH 32/41] Remove `os.add_dll_directory()` and `os.environ["PATH"]` manipulations from find_nvidia_dynamic_library.py. Add `supported_libs.LIBNAMES_REQUIRING_OS_ADD_DLL_DIRECTORY` and use from `load_nvidia_dynamic_library()`. --- .../find_nvidia_dynamic_library.py | 15 +------------ .../load_nvidia_dynamic_library.py | 22 +++++++++++++++++-- .../bindings/_path_finder/supported_libs.py | 5 +++++ cuda_bindings/tests/test_path_finder.py | 4 ++++ 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py index db81d0608e..2b2455e5c7 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py @@ -38,14 +38,8 @@ def _find_so_using_nvidia_lib_dirs(libname, so_basename, error_messages, attachm return None -def _append_to_os_environ_path(dirpath): - curr_path = os.environ.get("PATH") - os.environ["PATH"] = dirpath if curr_path is None else os.pathsep.join((curr_path, dirpath)) - - def _find_dll_under_dir(dirpath, file_wild): dll_name = None - have_builtins = False for path in sorted(glob.glob(os.path.join(dirpath, file_wild))): # nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl: # nvidia\cuda_nvrtc\bin\ @@ -56,19 +50,12 @@ def _find_dll_under_dir(dirpath, file_wild): if node.endswith(".alt.dll"): continue if "-builtins" in node: - have_builtins = True continue if dll_name is not None: continue if os.path.isfile(path): dll_name = path - if dll_name is not None: - if have_builtins: - # Add the DLL directory to the search path - os.add_dll_directory(dirpath) - # Update PATH as a fallback for dependent DLL resolution - _append_to_os_environ_path(dirpath) - return dll_name + return dll_name def _find_dll_using_nvidia_bin_dirs(libname, error_messages, attachments): diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index b7b53afa07..5074839766 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -4,6 +4,7 @@ import ctypes import functools +import os import sys from typing import Optional, Tuple @@ -19,7 +20,6 @@ else: import ctypes.util - import os _LINUX_CDLL_MODE = os.RTLD_NOW | os.RTLD_GLOBAL @@ -38,7 +38,23 @@ class Dl_info(ctypes.Structure): from .find_nvidia_dynamic_library import _find_nvidia_dynamic_library -from .supported_libs import DIRECT_DEPENDENCIES, EXPECTED_LIB_SYMBOLS, SUPPORTED_LINUX_SONAMES, SUPPORTED_WINDOWS_DLLS +from .supported_libs import ( + DIRECT_DEPENDENCIES, + EXPECTED_LIB_SYMBOLS, + LIBNAMES_REQUIRING_OS_ADD_DLL_DIRECTORY, + SUPPORTED_LINUX_SONAMES, + SUPPORTED_WINDOWS_DLLS, +) + + +def _add_dll_directory(dll_abs_path): + dirpath = os.path.dirname(dll_abs_path) + assert os.path.isdir(dirpath), dll_abs_path + # Add the DLL directory to the search path + os.add_dll_directory(dirpath) + # Update PATH as a fallback for dependent DLL resolution + curr_path = os.environ.get("PATH") + os.environ["PATH"] = dirpath if curr_path is None else os.pathsep.join((curr_path, dirpath)) @functools.cache @@ -137,6 +153,8 @@ def load_nvidia_dynamic_library(libname: str) -> int: found.raise_if_abs_path_is_None() if sys.platform == "win32": + if libname in LIBNAMES_REQUIRING_OS_ADD_DLL_DIRECTORY: + _add_dll_directory(found.abs_path) flags = _WINBASE_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | _WINBASE_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR try: handle = win32api.LoadLibraryEx(found.abs_path, 0, flags) diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index 02596508ff..995f86aa5e 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -314,6 +314,11 @@ ), } +LIBNAMES_REQUIRING_OS_ADD_DLL_DIRECTORY = ( + "cufft", + "nvrtc", +) + # Based on nm output for Linux x86_64 /usr/local/cuda (12.8.1) EXPECTED_LIB_SYMBOLS = { "nvJitLink": ("nvJitLinkVersion",), diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index c3844760e6..6d1070d3ba 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -22,6 +22,10 @@ def test_all_libnames_windows_dlls_consistency(): assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.SUPPORTED_WINDOWS_DLLS.keys())) +def test_all_libnames_libnames_requiring_os_add_dll_directory_consistency(): + assert not (set(supported_libs.LIBNAMES_REQUIRING_OS_ADD_DLL_DIRECTORY) - set(ALL_LIBNAMES)) + + def test_all_libnames_expected_lib_symbols_consistency(): assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.EXPECTED_LIB_SYMBOLS.keys())) From c1a4983e44499c6e851e520860ea09948db40b0b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 23 Apr 2025 23:55:05 -0700 Subject: [PATCH 33/41] Move nvrtc-specific code from find_nvidia_dynamic_library.py to `supported_libs.is_suppressed_dll_file()` --- .../find_nvidia_dynamic_library.py | 20 +++++-------------- .../bindings/_path_finder/supported_libs.py | 12 +++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py index 2b2455e5c7..e60154aa5a 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/find_nvidia_dynamic_library.py @@ -7,6 +7,7 @@ import os from .cuda_paths import IS_WIN32, get_cuda_paths +from .supported_libs import is_suppressed_dll_file from .sys_path_find_sub_dirs import sys_path_find_sub_dirs @@ -39,23 +40,12 @@ def _find_so_using_nvidia_lib_dirs(libname, so_basename, error_messages, attachm def _find_dll_under_dir(dirpath, file_wild): - dll_name = None for path in sorted(glob.glob(os.path.join(dirpath, file_wild))): - # nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl: - # nvidia\cuda_nvrtc\bin\ - # nvrtc-builtins64_128.dll - # nvrtc64_120_0.alt.dll - # nvrtc64_120_0.dll - node = os.path.basename(path) - if node.endswith(".alt.dll"): + if not os.path.isfile(path): continue - if "-builtins" in node: - continue - if dll_name is not None: - continue - if os.path.isfile(path): - dll_name = path - return dll_name + if not is_suppressed_dll_file(os.path.basename(path)): + return path + return None def _find_dll_using_nvidia_bin_dirs(libname, error_messages, attachments): diff --git a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py index 995f86aa5e..ee62b92b8a 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py +++ b/cuda_bindings/cuda/bindings/_path_finder/supported_libs.py @@ -319,6 +319,18 @@ "nvrtc", ) + +def is_suppressed_dll_file(path_basename: str) -> bool: + if path_basename.startswith("nvrtc"): + # nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl: + # nvidia\cuda_nvrtc\bin\ + # nvrtc-builtins64_128.dll + # nvrtc64_120_0.alt.dll + # nvrtc64_120_0.dll + return path_basename.endswith(".alt.dll") or "-builtins" in path_basename + return False + + # Based on nm output for Linux x86_64 /usr/local/cuda (12.8.1) EXPECTED_LIB_SYMBOLS = { "nvJitLink": ("nvJitLinkVersion",), From 9ff46d8aa420e7277dbd50e3834bb5b7fcbcd6ac Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 10:31:17 -0700 Subject: [PATCH 34/41] Introduce dataclass LoadedDL as return type for load_nvidia_dynamic_library() --- .../cuda/bindings/_bindings/cynvrtc.pyx.in | 4 +- .../bindings/_internal/nvjitlink_linux.pyx | 2 +- .../bindings/_internal/nvjitlink_windows.pyx | 2 +- .../cuda/bindings/_internal/nvvm_linux.pyx | 2 +- .../cuda/bindings/_internal/nvvm_windows.pyx | 2 +- .../load_nvidia_dynamic_library.py | 41 ++++++++++--------- cuda_bindings/tests/path_finder.py | 26 +++++------- 7 files changed, 38 insertions(+), 41 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_bindings/cynvrtc.pyx.in b/cuda_bindings/cuda/bindings/_bindings/cynvrtc.pyx.in index 2b0f3dc236..d2bb0b63b0 100644 --- a/cuda_bindings/cuda/bindings/_bindings/cynvrtc.pyx.in +++ b/cuda_bindings/cuda/bindings/_bindings/cynvrtc.pyx.in @@ -56,7 +56,7 @@ cdef int cuPythonInit() except -1 nogil: {{if 'Windows' == platform.system()}} with gil: - handle = path_finder.load_nvidia_dynamic_library("nvrtc") + handle = path_finder.load_nvidia_dynamic_library("nvrtc").handle {{if 'nvrtcGetErrorString' in found_functions}} try: global __nvrtcGetErrorString @@ -242,7 +242,7 @@ cdef int cuPythonInit() except -1 nogil: {{else}} with gil: - handle = path_finder.load_nvidia_dynamic_library("nvrtc") + handle = path_finder.load_nvidia_dynamic_library("nvrtc").handle {{if 'nvrtcGetErrorString' in found_functions}} global __nvrtcGetErrorString __nvrtcGetErrorString = dlfcn.dlsym(handle, 'nvrtcGetErrorString') diff --git a/cuda_bindings/cuda/bindings/_internal/nvjitlink_linux.pyx b/cuda_bindings/cuda/bindings/_internal/nvjitlink_linux.pyx index 9d21a3e105..78b4d802b3 100644 --- a/cuda_bindings/cuda/bindings/_internal/nvjitlink_linux.pyx +++ b/cuda_bindings/cuda/bindings/_internal/nvjitlink_linux.pyx @@ -53,7 +53,7 @@ cdef void* __nvJitLinkVersion = NULL cdef void* load_library(int driver_ver) except* with gil: - cdef uintptr_t handle = path_finder.load_nvidia_dynamic_library("nvJitLink") + cdef uintptr_t handle = path_finder.load_nvidia_dynamic_library("nvJitLink").handle return handle diff --git a/cuda_bindings/cuda/bindings/_internal/nvjitlink_windows.pyx b/cuda_bindings/cuda/bindings/_internal/nvjitlink_windows.pyx index f86972216d..b306a30017 100644 --- a/cuda_bindings/cuda/bindings/_internal/nvjitlink_windows.pyx +++ b/cuda_bindings/cuda/bindings/_internal/nvjitlink_windows.pyx @@ -40,7 +40,7 @@ cdef void* __nvJitLinkVersion = NULL cdef void* load_library(int driver_ver) except* with gil: - cdef intptr_t handle = path_finder.load_nvidia_dynamic_library("nvJitLink") + cdef intptr_t handle = path_finder.load_nvidia_dynamic_library("nvJitLink").handle return handle diff --git a/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx b/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx index 33ba8e6105..82335508be 100644 --- a/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx +++ b/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx @@ -51,7 +51,7 @@ cdef void* __nvvmGetProgramLog = NULL cdef void* load_library(const int driver_ver) except* with gil: - cdef uintptr_t handle = path_finder.load_nvidia_dynamic_library("nvvm") + cdef uintptr_t handle = path_finder.load_nvidia_dynamic_library("nvvm").handle return handle diff --git a/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx b/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx index 6349fa5a1e..21b4d94180 100644 --- a/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx +++ b/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx @@ -38,7 +38,7 @@ cdef void* __nvvmGetProgramLog = NULL cdef void* load_library(int driver_ver) except* with gil: - cdef intptr_t handle = path_finder.load_nvidia_dynamic_library("nvvm") + cdef intptr_t handle = path_finder.load_nvidia_dynamic_library("nvvm").handle return handle diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index 5074839766..ebd0e62e29 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -6,6 +6,7 @@ import functools import os import sys +from dataclasses import dataclass from typing import Optional, Tuple if sys.platform == "win32": @@ -114,19 +115,29 @@ def _load_and_report_path_linux(libname, soname: str) -> Tuple[int, str]: return handle, info.dli_fname.decode() +@dataclass +class LoadedDL: + # ATTENTION: To convert `handle` back to `void*` in cython: + # Linux: `cdef void* ptr = ` + # Windows: `cdef void* ptr = ` + handle: int + abs_path: Optional[str] + was_already_loaded_from_elsewhere: bool + + @functools.cache -def load_nvidia_dynamic_library(libname: str) -> int: +def load_nvidia_dynamic_library(libname: str) -> LoadedDL: # Detect if the library was loaded already in some other way (i.e. not via this function). if sys.platform == "win32": - for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname): + for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname, ()): try: - return win32api.GetModuleHandle(dll_name) + return LoadedDL(win32api.GetModuleHandle(dll_name), None, True) except pywintypes.error: pass else: - for soname in SUPPORTED_LINUX_SONAMES.get(libname): + for soname in SUPPORTED_LINUX_SONAMES.get(libname, ()): try: - return ctypes.CDLL(soname, mode=os.RTLD_NOLOAD) + return LoadedDL(ctypes.CDLL(soname, mode=os.RTLD_NOLOAD), None, True) except OSError: pass @@ -138,18 +149,14 @@ def load_nvidia_dynamic_library(libname: str) -> int: if sys.platform == "win32": handle, abs_path = _windows_load_with_dll_basename(libname) if handle: - # Use `cdef void* ptr = ` in cython to convert back to void* - print(f"SYSTEM ABS_PATH for {libname=!r}: {abs_path}", flush=True) - return handle + return LoadedDL(handle, abs_path, False) else: try: handle, abs_path = _load_and_report_path_linux(libname, found.lib_searched_for) - except OSError as e: - print(f"SYSTEM OSError for {libname=!r}: {e!r}", flush=True) + except OSError: + pass else: - # Use `cdef void* ptr = ` in cython to convert back to void* - print(f"SYSTEM ABS_PATH for {libname=!r}: {abs_path}", flush=True) - return handle._handle # C unsigned int + return LoadedDL(handle._handle, abs_path, False) found.raise_if_abs_path_is_None() if sys.platform == "win32": @@ -160,14 +167,10 @@ def load_nvidia_dynamic_library(libname: str) -> int: handle = win32api.LoadLibraryEx(found.abs_path, 0, flags) except pywintypes.error as e: raise RuntimeError(f"Failed to load DLL at {found.abs_path}: {e}") from e - # Use `cdef void* ptr = ` in cython to convert back to void* - print(f"FOUND ABS_PATH for {libname=!r}: {found.abs_path}", flush=True) - return handle # C signed int, matches win32api.GetProcAddress + return LoadedDL(handle, found.abs_path, False) else: try: handle = ctypes.CDLL(found.abs_path, _LINUX_CDLL_MODE) except OSError as e: raise RuntimeError(f"Failed to dlopen {found.abs_path}: {e}") from e - # Use `cdef void* ptr = ` in cython to convert back to void* - print(f"FOUND ABS_PATH for {libname=!r}: {found.abs_path}", flush=True) - return handle._handle # C unsigned int + return LoadedDL(handle._handle, found.abs_path, False) diff --git a/cuda_bindings/tests/path_finder.py b/cuda_bindings/tests/path_finder.py index b4ec22a37d..7b969d0f2d 100644 --- a/cuda_bindings/tests/path_finder.py +++ b/cuda_bindings/tests/path_finder.py @@ -1,8 +1,8 @@ import sys +import traceback from cuda.bindings import path_finder from cuda.bindings._path_finder import cuda_paths, supported_libs -from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library ALL_LIBNAMES = path_finder.SUPPORTED_LIBNAMES + supported_libs.PARTIALLY_SUPPORTED_LIBNAMES @@ -11,26 +11,20 @@ def run(args): assert len(args) == 0 paths = cuda_paths.get_cuda_paths() - for k, v in paths.items(): print(f"{k}: {v}", flush=True) print() - for libname in supported_libs.SUPPORTED_WINDOWS_DLLS: - if libname not in ALL_LIBNAMES: - print(f"MISSING IN SUPPORTED_LIBNAMES: {libname}") - for libname in ALL_LIBNAMES: - print(libname) - dlls = supported_libs.SUPPORTED_WINDOWS_DLLS.get(libname) - if dlls is None: - print(f"MISSING IN SUPPORTED_WINDOWS_DLLS: {libname}") - for fun in (find_nvidia_dynamic_library, path_finder.load_nvidia_dynamic_library): - try: - out = fun(libname) - except Exception as e: - out = f"EXCEPTION: {type(e)} {str(e)}" - print(out) + print(f"{libname=}") + try: + loaded_dl = path_finder.load_nvidia_dynamic_library(libname) + except Exception as e: + print(f"EXCEPTION for {libname=}:") + traceback.print_exc(file=sys.stdout) + else: + print(f" {loaded_dl.abs_path=!r}") + print(f" {loaded_dl.was_already_loaded_from_elsewhere=!r}") print() From fb0a430a4efbcf00b7e673fb3595ee18eab440b3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 20:22:14 -0700 Subject: [PATCH 35/41] Factor out _abs_path_for_dynamic_library_* and use on handle obtained through "is already loaded" checks --- .../load_nvidia_dynamic_library.py | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index ebd0e62e29..4be141ab59 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -77,6 +77,14 @@ def _windows_cuDriverGetVersion() -> int: return driver_ver.value +def _abs_path_for_dynamic_library_windows(handle: int) -> str: + buf = ctypes.create_unicode_buffer(260) + n_chars = ctypes.windll.kernel32.GetModuleFileNameW(ctypes.wintypes.HMODULE(handle), buf, len(buf)) + if n_chars == 0: + raise OSError("GetModuleFileNameW failed") + return buf.value + + @functools.cache def _windows_load_with_dll_basename(name: str) -> Tuple[Optional[int], Optional[str]]: driver_ver = _windows_cuDriverGetVersion() @@ -86,33 +94,34 @@ def _windows_load_with_dll_basename(name: str) -> Tuple[Optional[int], Optional[ if dll_names is None: return None - kernel32 = ctypes.windll.kernel32 - for dll_name in dll_names: - handle = kernel32.LoadLibraryW(ctypes.c_wchar_p(dll_name)) + handle = ctypes.windll.kernel32.LoadLibraryW(ctypes.c_wchar_p(dll_name)) if handle: - buf = ctypes.create_unicode_buffer(260) - n_chars = kernel32.GetModuleFileNameW(ctypes.wintypes.HMODULE(handle), buf, len(buf)) - if n_chars == 0: - raise OSError("GetModuleFileNameW failed") - return handle, buf.value + return handle, _abs_path_for_dynamic_library_windows(handle) return None, None -def _load_and_report_path_linux(libname, soname: str) -> Tuple[int, str]: - handle = ctypes.CDLL(soname, _LINUX_CDLL_MODE) +def _abs_path_for_dynamic_library_linux(libname: str, handle: int) -> str: for symbol_name in EXPECTED_LIB_SYMBOLS[libname]: symbol = getattr(handle, symbol_name, None) if symbol is not None: break else: - raise RuntimeError(f"No expected symbol for {libname=!r}") + return None addr = ctypes.cast(symbol, ctypes.c_void_p) info = Dl_info() if _LIBDL.dladdr(addr, ctypes.byref(info)) == 0: - raise OSError(f"dladdr failed for {soname}") - return handle, info.dli_fname.decode() + raise OSError(f"dladdr failed for {libname=!r}") + return info.dli_fname.decode() + + +def _load_and_report_path_linux(libname: str, soname: str) -> Tuple[int, str]: + handle = ctypes.CDLL(soname, _LINUX_CDLL_MODE) + abs_path = _abs_path_for_dynamic_library_linux(libname, handle) + if abs_path is None: + raise RuntimeError(f"No expected symbol for {libname=!r}") + return handle, abs_path @dataclass @@ -131,15 +140,19 @@ def load_nvidia_dynamic_library(libname: str) -> LoadedDL: if sys.platform == "win32": for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname, ()): try: - return LoadedDL(win32api.GetModuleHandle(dll_name), None, True) + handle = win32api.GetModuleHandle(dll_name) except pywintypes.error: pass + else: + return LoadedDL(handle, _abs_path_for_dynamic_library_windows(handle), True) else: for soname in SUPPORTED_LINUX_SONAMES.get(libname, ()): try: - return LoadedDL(ctypes.CDLL(soname, mode=os.RTLD_NOLOAD), None, True) + handle = ctypes.CDLL(soname, mode=os.RTLD_NOLOAD) except OSError: pass + else: + return LoadedDL(handle, _abs_path_for_dynamic_library_linux(libname, handle), True) for dep in DIRECT_DEPENDENCIES.get(libname, ()): load_nvidia_dynamic_library(dep) From cb3ca5a90f08f1da79a1235d511c7dc07b9ddbc9 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 20:39:25 -0700 Subject: [PATCH 36/41] Factor out _load_nvidia_dynamic_library_no_cache() and use for exercising LoadedDL.was_already_loaded_from_elsewhere --- .../_path_finder/load_nvidia_dynamic_library.py | 8 ++++++-- cuda_bindings/tests/test_path_finder.py | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py index 4be141ab59..c770de67d0 100644 --- a/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py +++ b/cuda_bindings/cuda/bindings/_path_finder/load_nvidia_dynamic_library.py @@ -134,8 +134,7 @@ class LoadedDL: was_already_loaded_from_elsewhere: bool -@functools.cache -def load_nvidia_dynamic_library(libname: str) -> LoadedDL: +def _load_nvidia_dynamic_library_no_cache(libname: str) -> LoadedDL: # Detect if the library was loaded already in some other way (i.e. not via this function). if sys.platform == "win32": for dll_name in SUPPORTED_WINDOWS_DLLS.get(libname, ()): @@ -187,3 +186,8 @@ def load_nvidia_dynamic_library(libname: str) -> LoadedDL: except OSError as e: raise RuntimeError(f"Failed to dlopen {found.abs_path}: {e}") from e return LoadedDL(handle._handle, found.abs_path, False) + + +@functools.cache +def load_nvidia_dynamic_library(libname: str) -> LoadedDL: + return _load_nvidia_dynamic_library_no_cache(libname) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 6d1070d3ba..7f44dccd2d 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -52,7 +52,21 @@ def test_find_or_load_nvidia_dynamic_library(api, libname): else: code = f"""\ from cuda.bindings.path_finder import load_nvidia_dynamic_library -load_nvidia_dynamic_library({libname!r}) +from cuda.bindings._path_finder.load_nvidia_dynamic_library import _load_nvidia_dynamic_library_no_cache + +loaded_dl_fresh = load_nvidia_dynamic_library({libname!r}) +if loaded_dl_fresh.was_already_loaded_from_elsewhere: + raise RuntimeError("loaded_dl_fresh.was_already_loaded_from_elsewhere") + +loaded_dl_from_cache = load_nvidia_dynamic_library({libname!r}) +if loaded_dl_from_cache is not loaded_dl_fresh: + raise RuntimeError("loaded_dl_from_cache is not loaded_dl_fresh") + +loaded_dl_no_cache = _load_nvidia_dynamic_library_no_cache({libname!r}) +if not loaded_dl_no_cache.was_already_loaded_from_elsewhere: + raise RuntimeError("loaded_dl_no_cache.was_already_loaded_from_elsewhere") +if loaded_dl_no_cache.abs_path != loaded_dl_fresh.abs_path: + raise RuntimeError(f"{{loaded_dl_no_cache.abs_path=!r}} != {{loaded_dl_fresh.abs_path=!r}}") """ result = subprocess.run( [sys.executable, "-c", code], From 93db7304bbf2b13b7d55376c57c20f56561329c5 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 21:22:03 -0700 Subject: [PATCH 37/41] _check_nvjitlink_usable() in test_path_finder.py --- cuda_bindings/tests/test_path_finder.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 7f44dccd2d..6ff76cbeba 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -38,12 +38,21 @@ def _build_subprocess_failed_for_libname_message(libname, result): ) +def _check_nvjitlink_usable(): + from cuda.bindings._internal import nvjitlink as inner_nvjitlink + + return inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion") != 0 + + @pytest.mark.parametrize("api", ("find", "load")) @pytest.mark.parametrize("libname", TEST_LIBNAMES) def test_find_or_load_nvidia_dynamic_library(api, libname): if sys.platform == "win32" and not supported_libs.SUPPORTED_WINDOWS_DLLS[libname]: pytest.skip(f"{libname=!r} not supported on {sys.platform=}") + if libname == "nvJitLink" and not _check_nvjitlink_usable(): + pytest.skip(f"{libname=!r} not usable") + if api == "find": code = f"""\ from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library From 37ac259a3bd53b837a2d9fabe0002c7170e7f3c2 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 23:36:26 -0700 Subject: [PATCH 38/41] Undo changes in .github/workflows/ and cuda_bindings/pyproject.toml --- .github/workflows/build-and-test.yml | 10 ++++++++++ .github/workflows/test-wheel-linux.yml | 12 ++++-------- .github/workflows/test-wheel-windows.yml | 9 ++++----- cuda_bindings/pyproject.toml | 11 ----------- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b33bbcf65a..288a5624bc 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -21,9 +21,14 @@ jobs: matrix: host-platform: - linux-64 + - linux-aarch64 - win-64 python-version: + - "3.13" - "3.12" + - "3.11" + - "3.10" + - "3.9" cuda-version: # Note: this is for build-time only. - "12.8.0" @@ -206,8 +211,13 @@ jobs: matrix: host-platform: - linux-64 + - linux-aarch64 python-version: + - "3.13" - "3.12" + - "3.11" + - "3.10" + - "3.9" cuda-version: # Note: this is for test-time only. - "12.8.0" diff --git a/.github/workflows/test-wheel-linux.yml b/.github/workflows/test-wheel-linux.yml index 90709aa6c2..322f859e3d 100644 --- a/.github/workflows/test-wheel-linux.yml +++ b/.github/workflows/test-wheel-linux.yml @@ -74,10 +74,6 @@ jobs: fi fi - if [[ "${{ inputs.local-ctk }}" != 1 ]]; then - echo "CUDA_BINDINGS_PATH_FINDER_TEST_ALL_LIBNAMES=1" >> $GITHUB_ENV - fi - # make outputs from the previous job as env vars CUDA_CORE_ARTIFACT_BASENAME="cuda-core-python${PYTHON_VERSION_FORMATTED}-${{ inputs.host-platform }}" echo "PYTHON_VERSION_FORMATTED=${PYTHON_VERSION_FORMATTED}" >> $GITHUB_ENV @@ -198,7 +194,7 @@ jobs: pushd ./cuda_bindings pip install -r requirements.txt - pytest -ra -s -v tests/ + pytest -rxXs -v tests/ # It is a bit convoluted to run the Cython tests against CTK wheels, # so let's just skip them. @@ -209,7 +205,7 @@ jobs: # TODO: enable this once win-64 runners are up exit 1 fi - pytest -ra -s -v tests/cython + pytest -rxXs -v tests/cython fi popd @@ -233,7 +229,7 @@ jobs: pushd ./cuda_core pip install -r "tests/requirements-cu${TEST_CUDA_MAJOR}.txt" - pytest -ra -s -v tests/ + pytest -rxXs -v tests/ # It is a bit convoluted to run the Cython tests against CTK wheels, # so let's just skip them. Also, currently our CI always installs the @@ -247,7 +243,7 @@ jobs: # TODO: enable this once win-64 runners are up exit 1 fi - pytest -ra -s -v tests/cython + pytest -rxXs -v tests/cython fi popd diff --git a/.github/workflows/test-wheel-windows.yml b/.github/workflows/test-wheel-windows.yml index 36d1081fc1..948d2fae6b 100644 --- a/.github/workflows/test-wheel-windows.yml +++ b/.github/workflows/test-wheel-windows.yml @@ -61,8 +61,6 @@ jobs: } } - "CUDA_BINDINGS_PATH_FINDER_TEST_ALL_LIBNAMES=1" >> $env:GITHUB_ENV - # Make outputs from the previous job as env vars $CUDA_CORE_ARTIFACT_BASENAME = "cuda-core-python${PYTHON_VERSION_FORMATTED}-${{ inputs.host-platform }}" "PYTHON_VERSION_FORMATTED=${PYTHON_VERSION_FORMATTED}" >> $env:GITHUB_ENV @@ -163,7 +161,8 @@ jobs: uses: Jimver/cuda-toolkit@v0.2.21 with: cuda: ${{ inputs.cuda-version }} - method: 'local' + method: 'network' + sub-packages: ${{ env.MINI_CTK_DEPS }} - name: Run cuda.bindings tests if: ${{ env.SKIP_CUDA_BINDINGS_TEST == '0' }} @@ -180,7 +179,7 @@ jobs: Push-Location ./cuda_bindings pip install -r requirements.txt - pytest -ra -s -v tests/ + pytest -rxXs -v tests/ # skip Cython tests for now (NVIDIA/cuda-python#466) Pop-Location @@ -204,7 +203,7 @@ jobs: Push-Location ./cuda_core pip install -r "tests/requirements-cu${TEST_CUDA_MAJOR}.txt" - pytest -ra -s -v tests/ + pytest -rxXs -v tests/ Pop-Location - name: Ensure cuda-python installable diff --git a/cuda_bindings/pyproject.toml b/cuda_bindings/pyproject.toml index 70287ffa25..8921cc5a21 100644 --- a/cuda_bindings/pyproject.toml +++ b/cuda_bindings/pyproject.toml @@ -25,7 +25,6 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", "Environment :: GPU :: NVIDIA CUDA", ] dynamic = [ @@ -41,16 +40,6 @@ all = [ "nvidia-cuda-nvcc-cu12", "nvidia-cuda-nvrtc-cu12", "nvidia-nvjitlink-cu12>=12.3", - "nvidia-cuda-runtime-cu12", - "nvidia-cublas-cu12", - "nvidia-cufft-cu12", - "nvidia-curand-cu12", - "nvidia-cusolver-cu12", - "nvidia-cusparse-cu12", - "nvidia-npp-cu12", - "nvidia-nvjpeg-cu12", - "nvidia-nvfatbin-cu12", - "nvidia-cufile-cu12; sys_platform != 'win32'", ] [project.urls] From 5769b726d01fc6bced124c431a3d89a8e8213cb3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 23:40:22 -0700 Subject: [PATCH 39/41] Move cuda_bindings/tests/path_finder.py -> toolshed/run_cuda_bindings_path_finder.py --- .../run_cuda_bindings_path_finder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename cuda_bindings/tests/path_finder.py => toolshed/run_cuda_bindings_path_finder.py (85%) diff --git a/cuda_bindings/tests/path_finder.py b/toolshed/run_cuda_bindings_path_finder.py similarity index 85% rename from cuda_bindings/tests/path_finder.py rename to toolshed/run_cuda_bindings_path_finder.py index 7b969d0f2d..5f47b39903 100644 --- a/cuda_bindings/tests/path_finder.py +++ b/toolshed/run_cuda_bindings_path_finder.py @@ -4,7 +4,9 @@ from cuda.bindings import path_finder from cuda.bindings._path_finder import cuda_paths, supported_libs -ALL_LIBNAMES = path_finder.SUPPORTED_LIBNAMES + supported_libs.PARTIALLY_SUPPORTED_LIBNAMES +ALL_LIBNAMES = ( + path_finder.SUPPORTED_LIBNAMES + supported_libs.PARTIALLY_SUPPORTED_LIBNAMES +) def run(args): @@ -19,7 +21,7 @@ def run(args): print(f"{libname=}") try: loaded_dl = path_finder.load_nvidia_dynamic_library(libname) - except Exception as e: + except Exception: print(f"EXCEPTION for {libname=}:") traceback.print_exc(file=sys.stdout) else: From b3cd45bc348d402d6d49a7a514fe8f32ed788419 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 24 Apr 2025 23:56:56 -0700 Subject: [PATCH 40/41] Add bandit suppressions in test_path_finder.py --- cuda_bindings/tests/test_path_finder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index 6ff76cbeba..ae946dd749 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -1,5 +1,5 @@ import os -import subprocess +import subprocess # nosec B404 import sys import pytest @@ -77,7 +77,7 @@ def test_find_or_load_nvidia_dynamic_library(api, libname): if loaded_dl_no_cache.abs_path != loaded_dl_fresh.abs_path: raise RuntimeError(f"{{loaded_dl_no_cache.abs_path=!r}} != {{loaded_dl_fresh.abs_path=!r}}") """ - result = subprocess.run( + result = subprocess.run( # nosec B603 [sys.executable, "-c", code], stdout=subprocess.PIPE, stderr=subprocess.PIPE, From bcac5cd73f562c94e25273e77be61b65024e65a9 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 25 Apr 2025 02:09:53 -0700 Subject: [PATCH 41/41] Add pytest info_summary_append fixture and use from test_path_finder.py to report the absolute paths of the loaded libraries. --- cuda_bindings/tests/conftest.py | 20 ++++++++++++++++++++ cuda_bindings/tests/test_path_finder.py | 23 ++++++++++++++--------- 2 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 cuda_bindings/tests/conftest.py diff --git a/cuda_bindings/tests/conftest.py b/cuda_bindings/tests/conftest.py new file mode 100644 index 0000000000..bcdc37db43 --- /dev/null +++ b/cuda_bindings/tests/conftest.py @@ -0,0 +1,20 @@ +import pytest + + +def pytest_configure(config): + config.custom_info = [] + + +def pytest_terminal_summary(terminalreporter, exitstatus, config): + if config.custom_info: + terminalreporter.write_sep("=", "INFO summary") + for msg in config.custom_info: + terminalreporter.line(f"INFO {msg}") + + +@pytest.fixture +def info_summary_append(request): + def _append(message): + request.config.custom_info.append(f"{request.node.name}: {message}") + + return _append diff --git a/cuda_bindings/tests/test_path_finder.py b/cuda_bindings/tests/test_path_finder.py index ae946dd749..cb659026fc 100644 --- a/cuda_bindings/tests/test_path_finder.py +++ b/cuda_bindings/tests/test_path_finder.py @@ -30,6 +30,12 @@ def test_all_libnames_expected_lib_symbols_consistency(): assert tuple(sorted(ALL_LIBNAMES)) == tuple(sorted(supported_libs.EXPECTED_LIB_SYMBOLS.keys())) +def _check_nvjitlink_usable(): + from cuda.bindings._internal import nvjitlink as inner_nvjitlink + + return inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion") != 0 + + def _build_subprocess_failed_for_libname_message(libname, result): return ( f"Subprocess failed for {libname=!r} with exit code {result.returncode}\n" @@ -38,15 +44,9 @@ def _build_subprocess_failed_for_libname_message(libname, result): ) -def _check_nvjitlink_usable(): - from cuda.bindings._internal import nvjitlink as inner_nvjitlink - - return inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion") != 0 - - @pytest.mark.parametrize("api", ("find", "load")) @pytest.mark.parametrize("libname", TEST_LIBNAMES) -def test_find_or_load_nvidia_dynamic_library(api, libname): +def test_find_or_load_nvidia_dynamic_library(info_summary_append, api, libname): if sys.platform == "win32" and not supported_libs.SUPPORTED_WINDOWS_DLLS[libname]: pytest.skip(f"{libname=!r} not supported on {sys.platform=}") @@ -56,7 +56,8 @@ def test_find_or_load_nvidia_dynamic_library(api, libname): if api == "find": code = f"""\ from cuda.bindings._path_finder.find_nvidia_dynamic_library import find_nvidia_dynamic_library -find_nvidia_dynamic_library({libname!r}) +abs_path = find_nvidia_dynamic_library({libname!r}) +print(f"{{abs_path!r}}") """ else: code = f"""\ @@ -76,6 +77,8 @@ def test_find_or_load_nvidia_dynamic_library(api, libname): raise RuntimeError("loaded_dl_no_cache.was_already_loaded_from_elsewhere") if loaded_dl_no_cache.abs_path != loaded_dl_fresh.abs_path: raise RuntimeError(f"{{loaded_dl_no_cache.abs_path=!r}} != {{loaded_dl_fresh.abs_path=!r}}") + +print(f"{{loaded_dl_fresh.abs_path!r}}") """ result = subprocess.run( # nosec B603 [sys.executable, "-c", code], @@ -83,5 +86,7 @@ def test_find_or_load_nvidia_dynamic_library(api, libname): stderr=subprocess.PIPE, encoding="utf-8", ) - if result.returncode != 0: + if result.returncode == 0: + info_summary_append(f"abs_path={result.stdout.rstrip()}") + else: raise RuntimeError(_build_subprocess_failed_for_libname_message(libname, result))