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
8 changes: 4 additions & 4 deletions .github/workflows/build-wheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ jobs:
CUDA_PATH=/host/${{ env.CUDA_PATH }}
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_CUDA_MAJOR }}
PIP_FIND_LINKS=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
CC="/host/${{ env.SCCACHE_PATH }} cc"
CXX="/host/${{ env.SCCACHE_PATH }} c++"
SCCACHE_GHA_ENABLED=true
Expand All @@ -265,7 +265,7 @@ jobs:
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_CUDA_MAJOR }}
PIP_FIND_LINKS="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
# check cache stats before leaving cibuildwheel
CIBW_BEFORE_TEST_LINUX: >
"/host${{ env.SCCACHE_PATH }}" --show-adv-stats &&
Expand Down Expand Up @@ -524,7 +524,7 @@ jobs:
CUDA_PATH=/host/${{ env.CUDA_PATH }}
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_PREV_CUDA_MAJOR }}
PIP_FIND_LINKS=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
CC="/host/${{ env.SCCACHE_PATH }} cc"
CXX="/host/${{ env.SCCACHE_PATH }} c++"
SCCACHE_GHA_ENABLED=true
Expand All @@ -539,7 +539,7 @@ jobs:
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_PREV_CUDA_MAJOR }}
PIP_FIND_LINKS="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
# check cache stats before leaving cibuildwheel
CIBW_BEFORE_TEST_LINUX: >
"/host${{ env.SCCACHE_PATH }}" --show-adv-stats &&
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,11 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
# --paginate fetches all pages; jq outputs one name per line per page;
# head -1 takes the first (newest) match since GitHub returns tags
# newest-first. Fails hard if no cuda-core-v* tag is found.
# sed prints the first (newest) match while consuming all pages, so
# gh can complete without SIGPIPE. Fails if no cuda-core-v* tag is found.
tag="$(gh api "repos/$GITHUB_REPOSITORY/tags" --paginate \
--jq '.[] | select(.name | startswith("cuda-core-v")) | .name' \
| head -1)"
| sed -n '1p')"
if [[ -z "${tag}" ]]; then
echo "::error::No cuda-core-v* tag found in the repository." >&2
exit 1
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,14 @@ jobs:
cd cuda_pathfinder
../.venv/bin/pip install -v . --group test

- name: Build cuda-bindings
- name: Build and install cuda-bindings wheel
run: |
cd cuda_bindings
../.venv/bin/pip install -v . --group test
.venv/bin/pip wheel -v --no-deps ./cuda_bindings -w ./wheels/
.venv/bin/pip install -v ./wheels/cuda_bindings*.whl --group ./cuda_bindings/pyproject.toml:test

- name: Build cuda-core
run: |
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(pwd)/wheels"
cd cuda_core
../.venv/bin/pip install -v . --group test

Expand Down Expand Up @@ -237,8 +238,11 @@ jobs:

- name: Build cuda.core wheel
run: |
# Keep the same-checkout prerelease pathfinder eligible. The bindings
# wheel is selected independently through the direct requirement below.
export PIP_FIND_LINKS="$(pwd)/wheels"
export PIP_PRE=1
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w "$(pwd)/wheels")"
cd cuda_core
../.venv/Scripts/pip wheel -v --no-deps . -w ../wheels/

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test-sdist-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ jobs:
run: |
export CUDA_PYTHON_PARALLEL_LEVEL=$(nproc)
export CUDA_CORE_BUILD_MAJOR="$(echo '${{ inputs.cuda-version }}' | cut -d. -f1)"
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(pwd)/cuda_bindings/dist"
export CC="sccache cc"
export CXX="sccache c++"
export PIP_FIND_LINKS="$(pwd)/cuda_bindings/dist $(pwd)/cuda_pathfinder/dist"
export PIP_FIND_LINKS="$(pwd)/cuda_pathfinder/dist"
python -m build --sdist cuda_core/
pip wheel --no-deps --wheel-dir cuda_core/dist cuda_core/dist/*.tar.gz

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test-sdist-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ jobs:
run: |
export CUDA_PYTHON_PARALLEL_LEVEL=$(nproc)
export CUDA_CORE_BUILD_MAJOR="$(echo '${{ inputs.cuda-version }}' | cut -d. -f1)"
export PIP_FIND_LINKS="$(cygpath -w "$(pwd)/cuda_bindings/dist") $(cygpath -w "$(pwd)/cuda_pathfinder/dist")"
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w "$(pwd)/cuda_bindings/dist")"
export PIP_FIND_LINKS="$(cygpath -w "$(pwd)/cuda_pathfinder/dist")"
python -m build --sdist cuda_core/
pip wheel --no-deps --wheel-dir cuda_core/dist cuda_core/dist/*.tar.gz
28 changes: 28 additions & 0 deletions cuda_core/build_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def _determine_cuda_major_version() -> str:
# used later by setup()
_extensions = None

_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR = "CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR"


def _build_cuda_core(debug=False):
# Customizing the build hooks is needed because we must defer cythonization until cuda-bindings,
Expand All @@ -136,6 +138,7 @@ def _build_cuda_core(debug=False):
import cuda.bindings

bindings_path = Path(cuda.bindings.__file__).parent # .../cuda/bindings/
print(f"Using cuda-bindings {cuda.bindings.__version__} from {bindings_path}", file=sys.stderr)
cuda_package_dir = bindings_path.parent.parent # .../cuda_bindings/ (contains cuda/)
if str(cuda_package_dir) not in sys.path:
sys.path.insert(0, str(cuda_package_dir))
Expand Down Expand Up @@ -329,7 +332,32 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):


def _get_cuda_bindings_require():
"""Return the cuda-bindings requirement for an isolated cuda.core build."""
cuda_major = _determine_cuda_major_version()

# A direct reference prevents pip from ranking the CI artifact against index candidates.
wheel_dir_value = os.environ.get(_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR)
if wheel_dir_value is not None:
if not wheel_dir_value:
raise RuntimeError(f"{_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR} must not be empty")

wheel_dir = Path(wheel_dir_value)
wheel_pattern = f"cuda_bindings-{cuda_major}.*-*.whl"
wheels = sorted(path for path in wheel_dir.glob(wheel_pattern) if path.is_file()) if wheel_dir.is_dir() else []
if len(wheels) != 1:
available = (
sorted(path.name for path in wheel_dir.glob("cuda_bindings-*.whl")) if wheel_dir.is_dir() else []
)
raise RuntimeError(
f"Expected exactly one CUDA {cuda_major} cuda-bindings wheel in {wheel_dir} "
f"via {_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR}, found {len(wheels)}. "
f"Available cuda-bindings wheels: {available}"
)

wheel = wheels[0].resolve()
print(f"Using local cuda-bindings build dependency: {wheel}", file=sys.stderr)
return [f"cuda-bindings @ {wheel.as_uri()}"]

return [f"cuda-bindings=={cuda_major}.*"]


Expand Down
2 changes: 1 addition & 1 deletion cuda_core/cuda/core/graph/_graph_node.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ cdef void _init_memcpy_params(
params.srcMemoryType = src_type[0]
params.dstMemoryType = dst_type[0]
if src_type[0] == cydriver.CU_MEMORYTYPE_HOST:
params.srcHost = <const void*><uintptr_t>src
params.srcHost = <void*><uintptr_t>src
else:
params.srcDevice = src
if dst_type[0] == cydriver.CU_MEMORYTYPE_HOST:
Expand Down
55 changes: 55 additions & 0 deletions cuda_core/tests/test_build_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,58 @@ def test_missing_cuda_path_raises_error(self):
pytest.raises(RuntimeError, match="CUDA_PATH or CUDA_HOME"),
):
build_hooks._determine_cuda_major_version()


@pytest.mark.agent_authored(model="gpt-5.6")
class TestGetCudaBindingsRequire:
"""Tests for cuda-bindings build dependency selection."""

def test_default_requirement_uses_cuda_major(self, monkeypatch):
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
monkeypatch.delenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", raising=False)
build_hooks._determine_cuda_major_version.cache_clear()

assert build_hooks._get_cuda_bindings_require() == ["cuda-bindings==13.*"]

def test_local_wheel_requirement_uses_matching_major(self, monkeypatch, tmp_path):
wheel_dir = tmp_path / "wheel directory"
wheel_dir.mkdir()
matching_wheel = wheel_dir / "cuda_bindings-13.3.2.dev1+gabc123.d20260731-py3-none-any.whl"
matching_wheel.touch()
(wheel_dir / "cuda_bindings-12.9.2-py3-none-any.whl").touch()

monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
monkeypatch.setenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", str(wheel_dir))
build_hooks._determine_cuda_major_version.cache_clear()

assert build_hooks._get_cuda_bindings_require() == [f"cuda-bindings @ {matching_wheel.resolve().as_uri()}"]

def test_local_wheel_directory_must_not_be_empty(self, monkeypatch):
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
monkeypatch.setenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", "")
build_hooks._determine_cuda_major_version.cache_clear()

with pytest.raises(RuntimeError, match="CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR must not be empty"):
build_hooks._get_cuda_bindings_require()

@pytest.mark.parametrize(
"wheel_names",
[
(),
(
"cuda_bindings-13.3.2.dev1-py3-none-any.whl",
"cuda_bindings-13.3.2.dev2-py3-none-any.whl",
),
],
ids=["missing", "ambiguous"],
)
def test_local_wheel_requirement_requires_exactly_one_match(self, monkeypatch, tmp_path, wheel_names):
for wheel_name in wheel_names:
(tmp_path / wheel_name).touch()

monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
monkeypatch.setenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", str(tmp_path))
build_hooks._determine_cuda_major_version.cache_clear()

with pytest.raises(RuntimeError, match="Expected exactly one CUDA 13 cuda-bindings wheel"):
build_hooks._get_cuda_bindings_require()
Loading