diff --git a/.github/workflows/build-wheel.yml b/.github/workflows/build-wheel.yml index 235cfbe813..4dac9c8eca 100644 --- a/.github/workflows/build-wheel.yml +++ b/.github/workflows/build-wheel.yml @@ -463,7 +463,9 @@ jobs: cuda-path: "./cuda_toolkit_prev" - name: Build cuda.core test binaries - run: bash ${{ env.CUDA_CORE_TEST_BINARIES_DIR }}/build_test_binaries.sh + run: | + nvcc --version + python "${{ env.CUDA_CORE_TEST_BINARIES_DIR }}/build_test_binaries.py" - name: Upload cuda.core test binaries uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/cuda_core/tests/test_binaries/build_test_binaries.py b/cuda_core/tests/test_binaries/build_test_binaries.py new file mode 100644 index 0000000000..5dfa1fe7f0 --- /dev/null +++ b/cuda_core/tests/test_binaries/build_test_binaries.py @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Build the relocatable-device-code fixtures used by cuda.core tests.""" + +from __future__ import annotations + +import os +import subprocess +import tempfile +from pathlib import Path + + +def _run(command: list[str]) -> None: + print(f"+ {subprocess.list2cmdline(command)}") + result = subprocess.run(command) # noqa: S603 + if result.returncode != 0: + raise SystemExit(result.returncode) + + +def main() -> None: + script_dir = Path(__file__).resolve().parent + source_path = script_dir / "saxpy.cu" + final_object_path = script_dir / "saxpy.o" + final_library_path = script_dir / ("saxpy.lib" if os.name == "nt" else "saxpy.a") + + nvcc_extra_flags = ["-std=c++17"] + if os.name == "nt": + nvcc_extra_flags.extend(["-Xcompiler", "/Zc:preprocessor"]) + + with tempfile.TemporaryDirectory(prefix="build_test_binaries-", dir=script_dir) as temp_dir: + temp_dir_path = Path(temp_dir) + object_path = temp_dir_path / final_object_path.name + library_path = temp_dir_path / final_library_path.name + + _run( + [ + "nvcc", + "-dc", + *nvcc_extra_flags, + "-arch=all-major", + "-o", + str(object_path), + str(source_path), + ] + ) + _run(["nvcc", "-lib", "-o", str(library_path), str(object_path)]) + + object_path.replace(final_object_path) + library_path.replace(final_library_path) + + for path in (final_object_path, final_library_path): + print(f"{path}: {path.stat().st_size} bytes") + + +if __name__ == "__main__": + main() diff --git a/cuda_core/tests/test_binaries/build_test_binaries.sh b/cuda_core/tests/test_binaries/build_test_binaries.sh deleted file mode 100755 index 077dd8e90c..0000000000 --- a/cuda_core/tests/test_binaries/build_test_binaries.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail - -# Build .o test fixtures. Invoked at CI build stage - -SCRIPTPATH=$(dirname "$(realpath "$0")") - -NVCC_EXTRA_FLAGS=(-std=c++17) -if [[ "${OS:-}" == "Windows_NT" ]]; then - NVCC_EXTRA_FLAGS+=(-Xcompiler /Zc:preprocessor) -fi - -NVCC="${NVCC:-nvcc}" - -"${NVCC}" -dc "${NVCC_EXTRA_FLAGS[@]}" -arch=all-major \ - -o "${SCRIPTPATH}/saxpy.o" "${SCRIPTPATH}/saxpy.cu" - -if [[ "${OS:-}" == "Windows_NT" ]]; then - "${NVCC}" -lib -o "${SCRIPTPATH}/saxpy.lib" "${SCRIPTPATH}/saxpy.o" - ls -lah "${SCRIPTPATH}/saxpy.o" "${SCRIPTPATH}/saxpy.lib" -else - "${NVCC}" -lib -o "${SCRIPTPATH}/saxpy.a" "${SCRIPTPATH}/saxpy.o" - ls -lah "${SCRIPTPATH}/saxpy.o" "${SCRIPTPATH}/saxpy.a" -fi diff --git a/cuda_core/tests/test_module.py b/cuda_core/tests/test_module.py index a060096661..12e402d39d 100644 --- a/cuda_core/tests/test_module.py +++ b/cuda_core/tests/test_module.py @@ -5,6 +5,7 @@ import os import pickle import subprocess +import sys import warnings from pathlib import Path @@ -16,7 +17,6 @@ from cuda.core._program import _can_load_generated_ptx from cuda.core._utils.cuda_utils import CUDAError, driver, handle_return from cuda.core._utils.version import binding_version, driver_version -from cuda.pathfinder import find_nvidia_binary_utility try: import numba @@ -193,22 +193,77 @@ def _read_saxpy_rdc(kind: str) -> bytes: raise ValueError(f"unknown saxpy RDC kind: {kind!r}") if not rdc_path.is_file(): - nvcc_path = find_nvidia_binary_utility("nvcc") - if nvcc_path is None: - pytest.skip( - f"{rdc_path.name} not found at {rdc_path} and nvcc is unavailable. " - "In CI this is downloaded from the build stage." - ) - env = os.environ.copy() - env["NVCC"] = nvcc_path - subprocess.run( # noqa: S603 - ["bash", str(binaries_dir / "build_test_binaries.sh")], # noqa: S607 - check=True, - env=env, - ) + _build_saxpy_rdc(binaries_dir) return rdc_path.read_bytes() +def _subprocess_output(result: subprocess.CompletedProcess[str]) -> str: + sections = [] + if result.stdout: + sections.append(f"stdout:\n{result.stdout.rstrip()}") + if result.stderr: + sections.append(f"stderr:\n{result.stderr.rstrip()}") + return "\n".join(sections) or "" + + +def _host_compiler_is_unavailable(output: str) -> bool: + normalized = output.lower() + # Keep these patterns narrow. Unknown nvcc failures should fail the test and + # expose their diagnostics, not be silently reclassified as environment skips. + windows_compiler_missing = ( + "cannot find compiler" in normalized and "cl.exe" in normalized and "in path" in normalized + ) + linux_compiler_missing = ( + "no such file or directory" in normalized and "failed to preprocess host compiler properties" in normalized + ) + return windows_compiler_missing or linux_compiler_missing + + +def _build_saxpy_rdc(binaries_dir: Path) -> None: + try: + version_result = subprocess.run( + ["nvcc", "--version"], # noqa: S607 - PATH lookup is the behavior under test. + capture_output=True, + text=True, + errors="replace", + ) + except FileNotFoundError: + pytest.skip("RDC test fixtures are absent and nvcc is not available on PATH") + + if version_result.returncode != 0: + pytest.fail( + f"nvcc --version failed with exit code {version_result.returncode}\n{_subprocess_output(version_result)}", + pytrace=False, + ) + + print(version_result.stdout, end="") + if version_result.stderr: + print(version_result.stderr, end="", file=sys.stderr) + + builder_path = binaries_dir / "build_test_binaries.py" + build_result = subprocess.run( # noqa: S603 + [sys.executable, str(builder_path)], + capture_output=True, + text=True, + errors="replace", + ) + if build_result.returncode == 0: + print(build_result.stdout, end="") + if build_result.stderr: + print(build_result.stderr, end="", file=sys.stderr) + return + + output = _subprocess_output(build_result) + if _host_compiler_is_unavailable(output): + print(output, file=sys.stderr) + pytest.skip("nvcc is available, but its host compiler is not configured") + + pytest.fail( + f"RDC test fixture build failed with exit code {build_result.returncode}\n{output}", + pytrace=False, + ) + + def test_get_kernel(init_cuda): kernel = """extern "C" __global__ void ABC() { }"""