Skip to content

Commit 04eb067

Browse files
committed
CI: build cuda.core against local cuda-bindings wheel
1 parent 755cf61 commit 04eb067

6 files changed

Lines changed: 98 additions & 9 deletions

File tree

‎.github/workflows/build-wheel.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ jobs:
250250
CUDA_PATH=/host/${{ env.CUDA_PATH }}
251251
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
252252
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_CUDA_MAJOR }}
253-
PIP_FIND_LINKS=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
253+
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
254254
CC="/host/${{ env.SCCACHE_PATH }} cc"
255255
CXX="/host/${{ env.SCCACHE_PATH }} c++"
256256
SCCACHE_GHA_ENABLED=true
@@ -265,7 +265,7 @@ jobs:
265265
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
266266
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
267267
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_CUDA_MAJOR }}
268-
PIP_FIND_LINKS="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
268+
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
269269
# check cache stats before leaving cibuildwheel
270270
CIBW_BEFORE_TEST_LINUX: >
271271
"/host${{ env.SCCACHE_PATH }}" --show-adv-stats &&
@@ -524,7 +524,7 @@ jobs:
524524
CUDA_PATH=/host/${{ env.CUDA_PATH }}
525525
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
526526
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_PREV_CUDA_MAJOR }}
527-
PIP_FIND_LINKS=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
527+
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR=/host/${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
528528
CC="/host/${{ env.SCCACHE_PATH }} cc"
529529
CXX="/host/${{ env.SCCACHE_PATH }} c++"
530530
SCCACHE_GHA_ENABLED=true
@@ -539,7 +539,7 @@ jobs:
539539
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
540540
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
541541
CUDA_CORE_BUILD_MAJOR=${{ env.BUILD_PREV_CUDA_MAJOR }}
542-
PIP_FIND_LINKS="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
542+
CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }})"
543543
# check cache stats before leaving cibuildwheel
544544
CIBW_BEFORE_TEST_LINUX: >
545545
"/host${{ env.SCCACHE_PATH }}" --show-adv-stats &&

‎.github/workflows/coverage.yml‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,14 @@ jobs:
123123
cd cuda_pathfinder
124124
../.venv/bin/pip install -v . --group test
125125
126-
- name: Build cuda-bindings
126+
- name: Build and install cuda-bindings wheel
127127
run: |
128-
cd cuda_bindings
129-
../.venv/bin/pip install -v . --group test
128+
.venv/bin/pip wheel -v --no-deps ./cuda_bindings -w ./wheels/
129+
.venv/bin/pip install -v ./wheels/cuda_bindings*.whl --group ./cuda_bindings/pyproject.toml:test
130130
131131
- name: Build cuda-core
132132
run: |
133+
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(pwd)/wheels"
133134
cd cuda_core
134135
../.venv/bin/pip install -v . --group test
135136
@@ -237,8 +238,11 @@ jobs:
237238
238239
- name: Build cuda.core wheel
239240
run: |
241+
# Keep the same-checkout prerelease pathfinder eligible. The bindings
242+
# wheel is selected independently through the direct requirement below.
240243
export PIP_FIND_LINKS="$(pwd)/wheels"
241244
export PIP_PRE=1
245+
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w "$(pwd)/wheels")"
242246
cd cuda_core
243247
../.venv/Scripts/pip wheel -v --no-deps . -w ../wheels/
244248

‎.github/workflows/test-sdist-linux.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ jobs:
9999
run: |
100100
export CUDA_PYTHON_PARALLEL_LEVEL=$(nproc)
101101
export CUDA_CORE_BUILD_MAJOR="$(echo '${{ inputs.cuda-version }}' | cut -d. -f1)"
102+
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(pwd)/cuda_bindings/dist"
102103
export CC="sccache cc"
103104
export CXX="sccache c++"
104-
export PIP_FIND_LINKS="$(pwd)/cuda_bindings/dist $(pwd)/cuda_pathfinder/dist"
105+
export PIP_FIND_LINKS="$(pwd)/cuda_pathfinder/dist"
105106
python -m build --sdist cuda_core/
106107
pip wheel --no-deps --wheel-dir cuda_core/dist cuda_core/dist/*.tar.gz
107108

‎.github/workflows/test-sdist-windows.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ jobs:
9191
run: |
9292
export CUDA_PYTHON_PARALLEL_LEVEL=$(nproc)
9393
export CUDA_CORE_BUILD_MAJOR="$(echo '${{ inputs.cuda-version }}' | cut -d. -f1)"
94-
export PIP_FIND_LINKS="$(cygpath -w "$(pwd)/cuda_bindings/dist") $(cygpath -w "$(pwd)/cuda_pathfinder/dist")"
94+
export CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR="$(cygpath -w "$(pwd)/cuda_bindings/dist")"
95+
export PIP_FIND_LINKS="$(cygpath -w "$(pwd)/cuda_pathfinder/dist")"
9596
python -m build --sdist cuda_core/
9697
pip wheel --no-deps --wheel-dir cuda_core/dist cuda_core/dist/*.tar.gz

‎cuda_core/build_hooks.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def _determine_cuda_major_version() -> str:
119119
# used later by setup()
120120
_extensions = None
121121

122+
_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR = "CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR"
123+
122124

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

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

330333

331334
def _get_cuda_bindings_require():
335+
"""Return the cuda-bindings requirement for an isolated cuda.core build."""
332336
cuda_major = _determine_cuda_major_version()
337+
338+
# A direct reference prevents pip from ranking the CI artifact against index candidates.
339+
wheel_dir_value = os.environ.get(_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR)
340+
if wheel_dir_value is not None:
341+
if not wheel_dir_value:
342+
raise RuntimeError(f"{_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR} must not be empty")
343+
344+
wheel_dir = Path(wheel_dir_value)
345+
wheel_pattern = f"cuda_bindings-{cuda_major}.*-*.whl"
346+
wheels = sorted(path for path in wheel_dir.glob(wheel_pattern) if path.is_file()) if wheel_dir.is_dir() else []
347+
if len(wheels) != 1:
348+
available = (
349+
sorted(path.name for path in wheel_dir.glob("cuda_bindings-*.whl")) if wheel_dir.is_dir() else []
350+
)
351+
raise RuntimeError(
352+
f"Expected exactly one CUDA {cuda_major} cuda-bindings wheel in {wheel_dir} "
353+
f"via {_CUDA_BINDINGS_WHEEL_DIR_ENV_VAR}, found {len(wheels)}. "
354+
f"Available cuda-bindings wheels: {available}"
355+
)
356+
357+
wheel = wheels[0].resolve()
358+
print(f"Using local cuda-bindings build dependency: {wheel}", file=sys.stderr)
359+
return [f"cuda-bindings @ {wheel.as_uri()}"]
360+
333361
return [f"cuda-bindings=={cuda_major}.*"]
334362

335363

‎cuda_core/tests/test_build_hooks.py‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,58 @@ def test_missing_cuda_path_raises_error(self):
135135
pytest.raises(RuntimeError, match="CUDA_PATH or CUDA_HOME"),
136136
):
137137
build_hooks._determine_cuda_major_version()
138+
139+
140+
@pytest.mark.agent_authored(model="gpt-5.6")
141+
class TestGetCudaBindingsRequire:
142+
"""Tests for cuda-bindings build dependency selection."""
143+
144+
def test_default_requirement_uses_cuda_major(self, monkeypatch):
145+
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
146+
monkeypatch.delenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", raising=False)
147+
build_hooks._determine_cuda_major_version.cache_clear()
148+
149+
assert build_hooks._get_cuda_bindings_require() == ["cuda-bindings==13.*"]
150+
151+
def test_local_wheel_requirement_uses_matching_major(self, monkeypatch, tmp_path):
152+
wheel_dir = tmp_path / "wheel directory"
153+
wheel_dir.mkdir()
154+
matching_wheel = wheel_dir / "cuda_bindings-13.3.2.dev1+gabc123.d20260731-py3-none-any.whl"
155+
matching_wheel.touch()
156+
(wheel_dir / "cuda_bindings-12.9.2-py3-none-any.whl").touch()
157+
158+
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
159+
monkeypatch.setenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", str(wheel_dir))
160+
build_hooks._determine_cuda_major_version.cache_clear()
161+
162+
assert build_hooks._get_cuda_bindings_require() == [f"cuda-bindings @ {matching_wheel.resolve().as_uri()}"]
163+
164+
def test_local_wheel_directory_must_not_be_empty(self, monkeypatch):
165+
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
166+
monkeypatch.setenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", "")
167+
build_hooks._determine_cuda_major_version.cache_clear()
168+
169+
with pytest.raises(RuntimeError, match="CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR must not be empty"):
170+
build_hooks._get_cuda_bindings_require()
171+
172+
@pytest.mark.parametrize(
173+
"wheel_names",
174+
[
175+
(),
176+
(
177+
"cuda_bindings-13.3.2.dev1-py3-none-any.whl",
178+
"cuda_bindings-13.3.2.dev2-py3-none-any.whl",
179+
),
180+
],
181+
ids=["missing", "ambiguous"],
182+
)
183+
def test_local_wheel_requirement_requires_exactly_one_match(self, monkeypatch, tmp_path, wheel_names):
184+
for wheel_name in wheel_names:
185+
(tmp_path / wheel_name).touch()
186+
187+
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
188+
monkeypatch.setenv("CUDA_CORE_BUILD_BINDINGS_WHEEL_DIR", str(tmp_path))
189+
build_hooks._determine_cuda_major_version.cache_clear()
190+
191+
with pytest.raises(RuntimeError, match="Expected exactly one CUDA 13 cuda-bindings wheel"):
192+
build_hooks._get_cuda_bindings_require()

0 commit comments

Comments
 (0)