Files
project_6/cccl_upstream/ci/windows/build_cuda_cccl_python.ps1
muh-bot 2a7ca101d7 feat(cccl): integrate missing CCCL directories — python/, ci/, .agent/, docs/, test/
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
2026-08-07 02:34:33 +00:00

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
}
}