| |
| |
|
|
| from __future__ import annotations |
|
|
| import functools |
| import glob |
| import os |
| from dataclasses import dataclass |
|
|
| from cuda.pathfinder._headers import supported_nvidia_headers |
| 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 |
|
|
|
|
| @dataclass |
| class LocatedHeaderDir: |
| abs_path: str | None |
| found_via: str |
|
|
| def __post_init__(self) -> None: |
| self.abs_path = _abs_norm(self.abs_path) |
|
|
|
|
| def _abs_norm(path: str | None) -> str | None: |
| if path: |
| return os.path.normpath(os.path.abspath(path)) |
| return None |
|
|
|
|
| def _joined_isfile(dirpath: str, basename: str) -> bool: |
| return os.path.isfile(os.path.join(dirpath, basename)) |
|
|
|
|
| def _locate_under_site_packages(sub_dir: str, h_basename: str) -> LocatedHeaderDir | None: |
| |
| hdr_dir: str |
| for hdr_dir in find_sub_dirs_all_sitepackages(tuple(sub_dir.split("/"))): |
| if _joined_isfile(hdr_dir, h_basename): |
| return LocatedHeaderDir(abs_path=hdr_dir, found_via="site-packages") |
| return None |
|
|
|
|
| def _locate_based_on_ctk_layout(libname: str, h_basename: str, anchor_point: str) -> str | None: |
| parts = [anchor_point] |
| if libname == "nvvm": |
| parts.append(libname) |
| parts.append("include") |
| idir = os.path.join(*parts) |
| if libname == "cccl": |
| if IS_WINDOWS: |
| cdir_ctk12 = os.path.join(idir, "targets", "x64") |
| cdir_ctk13 = os.path.join(cdir_ctk12, "cccl") |
| if _joined_isfile(cdir_ctk13, h_basename): |
| return cdir_ctk13 |
| if _joined_isfile(cdir_ctk12, h_basename): |
| return cdir_ctk12 |
| cdir = os.path.join(idir, "cccl") |
| if _joined_isfile(cdir, h_basename): |
| return cdir |
| if _joined_isfile(idir, h_basename): |
| return idir |
| return None |
|
|
|
|
| def _find_based_on_conda_layout(libname: str, h_basename: str, ctk_layout: bool) -> LocatedHeaderDir | None: |
| conda_prefix = os.environ.get("CONDA_PREFIX") |
| if not conda_prefix: |
| return None |
| if IS_WINDOWS: |
| anchor_point = os.path.join(conda_prefix, "Library") |
| if not os.path.isdir(anchor_point): |
| return None |
| else: |
| if ctk_layout: |
| targets_include_path = glob.glob(os.path.join(conda_prefix, "targets", "*", "include")) |
| if not targets_include_path: |
| return None |
| if len(targets_include_path) != 1: |
| |
| |
| return None |
| include_path = targets_include_path[0] |
| else: |
| include_path = os.path.join(conda_prefix, "include") |
| anchor_point = os.path.dirname(include_path) |
| found_header_path = _locate_based_on_ctk_layout(libname, h_basename, anchor_point) |
| if found_header_path: |
| return LocatedHeaderDir(abs_path=found_header_path, found_via="conda") |
| return None |
|
|
|
|
| def _find_ctk_header_directory(libname: str) -> LocatedHeaderDir | None: |
| h_basename = supported_nvidia_headers.SUPPORTED_HEADERS_CTK[libname] |
| candidate_dirs = supported_nvidia_headers.SUPPORTED_SITE_PACKAGE_HEADER_DIRS_CTK[libname] |
|
|
| for cdir in candidate_dirs: |
| if hdr_dir := _locate_under_site_packages(cdir, h_basename): |
| return hdr_dir |
|
|
| if hdr_dir := _find_based_on_conda_layout(libname, h_basename, True): |
| return hdr_dir |
|
|
| cuda_home = get_cuda_home_or_path() |
| if cuda_home: |
| if result := _locate_based_on_ctk_layout(libname, h_basename, cuda_home): |
| return LocatedHeaderDir(abs_path=result, found_via="CUDA_HOME") |
|
|
| return None |
|
|
|
|
| @functools.cache |
| def locate_nvidia_header_directory(libname: str) -> LocatedHeaderDir | None: |
| """Locate the header directory for a supported NVIDIA library. |
| |
| Args: |
| libname (str): The short name of the library whose headers are needed |
| (e.g., ``"nvrtc"``, ``"cusolver"``, ``"nvshmem"``). |
| |
| Returns: |
| LocatedHeaderDir or None: A LocatedHeaderDir object containing the absolute path |
| to the discovered header directory and information about where it was found, |
| or ``None`` if the headers cannot be found. |
| |
| Raises: |
| RuntimeError: If ``libname`` is not in the supported set. |
| |
| Search order: |
| 1. **NVIDIA Python wheels** |
| |
| - Scan installed distributions (``site-packages``) for header layouts |
| shipped in NVIDIA wheels (e.g., ``cuda-toolkit[nvrtc]``). |
| |
| 2. **Conda environments** |
| |
| - Check Conda-style installation prefixes, which use platform-specific |
| include directory layouts. |
| |
| 3. **CUDA Toolkit environment variables** |
| |
| - Use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order). |
| """ |
|
|
| if libname in supported_nvidia_headers.SUPPORTED_HEADERS_CTK: |
| return _find_ctk_header_directory(libname) |
|
|
| h_basename = supported_nvidia_headers.SUPPORTED_HEADERS_NON_CTK.get(libname) |
| if h_basename is None: |
| raise RuntimeError(f"UNKNOWN {libname=}") |
|
|
| candidate_dirs = supported_nvidia_headers.SUPPORTED_SITE_PACKAGE_HEADER_DIRS_NON_CTK.get(libname, []) |
|
|
| for cdir in candidate_dirs: |
| if found_hdr := _locate_under_site_packages(cdir, h_basename): |
| return found_hdr |
|
|
| if found_hdr := _find_based_on_conda_layout(libname, h_basename, False): |
| return found_hdr |
|
|
| |
| candidate_dirs = supported_nvidia_headers.SUPPORTED_INSTALL_DIRS_NON_CTK.get(libname, []) |
| for cdir in candidate_dirs: |
| for hdr_dir in sorted(glob.glob(cdir), reverse=True): |
| if _joined_isfile(hdr_dir, h_basename): |
| |
| return LocatedHeaderDir(abs_path=hdr_dir, found_via="supported_install_dir") |
| return None |
|
|
|
|
| def find_nvidia_header_directory(libname: str) -> str | None: |
| """Locate the header directory for a supported NVIDIA library. |
| |
| Args: |
| libname (str): The short name of the library whose headers are needed |
| (e.g., ``"nvrtc"``, ``"cusolver"``, ``"nvshmem"``). |
| |
| Returns: |
| str or None: Absolute path to the discovered header directory, or ``None`` |
| if the headers cannot be found. |
| |
| Raises: |
| RuntimeError: If ``libname`` is not in the supported set. |
| |
| Search order: |
| 1. **NVIDIA Python wheels** |
| |
| - Scan installed distributions (``site-packages``) for header layouts |
| shipped in NVIDIA wheels (e.g., ``cuda-toolkit[nvrtc]``). |
| |
| 2. **Conda environments** |
| |
| - Check Conda-style installation prefixes, which use platform-specific |
| include directory layouts. |
| |
| 3. **CUDA Toolkit environment variables** |
| |
| - Use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order). |
| """ |
| found = locate_nvidia_header_directory(libname) |
| return found.abs_path if found else None |
|
|