Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str, linux_lib_
for rel_path in rel_paths:
for dirname in sorted(glob.glob(os.path.join(anchor_point, rel_path))):
if os.path.isdir(dirname):
return dirname
return os.path.normpath(dirname)

return None

Expand Down Expand Up @@ -152,6 +152,57 @@ def _find_dll_using_lib_dir(
return None


def _derive_ctk_root_linux(resolved_lib_path: str) -> str | None:
"""Derive the CTK installation root from a resolved library path on Linux.

Standard system CTK layout: ``$CTK_ROOT/lib64/libfoo.so.XX``
(some installs use ``lib`` instead of ``lib64``).

Returns None if the path doesn't match a recognized layout.
"""
lib_dir = os.path.dirname(resolved_lib_path)
basename = os.path.basename(lib_dir)
if basename in ("lib64", "lib"):
return os.path.dirname(lib_dir)
return None


def _derive_ctk_root_windows(resolved_lib_path: str) -> str | None:
"""Derive the CTK installation root from a resolved library path on Windows.

Handles two CTK layouts:
- CTK 13: ``$CTK_ROOT/bin/x64/foo.dll``
- CTK 12: ``$CTK_ROOT/bin/foo.dll``

Returns None if the path doesn't match a recognized layout.

Uses ``ntpath`` explicitly so the function is testable on any platform.
"""
import ntpath

lib_dir = ntpath.dirname(resolved_lib_path)
basename = ntpath.basename(lib_dir).lower()
if basename == "x64":
parent = ntpath.dirname(lib_dir)
if ntpath.basename(parent).lower() == "bin":
return ntpath.dirname(parent)
elif basename == "bin":
return ntpath.dirname(lib_dir)
return None


def derive_ctk_root(resolved_lib_path: str) -> str | None:
"""Derive the CTK installation root from a resolved library path.

Given the absolute path of a loaded CTK shared library, walk up the
directory tree to find the CTK root. Returns None if the path doesn't
match any recognized CTK directory layout.
"""
if IS_WINDOWS:
return _derive_ctk_root_windows(resolved_lib_path)
return _derive_ctk_root_linux(resolved_lib_path)


class _FindNvidiaDynamicLib:
def __init__(self, libname: str):
self.libname = libname
Expand Down Expand Up @@ -185,6 +236,16 @@ def try_with_conda_prefix(self) -> str | None:
def try_with_cuda_home(self) -> str | None:
return self._find_using_lib_dir(_find_lib_dir_using_cuda_home(self.libname))

def try_via_ctk_root(self, ctk_root: str) -> str | None:
"""Find the library under a derived CTK root directory.

Uses :func:`_find_lib_dir_using_anchor_point` which already knows
about non-standard sub-paths (e.g. ``nvvm/lib64`` for nvvm).
"""
return self._find_using_lib_dir(
_find_lib_dir_using_anchor_point(self.libname, anchor_point=ctk_root, linux_lib_dir="lib64")
)

def _find_using_lib_dir(self, lib_dir: str | None) -> str | None:
if lib_dir is None:
return None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import struct
import sys

from cuda.pathfinder._dynamic_libs.find_nvidia_dynamic_lib import _FindNvidiaDynamicLib
from cuda.pathfinder._dynamic_libs.find_nvidia_dynamic_lib import (
_FindNvidiaDynamicLib,
derive_ctk_root,
)
from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL, load_dependencies
from cuda.pathfinder._utils.platform_aware import IS_WINDOWS

Expand All @@ -22,6 +25,36 @@
load_with_system_search,
)

# Libs that reside on the standard linker path in system CTK installs.
# Used to discover the CTK root when a lib with a non-standard path
# (e.g. nvvm under $CTK_ROOT/nvvm/lib64) can't be found directly.
_CTK_ROOT_CANARY_LIBNAMES = ("cudart",)


def _try_ctk_root_canary(finder: _FindNvidiaDynamicLib) -> str | None:
"""Derive the CTK root from a system-installed canary lib.

For libs like nvvm whose shared object doesn't reside on the standard
linker path, we locate a well-known CTK lib that IS on the linker path
via system search, derive the CTK installation root from its resolved
path, and then look for the target lib relative to that root.

The canary lib is loaded as a side-effect but this is harmless: it stays
loaded (handles are never closed) and will be reused by
:func:`load_nvidia_dynamic_lib` if requested later.
"""
for canary_libname in _CTK_ROOT_CANARY_LIBNAMES:
canary = load_with_system_search(canary_libname)
if canary is None or canary.abs_path is None:
continue
ctk_root = derive_ctk_root(canary.abs_path)
if ctk_root is None:
continue
abs_path: str | None = finder.try_via_ctk_root(ctk_root)
if abs_path is not None:
return abs_path
return None


def _load_lib_no_cache(libname: str) -> LoadedDL:
finder = _FindNvidiaDynamicLib(libname)
Expand Down Expand Up @@ -50,11 +83,21 @@ def _load_lib_no_cache(libname: str) -> LoadedDL:
loaded = load_with_system_search(libname)
if loaded is not None:
return loaded

abs_path = finder.try_with_cuda_home()
if abs_path is None:
finder.raise_not_found_error()
else:
if abs_path is not None:
found_via = "CUDA_HOME"
else:
# Canary probe: if the direct system search and CUDA_HOME both
# failed (e.g. nvvm isn't on the linker path and CUDA_HOME is
# unset), try to discover the CTK root by system-loading a
# well-known CTK lib that IS on the linker path, then look for
# the target lib relative to that root.
abs_path = _try_ctk_root_canary(finder)
if abs_path is not None:
found_via = "system-ctk-root"
else:
finder.raise_not_found_error()

return load_with_abs_path(libname, abs_path, found_via)

Expand Down Expand Up @@ -123,6 +166,14 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL:

- If set, use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order).

5. **CTK root canary probe**

- For libraries whose shared object doesn't reside on the standard
linker path (e.g. ``libnvvm.so`` lives under ``$CTK_ROOT/nvvm/lib64``),
attempt to discover the CTK installation root by system-loading a
well-known CTK library (``cudart``) that *is* on the linker path, then
derive the root from its resolved absolute path.

Notes:
The search is performed **per library**. There is currently no mechanism to
guarantee that multiple libraries are all resolved from the same location.
Expand Down
Loading
Loading