3333 -Config is the RAD Studio MSBuild property name (/p:Config); common values
3434 are Debug and Release.
3535
36- By default MSBuild output is captured and returned in the result object's
37- .output property. Use -ShowOutput to stream output to stdout in real time;
38- in that case .output is null and errors are written via Write-Error .
36+ MSBuild output is always captured and returned in the result object's
37+ .output property. Use -ShowOutput to also stream output to stdout in real
38+ time; .output is populated in both cases .
3939
4040 Exit codes:
4141 0 success
5353 Justification= ' Script accepts at most one piped installation object; end-block semantics are correct.' )]
5454[Diagnostics.CodeAnalysis.SuppressMessageAttribute (' PSUseSingularNouns' , ' Get-RsvarsEnvLines' ,
5555 Justification= ' Function returns multiple KEY=VALUE lines from cmd.exe set; plural noun is accurate.' )]
56+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute (' PSAvoidUsingWriteHost' , ' ' ,
57+ Justification= ' Write-Host is intentional: -ShowOutput streams build text directly to the console host.' )]
5658param (
5759 [Parameter (ValueFromPipeline = $true )]
5860 [psobject ]$DelphiInstallation ,
@@ -100,7 +102,7 @@ $ExitRootDirError = 3
100102$ExitProjectNotFound = 4
101103$ExitBuildFailed = 5
102104
103- $script :Version = ' 0.5 .0'
105+ $script :Version = ' 0.6 .0'
104106
105107# Resolve the Delphi root dir from the explicit -RootDir parameter or from a
106108# piped delphi-inspect result object (.rootDir property).
@@ -163,21 +165,18 @@ function Invoke-RsvarsEnvironment {
163165}
164166
165167# Invoke msbuild.exe with the given arguments.
166- # Returns [pscustomobject]@{ ExitCode; Output } where Output is $null when
167- # -ShowOutput is set (output streams to stdout instead of being captured).
168+ # Returns [pscustomobject]@{ ExitCode; Output } where Output is always the
169+ # captured build text. When -ShowOutput is set the text is also written to
170+ # the host so the caller sees it in real time.
168171# Separated into its own function so tests can mock it.
169172function Invoke-MsbuildExe {
170173 param (
171174 [string []]$Arguments ,
172175 [switch ]$ShowOutput
173176 )
174177
175- if ($ShowOutput ) {
176- & msbuild.exe @Arguments | Out-Host
177- return [pscustomobject ]@ { ExitCode = $LASTEXITCODE ; Output = $null }
178- }
179-
180178 $output = & msbuild.exe @Arguments 2>&1 | Out-String
179+ if ($ShowOutput ) { Write-Host $output }
181180 return [pscustomobject ]@ { ExitCode = $LASTEXITCODE ; Output = $output }
182181}
183182
@@ -208,11 +207,6 @@ function Invoke-MsbuildProject {
208207 if (-not [string ]::IsNullOrWhiteSpace($ExeOutputDir )) { $msbuildArgs += " /p:DCC_ExeOutput=$ExeOutputDir " }
209208 if (-not [string ]::IsNullOrWhiteSpace($DcuOutputDir )) { $msbuildArgs += " /p:DCC_DcuOutput=$DcuOutputDir " }
210209
211- if ($UnitSearchPath.Count -gt 0 ) {
212- $unitSearchValue = ' $(DCC_UnitSearchPath);' + ($UnitSearchPath -join ' ;' )
213- $msbuildArgs += " /p:DCC_UnitSearchPath=$unitSearchValue "
214- }
215-
216210 if ($UnitSearchPath.Count -gt 0 ) {
217211 $unitSearchValue = ' $(DCC_UnitSearchPath);' + ($UnitSearchPath -join ' ;' )
218212 $msbuildArgs += " /p:DCC_UnitSearchPath=`" $unitSearchValue `" "
@@ -226,6 +220,56 @@ function Invoke-MsbuildProject {
226220 return Invoke-MsbuildExe - Arguments $msbuildArgs - ShowOutput:$ShowOutput
227221}
228222
223+ # Parse the dcc32.exe invocation line from captured msbuild output and extract
224+ # the exe output dir (-E flag) and DCU output dir (-NO flag).
225+ # Paths are resolved to absolute using the project file directory as base.
226+ # Returns [pscustomobject]@{ ExeOutputDir; DcuOutputDir } -- either may be $null.
227+ function Get-BuildOutputDir {
228+ param (
229+ [string ]$Output ,
230+ [string ]$ProjectFileDir
231+ )
232+
233+ $result = [pscustomobject ]@ { ExeOutputDir = $null ; DcuOutputDir = $null }
234+ if ([string ]::IsNullOrWhiteSpace($Output )) { return $result }
235+
236+ $dcc32Line = ($Output -split " `n " ) |
237+ Where-Object { $_ -match ' [/\\]dcc32\.exe\s' } |
238+ Select-Object - First 1
239+ if (-not $dcc32Line ) { return $result }
240+
241+ if ($dcc32Line -match ' \s-E(\S+)' ) {
242+ $result.ExeOutputDir = [System.IO.Path ]::GetFullPath(
243+ [System.IO.Path ]::Combine($ProjectFileDir , $Matches [1 ]))
244+ }
245+
246+ if ($dcc32Line -match ' \s-NO(\S+)' ) {
247+ $result.DcuOutputDir = [System.IO.Path ]::GetFullPath(
248+ [System.IO.Path ]::Combine($ProjectFileDir , $Matches [1 ]))
249+ }
250+
251+ return $result
252+ }
253+
254+ # Parse the MSBuild summary block from captured output and return warning and
255+ # error counts as integers.
256+ # Returns [pscustomobject]@{ Warnings; Errors }.
257+ function Get-BuildCount {
258+ param ([string ]$Output )
259+
260+ $warnings = 0
261+ $errors = 0
262+ if (-not [string ]::IsNullOrWhiteSpace($Output )) {
263+ $wMatch = [regex ]::Match($Output , ' ^\s*(\d+)\s+Warning\(s\)' ,
264+ [System.Text.RegularExpressions.RegexOptions ]::Multiline)
265+ $eMatch = [regex ]::Match($Output , ' ^\s*(\d+)\s+Error\(s\)' ,
266+ [System.Text.RegularExpressions.RegexOptions ]::Multiline)
267+ if ($wMatch.Success ) { $warnings = [int ]$wMatch.Groups [1 ].Value }
268+ if ($eMatch.Success ) { $errors = [int ]$eMatch.Groups [1 ].Value }
269+ }
270+ return [pscustomobject ]@ { Warnings = $warnings ; Errors = $errors }
271+ }
272+
229273# Guard: skip top-level execution when the script is dot-sourced for testing.
230274if ($MyInvocation.InvocationName -eq ' .' ) { return }
231275
@@ -277,6 +321,11 @@ try {
277321 - Define $Define `
278322 - ShowOutput:$ShowOutput
279323
324+ $parsedDirs = Get-BuildOutputDir `
325+ - Output $buildResult.Output `
326+ - ProjectFileDir (Split-Path $resolvedProjectFile - Parent)
327+ $counts = Get-BuildCount - Output $buildResult.Output
328+
280329 $resultObj = [pscustomobject ]@ {
281330 scriptVersion = $script :Version
282331 projectFile = $resolvedProjectFile
@@ -286,11 +335,13 @@ try {
286335 define = $Define
287336 rootDir = $resolvedRootDir
288337 rsvarsPath = $rsvarsPath
289- exeOutputDir = if ([string ]::IsNullOrWhiteSpace($ExeOutputDir )) { $null } else { $ExeOutputDir }
290- dcuOutputDir = if ([string ]::IsNullOrWhiteSpace($DcuOutputDir )) { $null } else { $DcuOutputDir }
338+ exeOutputDir = if (-not [string ]::IsNullOrWhiteSpace($ExeOutputDir )) { $ExeOutputDir } else { $parsedDirs . ExeOutputDir }
339+ dcuOutputDir = if (-not [string ]::IsNullOrWhiteSpace($DcuOutputDir )) { $DcuOutputDir } else { $parsedDirs . DcuOutputDir }
291340 unitSearchPath = if ($UnitSearchPath.Count -eq 0 ) { $null } else { $UnitSearchPath }
292341 exitCode = $buildResult.ExitCode
293342 success = ($buildResult.ExitCode -eq 0 )
343+ warnings = $counts.Warnings
344+ errors = $counts.Errors
294345 output = $buildResult.Output
295346 }
296347
0 commit comments