Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .github/workflows/ci-pixi-source-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ on:
- "**/pixi.toml"
- "**/pixi.lock"
- "cuda_bindings/build_hooks.py"
- "cuda_bindings/pyproject.toml"
- "cuda_core/build_hooks.py"
- "cuda_core/CMakeLists.txt"
- "cuda_core/pyproject.toml"
- "cuda_bindings/cuda/bindings/**" # generated bindings sources
- "cuda_bindings/tests/cython/**"
- "cuda_core/tests/cython/**"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-sdist-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ jobs:
printf 'cuda-bindings @ %s\n' "${bindings_uri}"
} | tee wheel-constraints/cuda-core.txt

# cuda_core sdist delegates to setuptools (no CTK needed), but
# cuda_core sdist delegates directly to scikit-build-core (no CTK needed), but
# wheel-from-sdist needs CTK and cuda-bindings (dynamic build dep via
# get_requires_for_build_wheel in build_hooks.py).
- name: Build cuda.core sdist and wheel-from-sdist
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-sdist-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:
printf 'cuda-bindings @ %s\n' "${bindings_uri}"
} | tee wheel-constraints/cuda-core.txt

# cuda_core sdist delegates to setuptools (no CTK needed), but
# cuda_core sdist delegates directly to scikit-build-core (no CTK needed), but
# wheel-from-sdist needs CTK and cuda-bindings (dynamic build dep via
# get_requires_for_build_wheel in build_hooks.py).
- name: Build cuda.core sdist and wheel-from-sdist
Expand Down
1 change: 1 addition & 0 deletions cuda_bindings/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ include = ["cuda*"]

[tool.setuptools]
include-package-data = false
package-dir = {"" = "."}

[tool.setuptools.package-data]
"*" = ["*.pxd", "*.pxi"]
Expand Down
52 changes: 52 additions & 0 deletions cuda_bindings/tests/test_build_hooks.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

"""Tests for the cuda-bindings PEP 517 build wrapper."""

import importlib.util
import zipfile
from pathlib import Path
from unittest import mock

import pytest


def _load_build_hooks():
build_hooks_path = Path(__file__).parent.parent / "build_hooks.py"
spec = importlib.util.spec_from_file_location("_cuda_bindings_test_build_hooks", build_hooks_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module


build_hooks = _load_build_hooks()


@pytest.mark.agent_authored(model="gpt-5.6")
def test_editable_wheel_uses_data_only_source_root_pth(tmp_path, monkeypatch):
project_root = Path(build_hooks.__file__).resolve().parent
bindings_build = mock.Mock()
monkeypatch.chdir(project_root)
monkeypatch.setattr(build_hooks, "_build_cuda_bindings", bindings_build)

wheel_name = build_hooks.build_editable(tmp_path, {"debug": False})

bindings_build.assert_called_once_with(debug=False)
with zipfile.ZipFile(tmp_path / wheel_name) as wheel:
pth_files = [name for name in wheel.namelist() if name.endswith(".pth")]
assert len(pth_files) == 1
assert wheel.read(pth_files[0]).decode("utf-8") == f"{project_root}\n"


@pytest.mark.agent_authored(model="gpt-5.6")
def test_regular_wheel_does_not_include_editable_path(tmp_path, monkeypatch):
project_root = Path(build_hooks.__file__).resolve().parent
bindings_build = mock.Mock()
monkeypatch.chdir(project_root)
monkeypatch.setattr(build_hooks, "_build_cuda_bindings", bindings_build)

wheel_name = build_hooks.build_wheel(tmp_path, {"debug": False})

bindings_build.assert_called_once_with(debug=False)
with zipfile.ZipFile(tmp_path / wheel_name) as wheel:
assert not any(name.endswith(".pth") for name in wheel.namelist())
3 changes: 2 additions & 1 deletion cuda_core/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ This file describes `cuda_core`, the high-level Pythonic CUDA subpackage in the
- execution path: `_launcher.pyx`, `_launch_config.pyx`, `_stream.pyx`
- **C++ helpers**: module-specific C++ implementations live under
`cuda/core/_cpp/`.
- **Build backend**: `build_hooks.py` handles Cython extension setup and build
- **Build backend**: `CMakeLists.txt` defines the Cython extension build;
`build_hooks.py` wraps scikit-build-core to provide CUDA-major-specific build
dependency wiring.

## Build and version coupling
Expand Down
212 changes: 212 additions & 0 deletions cuda_core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

# FindPython 3.30.3 propagates Py_GIL_DISABLED through Python::Module on Windows.
cmake_minimum_required(VERSION 3.30.3...4.4)

project(cuda_core LANGUAGES CXX)

get_property(_cuda_core_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)

find_package(
Python
COMPONENTS Interpreter Development.Module
REQUIRED
)
find_package(Cython 3.2.5...<3.3 MODULE REQUIRED)
include(UseCython)

option(CUDA_PYTHON_COVERAGE "Build Cython line tracing support" "$ENV{CUDA_PYTHON_COVERAGE}")

if(DEFINED ENV{CUDA_PATH} AND NOT "$ENV{CUDA_PATH}" STREQUAL "")
set(_cuda_core_cuda_root "$ENV{CUDA_PATH}")
elseif(DEFINED ENV{CUDA_HOME} AND NOT "$ENV{CUDA_HOME}" STREQUAL "")
set(_cuda_core_cuda_root "$ENV{CUDA_HOME}")
endif()
if(NOT _cuda_core_cuda_root)
message(FATAL_ERROR "CUDA_PATH or CUDA_HOME must point to a CUDA Toolkit installation")
endif()

if(DEFINED ENV{CUDA_CORE_BUILD_MAJOR} AND NOT "$ENV{CUDA_CORE_BUILD_MAJOR}" STREQUAL "")
set(_cuda_core_build_major "$ENV{CUDA_CORE_BUILD_MAJOR}")
endif()

cmake_path(ABSOLUTE_PATH _cuda_core_cuda_root NORMALIZE)
set(_cuda_header "${_cuda_core_cuda_root}/include/cuda.h")
if(NOT EXISTS "${_cuda_header}")
message(FATAL_ERROR "CUDA header not found: ${_cuda_header}")
endif()

file(STRINGS "${_cuda_header}" _cuda_version_line REGEX "^#[ \t]*define[ \t]+CUDA_VERSION[ \t]+[0-9]+[ \t]*$")
if(NOT _cuda_version_line)
message(FATAL_ERROR "Cannot determine CUDA major version from ${_cuda_header}")
endif()
string(REGEX MATCH "[0-9]+" _cuda_version "${_cuda_version_line}")
math(EXPR _cuda_header_major "${_cuda_version} / 1000")

if("${_cuda_core_build_major}" STREQUAL "")
set(_cuda_core_build_major "${_cuda_header_major}")
elseif(NOT _cuda_core_build_major MATCHES "^[0-9]+$")
message(FATAL_ERROR "CUDA_CORE_BUILD_MAJOR must be an integer, got ${_cuda_core_build_major}")
elseif(NOT "${_cuda_core_build_major}" STREQUAL "${_cuda_header_major}")
message(
FATAL_ERROR
"CUDA_CORE_BUILD_MAJOR=${_cuda_core_build_major} does not match CUDA ${_cuda_header_major} "
"reported by ${_cuda_header}"
)
endif()
message(STATUS "Building cuda-core for CUDA ${_cuda_core_build_major} using ${_cuda_core_cuda_root}")

if(CUDA_PYTHON_COVERAGE)
set(_coverage_directive -X linetrace=True)
endif()

if(WIN32 AND NOT _cuda_core_is_multi_config AND CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
message(FATAL_ERROR "Debuggable builds are not supported on Windows")
endif()

set(_cuda_core_source_root "${CMAKE_CURRENT_SOURCE_DIR}/cuda/core")
set(_cython_common_args
-Werror
-X embedsignature=True
-X warn.deprecated.IF=False
-X freethreading_compatible=True
-E CUDA_CORE_BUILD_MAJOR=${_cuda_core_build_major}
-I "${CMAKE_CURRENT_SOURCE_DIR}"
${_coverage_directive}
)

if(NOT WIN32)
list(APPEND _cython_common_args "$<$<CONFIG:Debug>:--gdb>")
endif()

add_library(cuda_core_extension_settings INTERFACE)
if(WIN32 AND _cuda_core_is_multi_config)
# Multi-config generators select the build configuration after configure time.
add_custom_target(
cuda_core_validate_build_configuration
COMMAND "${CMAKE_COMMAND}" -E "$<IF:$<CONFIG:Debug>,false,true>"
COMMENT "$<IF:$<CONFIG:Debug>,Debuggable builds are not supported on Windows,Validating build configuration>"
VERBATIM
)
add_dependencies(cuda_core_extension_settings cuda_core_validate_build_configuration)
endif()
target_include_directories(
cuda_core_extension_settings
INTERFACE
"${_cuda_core_source_root}"
"${_cuda_core_source_root}/_include"
"${_cuda_core_cuda_root}/include"
)
target_compile_definitions(
cuda_core_extension_settings
INTERFACE
"$<$<AND:$<CONFIG:Debug>,$<PLATFORM_ID:Linux>,$<CXX_COMPILER_ID:GNU,Clang>>:_GLIBCXX_ASSERTIONS>"
)
if(CUDA_PYTHON_COVERAGE)
target_compile_definitions(
cuda_core_extension_settings
INTERFACE
CYTHON_TRACE_NOGIL=1
CYTHON_USE_SYS_MONITORING=0
)
endif()
file(
GLOB_RECURSE _cuda_core_modules
CONFIGURE_DEPENDS
RELATIVE "${_cuda_core_source_root}"
"${_cuda_core_source_root}/*.pyx"
)
list(TRANSFORM _cuda_core_modules REPLACE "\\.pyx$" "")

if(MSVC)
set(_aoti_shim_def "${_cuda_core_source_root}/_include/aoti_shim.def")
set(_aoti_shim_lib "${CMAKE_CURRENT_BINARY_DIR}/aoti_shim.lib")
set(_aoti_shim_exp "${CMAKE_CURRENT_BINARY_DIR}/aoti_shim.exp")
if(CMAKE_GENERATOR_PLATFORM MATCHES "^(ARM64|arm64)$" OR CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
set(_aoti_machine ARM64)
else()
set(_aoti_machine X64)
endif()
add_custom_command(
OUTPUT "${_aoti_shim_lib}"
BYPRODUCTS "${_aoti_shim_exp}"
COMMAND
"${CMAKE_AR}"
"/DEF:${_aoti_shim_def}"
"/OUT:${_aoti_shim_lib}"
"/MACHINE:${_aoti_machine}"
DEPENDS "${_aoti_shim_def}"
COMMENT "Generating the AOTI shim import library"
VERBATIM
)
add_custom_target(cuda_core_aoti_shim_generation DEPENDS "${_aoti_shim_lib}")
add_library(cuda_core_aoti_shim SHARED IMPORTED)
set_target_properties(cuda_core_aoti_shim PROPERTIES IMPORTED_IMPLIB "${_aoti_shim_lib}")
add_dependencies(cuda_core_aoti_shim cuda_core_aoti_shim_generation)
endif()

function(cuda_core_add_cython_module module_path)
string(REPLACE "/" "." module_suffix "${module_path}")
string(REPLACE "/" "_" target_suffix "${module_path}")
set(module_name "cuda.core.${module_suffix}")
set(target_name "cuda_core${target_suffix}")
set(pyx_file "${_cuda_core_source_root}/${module_path}.pyx")
if(_cuda_core_is_multi_config)
set(cython_cpp "${CMAKE_CURRENT_BINARY_DIR}/cython/$<CONFIG>/${module_path}.cpp")
else()
set(cython_cpp "${CMAKE_CURRENT_BINARY_DIR}/cython/${module_path}.cpp")
endif()
cmake_path(GET module_path FILENAME module_filename)
cmake_path(GET module_path PARENT_PATH module_directory)
set(install_directory "cuda/core/${module_directory}")
set(module_output_directory "${CMAKE_CURRENT_BINARY_DIR}/extensions/${module_directory}")

cython_transpile(
"${pyx_file}"
LANGUAGE CXX
OUTPUT "${cython_cpp}"
CYTHON_ARGS ${_cython_common_args} --module-name "${module_name}"
)

set(module_sources "${cython_cpp}")
if(module_path STREQUAL "_resource_handles")
list(APPEND module_sources "${_cuda_core_source_root}/_cpp/resource_handles.cpp")
elseif(module_path STREQUAL "_tensor_map")
list(APPEND module_sources "${_cuda_core_source_root}/_cpp/tensor_map.cpp")
endif()

python_add_library("${target_name}" MODULE WITH_SOABI ${module_sources})
set_target_properties(
"${target_name}"
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${module_output_directory}"
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS OFF
CXX_VISIBILITY_PRESET hidden
LIBRARY_OUTPUT_DIRECTORY "${module_output_directory}"
OUTPUT_NAME "${module_filename}"
RUNTIME_OUTPUT_DIRECTORY "${module_output_directory}"
VISIBILITY_INLINES_HIDDEN YES
)
target_link_libraries("${target_name}" PRIVATE cuda_core_extension_settings)

if(CUDA_PYTHON_COVERAGE)
install(FILES "${cython_cpp}" DESTINATION "${install_directory}")
endif()

if(MSVC AND module_path STREQUAL "_tensor_bridge")
target_link_libraries("${target_name}" PRIVATE cuda_core_aoti_shim)
endif()

install(TARGETS "${target_name}" LIBRARY DESTINATION "${install_directory}" RUNTIME DESTINATION "${install_directory}")
endfunction()

foreach(module_path IN LISTS _cuda_core_modules)
if(WIN32 AND module_path STREQUAL "_utils/_wsl_locale")
continue()
endif()
cuda_core_add_cython_module("${module_path}")
endforeach()
9 changes: 0 additions & 9 deletions cuda_core/MANIFEST.in

This file was deleted.

4 changes: 2 additions & 2 deletions cuda_core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ This subpackage adheres to the developing practices described in the parent meta

## Debugging

Pass the `pip` / `uv` configuration option `-C="debug=True"` or
`--config-settings="debug=True"` to explicitly to build debuggable binaries.
Pass the scikit-build-core configuration option `-Ccmake.build-type=Debug` to
build debuggable binaries explicitly.
Debuggable binaries are built by default for editable builds.

Debuggable builds are not supported on Windows.
Expand Down
Loading