From 1ec6443f140ca5bddd8551fc990f1784730f7570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ange-Marie=20MAURIN=20-=20A2M=20Syst=C3=A8mes?= Date: Fri, 6 Nov 2026 19:36:18 +0100 Subject: [PATCH] =?UTF-8?q?build:=20t=C3=A9l=C3=A9chargement=20CUDA=20avec?= =?UTF-8?q?=20reprise=20et=20v=C3=A9rification=20SHA256?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'installateur (~2,7 Go) est téléchargé par curl -C - dans un cache local et reprend après interruption ou relance du script ; URL et SHA256 lus dans le manifeste winget ; options -DryRun et -DownloadOnly ; SHA256 calculé via .NET. --- scripts/install-cuda.ps1 | 137 ++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 17 deletions(-) diff --git a/scripts/install-cuda.ps1 b/scripts/install-cuda.ps1 index 84c9ea7..664a1cb 100644 --- a/scripts/install-cuda.ps1 +++ b/scripts/install-cuda.ps1 @@ -1,22 +1,29 @@ # SPDX-License-Identifier: MPL-2.0 # -# Installe le CUDA Toolkit NVIDIA via winget, pour le prototype prototypes/gpu_interop +# Installe le CUDA Toolkit NVIDIA, pour le prototype prototypes/gpu_interop # (etapes 2 et suivantes). CUDA est un composant proprietaire : il reste un plugin # facultatif de SEQUA (docs, par. 10.2) et sa licence est acceptee par l'utilisateur -# lui-meme pendant l'installation (pas d'acceptation automatique ici). +# lui-meme dans l'installateur NVIDIA (pas d'acceptation automatique ici). # -# A lancer dans une fenetre PowerShell externe (installation longue, demande UAC) : -# powershell -ExecutionPolicy Bypass -File scripts\install-cuda.ps1 -DryRun # verifier sans installer -# powershell -ExecutionPolicy Bypass -File scripts\install-cuda.ps1 # installer -# powershell -ExecutionPolicy Bypass -File scripts\install-cuda.ps1 -Version 13.5 +# L'installateur (~2,8 Go) est telecharge avec reprise : si le telechargement est +# interrompu, relancer simplement le script, il repart du dernier octet recu. +# L'URL et le SHA256 viennent du manifeste winget officiel (Nvidia.CUDA). +# +# A lancer dans une fenetre PowerShell externe (telechargement long, UAC) : +# powershell -ExecutionPolicy Bypass -File scripts\install-cuda.ps1 -DryRun # verifier sans rien faire +# powershell -ExecutionPolicy Bypass -File scripts\install-cuda.ps1 -DownloadOnly # telecharger seulement +# powershell -ExecutionPolicy Bypass -File scripts\install-cuda.ps1 # telecharger puis installer # # Messages en ASCII : compatible Windows PowerShell 5.1 sans BOM. [CmdletBinding()] param( [string]$Version = "13.5", + [string]$CacheDir = (Join-Path $env:LOCALAPPDATA "SEQUA\downloads\cuda"), [switch]$DryRun, - [switch]$Force + [switch]$DownloadOnly, + [switch]$Force, + [int]$MaxAttempts = 50 ) $ErrorActionPreference = "Stop" @@ -48,6 +55,66 @@ function Get-Winget { throw "winget introuvable. Installer 'App Installer' depuis le Microsoft Store." } +# Lit l'URL et le SHA256 de l'installateur dans le manifeste winget +# (analyse independante de la langue : on cherche l'URL .exe et une empreinte de 64 hex). +function Get-InstallerInfo([string]$winget, [string]$version) { + $lines = & $winget show --id Nvidia.CUDA --version $version --exact --source winget --accept-source-agreements 2>&1 + if ($LASTEXITCODE -ne 0) { throw "Version $version introuvable dans winget (winget show --id Nvidia.CUDA --versions)." } + $text = $lines -join "`n" + $url = [regex]::Match($text, 'https://\S+?\.exe').Value + $sha = [regex]::Match($text, '\b[0-9a-fA-F]{64}\b').Value + if (-not $url -or -not $sha) { throw "URL ou SHA256 de l'installateur absents du manifeste winget." } + [pscustomobject]@{ Url = $url; Sha256 = $sha.ToUpperInvariant() } +} + +# SHA256 via .NET : Get-FileHash manque dans certains Windows PowerShell 5.1 +# (PSModulePath pointant vers les modules de PowerShell 7). +function Get-Sha256([string]$path) { + $stream = [System.IO.File]::OpenRead($path) + $sha = [System.Security.Cryptography.SHA256]::Create() + try { + ([System.BitConverter]::ToString($sha.ComputeHash($stream))).Replace("-", "") + } + finally { + $sha.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 ---------------------------------------------------------- Write-Step "Pilote NVIDIA" $driverCuda = Get-DriverCudaVersion @@ -71,22 +138,57 @@ if ($same -and -not $Force) { exit 0 } -$winget = Get-Winget -Write-Step "Paquet winget Nvidia.CUDA $Version" -& $winget show --id Nvidia.CUDA --version $Version --exact --source winget --accept-source-agreements | - Select-String -Pattern '^(Found|Trouv|Version|Publisher|Editeur|License|Licence|Installer Url|URL)' | - ForEach-Object { Write-Host " $($_.Line.Trim())" } -if ($LASTEXITCODE -ne 0) { throw "Version $Version introuvable dans winget (winget show --id Nvidia.CUDA --versions)." } +Write-Step "Manifeste winget Nvidia.CUDA $Version" +$info = Get-InstallerInfo (Get-Winget) $Version +$size = Get-RemoteSize $info.Url +$fileName = [System.IO.Path]::GetFileName(([uri]$info.Url).AbsolutePath) +$installer = Join-Path $CacheDir $fileName +Write-Host " URL : $($info.Url)" +Write-Host " SHA256 : $($info.Sha256)" +Write-Host (" Taille : {0:N1} Go" -f ($size / 1GB)) +Write-Host " Cache : $installer" if ($DryRun) { - Write-Host "DryRun : aucune installation effectuee." -ForegroundColor Yellow + $partial = "$installer.part" + if (Test-Path $installer) { Write-Host " Installateur deja telecharge." } + elseif (Test-Path $partial) { Write-Host (" Telechargement partiel : {0:N0} octets." -f (Get-Item $partial).Length) } + Write-Host "DryRun : rien n'a ete telecharge ni installe." -ForegroundColor Yellow + exit 0 +} + +# --- Telechargement --------------------------------------------------------- +New-Item -ItemType Directory -Force -Path $CacheDir | Out-Null +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)) + } + Write-Step "Telechargement avec reprise" + Invoke-ResumableDownload $info.Url $installer $size $MaxAttempts +} + +Write-Step "Verification SHA256" +$hash = Get-Sha256 $installer +if ($hash -ne $info.Sha256) { + Remove-Item $installer -Force + throw "SHA256 incorrect ($hash) : fichier supprime, relancer le script pour le retelecharger." +} +Write-Host " OK" + +if ($DownloadOnly) { + Write-Host "Installateur pret : $installer" -ForegroundColor Green exit 0 } # --- Installation ----------------------------------------------------------- Write-Step "Installation (UAC et acceptation de la licence NVIDIA demandees)" -& $winget install --id Nvidia.CUDA --version $Version --exact --source winget --accept-source-agreements --interactive -if ($LASTEXITCODE -ne 0) { throw "winget install a echoue (code $LASTEXITCODE)." } +Write-Host " Choisir l'installation PERSONNALISEE et decocher tout composant pilote graphique" -ForegroundColor Yellow +Write-Host " s'il est propose, pour conserver le pilote actuel." -ForegroundColor Yellow +$process = Start-Process -FilePath $installer -Wait -PassThru +if ($process.ExitCode -ne 0) { throw "L'installateur NVIDIA a retourne le code $($process.ExitCode)." } # --- Verification ----------------------------------------------------------- Write-Step "Verification" @@ -99,5 +201,6 @@ if (-not $nvcc -or -not (Test-Path $nvcc)) { } & $nvcc --version Write-Host "" -Write-Host "CUDA installe. Ouvrir un NOUVEAU terminal (PATH et CUDA_PATH mis a jour), puis :" -ForegroundColor Green +Write-Host "CUDA installe. L'installateur peut etre supprime du cache : $installer" -ForegroundColor Green +Write-Host "Ouvrir un NOUVEAU terminal (PATH et CUDA_PATH mis a jour), puis :" Write-Host " .\scripts\build.ps1 -Preset windows-msvc-prototypes"