Coverage for cuda/pathfinder/_static_libs/find_static_lib.py: 98.89%
90 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-03 02:41 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-03 02:41 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
4import functools
5import os
6from dataclasses import dataclass
7from pathlib import Path
8from typing import NoReturn, TypedDict
10from cuda.pathfinder._utils.env_vars import get_cuda_path_or_home
11from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs_all_sitepackages
12from cuda.pathfinder._utils.platform_aware import IS_WINDOWS
13from cuda.pathfinder._utils.windows_arch import windows_python_arch
16class StaticLibNotFoundError(RuntimeError):
17 """Raised when a static library cannot be found."""
20@dataclass(frozen=True)
21class LocatedStaticLib:
22 """Information about a located static library."""
24 name: str
25 abs_path: str
26 filename: str
27 found_via: str
30class _StaticLibInfo(TypedDict):
31 """Static-library metadata with ordered alternatives searched first-to-last."""
33 filename: str
34 ctk_rel_paths: tuple[str, ...]
35 conda_rel_paths: tuple[str, ...]
36 site_packages_dirs: tuple[str, ...]
39def _cudadevrt_info() -> _StaticLibInfo:
40 if not IS_WINDOWS: 1cgh
41 return {
42 "filename": "libcudadevrt.a",
43 "ctk_rel_paths": ("lib64", "lib"),
44 "conda_rel_paths": ("lib",),
45 "site_packages_dirs": ("nvidia/cu13/lib", "nvidia/cuda_runtime/lib"),
46 }
48 arch_dir = windows_python_arch() 1cgh
49 component_wheel_dirs = ("nvidia/cuda_runtime/lib/x64",) if arch_dir == "x64" else () 1cgh
50 conda_fallback_dirs = ("lib",) if arch_dir == "x64" else () 1cgh
51 return { 1cgh
52 "filename": "cudadevrt.lib",
53 "ctk_rel_paths": (str(Path("lib", arch_dir)),),
54 "conda_rel_paths": (str(Path("lib", arch_dir)), *conda_fallback_dirs),
55 "site_packages_dirs": (f"nvidia/cu13/lib/{arch_dir}", *component_wheel_dirs),
56 }
59_SUPPORTED_STATIC_LIBS_INFO: dict[str, _StaticLibInfo] = {
60 "cudadevrt": _cudadevrt_info(),
61}
63SUPPORTED_STATIC_LIBS: tuple[str, ...] = tuple(sorted(_SUPPORTED_STATIC_LIBS_INFO.keys()))
66def _no_such_file_in_dir(directory: Path, filename: str, error_messages: list[str], attachments: list[str]) -> None:
67 error_messages.append(f"No such file: {directory / filename}") 1a
68 if directory.is_dir(): 1a
69 attachments.append(f' listdir("{directory}"):') 1a
70 for node in sorted(node_path.name for node_path in directory.iterdir()): 1a
71 attachments.append(f" {node}") 1a
72 else:
73 attachments.append(f' Directory does not exist: "{directory}"')
76class _FindStaticLib:
77 def __init__(self, name: str) -> None:
78 if name not in _SUPPORTED_STATIC_LIBS_INFO: 1iadefb
79 raise ValueError(f"Unknown static library: '{name}'. Supported: {', '.join(SUPPORTED_STATIC_LIBS)}") 1i
80 self.name: str = name 1adefb
81 self.config: _StaticLibInfo = _SUPPORTED_STATIC_LIBS_INFO[name] 1adefb
82 self.filename: str = self.config["filename"] 1adefb
83 self.ctk_rel_paths: tuple[str, ...] = self.config["ctk_rel_paths"] 1adefb
84 self.conda_rel_paths: tuple[str, ...] = self.config["conda_rel_paths"] 1adefb
85 self.site_packages_dirs: tuple[str, ...] = self.config["site_packages_dirs"] 1adefb
86 self.error_messages: list[str] = [] 1adefb
87 self.attachments: list[str] = [] 1adefb
89 def try_site_packages(self) -> Path | None:
90 for rel_dir in self.site_packages_dirs: 1adefb
91 sub_dir = tuple(rel_dir.split("/")) 1adefb
92 for abs_dir in find_sub_dirs_all_sitepackages(sub_dir): 1adefb
93 file_path = Path(abs_dir, self.filename) 1b
94 if file_path.is_file(): 1b
95 return file_path 1b
96 return None 1adefb
98 def try_with_conda_prefix(self) -> Path | None:
99 conda_prefix = os.environ.get("CONDA_PREFIX") 1adefb
100 if not conda_prefix: 1adefb
101 return None 1ade
103 anchor = Path(conda_prefix, "Library") if IS_WINDOWS else Path(conda_prefix) 1fb
104 for rel_path in self.conda_rel_paths: 1fb
105 file_path = anchor / rel_path / self.filename 1fb
106 if file_path.is_file(): 1fb
107 return file_path 1fb
108 return None 1b
110 def try_with_cuda_home(self) -> Path | None:
111 cuda_home = get_cuda_path_or_home() 1adeb
112 if cuda_home is None: 1adeb
113 self.error_messages.append("CUDA_HOME/CUDA_PATH not set") 1d
114 return None 1d
116 anchor = Path(cuda_home) 1aeb
117 for rel_path in self.ctk_rel_paths: 1aeb
118 file_path = anchor / rel_path / self.filename 1aeb
119 if file_path.is_file(): 1aeb
120 return file_path 1eb
122 _no_such_file_in_dir( 1a
123 anchor / self.ctk_rel_paths[0],
124 self.filename,
125 self.error_messages,
126 self.attachments,
127 )
128 return None 1a
130 def raise_not_found_error(self) -> NoReturn:
131 err = ", ".join(self.error_messages) if self.error_messages else "No search paths available" 1ad
132 att = "\n".join(self.attachments) if self.attachments else "" 1ad
133 raise StaticLibNotFoundError(f'Failure finding "{self.filename}": {err}\n{att}') 1ad
136def locate_static_lib(name: str) -> LocatedStaticLib:
137 """Locate a static library by name.
139 Raises:
140 ValueError: If ``name`` is not a supported static library.
141 StaticLibNotFoundError: If the static library cannot be found.
142 """
143 finder = _FindStaticLib(name) 1iadefb
145 abs_path = finder.try_site_packages() 1adefb
146 if abs_path is not None: 1adefb
147 return LocatedStaticLib( 1b
148 name=name,
149 abs_path=str(abs_path),
150 filename=finder.filename,
151 found_via="site-packages",
152 )
154 abs_path = finder.try_with_conda_prefix() 1adefb
155 if abs_path is not None: 1adefb
156 return LocatedStaticLib( 1fb
157 name=name,
158 abs_path=str(abs_path),
159 filename=finder.filename,
160 found_via="conda",
161 )
163 abs_path = finder.try_with_cuda_home() 1adeb
164 if abs_path is not None: 1adeb
165 return LocatedStaticLib( 1eb
166 name=name,
167 abs_path=str(abs_path),
168 filename=finder.filename,
169 found_via="CUDA_PATH",
170 )
172 finder.raise_not_found_error() 1ad
175@functools.cache
176def find_static_lib(name: str) -> str:
177 """Find the absolute path to a static library.
179 Raises:
180 ValueError: If ``name`` is not a supported static library.
181 StaticLibNotFoundError: If the static library cannot be found.
183 Windows on ARM (WoA) Note:
184 On Windows, this API aims to return the path to a static library whose
185 architecture matches the Python interpreter architecture. For example,
186 x64 Python running on an Arm64 machine targets the x64 library, while
187 native Arm64 Python targets the Arm64 library. This differs from
188 ``find_nvidia_binary_utility``, which targets the native machine
189 architecture when selecting architecture-specific executables.
190 """
191 return locate_static_lib(name).abs_path 1ade