@@ -146,25 +146,42 @@ def raise_child_process_failed():
146146def test_load_nvrtc_without_cuda_home_or_cuda_path (info_summary_append ):
147147 """Regression test for issue #1781: nvrtc must load without CUDA_HOME/CUDA_PATH.
148148
149- On Windows, when CUDA DLLs are discovered via PATH (system search), the
150- previous LoadLibraryExW(flags=0) call would find the DLL but fail to
151- resolve co-located dependencies like nvrtc-builtins (error 126).
152-
153- The fix uses SearchPathW to resolve the full path, then loads with
154- LOAD_WITH_ALTERED_SEARCH_PATH so dependency search starts from the
155- DLL's directory.
156-
157- This test strips CUDA_HOME and CUDA_PATH, then loads nvrtc in a fresh
158- subprocess. In CI environments where nvrtc is only available via system
159- search, this exercises the exact code path that was broken.
149+ On Windows, Python 3.8+ calls SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
150+ at startup, which excludes PATH from the LoadLibraryExW search order. The fix uses
151+ SearchPathW (unaffected by SetDefaultDllDirectories) to locate the DLL via PATH, then
152+ loads it by absolute path with LOAD_WITH_ALTERED_SEARCH_PATH.
153+
154+ This test loads nvrtc twice in fresh subprocesses: once with the normal environment,
155+ once with CUDA_HOME and CUDA_PATH stripped. If the normal load finds nvrtc in a
156+ directory on PATH, the stripped load must also succeed — otherwise the system search
157+ is broken.
160158 """
159+ timeout = 120 if IS_WINDOWS else 30
160+
161+ # Phase 1: load nvrtc with normal environment.
162+ normal_result = run_load_nvidia_dynamic_lib_in_subprocess ("nvrtc" , timeout = timeout )
163+ if normal_result .returncode != 0 :
164+ raise RuntimeError (build_child_process_failed_for_libname_message ("nvrtc" , normal_result ))
165+ assert not normal_result .stderr
166+ normal_payload = parse_dynamic_lib_subprocess_payload (
167+ normal_result .stdout ,
168+ libname = "nvrtc" ,
169+ error_label = "Load subprocess child process (normal env)" ,
170+ )
171+ if normal_payload .status == STATUS_NOT_FOUND :
172+ info_summary_append ("nvrtc not found (normal env)" )
173+ pytest .skip ("nvrtc not available in this environment" )
174+ normal_abs_path = normal_payload .abs_path
175+ assert normal_abs_path is not None
176+ assert os .path .isfile (normal_abs_path )
177+ info_summary_append (f"nvrtc (normal env): abs_path={ quote_for_shell (normal_abs_path )} " )
178+
179+ # Phase 2: load nvrtc without CUDA_HOME/CUDA_PATH.
161180 env = os .environ .copy ()
162181 env .pop ("CUDA_HOME" , None )
163182 env .pop ("CUDA_PATH" , None )
164-
165- timeout = 120 if IS_WINDOWS else 30
166183 command = build_dynamic_lib_subprocess_command (LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_MODE , "nvrtc" )
167- result = subprocess .run ( # noqa: S603
184+ stripped_result = subprocess .run ( # noqa: S603
168185 command ,
169186 capture_output = True ,
170187 text = True ,
@@ -173,22 +190,36 @@ def test_load_nvrtc_without_cuda_home_or_cuda_path(info_summary_append):
173190 env = env ,
174191 cwd = LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_CWD ,
175192 )
176-
177- if result .returncode != 0 :
178- raise RuntimeError (build_child_process_failed_for_libname_message ("nvrtc" , result ))
179- assert not result .stderr
180-
181- payload = parse_dynamic_lib_subprocess_payload (
182- result .stdout ,
193+ if stripped_result .returncode != 0 :
194+ raise RuntimeError (build_child_process_failed_for_libname_message ("nvrtc" , stripped_result ))
195+ assert not stripped_result .stderr
196+ stripped_payload = parse_dynamic_lib_subprocess_payload (
197+ stripped_result .stdout ,
183198 libname = "nvrtc" ,
184199 error_label = "Load subprocess child process (no CUDA_HOME/CUDA_PATH)" ,
185200 )
186201
187- if payload .status == STATUS_NOT_FOUND :
188- info_summary_append ("nvrtc not found without CUDA_HOME/CUDA_PATH" )
189- pytest .skip ("nvrtc not available without CUDA_HOME/CUDA_PATH" )
190-
191- abs_path = payload .abs_path
192- assert abs_path is not None
193- info_summary_append (f"nvrtc (no CUDA_HOME/CUDA_PATH): abs_path={ quote_for_shell (abs_path )} " )
194- assert os .path .isfile (abs_path )
202+ # Phase 3: evaluate.
203+ if stripped_payload .status != STATUS_NOT_FOUND :
204+ stripped_abs_path = stripped_payload .abs_path
205+ assert stripped_abs_path is not None
206+ assert os .path .isfile (stripped_abs_path )
207+ info_summary_append (f"nvrtc (no CUDA_HOME/CUDA_PATH): abs_path={ quote_for_shell (stripped_abs_path )} " )
208+ return
209+
210+ # nvrtc was found normally but not without CUDA_HOME/CUDA_PATH.
211+ # If the DLL's directory is on PATH, the system search should have found it.
212+ dll_dir = os .path .normcase (os .path .normpath (os .path .dirname (normal_abs_path )))
213+ on_path = any (
214+ os .path .normcase (os .path .normpath (d )) == dll_dir for d in os .environ .get ("PATH" , "" ).split (os .pathsep ) if d
215+ )
216+ if on_path :
217+ pytest .fail (
218+ f"nvrtc was found at { normal_abs_path !r} (directory is on PATH) "
219+ f"but could not be loaded without CUDA_HOME/CUDA_PATH. "
220+ f"System search should find DLLs in PATH directories."
221+ )
222+ info_summary_append (
223+ f"nvrtc (no CUDA_HOME/CUDA_PATH): not found "
224+ f"(normal-env directory not on PATH: { quote_for_shell (os .path .dirname (normal_abs_path ))} )"
225+ )
0 commit comments