Skip to content

build: report cuda-bindings provenance in PEP 517 builds - #2520

Merged
rwgk merged 1 commit into
mainfrom
rwgk/stacked/ci/report_current_cuda_bindings_provenance
Aug 6, 2026
Merged

build: report cuda-bindings provenance in PEP 517 builds#2520
rwgk merged 1 commit into
mainfrom
rwgk/stacked/ci/report_current_cuda_bindings_provenance

Conversation

@rwgk

@rwgk rwgk commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Description

Related to #1824 and #2468.

Follow-up to #2509. This PR is intended to merge before #2510. #2510 separately enforces exact local-wheel selection, while this PR only makes the build hook's existing provenance reporting work consistently.

#2509 added a cuda.core build-hook message that reports the imported cuda-bindings version and package directory. The message appears for previous-CTK CUDA 12 builds, but not for the 24 current-CTK CUDA 13 builds (see #2468 (comment) for a full report).

The difference comes from call ordering in the PEP 517 in-tree backend. cuda_core/build_hooks.py tries to import cuda.bindings before calling _get_cuda_path(). At that point, backend-path = ["."] can leave the repository's cuda_core/cuda directory cached as the only path in the cuda namespace, so the installed build dependency is not visible. The import failure is then suppressed by the existing best-effort guard. _get_cuda_path() invokes the namespace repair documented in #1824, but currently does so only after that import attempt.

CUDA 12 wheels happen not to expose the problem because they still contain the historical _cuda_bindings_redirector.pth, which imports cuda during interpreter startup. That redirector stopped shipping with CUDA 13 as part of #792, explaining why only the previous-CTK builds currently reach the provenance print.

This PR moves the existing _get_cuda_path() call ahead of the cuda.bindings import and reuses its result later in the build. The namespace is therefore repaired before the import, allowing the existing #2509 message to report the CUDA 13 dependency's version and location and allowing the existing Cython path setup to use that imported package.

This does not change build requirements or dependency-resolution policy. _get_cuda_path() was already mandatory later in the same function, so the functional change is limited to performing that lookup earlier.

Resolve the CUDA path before importing cuda.bindings so the existing pathfinder import repairs PEP 517 namespace shadowing first. Reuse the resolved path for the CUDA include directory.
@copy-pr-bot

copy-pr-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions github-actions Bot added the cuda.core Everything related to the cuda.core module label Aug 6, 2026
@rwgk rwgk added P1 Medium priority - Should do cuda.bindings Everything related to the cuda.bindings module labels Aug 6, 2026
@rwgk rwgk added this to the cuda.core next milestone Aug 6, 2026
@rwgk

rwgk commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

@rwgk
rwgk marked this pull request as ready for review August 6, 2026 10:31
@rwgk
rwgk requested a review from juenglin August 6, 2026 10:31
@rwgk rwgk self-assigned this Aug 6, 2026
@rwgk
rwgk merged commit e963479 into main Aug 6, 2026
130 of 133 checks passed
@rwgk
rwgk deleted the rwgk/stacked/ci/report_current_cuda_bindings_provenance branch August 6, 2026 16:09
github-actions Bot pushed a commit that referenced this pull request Aug 7, 2026
Removed preview folders for the following PRs:
- PR #2456
- PR #2491
- PR #2514
- PR #2520
rparolin added a commit to rparolin/cuda-python that referenced this pull request Aug 12, 2026
Use pathlib and anchor build artifacts to build_hooks.py rather than the
working directory, since a project can be built from anywhere. This covers
the cythonize build_dir as well as the stamp: anchoring only one would let
the two caches resolve to different places when building from outside
cuda_core/.

Force a rebuild when the stamp is missing too. A missing stamp means the
last build's CUDA major is unknown, and on a first build forcing costs
nothing because there are no artifacts to reuse.

Move _check_build_major() after the cuda.bindings import. It re-enters
_get_cuda_path() and reads cuda.h, which must not happen before the
pathfinder import has repaired PEP 517 namespace shadowing (NVIDIA#2520); the
major is not needed until cythonize(). Restores
test_cuda_path_is_resolved_before_importing_bindings.

Also restores full-test's 90 minute timeout, unrelated to this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
rparolin added a commit that referenced this pull request Aug 27, 2026
* build: make Cython/extension artifacts CUDA-major aware

Moving a checkout between the cu12 and cu13 pixi environments failed at
compile time, pointing at code that is perfectly fine. Two independent
caches are to blame, and neither tool notices the configuration changed:

- Cython's up-to-date check hashes the .pyx and its cimport dependencies,
  but not compile_time_env. cuda.core feeds CUDA_CORE_BUILD_MAJOR through
  compile_time_env, so a cu13 -> cu12 switch silently reuses the cu13
  generated C++. Reproduced on main: the cu12 build dies on
  'CUdevWorkqueueConfigScope was not declared' in a build/cython/*.cpp
  generated under CUDA 13.

- setuptools' build_ext compares source mtimes against the output .so. In
  an editable install that .so lives in the source tree under a name keyed
  by the Python ABI tag alone -- there is nowhere to record the CUDA major.
  So on a cu12 -> cu13 -> cu12 round trip the final build finds an older
  generated source next to a newer .so and skips the rebuild entirely.

Both build backends now compute a build identity (CUDA major, plus the
debug and coverage flags, which likewise change the generated C++ that
neither tool tracks). Generated sources go to build/cython/<identity>, and
build/.build-identity records the last completed build so setup.py can
force build_ext when the configuration changes. Python version and platform
stay out of the identity: setuptools already encodes them in its own
build/lib.* and build/temp.* names.

CUDA_PYTHON_COVERAGE keeps generating in-tree (build_dir=".") so it can
still package the generated sources; it only contributes to the identity.

Migration is self-healing. An existing unkeyed build/cython is orphaned and
ignored; the first build after this change regenerates into a keyed
directory and overwrites the in-tree extensions.

Verified end to end with pixi on linux-64: cu13 -> cu12 -> cu13 for
cuda_core. Before, the cu12 leg failed to compile; after, all three legs
succeed, each major keeps its own build/cython/cu1X-debug directory, and
the second cu13 extension is byte-identical to the first (md5
ae4090a4f66ab9cee67b5b2f64b43781), proving it was recompiled rather than
left as the cu12 artifact.

The new CI job covers cuda_core only. cuda_bindings cannot be source-built
in its cu12 environment at all -- the 13.x sources reference CUDA 13-only
symbols (CUatomicOperation, nvrtcBundledHeadersInfo,
CUstreamCigCaptureParams), so even freshly generated cu12 sources fail
against CUDA 12 headers. That is a pre-existing problem, unrelated to
artifact staleness; the identical identity logic in cuda_bindings is
covered by unit tests instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* build_hooks: match cuda_core's missing-cuda.h error message

The two _determine_cuda_major_version implementations are annotated "keep in
sync"; a missing cuda.h surfaced as a bare FileNotFoundError in cuda_bindings
instead of the RuntimeError naming CUDA_PATH/CUDA_HOME.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* build: boil the CUDA-major fix down to essentials

Cuts the change to the two mechanisms that are actually load-bearing for the
cu12/cu13 problem, and drops everything that was speculative.

Removed the debug and coverage identity axes. The stated justification for
debug -- that gdb_debug changes the cythonize output -- is wrong: with
gdb_debug=True the generated .c is byte-identical, and Cython only writes a
side cython_debug/ directory. Keying by it produced a duplicate directory of
identical sources and re-ran cythonize on every editable/wheel switch for no
benefit. Coverage does change the generated C (linetrace), but toggling it is
a separate defect from the one this PR is about.

Removed the cuda_bindings half entirely. It has no compile_time_env, so its
generated C is CUDA-major independent, and it cannot be source-built against
CUDA 12 at all today -- the scenario the code guarded against is unreachable.
That also drops a second _determine_cuda_major_version, a duplicated 50-line
block, and a test file.

What remains: cuda_core generates into build/cython/cu<major>, and
build/.build-cuda-major records the last completed build so setup.py forces
build_ext when the major changes. The stamp is a bare major rather than a
composite identity string, so the "no build ran" guard and its test are gone
too.

Re-verified cu13 -> cu12 -> cu13 on linux-64: all three legs build, both
build/cython/cu12 and build/cython/cu13 exist, and the second cu13 extension
is byte-identical to the first (md5 1c53ab83a7201d54576175c912d03e93).
18 unit tests pass; pre-commit clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* tests: drop agent_authored markers from the new build-hook tests

Author's call. The convention stays in CLAUDE.md and on the ~30 tests that
already carry it; only the four added by this PR are affected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* build: address review feedback on CUDA-major-aware artifacts

Use pathlib and anchor build artifacts to build_hooks.py rather than the
working directory, since a project can be built from anywhere. This covers
the cythonize build_dir as well as the stamp: anchoring only one would let
the two caches resolve to different places when building from outside
cuda_core/.

Force a rebuild when the stamp is missing too. A missing stamp means the
last build's CUDA major is unknown, and on a first build forcing costs
nothing because there are no artifacts to reuse.

Move _check_build_major() after the cuda.bindings import. It re-enters
_get_cuda_path() and reads cuda.h, which must not happen before the
pathfinder import has repaired PEP 517 namespace shadowing (#2520); the
major is not needed until cythonize(). Restores
test_cuda_path_is_resolved_before_importing_bindings.

Also restores full-test's 90 minute timeout, unrelated to this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: cover the CUDA-major build wiring without a full build

The cu13 -> cu12 -> cu13 CI job is the only thing exercising this fix, and
it takes minutes. These check the same wiring in ~0.1s by replacing
cythonize(), so nothing is generated or compiled:

- generated sources for cu12 and cu13 land in different directories
- that directory is anchored to build_hooks.py, so it agrees with the stamp
  regardless of the working directory
- build_ext.finalize_options() picks up force_build_ext, and leaves force
  alone when it is clear

Each was confirmed to fail when the corresponding fix is reverted.

setup() is now guarded by __name__ == "__main__" so the command classes can
be imported. setuptools always runs setup.py as a script, so builds are
unaffected; verified with a clean rebuild.

Note the sys.path handling in the cythonize helper: _build_cuda_core()
permanently prepends cuda_bindings/ to sys.path, which makes setup.py's bare
"import build_hooks" resolve to cuda_bindings' copy. The tests contain that
mutation and pin the intended module.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda.bindings Everything related to the cuda.bindings module cuda.core Everything related to the cuda.core module P1 Medium priority - Should do

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants