Skip to content

Build & Release TypePHP Webman #54

Build & Release TypePHP Webman

Build & Release TypePHP Webman #54

Workflow file for this run

name: Build & Release TypePHP Webman
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
platform:
description: Build target
type: choice
required: true
default: all
options:
- all
- linux
- windows
permissions:
contents: write
env:
COMPOSER_NO_INTERACTION: 1
COMPOSER_PROCESS_TIMEOUT: 0
jobs:
select-platform:
name: Select build platform
runs-on: ubuntu-22.04
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- name: Generate build matrix
id: matrix
shell: bash
env:
REQUESTED_PLATFORM: ${{ inputs.platform || 'linux' }}
run: |
case "$REQUESTED_PLATFORM" in
linux)
matrix='{"include":[{"os":"ubuntu-22.04","platform":"linux-x64","archive_name":"tinywan-typephp-webman-linux-x64.tar.gz"}]}'
;;
windows)
matrix='{"include":[{"os":"windows-latest","platform":"windows-x64","archive_name":"tinywan-typephp-webman-windows-x64.zip"}]}'
;;
all)
matrix='{"include":[{"os":"ubuntu-22.04","platform":"linux-x64","archive_name":"tinywan-typephp-webman-linux-x64.tar.gz"},{"os":"windows-latest","platform":"windows-x64","archive_name":"tinywan-typephp-webman-windows-x64.zip"}]}'
;;
*)
echo "Unsupported platform: $REQUESTED_PLATFORM" >&2
exit 1
;;
esac
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
build:
needs: select-platform
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.select-platform.outputs.matrix) }}
env:
PHPX_HOME: ${{ github.workspace }}/vendor/swoole/phpx
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Load PHP Extensions List
id: ext
shell: bash
run: |
if [ "${{ runner.os }}" = "Linux" ] && [ -f "extensions.txt" ]; then
EXT_LIST=$(grep -v '^[[:space:]]*#' extensions.txt | grep -v '^[[:space:]]*$' | tr '\r\n' ',' | sed 's/,,*/,/g; s/^,//; s/,$//')
echo "Loaded Linux extensions: $EXT_LIST"
echo "list=$EXT_LIST" >> "$GITHUB_OUTPUT"
else
echo "list=zip" >> "$GITHUB_OUTPUT"
fi
# ---------------- Setup PHP 8.4 (参考官方配置 ZTS 与 embed 支持) ----------------
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: ${{ steps.ext.outputs.list }}
coverage: none
ini-values: precision=17, memory_limit=4G, error_reporting=E_ERROR|E_WARNING, display_errors=1, display_startup_errors=1, log_errors=0
tools: composer:v2
env:
fail-fast: true
phpts: ts
update: true
# ---------------- Linux 构建环境准备 (官方原生依赖) ----------------
- name: Install Linux native build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes build-essential cmake libgmp-dev libmpfr-dev pkg-config python3-dev patchelf
- name: Patch php_hash.h for C++ compatibility
if: runner.os == 'Linux'
shell: bash
run: |
PHP_INC="$(php-config --include-dir 2>/dev/null || echo '')"
echo "PHP include dir: $PHP_INC"
if [ -n "$PHP_INC" ]; then
sudo find "$PHP_INC" /usr/include -name "php_hash.h" -exec sed -i 's/static inline const php_hash_ops\* php_hash_fetch_ops/static inline const struct _php_hash_ops\* php_hash_fetch_ops/g' {} + 2>/dev/null || true
fi
- name: Configure Linux ZTS PHP embed library
if: runner.os == 'Linux'
shell: bash
run: |
php_home="$(php-config --prefix)"
embed_library="${php_home}/lib/libphp.so"
test -x "${php_home}/bin/php-config"
test -f "${embed_library}"
echo "PHP_HOME=${php_home}" >> "${GITHUB_ENV}"
echo "Using ZTS PHP $(php-config --version) embed library: ${embed_library}"
# ---------------- Windows 构建环境准备 ----------------
- name: Setup MSVC Toolchain
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
- name: Restore Windows GMP and MPFR cache
if: runner.os == 'Windows'
id: cache-gmp-mpfr
uses: actions/cache/restore@v4
with:
path: ${{ runner.temp }}\typephp-cache\vcpkg-x64-windows
key: windows-2022-msvc-vcpkg-gmp-mpfr-x64-v1
- name: Install Windows GMP and MPFR dependencies
if: runner.os == 'Windows' && steps.cache-gmp-mpfr.outputs.cache-hit != 'true'
shell: powershell
run: |
if (-not $env:VCPKG_INSTALLATION_ROOT) {
throw "[CI] VCPKG_INSTALLATION_ROOT is not set on windows-latest"
}
$vcpkg = Join-Path $env:VCPKG_INSTALLATION_ROOT "vcpkg.exe"
if (-not (Test-Path $vcpkg)) {
throw "[CI] vcpkg executable not found: $vcpkg"
}
$triplet = "x64-windows"
& $vcpkg install gmp mpfr --triplet=$triplet --clean-after-build
if ($LASTEXITCODE -ne 0) {
throw "[CI] vcpkg failed to install GMP/MPFR (exit code $LASTEXITCODE)"
}
$nativeRoot = Join-Path $env:VCPKG_INSTALLATION_ROOT "installed\$triplet"
$cacheRoot = Join-Path $env:RUNNER_TEMP "typephp-cache\vcpkg-x64-windows"
New-Item -ItemType Directory -Force -Path "$cacheRoot\include", "$cacheRoot\lib", "$cacheRoot\bin" | Out-Null
$requiredFiles = @(
(Join-Path $nativeRoot "include\gmp.h"),
(Join-Path $nativeRoot "include\mpfr.h"),
(Join-Path $nativeRoot "lib\gmp.lib"),
(Join-Path $nativeRoot "lib\gmpxx.lib"),
(Join-Path $nativeRoot "lib\mpfr.lib")
)
$missing = $requiredFiles | Where-Object { -not (Test-Path $_) }
if ($missing) {
Write-Host "[CI] vcpkg GMP/MPFR root: $nativeRoot"
Get-ChildItem (Join-Path $nativeRoot "include") -ErrorAction SilentlyContinue | Select-Object Name
Get-ChildItem (Join-Path $nativeRoot "lib") -ErrorAction SilentlyContinue | Select-Object Name
throw "[CI] Missing GMP/MPFR files: $($missing -join ', ')"
}
Copy-Item "$nativeRoot\include\*" "$cacheRoot\include" -Recurse -Force
Copy-Item "$nativeRoot\lib\gmp.lib", "$nativeRoot\lib\gmpxx.lib", "$nativeRoot\lib\mpfr.lib" "$cacheRoot\lib" -Force
Copy-Item "$nativeRoot\bin\*.dll" "$cacheRoot\bin" -Force -ErrorAction SilentlyContinue
Write-Host "[CI] Cached GMP/MPFR under: $cacheRoot"
- name: Save Windows GMP and MPFR cache
if: runner.os == 'Windows' && steps.cache-gmp-mpfr.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}\typephp-cache\vcpkg-x64-windows
key: ${{ steps.cache-gmp-mpfr.outputs.cache-primary-key }}
- name: Stage Windows GMP and MPFR dependencies
if: runner.os == 'Windows'
shell: powershell
run: |
$cacheRoot = Join-Path $env:RUNNER_TEMP "typephp-cache\vcpkg-x64-windows"
$requiredFiles = @(
(Join-Path $cacheRoot "include\gmp.h"),
(Join-Path $cacheRoot "include\mpfr.h"),
(Join-Path $cacheRoot "lib\gmp.lib"),
(Join-Path $cacheRoot "lib\gmpxx.lib"),
(Join-Path $cacheRoot "lib\mpfr.lib")
)
$missing = $requiredFiles | Where-Object { -not (Test-Path $_) }
if ($missing) {
throw "[CI] GMP/MPFR cache is incomplete: $($missing -join ', ')"
}
"GMP_MPFR_ROOT=$cacheRoot" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
Write-Host "[CI] Using cached GMP/MPFR root: $cacheRoot"
# ---------------- 安装 Composer 依赖 ----------------
- name: Install Composer Dependencies
run: composer install --prefer-dist --no-progress
# ---------------- Linux 编译与打包 ----------------
- name: Build and Package on Linux
if: runner.os == 'Linux'
shell: bash
run: |
chmod +x package.sh start.sh
./package.sh
echo "=== Verify dynamic library dependencies (ldd) ==="
ldd dist/webman-server.bin || true
ldd dist/libphpx.so || true
echo "=== Linux Smoke Test: running dist/start.sh status ==="
./dist/start.sh status || true
tar -czvf ${{ matrix.archive_name }} -C dist .
# ---------------- Windows 编译与打包 ----------------
- name: Setup PHP Windows Devpack (Headers & Libs for MSVC)
if: runner.os == 'Windows'
shell: powershell
run: |
$phpBin = (Get-Command php.exe).Source
$phpDir = Split-Path -Parent $phpBin
Write-Host "PHP Directory: $phpDir"
$phpVer = (php -r "echo PHP_VERSION;")
Write-Host "[CI] Installed PHP version is $phpVer. Resolving Windows Devpack URL..."
$urls = @(
"https://windows.php.net/downloads/releases/php-devel-pack-$phpVer-Win32-vs17-x64.zip",
"https://windows.php.net/downloads/releases/archives/php-devel-pack-$phpVer-Win32-vs17-x64.zip"
)
$downloaded = $false
foreach ($url in $urls) {
try {
Write-Host "[CI] Trying to download from $url ..."
Invoke-WebRequest -Uri $url -OutFile "$env:TEMP\php-devel-pack.zip" -UseBasicParsing -ErrorAction Stop
$downloaded = $true
Write-Host "[CI] Download successful!"
break
} catch {
Write-Host "[CI] URL not available, trying next..."
}
}
if (-not $downloaded) {
# 兜底抓取 releases 目录里的任一最新 8.4 devpack
Write-Host "[CI] Querying index of windows.php.net/downloads/releases/ ..."
$html = (Invoke-WebRequest -Uri "https://windows.php.net/downloads/releases/" -UseBasicParsing).Content
if ($html -match 'href="([^"]*php-devel-pack-8\.4\.[^"]*-Win32-vs17-x64\.zip)"') {
$fallbackUrl = "https://windows.php.net/downloads/releases/" + $matches[1]
Write-Host "[CI] Found fallback devpack: $fallbackUrl"
Invoke-WebRequest -Uri $fallbackUrl -OutFile "$env:TEMP\php-devel-pack.zip" -UseBasicParsing
$downloaded = $true
}
}
Expand-Archive -Path "$env:TEMP\php-devel-pack.zip" -DestinationPath "$env:TEMP\php-devpack" -Force
$extracted = Get-ChildItem "$env:TEMP\php-devpack" -Directory | Select-Object -First 1
if ($extracted) {
@("$phpDir\SDK\lib", "$phpDir\SDK\include", "$phpDir\dev", "$phpDir\lib", "$phpDir\include") | ForEach-Object {
if (-not (Test-Path $_)) { New-Item -ItemType Directory -Path $_ -Force }
}
Copy-Item -Path "$($extracted.FullName)\lib\*" -Destination "$phpDir\SDK\lib\" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$($extracted.FullName)\include\*" -Destination "$phpDir\SDK\include\" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$($extracted.FullName)\lib\*" -Destination "$phpDir\lib\" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$($extracted.FullName)\lib\*" -Destination "$phpDir\dev\" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$($extracted.FullName)\lib\*" -Destination "$phpDir\" -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$($extracted.FullName)\include\*" -Destination "$phpDir\include\" -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "[CI] Successfully placed .lib files in $phpDir\SDK\lib and $phpDir"
Get-ChildItem "$phpDir\SDK\lib" | Select-Object Name
# phpx_compat.h includes the UCRT declaration before PHP headers.
# Do not inject a second _wchmod declaration into ioutil.h: newer
# Windows SDKs already declare it in corecrt_wio.h.
Write-Host "[CI] Using the UCRT _wchmod declaration from corecrt_wio.h."
}
"PHP_HOME=$phpDir" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Build PHPX Library on Windows
if: runner.os == 'Windows'
shell: cmd
run: |
cd "%GITHUB_WORKSPACE%\vendor\swoole\phpx"
if not exist "build" mkdir "build"
cd build
cmake .. -G "NMake Makefiles" -Dphp_dir="%PHP_HOME%" -DGMP_MPFR_ROOT="%GMP_MPFR_ROOT%" -DBUILD_TESTS=OFF -DBUILD_EXT=OFF -DCMAKE_CXX_FLAGS="/DNOMINMAX"
if not exist "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib" mkdir "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib"
nmake phpx
if not exist "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib\phpx.lib" if exist "phpx.lib" copy /y "phpx.lib" "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib\"
if not exist "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib\phpx.lib" (
echo [ERROR] PHPX import library was not produced by CMake.
dir /s /b "%GITHUB_WORKSPACE%\vendor\swoole\phpx\*.lib"
exit /b 1
)
copy /y "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib\phpx.lib" "%GITHUB_WORKSPACE%\vendor\swoole\phpx\"
dir "%GITHUB_WORKSPACE%\vendor\swoole\phpx\lib"
- name: Build and Package on Windows
if: runner.os == 'Windows'
shell: cmd
run: |
call package.bat
powershell Compress-Archive -Path dist/* -DestinationPath ${{ matrix.archive_name }}
# ---------------- 上传构建产物 (Artifacts) ----------------
- name: Upload Build Artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.archive_name }}
path: ${{ matrix.archive_name }}
# ---------------- 发布 GitHub Release ----------------
- name: Create Release and Upload Asset
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: ${{ matrix.archive_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}