Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions cuda_bindings/tests/cython/build_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
"""Build cuda_bindings Cython test extensions in-place.

pixi-build's editable install exposes the `cuda` namespace package via a
PEP 660 finder hook. Python's import machinery honors the hook, but
Cython's filesystem .pxd resolver only walks real directories on sys.path,
so `cimport cuda.bindings.*` fails to locate the .pxd files. We resolve
the namespace package's source root from `cuda.bindings.__file__` and pass
it via `include_path=` so cythonize finds the .pxd tree on every platform.
"""

from __future__ import annotations

import sys
from pathlib import Path

from Cython.Build import cythonize
from setuptools import setup

import cuda.bindings


def _bindings_source_root() -> Path:
# cuda.bindings.__file__ -> .../<root>/cuda/bindings/__init__.py
root = Path(cuda.bindings.__file__).resolve().parents[2]
if not (root / "cuda" / "bindings").is_dir():
raise RuntimeError(
f"cuda.bindings source tree not found at {root}; pixi-build editable install layout may have changed."
)
return root


def main() -> None:
script_dir = Path(__file__).resolve().parent
pyx_files = sorted(str(p) for p in script_dir.glob("test_*.pyx"))
if not pyx_files:
raise SystemExit(f"no test_*.pyx files under {script_dir}")

ext_modules = cythonize(
pyx_files,
language_level=3,
nthreads=1,
include_path=[str(_bindings_source_root())],
compiler_directives={"freethreading_compatible": True},
)

sys.argv = [sys.argv[0], "build_ext", "--inplace"]
setup(name="cuda_bindings_cython_tests", ext_modules=ext_modules)


if __name__ == "__main__":
main()
13 changes: 9 additions & 4 deletions cuda_bindings/tests/cython/build_tests.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
#!/bin/bash
set -eo pipefail

# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE

UNAME=$(uname)
if [ "$UNAME" == "Linux" ] ; then
SCRIPTPATH=$(dirname $(realpath "$0"))
export CPLUS_INCLUDE_PATH=$CUDA_HOME/include:$CPLUS_INCLUDE_PATH
export CPLUS_INCLUDE_PATH=$CUDA_HOME/include:${CPLUS_INCLUDE_PATH:-}
elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* || "$UNAME" == MSYS* ]] ; then
SCRIPTPATH="$(dirname $(cygpath -w $(realpath "$0")))"
export CL="/I\"${CUDA_HOME}\\include\" ${CL}"
export CL="/I\"${CUDA_HOME}\\include\" ${CL:-}"
else
exit 1
fi

# Use -j 1 to side-step any process-pool issues and ensure deterministic single-threaded builds
cythonize -3 -j 1 -i -Xfreethreading_compatible=True ${SCRIPTPATH}/test_*.pyx
# Use a Python driver so the cuda.bindings source root is resolved at
# runtime and passed via Cython's include_path -- avoids platform-specific
# PYTHONPATH separator handling and surfaces import errors as exceptions.
# nthreads=1 inside the driver mirrors the previous `-j 1` to side-step
# any process-pool issues and keep builds deterministic.
python "${SCRIPTPATH}/build_tests.py"
52 changes: 52 additions & 0 deletions cuda_core/tests/cython/build_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Build cuda_core Cython test extensions in-place.

pixi-build's editable install exposes the `cuda` namespace package via a
PEP 660 finder hook. Python's import machinery honors the hook, but
Cython's filesystem .pxd resolver only walks real directories on sys.path,
so `cimport cuda.bindings.*` fails to locate the .pxd files. We resolve
the namespace package's source root from `cuda.bindings.__file__` and pass
it via `include_path=` so cythonize finds the .pxd tree on every platform.
"""

from __future__ import annotations

import sys
from pathlib import Path

from Cython.Build import cythonize
from setuptools import setup

import cuda.bindings


def _bindings_source_root() -> Path:
# cuda.bindings.__file__ -> .../<root>/cuda/bindings/__init__.py
root = Path(cuda.bindings.__file__).resolve().parents[2]
if not (root / "cuda" / "bindings").is_dir():
raise RuntimeError(
f"cuda.bindings source tree not found at {root}; pixi-build editable install layout may have changed."
)
return root


def main() -> None:
script_dir = Path(__file__).resolve().parent
pyx_files = sorted(str(p) for p in script_dir.glob("test_*.pyx"))
if not pyx_files:
raise SystemExit(f"no test_*.pyx files under {script_dir}")

ext_modules = cythonize(
pyx_files,
language_level=3,
include_path=[str(_bindings_source_root())],
compiler_directives={"freethreading_compatible": True},
)

sys.argv = [sys.argv[0], "build_ext", "--inplace"]
setup(name="cuda_core_cython_tests", ext_modules=ext_modules)


if __name__ == "__main__":
main()
10 changes: 7 additions & 3 deletions cuda_core/tests/cython/build_tests.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
#!/bin/bash
set -eo pipefail

# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

UNAME=$(uname)
if [ "$UNAME" == "Linux" ] ; then
SCRIPTPATH=$(dirname $(realpath "$0"))
export CPLUS_INCLUDE_PATH=${SCRIPTPATH}/../../cuda/core/_include:$CUDA_HOME/include:$CPLUS_INCLUDE_PATH
export CPLUS_INCLUDE_PATH=${SCRIPTPATH}/../../cuda/core/_include:$CUDA_HOME/include:${CPLUS_INCLUDE_PATH:-}
elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* || "$UNAME" == MSYS* ]] ; then
SCRIPTPATH="$(dirname $(cygpath -w $(realpath "$0")))"
CUDA_CORE_INCLUDE_PATH=$(echo "${SCRIPTPATH}\..\..\cuda\core\_include" | sed 's/\\/\\\\/g')
export CL="/I\"${CUDA_CORE_INCLUDE_PATH}\" /I\"${CUDA_HOME}\\include\" ${CL}"
export CL="/I\"${CUDA_CORE_INCLUDE_PATH}\" /I\"${CUDA_HOME}\\include\" ${CL:-}"
else
exit 1
fi

cythonize -3 -i -Xfreethreading_compatible=True ${SCRIPTPATH}/test_*.pyx
# Use a Python driver so the cuda.bindings source root is resolved at
# runtime and passed via Cython's include_path -- avoids platform-specific
# PYTHONPATH separator handling and surfaces import errors as exceptions.
python "${SCRIPTPATH}/build_tests.py"
36 changes: 23 additions & 13 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,40 @@ docs = { features = [], solve-group = "docs" }
PIXI_ENVIRONMENT_NAME = "${PIXI_ENVIRONMENT_NAME/default/cu13}"

# Test Tasks
# Runs tests across all sub-packages: pathfinder → bindings → core (dependency order)
# Each sub-package has its own pixi.toml; the -e environment propagates via PIXI_ENVIRONMENT_NAME
# Runs tests across all sub-packages: pathfinder → bindings → core (dependency order).
#
# Each sub-package has its own pixi.toml. We wrap each task in `bash -c '...'`
# (single-quoted in TOML, double-quoted inside) because pixi `cmd` arrays do
# not expand shell variables -- without this, the inner `pixi run` would drop
# into the sub-package's `default` environment, which lacks a cuda-version
# pin, causing the conda solver to pick a mismatched CUDA toolkit. Forwarding
# `-e "$PIXI_ENVIRONMENT_NAME"` keeps the active environment consistent end
# to end.
#
# Linux-only for now; Windows/macOS root test-task parity is tracked
# separately.
#
# Usage: pixi run test | pixi run -e cu12 test | pixi run -e cu13 test
[target.linux.tasks.test-pathfinder]
cmd = [
"pixi",
"run",
"--manifest-path",
"$PIXI_PROJECT_ROOT/cuda_pathfinder",
"test",
"bash",
"-c",
'pixi run --manifest-path "$PIXI_PROJECT_ROOT/cuda_pathfinder" -e "$PIXI_ENVIRONMENT_NAME" test',
]

[target.linux.tasks.test-bindings]
cmd = [
"pixi",
"run",
"--manifest-path",
"$PIXI_PROJECT_ROOT/cuda_bindings",
"test",
"bash",
"-c",
'pixi run --manifest-path "$PIXI_PROJECT_ROOT/cuda_bindings" -e "$PIXI_ENVIRONMENT_NAME" test',
]

[target.linux.tasks.test-core]
cmd = ["pixi", "run", "--manifest-path", "$PIXI_PROJECT_ROOT/cuda_core", "test"]
cmd = [
"bash",
"-c",
'pixi run --manifest-path "$PIXI_PROJECT_ROOT/cuda_core" -e "$PIXI_ENVIRONMENT_NAME" test',
]

[target.linux.tasks.test]
depends-on = [
Expand Down
Loading