<# .SYNOPSIS Verificare de sintaxa prin compilare headless a fisierelor .prg din FLORA2ROA. .DESCRIPTION Cel mai ieftin test dupa orice editare de .prg (vezi COMUN\docs\depanare_testare_vfp.md, sect. 4). Compileaza COPII ale fisierelor intr-un folder temporar, ca .fxp-urile si .err-urile generate sa nu murdareasca arborele proiectului. VFP nu rezolva simboluri externe la compilare, deci copia izolata e suficienta pentru sintaxa. Toate fisierele se compileaza intr-o SINGURA rulare vfp9: lansarile repetate de vfp9 din aceeasi sesiune PowerShell esueaza tacut (procesul porneste dar nu executa - vezi COMUN\docs\testare-ui-vfp.md, capcana d). Verdict per fisier: existenta unui .err dupa compilare. Se raporteaza si liniile mai lungi de 250 de caractere (capcana VFP: literal/linie prea lunga = eroare de compilare sau eroare 11 derutanta la runtime). NU inlocuieste testul functional: prinde sintaxa, nu apeluri catre metode inexistente. .PARAMETER Files Fisiere .prg de verificat. Implicit toate .prg-urile din radacina proiectului si din Teste\. .PARAMETER ProjectRoot Radacina proiectului (implicit folderul parinte al acestui script). .EXAMPLE powershell -ExecutionPolicy Bypass -File Teste\verifica_sintaxa.ps1 .EXAMPLE powershell -ExecutionPolicy Bypass -File Teste\verifica_sintaxa.ps1 -Files myutils.prg,app.prg #> [CmdletBinding()] param( [string[]]$Files, [string]$ProjectRoot, [int]$MaxLineLen = 250, [string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' ) $ErrorActionPreference = 'Stop' if (-not $ProjectRoot) { $ProjectRoot = Split-Path $PSScriptRoot -Parent } if (-not (Test-Path $Vfp)) { throw "Nu gasesc vfp9.exe: $Vfp" } if (-not $Files) { $Files = @(Get-ChildItem -Path $ProjectRoot -Filter '*.prg' -File | ForEach-Object { $_.FullName }) $Files += @(Get-ChildItem -Path (Join-Path $ProjectRoot 'Teste') -Filter '*.prg' -File -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName }) } $Files = $Files | ForEach-Object { if ([IO.Path]::IsPathRooted($_)) { $_ } else { Join-Path $ProjectRoot $_ } } $work = Join-Path $env:TEMP ('flora2roa_syntax_' + [Guid]::NewGuid().ToString('N').Substring(0,8)) New-Item -ItemType Directory -Force -Path $work | Out-Null # fisierele .h referite prin #INCLUDE trebuie sa fie langa copie, altfel compilarea da eroare Get-ChildItem -Path $ProjectRoot -Filter '*.h' -File -ErrorAction SilentlyContinue | ForEach-Object { Copy-Item $_.FullName (Join-Path $work $_.Name) -Force } # omoara doar instantele vfp9 proprii (linia de comanda contine folderul de lucru temporar) function Stop-VfpProprii([string]$Marker) { Get-CimInstance Win32_Process -Filter "Name='vfp9.exe'" -ErrorAction SilentlyContinue | ForEach-Object { if ($_.CommandLine -and $_.CommandLine.ToLower().Contains($Marker.ToLower())) { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } } } # copiaza sursele + strange liniile prea lungi (analiza statica, fara VFP) $copii = @() $liniiLungi = @{} foreach ($f in $Files) { if (-not (Test-Path $f)) { Write-Warning "lipsa: $f"; continue } $nume = [IO.Path]::GetFileName($f) $copie = Join-Path $work $nume Copy-Item -LiteralPath $f -Destination $copie -Force $copii += [pscustomobject]@{ Nume = $nume; Sursa = $f; Copie = $copie } $lungi = @() $linii = [IO.File]::ReadAllLines($f, [Text.Encoding]::GetEncoding(1252)) for ($i = 0; $i -lt $linii.Length; $i++) { if ($linii[$i].Length -gt $MaxLineLen) { $lungi += ('{0} (len={1})' -f ($i + 1), $linii[$i].Length) } } if ($lungi) { $liniiLungi[$nume] = ($lungi -join '; ') } } if (-not $copii) { Write-Host 'Nimic de verificat.'; return } # un singur .prg care compileaza toate copiile (SAFETY OFF: fara dialog "overwrite?" headless) $linii = @('SET SAFETY OFF', 'SET TALK OFF') foreach ($c in $copii) { $linii += ('lcT = "{0}"' -f $c.Copie); $linii += 'COMPILE (lcT)' } $linii += 'STRTOFILE("gata", "' + (Join-Path $work '_gata.txt') + '")' $linii += 'QUIT' $tmpPrg = Join-Path $work '_compile_all.prg' Set-Content -Path $tmpPrg -Value (($linii -join "`r`n") + "`r`n") -Encoding ascii # -A ignora config.fpw implicit, -T sare peste splash; ambele obligatorii headless $p = Start-Process -FilePath $Vfp -ArgumentList @('-A', '-T', "`"$tmpPrg`"") -PassThru -WindowStyle Minimized if (-not $p.WaitForExit(180000)) { Write-Warning 'Timeout la compilare (180s).'; try { $p.Kill() } catch {} } Stop-VfpProprii $work if (-not (Test-Path (Join-Path $work '_gata.txt'))) { Write-Warning 'Compilarea nu a ajuns la final - verdictul per fisier poate fi incomplet.' } $rezultate = foreach ($c in $copii) { $err = [IO.Path]::ChangeExtension($c.Copie, 'err') $fxp = [IO.Path]::ChangeExtension($c.Copie, 'fxp') $erori = if (Test-Path $err) { (Get-Content -Raw $err).Trim() } else { '' } [pscustomobject]@{ Fisier = $c.Nume Sintaxa = if ($erori) { 'ERORI' } elseif (Test-Path $fxp) { 'OK' } else { 'NECOMPILAT' } Erori = $erori LiniiLungi = $(if ($liniiLungi.ContainsKey($c.Nume)) { $liniiLungi[$c.Nume] } else { '' }) } } Remove-Item -Recurse -Force $work -ErrorAction SilentlyContinue $rezultate | Format-Table Fisier, Sintaxa -AutoSize foreach ($r in $rezultate | Where-Object { $_.Erori }) { Write-Host '' Write-Warning ("ERORI in {0}:`n{1}" -f $r.Fisier, $r.Erori) } foreach ($r in $rezultate | Where-Object { $_.LiniiLungi }) { Write-Host '' Write-Warning ("Linii peste $MaxLineLen caractere in {0}: {1}" -f $r.Fisier, $r.LiniiLungi) } $nrProbleme = @($rezultate | Where-Object { $_.Sintaxa -ne 'OK' }).Count Write-Host '' Write-Host ("Gata. Fisiere verificate: {0} Cu probleme: {1}" -f @($rezultate).Count, $nrProbleme) exit ([int]($nrProbleme -gt 0))