Modules/businessdev.ALbuild.Apps/Private/ConvertFrom-BcCompilerOutput.ps1
|
function ConvertFrom-BcCompilerOutput { <# .SYNOPSIS Parses AL compiler (alc) output into structured diagnostics. .DESCRIPTION Internal, pure helper (no I/O) so it is unit-testable. AL diagnostics follow the MSBuild convention: <file>(<line>,<col>): <severity> <code>: <message> where severity is error | warning | info and code is like AL0118 / AA0137 / AW0006. Some diagnostics have no source location (<severity> <code>: <message>). Returns one object per matched diagnostic (Severity, Code, File, Line, Column, Message); non-diagnostic lines (progress, summary counts, ...) are ignored. .PARAMETER Output The raw compiler output (stdout+stderr) as a single string. .OUTPUTS PSCustomObject[]: Severity, Code, File, Line, Column, Message. #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param([string] $Output) $result = [System.Collections.Generic.List[PSCustomObject]]::new() if ([string]::IsNullOrEmpty($Output)) { return $result.ToArray() } # 'file(line,col): sev CODE: message' and the location-less 'sev CODE: message'. $withLoc = [regex] '^(?<file>.+?)\((?<line>\d+),(?<col>\d+)\):\s*(?<sev>error|warning|info)\s+(?<code>[A-Za-z]{2}\d+):\s*(?<msg>.*)$' $noLoc = [regex] '^\s*(?<sev>error|warning|info)\s+(?<code>[A-Za-z]{2}\d+):\s*(?<msg>.*)$' foreach ($line in ($Output -split "`r?`n")) { $t = $line.TrimEnd() if (-not $t) { continue } $m = $withLoc.Match($t) if ($m.Success) { $result.Add([PSCustomObject]@{ Severity = $m.Groups['sev'].Value.ToLowerInvariant() Code = $m.Groups['code'].Value File = $m.Groups['file'].Value.Trim() Line = [int] $m.Groups['line'].Value Column = [int] $m.Groups['col'].Value Message = $m.Groups['msg'].Value.Trim() }) continue } $m = $noLoc.Match($t) if ($m.Success) { $result.Add([PSCustomObject]@{ Severity = $m.Groups['sev'].Value.ToLowerInvariant() Code = $m.Groups['code'].Value File = '' Line = 0 Column = 0 Message = $m.Groups['msg'].Value.Trim() }) } } return $result.ToArray() } |