diff --git a/cuda_bindings/pixi.lock b/cuda_bindings/pixi.lock index f84d569dff..fb3d0ad393 100644 --- a/cuda_bindings/pixi.lock +++ b/cuda_bindings/pixi.lock @@ -26,7 +26,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-13.1.80-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-13.1.80-h376f20c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-13.1.80-h376f20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.1.80-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.1.115-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-13.1.80-h69a702a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-13.1.80-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-13.1.80-h4bc722e_0.conda @@ -72,7 +72,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.16.0.49-hd07211c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.16.1.26-hd07211c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda @@ -220,7 +220,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-static-13.1.80-h8f3c8d4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-aarch64-13.1.80-h8f3c8d4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-aarch64-13.1.80-h8f3c8d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.1.80-h8f3c8d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.1.115-h8f3c8d4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-13.1.80-he9431aa_100.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-aarch64-13.1.80-h579c4fd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-impl-13.1.80-h7b14b0b_0.conda @@ -263,7 +263,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-5_haddc8a3_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcap-2.77-h68e9139_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-5_hd72aa62_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.16.0.49-hbf501ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.16.1.26-hbf501ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-h1af38f5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.125-he30d5cf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_2.conda @@ -401,7 +401,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-cudart-static-13.1.80-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_win-64-13.1.80-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_win-64-13.1.80-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.1.80-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.1.115-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-13.1.80-h719f0c7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_win-64-13.1.80-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-impl-13.1.80-h2466b09_0.conda @@ -542,7 +542,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-cudart-static-13.1.80-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-13.1.80-h376f20c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-13.1.80-h376f20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.1.80-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.1.115-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-13.1.80-h69a702a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-64-13.1.80-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-impl-13.1.80-h4bc722e_0.conda @@ -588,7 +588,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.77-h3ff7636_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.16.0.49-hd07211c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.16.1.26-hd07211c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda @@ -736,7 +736,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-cudart-static-13.1.80-h8f3c8d4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-aarch64-13.1.80-h8f3c8d4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-aarch64-13.1.80-h8f3c8d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.1.80-h8f3c8d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.1.115-h8f3c8d4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-13.1.80-he9431aa_100.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_linux-aarch64-13.1.80-h579c4fd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvvm-impl-13.1.80-h7b14b0b_0.conda @@ -779,7 +779,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-5_haddc8a3_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcap-2.77-h68e9139_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-5_hd72aa62_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.16.0.49-hbf501ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.16.1.26-hbf501ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-h1af38f5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.125-he30d5cf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_2.conda @@ -917,7 +917,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-cudart-static-13.1.80-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_win-64-13.1.80-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_win-64-13.1.80-hac47afa_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.1.80-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.1.115-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-13.1.80-h719f0c7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-nvvm-dev_win-64-13.1.80-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvvm-impl-13.1.80-h2466b09_0.conda @@ -1461,10 +1461,10 @@ packages: - cuda-pathfinder >=1.1,<2 - libnvjitlink - cuda-nvrtc - - cuda-nvrtc >=13.1.80,<14.0a0 + - cuda-nvrtc >=13.1.115,<14.0a0 - cuda-nvvm - libcufile - - libcufile >=1.16.0.49,<2.0a0 + - libcufile >=1.16.1.26,<2.0a0 - libgcc >=15 - libgcc >=15 - libstdcxx >=15 @@ -1483,7 +1483,7 @@ packages: - cuda-pathfinder >=1.1,<2 - libnvjitlink - cuda-nvrtc - - cuda-nvrtc >=13.1.80,<14.0a0 + - cuda-nvrtc >=13.1.115,<14.0a0 - cuda-nvvm - vc >=14.1,<15 - vc14_runtime >=14.16.27033 @@ -1502,10 +1502,10 @@ packages: - cuda-pathfinder >=1.1,<2 - libnvjitlink - cuda-nvrtc - - cuda-nvrtc >=13.1.80,<14.0a0 + - cuda-nvrtc >=13.1.115,<14.0a0 - cuda-nvvm - libcufile - - libcufile >=1.16.0.49,<2.0a0 + - libcufile >=1.16.1.26,<2.0a0 - libgcc >=15 - libgcc >=15 - libstdcxx >=15 @@ -1759,39 +1759,39 @@ packages: license: LicenseRef-NVIDIA-End-User-License-Agreement size: 24082 timestamp: 1764883821516 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.1.80-hecca717_0.conda - sha256: d6b326bdbf6fa7bfa0fa617dda547dc585159816b8f130f2535740c4e53fd12c - md5: 7ef874b2dc4ca388ecef3b3893305459 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-13.1.115-hecca717_0.conda + sha256: 9cc4f9df70c02eea5121cdb0e865207b04cd52591f57ebcac2ba44fada10eb5b + md5: df16c9049d882cdaf4f83a5b90079589 depends: - __glibc >=2.17,<3.0.a0 - cuda-version >=13.1,<13.2.0a0 - libgcc >=14 - libstdcxx >=14 license: LicenseRef-NVIDIA-End-User-License-Agreement - size: 35479197 - timestamp: 1764880529154 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.1.80-h8f3c8d4_0.conda - sha256: 5e10ce4dd84c22c73e58a9f8359fb1e5ef4596afd3a0bc12b9fbde73b388ec0d - md5: 0473ebdb01f2f4024177b024fc19fa72 + size: 35339417 + timestamp: 1768272955912 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cuda-nvrtc-13.1.115-h8f3c8d4_0.conda + sha256: a1ec61512cecb093797e00590ad381ecd5852d2a32440ff22b34f78c743f3d5a + md5: 34da2ff2c64054d65eb8f04d76c40cca depends: - arm-variant * sbsa - cuda-version >=13.1,<13.2.0a0 - libgcc >=14 - libstdcxx >=14 license: LicenseRef-NVIDIA-End-User-License-Agreement - size: 33619044 - timestamp: 1764880672755 -- conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.1.80-hac47afa_0.conda - sha256: 3f67de8a9eb182fa20bbc80bda7185afb676cfe8894f6a0549173bd752a7d2f4 - md5: 7b42337a35cd887ec3eed254b5ed606f + size: 33616576 + timestamp: 1768272976976 +- conda: https://conda.anaconda.org/conda-forge/win-64/cuda-nvrtc-13.1.115-hac47afa_0.conda + sha256: a8869b7d997722f90b9f8a602dc0b1d0d497f2a6f3561dc89383aeb2cd379a66 + md5: 372d3c612a832d5f87d8dd9702d487b2 depends: - cuda-version >=13.1,<13.2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LicenseRef-NVIDIA-End-User-License-Agreement - size: 31012754 - timestamp: 1764880740086 + size: 31006920 + timestamp: 1768273107962 - conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-13.1.80-h69a702a_0.conda sha256: 84f971ab146e2c822103cfe06f478ece244747a6f2aa565be639a4709d0a1579 md5: 9250c651d8758c8f665dff7519ef21ff @@ -3275,9 +3275,9 @@ packages: license_family: BSD size: 68079 timestamp: 1765819124349 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.16.0.49-hd07211c_0.conda - sha256: 6aabad84132b1f3ee367e5d24291febf8a11d9a7f3967a64fc07e77d9b0b22df - md5: 9cb68a85f8c08f0512931f944f6a75df +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcufile-1.16.1.26-hd07211c_0.conda + sha256: 8c44b5bf947afad827df0df49fe7483cf1b2916694081b2db4fecdfd6a2bacd1 + md5: 48418c48dac04671fa46cb446122b8a5 depends: - __glibc >=2.28,<3.0.a0 - cuda-version >=13.1,<13.2.0a0 @@ -3285,11 +3285,11 @@ packages: - libstdcxx >=14 - rdma-core >=60.0 license: LicenseRef-NVIDIA-End-User-License-Agreement - size: 990030 - timestamp: 1764881892686 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.16.0.49-hbf501ad_0.conda - sha256: d03963dc7708ded20340176ade987fc4c3e49da4f7b139a85e69ca7eb413f57a - md5: 315e1b144eaf890519fc63049b6e9228 + size: 990938 + timestamp: 1768273732081 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcufile-1.16.1.26-hbf501ad_0.conda + sha256: 7451b3e2204e6cad21db501052dfe595c3440213ef3e22c0f9c784012f6a8419 + md5: ee60a24c702ce02de95ae1982c4841d8 depends: - __glibc >=2.28,<3.0.a0 - arm-variant * sbsa @@ -3300,8 +3300,8 @@ packages: constrains: - arm-variant * sbsa license: LicenseRef-NVIDIA-End-User-License-Agreement - size: 887547 - timestamp: 1764881951574 + size: 891752 + timestamp: 1768273724252 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 md5: 6c77a605a7a689d17d4819c0f8ac9a00 diff --git a/cuda_pathfinder/cuda/pathfinder/__init__.py b/cuda_pathfinder/cuda/pathfinder/__init__.py index 8da4020116..f5a688e1ea 100644 --- a/cuda_pathfinder/cuda/pathfinder/__init__.py +++ b/cuda_pathfinder/cuda/pathfinder/__init__.py @@ -3,6 +3,10 @@ """cuda.pathfinder public APIs""" +from cuda.pathfinder._version import __version__ # noqa: F401 + +from cuda.pathfinder._binaries.find_nvidia_binaries import find_nvidia_binary as find_nvidia_binary +from cuda.pathfinder._binaries.supported_nvidia_binaries import SUPPORTED_BINARIES as SUPPORTED_NVIDIA_BINARIES from cuda.pathfinder._dynamic_libs.load_dl_common import DynamicLibNotFoundError as DynamicLibNotFoundError from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL as LoadedDL from cuda.pathfinder._dynamic_libs.load_nvidia_dynamic_lib import load_nvidia_dynamic_lib as load_nvidia_dynamic_lib @@ -11,6 +15,10 @@ ) from cuda.pathfinder._headers.find_nvidia_headers import find_nvidia_header_directory as find_nvidia_header_directory from cuda.pathfinder._headers.supported_nvidia_headers import SUPPORTED_HEADERS_CTK as _SUPPORTED_HEADERS_CTK +from cuda.pathfinder._static_libs.find_nvidia_static_libs import find_nvidia_static_lib as find_nvidia_static_lib +from cuda.pathfinder._static_libs.supported_nvidia_static_libs import ( + SUPPORTED_STATIC_LIBS as SUPPORTED_NVIDIA_STATIC_LIBS, +) from cuda.pathfinder._version import __version__ # isort: skip # noqa: F401 diff --git a/cuda_pathfinder/cuda/pathfinder/_binaries/find_nvidia_binaries.py b/cuda_pathfinder/cuda/pathfinder/_binaries/find_nvidia_binaries.py new file mode 100644 index 0000000000..cc46899f54 --- /dev/null +++ b/cuda_pathfinder/cuda/pathfinder/_binaries/find_nvidia_binaries.py @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Find CUDA binary executables across different installation sources.""" + +import functools +import os +from typing import Optional + +from cuda.pathfinder._binaries.supported_nvidia_binaries import SITE_PACKAGES_BINDIRS, SUPPORTED_BINARIES +from cuda.pathfinder._utils.env_vars import get_cuda_home_or_path +from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs_all_sitepackages +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS + + +@functools.cache +def find_nvidia_binary(binary_name: str) -> Optional[str]: + """Locate a CUDA binary executable. + + This function searches for CUDA binaries across multiple installation + sources in priority order. + + Args: + binary_name: The name of the binary to find (e.g., ``"nvdisasm"``, + ``"cuobjdump"``). + + Returns: + Absolute path to the discovered binary, or ``None`` if the + binary cannot be found. + + Raises: + ValueError: If ``binary_name`` is not in the supported set. + + Search order: + 1. **NVIDIA Python wheels (site-packages)** + + - Scan installed distributions for binaries + shipped in NVIDIA wheels (e.g., ``cuda-nvcc``). + + 2. **Conda environments** + + - Check ``$CONDA_PREFIX/bin`` (Linux/Mac) or + ``$CONDA_PREFIX/Library/bin`` (Windows). + + 3. **CUDA Toolkit environment variables** + + - Use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order), + look in the ``bin`` subdirectory. + + Examples: + Basic usage: + + >>> from cuda.pathfinder import find_nvidia_binary + >>> path = find_nvidia_binary("nvcc") + >>> if path: + ... print(f"Found nvcc at {path}") + + Note: + Results are cached via ``functools.cache`` for performance. + """ + if binary_name not in SUPPORTED_BINARIES: + raise ValueError(f"Unknown binary: {binary_name!r}") + + # Filename variants (try both with and without .exe for cross-platform support) + variants = (binary_name, f"{binary_name}.exe") + + # 1. Search site-packages (NVIDIA Python wheels) + if site_dirs := SITE_PACKAGES_BINDIRS.get(binary_name): + for rel_path in site_dirs: + for abs_dir in find_sub_dirs_all_sitepackages(tuple(rel_path.split("/"))): + for variant in variants: + path = os.path.join(abs_dir, variant) + if os.path.isfile(path): + return os.path.abspath(path) + + # 2. Search Conda environment + if conda_prefix := os.environ.get("CONDA_PREFIX"): + subdirs = ("Library/bin", "bin") if IS_WINDOWS else ("bin",) + for subdir in subdirs: + for variant in variants: + path = os.path.join(conda_prefix, subdir, variant) + if os.path.isfile(path): + return os.path.abspath(path) + + # 3. Search CUDA_HOME/CUDA_PATH + if cuda_home := get_cuda_home_or_path(): + for variant in variants: + path = os.path.join(cuda_home, "bin", variant) + if os.path.isfile(path): + return os.path.abspath(path) + + return None diff --git a/cuda_pathfinder/cuda/pathfinder/_binaries/supported_nvidia_binaries.py b/cuda_pathfinder/cuda/pathfinder/_binaries/supported_nvidia_binaries.py new file mode 100644 index 0000000000..0e9bc6ca8a --- /dev/null +++ b/cuda_pathfinder/cuda/pathfinder/_binaries/supported_nvidia_binaries.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# THIS FILE NEEDS TO BE REVIEWED/UPDATED FOR EACH CTK RELEASE +# Likely candidates for updates are: +# SUPPORTED_BINARIES +# SITE_PACKAGES_BINDIRS + +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS + +# Supported CUDA binaries that can be found +SUPPORTED_BINARIES_COMMON = ( + "nvdisasm", + "cuobjdump", +) + +SUPPORTED_BINARIES = SUPPORTED_BINARIES_COMMON + +# Map from binary name to relative paths under site-packages +# These are typically from cuda-toolkit[nvcc] wheels +SITE_PACKAGES_BINDIRS = { + "nvdisasm": ["nvidia/cuda_nvcc/bin"], + "cuobjdump": ["nvidia/cuda_nvcc/bin"], +} diff --git a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py index 63f8a627fd..81586dbb67 100644 --- a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py +++ b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py @@ -12,6 +12,7 @@ def _abs_norm(path: str | None) -> str | None: + """Normalize and return absolute path, or None if path is None.""" if path: return os.path.normpath(os.path.abspath(path)) return None @@ -111,14 +112,14 @@ def find_nvidia_header_directory(libname: str) -> str | None: RuntimeError: If ``libname`` is not in the supported set. Search order: - 1. **NVIDIA Python wheels** + 1. **NVIDIA Python wheels (site-packages)** - - Scan installed distributions (``site-packages``) for header layouts + - Scan installed distributions for header layouts shipped in NVIDIA wheels (e.g., ``cuda-toolkit[nvrtc]``). 2. **Conda environments** - - Check Conda-style installation prefixes, which use platform-specific + - Check ``$CONDA_PREFIX`` with platform-specific include directory layouts. 3. **CUDA Toolkit environment variables** diff --git a/cuda_pathfinder/cuda/pathfinder/_static_libs/find_nvidia_static_libs.py b/cuda_pathfinder/cuda/pathfinder/_static_libs/find_nvidia_static_libs.py new file mode 100644 index 0000000000..31bab1604b --- /dev/null +++ b/cuda_pathfinder/cuda/pathfinder/_static_libs/find_nvidia_static_libs.py @@ -0,0 +1,153 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Find CUDA static libraries and artifacts across different installation sources.""" + +import functools +import glob +import os +from typing import Optional + +from cuda.pathfinder._static_libs.supported_nvidia_static_libs import ( + SITE_PACKAGES_STATIC_LIBDIRS, + SUPPORTED_STATIC_LIBS, +) +from cuda.pathfinder._utils.env_vars import get_cuda_home_or_path +from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs_all_sitepackages +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS + + +def _get_lib_filename_variants(artifact_name: str) -> tuple[str, ...]: + """Generate platform-appropriate library filename variants. + + Args: + artifact_name: Canonical artifact name (e.g., "cudadevrt", "libdevice.10.bc"). + + Returns: + Tuple of possible filenames for the current platform. + + Examples: + On Linux: + "cudadevrt" -> ("libcudadevrt.a",) + "libdevice.10.bc" -> ("libdevice.10.bc",) + On Windows: + "cudadevrt" -> ("cudadevrt.lib",) + "libdevice.10.bc" -> ("libdevice.10.bc",) + """ + # Files with extensions (e.g., .bc bitcode files) are the same on all platforms + if "." in artifact_name: + return (artifact_name,) + + # Platform-specific library naming conventions + if IS_WINDOWS: + return (f"{artifact_name}.lib",) + else: + return (f"lib{artifact_name}.a",) + + +@functools.cache +def find_nvidia_static_lib(artifact_name: str) -> Optional[str]: + """Locate a CUDA static library or artifact file. + + This function searches for CUDA static libraries and artifacts (like + bitcode files) across multiple installation sources. + + Args: + artifact_name: Canonical artifact name (e.g., "libdevice.10.bc", "cudadevrt"). + Platform-specific filenames are resolved automatically: + + - "cudadevrt" -> "libcudadevrt.a" on Linux, "cudadevrt.lib" on Windows + - "libdevice.10.bc" -> "libdevice.10.bc" (same on all platforms) + + Returns: + Absolute path to the artifact, or ``None`` if not found. + + Raises: + ValueError: If ``artifact_name`` is not supported. + + Search order: + 1. **NVIDIA Python wheels (site-packages)** + + - Scan installed distributions for libraries + shipped in NVIDIA wheels (e.g., ``cuda-cudart``). + + 2. **Conda environments** + + - Check ``$CONDA_PREFIX`` installation: + + - ``lib`` (Linux/Mac) or ``Library/lib`` (Windows) + - ``nvvm/libdevice`` (for bitcode files) + + 3. **CUDA Toolkit environment variables** + + - Use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order): + + - ``lib64``, ``lib`` subdirectories + - ``nvvm/libdevice`` (for bitcode files) + - ``targets/*/lib64``, ``targets/*/lib`` (Linux cross-compilation) + + Examples: + Basic usage: + + >>> from cuda.pathfinder import find_nvidia_static_lib + >>> path = find_nvidia_static_lib("cudadevrt") + >>> if path: + ... print(f"Found cudadevrt at {path}") + + Finding bitcode files: + + >>> libdevice = find_nvidia_static_lib("libdevice.10.bc") + + Note: + Results are cached via ``functools.cache`` for performance. + """ + if artifact_name not in SUPPORTED_STATIC_LIBS: + raise ValueError(f"Unknown artifact: {artifact_name!r}") + + # Get platform-appropriate filename variants + variants = _get_lib_filename_variants(artifact_name) + + # 1. Search site-packages (NVIDIA Python wheels) + if site_dirs := SITE_PACKAGES_STATIC_LIBDIRS.get(artifact_name): + for rel_path in site_dirs: + for abs_dir in find_sub_dirs_all_sitepackages(tuple(rel_path.split("/"))): + for variant in variants: + path = os.path.join(abs_dir, variant) + if os.path.isfile(path): + return os.path.abspath(path) + + # 2. Search Conda environment + if conda_prefix := os.environ.get("CONDA_PREFIX"): + if IS_WINDOWS: + subdirs = ("Library/lib", "Library/lib/x64", "Library/nvvm/libdevice") + else: + subdirs = ("lib", "nvvm/libdevice") + + for subdir in subdirs: + for variant in variants: + path = os.path.join(conda_prefix, subdir, variant) + if os.path.isfile(path): + return os.path.abspath(path) + + # 3. Search CUDA_HOME/CUDA_PATH + if cuda_home := get_cuda_home_or_path(): + if IS_WINDOWS: + subdirs = ["lib/x64", "lib", "nvvm/libdevice"] + else: + subdirs = ["lib64", "lib", "nvvm/libdevice"] + + # On Linux, also search cross-compilation targets + for lib_subdir in ("lib64", "lib"): + pattern = os.path.join(cuda_home, "targets", "*", lib_subdir) + for target_dir in sorted(glob.glob(pattern), reverse=True): + # Make relative to cuda_home + rel_path = os.path.relpath(target_dir, cuda_home) + subdirs.append(rel_path) + + for subdir in subdirs: + for variant in variants: + path = os.path.join(cuda_home, subdir, variant) + if os.path.isfile(path): + return os.path.abspath(path) + + return None diff --git a/cuda_pathfinder/cuda/pathfinder/_static_libs/supported_nvidia_static_libs.py b/cuda_pathfinder/cuda/pathfinder/_static_libs/supported_nvidia_static_libs.py new file mode 100644 index 0000000000..f0b7a62bfb --- /dev/null +++ b/cuda_pathfinder/cuda/pathfinder/_static_libs/supported_nvidia_static_libs.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# THIS FILE NEEDS TO BE REVIEWED/UPDATED FOR EACH CTK RELEASE +# Likely candidates for updates are: +# SUPPORTED_STATIC_LIBS +# SITE_PACKAGES_STATIC_LIBDIRS + +# Supported CUDA static libraries and artifacts (canonical names) +SUPPORTED_STATIC_LIBS_COMMON = ( + "libdevice.10.bc", # Bitcode file (same name on all platforms) + "cudadevrt", # Static device runtime library (libcudadevrt.a on Linux, cudadevrt.lib on Windows) +) + +SUPPORTED_STATIC_LIBS = SUPPORTED_STATIC_LIBS_COMMON + +# Map from canonical artifact name to relative paths under site-packages +SITE_PACKAGES_STATIC_LIBDIRS = { + "libdevice.10.bc": ["nvidia/cuda_nvvm/nvvm/libdevice"], + "cudadevrt": [ + "nvidia/cuda_cudart/lib", # Linux + "nvidia/cuda_cudart/lib/x64", # Windows (if present) + ], +} diff --git a/cuda_pathfinder/tests/test_find_nvidia_binaries.py b/cuda_pathfinder/tests/test_find_nvidia_binaries.py new file mode 100644 index 0000000000..3a586131f8 --- /dev/null +++ b/cuda_pathfinder/tests/test_find_nvidia_binaries.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import os + +import pytest + +from cuda.pathfinder import find_nvidia_binary +from cuda.pathfinder._binaries.supported_nvidia_binaries import SUPPORTED_BINARIES + + +def test_unknown_binary(): + with pytest.raises(ValueError, match=r"Unknown binary: 'unknown-binary'"): + find_nvidia_binary("unknown-binary") + + +@pytest.mark.parametrize("binary_name", SUPPORTED_BINARIES) +def test_find_binaries(info_summary_append, binary_name): + binary_path = find_nvidia_binary(binary_name) + info_summary_append(f"{binary_path=!r}") + if binary_path: + assert os.path.isfile(binary_path) + # Verify the binary name is in the path + assert binary_name in os.path.basename(binary_path) diff --git a/cuda_pathfinder/tests/test_find_nvidia_static_libs.py b/cuda_pathfinder/tests/test_find_nvidia_static_libs.py new file mode 100644 index 0000000000..101aebec8d --- /dev/null +++ b/cuda_pathfinder/tests/test_find_nvidia_static_libs.py @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import os + +import pytest + +from cuda.pathfinder import find_nvidia_static_lib +from cuda.pathfinder._static_libs.supported_nvidia_static_libs import SUPPORTED_STATIC_LIBS + + +def test_unknown_artifact(): + with pytest.raises(ValueError, match=r"Unknown artifact: 'unknown-artifact'"): + find_nvidia_static_lib("unknown-artifact") + + +@pytest.mark.parametrize("artifact_name", SUPPORTED_STATIC_LIBS) +def test_find_static_libs(info_summary_append, artifact_name): + artifact_path = find_nvidia_static_lib(artifact_name) + info_summary_append(f"{artifact_path=!r}") + if artifact_path: + assert os.path.isfile(artifact_path) + # Verify the artifact name (or its base) is in the path + base_name = artifact_name.replace(".10", "") # Handle libdevice.10.bc -> libdevice + assert base_name.split(".")[0] in artifact_path.lower() + + +def test_libdevice_specific(info_summary_append): + """Specific test for libdevice.10.bc to ensure it's working.""" + artifact_path = find_nvidia_static_lib("libdevice.10.bc") + info_summary_append(f"libdevice.10.bc path: {artifact_path!r}") + if artifact_path: + assert os.path.isfile(artifact_path) + assert "libdevice" in artifact_path + # Should end with .bc + assert artifact_path.endswith(".bc") + + +def test_libcudadevrt_specific(info_summary_append): + """Specific test for cudadevrt to ensure it's working.""" + artifact_path = find_nvidia_static_lib("cudadevrt") + info_summary_append(f"cudadevrt path: {artifact_path!r}") + if artifact_path: + assert os.path.isfile(artifact_path) + # On Linux it should be .a, on Windows it might be .lib + assert artifact_path.endswith((".a", ".lib")) + assert "cudadevrt" in artifact_path.lower() + + +def test_caching(): + """Test that the find functions are properly cached.""" + # Call twice and ensure we get the same object (due to functools.cache) + path1 = find_nvidia_static_lib("libdevice.10.bc") + path2 = find_nvidia_static_lib("libdevice.10.bc") + assert path1 is path2 # Should be the exact same object due to caching diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index cff5b74290..bcfe4aaaf1 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -15,9 +15,6 @@ from cuda.pathfinder._dynamic_libs import supported_nvidia_libs from cuda.pathfinder._utils.platform_aware import IS_WINDOWS, quote_for_shell -STRICTNESS = os.environ.get("CUDA_PATHFINDER_TEST_LOAD_NVIDIA_DYNAMIC_LIB_STRICTNESS", "see_what_works") -assert STRICTNESS in ("see_what_works", "all_must_work") - def test_supported_libnames_linux_sonames_consistency(): assert tuple(sorted(supported_nvidia_libs.SUPPORTED_LIBNAMES_LINUX)) == tuple( @@ -104,8 +101,6 @@ def raise_child_process_failed(): raise_child_process_failed() assert not result.stderr if result.stdout.startswith("CHILD_LOAD_NVIDIA_DYNAMIC_LIB_HELPER_DYNAMIC_LIB_NOT_FOUND_ERROR:"): - if STRICTNESS == "all_must_work" and not _is_expected_load_nvidia_dynamic_lib_failure(libname): - raise_child_process_failed() info_summary_append(f"Not found: {libname=!r}") else: abs_path = json.loads(result.stdout.rstrip())