Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream: Added: - python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc. Includes 204 .py files with full test coverage for all 27 algorithms - ci/ (163 files) — Build/test infrastructure build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml Directly maps to our [INFRA-CI] and [INFRA-BUILD] items - .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md - docs/ (491 files) — Official CCCL documentation CI references, CMake guides, Python compute docs, libcudacxx PTX docs - test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar) - Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml - CLAUDE.md symlink → AGENTS.md (NVIDIA's standard) cccl_upstream now mirrors full NVIDIA/cccl structure: Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks) After: 53M (+python +ci +docs +.agent +test +configs) This completes the CCCL base needed for: - [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds - [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations - [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh - Agent workflow: .agent/skills/ for consistent style and test patterns
423 lines
15 KiB
PowerShell
423 lines
15 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Build Python cuda-cccl wheels on Windows.
|
|
|
|
.DESCRIPTION
|
|
This script is the Windows analog to the Linux ../build_cuda_cccl_python.sh
|
|
script. It is responsible for building CUDA 12.x and CUDA 13.x wheels that
|
|
are then merged together into a singular cuda-cccl wheel.
|
|
|
|
A single CUDA 12.9 builder image (i.e. Docker devcontainer) is used to
|
|
build each distinct Python/MSVC combo. Much like the Linux approach, this
|
|
script detects when launched via the outer 12.9 instance, builds a `cu12`
|
|
wheel, then dispatches a inner Docker instance (Docker-out-of-Docker) to
|
|
execute this script with `-OnlyCudaMajor 13 -SkipUpload` parameters, which
|
|
yields a `cu13` build.
|
|
|
|
Upon completion of the `cu13` build, the outer 12.9 container merges both
|
|
`cu12` and `cu13` wheels into a single cuda-cccl wheel, and uploads that
|
|
via the standard CCCL CI artifact upload mechanisms.
|
|
|
|
.PARAMETER PyVersion
|
|
**Required.** The Python version to use for building the wheel, expressed
|
|
as `<major>.<minor>` (e.g. `3.11`) or a free-threaded version such as
|
|
`3.14t`.
|
|
|
|
.PARAMETER OnlyCudaMajor
|
|
Optional. Restricts the build to a single CUDA major version (`12` or `13`).
|
|
When set, only that version is built and the *merge* step is skipped.
|
|
|
|
.PARAMETER Cuda13Image
|
|
Optional. The Docker image name used for a nested build of the CUDA 13
|
|
wheel when the outer container defaults to CUDA 12.9. The default value
|
|
matches the RAPIDS dev-container image that contains the required
|
|
toolchain: `rapidsai/devcontainers:26.06-cuda13.0-cl14.44-windows2022`.
|
|
|
|
.PARAMETER SkipUpload
|
|
When set, prevents the final wheel(s) from being uploaded as a GitHub
|
|
Actions artifact even when the script detects it is running inside an
|
|
Action.
|
|
|
|
.EXAMPLE
|
|
# Build a single cuda-cccl wheel for Python 3.13 (consisting of both CUDA
|
|
# 12 and 13 versions), and, if in CI, upload the resulting wheel as an
|
|
# artifact.
|
|
.\build_cuda_cccl_python.ps1 -PyVersion 3.11
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
Param(
|
|
[Parameter(Mandatory = $true)]
|
|
[Alias("py-version")]
|
|
[ValidatePattern("^\d+\.\d+t?$")]
|
|
[string]$PyVersion,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[ValidateSet('12', '13')]
|
|
[string]$OnlyCudaMajor,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Cuda13Image = "rapidsai/devcontainers:26.06-cuda13.0-cl14.44-windows2022",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$SkipUpload
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
# Import shared helpers.
|
|
Import-Module "$PSScriptRoot/build_common.psm1"
|
|
Import-Module "$PSScriptRoot/build_common_python.psm1" -Force
|
|
|
|
# Resolve repo root from this script's location.
|
|
$RepoRoot = Resolve-Path "$PSScriptRoot/../.."
|
|
Write-Host "Repo root: $RepoRoot"
|
|
|
|
# Get the full path to the python.exe for the version we need.
|
|
Write-Host "Looking for Python version $PyVersion..."
|
|
$PythonExe = Get-Python -Version $PyVersion
|
|
Write-Host "Using Python: $PythonExe"
|
|
& $PythonExe -m pip --version
|
|
|
|
# Ensure MSVC is available.
|
|
$clPath = (Get-Command cl).Source
|
|
if (-not $clPath) {
|
|
throw "cl.exe not found in PATH. Run from a Developer PowerShell prompt."
|
|
}
|
|
Write-Host "Found cl.exe at: $clPath"
|
|
|
|
function Resolve-CudaPathForMajor {
|
|
Param(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet('12', '13')]
|
|
[string]$Major
|
|
)
|
|
$candidates = @()
|
|
Get-ChildItem Env: |
|
|
Where-Object { $_.Name -match "^CUDA_PATH_V${Major}_(\d+)$" } |
|
|
ForEach-Object {
|
|
$minor = [int]([regex]::Match(
|
|
$_.Name,
|
|
"^CUDA_PATH_V${Major}_(\d+)$"
|
|
).Groups[1].Value)
|
|
$candidates += [PSCustomObject]@{
|
|
Minor = $minor;
|
|
Path = $_.Value
|
|
}
|
|
}
|
|
|
|
if ($candidates.Count -gt 0) {
|
|
return ($candidates | Sort-Object -Property Minor -Descending |
|
|
Select-Object -First 1).Path
|
|
}
|
|
|
|
if ($env:CUDA_PATH) {
|
|
$maybe = $env:CUDA_PATH
|
|
$nvcc = Join-Path $maybe 'bin/nvcc.exe'
|
|
if (Test-Path $nvcc) {
|
|
$out = & $nvcc --version 2>&1
|
|
$text = ($out -join "`n")
|
|
if ($text -match 'release\s+(\d+)\.') {
|
|
if ($Matches[1] -eq $Major) {
|
|
return $maybe
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
# If $OnlyCudaMajor is present, it means we're being launched from a
|
|
# nested Docker container build (12.x launched a 13.x build via DooD).
|
|
if ($OnlyCudaMajor) {
|
|
$CudaMajorsToBuild = @($OnlyCudaMajor)
|
|
}
|
|
else {
|
|
$CudaMajorsToBuild = @('12', '13')
|
|
}
|
|
$DoMerge = -not [bool]$OnlyCudaMajor
|
|
|
|
# Base pip/CMake options
|
|
$pipBaseConfigArgs = @(
|
|
'-C', 'cmake.define.CMAKE_C_COMPILER=cl.exe',
|
|
'-C', 'cmake.define.CMAKE_CXX_COMPILER=cl.exe'
|
|
)
|
|
|
|
$env:CMAKE_GENERATOR = "Ninja"
|
|
|
|
# Ensure wheelhouse directories exist.
|
|
$Wheelhouse = Join-Path $RepoRoot "wheelhouse"
|
|
New-Item -ItemType Directory -Path $Wheelhouse -Force | Out-Null
|
|
${null} = New-Item -ItemType Directory -Path (Join-Path $RepoRoot 'wheelhouse_cu12') -Force
|
|
${null} = New-Item -ItemType Directory -Path (Join-Path $RepoRoot 'wheelhouse_cu13') -Force
|
|
|
|
function Invoke-Cuda13NestedBuild {
|
|
<#
|
|
.SYNOPSIS
|
|
Run the nested Docker build for CUDA 13 when we are already inside a
|
|
CUDA 12 builder image.
|
|
|
|
.DESCRIPTION
|
|
This routine launches a Docker devcontainer CUDA 13 build for the given
|
|
Python version by way of Docker-out-of-Docker (DooD) facilities.
|
|
#>
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)] [string] $Cuda13Image,
|
|
[Parameter(Mandatory)] [string] $PyVersion,
|
|
[ValidateNotNullOrEmpty()] [string] $HostWorkspace = $env:HOST_WORKSPACE,
|
|
[ValidateNotNullOrEmpty()] [string] $ContainerWorkspace = $env:CONTAINER_WORKSPACE
|
|
)
|
|
|
|
# Validate required environment variables.
|
|
if (-not $HostWorkspace) {
|
|
throw "HOST_WORKSPACE env var is not set; required for DooD " +
|
|
"nested docker mounts on Windows."
|
|
}
|
|
if (-not $ContainerWorkspace) {
|
|
throw "CONTAINER_WORKSPACE env var is not set; required for " +
|
|
"DooD nested docker mounts on Windows."
|
|
}
|
|
|
|
# Validate Docker CLI availability.
|
|
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
|
throw "docker CLI not found in the devcontainer image (required for DooD)."
|
|
}
|
|
|
|
Write-Host "Checking DooD connectivity..."
|
|
$dockerVersionOutput = & docker version 2>&1
|
|
$dockerExitCode = $LASTEXITCODE
|
|
$dockerVersionOutput | Out-Host
|
|
if ($dockerExitCode -ne 0) {
|
|
throw "DooD connectivity check failed (exit code $dockerExitCode). See Docker output above."
|
|
}
|
|
Write-Host "DooD appears to be working, continuing..."
|
|
|
|
# Detect outer-container resources so we can set sensible limits.
|
|
$os = Get-WmiObject -Class Win32_OperatingSystem
|
|
$totalGB = [math]::Floor($os.TotalVisibleMemorySize / 1MB) # KB -> GB
|
|
$procCount = [Environment]::ProcessorCount
|
|
|
|
# Leave a little head-room so the outer container doesn't starve
|
|
$memLimitGB = [math]::Max(2, [int]([math]::Floor($totalGB * 0.9)))
|
|
$cpuCount = [math]::Max(2, $procCount)
|
|
|
|
Write-Host "Launching nested Docker for CUDA 13 build using image: $Cuda13Image"
|
|
$targetFile = Join-Path $ContainerWorkspace 'ci\windows\build_cuda_cccl_python.ps1'
|
|
$dockerArgs = @(
|
|
'run', '--rm', '-i',
|
|
'--cpu-count', "$cpuCount",
|
|
'--memory', "${memLimitGB}g",
|
|
'--workdir', $ContainerWorkspace,
|
|
'--mount', "type=bind,source=$HostWorkspace,target=$ContainerWorkspace",
|
|
'--env', "py_version=$PyVersion",
|
|
'--env', "GITHUB_ACTIONS=$($env:GITHUB_ACTIONS)",
|
|
'--env', "GITHUB_RUN_ID=$($env:GITHUB_RUN_ID)",
|
|
'--env', "JOB_ID=$($env:JOB_ID)",
|
|
$Cuda13Image,
|
|
'PowerShell.exe', '-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass',
|
|
'-File', $targetFile,
|
|
'-py-version', $PyVersion,
|
|
'-OnlyCudaMajor', '13',
|
|
'-SkipUpload'
|
|
)
|
|
|
|
Write-Host ("About to invoke: docker " + ($dockerArgs -join ' '))
|
|
Invoke-Checked { & docker @dockerArgs } 'Nested CUDA 13 wheel build failed'
|
|
}
|
|
|
|
function Build-CudaCcclWheel {
|
|
<#
|
|
.SYNOPSIS
|
|
Perform the regular wheel build for a given CUDA major version.
|
|
|
|
.DESCRIPTION
|
|
This routine is used to build both CUDA 12 and CUDA 13 based wheels,
|
|
and is called from normal "outer" Docker containers, as well as the
|
|
"inner" nested ones.
|
|
#>
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)] [ValidateSet('12', '13')] [string] $Major,
|
|
[Parameter(Mandatory)] [string] $RepoRoot,
|
|
[Parameter(Mandatory)] [string] $PythonExe,
|
|
[Parameter(Mandatory)] [string[]] $PipBaseConfigArgs
|
|
)
|
|
|
|
# Resolve CUDA toolkit location for the requested major version.
|
|
$CudaPathForMajor = Resolve-CudaPathForMajor -Major $Major
|
|
if (-not $CudaPathForMajor) {
|
|
throw "CUDA Toolkit $Major not found. Ensure CUDA_PATH_V${Major}_* " +
|
|
"is set or matching toolkit is installed."
|
|
}
|
|
|
|
$NvccForMajor = Join-Path $CudaPathForMajor 'bin/nvcc.exe'
|
|
if (-not (Test-Path $NvccForMajor)) {
|
|
throw "nvcc not found at $NvccForMajor"
|
|
}
|
|
|
|
# Convert Windows paths to Unix-style for CMake
|
|
$NvccUnix = Convert-ToUnixPath $NvccForMajor
|
|
$CudaUnix = Convert-ToUnixPath $CudaPathForMajor
|
|
|
|
# Build the pip configuration arguments that inject the CUDA toolchain.
|
|
$pipConfigArgs = $PipBaseConfigArgs + @(
|
|
'-C', "cmake.define.CMAKE_CUDA_COMPILER=$NvccUnix",
|
|
'-C', "cmake.define.CUDAToolkit_ROOT=$CudaUnix"
|
|
)
|
|
|
|
$extra = "cu$Major"
|
|
# Use separate output directories for 12 vs 13.
|
|
$outDir = Join-Path $RepoRoot "wheelhouse_$extra"
|
|
|
|
Write-Host "Building cuda-cccl wheel for CUDA $Major at $CudaPathForMajor..."
|
|
|
|
# Run pip wheel to build the wheel.
|
|
$pythonArgs = @(
|
|
'-m', 'pip', 'wheel',
|
|
'-w', $outDir,
|
|
".[${extra}]",
|
|
'-v'
|
|
) + $pipConfigArgs
|
|
|
|
Write-Host ("python " + ($pythonArgs -join ' '))
|
|
Invoke-Checked { & $PythonExe @pythonArgs } "Wheel build failed for CUDA $Major"
|
|
|
|
# Normalise the wheel filename (append .cu12/.cu13) and prune duplicates.
|
|
$builtWheel = Get-OnePathMatch -Path $outDir `
|
|
-Pattern '^cuda_cccl-.*\.whl' `
|
|
-File
|
|
if (-not $builtWheel) {
|
|
throw "Failed to locate built wheel in $outDir for CUDA $Major"
|
|
}
|
|
|
|
$builtName = [System.IO.Path]::GetFileName($builtWheel)
|
|
if ($builtName -notmatch ".cu$Major\.whl$") {
|
|
$newName = ([System.IO.Path]::GetFileNameWithoutExtension($builtName)) `
|
|
+ ".cu$Major.whl"
|
|
Write-Host "Renaming wheel to: $newName"
|
|
Rename-Item -Path $builtWheel -NewName $newName -Force
|
|
}
|
|
|
|
# Remove any stray wheels that lack the .cuXX suffix.
|
|
Get-ChildItem -Path $outDir -Filter 'cuda_cccl-*.whl' |
|
|
Where-Object { $_.Name -notmatch "\.cu$Major\.whl$" } |
|
|
ForEach-Object {
|
|
Write-Host "Removing duplicate wheel: $($_.FullName)"
|
|
Remove-Item -Force $_.FullName
|
|
}
|
|
}
|
|
|
|
# Main build entry code.
|
|
Push-Location (Join-Path $RepoRoot 'python/cuda_cccl')
|
|
try {
|
|
foreach ($major in $CudaMajorsToBuild) {
|
|
|
|
# Nested Docker build for CUDA 13 for when we are currently inside a
|
|
# CUDA 12 image.
|
|
if (-not $OnlyCudaMajor -and $major -eq '13' -and $Cuda13Image) {
|
|
Invoke-Cuda13NestedBuild `
|
|
-Cuda13Image $Cuda13Image `
|
|
-PyVersion $PyVersion
|
|
|
|
continue
|
|
}
|
|
|
|
# Perform a normal build for the current major version. This may
|
|
# be invoked from either an "outer" or inner "nested" image.
|
|
Build-CudaCcclWheel `
|
|
-Major $major `
|
|
-RepoRoot $RepoRoot `
|
|
-PythonExe $PythonExe `
|
|
-PipBaseConfigArgs $pipBaseConfigArgs
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
|
|
|
|
# Merge the two major-version wheels (if both were built). This will fail if
|
|
# either wheel can't be found. This only runs on the outer (non-nested)
|
|
# container image.
|
|
if ($DoMerge) {
|
|
|
|
$Cu12Wheel = Get-OnePathMatch `
|
|
-Path (Join-Path $RepoRoot 'wheelhouse_cu12') `
|
|
-Pattern '^cuda_cccl-.*\.cu12\.whl' `
|
|
-File
|
|
|
|
$Cu13Wheel = Get-OnePathMatch `
|
|
-Path (Join-Path $RepoRoot 'wheelhouse_cu13') `
|
|
-Pattern '^cuda_cccl-.*\.cu13\.whl' `
|
|
-File
|
|
|
|
Write-Host "Found CUDA 12 wheel: $Cu12Wheel"
|
|
Write-Host "Found CUDA 13 wheel: $Cu13Wheel"
|
|
|
|
Write-Host 'Merging CUDA wheels...'
|
|
Invoke-Checked { & $PythonExe -m pip install wheel | Write-Host } 'Failed to install wheel for merging'
|
|
|
|
$WheelhouseMerged = Join-Path $RepoRoot 'wheelhouse_merged'
|
|
${null} = New-Item -ItemType Directory -Path $WheelhouseMerged -Force
|
|
|
|
$mergePy = Join-Path $RepoRoot 'python/cuda_cccl/merge_cuda_wheels.py'
|
|
Invoke-Checked { & $PythonExe $mergePy $Cu12Wheel $Cu13Wheel --output-dir $WheelhouseMerged } 'Merging wheels failed'
|
|
|
|
# Clean up the per-major directories and move the merged wheel into the
|
|
# final location.
|
|
Get-ChildItem $Wheelhouse -Filter '*.whl' |
|
|
ForEach-Object {
|
|
Remove-Item -Force $_.FullName
|
|
}
|
|
$MergedWheel = Get-OnePathMatch `
|
|
-Path $WheelhouseMerged `
|
|
-Pattern '^cuda_cccl-.*\.whl' `
|
|
-File
|
|
Move-Item -Force $MergedWheel $Wheelhouse
|
|
|
|
Remove-Item $WheelhouseMerged -Recurse -Force -ErrorAction SilentlyContinue
|
|
Remove-Item (Join-Path $RepoRoot 'wheelhouse_cu12') `
|
|
-Recurse -Force -ErrorAction SilentlyContinue
|
|
Remove-Item (Join-Path $RepoRoot 'wheelhouse_cu13') `
|
|
-Recurse -Force -ErrorAction SilentlyContinue
|
|
|
|
Write-Host 'Final wheels in wheelhouse:'
|
|
Get-ChildItem $Wheelhouse -Filter '*.whl' |
|
|
ForEach-Object {
|
|
Write-Host " - $($_.Name)"
|
|
}
|
|
}
|
|
|
|
# If it turns out we need delvewheel, we'd handle it here, after the merging
|
|
# of wheels. The two DLLs that seem like they might be problematic are
|
|
# msvc140p.dll, and dbghelp.dll. The former comes from llvmlite, upon which
|
|
# we depend. Dbghelp.dll ships in C:\Windows\System32, but that will often
|
|
# be a much older version compared to the one used by Visual Studio. We only
|
|
# use one symbol from Dbghelp.dll: UnDecorateSymbolName, which is used by
|
|
# nvrtc. If we encounter weird issues with c.parallel jit compilation and
|
|
# nvrtc in the wild on Windows, an out-of-date Dbghelp.dll could possibly be
|
|
# the culprit.
|
|
#
|
|
# For now, though, it doesn't appear to be necessary.
|
|
|
|
# Optionally upload the wheel artifact.
|
|
if ($env:GITHUB_ACTIONS -and -not $SkipUpload) {
|
|
Push-Location $RepoRoot
|
|
try {
|
|
Write-Host 'GITHUB_ACTIONS detected; uploading wheel artifact'
|
|
$wheelArtifactName = (& bash -lc "ci/util/workflow/get_wheel_artifact_name.sh").Trim()
|
|
if (-not $wheelArtifactName) {
|
|
throw 'Failed to resolve wheel artifact name'
|
|
}
|
|
Write-Host "Wheel artifact name: $wheelArtifactName"
|
|
|
|
$uploadCmd = "ci/util/artifacts/upload.sh $wheelArtifactName 'wheelhouse/.*'"
|
|
Invoke-Checked { & bash -lc $uploadCmd } 'Wheel artifact upload failed'
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|