1818
1919jobs :
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
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