From bc729ea00a5749cc60a4492dabd908b0a85a574d Mon Sep 17 00:00:00 2001 From: Thomas Arcila <134677+tarcila@users.noreply.github.com> Date: Tue, 3 Feb 2026 07:35:39 -0500 Subject: [PATCH] Try and improve CUDA setup Also move TSD CUDA build from visrtx to tsd yml --- .github/actions/build-anari-sdk/action.yml | 53 ++ .github/actions/setup-cuda/action.yml | 133 ++++ .github/actions/setup-cuda/components.json | 73 ++ .../setup-cuda/scripts/install-cuda.ps1 | 316 ++++++++ .../setup-cuda/scripts/install-cuda.sh | 246 ++++++ .github/scripts/cuda-version-manager.py | 699 ++++++++++++++++++ .github/workflows/build-tsd.yml | 118 +++ .github/workflows/build-visrtx.yml | 69 +- .github/workflows/tsd_ci.yml | 57 -- .../cmake/build_deps/superbuild_macros.cmake | 4 +- tsd/cmake/build_deps/superbuild_macros.cmake | 4 +- 11 files changed, 1670 insertions(+), 102 deletions(-) create mode 100644 .github/actions/build-anari-sdk/action.yml create mode 100644 .github/actions/setup-cuda/action.yml create mode 100644 .github/actions/setup-cuda/components.json create mode 100644 .github/actions/setup-cuda/scripts/install-cuda.ps1 create mode 100755 .github/actions/setup-cuda/scripts/install-cuda.sh create mode 100755 .github/scripts/cuda-version-manager.py create mode 100644 .github/workflows/build-tsd.yml delete mode 100644 .github/workflows/tsd_ci.yml diff --git a/.github/actions/build-anari-sdk/action.yml b/.github/actions/build-anari-sdk/action.yml new file mode 100644 index 000000000..fd8781edc --- /dev/null +++ b/.github/actions/build-anari-sdk/action.yml @@ -0,0 +1,53 @@ +name: 'Build ANARI-SDK' +description: 'Build and cache ANARI-SDK dependency' +inputs: + config: + description: 'Build configuration (Release, Debug, etc.)' + required: true + install-prefix: + description: 'Installation prefix for ANARI-SDK' + required: true + build-deps-path: + description: 'Path to the build_deps directory containing CMakeLists.txt' + required: false + default: 'tsd/cmake/build_deps' + generator: + description: 'CMake generator to use' + required: false + default: '' +outputs: + cache-hit: + description: 'Whether the cache was hit' + value: ${{ steps.cache-anari.outputs.cache-hit }} +runs: + using: 'composite' + steps: + - name: Cache ANARI-SDK + uses: actions/cache@v4 + id: cache-anari + with: + path: ${{ inputs.install-prefix }} + key: anari-sdk-${{ runner.os }}-${{ inputs.config }}-${{ inputs.generator || 'default' }}-${{ hashFiles('**/build_deps/CMakeLists.txt', '**/build_deps/superbuild_macros.cmake') }} + + - name: Configure ANARI-SDK CMake (with generator) + if: steps.cache-anari.outputs.cache-hit != 'true' && inputs.generator != '' + shell: bash + run: | + cmake -LA -G "${{ inputs.generator }}" -B "${{ github.workspace }}/anari_deps_build" \ + -DCMAKE_BUILD_TYPE=${{ inputs.config }} \ + -DCMAKE_INSTALL_PREFIX="${{ inputs.install-prefix }}" \ + "${{ github.workspace }}/${{ inputs.build-deps-path }}" + + - name: Configure ANARI-SDK CMake (default generator) + if: steps.cache-anari.outputs.cache-hit != 'true' && inputs.generator == '' + shell: bash + run: | + cmake -LA -B "${{ github.workspace }}/anari_deps_build" \ + -DCMAKE_BUILD_TYPE=${{ inputs.config }} \ + -DCMAKE_INSTALL_PREFIX="${{ inputs.install-prefix }}" \ + "${{ github.workspace }}/${{ inputs.build-deps-path }}" + + - name: Build + install ANARI-SDK + if: steps.cache-anari.outputs.cache-hit != 'true' + shell: bash + run: cmake --build "${{ github.workspace }}/anari_deps_build" --config ${{ inputs.config }} diff --git a/.github/actions/setup-cuda/action.yml b/.github/actions/setup-cuda/action.yml new file mode 100644 index 000000000..5f86e2ba7 --- /dev/null +++ b/.github/actions/setup-cuda/action.yml @@ -0,0 +1,133 @@ +name: 'Setup CUDA Toolkit' +description: 'Install CUDA toolkit using redistributable archives with checksum verification and caching' +inputs: + cuda-version: + description: 'CUDA version to install (e.g., 12.4.1, 13.0.2)' + required: true +outputs: + cuda-path: + description: 'Path to CUDA installation' + value: ${{ steps.set-env.outputs.cuda-path }} + cache-hit: + description: 'Whether cache was hit' + value: ${{ steps.cache-cuda.outputs.cache-hit }} +runs: + using: 'composite' + steps: + # Parse version components + - name: Parse CUDA version + id: parse-version + shell: bash + run: | + CUDA_VERSION="${{ inputs.cuda-version }}" + CUDA_MAJOR=$(echo $CUDA_VERSION | cut -d. -f1) + CUDA_MINOR=$(echo $CUDA_VERSION | cut -d. -f2) + CUDA_PATCH=$(echo $CUDA_VERSION | cut -d. -f3) + echo "major=$CUDA_MAJOR" >> $GITHUB_OUTPUT + echo "minor=$CUDA_MINOR" >> $GITHUB_OUTPUT + echo "patch=$CUDA_PATCH" >> $GITHUB_OUTPUT + echo "major-minor=${CUDA_MAJOR}.${CUDA_MINOR}" >> $GITHUB_OUTPUT + + # Cache the CUDA installation + # Install directly to cache directory - no sudo needed + - name: Cache CUDA installation (Linux) + if: runner.os == 'Linux' + uses: actions/cache@v4 + id: cache-cuda-linux + with: + path: ~/cuda-${{ steps.parse-version.outputs.major-minor }} + key: cuda-Linux-${{ inputs.cuda-version }}-redist-v4 + + - name: Cache CUDA installation (Windows) + if: runner.os == 'Windows' + uses: actions/cache@v4 + id: cache-cuda-windows + with: + path: C:\cuda-${{ steps.parse-version.outputs.major-minor }} + key: cuda-Windows-${{ inputs.cuda-version }}-redist-v2 + + # Set cache-hit output for use in conditions + - name: Set cache status + id: cache-cuda + shell: bash + run: | + if [ "${{ runner.os }}" == "Linux" ]; then + echo "cache-hit=${{ steps.cache-cuda-linux.outputs.cache-hit }}" >> $GITHUB_OUTPUT + else + echo "cache-hit=${{ steps.cache-cuda-windows.outputs.cache-hit }}" >> $GITHUB_OUTPUT + fi + + # Linux installation via redistributables (cache miss) + - name: Install CUDA (Linux) + if: runner.os == 'Linux' && steps.cache-cuda-linux.outputs.cache-hit != 'true' + shell: bash + run: | + CUDA_VERSION="${{ inputs.cuda-version }}" + CUDA_MAJOR_MINOR="${{ steps.parse-version.outputs.major-minor }}" + INSTALL_DIR="$HOME/cuda-${CUDA_MAJOR_MINOR}" + ACTION_DIR="${{ github.action_path }}" + + echo "Installing CUDA $CUDA_VERSION using redistributables" + + # Make install script executable and run it + chmod +x "$ACTION_DIR/scripts/install-cuda.sh" + "$ACTION_DIR/scripts/install-cuda.sh" \ + -v "$CUDA_VERSION" \ + -p "$INSTALL_DIR" \ + -c "$ACTION_DIR/components.json" + + # Windows installation via redistributables (cache miss) + - name: Install CUDA (Windows) + if: runner.os == 'Windows' && steps.cache-cuda-windows.outputs.cache-hit != 'true' + shell: pwsh + run: | + $cudaVersion = "${{ inputs.cuda-version }}" + $cudaMajorMinor = "${{ steps.parse-version.outputs.major-minor }}" + $installPath = "C:\cuda-$cudaMajorMinor" + $actionDir = "${{ github.action_path }}" + + Write-Host "Installing CUDA $cudaVersion using redistributables" + + & "$actionDir\scripts\install-cuda.ps1" ` + -CudaVersion $cudaVersion ` + -InstallPath $installPath ` + -ComponentsFile "$actionDir\components.json" + + # Set environment variables + - name: Set CUDA environment + id: set-env + shell: bash + run: | + CUDA_MAJOR_MINOR="${{ steps.parse-version.outputs.major-minor }}" + + if [ "${{ runner.os }}" == "Linux" ]; then + CUDA_PATH="$HOME/cuda-${CUDA_MAJOR_MINOR}" + echo "CUDA_PATH=${CUDA_PATH}" >> $GITHUB_ENV + echo "${CUDA_PATH}/bin" >> $GITHUB_PATH + echo "LD_LIBRARY_PATH=${CUDA_PATH}/lib64:${LD_LIBRARY_PATH:-}" >> $GITHUB_ENV + else + CUDA_PATH="C:\\cuda-${CUDA_MAJOR_MINOR}" + echo "CUDA_PATH=${CUDA_PATH}" >> $GITHUB_ENV + echo "${CUDA_PATH}\\bin" >> $GITHUB_PATH + # Required for CMake to find CUDA on Windows with Visual Studio + echo "CUDA_TOOLKIT_ROOT_DIR=${CUDA_PATH}" >> $GITHUB_ENV + echo "CudaToolkitDir=${CUDA_PATH}" >> $GITHUB_ENV + # CUDACXX tells CMake which CUDA compiler to use, bypassing VS toolset detection + echo "CUDACXX=${CUDA_PATH}\\bin\\nvcc.exe" >> $GITHUB_ENV + fi + + echo "cuda-path=${CUDA_PATH}" >> $GITHUB_OUTPUT + echo "CUDA environment configured: ${CUDA_PATH}" + + # Verify installation + - name: Verify CUDA installation + shell: bash + run: | + echo "Verifying CUDA installation..." + if command -v nvcc &> /dev/null; then + nvcc --version + else + echo "Warning: nvcc not found in PATH, but installation may still be valid" + echo "CUDA_PATH: $CUDA_PATH" + ls -la "$CUDA_PATH/bin" 2>/dev/null || dir "$CUDA_PATH\\bin" 2>/dev/null || true + fi diff --git a/.github/actions/setup-cuda/components.json b/.github/actions/setup-cuda/components.json new file mode 100644 index 000000000..00edc5559 --- /dev/null +++ b/.github/actions/setup-cuda/components.json @@ -0,0 +1,73 @@ +{ + "components": { + "cuda_nvcc": { + "required": true, + "description": "NVIDIA CUDA Compiler", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ] + }, + "cuda_cudart": { + "required": true, + "description": "CUDA Runtime", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ] + }, + "cuda_crt": { + "required": false, + "description": "C Runtime headers - only in CUDA 13+, bundled in cuda_cudart for 12.x", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ], + "min_version": "13.0.0" + }, + "libnvvm": { + "required": false, + "description": "NVVM library (cicc compiler) - only in CUDA 13+, bundled in cuda_nvcc for 12.x", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ], + "min_version": "13.0.0" + }, + "cuda_cccl": { + "required": true, + "description": "CUDA C++ Core Libraries (Thrust/CUB/libcudacxx)", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ] + }, + "cuda_nvml_dev": { + "required": true, + "description": "NVML development headers", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ] + }, + "libcurand": { + "required": true, + "description": "Random number generation library", + "platforms": [ + "linux-x86_64", + "windows-x86_64" + ] + }, + "visual_studio_integration": { + "required": true, + "description": "Visual Studio build integration (MSBuild rules)", + "platforms": [ + "windows-x86_64" + ] + } + }, + "supported_versions": [ + "12.4.1", + "13.0.2" + ] +} \ No newline at end of file diff --git a/.github/actions/setup-cuda/scripts/install-cuda.ps1 b/.github/actions/setup-cuda/scripts/install-cuda.ps1 new file mode 100644 index 000000000..976cc4db4 --- /dev/null +++ b/.github/actions/setup-cuda/scripts/install-cuda.ps1 @@ -0,0 +1,316 @@ +# CUDA Installation Script for Windows using Redistributables +# Downloads and installs CUDA components from NVIDIA redistributable archives +# with SHA256 checksum verification. + +param( + [Parameter(Mandatory=$true)] + [string]$CudaVersion, + + [Parameter(Mandatory=$true)] + [string]$InstallPath, + + [Parameter(Mandatory=$false)] + [string]$ComponentsFile +) + +$ErrorActionPreference = "Stop" + +# Base URL for CUDA redistributables +$redistBaseUrl = "https://developer.download.nvidia.com/compute/cuda/redist" + +# Platform identifier for Windows +$platform = "windows-x86_64" + +# Required components (can be overridden via components.json) +$defaultComponents = @( + "cuda_nvcc", + "cuda_cudart", + "cuda_cccl", + "cuda_nvml_dev", + "libcurand", + "visual_studio_integration" +) + +function Compare-Version { + param( + [string]$Version1, + [string]$Version2 + ) + # Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2 + $v1Parts = $Version1.Split('.') | ForEach-Object { [int]$_ } + $v2Parts = $Version2.Split('.') | ForEach-Object { [int]$_ } + + for ($i = 0; $i -lt [Math]::Max($v1Parts.Count, $v2Parts.Count); $i++) { + $p1 = if ($i -lt $v1Parts.Count) { $v1Parts[$i] } else { 0 } + $p2 = if ($i -lt $v2Parts.Count) { $v2Parts[$i] } else { 0 } + if ($p1 -lt $p2) { return -1 } + if ($p1 -gt $p2) { return 1 } + } + return 0 +} + +function Get-RequiredComponents { + param( + [string]$ComponentsFile, + [string]$CudaVersion + ) + + if ($ComponentsFile -and (Test-Path $ComponentsFile)) { + $config = Get-Content $ComponentsFile | ConvertFrom-Json + $components = @() + foreach ($name in $config.components.PSObject.Properties.Name) { + $comp = $config.components.$name + + # Check if this component is for our platform + if ($comp.platforms -and $comp.platforms -notcontains $platform) { + continue + } + + # Check min_version requirement + if ($comp.min_version) { + if ((Compare-Version $CudaVersion $comp.min_version) -lt 0) { + Write-Host "Skipping $name (requires CUDA >= $($comp.min_version))" + continue + } + } + + # Include if required OR if min_version is satisfied (for optional components) + if ($comp.required -or $comp.min_version) { + $components += $name + } + } + return $components + } + return $defaultComponents +} + +function Download-File { + param( + [string]$Url, + [string]$OutFile, + [string]$ExpectedHash + ) + + Write-Host "Downloading: $Url" + # Use Invoke-WebRequest (System.Net.WebClient is deprecated in modern .NET) + Invoke-WebRequest -Uri $Url -OutFile $OutFile -UseBasicParsing + + if ($ExpectedHash) { + $actualHash = (Get-FileHash -Path $OutFile -Algorithm SHA256).Hash.ToLower() + if ($actualHash -ne $ExpectedHash.ToLower()) { + Remove-Item $OutFile -ErrorAction SilentlyContinue + throw "Checksum mismatch for $OutFile`nExpected: $ExpectedHash`nActual: $actualHash" + } + Write-Host " Checksum verified: $actualHash" + } +} + +function Install-CudaComponent { + param( + [string]$ComponentName, + [object]$ComponentInfo, + [string]$InstallPath, + [string]$TempDir + ) + + $platformInfo = $ComponentInfo.$platform + if (-not $platformInfo) { + Write-Host " Component $ComponentName not available for $platform, skipping" + return $false + } + + $relativePath = $platformInfo.relative_path + $sha256 = $platformInfo.sha256 + $downloadUrl = "$redistBaseUrl/$relativePath" + $zipFile = Join-Path $TempDir "$ComponentName.zip" + + Write-Host "Installing component: $ComponentName" + Download-File -Url $downloadUrl -OutFile $zipFile -ExpectedHash $sha256 + + # Extract to temp directory first + $extractDir = Join-Path $TempDir "${ComponentName}_extract" + Write-Host " Extracting to: $extractDir" + Expand-Archive -Path $zipFile -DestinationPath $extractDir -Force + + # Find the actual content directory (usually named like cuda_nvcc-version) + $contentDirs = Get-ChildItem -Path $extractDir -Directory + if ($contentDirs.Count -eq 1) { + $sourceDir = $contentDirs[0].FullName + } else { + $sourceDir = $extractDir + } + + # Copy contents to install path, merging directories + Write-Host " Merging to: $InstallPath" + Copy-ItemWithMerge -Source $sourceDir -Destination $InstallPath + + # Cleanup + Remove-Item $zipFile -Force + Remove-Item $extractDir -Recurse -Force + + return $true +} + +function Copy-ItemWithMerge { + param( + [string]$Source, + [string]$Destination + ) + + if (-not (Test-Path $Destination)) { + New-Item -ItemType Directory -Path $Destination -Force | Out-Null + } + + Get-ChildItem -Path $Source | ForEach-Object { + $destPath = Join-Path $Destination $_.Name + if ($_.PSIsContainer) { + Copy-ItemWithMerge -Source $_.FullName -Destination $destPath + } else { + Copy-Item -Path $_.FullName -Destination $destPath -Force + } + } +} + +function Install-VisualStudioIntegration { + param( + [string]$CudaPath + ) + + # VS integration files need to be copied to MSBuild directories + # The redistributable package structure varies by CUDA version, so check multiple paths + $potentialPaths = @( + # CUDA 12.x redistributable structure + (Join-Path $CudaPath "visual_studio_integration\CUDAVisualStudioIntegration\extras\visual_studio_integration\MSBuildExtensions"), + (Join-Path $CudaPath "extras\visual_studio_integration\MSBuildExtensions"), + # Some versions have it directly under visual_studio_integration + (Join-Path $CudaPath "visual_studio_integration\MSBuildExtensions"), + # CUDA toolkit installer structure + (Join-Path $CudaPath "extras\visual_studio_integration\MSBuild") + ) + + $vsIntegrationSource = $null + foreach ($path in $potentialPaths) { + Write-Host " Checking: $path" + if (Test-Path $path) { + $vsIntegrationSource = $path + Write-Host " Found VS integration at: $path" + break + } + } + + if (-not $vsIntegrationSource) { + # Search recursively for MSBuild extension files as fallback + Write-Host " Searching recursively for CUDA MSBuild extensions..." + $cudaProps = Get-ChildItem -Path $CudaPath -Recurse -Filter "CUDA *.props" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($cudaProps) { + $vsIntegrationSource = $cudaProps.DirectoryName + Write-Host " Found VS integration at: $vsIntegrationSource" + } + } + + if ($vsIntegrationSource) { + Write-Host "Installing Visual Studio integration from: $vsIntegrationSource" + + $installed = $false + + # Find VS installation paths using vswhere + $vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + if (Test-Path $vswherePath) { + $vsInstalls = & $vswherePath -products * -requires Microsoft.Component.MSBuild -property installationPath + foreach ($vsInstall in $vsInstalls) { + # Try both v170 (VS 2022) and v160 (VS 2019) paths + foreach ($vcVersion in @("v170", "v160")) { + $msbuildExtPath = Join-Path $vsInstall "MSBuild\Microsoft\VC\$vcVersion\BuildCustomizations" + if (Test-Path $msbuildExtPath) { + Write-Host " Copying to: $msbuildExtPath" + Copy-Item -Path "$vsIntegrationSource\*" -Destination $msbuildExtPath -Recurse -Force + $installed = $true + } + } + } + } + + # Also try common MSBuild paths for GitHub Actions runners + $commonPaths = @( + "${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\BuildCustomizations", + "${env:ProgramFiles}\Microsoft Visual Studio\2022\Professional\MSBuild\Microsoft\VC\v170\BuildCustomizations", + "${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\BuildCustomizations", + "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\BuildCustomizations" + ) + + foreach ($msbuildPath in $commonPaths) { + if ((Test-Path $msbuildPath) -and -not $installed) { + Write-Host " Copying to: $msbuildPath" + Copy-Item -Path "$vsIntegrationSource\*" -Destination $msbuildPath -Recurse -Force + $installed = $true + } + } + + if ($installed) { + Write-Host " Visual Studio integration installed successfully" + } else { + Write-Host "Warning: Could not find Visual Studio MSBuild directory to install CUDA integration" + } + } else { + Write-Host "Warning: Visual Studio integration files not found" + Write-Host " Searched in: $($potentialPaths -join ', ')" + Write-Host " Listing contents of $CudaPath for debugging:" + Get-ChildItem -Path $CudaPath -Depth 2 | ForEach-Object { Write-Host " $($_.FullName)" } + } +} + +# Main execution +Write-Host "==========================================" +Write-Host "CUDA Redistributable Installer for Windows" +Write-Host "==========================================" +Write-Host "CUDA Version: $CudaVersion" +Write-Host "Install Path: $InstallPath" +Write-Host "Platform: $platform" + +# Download redistrib manifest +$manifestUrl = "$redistBaseUrl/redistrib_$CudaVersion.json" +$tempDir = Join-Path $env:TEMP "cuda_install_$([guid]::NewGuid().ToString('N').Substring(0,8))" +New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + +$manifestFile = Join-Path $tempDir "redistrib.json" +Write-Host "`nDownloading manifest: $manifestUrl" +Download-File -Url $manifestUrl -OutFile $manifestFile + +$manifest = Get-Content $manifestFile | ConvertFrom-Json + +# Create install directory +if (-not (Test-Path $InstallPath)) { + New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null +} + +# Get required components +$components = Get-RequiredComponents -ComponentsFile $ComponentsFile -CudaVersion $CudaVersion +Write-Host "`nComponents to install: $($components -join ', ')" + +# Install each component +$installedCount = 0 +foreach ($componentName in $components) { + $componentInfo = $manifest.$componentName + if ($componentInfo) { + $result = Install-CudaComponent -ComponentName $componentName ` + -ComponentInfo $componentInfo ` + -InstallPath $InstallPath ` + -TempDir $tempDir + if ($result) { + $installedCount++ + } + } else { + Write-Host "Warning: Component '$componentName' not found in manifest" + } +} + +# Install VS integration +Install-VisualStudioIntegration -CudaPath $InstallPath + +# Cleanup temp directory +Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + +Write-Host "`n==========================================" +Write-Host "Installation complete!" +Write-Host "Installed $installedCount components to: $InstallPath" +Write-Host "==========================================" diff --git a/.github/actions/setup-cuda/scripts/install-cuda.sh b/.github/actions/setup-cuda/scripts/install-cuda.sh new file mode 100755 index 000000000..19a937ea6 --- /dev/null +++ b/.github/actions/setup-cuda/scripts/install-cuda.sh @@ -0,0 +1,246 @@ +#!/bin/bash +# CUDA Installation Script for Linux using Redistributables +# Downloads and installs CUDA components from NVIDIA redistributable archives +# with SHA256 checksum verification. + +set -euo pipefail + +usage() { + echo "Usage: $0 -v VERSION -p INSTALL_PATH [-c COMPONENTS_FILE]" + echo "" + echo "Options:" + echo " -v VERSION CUDA version (e.g., 12.4.1, 13.0.2)" + echo " -p INSTALL_PATH Installation directory (e.g., ~/cuda-12.4)" + echo " -c COMPONENTS Path to components.json (optional)" + exit 1 +} + +# Parse arguments +CUDA_VERSION="" +INSTALL_PATH="" +COMPONENTS_FILE="" + +while getopts "v:p:c:h" opt; do + case $opt in + v) CUDA_VERSION="$OPTARG" ;; + p) INSTALL_PATH="$OPTARG" ;; + c) COMPONENTS_FILE="$OPTARG" ;; + h) usage ;; + *) usage ;; + esac +done + +if [[ -z "$CUDA_VERSION" || -z "$INSTALL_PATH" ]]; then + echo "Error: VERSION and INSTALL_PATH are required" + usage +fi + +# Configuration +REDIST_BASE_URL="https://developer.download.nvidia.com/compute/cuda/redist" +PLATFORM="linux-x86_64" +TEMP_DIR=$(mktemp -d) + +# Default required components +DEFAULT_COMPONENTS=( + "cuda_nvcc" + "cuda_cudart" + "cuda_cccl" + "cuda_nvml_dev" + "libcurand" +) + +# Cleanup on exit +cleanup() { + rm -rf "$TEMP_DIR" +} +trap cleanup EXIT + +# Compare semantic versions: returns 0 if v1 >= v2, 1 otherwise +version_gte() { + local v1="$1" + local v2="$2" + + # Split into arrays + IFS='.' read -ra v1_parts <<< "$v1" + IFS='.' read -ra v2_parts <<< "$v2" + + local max_len=${#v1_parts[@]} + [[ ${#v2_parts[@]} -gt $max_len ]] && max_len=${#v2_parts[@]} + + for ((i = 0; i < max_len; i++)); do + local p1=${v1_parts[i]:-0} + local p2=${v2_parts[i]:-0} + if ((p1 > p2)); then + return 0 + elif ((p1 < p2)); then + return 1 + fi + done + return 0 +} + +# Get required components from config file or use defaults +get_required_components() { + if [[ -n "$COMPONENTS_FILE" && -f "$COMPONENTS_FILE" ]]; then + if ! command -v jq &> /dev/null; then + echo "Warning: jq not available, using default components" >&2 + printf '%s\n' "${DEFAULT_COMPONENTS[@]}" + return + fi + + # Process each component from the config + jq -r '.components | to_entries[] | "\(.key)|\(.value.required)|\(.value.min_version // "")|\(.value.platforms // [] | join(","))"' "$COMPONENTS_FILE" | \ + while IFS='|' read -r name required min_version platforms; do + # Skip if not for our platform + if [[ -n "$platforms" && "$platforms" != *"$PLATFORM"* ]]; then + continue + fi + + # Check min_version requirement + if [[ -n "$min_version" ]]; then + if ! version_gte "$CUDA_VERSION" "$min_version"; then + echo "Skipping $name (requires CUDA >= $min_version)" >&2 + continue + fi + fi + + # Include if required OR if min_version is satisfied (for optional components) + if [[ "$required" == "true" ]] || [[ -n "$min_version" ]]; then + echo "$name" + fi + done + else + printf '%s\n' "${DEFAULT_COMPONENTS[@]}" + fi +} + +# Download file with checksum verification +download_file() { + local url="$1" + local output="$2" + local expected_hash="${3:-}" + + echo "Downloading: $url" + if command -v curl &> /dev/null; then + curl -fsSL "$url" -o "$output" + else + wget -q "$url" -O "$output" + fi + + if [[ -n "$expected_hash" ]]; then + local actual_hash + actual_hash=$(sha256sum "$output" | cut -d' ' -f1) + if [[ "${actual_hash,,}" != "${expected_hash,,}" ]]; then + rm -f "$output" + echo "Error: Checksum mismatch for $output" + echo " Expected: $expected_hash" + echo " Actual: $actual_hash" + exit 1 + fi + echo " Checksum verified: $actual_hash" + fi +} + +# Install a single component +install_component() { + local component_name="$1" + local manifest_file="$2" + + # Extract component info using jq + local relative_path sha256 + + if ! command -v jq &> /dev/null; then + echo "Error: jq is required for parsing JSON manifests" + exit 1 + fi + + # Check if component exists in manifest + if ! jq -e ".$component_name" "$manifest_file" > /dev/null 2>&1; then + echo "Warning: Component '$component_name' not found in manifest" + return 1 + fi + + # Check if component is available for our platform + if ! jq -e ".$component_name.\"$PLATFORM\"" "$manifest_file" > /dev/null 2>&1; then + echo " Component $component_name not available for $PLATFORM, skipping" + return 0 + fi + + relative_path=$(jq -r ".$component_name.\"$PLATFORM\".relative_path" "$manifest_file") + sha256=$(jq -r ".$component_name.\"$PLATFORM\".sha256" "$manifest_file") + + local download_url="$REDIST_BASE_URL/$relative_path" + local archive_file="$TEMP_DIR/$component_name.tar.xz" + local extract_dir="$TEMP_DIR/${component_name}_extract" + + echo "Installing component: $component_name" + download_file "$download_url" "$archive_file" "$sha256" + + # Extract archive + echo " Extracting..." + mkdir -p "$extract_dir" + tar -xJf "$archive_file" -C "$extract_dir" + + # Find the content directory (usually named like cuda_nvcc-version) + local content_dir + content_dir=$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -1) + if [[ -z "$content_dir" ]]; then + content_dir="$extract_dir" + fi + + # Merge contents to install path + echo " Merging to: $INSTALL_PATH" + cp -a "$content_dir"/* "$INSTALL_PATH"/ + + # Cleanup + rm -f "$archive_file" + rm -rf "$extract_dir" + + return 0 +} + +# Main execution +echo "==========================================" +echo "CUDA Redistributable Installer for Linux" +echo "==========================================" +echo "CUDA Version: $CUDA_VERSION" +echo "Install Path: $INSTALL_PATH" +echo "Platform: $PLATFORM" + +# Download redistrib manifest +MANIFEST_URL="$REDIST_BASE_URL/redistrib_$CUDA_VERSION.json" +MANIFEST_FILE="$TEMP_DIR/redistrib.json" + +echo "" +echo "Downloading manifest: $MANIFEST_URL" +download_file "$MANIFEST_URL" "$MANIFEST_FILE" + +# Create install directory +mkdir -p "$INSTALL_PATH" + +# Get components to install +echo "" +echo "Determining components to install..." +mapfile -t COMPONENTS < <(get_required_components) +echo "Components to install: ${COMPONENTS[*]}" + +# Install each component +INSTALLED_COUNT=0 +for component in "${COMPONENTS[@]}"; do + if install_component "$component" "$MANIFEST_FILE"; then + ((INSTALLED_COUNT++)) || true + fi +done + +# Create lib64 symlink if needed (nvcc expects lib64/ but redistributables use lib/) +if [[ -d "$INSTALL_PATH/lib" && ! -e "$INSTALL_PATH/lib64" ]]; then + echo "" + echo "Creating symlink: lib64 -> lib" + ln -s lib "$INSTALL_PATH/lib64" +fi + +echo "" +echo "==========================================" +echo "Installation complete!" +echo "Installed $INSTALLED_COUNT components to: $INSTALL_PATH" +echo "==========================================" diff --git a/.github/scripts/cuda-version-manager.py b/.github/scripts/cuda-version-manager.py new file mode 100755 index 000000000..059d123ef --- /dev/null +++ b/.github/scripts/cuda-version-manager.py @@ -0,0 +1,699 @@ +#!/usr/bin/env python3 +""" +CUDA Version Manager + +A utility for managing CUDA redistributable versions in the setup-cuda GitHub Action. +Uses only Python standard library (no external dependencies). + +Usage: + python cuda-version-manager.py list-versions + python cuda-version-manager.py show-components 13.0.2 + python cuda-version-manager.py validate 13.0.2 + python cuda-version-manager.py add-version 13.1.1 + python cuda-version-manager.py add-component libcublas libcusolver + python cuda-version-manager.py add-component libcublas --no-required --min-version 13.0.0 + python cuda-version-manager.py remove-component libcublas + python cuda-version-manager.py check-updates + python cuda-version-manager.py urls 13.0.2 --platform windows-x86_64 +""" + +import argparse +import json +import os +import re +import sys +import urllib.request +import urllib.error +from pathlib import Path +from typing import Optional + +# NVIDIA redistributable base URL +REDIST_BASE_URL = "https://developer.download.nvidia.com/compute/cuda/redist" + +# Path to components.json relative to this script +SCRIPT_DIR = Path(__file__).parent +COMPONENTS_FILE = SCRIPT_DIR.parent / "actions" / "setup-cuda" / "components.json" + +# ANSI color codes (disabled if not a TTY) +USE_COLOR = sys.stdout.isatty() + + +def color(text: str, code: str) -> str: + """Apply ANSI color code if terminal supports it.""" + if not USE_COLOR: + return text + return f"\033[{code}m{text}\033[0m" + + +def green(text: str) -> str: + return color(text, "32") + + +def red(text: str) -> str: + return color(text, "31") + + +def yellow(text: str) -> str: + return color(text, "33") + + +def cyan(text: str) -> str: + return color(text, "36") + + +def bold(text: str) -> str: + return color(text, "1") + + +def fetch_url(url: str) -> str: + """Fetch content from URL.""" + try: + with urllib.request.urlopen(url, timeout=30) as response: + return response.read().decode("utf-8") + except urllib.error.HTTPError as e: + raise RuntimeError(f"HTTP error {e.code} fetching {url}") from e + except urllib.error.URLError as e: + raise RuntimeError(f"URL error fetching {url}: {e.reason}") from e + + +def fetch_json(url: str) -> dict: + """Fetch and parse JSON from URL.""" + content = fetch_url(url) + return json.loads(content) + + +def get_available_versions() -> list[str]: + """ + Get list of available CUDA versions from NVIDIA redistributable index. + Parses the HTML directory listing to find redistrib_*.json files. + """ + index_url = f"{REDIST_BASE_URL}/" + html = fetch_url(index_url) + + # Find all redistrib_X.Y.Z.json files + pattern = r'redistrib_(\d+\.\d+\.\d+)\.json' + versions = re.findall(pattern, html) + + # Sort versions semantically + def version_key(v: str) -> tuple: + parts = v.split(".") + return tuple(int(p) for p in parts) + + versions = sorted(set(versions), key=version_key) + return versions + + +def get_manifest(version: str) -> dict: + """Download and parse the redistrib manifest for a specific version.""" + url = f"{REDIST_BASE_URL}/redistrib_{version}.json" + return fetch_json(url) + + +def load_components_config() -> dict: + """Load the components.json configuration file.""" + if not COMPONENTS_FILE.exists(): + raise FileNotFoundError(f"Components file not found: {COMPONENTS_FILE}") + with open(COMPONENTS_FILE) as f: + return json.load(f) + + +def save_components_config(config: dict) -> None: + """Save the components.json configuration file.""" + with open(COMPONENTS_FILE, "w") as f: + json.dump(config, f, indent=2) + f.write("\n") + + +def format_size(size_bytes: int | str) -> str: + """Format byte size as human-readable string.""" + size_bytes = int(size_bytes) if isinstance(size_bytes, str) else size_bytes + for unit in ["B", "KB", "MB", "GB"]: + if size_bytes < 1024: + return f"{size_bytes:.1f} {unit}" + size_bytes /= 1024 + return f"{size_bytes:.1f} TB" + + +def version_tuple(v: str) -> tuple: + """Convert version string to tuple for comparison.""" + return tuple(int(p) for p in v.split(".")) + + +def version_gte(v1: str, v2: str) -> bool: + """Return True if v1 >= v2.""" + return version_tuple(v1) >= version_tuple(v2) + + +def cmd_list_versions(args: argparse.Namespace) -> int: + """List all available CUDA versions from NVIDIA redistributables.""" + print("Fetching available CUDA versions...") + versions = get_available_versions() + + if not versions: + print(red("No versions found")) + return 1 + + print(f"\nAvailable CUDA versions ({len(versions)} total):") + print("-" * 40) + + # Load supported versions once + try: + config = load_components_config() + supported_versions = set(config.get("supported_versions", [])) + except FileNotFoundError: + supported_versions = set() + + # Group by major version + current_major = None + for version in versions: + major = version.split(".")[0] + if major != current_major: + if current_major is not None: + print() + current_major = major + print(bold(f"CUDA {major}.x:")) + + marker = green(" [supported]") if version in supported_versions else "" + print(f" {version}{marker}") + + return 0 + + +def cmd_show_components(args: argparse.Namespace) -> int: + """Show components available for a specific CUDA version.""" + version = args.version + platform = args.platform + + print(f"Fetching manifest for CUDA {version}...") + try: + manifest = get_manifest(version) + except RuntimeError as e: + print(red(f"Error: {e}")) + return 1 + + # Get list of components (exclude metadata keys and non-dict entries) + components = [ + k for k, v in manifest.items() + if not k.startswith("_") and isinstance(v, dict) + ] + components.sort() + + print(f"\nComponents in CUDA {version}:") + print("-" * 80) + print(f"{'Component':<35} {'Version':<15} {'Platform':<20} {'Size':<10}") + print("-" * 80) + + total_size = 0 + for comp_name in components: + comp_info = manifest[comp_name] + comp_version = comp_info.get("version", "N/A") + + # Check each platform + platforms_found = [] + for plat in ["linux-x86_64", "linux-aarch64", "windows-x86_64"]: + if plat in comp_info: + platforms_found.append(plat) + + if platform and platform not in platforms_found: + continue + + # Get size for specified platform or first available + target_plat = platform if platform else (platforms_found[0] if platforms_found else None) + if target_plat and target_plat in comp_info: + size = comp_info[target_plat].get("size", 0) + size = int(size) if isinstance(size, str) else size + size_str = format_size(size) + total_size += size + else: + size_str = "N/A" + + plat_str = ", ".join(p.replace("-x86_64", "").replace("-aarch64", "-arm") for p in platforms_found) + print(f"{comp_name:<35} {comp_version:<15} {plat_str:<20} {size_str:<10}") + + print("-" * 80) + print(f"Total components: {len(components)}") + if platform: + print(f"Total size ({platform}): {format_size(total_size)}") + + return 0 + + +def cmd_validate(args: argparse.Namespace) -> int: + """Validate that a CUDA version provides all required components.""" + version = args.version + + print(f"Validating CUDA {version} against components.json...") + + try: + config = load_components_config() + except FileNotFoundError as e: + print(red(f"Error: {e}")) + return 1 + + try: + manifest = get_manifest(version) + except RuntimeError as e: + print(red(f"Error: {e}")) + return 1 + + components = config.get("components", {}) + missing = [] + warnings = [] + skipped = [] + + for comp_name, comp_config in components.items(): + # Check min_version requirement + min_ver = comp_config.get("min_version") + if min_ver and not version_gte(version, min_ver): + skipped.append(f"{comp_name} (requires CUDA >= {min_ver})") + continue + + # Skip optional components that aren't version-gated + if not comp_config.get("required", False) and not min_ver: + continue + + platforms = comp_config.get("platforms", ["linux-x86_64", "windows-x86_64"]) + + if comp_name not in manifest: + missing.append((comp_name, "not found in manifest")) + continue + + comp_info = manifest[comp_name] + for plat in platforms: + if plat not in comp_info: + warnings.append(f"{comp_name}: not available for {plat}") + + print() + if skipped: + print(f"Skipped components (version requirements not met):") + for s in skipped: + print(f" - {s}") + print() + + if missing: + print(red("VALIDATION FAILED")) + print("\nMissing required components:") + for comp, reason in missing: + print(f" {red('X')} {comp}: {reason}") + return 1 + + if warnings: + print(yellow("VALIDATION PASSED WITH WARNINGS")) + print("\nWarnings:") + for warning in warnings: + print(f" {yellow('!')} {warning}") + else: + print(green("VALIDATION PASSED")) + print("\nAll required components are available.") + + return 0 + + +def cmd_add_version(args: argparse.Namespace) -> int: + """Add a new version to supported_versions in components.json.""" + version = args.version + + # Validate version exists + print(f"Validating CUDA {version}...") + try: + manifest = get_manifest(version) + except RuntimeError as e: + print(red(f"Error: Version {version} not found: {e}")) + return 1 + + try: + config = load_components_config() + except FileNotFoundError as e: + print(red(f"Error: {e}")) + return 1 + + supported = config.get("supported_versions", []) + if version in supported: + print(yellow(f"Version {version} is already in supported_versions")) + return 0 + + # Add and sort versions + supported.append(version) + + def version_key(v: str) -> tuple: + return tuple(int(p) for p in v.split(".")) + + supported.sort(key=version_key) + config["supported_versions"] = supported + + save_components_config(config) + print(green(f"Added {version} to supported_versions")) + print(f"Supported versions: {', '.join(supported)}") + + return 0 + + +def cmd_check_updates(args: argparse.Namespace) -> int: + """Check for new CUDA versions not in supported_versions.""" + print("Checking for updates...") + + try: + config = load_components_config() + except FileNotFoundError as e: + print(red(f"Error: {e}")) + return 1 + + available = get_available_versions() + supported = set(config.get("supported_versions", [])) + + # Find versions newer than our latest + if supported: + def version_key(v: str) -> tuple: + return tuple(int(p) for p in v.split(".")) + + latest_supported = max(supported, key=version_key) + latest_tuple = version_key(latest_supported) + + new_versions = [v for v in available if version_key(v) > latest_tuple] + else: + new_versions = available + + if new_versions: + print(yellow(f"\nNew versions available ({len(new_versions)}):")) + for v in new_versions: + print(f" {cyan(v)}") + print(f"\nTo add a version: python {sys.argv[0]} add-version ") + return 0 + else: + print(green("\nNo new versions available.")) + return 0 + + +def cmd_add_component(args: argparse.Namespace) -> int: + """Add one or more components to components.json.""" + names = args.names + + try: + config = load_components_config() + except FileNotFoundError as e: + print(red(f"Error: {e}")) + return 1 + + # Determine which manifest to validate against + version = args.version + if not version: + supported = config.get("supported_versions", []) + if supported: + version = max(supported, key=version_tuple) + else: + print(red("Error: No supported versions in components.json and no --version specified")) + return 1 + + print(f"Fetching manifest for CUDA {version}...") + try: + manifest = get_manifest(version) + except RuntimeError as e: + print(red(f"Error: {e}")) + return 1 + + # Collect all manifest component names for suggestions + manifest_components = sorted( + k for k, v in manifest.items() + if not k.startswith("_") and isinstance(v, dict) + ) + + existing_components = config.get("components", {}) + added = [] + skipped = [] + errors = [] + + for name in names: + if name in existing_components: + skipped.append((name, "already exists in components.json")) + continue + + if name not in manifest: + # Try a fuzzy suggestion + suggestions = [c for c in manifest_components if name.lower() in c.lower()] + hint = "" + if suggestions: + hint = f" Did you mean: {', '.join(suggestions[:5])}?" + errors.append(f"{name}: not found in CUDA {version} manifest.{hint}") + continue + + comp_info = manifest[name] + + # Auto-detect platforms from manifest + if args.platforms: + platforms = args.platforms + else: + platforms = [ + p for p in ["linux-x86_64", "linux-aarch64", "windows-x86_64"] + if p in comp_info + ] + + # Auto-populate description from manifest 'name' field + if args.description: + description = args.description + else: + description = comp_info.get("name", name) + + required = args.required + + entry: dict = { + "required": required, + "description": description, + "platforms": platforms, + } + if args.min_version: + entry["min_version"] = args.min_version + + existing_components[name] = entry + added.append(name) + + config["components"] = existing_components + if added: + save_components_config(config) + + # Report results + print() + for name in added: + entry = existing_components[name] + req = "required" if entry["required"] else "optional" + plats = ", ".join(entry["platforms"]) + print(green(f"+ {name}") + f" ({req}, {plats})") + print(f" {entry['description']}") + for name, reason in skipped: + print(yellow(f"~ {name}: {reason}")) + for msg in errors: + print(red(f"X {msg}")) + + if added: + print(f"\n{green(f'{len(added)} component(s) added')} to {COMPONENTS_FILE.name}") + if errors: + return 1 + return 0 + + +def cmd_remove_component(args: argparse.Namespace) -> int: + """Remove one or more components from components.json.""" + names = args.names + + try: + config = load_components_config() + except FileNotFoundError as e: + print(red(f"Error: {e}")) + return 1 + + components = config.get("components", {}) + removed = [] + not_found = [] + + for name in names: + if name not in components: + not_found.append(name) + continue + del components[name] + removed.append(name) + + config["components"] = components + if removed: + save_components_config(config) + + print() + for name in removed: + print(red(f"- {name}")) + for name in not_found: + print(yellow(f"~ {name}: not found in components.json")) + + if removed: + print(f"\n{len(removed)} component(s) removed from {COMPONENTS_FILE.name}") + return 0 + + +def cmd_urls(args: argparse.Namespace) -> int: + """Generate download URLs for a CUDA version.""" + version = args.version + platform = args.platform + + try: + config = load_components_config() + except FileNotFoundError as e: + print(red(f"Error: {e}")) + return 1 + + try: + manifest = get_manifest(version) + except RuntimeError as e: + print(red(f"Error: {e}")) + return 1 + + components = config.get("components", {}) + total_size = 0 + + print(f"Download URLs for CUDA {version} ({platform}):") + print("-" * 80) + + for comp_name, comp_config in components.items(): + # Check min_version requirement + min_ver = comp_config.get("min_version") + if min_ver and not version_gte(version, min_ver): + continue + + # Skip optional components that aren't version-gated + if not comp_config.get("required", False) and not min_ver: + continue + + platforms = comp_config.get("platforms", ["linux-x86_64", "windows-x86_64"]) + if platform not in platforms: + continue + + if comp_name not in manifest: + print(f"{red('X')} {comp_name}: not found in manifest") + continue + + comp_info = manifest[comp_name] + if platform not in comp_info: + print(f"{yellow('!')} {comp_name}: not available for {platform}") + continue + + plat_info = comp_info[platform] + relative_path = plat_info.get("relative_path", "") + sha256 = plat_info.get("sha256", "") + size = plat_info.get("size", 0) + size = int(size) if isinstance(size, str) else size + total_size += size + + url = f"{REDIST_BASE_URL}/{relative_path}" + print(f"\n{bold(comp_name)}:") + print(f" URL: {url}") + print(f" SHA256: {sha256}") + print(f" Size: {format_size(size)}") + + print("-" * 80) + print(f"Total download size: {bold(format_size(total_size))}") + + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser( + description="CUDA Version Manager for setup-cuda GitHub Action", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s list-versions List all available CUDA versions + %(prog)s show-components 13.0.2 Show components for CUDA 13.0.2 + %(prog)s validate 13.0.2 Validate components.json against 13.0.2 + %(prog)s add-version 13.1.1 Add 13.1.1 to supported versions + %(prog)s add-component libcublas libcusolver Add components to components.json + %(prog)s add-component libcublas --no-required Add as optional component + %(prog)s remove-component libcublas Remove a component + %(prog)s check-updates Check for newer versions + %(prog)s urls 13.0.2 -p windows-x86_64 Generate download URLs + """, + ) + + subparsers = parser.add_subparsers(dest="command", help="Command to run") + + # list-versions + subparsers.add_parser("list-versions", help="List all available CUDA versions") + + # show-components + show_parser = subparsers.add_parser("show-components", help="Show components for a CUDA version") + show_parser.add_argument("version", help="CUDA version (e.g., 13.0.2)") + show_parser.add_argument("-p", "--platform", help="Filter by platform (e.g., linux-x86_64)") + + # validate + validate_parser = subparsers.add_parser("validate", help="Validate components.json against a version") + validate_parser.add_argument("version", help="CUDA version to validate") + + # add-version + add_parser = subparsers.add_parser("add-version", help="Add a version to supported_versions") + add_parser.add_argument("version", help="CUDA version to add") + + # add-component + add_comp_parser = subparsers.add_parser( + "add-component", + help="Add component(s) to components.json", + description="Add one or more CUDA redistributable components to components.json. " + "Validates against the NVIDIA manifest and auto-detects platforms and description.", + ) + add_comp_parser.add_argument( + "names", nargs="+", help="Component name(s) as they appear in the NVIDIA manifest" + ) + add_comp_parser.add_argument( + "--version", default=None, + help="CUDA version manifest to validate against (default: latest supported)", + ) + add_comp_parser.add_argument( + "--required", default=True, action=argparse.BooleanOptionalAction, + help="Mark component as required (default: --required)", + ) + add_comp_parser.add_argument( + "--description", default=None, + help="Override auto-detected description", + ) + add_comp_parser.add_argument( + "--platforms", nargs="+", default=None, + help="Override auto-detected platforms (e.g., linux-x86_64 windows-x86_64)", + ) + add_comp_parser.add_argument( + "--min-version", default=None, + help="Minimum CUDA version for this component (e.g., 13.0.0)", + ) + + # remove-component + remove_comp_parser = subparsers.add_parser( + "remove-component", + help="Remove component(s) from components.json", + ) + remove_comp_parser.add_argument( + "names", nargs="+", help="Component name(s) to remove" + ) + + # check-updates + subparsers.add_parser("check-updates", help="Check for new CUDA versions") + + # urls + urls_parser = subparsers.add_parser("urls", help="Generate download URLs for a version") + urls_parser.add_argument("version", help="CUDA version") + urls_parser.add_argument("-p", "--platform", default="linux-x86_64", help="Platform (default: linux-x86_64)") + + args = parser.parse_args() + + if not args.command: + parser.print_help() + return 1 + + commands = { + "list-versions": cmd_list_versions, + "show-components": cmd_show_components, + "validate": cmd_validate, + "add-version": cmd_add_version, + "add-component": cmd_add_component, + "remove-component": cmd_remove_component, + "check-updates": cmd_check_updates, + "urls": cmd_urls, + } + + return commands[args.command](args) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/build-tsd.yml b/.github/workflows/build-tsd.yml new file mode 100644 index 000000000..6ceee78c8 --- /dev/null +++ b/.github/workflows/build-tsd.yml @@ -0,0 +1,118 @@ +name: TSD CI + +on: + push: + branches: [ next_release ] + paths: + - 'tsd/**' + - '.github/actions/**' + - '.github/workflows/build-tsd.yml' + pull_request: + branches: [ main, next_release ] + paths: + - 'tsd/**' + - '.github/actions/**' + - '.github/workflows/build-tsd.yml' + +jobs: + build: + runs-on: ${{ matrix.runs-on }} + strategy: + matrix: + os: [linux, windows] + config: [Release, Debug] + include: + - os: linux + runs-on: ubuntu-22.04 + generator: Ninja + - os: windows + runs-on: windows-2022 + generator: Ninja + steps: + - uses: actions/checkout@v6 + - name: Setup MSVC (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + - name: Install Packages + if: ${{ matrix.os == 'linux' }} + run: sudo apt install -y libtbb-dev + - name: Build ANARI-SDK + uses: ./.github/actions/build-anari-sdk + with: + config: ${{ matrix.config }} + install-prefix: ${{ github.workspace }}/deps + build-deps-path: tsd/cmake/build_deps + generator: Ninja + - name: Configure TSD CMake + run: > + cmake -LA -G Ninja -B ${{github.workspace}}/build + -DCMAKE_PREFIX_PATH=${{ github.workspace }}/deps + -DCMAKE_BUILD_TYPE=${{ matrix.config }} + -DTSD_BUILD_APPS=ON + -DTSD_BUILD_INTERACTIVE_APPS=OFF + -DTSD_BUILD_UI_LIBRARY=OFF + -DTSD_USE_ASSIMP=OFF + -DTSD_USE_CUDA=OFF + -DTSD_USE_HDF5=OFF + -DTSD_USE_SDL3=OFF + -DTSD_USE_TBB=OFF + -DTSD_USE_USD=OFF + -DTSD_ENABLE_SERIALIZATION=OFF + ${{ github.workspace }}/tsd + - name: Build + run: cmake --build ${{ github.workspace }}/build --config ${{ matrix.config }} + - name: Unit Tests + working-directory: ${{ github.workspace }}/build + run: ctest -C ${{ matrix.config }} --output-on-failure + + build-cuda: + runs-on: ${{ matrix.runs-on }} + strategy: + matrix: + os: [linux, windows] + cudaversion: ['12.4.1', '13.0.2'] + config: [Release, Debug] + include: + - os: linux + runs-on: ubuntu-22.04 + generator: Ninja + - os: windows + runs-on: windows-2022 + generator: Ninja + steps: + - uses: actions/checkout@v6 + - name: Setup MSVC (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + - name: Setup CUDA Toolkit + uses: ./.github/actions/setup-cuda + with: + cuda-version: ${{ matrix.cudaversion }} + - name: Install Packages + if: ${{ matrix.os == 'linux' }} + run: sudo apt install -y libtbb-dev + - name: Build ANARI-SDK + uses: ./.github/actions/build-anari-sdk + with: + config: ${{ matrix.config }} + install-prefix: ${{ github.workspace }}/deps + build-deps-path: tsd/cmake/build_deps + generator: Ninja + - name: Configure TSD CMake (CUDA) + run: > + cmake -LA -G Ninja -B ${{github.workspace}}/build-tsd + -DCMAKE_PREFIX_PATH=${{ github.workspace }}/deps + -DCMAKE_BUILD_TYPE=${{ matrix.config }} + -DTSD_BUILD_APPS=ON + -DTSD_BUILD_INTERACTIVE_APPS=OFF + -DTSD_BUILD_UI_LIBRARY=OFF + -DTSD_USE_ASSIMP=OFF + -DTSD_USE_CUDA=ON + -DTSD_USE_HDF5=OFF + -DTSD_USE_SDL3=OFF + -DTSD_USE_TBB=OFF + -DTSD_USE_USD=OFF + -DTSD_ENABLE_SERIALIZATION=OFF + ${{ github.workspace }}/tsd + - name: Build TSD (CUDA) + run: cmake --build ${{ github.workspace }}/build-tsd --config ${{ matrix.config }} --parallel diff --git a/.github/workflows/build-visrtx.yml b/.github/workflows/build-visrtx.yml index 6b9f6b4e4..348716570 100644 --- a/.github/workflows/build-visrtx.yml +++ b/.github/workflows/build-visrtx.yml @@ -1,11 +1,18 @@ -name: Build VisRTX +name: VisRTX CI on: push: branches: [ next_release ] + paths: + - 'devices/**' + - '.github/actions/**' + - '.github/workflows/build-visrtx.yml' pull_request: branches: [ main, next_release ] paths: - - 'devices/**' # Only trigger if VisRTX device has changes + - 'devices/**' + - '.github/actions/**' + - '.github/workflows/build-visrtx.yml' + jobs: build: strategy: @@ -13,34 +20,32 @@ jobs: matrix: os: [linux, windows] cudaversion: ['12.4.1', '13.0.2'] - config: [release, debug] + config: [Release, Debug] include: - - runs-on: ubuntu-22.04 - os: linux + - os: linux + runs-on: ubuntu-22.04 + generator: Ninja + - os: windows + runs-on: windows-2022 generator: Ninja - - runs-on: windows-2022 - os: windows - generator: Visual Studio 17 2022 runs-on: ${{ matrix.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 + - name: Setup MSVC (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 - name: Setup CUDA Toolkit - uses: Jimver/cuda-toolkit@v0.2.29 + uses: ./.github/actions/setup-cuda with: - cuda: ${{ matrix.cudaversion }} - method: 'network' - sub-packages: ${{ matrix.os == 'linux' && '["nvcc", "cudart", "nvml-dev", "thrust"]' || '[]' }} - non-cuda-sub-packages: ${{ matrix.os == 'linux' && '["libcurand", "libcurand-dev"]' || '[]' }} - - name: Configure dependencies - run: > - cmake -LA -G "${{ matrix.generator }}" -B ${{github.workspace}}/deps_build - -DCMAKE_BUILD_TYPE=${{ matrix.config }} - -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/deps - ${{github.workspace}}/devices/rtx/cmake/build_deps - - name: Install dependencies - run: > - cmake --build ${{ github.workspace }}/deps_build --config ${{ matrix.config }} + cuda-version: ${{ matrix.cudaversion }} + - name: Build ANARI-SDK + uses: ./.github/actions/build-anari-sdk + with: + config: ${{ matrix.config }} + install-prefix: ${{ github.workspace }}/deps + build-deps-path: devices/rtx/cmake/build_deps + generator: ${{ matrix.generator }} - name: Configure VisRTX run: > cmake -LA -G "${{ matrix.generator }}" -B ${{github.workspace}}/build-visrtx @@ -54,21 +59,3 @@ jobs: - name: Install VisRTX run: > cmake --build ${{ github.workspace }}/build-visrtx --config ${{ matrix.config }} --target install --parallel - - name: Configure TSD CMake (CUDA) - run: > - cmake -LA -B ${{github.workspace}}/build-tsd - -DCMAKE_PREFIX_PATH=${{ github.workspace }}/deps - -DCMAKE_BUILD_TYPE=${{ matrix.config }} - -DTSD_BUILD_APPS=ON - -DTSD_BUILD_INTERACTIVE_APPS=OFF - -DTSD_BUILD_UI_LIBRARY=OFF - -DTSD_USE_ASSIMP=OFF - -DTSD_USE_CUDA=ON - -DTSD_USE_HDF5=OFF - -DTSD_USE_SDL3=OFF - -DTSD_USE_TBB=OFF - -DTSD_USE_USD=OFF - -DTSD_ENABLE_SERIALIZATION=OFF - ${{ github.workspace }}/tsd - - name: Build TSD (CUDA) - run: cmake --build ${{ github.workspace }}/build-tsd --config ${{ matrix.config }} --parallel diff --git a/.github/workflows/tsd_ci.yml b/.github/workflows/tsd_ci.yml deleted file mode 100644 index 659c80ecf..000000000 --- a/.github/workflows/tsd_ci.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: TSD CI - -on: - push: - branches: [ next_release ] - pull_request: - branches: [ main, next_release ] - paths: - - 'tsd/**' # Only trigger if TSD has changes - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - config: [Release, Debug] - - steps: - - uses: actions/checkout@v4 - - - name: Install Packages - if: ${{ matrix.os == 'ubuntu-latest' }} - run: sudo apt install -y libtbb-dev - - - name: Configure ANARI-SDK CMake - run: > - cmake -LA -B ${{github.workspace}}/deps_build - -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/deps - ${{github.workspace}}/tsd/cmake/build_deps - - - name: Build + install ANARI-SDK - run: cmake --build ${{ github.workspace }}/deps_build --config ${{ matrix.config }} - - - name: Configure TSD CMake - run: > - cmake -LA -B ${{github.workspace}}/build - -DCMAKE_PREFIX_PATH=${{ github.workspace }}/deps - -DCMAKE_BUILD_TYPE=${{ matrix.config }} - -DTSD_BUILD_APPS=ON - -DTSD_BUILD_INTERACTIVE_APPS=OFF - -DTSD_BUILD_UI_LIBRARY=OFF - -DTSD_USE_ASSIMP=OFF - -DTSD_USE_CUDA=OFF - -DTSD_USE_HDF5=OFF - -DTSD_USE_SDL3=OFF - -DTSD_USE_TBB=OFF - -DTSD_USE_USD=OFF - -DTSD_ENABLE_SERIALIZATION=OFF - ${{ github.workspace }}/tsd - - - name: Build - run: cmake --build ${{ github.workspace }}/build --config ${{ matrix.config }} - - - name: Unit Tests - working-directory: ${{ github.workspace }}/build - run: ctest -C ${{ matrix.config }} --output-on-failure diff --git a/devices/rtx/cmake/build_deps/superbuild_macros.cmake b/devices/rtx/cmake/build_deps/superbuild_macros.cmake index 96a3feedc..d94700b34 100644 --- a/devices/rtx/cmake/build_deps/superbuild_macros.cmake +++ b/devices/rtx/cmake/build_deps/superbuild_macros.cmake @@ -37,7 +37,7 @@ include(ProcessorCount) ProcessorCount(PROCESSOR_COUNT) set(NUM_BUILD_JOBS ${PROCESSOR_COUNT} CACHE STRING "Number of build jobs '-j '") -set(DEFAULT_BUILD_COMMAND cmake --build . --config release -j ${NUM_BUILD_JOBS}) +set(DEFAULT_BUILD_COMMAND cmake --build . --config ${CMAKE_BUILD_TYPE} -j ${NUM_BUILD_JOBS}) get_filename_component(INSTALL_DIR_ABSOLUTE ${CMAKE_INSTALL_PREFIX} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -85,7 +85,7 @@ macro(build_subproject) URL ${BUILD_SUBPROJECT_URL} LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS - -DCMAKE_BUILD_TYPE=Release + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_INSTALL_PREFIX:PATH=${SUBPROJECT_INSTALL_PATH} diff --git a/tsd/cmake/build_deps/superbuild_macros.cmake b/tsd/cmake/build_deps/superbuild_macros.cmake index 96a3feedc..d94700b34 100644 --- a/tsd/cmake/build_deps/superbuild_macros.cmake +++ b/tsd/cmake/build_deps/superbuild_macros.cmake @@ -37,7 +37,7 @@ include(ProcessorCount) ProcessorCount(PROCESSOR_COUNT) set(NUM_BUILD_JOBS ${PROCESSOR_COUNT} CACHE STRING "Number of build jobs '-j '") -set(DEFAULT_BUILD_COMMAND cmake --build . --config release -j ${NUM_BUILD_JOBS}) +set(DEFAULT_BUILD_COMMAND cmake --build . --config ${CMAKE_BUILD_TYPE} -j ${NUM_BUILD_JOBS}) get_filename_component(INSTALL_DIR_ABSOLUTE ${CMAKE_INSTALL_PREFIX} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -85,7 +85,7 @@ macro(build_subproject) URL ${BUILD_SUBPROJECT_URL} LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS - -DCMAKE_BUILD_TYPE=Release + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_INSTALL_PREFIX:PATH=${SUBPROJECT_INSTALL_PATH}