Skip to content

Commit 30d9c60

Browse files
authored
Build cuda.core RDC test fixtures without Bash (#2335)
* Run nvcc fixture build without Bash * Document narrow nvcc skip detection * Document nvcc test environment requirements
1 parent 1af2e22 commit 30d9c60

4 files changed

Lines changed: 129 additions & 43 deletions

File tree

.github/workflows/build-wheel.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,9 @@ jobs:
463463
cuda-path: "./cuda_toolkit_prev"
464464

465465
- name: Build cuda.core test binaries
466-
run: bash ${{ env.CUDA_CORE_TEST_BINARIES_DIR }}/build_test_binaries.sh
466+
run: |
467+
nvcc --version
468+
python "${{ env.CUDA_CORE_TEST_BINARIES_DIR }}/build_test_binaries.py"
467469
468470
- name: Upload cuda.core test binaries
469471
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Build the relocatable-device-code fixtures used by cuda.core tests."""
4+
5+
from __future__ import annotations
6+
7+
import os
8+
import subprocess
9+
import tempfile
10+
from pathlib import Path
11+
12+
13+
def _run(command: list[str]) -> None:
14+
print(f"+ {subprocess.list2cmdline(command)}")
15+
result = subprocess.run(command) # noqa: S603
16+
if result.returncode != 0:
17+
raise SystemExit(result.returncode)
18+
19+
20+
def main() -> None:
21+
script_dir = Path(__file__).resolve().parent
22+
source_path = script_dir / "saxpy.cu"
23+
final_object_path = script_dir / "saxpy.o"
24+
final_library_path = script_dir / ("saxpy.lib" if os.name == "nt" else "saxpy.a")
25+
26+
nvcc_extra_flags = ["-std=c++17"]
27+
if os.name == "nt":
28+
nvcc_extra_flags.extend(["-Xcompiler", "/Zc:preprocessor"])
29+
30+
with tempfile.TemporaryDirectory(prefix="build_test_binaries-", dir=script_dir) as temp_dir:
31+
temp_dir_path = Path(temp_dir)
32+
object_path = temp_dir_path / final_object_path.name
33+
library_path = temp_dir_path / final_library_path.name
34+
35+
_run(
36+
[
37+
"nvcc",
38+
"-dc",
39+
*nvcc_extra_flags,
40+
"-arch=all-major",
41+
"-o",
42+
str(object_path),
43+
str(source_path),
44+
]
45+
)
46+
_run(["nvcc", "-lib", "-o", str(library_path), str(object_path)])
47+
48+
object_path.replace(final_object_path)
49+
library_path.replace(final_library_path)
50+
51+
for path in (final_object_path, final_library_path):
52+
print(f"{path}: {path.stat().st_size} bytes")
53+
54+
55+
if __name__ == "__main__":
56+
main()

cuda_core/tests/test_binaries/build_test_binaries.sh

Lines changed: 0 additions & 28 deletions
This file was deleted.

cuda_core/tests/test_module.py

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import pickle
77
import subprocess
8+
import sys
89
import warnings
910
from pathlib import Path
1011

@@ -16,7 +17,6 @@
1617
from cuda.core._program import _can_load_generated_ptx
1718
from cuda.core._utils.cuda_utils import CUDAError, driver, handle_return
1819
from cuda.core._utils.version import binding_version, driver_version
19-
from cuda.pathfinder import find_nvidia_binary_utility
2020

2121
try:
2222
import numba
@@ -193,22 +193,78 @@ def _read_saxpy_rdc(kind: str) -> bytes:
193193
raise ValueError(f"unknown saxpy RDC kind: {kind!r}")
194194

195195
if not rdc_path.is_file():
196-
nvcc_path = find_nvidia_binary_utility("nvcc")
197-
if nvcc_path is None:
198-
pytest.skip(
199-
f"{rdc_path.name} not found at {rdc_path} and nvcc is unavailable. "
200-
"In CI this is downloaded from the build stage."
201-
)
202-
env = os.environ.copy()
203-
env["NVCC"] = nvcc_path
204-
subprocess.run( # noqa: S603
205-
["bash", str(binaries_dir / "build_test_binaries.sh")], # noqa: S607
206-
check=True,
207-
env=env,
208-
)
196+
_build_saxpy_rdc(binaries_dir)
209197
return rdc_path.read_bytes()
210198

211199

200+
def _subprocess_output(result: subprocess.CompletedProcess[str]) -> str:
201+
sections = []
202+
if result.stdout:
203+
sections.append(f"stdout:\n{result.stdout.rstrip()}")
204+
if result.stderr:
205+
sections.append(f"stderr:\n{result.stderr.rstrip()}")
206+
return "\n".join(sections) or "<no output>"
207+
208+
209+
def _host_compiler_is_unavailable(output: str) -> bool:
210+
normalized = output.lower()
211+
# Keep these patterns narrow. Unknown nvcc failures should fail the test and
212+
# expose their diagnostics, not be silently reclassified as environment skips.
213+
windows_compiler_missing = (
214+
"cannot find compiler" in normalized and "cl.exe" in normalized and "in path" in normalized
215+
)
216+
linux_compiler_missing = (
217+
"no such file or directory" in normalized and "failed to preprocess host compiler properties" in normalized
218+
)
219+
return windows_compiler_missing or linux_compiler_missing
220+
221+
222+
def _build_saxpy_rdc(binaries_dir: Path) -> None:
223+
"""Use nvcc from PATH with the host compiler environment configured by the caller."""
224+
try:
225+
version_result = subprocess.run(
226+
["nvcc", "--version"], # noqa: S607 - PATH lookup is the behavior under test.
227+
capture_output=True,
228+
text=True,
229+
errors="replace",
230+
)
231+
except FileNotFoundError:
232+
pytest.skip("RDC test fixtures are absent and nvcc is not available on PATH")
233+
234+
if version_result.returncode != 0:
235+
pytest.fail(
236+
f"nvcc --version failed with exit code {version_result.returncode}\n{_subprocess_output(version_result)}",
237+
pytrace=False,
238+
)
239+
240+
print(version_result.stdout, end="")
241+
if version_result.stderr:
242+
print(version_result.stderr, end="", file=sys.stderr)
243+
244+
builder_path = binaries_dir / "build_test_binaries.py"
245+
build_result = subprocess.run( # noqa: S603
246+
[sys.executable, str(builder_path)],
247+
capture_output=True,
248+
text=True,
249+
errors="replace",
250+
)
251+
if build_result.returncode == 0:
252+
print(build_result.stdout, end="")
253+
if build_result.stderr:
254+
print(build_result.stderr, end="", file=sys.stderr)
255+
return
256+
257+
output = _subprocess_output(build_result)
258+
if _host_compiler_is_unavailable(output):
259+
print(output, file=sys.stderr)
260+
pytest.skip("nvcc is available, but its host compiler is not configured")
261+
262+
pytest.fail(
263+
f"RDC test fixture build failed with exit code {build_result.returncode}\n{output}",
264+
pytrace=False,
265+
)
266+
267+
212268
def test_get_kernel(init_cuda):
213269
kernel = """extern "C" __global__ void ABC() { }"""
214270

0 commit comments

Comments
 (0)