Skip to content

Commit 87c328b

Browse files
committed
ci: add Windows PHP ABI build matrix
1 parent 0634763 commit 87c328b

6 files changed

Lines changed: 298 additions & 71 deletions

File tree

.github/patches/phpx-windows-mpdecimal-target.patch

Lines changed: 0 additions & 36 deletions
This file was deleted.

.github/workflows/windows-build.yml

Lines changed: 215 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ env:
1818

1919
jobs:
2020
build-tpc:
21-
name: tpc.exe (PHP ${{ matrix.php }})
21+
name: tpc.exe (PHP ${{ matrix.php }} ${{ matrix.thread_safety }})
2222
runs-on: windows-2022
2323
timeout-minutes: 90
2424
strategy:
2525
fail-fast: false
2626
matrix:
2727
php: ["8.4", "8.5"]
28+
thread_safety: [nts, zts]
2829

2930
steps:
3031
- name: Checkout TypePHP
@@ -38,36 +39,72 @@ jobs:
3839
tools: composer:v2
3940
env:
4041
fail-fast: true
41-
phpts: nts
42+
phpts: ${{ matrix.thread_safety == 'zts' && 'ts' || 'nts' }}
4243
update: true
4344

44-
- name: Install matching PHP development SDK
45+
- name: Resolve PHP build environment
46+
id: php-build-env
4547
shell: pwsh
4648
run: |
4749
$ErrorActionPreference = 'Stop'
4850
4951
$phpVersion = php -r 'echo PHP_VERSION;'
52+
$threadSafety = php -r 'echo PHP_ZTS ? "zts" : "nts";'
53+
if ($threadSafety -ne '${{ matrix.thread_safety }}') {
54+
throw "setup-php installed $threadSafety PHP, expected ${{ matrix.thread_safety }}"
55+
}
5056
$phpExe = (Get-Command php).Source
5157
$phpHome = Split-Path -Parent $phpExe
52-
$archiveName = "php-devel-pack-$phpVersion-nts-Win32-vs17-x64.zip"
58+
$threadSuffix = if ($threadSafety -eq 'nts') { '-nts' } else { '' }
59+
$archiveName = "php-devel-pack-$phpVersion$threadSuffix-Win32-vs17-x64.zip"
5360
$archive = Join-Path $env:RUNNER_TEMP $archiveName
61+
62+
"PHP_VERSION=$phpVersion" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
63+
"PHP_THREAD_SAFETY=$threadSafety" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
64+
"PHP_HOME=$phpHome" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
65+
"PHPX_HOME=${{ github.workspace }}\vendor\swoole\phpx" |
66+
Out-File $env:GITHUB_ENV -Append -Encoding utf8
67+
"PHP_DEVEL_ARCHIVE=$archive" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
68+
"version=$phpVersion" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
69+
"thread_safety=$threadSafety" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
70+
"archive=$archive" | Out-File $env:GITHUB_OUTPUT -Append -Encoding utf8
71+
72+
- name: Cache PHP development package
73+
id: cache-php-devel
74+
uses: actions/cache/restore@v4
75+
with:
76+
path: ${{ steps.php-build-env.outputs.archive }}
77+
key: windows-2022-php-devel-${{ steps.php-build-env.outputs.version }}-${{ steps.php-build-env.outputs.thread_safety }}-vs17-x64
78+
79+
- name: Install matching PHP development SDK
80+
shell: pwsh
81+
run: |
82+
$ErrorActionPreference = 'Stop'
83+
84+
$phpVersion = $env:PHP_VERSION
85+
$phpHome = $env:PHP_HOME
86+
$threadSafety = $env:PHP_THREAD_SAFETY
87+
$archive = $env:PHP_DEVEL_ARCHIVE
88+
$archiveName = Split-Path -Leaf $archive
5489
$extractDir = Join-Path $env:RUNNER_TEMP "php-devel-$phpVersion"
55-
$downloaded = $false
5690
57-
foreach ($baseUrl in @(
58-
'https://downloads.php.net/~windows/releases',
59-
'https://downloads.php.net/~windows/releases/archives'
60-
)) {
61-
try {
62-
Invoke-WebRequest -Uri "$baseUrl/$archiveName" -OutFile $archive
63-
$downloaded = $true
64-
break
65-
} catch {
66-
Remove-Item $archive -Force -ErrorAction SilentlyContinue
91+
if (-not (Test-Path $archive)) {
92+
$downloaded = $false
93+
foreach ($baseUrl in @(
94+
'https://downloads.php.net/~windows/releases',
95+
'https://downloads.php.net/~windows/releases/archives'
96+
)) {
97+
try {
98+
Invoke-WebRequest -Uri "$baseUrl/$archiveName" -OutFile $archive
99+
$downloaded = $true
100+
break
101+
} catch {
102+
Remove-Item $archive -Force -ErrorAction SilentlyContinue
103+
}
104+
}
105+
if (-not $downloaded) {
106+
throw "Unable to download the PHP $phpVersion NTS development pack"
67107
}
68-
}
69-
if (-not $downloaded) {
70-
throw "Unable to download the PHP $phpVersion NTS development pack"
71108
}
72109
73110
Expand-Archive -Path $archive -DestinationPath $extractDir -Force
@@ -91,6 +128,9 @@ jobs:
91128
}
92129
Copy-Item $embedLibrary $sdkLib -Force
93130
131+
$coreLibrary = if ($threadSafety -eq 'zts') { 'php8ts.lib' } else { 'php8.lib' }
132+
$runtimeLibrary = if ($threadSafety -eq 'zts') { 'php8ts.dll' } else { 'php8.dll' }
133+
94134
# Temporary compatibility fix for PHP packages predating php/php-src#22940.
95135
$hashHeader = Join-Path $sdkInclude 'ext\hash\php_hash.h'
96136
$hashSource = [IO.File]::ReadAllText($hashHeader)
@@ -109,33 +149,72 @@ jobs:
109149
110150
foreach ($required in @(
111151
(Join-Path $sdkInclude 'main\php.h'),
112-
(Join-Path $sdkLib 'php8.lib'),
152+
(Join-Path $sdkLib $coreLibrary),
113153
(Join-Path $sdkLib 'php8embed.lib'),
114-
(Join-Path $phpHome 'php8.dll')
154+
(Join-Path $phpHome $runtimeLibrary)
115155
)) {
116156
if (-not (Test-Path $required)) {
117157
throw "Required PHP SDK file is missing: $required"
118158
}
119159
}
120160
121-
"PHP_HOME=$phpHome" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
122-
"PHPX_HOME=${{ github.workspace }}\vendor\swoole\phpx" |
123-
Out-File $env:GITHUB_ENV -Append -Encoding utf8
124161
Write-Host "Using PHP $phpVersion from $phpHome"
125162
163+
- name: Save PHP development package
164+
if: steps.cache-php-devel.outputs.cache-hit != 'true'
165+
uses: actions/cache/save@v4
166+
with:
167+
path: ${{ steps.php-build-env.outputs.archive }}
168+
key: ${{ steps.cache-php-devel.outputs.cache-primary-key }}
169+
126170
- name: Install Composer dependencies
127171
run: composer install --prefer-dist --no-progress
128172

129173
- name: Patch PHPX Windows CMake target ordering
130174
shell: pwsh
131-
run: git apply --directory=vendor/swoole/phpx .github/patches/phpx-windows-mpdecimal-target.patch
175+
run: |
176+
$ErrorActionPreference = 'Stop'
177+
178+
$cmakeFile = Join-Path $env:PHPX_HOME 'CMakeLists.txt'
179+
$source = [IO.File]::ReadAllText($cmakeFile).Replace("`r`n", "`n")
180+
$copyBlockPattern = '(?ms)^ # 复制 DLL 到输出目录\n file\(GLOB MPDEC_DLLS .*?^ endforeach\(\)\n'
181+
$copyBlock = [regex]::Match($source, $copyBlockPattern)
182+
if (-not $copyBlock.Success) {
183+
throw 'Unable to locate the pre-target PHPX mpdecimal copy block'
184+
}
185+
$source = $source.Remove($copyBlock.Index, $copyBlock.Length)
186+
187+
$targetPattern = '(?ms)(add_library\(phpx SHARED \$\{SRC_FILES\}\)\nset_target_properties\(phpx PROPERTIES\n CLEAN_DIRECT_OUTPUT 1\n\)\n)'
188+
$target = [regex]::Match($source, $targetPattern)
189+
if (-not $target.Success) {
190+
throw 'Unable to locate the PHPX target declaration'
191+
}
192+
193+
$copyBlockText = $copyBlock.Value.Replace(' # 复制 DLL 到输出目录', ' # Copy mpdecimal DLLs after the phpx target exists.')
194+
$guardedCopyBlock = "`nif (IS_WINDOWS)`n$copyBlockText" + "endif()`n"
195+
$source = $source.Insert($target.Index + $target.Length, $guardedCopyBlock)
196+
[IO.File]::WriteAllText($cmakeFile, $source, [Text.UTF8Encoding]::new($false))
197+
198+
$targetOffset = $source.IndexOf('add_library(phpx SHARED')
199+
$copyOffset = $source.IndexOf('add_custom_command(TARGET phpx POST_BUILD')
200+
if ($targetOffset -lt 0 -or $copyOffset -le $targetOffset) {
201+
throw 'PHPX post-build command still precedes its target declaration'
202+
}
132203
133204
- name: Configure MSVC
134205
uses: ilammy/msvc-dev-cmd@v1
135206
with:
136207
arch: x64
137208

209+
- name: Cache GMP and MPFR
210+
id: cache-gmp-mpfr
211+
uses: actions/cache/restore@v4
212+
with:
213+
path: ${{ runner.temp }}\typephp-cache\vcpkg-x64-windows
214+
key: windows-2022-msvc-vcpkg-gmp-mpfr-x64-v1
215+
138216
- name: Install GMP and MPFR
217+
if: steps.cache-gmp-mpfr.outputs.cache-hit != 'true'
139218
shell: pwsh
140219
run: |
141220
$ErrorActionPreference = 'Stop'
@@ -148,30 +227,83 @@ jobs:
148227
}
149228
150229
$installed = Join-Path $env:VCPKG_INSTALLATION_ROOT "installed\$triplet"
151-
$sdkInclude = Join-Path $env:PHP_HOME 'SDK\include'
152-
$sdkLib = Join-Path $env:PHP_HOME 'SDK\lib'
153-
Copy-Item (Join-Path $installed 'include\*') $sdkInclude -Recurse -Force
230+
$cache = Join-Path $env:RUNNER_TEMP 'typephp-cache\vcpkg-x64-windows'
231+
New-Item "$cache\include", "$cache\lib", "$cache\bin" -ItemType Directory -Force |
232+
Out-Null
233+
Copy-Item (Join-Path $installed 'include\*') "$cache\include" -Recurse -Force
154234
155235
foreach ($library in @('gmp.lib', 'gmpxx.lib', 'mpfr.lib')) {
156236
$source = Join-Path $installed "lib\$library"
157237
if (-not (Test-Path $source)) {
158238
throw "vcpkg did not install $library"
159239
}
240+
Copy-Item $source "$cache\lib" -Force
241+
}
242+
Copy-Item (Join-Path $installed 'bin\*.dll') "$cache\bin" -Force
243+
244+
- name: Save GMP and MPFR
245+
if: steps.cache-gmp-mpfr.outputs.cache-hit != 'true'
246+
uses: actions/cache/save@v4
247+
with:
248+
path: ${{ runner.temp }}\typephp-cache\vcpkg-x64-windows
249+
key: ${{ steps.cache-gmp-mpfr.outputs.cache-primary-key }}
250+
251+
- name: Stage GMP and MPFR
252+
shell: pwsh
253+
run: |
254+
$ErrorActionPreference = 'Stop'
255+
256+
$cache = Join-Path $env:RUNNER_TEMP 'typephp-cache\vcpkg-x64-windows'
257+
$sdkInclude = Join-Path $env:PHP_HOME 'SDK\include'
258+
$sdkLib = Join-Path $env:PHP_HOME 'SDK\lib'
259+
Copy-Item "$cache\include\*" $sdkInclude -Recurse -Force
260+
261+
foreach ($library in @('gmp.lib', 'gmpxx.lib', 'mpfr.lib')) {
262+
$source = Join-Path $cache "lib\$library"
263+
if (-not (Test-Path $source)) {
264+
throw "The dependency cache does not contain $library"
265+
}
160266
Copy-Item $source $sdkLib -Force
161267
}
162-
Copy-Item (Join-Path $installed 'bin\*.dll') $env:PHP_HOME -Force
268+
Copy-Item "$cache\bin\*.dll" $env:PHP_HOME -Force
269+
270+
- name: Cache mpdecimal
271+
id: cache-mpdecimal
272+
uses: actions/cache/restore@v4
273+
with:
274+
path: ${{ env.PHPX_HOME }}\thirdparty\mpdecimal\vcbuild\dist64
275+
key: windows-2022-msvc-mpdecimal-x64-${{ hashFiles('vendor/swoole/phpx/thirdparty/mpdecimal/**') }}
163276

164277
- name: Build mpdecimal
278+
if: steps.cache-mpdecimal.outputs.cache-hit != 'true'
165279
shell: pwsh
166280
run: |
167281
$ErrorActionPreference = 'Stop'
168282
169283
$buildScript = Join-Path $env:PHPX_HOME 'thirdparty\mpdecimal\vcbuild\vcbuild64.bat'
170-
& cmd.exe /d /s /c "`"$buildScript`""
171-
if ($LASTEXITCODE -ne 0) {
172-
throw "mpdecimal build failed with exit code $LASTEXITCODE"
284+
$buildDirectory = Split-Path -Parent $buildScript
285+
Push-Location $buildDirectory
286+
try {
287+
& cmd.exe /d /s /c vcbuild64.bat
288+
if ($LASTEXITCODE -ne 0) {
289+
throw "mpdecimal build failed with exit code $LASTEXITCODE"
290+
}
291+
} finally {
292+
Pop-Location
173293
}
174294
295+
- name: Save mpdecimal
296+
if: steps.cache-mpdecimal.outputs.cache-hit != 'true'
297+
uses: actions/cache/save@v4
298+
with:
299+
path: ${{ env.PHPX_HOME }}\thirdparty\mpdecimal\vcbuild\dist64
300+
key: ${{ steps.cache-mpdecimal.outputs.cache-primary-key }}
301+
302+
- name: Stage mpdecimal
303+
shell: pwsh
304+
run: |
305+
$ErrorActionPreference = 'Stop'
306+
175307
$dist = Join-Path $env:PHPX_HOME 'thirdparty\mpdecimal\vcbuild\dist64'
176308
$sdkInclude = Join-Path $env:PHP_HOME 'SDK\include'
177309
$sdkLib = Join-Path $env:PHP_HOME 'SDK\lib'
@@ -224,10 +356,58 @@ jobs:
224356
throw 'The compiler did not produce tpc.exe'
225357
}
226358
227-
- name: Upload tpc.exe
359+
- name: Run Windows smoke tests
360+
shell: pwsh
361+
run: |
362+
$ErrorActionPreference = 'Stop'
363+
364+
$env:PATH = "$env:PHPX_HOME\build;$env:PATH"
365+
& .\tpc.exe tests\windows\smoke\project.yml --job 1 --no-progress
366+
if ($LASTEXITCODE -ne 0) {
367+
throw "Windows smoke project compilation failed with exit code $LASTEXITCODE"
368+
}
369+
370+
$smokeExe = Join-Path '${{ github.workspace }}' 'tests\windows\smoke\windows_smoke.exe'
371+
if (-not (Test-Path $smokeExe)) {
372+
throw "Windows smoke executable was not generated: $smokeExe"
373+
}
374+
375+
$processInfo = [Diagnostics.ProcessStartInfo]::new()
376+
$processInfo.FileName = $smokeExe
377+
$processInfo.ArgumentList.Add('${{ matrix.thread_safety }}')
378+
$processInfo.UseShellExecute = $false
379+
$processInfo.RedirectStandardOutput = $true
380+
$processInfo.RedirectStandardError = $true
381+
382+
$process = [Diagnostics.Process]::new()
383+
$process.StartInfo = $processInfo
384+
if (-not $process.Start()) {
385+
throw 'Unable to start the Windows smoke executable'
386+
}
387+
$stdout = $process.StandardOutput.ReadToEnd()
388+
$stderr = $process.StandardError.ReadToEnd()
389+
$process.WaitForExit()
390+
391+
Write-Host "Windows smoke stdout: $stdout"
392+
Write-Host "Windows smoke stderr: $stderr"
393+
Write-Host "Windows smoke exit code: $($process.ExitCode)"
394+
if ($process.ExitCode -ne 0) {
395+
throw "Windows smoke executable failed with exit code $($process.ExitCode)"
396+
}
397+
if ($stdout.Trim() -ne 'windows-smoke-ok:${{ matrix.thread_safety }}') {
398+
throw "Unexpected Windows smoke output: $stdout"
399+
}
400+
401+
- name: Upload Windows build outputs
402+
if: always()
228403
uses: actions/upload-artifact@v4
229404
with:
230-
name: tpc-windows-x64-php-${{ matrix.php }}
231-
if-no-files-found: error
405+
name: tpc-windows-x64-php-${{ matrix.php }}-${{ matrix.thread_safety }}
406+
if-no-files-found: warn
232407
retention-days: 7
233-
path: tpc.exe
408+
path: |
409+
tpc.exe
410+
tests/windows/smoke/windows_smoke.exe
411+
tests/windows/smoke/build/**/*.cc
412+
tests/windows/smoke/build/**/*.h
413+
tests/windows/smoke/build/**/*.rsp

0 commit comments

Comments
 (0)