# SPDX-License-Identifier: MPL-2.0 # Messages en ASCII : compatible Windows PowerShell 5.1 sans BOM. <# .SYNOPSIS Telecharge (avec reprise) et installe cuDNN NVIDIA pour Windows x86_64. .DESCRIPTION Utilise pour le prototype prototypes/gpu_interop (etape 3 : inference ONNX Runtime sur GPU). cuDNN est un composant PROPRIETAIRE NVIDIA : il reste un plugin facultatif de SEQUA (docs, par. 10.2). Le script n'accepte la licence qu'avec -AcceptLicense ou apres confirmation interactive (O/N) ; il ne modifie jamais le PATH. Etapes : 1. detecte la majeure CUDA via la variable machine CUDA_PATH (ou -CudaMajor) ; 2. lit l'index officiel NVIDIA et, si -Version est absent, prend la version la plus recente (tri par version, pas par chaine : 9.28 > 9.9) ; 3. lit le manifeste redistrib_.json et l'entree Windows x86_64 / cuda ; 4. telecharge l'archive (~1,3 Go) avec reprise : en cas de coupure, relancer le script repart du dernier octet recu (curl -C -) ; 5. verifie le SHA256 (le fichier est supprime s'il est faux) ; 6. extrait dans un dossier temporaire a cote de la destination, affiche la licence extraite et ses premieres lignes, puis demande confirmation (O/N) ; 7. installe en aplatissant le dossier racine de l'archive : \bin, \include, \lib ; 8. sauf -NoEnv, definit la variable d'environnement UTILISATEUR CUDNN_PATH (aucun droit administrateur requis) et n'ajoute PAS au PATH. A lancer dans une fenetre PowerShell externe (telechargement long). .PARAMETER Version Version cuDNN (ex. 9.28.0). Defaut : la plus recente de l'index NVIDIA. .PARAMETER CudaMajor Majeure CUDA du paquet a installer (12 ou 13). Defaut : deduite de CUDA_PATH machine. .PARAMETER InstallDir Dossier d'installation. Defaut : %LOCALAPPDATA%\SEQUA\sdk\cudnn\_cuda. .PARAMETER CacheDir Dossier de telechargement. Defaut : %LOCALAPPDATA%\SEQUA\downloads\cudnn. .PARAMETER ArchivePath Installe depuis une archive locale deja telechargee, sans acces au manifeste. Aucune empreinte connue : la verification SHA256 est clairement signalee comme ignoree. .PARAMETER DryRun Affiche ce qui serait fait (URL, SHA256, taille, destination, etat du cache) sans rien telecharger, extraire ni installer. .PARAMETER DownloadOnly Telecharge et verifie l'archive sans l'extraire ni l'installer. .PARAMETER AcceptLicense Accepte la licence cuDNN sans demande interactive. .PARAMETER NoEnv N'ecrit pas la variable d'environnement utilisateur CUDNN_PATH (utile pour les tests). .PARAMETER Force Reinstalle meme si une DLL cudnn64_*.dll est deja presente dans la destination. .PARAMETER Help Affiche cette aide. .EXAMPLE powershell -ExecutionPolicy Bypass -File scripts\install-cudnn.ps1 -DryRun .EXAMPLE powershell -ExecutionPolicy Bypass -File scripts\install-cudnn.ps1 -DownloadOnly .EXAMPLE powershell -ExecutionPolicy Bypass -File scripts\install-cudnn.ps1 .EXAMPLE powershell -ExecutionPolicy Bypass -File scripts\install-cudnn.ps1 -ArchivePath C:\cudnn.zip -AcceptLicense -NoEnv -InstallDir C:\tmp\cudnn #> [CmdletBinding()] param( [string]$Version = "", [int]$CudaMajor = 0, [string]$InstallDir = "", [string]$CacheDir = (Join-Path $env:LOCALAPPDATA "SEQUA\downloads\cudnn"), [string]$ArchivePath = "", [switch]$DryRun, [switch]$DownloadOnly, [switch]$AcceptLicense, [switch]$NoEnv, [switch]$Force, [Alias("h", "?")] [switch]$Help ) if ($Help) { Get-Help $PSCommandPath -Detailed exit 0 } $ErrorActionPreference = "Stop" . (Join-Path $PSScriptRoot "lib\SequaDownload.ps1") $nvidiaBase = "https://developer.download.nvidia.com/compute/cudnn/redist" $maxAttempts = 50 # Majeure CUDA depuis CUDA_PATH (machine d'abord : c'est la variable installee par le # CUDA Toolkit ; le processus courant peut ne pas encore l'avoir heritee). function Get-CudaMajor { foreach ($scope in @("Machine", "Process")) { $path = [System.Environment]::GetEnvironmentVariable("CUDA_PATH", $scope) if ($path -and $path -match 'v([0-9]+)\.[0-9]+') { return [int]$Matches[1] } } return 0 } # Versions cuDNN publiees dans l'index, triees par version (9.28 > 9.9), sans doublon. function Get-CudnnVersions { $index = & curl.exe -sL --fail "$nvidiaBase/" if ($LASTEXITCODE -ne 0) { throw "Index cuDNN injoignable : $nvidiaBase/" } $index | ForEach-Object { if ($_ -match 'redistrib_([0-9]+\.[0-9]+\.[0-9]+)\.json') { $Matches[1] } } | Sort-Object { [version]$_ } -Unique } # Manifeste officiel de la version : donne chemin relatif, sha256, md5, taille. function Get-CudnnManifest([string]$version) { $url = "$nvidiaBase/redistrib_$version.json" $json = & curl.exe -sL --fail $url if ($LASTEXITCODE -ne 0) { throw "Manifeste cuDNN introuvable : $url" } ($json -join "`n") | ConvertFrom-Json } # Extraction ZIP sans Expand-Archive (absent de certains Windows PowerShell 5.1) : # .NET en premier, tar.exe de Windows 10+ en repli. function Expand-ZipArchive([string]$archive, [string]$destination) { New-Item -ItemType Directory -Force -Path $destination | Out-Null try { Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $destination) return } catch { Write-Host " Extraction .NET indisponible : repli sur tar.exe." -ForegroundColor Yellow Remove-Item -Recurse -Force $destination -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $destination | Out-Null } & tar.exe -xf $archive -C $destination if ($LASTEXITCODE -ne 0) { throw "Extraction impossible : $archive" } } # L'archive cuDNN contient un dossier racine unique : on le detecte pour l'aplatir. function Get-ExtractedRoot([string]$dir) { $entries = @(Get-ChildItem -Force -LiteralPath $dir) if ($entries.Count -eq 1 -and $entries[0].PSIsContainer) { return $entries[0].FullName } return $dir } # DLL cuDNN a la racine du dossier bin, ou dans un sous-dossier de version CUDA. function Find-CudnnDll([string]$root) { if (-not (Test-Path -LiteralPath $root)) { return @() } @(Get-ChildItem -Path $root -Recurse -File -Filter "cudnn64_*.dll" -ErrorAction SilentlyContinue) } # --- Detection de CUDA ------------------------------------------------------ Write-Step "CUDA installe" if ($CudaMajor -gt 0) { $cudaMajor = $CudaMajor } else { $cudaMajor = Get-CudaMajor } if ($cudaMajor -gt 0) { Write-Host " Majeure CUDA : $cudaMajor (CUDA_PATH)" } else { Write-Host " Majeure CUDA inconnue (CUDA_PATH absent)." -ForegroundColor Yellow } $fromArchive = -not [string]::IsNullOrWhiteSpace($ArchivePath) if ($fromArchive) { if (-not (Test-Path -LiteralPath $ArchivePath -PathType Leaf)) { throw "Archive introuvable : $ArchivePath" } $archive = (Resolve-Path -LiteralPath $ArchivePath).Path } # --- Version ---------------------------------------------------------------- if ([string]::IsNullOrWhiteSpace($Version)) { if ($fromArchive) { if ((Split-Path -Leaf $archive) -match '([0-9]+\.[0-9]+\.[0-9]+)') { $Version = $Matches[1] } else { $Version = "local" } } else { Write-Step "Index cuDNN" $versions = @(Get-CudnnVersions) if ($versions.Count -eq 0) { throw "Aucune version cuDNN dans l'index $nvidiaBase/." } $Version = $versions[-1] Write-Host " Derniere version : $Version" } } if ($Version -ne "local" -and $Version -notmatch '^[0-9]+\.[0-9]+\.[0-9]+$') { throw "-Version attend une version x.y.z, ex. 9.28.0 (recu : $Version)." } # --- Destination ------------------------------------------------------------ if ([string]::IsNullOrWhiteSpace($InstallDir)) { if ($cudaMajor -le 0) { throw "Destination indeterminable : fournir -CudaMajor (CUDA_PATH absent) ou -InstallDir." } $InstallDir = Join-Path $env:LOCALAPPDATA "SEQUA\sdk\cudnn\${Version}_cuda${cudaMajor}" } # --- Deja installe ---------------------------------------------------------- $existing = @(Find-CudnnDll $InstallDir) if ($existing.Count -gt 0 -and -not $Force) { Write-Step "cuDNN deja installe" Write-Host " Dossier : $InstallDir" -ForegroundColor Green $existing | ForEach-Object { Write-Host " $($_.FullName)" } Write-Host "Rien a faire (utiliser -Force pour reinstaller)." -ForegroundColor Green exit 0 } # --- Archive, manifeste, empreinte ------------------------------------------ if ($fromArchive) { $sha256 = $null $size = (Get-Item -LiteralPath $archive).Length $url = $archive $download = $false } else { Write-Step "Manifeste cuDNN $Version" $manifest = Get-CudnnManifest $Version $key = "cuda$cudaMajor" $entry = $manifest.cudnn.'windows-x86_64'.$key if (-not $entry) { $available = ((Get-Member -InputObject $manifest.cudnn.'windows-x86_64' -MemberType NoteProperty).Name) -join ", " throw "Pas de binaire cuDNN Windows x86_64 pour $key (disponibles : $available)." } $url = "$nvidiaBase/$($entry.relative_path)" $sha256 = $entry.sha256.ToUpperInvariant() $size = [int64]$entry.size $archive = Join-Path $CacheDir (Split-Path -Leaf $entry.relative_path) $download = $true } Write-Step "cuDNN $Version (cuda$cudaMajor)" Write-Host " URL : $url" if ($sha256) { Write-Host " SHA256 : $sha256" } else { Write-Host " SHA256 : inconnu (archive locale : verification d'empreinte ignoree)" -ForegroundColor Yellow } Write-Host (" Taille : {0:N0} octets ({1:N1} Mo)" -f $size, ($size / 1MB)) Write-Host " Archive : $archive" Write-Host " Destination: $InstallDir" if ($DryRun) { if ($download) { $partial = "$archive.part" if (Test-Path $archive) { Write-Host " Archive deja telechargee dans le cache." } elseif (Test-Path $partial) { Write-Host (" Telechargement partiel : {0:N0} octets." -f (Get-Item $partial).Length) } } Write-Host "DryRun : rien n'a ete telecharge, extrait ni installe." -ForegroundColor Yellow exit 0 } # --- Telechargement et verification ----------------------------------------- if ($download) { New-Item -ItemType Directory -Force -Path $CacheDir | Out-Null if (Test-Path $archive) { Write-Step "Archive deja presente dans le cache" } else { Assert-FreeSpace -Path $CacheDir -Needed ($size * 3) Write-Step "Telechargement avec reprise" Invoke-ResumableDownload $url $archive $size $maxAttempts } Write-Step "Verification SHA256" $hash = Get-FileDigest -Path $archive -Algorithm SHA256 if ($hash -ne $sha256) { Remove-Item $archive -Force throw "SHA256 incorrect ($hash) : fichier supprime, relancer le script pour le retelecharger." } Write-Host " OK" } if ($DownloadOnly) { Write-Host "Archive prete : $archive" -ForegroundColor Green exit 0 } # --- Extraction dans un dossier temporaire a cote de la destination --------- Write-Step "Extraction" $parent = Split-Path -Parent $InstallDir if (-not $parent) { $parent = "." } New-Item -ItemType Directory -Force -Path $parent | Out-Null $temp = Join-Path $parent ((Split-Path -Leaf $InstallDir) + ".tmp-" + [guid]::NewGuid().ToString("N").Substring(0, 8)) if (Test-Path $temp) { Remove-Item -Recurse -Force $temp } Expand-ZipArchive $archive $temp $root = Get-ExtractedRoot $temp Write-Host " Archive : $archive" Write-Host " Temporaire : $temp" Write-Host " Racine archive : $root" # --- Licence ---------------------------------------------------------------- Write-Step "Licence cuDNN (proprietaire NVIDIA)" $license = Get-ChildItem -Path $temp -Recurse -File -Filter "LICENSE.txt" -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $license) { Remove-Item -Recurse -Force $temp throw "LICENSE.txt introuvable dans l'archive extraite : installation refusee." } Write-Host " Licence : $($license.FullName)" Get-Content -LiteralPath $license.FullName -TotalCount 8 | ForEach-Object { Write-Host " $_" } Write-Host " cuDNN est proprietaire NVIDIA : son usage est regi par cette licence." -ForegroundColor Yellow if (-not $AcceptLicense) { $answer = Read-Host "Accepter la licence cuDNN et installer ? (O/N)" if ($answer -notmatch '^(o|oui|y|yes)$') { Remove-Item -Recurse -Force $temp Write-Host "Licence refusee : aucune installation, dossier temporaire supprime." -ForegroundColor Yellow exit 2 } } # --- Installation (aplatir le dossier racine de l'archive) ------------------- Write-Step "Installation" # On ne remplace qu'un dossier vide ou une installation cuDNN precedente : jamais un # dossier quelconque passe par erreur dans -InstallDir. if (Test-Path -LiteralPath $InstallDir) { $isEmpty = @(Get-ChildItem -Force -LiteralPath $InstallDir).Count -eq 0 $isCudnn = Test-Path -LiteralPath (Join-Path $InstallDir "include\cudnn.h") if (-not $isEmpty -and -not $isCudnn) { Remove-Item -Recurse -Force $temp throw "$InstallDir existe, n'est pas vide et ne contient pas d'installation cuDNN : rien n'a ete supprime. Choisir un autre -InstallDir." } Remove-Item -Recurse -Force -LiteralPath $InstallDir } New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null Get-ChildItem -Force -LiteralPath $root | ForEach-Object { Move-Item -LiteralPath $_.FullName -Destination $InstallDir } Remove-Item -Recurse -Force $temp Write-Host " Installe dans : $InstallDir" if (-not $NoEnv) { [System.Environment]::SetEnvironmentVariable("CUDNN_PATH", $InstallDir, "User") Write-Host " CUDNN_PATH (utilisateur) = $InstallDir" } # --- Verification ----------------------------------------------------------- Write-Step "Verification" $dlls = @(Find-CudnnDll $InstallDir) $header = Join-Path $InstallDir "include\cudnn.h" if ($dlls.Count -gt 0) { Write-Host " DLL cudnn64_*.dll :" -ForegroundColor Green $dlls | ForEach-Object { Write-Host " $($_.FullName)" } } else { Write-Host " Aucune cudnn64_*.dll trouvee sous $InstallDir" -ForegroundColor Red } if (Test-Path $header) { Write-Host " En-tete : $header" -ForegroundColor Green } else { Write-Host " En-tete include\cudnn.h introuvable." -ForegroundColor Red } if ($dlls.Count -eq 0 -or -not (Test-Path $header)) { Write-Host "" Write-Host "Installation incomplete : verifier l'archive extraite." -ForegroundColor Red exit 1 } Write-Host "" Write-Host "cuDNN $Version (cuda$cudaMajor) installe : $InstallDir" -ForegroundColor Green if (-not $NoEnv) { Write-Host "CUDNN_PATH (utilisateur) defini : ouvrir un NOUVEAU terminal pour l'utiliser." } else { Write-Host "CUDNN_PATH non defini (-NoEnv)." } Write-Host "Ce script n'ajoute rien au PATH : pointer l'application sur bin\ (ou copier cudnn64_*.dll a cote de l'executable)."