I'm using a PowerShell script to control different compilation steps of an compiler (ghdl.exe).
The compiler has 3 different output formats:
- No output and no error => $LastExitCode = 0
- outputs on stderr (warnings), but no errors => $LastExitCode = 0
- outputs on stderr (errors), and maybe warnings => $LastExitCode != 0
Because handling of stderr and stdout seams to be very buggy, I used the method presented in this StackOverflow post: PowerShell: Manage errors with Invoke-Expression
Here is my implementation with addition message coloring:
function Format-NativeCommandStreams
{ param([Parameter(ValueFromPipeline=$true)]$InputObject)
begin
{ $ErrorRecordFound = $false }
process
{ if (-not $InputObject)
{ Write-Host "Empty" }
elseif ($InputObject -is [System.Management.Automation.ErrorRecord])
{ $ErrorRecordFound = $true
$text = $InputObject.ToString()
Write-Host $text -ForegroundColor Gray
$stdErr = $InputObject.TargetObject
if ($stdErr)
{ #Write-Host ("err: type=" + $stdErr.GetType() + " " + $stdErr)
if ($stdErr.Contains("warning"))
{ Write-Host "WARNING: " -NoNewline -ForegroundColor Yellow }
else
{ Write-Host "ERROR: " -NoNewline -ForegroundColor Red }
Write-Host $stdErr
}
}
else
{ $stdOut = $InputObject
if ($stdOut.Contains("warning"))
{ Write-Host "WARNING: " -NoNewline -ForegroundColor Yellow }
else
{ Write-Host "ERROR: " -NoNewline -ForegroundColor Red }
Write-Host $stdOut
}
}
end
{ $ErrorRecordFound }
}
Usage:
$Options = @(.....)
$Expr = "ghdl.exe -a " + ($Options -join " ") + " " + $File + " 2>&1"
$ret = Invoke-Expression $Expr | Format-NativeCommandStreams
Normally, the compiler emits one message (error or warning) per line. As shown in the screenshot below, some messages got chopped in up to 8 lines. That's the reason why my output coloring does not work as expected. More over some lines are detected as errors (false positives), so I can't find the real error in the logs.
Example:
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd:
39963:
53
:
warning:
universal integer bound must be numeric literal or attribute
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd
:41794:36:warning: universal integer bound must be numeric literal or attribute
Expected Result:
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd:39963:53:warning: universal integer bound must be numeric literal or attribute
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd:41794:36:warning: universal integer bound must be numeric literal or attribute
As far as I can see, the compiler (ghdl.exe) does emit the messages as full lines.
Questions:
- Why does this happen?
- Who can I solve this?
You can a bit of debugging to sort this out. I suggest starting with something like this:
This will list the line number, type of the output line, and each live of the output. You may have to tweak the code some to get the output to look OK.
From there you can see if the compiler is really splitting up errors into multiple lines. If it is you can try devise a strategy for determining which lines are stdout and which are stderr. If not, then you'll have some clues to debugging your script above.
Or can bag this whole approach and use the .NET system.diagnostics.process class and redirect stdout and stderr as separate streams. Use the Start method that takes a ProcessStartInfo. You should be able to google examples of doing this if you need to.
Solution
The complete output on
stderr
of the executable is simply split across several objects of typeSystem.Management.Automation.ErrorRecord
. The actual splitting seems to be non deterministic (*). Moreover, the partial strings are stored inside the propertyException
instead ofTargetObject
. Only the firstErrorRecord
has a non-nullTargetObject
. That is, why subsequent lines of your output containing the string"warning"
are not formatted in yellow and white, like this one:Your grey output comes from the
toString()
method of eachErrorRecord
which returns the value of the propertyException.Message
of this record. So one must concatenate all messages together to get the whole output before formatting it. Newlines are preserved in these messages.EDIT: (*) It depends on the order of write/flush calls of the program in relation to the read calls of the Powershell. If one adds a
fflush(stderr)
after eachfprintf()
in my test program below, there will be much moreErrorRecord
objects. Except the first one, which seems deterministic, some of them include 2 output lines and some of them 3.My testbench
Instead of using GHDL I started with a new Visual Studio project and created a console application (HelloWorldEx) with the following code. It simply prints out a lot of numbered lines on
stderr
Then I compiled the program and executed it inside the Powershell with: (EDIT: removed debug code from my own script)
This was the output of the script. As you can see, the output of my program is split accross 3
ErrorRecords
(the actual might differ):Just for completeness, here are my current CommandLets, which restore the error messages as a single line and color them as wanted:
Usage:
CommandLet to restore the error messages:
CommandLet to color warnings and errors: