Skip to content

Commit f909feb

Browse files
committed
fix(frankenphp): strip Go's -mthreads via a .bat wrapper (golang/go#16932)
1 parent f66e687 commit f909feb

2 files changed

Lines changed: 103 additions & 2 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Check FrankenPHP Windows build
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: php version
8+
default: '8.5'
9+
type: string
10+
extensions:
11+
description: extensions to compile (comma separated)
12+
default: 'bcmath,ctype,curl,dom,filter,fileinfo,iconv,mbstring,opcache,openssl,pdo,pdo_sqlite,phar,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib'
13+
type: string
14+
push:
15+
paths:
16+
- src/Package/Target/php/frankenphp.php
17+
- .github/workflows/frankenphp-windows-check.yml
18+
19+
env:
20+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
PHP_VER: ${{ inputs.version || '8.5' }}
22+
EXTS: ${{ inputs.extensions || 'bcmath,ctype,curl,dom,filter,fileinfo,iconv,mbstring,opcache,openssl,pdo,pdo_sqlite,phar,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib' }}
23+
24+
jobs:
25+
build:
26+
name: frankenphp ${{ inputs.version || '8.5' }} on Windows x86_64
27+
runs-on: windows-latest
28+
timeout-minutes: 180
29+
steps:
30+
- uses: actions/checkout@v4
31+
- uses: shivammathur/setup-php@v2
32+
with:
33+
php-version: '8.4'
34+
- run: composer update --no-dev --classmap-authoritative
35+
# FrankenPHP requires Go >= 1.26; make sure the runner's older Go cannot shadow it
36+
- uses: actions/setup-go@v6
37+
with:
38+
go-version: stable
39+
cache: false
40+
- run: go version
41+
- run: ./bin/spc doctor --auto-fix
42+
- run: ./bin/spc download --with-php="$env:PHP_VER" --for-extensions="$env:EXTS" --for-libs=nghttp2 --ignore-cache-sources=php-src --retry 5
43+
- name: Build php-embed
44+
run: ./bin/spc build:php-embed --enable-zts "--with-libs=nghttp2,pthreads4w" "$env:EXTS"
45+
- name: Inspect php8embed.lib for zend_atomic_bool_store
46+
run: |
47+
$lib = Get-ChildItem -Recurse -Filter php8embed.lib | Select-Object -First 1
48+
Write-Host "lib: $($lib.FullName)"
49+
$dumpbin = (Get-ChildItem "C:/Program Files/Microsoft Visual Studio/*/*/VC/Tools/MSVC/*/bin/HostX64/x64/dumpbin.exe" | Select-Object -First 1).FullName
50+
& $dumpbin /SYMBOLS $lib.FullName | Select-String "zend_atomic_bool_store|InitSecurityInterface|PathCchCanonicalize" | Select-Object -First 20
51+
- name: Build frankenphp
52+
run: ./bin/spc build:frankenphp --enable-zts --with-libs=nghttp2 "$env:EXTS"
53+
- name: Smoke test binary
54+
run: |
55+
$bin = Get-ChildItem -Recurse -Filter frankenphp.exe | Select-Object -First 1
56+
& $bin.FullName version
57+
& $bin.FullName build-info
58+
- uses: actions/upload-artifact@v7
59+
if: always()
60+
with:
61+
name: frankenphp-windows-x86_64
62+
path: buildroot/bin/frankenphp.exe
63+
if-no-files-found: ignore
64+
- uses: actions/upload-artifact@v7
65+
if: failure()
66+
with:
67+
name: spc-logs
68+
path: log/

src/Package/Target/php/frankenphp.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,11 @@ public function buildFrankenphpForWindows(TargetPackage $package, PackageInstall
273273
// Fix: prepend clang's directory to PATH and use plain executable names instead,
274274
// which matches FrankenPHP's official CI approach (CC=clang, CXX=clang++).
275275
$clang_dir = dirname($clang_info['clang']);
276+
[$cc, $cxx] = $this->windowsCgoCompilers($package, $clang_info['clang']);
276277
$env = [
277278
'CGO_ENABLED' => '1',
278-
'CC' => 'clang.exe',
279-
'CXX' => 'clang++.exe',
279+
'CC' => $cc,
280+
'CXX' => $cxx,
280281
'PATH' => $clang_dir . ';' . getenv('PATH'),
281282
'CGO_CFLAGS' => clean_spaces($cgo_cflags),
282283
'CGO_LDFLAGS' => $cgo_ldflags,
@@ -377,6 +378,38 @@ public function smokeTestFrankenphpForWindows(PackageBuilder $builder): void
377378
}
378379
}
379380

381+
/**
382+
* Return the [CC, CXX] cgo should use to build FrankenPHP on Windows.
383+
*
384+
* Go passes the MinGW-only `-mthreads` flag to the C compiler for cgo builds
385+
* (golang/go#16932); Clang >= 20 rejects it for the MSVC target. When it does,
386+
* wrap Clang with a tiny .bat that strips the flag before forwarding.
387+
*
388+
* @return array{0: string, 1: string}
389+
*/
390+
protected function windowsCgoCompilers(TargetPackage $package, string $clang): array
391+
{
392+
$probe = $package->getSourceDir() . '\mthreads-probe.c';
393+
file_put_contents($probe, "int main(void){return 0;}\n");
394+
[$ret] = cmd()->execWithResult('"' . $clang . '" -mthreads -c ' . escapeshellarg($probe) . ' -o ' . escapeshellarg($probe . '.o'), false);
395+
FileSystem::removeFileIfExists($probe);
396+
FileSystem::removeFileIfExists($probe . '.o');
397+
if ($ret === 0) {
398+
return ['clang.exe', 'clang++.exe'];
399+
}
400+
401+
logger()->info('Clang rejects -mthreads; wrapping it to strip the flag (golang/go#16932)');
402+
$dir = dirname($clang);
403+
$cc = $package->getSourceDir() . '\cc-nothreads.bat';
404+
$cxx = $package->getSourceDir() . '\cxx-nothreads.bat';
405+
// %*: the whole command line; the substring replace drops the -mthreads token,
406+
// preserving quoting of paths that a for-loop would mangle.
407+
file_put_contents($cc, "@echo off\r\nset \"a=%*\"\r\nset \"a=%a: -mthreads = %\"\r\n\"{$dir}\\clang.exe\" %a%\r\n");
408+
file_put_contents($cxx, "@echo off\r\nset \"a=%*\"\r\nset \"a=%a: -mthreads = %\"\r\n\"{$dir}\\clang++.exe\" %a%\r\n");
409+
410+
return [$cc, $cxx];
411+
}
412+
380413
protected function getFrankenPHPVersion(TargetPackage $package): string
381414
{
382415
if ($version = getenv('FRANKENPHP_VERSION')) {

0 commit comments

Comments
 (0)