Skip to content

Commit 1112fce

Browse files
committed
Add & use _find_dll_using_nvidia_bin_dirs(), _find_dll_using_cudalib_dir()
1 parent 389cd5a commit 1112fce

5 files changed

Lines changed: 183 additions & 81 deletions

File tree

cuda_bindings/cuda/bindings/_path_finder_utils/find_nvidia_dynamic_library.py

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,52 @@
55
import functools
66
import glob
77
import os
8-
import sys
9-
import traceback
108

119
from .cuda_paths import IS_WIN32, get_cuda_paths
12-
from .find_nvidia_lib_dirs import find_nvidia_lib_dirs
10+
from .sys_path_find_sub_dirs import sys_path_find_sub_dirs
1311

1412

15-
def _find_using_nvidia_lib_dirs(so_basename, error_messages, attachments):
16-
so_wild = so_basename + "*"
17-
for lib_dir in find_nvidia_lib_dirs():
13+
def _no_such_file_in_sub_dirs(sub_dirs, file_wild, error_messages, attachments):
14+
error_messages.append(f"No such file: {file_wild}")
15+
for sub_dir in sys_path_find_sub_dirs(sub_dirs):
16+
attachments.append(f' listdir("{sub_dir}"):')
17+
for node in sorted(os.listdir(sub_dir)):
18+
attachments.append(f" {node}")
19+
20+
21+
def _find_so_using_nvidia_lib_dirs(libbasename, so_basename, error_messages, attachments):
22+
if libbasename == "nvvm": # noqa: SIM108
23+
nvidia_sub_dirs = ("nvidia", "*", "nvvm", "lib64")
24+
else:
25+
nvidia_sub_dirs = ("nvidia", "*", "lib")
26+
file_wild = so_basename + "*"
27+
for lib_dir in sys_path_find_sub_dirs(nvidia_sub_dirs):
1828
# First look for an exact match
1929
so_name = os.path.join(lib_dir, so_basename)
2030
if os.path.isfile(so_name):
2131
return so_name
2232
# Look for a versioned library
2333
# Using sort here mainly to make the result deterministic.
24-
for node in sorted(glob.glob(os.path.join(lib_dir, so_wild))):
34+
for node in sorted(glob.glob(os.path.join(lib_dir, file_wild))):
2535
so_name = os.path.join(lib_dir, node)
2636
if os.path.isfile(so_name):
2737
return so_name
28-
error_messages.append(f"No such file: {so_wild}")
29-
for lib_dir in find_nvidia_lib_dirs():
30-
attachments.append(f" listdir({repr(lib_dir)}):")
31-
for node in sorted(os.listdir(lib_dir)):
32-
attachments.append(f" {node}")
38+
_no_such_file_in_sub_dirs(nvidia_sub_dirs, file_wild, error_messages, attachments)
39+
return None
40+
41+
42+
def _find_dll_using_nvidia_bin_dirs(libbasename, error_messages, attachments):
43+
if libbasename == "nvvm": # noqa: SIM108
44+
nvidia_sub_dirs = ("nvidia", "*", "nvvm", "bin")
45+
else:
46+
nvidia_sub_dirs = ("nvidia", "*", "bin")
47+
file_wild = libbasename + "*.dll"
48+
for bin_dir in sys_path_find_sub_dirs(nvidia_sub_dirs):
49+
for node in sorted(glob.glob(os.path.join(bin_dir, file_wild))):
50+
dll_name = os.path.join(bin_dir, node)
51+
if os.path.isfile(dll_name):
52+
return dll_name
53+
_no_such_file_in_sub_dirs(nvidia_sub_dirs, file_wild, error_messages, attachments)
3354
return None
3455

3556

@@ -44,11 +65,11 @@ def _get_cuda_paths_info(key, error_messages):
4465
return env_path_tuple.info
4566

4667

47-
def _find_using_cudalib_dir(so_basename, error_messages, attachments):
48-
lib_dir = _get_cuda_paths_info("cudalib_dir", error_messages)
49-
if lib_dir is None:
68+
def _find_so_using_cudalib_dir(so_basename, error_messages, attachments):
69+
cudalib_dir = _get_cuda_paths_info("cudalib_dir", error_messages)
70+
if cudalib_dir is None:
5071
return None
51-
primary_so_dir = lib_dir + "/"
72+
primary_so_dir = cudalib_dir + "/"
5273
candidate_so_dirs = [primary_so_dir]
5374
libs = ["/lib/", "/lib64/"]
5475
for _ in range(2):
@@ -63,7 +84,7 @@ def _find_using_cudalib_dir(so_basename, error_messages, attachments):
6384
return so_name
6485
error_messages.append(f"No such file: {so_name}")
6586
for so_dirname in candidate_so_dirs:
66-
attachments.append(f" listdir({repr(so_dirname)}):")
87+
attachments.append(f' listdir("{so_dirname}"):')
6788
if not os.path.isdir(so_dirname):
6889
attachments.append(" DIRECTORY DOES NOT EXIST")
6990
else:
@@ -72,47 +93,46 @@ def _find_using_cudalib_dir(so_basename, error_messages, attachments):
7293
return None
7394

7495

75-
def _inspect_environment(libbasename, handle):
76-
if IS_WIN32:
77-
import win32api
78-
79-
dll_path = win32api.GetModuleFileName(handle)
80-
print(f"LOOOK {libbasename=} Loaded DLL path:", dll_path)
81-
error_messages = []
82-
lib_dir = _get_cuda_paths_info("cudalib_dir", error_messages)
83-
if lib_dir is None:
84-
print(f"LOOOK {libbasename=} {error_messages=}")
85-
elif not os.path.isdir(lib_dir):
86-
print(f"LOOOK {libbasename=} not isdir({lib_dir=})")
87-
else:
88-
print(f"LOOOK {libbasename=} cudalib_dir {lib_dir=}")
89-
for node in sorted(os.listdir(lib_dir)):
90-
print(f"LOOOK {node}")
91-
for lib_dir in find_nvidia_lib_dirs():
92-
print(f"LOOOK {libbasename=} NVIDIA {lib_dir=}")
93-
for node in sorted(os.listdir(lib_dir)):
94-
print(f"LOOOK {node}")
96+
def _find_dll_using_cudalib_dir(libbasename, error_messages, attachments):
97+
cudalib_dir = _get_cuda_paths_info("cudalib_dir", error_messages)
98+
if cudalib_dir is None:
99+
return None
100+
file_wild = libbasename + "*.dll"
101+
for node in sorted(glob.glob(os.path.join(cudalib_dir, file_wild))):
102+
dll_name = os.path.join(cudalib_dir, node)
103+
if os.path.isfile(dll_name):
104+
return dll_name
105+
error_messages.append(f"No such file: {file_wild}")
106+
attachments.append(f' listdir("{cudalib_dir}"):')
107+
for node in sorted(os.listdir(cudalib_dir)):
108+
attachments.append(f" {node}")
109+
return None
95110

96111

97112
@functools.cache
98-
def find_nvidia_dynamic_library(libbasename, handle=None):
99-
if handle is not None:
100-
try:
101-
_inspect_environment(libbasename, handle)
102-
except Exception as e:
103-
print("LOOOK EXCEPTION:")
104-
traceback.print_exception(type(e), e, e.__traceback__, file=sys.stdout)
105-
if IS_WIN32:
106-
return
107-
so_basename = f"lib{libbasename}.so"
113+
def find_nvidia_dynamic_library(libbasename):
108114
error_messages = []
109115
attachments = []
110-
so_name = _find_using_nvidia_lib_dirs(so_basename, error_messages, attachments)
116+
117+
if IS_WIN32:
118+
dll_name = _find_dll_using_nvidia_bin_dirs(libbasename, error_messages, attachments)
119+
if dll_name is None:
120+
if libbasename == "nvvm":
121+
dll_name = _get_cuda_paths_info("nvvm", error_messages)
122+
else:
123+
dll_name = _find_dll_using_cudalib_dir(libbasename, error_messages, attachments)
124+
if dll_name is None:
125+
attachments = "\n".join(attachments)
126+
raise RuntimeError(f"Failure finding {libbasename}*.dll: {', '.join(error_messages)}\n{attachments}")
127+
return dll_name
128+
129+
so_basename = f"lib{libbasename}.so"
130+
so_name = _find_so_using_nvidia_lib_dirs(libbasename, so_basename, error_messages, attachments)
111131
if so_name is None:
112132
if libbasename == "nvvm":
113133
so_name = _get_cuda_paths_info("nvvm", error_messages)
114134
else:
115-
so_name = _find_using_cudalib_dir(so_basename, error_messages, attachments)
135+
so_name = _find_so_using_cudalib_dir(so_basename, error_messages, attachments)
116136
if so_name is None:
117137
attachments = "\n".join(attachments)
118138
raise RuntimeError(f"Failure finding {so_basename}: {', '.join(error_messages)}\n{attachments}")

cuda_bindings/cuda/bindings/_path_finder_utils/find_nvidia_lib_dirs.py

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2024-2025 NVIDIA Corporation. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
4+
5+
import functools
6+
import os
7+
import sys
8+
9+
10+
@functools.cache
11+
def _impl(sys_path, sub_dirs):
12+
results = []
13+
for base in sys_path:
14+
stack = [(base, 0)] # (current_path, index into sub_dirs)
15+
while stack:
16+
current_path, idx = stack.pop()
17+
if idx == len(sub_dirs):
18+
if os.path.isdir(current_path):
19+
results.append(current_path)
20+
continue
21+
22+
sub = sub_dirs[idx]
23+
if sub == "*":
24+
try:
25+
entries = sorted(os.listdir(current_path))
26+
except OSError:
27+
continue
28+
for entry in entries:
29+
entry_path = os.path.join(current_path, entry)
30+
if os.path.isdir(entry_path):
31+
stack.append((entry_path, idx + 1))
32+
else:
33+
next_path = os.path.join(current_path, sub)
34+
if os.path.isdir(next_path):
35+
stack.append((next_path, idx + 1))
36+
return results
37+
38+
39+
def sys_path_find_sub_dirs(sub_dirs):
40+
return _impl(tuple(sys.path), tuple(sub_dirs))

cuda_bindings/tests/path_finder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
for k, v in paths.items():
66
print(f"{k}: {v}", flush=True)
77

8-
print(path_finder.find_nvidia_dynamic_library("nvvm", "TEST"))
9-
print(path_finder.find_nvidia_dynamic_library("nvJitLink", "TEST"))
8+
print(path_finder.find_nvidia_dynamic_library("nvvm"))
9+
print(path_finder.find_nvidia_dynamic_library("nvJitLink"))
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import os
2+
3+
import pytest
4+
5+
from cuda.bindings._path_finder_utils.sys_path_find_sub_dirs import _impl
6+
7+
8+
@pytest.fixture
9+
def test_tree(tmp_path):
10+
# Build:
11+
# tmp_path/
12+
# sys1/nvidia/foo/lib
13+
# sys1/nvidia/bar/lib
14+
# sys2/nvidia/baz/nvvm/lib64
15+
base = tmp_path
16+
(base / "sys1" / "nvidia" / "foo" / "lib").mkdir(parents=True)
17+
(base / "sys1" / "nvidia" / "bar" / "lib").mkdir(parents=True)
18+
(base / "sys2" / "nvidia" / "baz" / "nvvm" / "lib64").mkdir(parents=True)
19+
20+
return {
21+
"sys_path": (
22+
str(base / "sys1"),
23+
str(base / "sys2"),
24+
str(base / "nonexistent"), # should be ignored
25+
),
26+
"base": base,
27+
}
28+
29+
30+
def test_exact_match(test_tree):
31+
sys_path = test_tree["sys_path"]
32+
base = test_tree["base"]
33+
result = _impl(sys_path, ("nvidia", "foo", "lib"))
34+
expected = [str(base / "sys1" / "nvidia" / "foo" / "lib")]
35+
assert result == expected
36+
37+
38+
def test_single_wildcard(test_tree):
39+
sys_path = test_tree["sys_path"]
40+
base = test_tree["base"]
41+
result = _impl(sys_path, ("nvidia", "*", "lib"))
42+
expected = [
43+
str(base / "sys1" / "nvidia" / "bar" / "lib"),
44+
str(base / "sys1" / "nvidia" / "foo" / "lib"),
45+
]
46+
assert sorted(result) == sorted(expected)
47+
48+
49+
def test_double_wildcard(test_tree):
50+
sys_path = test_tree["sys_path"]
51+
base = test_tree["base"]
52+
result = _impl(sys_path, ("nvidia", "*", "nvvm", "lib64"))
53+
expected = [str(base / "sys2" / "nvidia" / "baz" / "nvvm" / "lib64")]
54+
assert result == expected
55+
56+
57+
def test_no_match(test_tree):
58+
sys_path = test_tree["sys_path"]
59+
result = _impl(sys_path, ("nvidia", "nonexistent", "lib"))
60+
assert result == []
61+
62+
63+
def test_empty_sys_path():
64+
result = _impl((), ("nvidia", "*", "lib"))
65+
assert result == []
66+
67+
68+
def test_empty_sub_dirs(test_tree):
69+
sys_path = test_tree["sys_path"]
70+
result = _impl(sys_path, ())
71+
expected = [p for p in sys_path if os.path.isdir(p)]
72+
assert sorted(result) == sorted(expected)

0 commit comments

Comments
 (0)