Skip to content

build: consolidate toolchain helpers - #3000

Open
juenglin wants to merge 10 commits into
NVIDIA:mainfrom
juenglin:consolidate-toolchain-helpers2
Open

juenglin wants to merge 10 commits into
NVIDIA:mainfrom
juenglin:consolidate-toolchain-helpers2

Conversation

@juenglin

@juenglin juenglin commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements the single-source-of-truth follow-up requested in the approval of #2903: the toolchain, Cython cache, stamp and CUDA-path helpers that were duplicated in cuda_bindings/build_hooks.py and cuda_core/build_hooks.py now live in one file, cuda_bindings/_build_shared.py. cuda_core/_build_shared.py is a symlink to it. Each build_hooks.py keeps only what is genuinely per-package. Supersedes #2965.

Why a symlink works

Both packages already declare backend-path = ["."], so _build_shared.py is importable while the PEP 517 backend runs. Python does not dereference symlinks in __file__, so Path(__file__)-relative locations (_BUILD_DIR, the Cython alias location) resolve under whichever package loads the module. Windows clones need core.symlinks=true; that was handled in #2992, and the install docs now point to it.

Changes

build: share toolchain selection and flag assembly via _build_shared.py

  • Adds _build_shared.py with the toolchain helpers (moved verbatim from the duplicated block) and the flag set from build: unify compiler flags #2998.
  • resolve_toolchain() takes the per-package choices as arguments: a required cxx_std (bindings stays on c++14, core on c++17), warnings_as_errors (core only, from CUDA_PYTHON_WERROR), and an optional tweak hook. Bindings moves -Wno-deprecated-declarations into its tweak.
  • No behavior change: the resulting compiler/linker flags and CC/CXX/LDCXXSHARED are identical across platform x toolchain x debug x coverage x werror, except that -Wno-deprecated-declarations is now last for bindings.
  • MANIFEST.in in both packages includes _build_shared.py so sdists carry it. _build_shared is added to isort known-first-party, with a T201 exemption in ruff.toml. The package AGENTS.md files note the symlink.

build: move Cython cache helpers and rebuild stamps into _build_shared.py

  • Moves the Cython cache helpers (_cython_cache_path, _stable_cython_alias) and the stamp mechanics (_BUILD_DIR, _abi_stamp_path, force_build_ext, check_build_key/record_build_key) into the shared module.
  • Deletes toolshed/check_build_hooks_sync.py and its pre-commit hook, since a single source needs no drift check.
  • Each build_hooks.py keeps its own stamp file and key: toolchain for cuda-bindings; CUDA major, toolchain, debug and coverage for cuda-core.
  • setup.py is unchanged. build_hooks re-exports force_build_ext through a module-level __getattr__.
  • The tests/cython/build_tests.py scripts now load _build_shared.py directly.

build: move the CUDA path lookup into _build_shared.py

test: share _build_shared tests through mixins and prune wheel-covered ones

  • The toolchain, linker-command and stamp tests were duplicated verbatim in both packages' test_build_hooks.py. They are now mixins in cuda_python_test_helpers/build_shared.py, mixed into each package against the _build_shared module it loaded. Each package keeps only its own choices (bindings' c++14 and -Wno-deprecated-declarations, core's c++17 and CUDA_PYTHON_WERROR wiring, the stamped keys).
  • Prunes what any wheel build already proves: the llvm preflight being a no-op for the default toolchain, and the default toolchain's name and compiler binaries.
  • Adds tests for previously untested behavior: the Werror flags per platform, the check_build_key/record_build_key protocol, and that build_hooks re-exports force_build_ext without holding a copy. These tests patch _build_shared, not build_hooks. Patching build_hooks leaves a plain attribute behind on teardown that shadows the re-export for later tests.

docs: point the source-install notes at Development on Windows

  • Links the "Development on Windows" section from the source-install notes in cuda_bindings/docs/source/install.rst and cuda_core/docs/source/install.rst. Without symlink support, a Windows source build fails confusingly because cuda_core/_build_shared.py is a symlink.

Test plan

  • pytest cuda_bindings/tests/test_build_hooks.py --noconftest: 67 passed, 3 skipped
  • pytest cuda_core/tests/test_build_hooks.py --noconftest: 124 passed, 3 skipped
  • Editable installs of cuda_bindings and cuda_core build cleanly; the isolated build finds cuda.pathfinder and resolves the CUDA path
  • ruff check and ruff format --check clean; all pre-commit hooks pass on every commit
  • pytest-randomly (-p randomly, seeds 1-5) on both suites: all pass, including seeds 1, 2, 4 that failed in build: consolidate toolchain helpers via symlink #2965 before its fix
  • python -m build --sdist for both packages: the tarball contains _build_shared.py byte-identical to the canonical, and a from-scratch backend load resolves every shared helper to _build_shared
  • Full CI (needs /ok to test)

Reuse the wheel Cython cache in tests/cython/build_tests.py so the
new test stage can hit CUDA_PYTHON_CYTHON_CACHE_DIR.

(cherry picked from commit 392178690f26c7d7cb8117a5556a70f2c728c321)
Close the audit from NVIDIA#1882. The two packages' Linux and MSVC flag sets had
drifted for legacy reasons; both now go through the same structure.

Changes in cuda-bindings:
- Drop -fpermissive and -fno-var-tracking-assignments (gcc-only; not
  needed by current Cython-generated C++; confirmed by a rebuild).
- Change -O3 to -O2 (consistent with cuda-core; -O3 was not the source
  of the measured launch_{256,512}_args latency difference vs c++17).
- Add /std:c++14 and /O2 on MSVC (was missing entirely).
- Split '-std=c++14 -Wno-deprecated-declarations' onto separate lines
  with an explanatory comment on each.

Changes in cuda-core:
- Add /O2 on MSVC (modern setuptools no longer forces /Ox).
- Add comments at both the MSVC and Linux flag sites explaining why
  c++17 is required (structured bindings / if constexpr in _cpp/).

Tests:
- Update the bindings TestResolveToolchain: replace test_gnu_keeps_gcc_only_flags
  with test_linux_opt_flag_set (asserts -O2, not -O3; no gnu-only flags);
  add test_msvc_opt_flag_set; update test_gnu_sets_env_and_flags assertions.
- Add test_linux_opt_flag_set and test_msvc_opt_flag_set to the core
  TestResolveToolchain; update the comment in test_gnu_sets_env_and_flags.
Add cuda_bindings/_build_shared.py as the single source of truth for the
toolchain helpers (moved verbatim out of the duplicated block in both
build_hooks.py files) and for the flag set unified in the previous commit.
cuda_core/_build_shared.py is a symlink to it. Both packages already use
backend-path = ["."], so the module is importable while the PEP 517 backend
runs, and Python does not dereference symlinks in __file__, so
Path(__file__)-relative locations still resolve under each package.

resolve_toolchain() takes the per-package choices as arguments: a required
cxx_std (no shared default; bindings stays on c++14, core on c++17),
warnings_as_errors (core only, from CUDA_PYTHON_WERROR), and an optional
tweak hook. Each build_hooks.py keeps a thin _resolve_toolchain() declaring
just that. cuda-bindings moves -Wno-deprecated-declarations into its tweak.

No behavior change: the resulting compiler/linker flags and CC/CXX/
LDCXXSHARED environment are identical across platform x toolchain x debug x
coverage x werror, apart from -Wno-deprecated-declarations now being last
for bindings. The Cython cache helpers stay in the synced block for now.

Also include _build_shared.py in both sdists (MANIFEST.in), add it to
known-first-party for isort, load it ahead of build_hooks.py in the tests
and the tests/cython/build_tests.py scripts, and note the symlink in the
package AGENTS.md files.
@copy-pr-bot

copy-pr-bot Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added cuda.bindings Everything related to the cuda.bindings module cuda.core Everything related to the cuda.core module labels Oct 2, 2026
…d.py

The Cython cache helpers were duplicated verbatim in both build_hooks.py
files, kept identical by a pre-commit sync check. The stamp mechanics
(_BUILD_DIR, _abi_stamp_path, the force_build_ext flag, check/record of a
stamped key) were near copies.

Move them into _build_shared.py and delete toolshed/check_build_hooks_sync.py
with its hook. Each build_hooks.py keeps only what is per package: its stamp
file and key (toolchain for cuda-bindings; CUDA major, toolchain, debug and
coverage for cuda-core). setup.py is unchanged: build_hooks re-exports
force_build_ext through a module-level __getattr__.

The Cython test builders now load _build_shared.py directly instead of
build_hooks.py. The tests patch force_build_ext and sysconfig on
_build_shared.
…d ones

The toolchain, linker-command and stamp tests were duplicated verbatim in
cuda_bindings/tests/test_build_hooks.py and cuda_core/tests/test_build_hooks.py,
although they exercise code that now lives in the one _build_shared.py.

Move them into mixins in cuda_python_test_helpers/build_shared.py, which each
package mixes in against the _build_shared module it loaded. Each package keeps
only its own choices: bindings' c++14 and -Wno-deprecated-declarations, core's
c++17 and CUDA_PYTHON_WERROR wiring, and the key each one stamps.

Prune what a wheel build already proves: the llvm preflight being a no-op for
the default toolchain, and the default toolchain's name and compiler binaries.

Add tests for behavior that had none: the Werror flags per platform, the
check_build_key/record_build_key protocol, and that build_hooks re-exports
force_build_ext from _build_shared without holding a copy. Patching the flag on
build_hooks instead would leave a plain attribute there on teardown that
shadows the re-export for later tests, so the tests patch _build_shared.
cuda_core/_build_shared.py is a symlink into cuda_bindings, so a Windows
source build fails confusingly when git symlinks were off at clone time.
CONTRIBUTING.md now documents the setting; link to it from the cuda-bindings
and cuda-core source-install notes.
_import_get_cuda_path_or_home() and _get_cuda_path() were byte-identical
copies in the two build_hooks.py files, held together by "keep in sync"
comments. Move them into the shared helper module and import them from
both backends.

The tests that patch build_hooks._get_cuda_path or call its cache_clear()
keep working: the name is bound in each build_hooks module, and the
cached function object is the same.
@juenglin juenglin added the CI/CD CI/CD infrastructure label Oct 2, 2026
@juenglin juenglin self-assigned this Oct 2, 2026
@juenglin juenglin added this to the cuda.core 1.3.0 milestone Oct 2, 2026
@juenglin
juenglin requested review from Andy-Jost and leofang October 2, 2026 16:52
@juenglin

juenglin commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test b4ba4b8

coverage.yml and precommit-windows still cloned without core.symlinks=true,
so cuda_core/_build_shared.py would land as a text stub. Drop the unused
_fake_sysconfig leftovers from the mixin move.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

…in-helpers2

Keep the _build_shared refactor where it overlapped NVIDIA#2998's inlined flag
and test copies now on main.
@juenglin

juenglin commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 0d0c279

@juenglin
juenglin marked this pull request as ready for review October 2, 2026 19:05

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/CD CI/CD infrastructure cuda.bindings Everything related to the cuda.bindings module cuda.core Everything related to the cuda.core module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant