Skip to content

Commit 32c94ef

Browse files
authored
Minor changes needed to delphi-msbuild.ps1 to run under PowerShell.exe and pwsh.exe (#14)
+ Add tests to ensure future compatibility remains All 54 tests pass For issue #13
1 parent 926d1b0 commit 32c94ef

6 files changed

Lines changed: 190 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
All notable changes to this project will be documented in this file.
44

55
---
6+
7+
## [0.4.0] - Unreleased
8+
9+
- Ensure `PowerShell 5.1` compatibility for the delphi-msbuild.ps1 script
10+
(Tests remain the newer `pwsh`)
11+
[#13](https://github.com/continuous-delphi/delphi-msbuild/issues/13)
12+
613
## [0.3.0] - 2026-03-16
714

815
- Add `-ExeOutputDir` parameter to set the compiled executable output directory

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
![Status](https://img.shields.io/badge/status-incubator-orange)
77
![License](https://img.shields.io/github/license/continuous-delphi/delphi-inspect.svg)
88
![Delphi](https://img.shields.io/badge/delphi-red)
9-
![PowerShell](https://img.shields.io/badge/powershell-7.4%2B-blue)
9+
![PowerShell](https://img.shields.io/badge/powershell-blue)
1010
![Continuous Delphi](https://img.shields.io/badge/org-continuous--delphi-red)
1111

1212
Quick-start, or enhance your Delphi build automation with a standalone,
@@ -40,6 +40,13 @@ delphi-inspect.ps1 -DetectLatest -Platform Win32 -BuildSystem MSBuild |
4040
delphi-msbuild.ps1 -ProjectFile .\src\MyApp.dproj
4141
```
4242

43+
## PowerShell Compatibility
44+
45+
Runs on the widely available Windows PowerShell 5.1 (`powershell.exe`)
46+
and the newer PowerShell 7+ (`pwsh`).
47+
48+
Note: the test suite requires `pwsh`.
49+
4350
# Usage
4451

4552
```powershell

source/delphi-msbuild.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function Resolve-RootDir {
128128
# Derive the expected rsvars.bat path from the Delphi root dir.
129129
function Get-RsvarsPath {
130130
param([string]$RootDir)
131-
return Join-Path -Path $RootDir -ChildPath 'bin' -AdditionalChildPath 'rsvars.bat'
131+
return Join-Path (Join-Path $RootDir 'bin') 'rsvars.bat'
132132
}
133133

134134
# Invoke cmd.exe to source rsvars.bat and capture the resulting environment.

tests/pwsh/TestHelpers.ps1

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# Get-MsBuildScriptPath - alias of Get-ScriptUnderTestPath
1313
# Invoke-ToolProcess - runs a .ps1 as a child process and returns
1414
# [pscustomobject]@{ ExitCode; StdOut; StdErr }
15+
# Optional -Shell parameter selects the host
16+
# executable (default: 'pwsh').
1517

1618
function Get-ScriptUnderTestPath {
1719
$path = Join-Path $PSScriptRoot '..\..\source\delphi-msbuild.ps1'
@@ -24,12 +26,18 @@ function Get-MsBuildScriptPath { Get-ScriptUnderTestPath }
2426
function Invoke-ToolProcess {
2527
param(
2628
[Parameter(Mandatory=$true)][string]$ScriptPath,
27-
[Parameter()][string[]]$Arguments = @()
29+
[Parameter()][string[]]$Arguments = @(),
30+
[Parameter()][string]$Shell = 'pwsh',
31+
[Parameter()][string]$ExecutionPolicy = ''
2832
)
2933

34+
$shellArgs = @('-NoProfile', '-NonInteractive')
35+
if ($ExecutionPolicy) { $shellArgs += @('-ExecutionPolicy', $ExecutionPolicy) }
36+
$shellArgs += @('-File', $ScriptPath)
37+
3038
$psi = [System.Diagnostics.ProcessStartInfo]::new()
31-
$psi.FileName = 'pwsh'
32-
foreach ($a in @('-NoProfile', '-NonInteractive', '-File', $ScriptPath) + $Arguments) {
39+
$psi.FileName = $Shell
40+
foreach ($a in $shellArgs + $Arguments) {
3341
[void]$psi.ArgumentList.Add($a)
3442
}
3543
$psi.RedirectStandardOutput = $true
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#Requires -Modules @{ ModuleName='Pester'; ModuleVersion='5.7.0' }
2+
<#
3+
.SYNOPSIS
4+
Windows PowerShell 5.1 compatibility tests for delphi-msbuild.ps1.
5+
6+
.DESCRIPTION
7+
Verifies that delphi-msbuild.ps1 can be launched under powershell.exe
8+
(Windows PowerShell 5.1) and produces correct exit codes.
9+
All tests in this file skip automatically on platforms where
10+
powershell.exe is absent (e.g. Linux CI runners).
11+
12+
The test suite itself continues to require pwsh 7+ (see run-tests.ps1);
13+
these tests only invoke the script-under-test via powershell.exe.
14+
15+
Scenarios tested:
16+
Exits 3 when no -RootDir and no pipeline input.
17+
Exits 3 when -RootDir directory does not exist on disk.
18+
Exits 3 when -RootDir exists but rsvars.bat is absent.
19+
Exits 4 when rsvars.bat exists but -ProjectFile does not.
20+
21+
.NOTES
22+
Get-Command is NOT used to locate powershell.exe. The Invoke-RsvarsEnvironment
23+
unit test applies fake environment variables (including a truncated PATH) to the
24+
live process, which removes C:\Windows\System32\WindowsPowerShell\v1.0 from PATH
25+
and breaks Get-Command resolution for external commands. Instead, powershell.exe
26+
is located via its well-known fixed path under $env:SystemRoot. On Linux/macOS
27+
$env:SystemRoot is absent so Test-Path returns $false and all tests skip cleanly.
28+
29+
$skipTests is a discovery-time local variable captured by -Skip:.
30+
$script:winPS51Exe is set in BeforeAll (run time) so it is visible in
31+
It and Context BeforeAll blocks.
32+
33+
-ExecutionPolicy Bypass is passed to powershell.exe because the machine's
34+
default execution policy may not permit running unsigned scripts.
35+
#>
36+
37+
Describe 'Windows PowerShell 5.1 compatibility' {
38+
39+
# Evaluated at Pester discovery time -- captured by -Skip: on each It.
40+
# Uses Test-Path (filesystem) rather than Get-Command (PATH-dependent) to
41+
# locate powershell.exe safely regardless of prior process PATH changes.
42+
$ps51Path = if ($env:SystemRoot) {
43+
[System.IO.Path]::Combine($env:SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
44+
} else { $null }
45+
$skipTests = -not ($ps51Path -and (Test-Path -LiteralPath $ps51Path))
46+
47+
BeforeAll {
48+
. "$PSScriptRoot/TestHelpers.ps1"
49+
$script:scriptPath = Get-ScriptUnderTestPath
50+
51+
$script:winPS51Exe = $null
52+
$sysRoot = $env:SystemRoot
53+
if ($sysRoot) {
54+
$candidate = [System.IO.Path]::Combine($sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
55+
if (Test-Path -LiteralPath $candidate) { $script:winPS51Exe = $candidate }
56+
}
57+
}
58+
59+
It 'powershell.exe (Windows PowerShell 5.1) is present on this machine' -Skip:$skipTests {
60+
$script:winPS51Exe | Should -Not -BeNullOrEmpty
61+
}
62+
63+
Context 'exits 3 when no rootDir is provided and no pipeline input' {
64+
65+
BeforeAll {
66+
if (-not $script:winPS51Exe) { return }
67+
$script:result = Invoke-ToolProcess `
68+
-Shell $script:winPS51Exe `
69+
-ExecutionPolicy 'Bypass' `
70+
-ScriptPath $script:scriptPath `
71+
-Arguments @('-ProjectFile', 'C:\Fake\MyApp.dproj')
72+
}
73+
74+
It 'exit code is 3' -Skip:$skipTests {
75+
$script:result.ExitCode | Should -Be 3
76+
}
77+
78+
It 'stderr contains helpful message' -Skip:$skipTests {
79+
$script:result.StdErr -join ' ' | Should -Match 'root dir'
80+
}
81+
82+
}
83+
84+
Context 'exits 3 when rootDir directory does not exist on disk' {
85+
86+
BeforeAll {
87+
if (-not $script:winPS51Exe) { return }
88+
$script:result = Invoke-ToolProcess `
89+
-Shell $script:winPS51Exe `
90+
-ExecutionPolicy 'Bypass' `
91+
-ScriptPath $script:scriptPath `
92+
-Arguments @('-ProjectFile', 'C:\Fake\MyApp.dproj', '-RootDir', 'C:\DoesNotExist\AtAll\9999')
93+
}
94+
95+
It 'exit code is 3' -Skip:$skipTests {
96+
$script:result.ExitCode | Should -Be 3
97+
}
98+
99+
It 'stderr mentions the missing directory' -Skip:$skipTests {
100+
$script:result.StdErr -join ' ' | Should -Match 'not found'
101+
}
102+
103+
}
104+
105+
Context 'exits 3 when rootDir exists but rsvars.bat is absent' {
106+
107+
BeforeAll {
108+
if (-not $script:winPS51Exe) { return }
109+
$script:result = Invoke-ToolProcess `
110+
-Shell $script:winPS51Exe `
111+
-ExecutionPolicy 'Bypass' `
112+
-ScriptPath $script:scriptPath `
113+
-Arguments @('-ProjectFile', 'C:\Fake\MyApp.dproj', '-RootDir', ([System.IO.Path]::GetTempPath()))
114+
}
115+
116+
It 'exit code is 3' -Skip:$skipTests {
117+
$script:result.ExitCode | Should -Be 3
118+
}
119+
120+
It 'stderr mentions rsvars.bat' -Skip:$skipTests {
121+
$script:result.StdErr -join ' ' | Should -Match 'rsvars\.bat'
122+
}
123+
124+
}
125+
126+
Context 'exits 4 when rsvars.bat exists but project file does not' {
127+
128+
BeforeAll {
129+
if (-not $script:winPS51Exe) { return }
130+
$script:tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) 'delphi-msbuild-winps51-test'
131+
$script:tempBin = Join-Path $script:tempRoot 'bin'
132+
$null = New-Item -ItemType Directory -Path $script:tempBin -Force
133+
$null = New-Item -ItemType File -Path (Join-Path $script:tempBin 'rsvars.bat') -Force
134+
135+
$script:result = Invoke-ToolProcess `
136+
-Shell $script:winPS51Exe `
137+
-ExecutionPolicy 'Bypass' `
138+
-ScriptPath $script:scriptPath `
139+
-Arguments @('-ProjectFile', 'C:\Fake\DoesNotExist.dproj', '-RootDir', $script:tempRoot)
140+
}
141+
142+
AfterAll {
143+
if ($script:tempRoot) {
144+
Remove-Item -LiteralPath $script:tempRoot -Recurse -Force -ErrorAction SilentlyContinue
145+
}
146+
}
147+
148+
It 'exit code is 4' -Skip:$skipTests {
149+
$script:result.ExitCode | Should -Be 4
150+
}
151+
152+
It 'stderr mentions the missing project file' -Skip:$skipTests {
153+
$script:result.StdErr -join ' ' | Should -Match 'not found'
154+
}
155+
156+
}
157+
158+
}

tests/run-tests.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# tests/run-tests.ps1
2+
if ($PSVersionTable.PSVersion.Major -lt 7) {
3+
Write-Error "Tests require PowerShell 7+."
4+
exit 1
5+
}
6+
27
if ($IsWindows) {
38
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
49
}

0 commit comments

Comments
 (0)