@@ -138,8 +138,25 @@ def _collect_cuda_lib_dirs(cuda_target: Path) -> list[str]:
138138 return lib_dirs
139139
140140
141+ def _collect_site_packages_cuda_lib_dirs () -> list [str ]:
142+ """Return all nvidia/torch lib dirs found in any site-packages directory."""
143+ lib_dirs : list [str ] = []
144+ for sp_dir in _get_site_packages_dirs ():
145+ torch_lib = sp_dir / "torch" / "lib"
146+ if torch_lib .exists ():
147+ lib_dirs .append (str (torch_lib ))
148+ nvidia_dir = sp_dir / "nvidia"
149+ if nvidia_dir .exists ():
150+ for pkg_dir in sorted (nvidia_dir .iterdir ()):
151+ if pkg_dir .is_dir ():
152+ lib_subdir = pkg_dir / "lib"
153+ if lib_subdir .exists ():
154+ lib_dirs .append (str (lib_subdir ))
155+ return lib_dirs
156+
157+
141158def _setup_linux_cuda ():
142- """Set up CUDA libraries on Linux for snap/flatpak CUDA installs .
159+ """Set up CUDA libraries on Linux.
143160
144161 LD_LIBRARY_PATH is read by the dynamic linker only at process start, so
145162 preloading individual .so files with ctypes is fragile: if *any* library
@@ -148,39 +165,36 @@ def _setup_linux_cuda():
148165 or torchaudio crash with "cannot open shared object file".
149166
150167 The reliable fix is to re-exec the current process with LD_LIBRARY_PATH
151- extended to include all cuda_packages lib dirs. After re-exec the dynamic
152- linker finds every CUDA library via the standard search path for the
153- entire lifetime of the process.
168+ extended to include all cuda lib dirs. After re-exec the dynamic linker
169+ finds every CUDA library via the standard search path for the entire
170+ lifetime of the process.
154171
155- A sentinel value in LD_LIBRARY_PATH prevents infinite re-exec loops.
172+ A sentinel env var prevents infinite re-exec loops.
156173 """
157- # Ensure cuda_packages is on sys.path so Python can import torch
174+ # Ensure cuda_packages (snap/flatpak) is on sys.path so Python can import torch
158175 _get_site_packages_dirs ()
159176
160- cuda_target = _get_cuda_target_dir ()
161- if cuda_target is None or not cuda_target .exists ():
162- logger .debug ("CUDA setup: no cuda_packages dir found, skipping" )
163- return
164-
165- torch_lib = cuda_target / "torch" / "lib"
166- if not torch_lib .exists ():
167- logger .debug ("CUDA setup: no torch/lib in cuda_packages, skipping" )
177+ if os .environ .get ("BUZZ_CUDA_SETUP_DONE" ):
178+ logger .info ("CUDA setup: already done (sentinel set), proceeding" )
168179 return
169180
170- sentinel = str (torch_lib )
171- current_ld = os .environ .get ("LD_LIBRARY_PATH" , "" )
181+ # Collect lib dirs from snap/flatpak cuda_packages OR regular site-packages
182+ cuda_target = _get_cuda_target_dir ()
183+ if cuda_target is not None and cuda_target .exists ():
184+ extra_dirs = _collect_cuda_lib_dirs (cuda_target )
185+ else :
186+ extra_dirs = _collect_site_packages_cuda_lib_dirs ()
172187
173- if sentinel in current_ld .split (":" ):
174- # Already re-exec'd (or user manually set the path) — nothing to do
175- logger .info ("CUDA setup: LD_LIBRARY_PATH already contains cuda torch/lib, proceeding" )
188+ if not extra_dirs :
189+ logger .debug ("CUDA setup: no nvidia/torch lib dirs found, skipping" )
176190 return
177191
178- # Build new LD_LIBRARY_PATH: cuda_packages lib dirs prepended to existing path
179- extra_dirs = _collect_cuda_lib_dirs (cuda_target )
192+ current_ld = os .environ .get ("LD_LIBRARY_PATH" , "" )
180193 new_ld = ":" .join (extra_dirs )
181194 if current_ld :
182195 new_ld = new_ld + ":" + current_ld
183196 os .environ ["LD_LIBRARY_PATH" ] = new_ld
197+ os .environ ["BUZZ_CUDA_SETUP_DONE" ] = "1"
184198
185199 # Re-exec the process so the dynamic linker picks up the new LD_LIBRARY_PATH.
186200 # We read the original argv from /proc/self/cmdline to preserve the exact
0 commit comments