Skip to content

Build PHP SDK

Build PHP SDK #39

Workflow file for this run

name: Build PHP SDK
on:
workflow_dispatch:
inputs:
php_version:
description: "PHP version to build (e.g. 8.5.2, 8.4.7, 8.3.20)"
required: true
type: string
env:
SPC_VERSION: "2.8.5"
PHP_EXTENSIONS: "bcmath,calendar,ctype,curl,dom,exif,fileinfo,filter,gd,hash,iconv,mbstring,mysqli,mysqlnd,openssl,pcntl,pcre,pdo,pdo_mysql,phar,posix,session,simplexml,sodium,tokenizer,xml,xmlreader,xmlwriter,zip,zlib"
jobs:
build-linux:
name: Linux (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: [self-hosted, linux, x64]
spc_arch: x86_64
- arch: aarch64
runner: [self-hosted, linux, arm64]
spc_arch: aarch64
steps:
- name: Clean up previous builds
run: rm -rf buildroot source downloads sdk *.tar.gz
- name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq build-essential autoconf automake bison \
cmake flex libtool make pkg-config re2c
- name: Install cross-compilation tools (aarch64)
if: matrix.arch == 'aarch64'
run: sudo apt-get install -y -qq gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- name: Download spc
run: |
mkdir -p "$HOME/bin"
curl -fsSL "https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-linux-${{ matrix.spc_arch }}.tar.gz" \
| tar xz -C "$HOME/bin"
echo "$HOME/bin" >> "$GITHUB_PATH"
- name: Download PHP sources
run: |
spc download \
--with-php=${{ inputs.php_version }} \
--for-extensions=${{ env.PHP_EXTENSIONS }} \
--prefer-pre-built
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run spc doctor
run: spc doctor --auto-fix || true
- name: Build libphp.a
run: |
spc build ${{ env.PHP_EXTENSIONS }} \
--build-embed \
--enable-zts \
--no-strip \
--debug
- name: Package SDK
run: |
mkdir -p sdk/lib sdk/include
cp buildroot/lib/libphp.a sdk/lib/
cp -r buildroot/include/php sdk/include/php
tar czf php-sdk-${{ inputs.php_version }}-linux-${{ matrix.arch }}.tar.gz -C sdk .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: php-sdk-${{ inputs.php_version }}-linux-${{ matrix.arch }}
path: php-sdk-${{ inputs.php_version }}-linux-${{ matrix.arch }}.tar.gz
build-macos:
name: macOS (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: aarch64
runner: [self-hosted, macos, arm64]
spc_arch: aarch64
steps:
# spc on macOS is a self-contained micro-PHP binary (same as Windows
# spc.exe), so no host PHP install is needed. The runner image is bare,
# so we let `spc doctor --auto-fix` install Homebrew and the build deps
# it lists in MacOSToolCheckList::REQUIRED_COMMANDS (autoconf, automake,
# bison, cmake, flex, libtool, make, pkg-config, re2c, etc.).
- name: Download spc
run: |
mkdir -p "$HOME/bin"
curl -fsSL "https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-macos-${{ matrix.spc_arch }}.tar.gz" \
| tar xz -C "$HOME/bin"
echo "$HOME/bin" >> "$GITHUB_PATH"
# Pre-add Homebrew to PATH so spc doctor can use brew immediately
# after installing it (spc doesn't update PATH itself).
echo "/opt/homebrew/bin" >> "$GITHUB_PATH"
- name: Install Homebrew + build deps via spc doctor
# NONINTERACTIVE=1 is required so the Homebrew installer doesn't prompt
# for sudo confirmation (which would hang in CI).
env:
NONINTERACTIVE: "1"
run: spc doctor --auto-fix
# Apple clang crashes on PHP's JIT code (zend_jit_vm_helpers.c, exit 70),
# so use Homebrew LLVM instead. Doctor's REQUIRED_COMMANDS doesn't
# include llvm, so we install it explicitly after brew is up.
- name: Install LLVM and prepend to PATH
run: |
brew install llvm
echo "$(brew --prefix llvm)/bin" >> "$GITHUB_PATH"
- name: Download PHP sources
run: |
spc download \
--with-php=${{ inputs.php_version }} \
--for-extensions=${{ env.PHP_EXTENSIONS }} \
--prefer-pre-built
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build libphp.a
run: |
spc build ${{ env.PHP_EXTENSIONS }} \
--build-embed \
--enable-zts \
--no-strip \
--debug
- name: Package SDK
run: |
mkdir -p sdk/lib sdk/include
cp buildroot/lib/libphp.a sdk/lib/
cp -r buildroot/include/php sdk/include/php
tar czf php-sdk-${{ inputs.php_version }}-macos-${{ matrix.arch }}.tar.gz -C sdk .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: php-sdk-${{ inputs.php_version }}-macos-${{ matrix.arch }}
path: php-sdk-${{ inputs.php_version }}-macos-${{ matrix.arch }}.tar.gz
build-windows:
name: Windows (x86_64)
runs-on: [self-hosted, windows, x64]
# Windows doesn't support pcntl or posix
env:
WIN_PHP_EXTENSIONS: "bcmath,calendar,ctype,curl,dom,exif,fileinfo,filter,gd,hash,iconv,mbstring,mysqli,mysqlnd,openssl,pcre,pdo,pdo_mysql,phar,session,simplexml,sodium,tokenizer,xml,xmlreader,xmlwriter,zip,zlib"
steps:
- name: Clean up previous builds
shell: powershell
run: |
foreach ($dir in @('buildroot', 'source', 'downloads', 'sdk', 'php-sdk-binary-tools')) {
if (Test-Path $dir) { Remove-Item -Recurse -Force $dir }
}
Remove-Item -Force *.tar.gz -ErrorAction SilentlyContinue
- name: Install Git for Windows
shell: powershell
# Server Core has no git. spc needs:
# - git.exe: doctor's install-php-sdk auto-fix clones php-sdk-binary-tools
# - usr\bin\patch.exe: source patching during build
# Use PortableGit (self-extracting 7z) installed at the standard Git
# for Windows path so spc's expected layout matches.
run: |
$gitRoot = "C:\Program Files\Git"
if (-not (Test-Path "$gitRoot\cmd\git.exe")) {
$ver = "2.49.0"
$url = "https://github.com/git-for-windows/git/releases/download/v${ver}.windows.1/PortableGit-${ver}-64-bit.7z.exe"
Write-Host "Downloading PortableGit $ver..."
Invoke-WebRequest -Uri $url -OutFile portable-git.7z.exe
Write-Host "Extracting to $gitRoot..."
Start-Process -FilePath .\portable-git.7z.exe `
-ArgumentList @("-o`"$gitRoot`"", '-y') -Wait -NoNewWindow
Remove-Item portable-git.7z.exe
if (-not (Test-Path "$gitRoot\cmd\git.exe")) {
Write-Error "PortableGit extraction failed: $gitRoot\cmd\git.exe not found"
exit 1
}
}
"$gitRoot\cmd" | Out-File -Append -FilePath $env:GITHUB_PATH
"$gitRoot\usr\bin" | Out-File -Append -FilePath $env:GITHUB_PATH
- name: Install Visual Studio Build Tools
shell: powershell
# PHP requires MSVC to compile on Windows. Runner image is
# mcr.microsoft.com/windows/servercore:ltsc2025 with no compiler.
# Install Build Tools per-job (~10-15 min). Long-term: dedicated
# php-sdk runner image with VS pre-baked.
#
# Install path: C:\BuildTools (Microsoft's recommended path for
# Server Core containers; nested Program Files paths are flaky).
# spc's findVisualStudio() probes Community/Professional/Enterprise
# under standard Program Files locations, so we junction one of
# those to C:\BuildTools after install.
#
# The bootstrapper (vs_buildtools.exe) exits before the underlying
# installer engine actually finishes on Server Core, so we poll
# for MSBuild.exe rather than trusting its exit code.
run: |
$installPath = "C:\BuildTools"
$msbuild = "$installPath\MSBuild\Current\Bin\MSBuild.exe"
$communityPath = "C:\Program Files\Microsoft Visual Studio\2022\Community"
if (-not (Test-Path $msbuild)) {
Write-Host "Downloading VS Build Tools bootstrapper..."
Invoke-WebRequest -Uri "https://aka.ms/vs/17/release/vs_buildtools.exe" -OutFile vs_buildtools.exe
Write-Host "Launching install (bootstrapper may exit before install completes)..."
$proc = Start-Process -FilePath .\vs_buildtools.exe `
-ArgumentList @(
'--quiet', '--wait', '--norestart', '--nocache',
'--installPath', $installPath,
'--add', 'Microsoft.VisualStudio.Workload.VCTools',
'--includeRecommended'
) -Wait -PassThru -NoNewWindow
Write-Host "Bootstrapper exit code: $($proc.ExitCode)"
Remove-Item vs_buildtools.exe
# Poll for MSBuild.exe; bootstrapper exit is unreliable on Server Core
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$timeoutMin = 20
while (-not (Test-Path $msbuild) -and $sw.Elapsed.TotalMinutes -lt $timeoutMin) {
Start-Sleep -Seconds 30
Write-Host (" ...waiting for VS install ({0:F0}s)" -f $sw.Elapsed.TotalSeconds)
}
if (-not (Test-Path $msbuild)) {
Write-Host "::error::VS install timed out after $timeoutMin min - $msbuild never appeared"
Write-Host "==> Searching for MSBuild.exe anywhere on disk:"
Get-ChildItem -Path C:\ -Recurse -Filter MSBuild.exe -ErrorAction SilentlyContinue |
Select-Object -First 10 |
ForEach-Object { Write-Host " $($_.FullName)" }
Write-Host "==> Recent VS install logs in `$env:TEMP (dd_*.log):"
Get-ChildItem -Path $env:TEMP -Filter 'dd_*.log' -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object -First 5 |
ForEach-Object {
Write-Host ('--- ' + $_.Name + ' ---')
Get-Content $_.FullName -Tail 80
}
exit 1
}
Write-Host ("MSBuild.exe found after {0:F0}s" -f $sw.Elapsed.TotalSeconds)
} else {
Write-Host "VS Build Tools already present at $installPath"
}
# Junction the path spc's hardcoded check expects -> our install path
if (-not (Test-Path $communityPath)) {
$parent = Split-Path $communityPath -Parent
if (-not (Test-Path $parent)) {
New-Item -ItemType Directory -Path $parent -Force | Out-Null
}
New-Item -ItemType Junction -Path $communityPath -Target $installPath | Out-Null
Write-Host "Junction $communityPath -> $installPath"
}
- name: Download spc
shell: powershell
# Windows uses luthermonson/static-php-cli fork (2.8.5 + embed SAPI patches);
# upstream removed embed support in v3 and won't backport. Other platforms
# still use upstream SPC_VERSION.
run: |
Invoke-WebRequest -Uri "https://github.com/luthermonson/static-php-cli/releases/download/2.8.5%2Bembed-windows/spc-windows-x64.exe" -OutFile spc.exe
"$PWD" | Out-File -Append -FilePath $env:GITHUB_PATH
- name: Download PHP sources
shell: powershell
run: |
spc download `
--with-php=${{ inputs.php_version }} `
--for-extensions=${{ env.WIN_PHP_EXTENSIONS }} `
--prefer-pre-built
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run spc doctor
shell: powershell
# --auto-fix runs install-php-sdk (clones php-sdk-binary-tools, needed
# for 7za.exe at extraction time), install-nasm, install-perl.
# Non-fatal in case any individual check has issues.
run: |
spc doctor --auto-fix
exit 0
- name: Build php8embed (patched spc with embed SAPI support)
shell: powershell
run: |
spc build ${{ env.WIN_PHP_EXTENSIONS }} `
--build-embed `
--enable-zts `
--no-strip `
--debug
- name: Find build outputs
shell: powershell
run: |
Write-Host "==> Searching for php8embed files..."
Get-ChildItem -Recurse -Filter "php8embed.*" | ForEach-Object { Write-Host $_.FullName }
Write-Host "==> Searching for php8ts files..."
Get-ChildItem -Recurse -Filter "php8ts.*" | ForEach-Object { Write-Host $_.FullName }
Write-Host "==> Searching for libphp files..."
Get-ChildItem -Recurse -Filter "libphp.*" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_.FullName }
- name: Package SDK
shell: powershell
run: |
New-Item -ItemType Directory -Force -Path sdk/lib, sdk/include
# Find php8embed.lib (import library for linking)
$lib = Get-ChildItem -Recurse -Filter "php8embed.lib" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($lib) {
Copy-Item $lib.FullName sdk/lib/
Write-Host "==> Found php8embed.lib at $($lib.FullName)"
} else {
Write-Error "php8embed.lib not found anywhere in build tree"
exit 1
}
# Find the runtime DLL — spc ZTS builds produce php8ts.dll (not php8embed.dll).
# The embed .lib links against php8ts.dll at runtime.
# Check for php8embed.dll first (windows.php.net style), fall back to php8ts.dll (spc style).
$dll = Get-ChildItem -Recurse -Filter "php8embed.dll" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $dll) {
$dll = Get-ChildItem -Recurse -Filter "php8ts.dll" -ErrorAction SilentlyContinue | Select-Object -First 1
}
if ($dll) {
# Always ship as php8embed.dll — ephpm expects this name at build time
# (include_bytes! via PHP_EMBED_DLL_PATH) and runtime (delay-load).
Copy-Item $dll.FullName sdk/lib/php8embed.dll
Write-Host "==> Found runtime DLL at $($dll.FullName) -> sdk/lib/php8embed.dll"
} else {
Write-Error "No runtime DLL found (checked php8embed.dll and php8ts.dll)"
exit 1
}
# Optional: libphp.a (static lib, may not exist on Windows)
$static = Get-ChildItem -Recurse -Filter "libphp.a" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($static) {
Copy-Item $static.FullName sdk/lib/
Write-Host "==> Found libphp.a at $($static.FullName)"
}
Copy-Item -Recurse buildroot/include/php sdk/include/php
Write-Host "==> SDK contents:"
Get-ChildItem -Recurse sdk/lib/
tar czf php-sdk-${{ inputs.php_version }}-windows-x86_64.tar.gz -C sdk .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: php-sdk-${{ inputs.php_version }}-windows-x86_64
path: php-sdk-${{ inputs.php_version }}-windows-x86_64.tar.gz
release:
name: Create Release
needs: [build-linux, build-macos, build-windows]
runs-on: [self-hosted, linux, x64]
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: List artifacts
run: ls -lh artifacts/
- name: Create release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ inputs.php_version }}
name: PHP ${{ inputs.php_version }}
body: |
Pre-built PHP embed SAPI SDK for ephpm.
**PHP version:** ${{ inputs.php_version }}
**Extensions:** ${{ env.PHP_EXTENSIONS }}
## Artifacts
| File | Platform |
|------|----------|
| `php-sdk-${{ inputs.php_version }}-linux-x86_64.tar.gz` | Linux x86_64 (musl static) |
| `php-sdk-${{ inputs.php_version }}-linux-aarch64.tar.gz` | Linux aarch64 (musl static) |
| `php-sdk-${{ inputs.php_version }}-macos-aarch64.tar.gz` | macOS Apple Silicon |
| `php-sdk-${{ inputs.php_version }}-windows-x86_64.tar.gz` | Windows x86_64 (ZTS) |
files: artifacts/*.tar.gz