build: script d'installation de cuDNN, fonctions de téléchargement communes
install-cudnn.ps1 : version la plus récente lue dans l'index NVIDIA, archive cuda13 téléchargée avec reprise et vérifiée par SHA256, licence NVIDIA affichée et acceptée par l'utilisateur, installation sans droits admin dans %LOCALAPPDATA%\SEQUA\sdk\cudnn, variable utilisateur CUDNN_PATH. Refuse de remplacer un dossier de destination non vide qui n'est pas une installation cuDNN. Fonctions de téléchargement et d'empreinte factorisées dans scripts/lib/SequaDownload.ps1 (install-cuda.ps1 inchangé fonctionnellement). Écrit par un subagent (DeepSeek V4.1 Flash), relu et corrigé.
This commit is contained in:
1 parent
545b8fd369
commit
522ea04eb4
4 files changed
+437
-55
No files matched your search
@@ -37,6 +37,7 @@ Il n'y a pas d'intégration continue sur serveur : les vérifications se lancent
|
||||
| Windows, avant commit | `.\scripts\check.ps1` | idem + Node.js pour Studio |
|
||||
| Linux / macOS | `./scripts/build.sh [<preset>]` | CMake ≥ 3.25, Ninja, GCC ou Clang |
|
||||
| Lancer Studio | `.\scripts\studio.ps1 [-Core ws://hôte:port]` ou `./scripts/studio.sh [ws://hôte:port]` | Node.js 24 |
|
||||
| Windows, cuDNN pour l'inférence GPU | `.\scripts\install-cudnn.ps1 [-Version <x.y.z>] [-CudaMajor 12\|13] [-DryRun]` | CUDA Toolkit installé + GPU NVIDIA ; télécharge cuDNN (~1,3 Go), demande d'accepter la licence NVIDIA et définit `CUDNN_PATH` (utilisateur, sans droits admin) |
|
||||
|
||||
Chaque script PowerShell affiche son aide avec `-Help`.
|
||||
|
||||
|
||||
@@ -74,10 +74,9 @@ if ($Help) {
|
||||
}
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
. (Join-Path $PSScriptRoot "lib\SequaDownload.ps1")
|
||||
$nvidiaBase = "https://developer.download.nvidia.com/compute/cuda"
|
||||
|
||||
function Write-Step([string]$text) { Write-Host "==> $text" -ForegroundColor Cyan }
|
||||
|
||||
function Get-DriverCudaVersion {
|
||||
$smi = Get-Command nvidia-smi.exe -ErrorAction SilentlyContinue
|
||||
if (-not $smi) { throw "nvidia-smi introuvable : pilote NVIDIA absent." }
|
||||
@@ -112,54 +111,6 @@ function Get-InstallerInfo([string]$version) {
|
||||
}
|
||||
}
|
||||
|
||||
# Empreinte via .NET : Get-FileHash manque dans certains Windows PowerShell 5.1
|
||||
# (PSModulePath pointant vers les modules de PowerShell 7).
|
||||
function Get-Md5([string]$path) {
|
||||
$stream = [System.IO.File]::OpenRead($path)
|
||||
$md5 = [System.Security.Cryptography.MD5]::Create()
|
||||
try {
|
||||
([System.BitConverter]::ToString($md5.ComputeHash($stream))).Replace("-", "")
|
||||
}
|
||||
finally {
|
||||
$md5.Dispose()
|
||||
$stream.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
function Get-RemoteSize([string]$url) {
|
||||
$headers = & curl.exe -sIL --fail $url
|
||||
if ($LASTEXITCODE -ne 0) { throw "Serveur injoignable : $url" }
|
||||
$length = $headers | Select-String -Pattern '^Content-Length:\s*(\d+)' | Select-Object -Last 1
|
||||
if (-not $length) { throw "Taille du fichier inconnue (pas de Content-Length)." }
|
||||
[int64]$length.Matches[0].Groups[1].Value
|
||||
}
|
||||
|
||||
# Telechargement avec reprise : curl -C - repart de la taille du fichier .part.
|
||||
function Invoke-ResumableDownload([string]$url, [string]$destination, [int64]$expectedSize, [int]$maxAttempts) {
|
||||
$part = "$destination.part"
|
||||
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
|
||||
$have = if (Test-Path $part) { (Get-Item $part).Length } else { 0 }
|
||||
if ($have -eq $expectedSize) { break }
|
||||
if ($have -gt $expectedSize) {
|
||||
Write-Host " Fichier partiel plus grand que prevu : on recommence." -ForegroundColor Yellow
|
||||
Remove-Item $part -Force
|
||||
$have = 0
|
||||
}
|
||||
$pct = [math]::Round(100.0 * $have / $expectedSize, 1)
|
||||
Write-Host (" Tentative {0}/{1} : {2:N0} / {3:N0} octets ({4} %)" -f $attempt, $maxAttempts, $have, $expectedSize, $pct)
|
||||
& curl.exe -L --fail -C - --retry 5 --retry-delay 5 --retry-all-errors --speed-limit 1024 --speed-time 60 -o $part $url
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host " curl interrompu (code $LASTEXITCODE), reprise dans 10 s..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
}
|
||||
$final = if (Test-Path $part) { (Get-Item $part).Length } else { 0 }
|
||||
if ($final -ne $expectedSize) {
|
||||
throw "Telechargement incomplet apres $maxAttempts tentatives ($final / $expectedSize octets). Relancer le script pour reprendre."
|
||||
}
|
||||
Move-Item $part $destination -Force
|
||||
}
|
||||
|
||||
# --- Verifications ----------------------------------------------------------
|
||||
if ($Version -notmatch '^[0-9]+\.[0-9]+\.[0-9]+$') { throw "-Version attend une version complete, ex. 13.5.0 (recu : $Version)." }
|
||||
$wanted = [version]$Version
|
||||
@@ -208,16 +159,13 @@ if (Test-Path $installer) {
|
||||
Write-Step "Installateur deja present dans le cache"
|
||||
}
|
||||
else {
|
||||
$drive = Get-PSDrive -Name ([System.IO.Path]::GetPathRoot($CacheDir).Substring(0, 1))
|
||||
if ($drive.Free -lt ($size * 2)) {
|
||||
throw ("Espace disque insuffisant sur {0}: ({1:N1} Go libres, {2:N1} Go conseilles)." -f $drive.Name, ($drive.Free / 1GB), ($size * 2 / 1GB))
|
||||
}
|
||||
Assert-FreeSpace -Path $CacheDir -Needed ($size * 2)
|
||||
Write-Step "Telechargement avec reprise"
|
||||
Invoke-ResumableDownload $info.Url $installer $size $MaxAttempts
|
||||
}
|
||||
|
||||
Write-Step "Verification MD5"
|
||||
$hash = Get-Md5 $installer
|
||||
$hash = Get-FileDigest -Path $installer -Algorithm MD5
|
||||
if ($hash -ne $info.Md5) {
|
||||
Remove-Item $installer -Force
|
||||
throw "MD5 incorrect ($hash) : fichier supprime, relancer le script pour le retelecharger."
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
# 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_<version>.json et l'entree Windows x86_64 / cuda<maj> ;
|
||||
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 : <InstallDir>\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\<version>_cuda<maj>.
|
||||
|
||||
.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)."
|
||||
@@ -0,0 +1,70 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
# Fonctions de telechargement communes (install-cuda.ps1, install-cudnn.ps1).
|
||||
# ASCII uniquement : compatible Windows PowerShell 5.1 sans BOM.
|
||||
# Ce fichier n'est pas un script autonome : il se dot-source depuis un autre script.
|
||||
|
||||
# Titre d'etape, meme style dans tous les scripts d'installation.
|
||||
function Write-Step([string]$text) {
|
||||
Write-Host "==> $text" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# Taille distante via une requete HEAD (curl.exe), en suivant les redirections.
|
||||
function Get-RemoteSize([string]$url) {
|
||||
$headers = & curl.exe -sIL --fail $url
|
||||
if ($LASTEXITCODE -ne 0) { throw "Serveur injoignable : $url" }
|
||||
$length = $headers | Select-String -Pattern '^Content-Length:\s*(\d+)' | Select-Object -Last 1
|
||||
if (-not $length) { throw "Taille du fichier inconnue (pas de Content-Length)." }
|
||||
[int64]$length.Matches[0].Groups[1].Value
|
||||
}
|
||||
|
||||
# Empreinte generique via .NET : Get-FileHash manque dans certains Windows PowerShell 5.1
|
||||
# (PSModulePath pointant vers les modules de PowerShell 7).
|
||||
function Get-FileDigest([string]$path, [string]$Algorithm = "SHA256") {
|
||||
switch ($Algorithm.ToUpperInvariant()) {
|
||||
"SHA256" { $hasher = [System.Security.Cryptography.SHA256]::Create() }
|
||||
"MD5" { $hasher = [System.Security.Cryptography.MD5]::Create() }
|
||||
default { throw "Algorithme d'empreinte non gere : $Algorithm (SHA256 ou MD5)." }
|
||||
}
|
||||
$stream = [System.IO.File]::OpenRead($path)
|
||||
try {
|
||||
([System.BitConverter]::ToString($hasher.ComputeHash($stream))).Replace("-", "")
|
||||
}
|
||||
finally {
|
||||
$hasher.Dispose()
|
||||
$stream.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
# Verifie qu'il reste au moins $Needed octets libres sur le volume de $Path.
|
||||
function Assert-FreeSpace([string]$Path, [int64]$Needed) {
|
||||
$drive = Get-PSDrive -Name ([System.IO.Path]::GetPathRoot($Path).Substring(0, 1))
|
||||
if ($drive.Free -lt $Needed) {
|
||||
throw ("Espace disque insuffisant sur {0}: ({1:N1} Go libres, {2:N1} Go conseilles)." -f $drive.Name, ($drive.Free / 1GB), ($Needed / 1GB))
|
||||
}
|
||||
}
|
||||
|
||||
# Telechargement avec reprise : curl -C - repart de la taille du fichier .part.
|
||||
function Invoke-ResumableDownload([string]$url, [string]$destination, [int64]$expectedSize, [int]$maxAttempts) {
|
||||
$part = "$destination.part"
|
||||
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
|
||||
$have = if (Test-Path $part) { (Get-Item $part).Length } else { 0 }
|
||||
if ($have -eq $expectedSize) { break }
|
||||
if ($have -gt $expectedSize) {
|
||||
Write-Host " Fichier partiel plus grand que prevu : on recommence." -ForegroundColor Yellow
|
||||
Remove-Item $part -Force
|
||||
$have = 0
|
||||
}
|
||||
$pct = [math]::Round(100.0 * $have / $expectedSize, 1)
|
||||
Write-Host (" Tentative {0}/{1} : {2:N0} / {3:N0} octets ({4} %)" -f $attempt, $maxAttempts, $have, $expectedSize, $pct)
|
||||
& curl.exe -L --fail -C - --retry 5 --retry-delay 5 --retry-all-errors --speed-limit 1024 --speed-time 60 -o $part $url
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host " curl interrompu (code $LASTEXITCODE), reprise dans 10 s..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
}
|
||||
$final = if (Test-Path $part) { (Get-Item $part).Length } else { 0 }
|
||||
if ($final -ne $expectedSize) {
|
||||
throw "Telechargement incomplet apres $maxAttempts tentatives ($final / $expectedSize octets). Relancer le script pour reprendre."
|
||||
}
|
||||
Move-Item $part $destination -Force
|
||||
}
|
||||
Reference in new issue
Block a user