Compare commits

..

13 Commits

Author SHA1 Message Date
henderkes
2adedc0943 clang runtime bits must be passed target flag too 2026-07-09 14:07:41 +07:00
henderkes
fbb91e347e fix event build 2026-07-09 10:17:23 +07:00
henderkes
f3b7149287 fix brotli 2026-07-08 12:45:18 +07:00
henderkes
3823631a58 fix lto crash 2026-07-07 19:08:26 +07:00
henderkes
435d2c07b0 zstd stable too 2026-06-29 12:28:11 +07:00
henderkes
ed0a45da31 brotli stable 2026-06-29 12:26:39 +07:00
henderkes
6bc6e32dd0 strip env vars from configure command 2026-06-29 11:10:19 +07:00
henderkes
dac2ea5677 --with-sysconfdir option 2026-06-22 16:12:22 +07:00
henderkes
9398a6677d update protobuf 2026-06-14 11:52:30 +07:00
henderkes
967122cc7c fix 2026-06-09 15:14:47 +07:00
henderkes
53bd0fca4f add cpu builtins for zig 2026-06-05 19:33:44 +07:00
henderkes
daa694ab2c gmssl update 2026-06-05 10:07:12 +07:00
henderkes
1ae4a00e10 better zig-cc arch handling 2026-05-26 00:47:42 +00:00
14 changed files with 181 additions and 29 deletions

View File

@@ -287,6 +287,7 @@
"support": {
"BSD": "wip"
},
"arg-type": "with-path",
"type": "external",
"source": "ext-gmssl",
"lib-depends": [

View File

@@ -124,10 +124,9 @@
}
},
"ext-brotli": {
"type": "git",
"type": "ghtagtar",
"repo": "kjdev/php-ext-brotli",
"path": "php-src/ext/brotli",
"rev": "master",
"url": "https://github.com/kjdev/php-ext-brotli",
"license": {
"type": "file",
"path": "LICENSE"
@@ -361,10 +360,9 @@
}
},
"ext-zstd": {
"type": "git",
"type": "ghtar",
"repo": "kjdev/php-ext-zstd",
"path": "php-src/ext/zstd",
"rev": "master",
"url": "https://github.com/kjdev/php-ext-zstd",
"license": {
"type": "file",
"path": "LICENSE"
@@ -421,9 +419,9 @@
}
},
"gmssl": {
"type": "ghtar",
"repo": "guanzhi/GmSSL",
"provide-pre-built": true,
"type": "git",
"url": "https://github.com/guanzhi/GmSSL.git",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE"
@@ -1108,7 +1106,7 @@
},
"protobuf": {
"type": "url",
"url": "https://pecl.php.net/get/protobuf-5.34.1.tgz",
"url": "https://pecl.php.net/get/protobuf",
"path": "php-src/ext/protobuf",
"filename": "protobuf.tgz",
"license": {

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
#[CustomExt('brotli')]
class brotli extends Extension
{
public function getUnixConfigureArg(bool $shared = false): string
{
return $this->getEnableArg($shared) . '--with-libbrotli';
}
}

View File

@@ -38,7 +38,23 @@ class event extends Extension
// Prevent event extension compile error on macOS
if ($this->builder instanceof MacOSBuilder) {
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
return true;
$patched = true;
}
return $this->patchLibeventConstPeer() || $patched;
}
public function patchBeforeSharedMake(): bool
{
$patched = parent::patchBeforeSharedMake();
return $this->patchLibeventConstPeer() || $patched;
}
private function patchLibeventConstPeer(): bool
{
$patched = false;
$file = SOURCE_PATH . '/php-src/ext/event/php8/classes/http_connection.c';
if (is_file($file) && FileSystem::replaceFileRegex($file, '/^\tchar \*address;$/m', "\tconst char *address;")) {
$patched = true;
}
return $patched;
}

View File

@@ -65,6 +65,8 @@ class BSDBuilder extends UnixBuilderBase
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$sysconfdir = $this->getOption('with-sysconfdir', false) ?
('--sysconfdir=' . $this->getOption('with-sysconfdir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
@@ -89,6 +91,7 @@ class BSDBuilder extends UnixBuilderBase
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$sysconfdir .
$json_74 .
$zts .
$this->makeStaticExtensionArgs()

View File

@@ -88,6 +88,8 @@ class LinuxBuilder extends UnixBuilderBase
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$sysconfdir = $this->getOption('with-sysconfdir', false) ?
('--sysconfdir=' . $this->getOption('with-sysconfdir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
@@ -125,6 +127,7 @@ class LinuxBuilder extends UnixBuilderBase
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_path .
$config_file_scan_dir .
$sysconfdir .
$json_74 .
$zts .
$maxExecutionTimers .

View File

@@ -96,6 +96,8 @@ class MacOSBuilder extends UnixBuilderBase
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$sysconfdir = $this->getOption('with-sysconfdir', false) ?
('--sysconfdir=' . $this->getOption('with-sysconfdir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
@@ -129,6 +131,7 @@ class MacOSBuilder extends UnixBuilderBase
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_path .
$config_file_scan_dir .
$sysconfdir .
$json_74 .
$zts .
$this->makeStaticExtensionArgs() . ' ' .

View File

@@ -30,7 +30,11 @@ trait libaom
->setBuildDir("{$this->source_dir}/builddir")
->addConfigureArgs(
"-DAOM_TARGET_CPU={$targetCpu}",
'-DCONFIG_RUNTIME_CPU_DETECT=1'
'-DCONFIG_RUNTIME_CPU_DETECT=1',
'-DENABLE_EXAMPLES=0',
'-DENABLE_TOOLS=0',
'-DENABLE_TESTS=0',
'-DENABLE_DOCS=0'
)
->build();
f_putenv("SPC_COMPILER_EXTRA={$extra}");

View File

@@ -39,6 +39,7 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
$this->addOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', $isWindows ? null : '/usr/local/etc/php');
$this->addOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', $isWindows ? null : '/usr/local/etc/php/conf.d');
$this->addOption('with-sysconfdir', null, InputOption::VALUE_REQUIRED, 'Set the sysconfdir (e.g. /etc/php-zts) used by configure (not available on Windows)');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
$this->addOption('with-micro-fake-cli', null, null, 'Let phpmicro\'s PHP_SAPI use "cli" instead of "micro"');
$this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs');

View File

@@ -175,6 +175,16 @@ class Downloader
hooks: [[CurlHook::class, 'setupGithubToken']],
retries: self::getRetryAttempts()
), true);
if (($source['prefer-stable'] ?? false) === true) {
$latest = json_decode(self::curlExec(
url: "https://api.github.com/repos/{$source['repo']}/releases/latest",
hooks: [[CurlHook::class, 'setupGithubToken']],
retries: self::getRetryAttempts()
), true);
if (is_array($latest) && isset($latest['assets'])) {
array_unshift($data, $latest);
}
}
$url = null;
$filename = null;
foreach ($data as $release) {

View File

@@ -138,6 +138,22 @@ class SourcePatcher
);
}
// strip our build-time env vars from phpinfo's "Configure Command" ('|' delimiter: PHP_BUILD_PROVIDER may contain '#')
if (is_unix()) {
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/configure',
'for var in CFLAGS CXXFLAGS CPPFLAGS LDFLAGS EXTRA_LDFLAGS_PROGRAM LIBS CC CXX; do',
'for var in CFLAGS CXXFLAGS CPPFLAGS LDFLAGS EXTRA_LDFLAGS_PROGRAM LIBS CC CXX '
. 'PKG_CONFIG PKG_CONFIG_PATH EXTENSION_DIR OPENSSL_LIBS '
. 'PHP_BUILD_SYSTEM PHP_BUILD_PROVIDER PHP_BUILD_COMPILER PHP_BUILD_ARCH; do',
);
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/configure',
'clean_configure_args=$(echo $clean_configure_args | $SED -e "s#\'$var=$val\'##")',
'clean_configure_args=$(echo $clean_configure_args | $SED -e "s|\'$var=$val\'||")',
);
}
if (file_exists(SOURCE_PATH . '/php-src/configure.ac.bak')) {
// restore configure.ac
FileSystem::restoreBackupFile(SOURCE_PATH . '/php-src/configure.ac');

View File

@@ -10,6 +10,7 @@ use SPC\store\CurlHook;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\LockFile;
use SPC\util\SPCTarget;
class Zig extends CustomPackage
{
@@ -157,32 +158,97 @@ class Zig extends CustomPackage
$profileLib = "{$libDir}/libclang_rt.profile.a";
$crtBegin = "{$libDir}/clang_rt.crtbegin.o";
$crtEnd = "{$libDir}/clang_rt.crtend.o";
if (file_exists($profileLib) && file_exists($crtBegin) && file_exists($crtEnd)) {
$cpuModelLib = "{$libDir}/libclang_rt.cpu_model.a";
if (file_exists($profileLib) && file_exists($crtBegin) && file_exists($crtEnd) && file_exists($cpuModelLib)) {
return;
}
$zig = "{$zig_bin_dir}/zig";
$verLine = trim((string) shell_exec(escapeshellarg($zig) . ' cc --version 2>/dev/null'));
if (!preg_match('/clang version (\d+\.\d+\.\d+)/', $verLine, $m)) {
logger()->warning('[zig] could not detect bundled clang version; skipping runtime bit build (--pgo + shared libs without __dso_handle)');
return;
throw new \RuntimeException('[zig] could not detect bundled clang version; cannot build clang runtime bits (--pgo, __dso_handle, __cpu_model)');
}
$llvmVersion = $m[1];
logger()->info("Building clang runtime bits for LLVM {$llvmVersion} (zig's bundled clang)");
$srcRoot = $this->fetchCompilerRtSource($llvmVersion);
if ($srcRoot === null) {
return;
throw new \RuntimeException("[zig] failed to fetch compiler-rt {$llvmVersion} sources; cannot build clang runtime bits (--pgo, __dso_handle, __cpu_model)");
}
$targetFlag = $this->baselineZigTarget();
f_mkdir($libDir, recursive: true);
if (!file_exists($profileLib)) {
$this->buildProfileRuntime($zig, $srcRoot, $profileLib);
$this->buildProfileRuntime($zig, $srcRoot, $profileLib, $targetFlag);
}
if (!file_exists($crtBegin) || !file_exists($crtEnd)) {
$this->buildCrtObjects($zig, $srcRoot, $crtBegin, $crtEnd);
$this->buildCrtObjects($zig, $srcRoot, $crtBegin, $crtEnd, $targetFlag);
}
if (!file_exists($cpuModelLib)) {
$this->buildCpuModelBuiltins($zig, $srcRoot, $cpuModelLib, $targetFlag);
}
FileSystem::removeDir($srcRoot);
// A zig install missing any of these degrades silently at build time
// (no .profraw with --pgo, no __dso_handle in shared libs, unresolved
// __cpu_model for __builtin_cpu_supports — the last one even changes
// which code paths PHP's configure picks, making builds
// machine-dependent). Fail the install instead.
$missing = array_filter(
[$profileLib, $crtBegin, $crtEnd, $cpuModelLib],
fn ($f) => !file_exists($f)
);
if ($missing !== []) {
throw new \RuntimeException('[zig] failed to build clang runtime bits: missing ' . implode(', ', array_map('basename', $missing)) . ' (see warnings above)');
}
}
/**
* A concrete `-target {arch}-linux-{abi}` for the clang runtime bits.
*/
private function baselineZigTarget(): string
{
$arch = SPCTarget::getTargetArch();
$abi = SPCTarget::getLibc() === 'musl' ? 'musl' : 'gnu';
return "-target {$arch}-linux-{$abi}";
}
private function buildCpuModelBuiltins(string $zig, string $srcRoot, string $libPath, string $targetFlag): void
{
$builtins = "{$srcRoot}/lib/builtins";
$arch = php_uname('m');
$cpuModelDir = "{$builtins}/cpu_model";
if (is_dir($cpuModelDir)) {
$src = match (true) {
in_array($arch, ['x86_64', 'amd64', 'i386', 'i686', 'x86'], true) => "{$cpuModelDir}/x86.c",
in_array($arch, ['aarch64', 'arm64'], true) => "{$cpuModelDir}/aarch64.c",
str_starts_with($arch, 'riscv') => "{$cpuModelDir}/riscv.c",
default => null,
};
$includes = '-I' . escapeshellarg($builtins) . ' -I' . escapeshellarg($cpuModelDir);
} else {
$src = "{$builtins}/cpu_model.c";
$includes = '-I' . escapeshellarg($builtins);
}
if ($src === null || !is_file($src)) {
logger()->warning("[zig] cpu_model source not found for arch {$arch} under {$builtins} — __builtin_cpu_supports/__cpu_model will be unresolved");
return;
}
$objDir = "{$srcRoot}/obj-cpu-model";
f_mkdir($objDir, recursive: true);
$obj = "{$objDir}/cpu_model.o";
$cflags = $targetFlag . ' -c -O2 -fPIC ' . $includes;
$cmd = escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($obj) . ' ' . escapeshellarg($src) . ' 2>&1';
if (!$this->runZigCmd($cmd, $obj, "failed to compile {$src}")) {
return;
}
$arCmd = escapeshellarg($zig) . ' ar rcs ' . escapeshellarg($libPath) . ' ' . escapeshellarg($obj) . ' 2>&1';
if (!$this->runZigCmd($arCmd, $libPath, 'zig ar failed for cpu_model')) {
return;
}
logger()->info('[zig] libclang_rt.cpu_model.a installed (' . filesize($libPath) . ' bytes)');
}
private function fetchCompilerRtSource(string $llvmVersion): ?string
@@ -206,7 +272,7 @@ class Zig extends CustomPackage
return $srcRoot;
}
private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath): void
private function buildProfileRuntime(string $zig, string $srcRoot, string $libPath, string $targetFlag): void
{
$profileSrc = "{$srcRoot}/lib/profile";
$profileInc = "{$srcRoot}/include";
@@ -230,7 +296,7 @@ class Zig extends CustomPackage
$objDir = "{$srcRoot}/obj-profile";
f_mkdir($objDir, recursive: true);
$cflags = '-c -O2 -fPIC -fvisibility=hidden ' .
$cflags = $targetFlag . ' -c -O2 -fPIC -fvisibility=hidden ' .
'-I' . escapeshellarg($profileInc) . ' ' .
'-DCOMPILER_RT_HAS_ATOMICS=1 -DCOMPILER_RT_HAS_FCNTL_LCK=1 -DCOMPILER_RT_HAS_UNAME=1';
$objs = [];
@@ -249,7 +315,7 @@ class Zig extends CustomPackage
logger()->info('[zig] libclang_rt.profile.a installed (' . filesize($libPath) . ' bytes)');
}
private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd): void
private function buildCrtObjects(string $zig, string $srcRoot, string $crtBegin, string $crtEnd, string $targetFlag): void
{
$beginSrc = "{$srcRoot}/lib/builtins/crtbegin.c";
$endSrc = "{$srcRoot}/lib/builtins/crtend.c";
@@ -257,7 +323,7 @@ class Zig extends CustomPackage
logger()->error("[zig] crtbegin/crtend source missing under {$srcRoot}/lib/builtins — shared libs will lack __dso_handle");
return;
}
$cflags = '-c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY';
$cflags = $targetFlag . ' -c -O2 -fPIC -fvisibility=hidden -DCRT_HAS_INITFINI_ARRAY';
foreach ([[$beginSrc, $crtBegin], [$endSrc, $crtEnd]] as [$src, $dst]) {
$cmd = escapeshellarg($zig) . ' cc ' . $cflags . ' -o ' . escapeshellarg($dst) . ' ' . escapeshellarg($src) . ' 2>&1';
if (!$this->runZigCmd($cmd, $dst, "failed to compile {$src}")) {

View File

@@ -27,12 +27,14 @@ while [[ $# -gt 0 ]]; do
-march=*|-mcpu=*)
OPT_NAME="${1%%=*}"
OPT_VALUE="${1#*=}"
# Skip armv8- flags entirely as Zig doesn't support them
if [[ "$OPT_VALUE" == armv8-* ]]; then
shift
continue
# zig rejects -march=armv8-a but accepts -mcpu=generic+v8a; rewrite
# armv<X>[.<Y>]-a[+feat] -> generic+v<X>[_<Y>]a[+feat] so it goes through.
if [[ "$OPT_VALUE" =~ ^armv([89])(\.([0-9]+))?-a(\+.*)?$ ]]; then
arch_feat="v${BASH_REMATCH[1]}"
[[ -n "${BASH_REMATCH[3]}" ]] && arch_feat="${arch_feat}_${BASH_REMATCH[3]}"
OPT_VALUE="generic+${arch_feat}a${BASH_REMATCH[4]}"
fi
# replace -march=x86-64 with -march=x86_64
# zig uses snake_case in CPU/feature names (x86-64 -> x86_64).
OPT_VALUE="${OPT_VALUE//-/_}"
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
shift
@@ -60,10 +62,13 @@ for _a in "${PARSED_ARGS[@]}"; do
done
[[ "$SPC_COMPILER_EXTRA" == *-fprofile-generate* || "$SPC_COMPILER_EXTRA" == *-fcs-profile-generate* ]] && NEED_PROFILE_RT=1
if [[ $IS_LINK -eq 1 && $NEED_PROFILE_RT -eq 1 && -f "$SCRIPT_DIR/lib/libclang_rt.profile.a" ]]; then
PARSED_ARGS+=("$SCRIPT_DIR/lib/libclang_rt.profile.a" "-Wl,-u,__llvm_profile_runtime")
PARSED_ARGS+=("-x" "none" "$SCRIPT_DIR/lib/libclang_rt.profile.a" "-Wl,-u,__llvm_profile_runtime")
fi
if [[ $IS_LINK -eq 1 && $NEED_CRT -eq 1 && -f "$SCRIPT_DIR/lib/clang_rt.crtbegin.o" && -f "$SCRIPT_DIR/lib/clang_rt.crtend.o" ]]; then
PARSED_ARGS+=("$SCRIPT_DIR/lib/clang_rt.crtbegin.o" "$SCRIPT_DIR/lib/clang_rt.crtend.o")
PARSED_ARGS+=("-x" "none" "$SCRIPT_DIR/lib/clang_rt.crtbegin.o" "$SCRIPT_DIR/lib/clang_rt.crtend.o")
fi
if [[ $IS_LINK -eq 1 && -f "$SCRIPT_DIR/lib/libclang_rt.cpu_model.a" ]]; then
PARSED_ARGS+=("-x" "none" "$SCRIPT_DIR/lib/libclang_rt.cpu_model.a")
fi
[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET=""

View File

@@ -50,6 +50,15 @@ class ZigToolchain implements ToolchainInterface
$extra_vars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
GlobalEnvManager::putenv("SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no {$extra_vars}");
}
// An __attribute__((ifunc)) in LTO bitcode crashes zig 0.16's lld (LLVM 21)
// during thin-link symbol resolution
$all_flags = $cflags . ' ' . (getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') ?: '') . ' ' . (getenv('SPC_DEFAULT_LD_FLAGS') ?: '');
if (str_contains($all_flags, '-flto')) {
$extra_vars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
if (!str_contains($extra_vars, 'ax_cv_have_func_attribute_ifunc')) {
GlobalEnvManager::putenv("SPC_EXTRA_PHP_VARS={$extra_vars} ax_cv_have_func_attribute_ifunc=no");
}
}
}
public function getCompilerInfo(): ?string