Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
656a58c
remove source dir after successful build in CI environment
henderkes Dec 18, 2025
0247458
we were installing to wrong dir if source name != lib name
henderkes Dec 18, 2025
ce44e00
@crazywhalecc how to use patch points to delete source dirs?
henderkes Dec 18, 2025
037d224
why does phpstan think this is necessary?
henderkes Dec 18, 2025
e677be7
remove
henderkes Dec 18, 2025
9e051c8
fix: check for link first before checking for is_dir
henderkes Dec 18, 2025
e1a14bb
fix implicit include
henderkes Dec 18, 2025
53f7cde
fix swoole compilation with php 8.5.1
henderkes Dec 18, 2025
6b52000
fix downloader selecting drafts
henderkes Dec 20, 2025
f7ca621
Test
crazywhalecc Dec 26, 2025
9a681a9
add mariadb mysqlnd plugins
henderkes Dec 27, 2025
09b89a3
WIP: use system libraries for grpc without building our own grpc lib
henderkes Dec 27, 2025
e952f1c
we don't even need to build grpc library for grpc extension...
henderkes Dec 27, 2025
5ef4623
grpc will fail for php 8.5, it's not updated yet
henderkes Dec 27, 2025
93a3590
factor grpc extension out to ext-grpc, keep library for now, even tho…
henderkes Dec 28, 2025
2f31226
make grpc php 8.5 compatible
henderkes Dec 28, 2025
e7a88f1
enable fat for gmp when next version releases
henderkes Dec 29, 2025
08388c0
force enable tailcall vm with zig
henderkes Dec 29, 2025
7688a55
don't get zig master branch
henderkes Dec 29, 2025
022fdb2
fix no-strip
henderkes Dec 29, 2025
a06cc32
pin libpng to released tags, not git
henderkes Dec 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
428 changes: 221 additions & 207 deletions composer.lock

Large diffs are not rendered by default.

36 changes: 34 additions & 2 deletions config/ext.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,13 @@
"BSD": "wip"
},
"type": "external",
"source": "grpc",
"source": "ext-grpc",
"arg-type-unix": "enable-path",
"cpp-extension": true,
"lib-depends": [
"grpc"
"zlib",
"openssl",
"libcares"
]
},
"iconv": {
Expand Down Expand Up @@ -487,6 +489,36 @@
"zlib"
]
},
"mysqlnd_ed25519": {
"type": "external",
"source": "mysqlnd_ed25519",
"arg-type": "enable",
"target": [
"shared"
],
"ext-depends": [
"mysqlnd"
],
"lib-depends": [
"libsodium",
"openssl"
]
},
"mysqlnd_parsec": {
"type": "external",
"source": "mysqlnd_parsec",
"arg-type": "enable",
"target": [
"shared"
],
"ext-depends": [
"mysqlnd"
],
"lib-depends": [
"libsodium",
"openssl"
]
},
"oci8": {
"type": "wip",
"support": {
Expand Down
43 changes: 37 additions & 6 deletions config/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@
"path": "LICENSE"
}
},
"ext-grpc": {
"type": "url",
"url": "https://pecl.php.net/get/grpc",
"path": "php-src/ext/grpc",
"filename": "grpc.tgz",
"license": {
"type": "file",
"path": [
"LICENSE"
]
}
},
"ext-imagick": {
"type": "url",
"url": "https://pecl.php.net/get/imagick",
Expand Down Expand Up @@ -670,19 +682,20 @@
}
},
"libpng": {
"type": "git",
"url": "https://github.com/glennrp/libpng.git",
"rev": "libpng16",
"type": "ghtagtar",
"repo": "pnggroup/libpng",
"match": "v1\\.6\\.\\d+",
"query": "?per_page=150",
"provide-pre-built": true,
"license": {
"type": "file",
"path": "LICENSE"
}
},
"librabbitmq": {
"type": "git",
"url": "https://github.com/alanxz/rabbitmq-c.git",
"rev": "master",
"type": "ghtar",
"repo": "alanxz/rabbitmq-c",
"prefer-stable": true,
"license": {
"type": "file",
"path": "LICENSE"
Expand Down Expand Up @@ -871,6 +884,24 @@
"path": "LICENSE"
}
},
"mysqlnd_ed25519": {
"type": "pie",
"repo": "mariadb/mysqlnd_ed25519",
"path": "php-src/ext/mysqlnd_ed25519",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"mysqlnd_parsec": {
"type": "pie",
"repo": "mariadb/mysqlnd_parsec",
"path": "php-src/ext/mysqlnd_parsec",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ncurses": {
"type": "filelist",
"url": "https://ftp.gnu.org/pub/gnu/ncurses/",
Expand Down
3 changes: 3 additions & 0 deletions src/SPC/builder/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,9 @@ public function buildShared(array $visited = []): void
logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)');
return;
}
if ((string) Config::getExt($this->getName(), 'type') === 'addon') {
return;
}
logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)');
foreach ($this->dependencies as $dependency) {
if (!$dependency instanceof Extension) {
Expand Down
25 changes: 17 additions & 8 deletions src/SPC/builder/LibraryBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,18 +184,18 @@ public function tryBuild(bool $force_build = false): int

// extract first if not exists
if (!is_dir($this->source_dir)) {
$this->getBuilder()->emitPatchPoint('before-library[ ' . static::NAME . ']-extract');
$this->getBuilder()->emitPatchPoint('before-library[' . static::NAME . ']-extract');
SourceManager::initSource(libs: [static::NAME], source_only: true);
$this->getBuilder()->emitPatchPoint('after-library[ ' . static::NAME . ']-extract');
$this->getBuilder()->emitPatchPoint('after-library[' . static::NAME . ']-extract');
}

if (!$this->patched && $this->patchBeforeBuild()) {
file_put_contents($this->source_dir . '/.spc.patched', 'PATCHED!!!');
}
$this->getBuilder()->emitPatchPoint('before-library[ ' . static::NAME . ']-build');
$this->getBuilder()->emitPatchPoint('before-library[' . static::NAME . ']-build');
$this->build();
$this->installLicense();
$this->getBuilder()->emitPatchPoint('after-library[ ' . static::NAME . ']-build');
$this->getBuilder()->emitPatchPoint('after-library[' . static::NAME . ']-build');
return LIB_STATUS_OK;
}

Expand Down Expand Up @@ -346,19 +346,19 @@ protected function getSnakeCaseName(): string
*/
protected function installLicense(): void
{
FileSystem::createDir(BUILD_ROOT_PATH . '/source-licenses/' . $this->getName());
$source = Config::getLib($this->getName(), 'source');
FileSystem::createDir(BUILD_ROOT_PATH . "/source-licenses/{$source}");
$license_files = Config::getSource($source)['license'] ?? [];
if (is_assoc_array($license_files)) {
$license_files = [$license_files];
}
foreach ($license_files as $index => $license) {
if ($license['type'] === 'text') {
FileSystem::writeFile(BUILD_ROOT_PATH . '/source-licenses/' . $this->getName() . "/{$index}.txt", $license['text']);
FileSystem::writeFile(BUILD_ROOT_PATH . "/source-licenses/{$source}/{$index}.txt", $license['text']);
continue;
}
if ($license['type'] === 'file') {
copy($this->source_dir . '/' . $license['path'], BUILD_ROOT_PATH . '/source-licenses/' . $this->getName() . "/{$index}.txt");
copy($this->source_dir . '/' . $license['path'], BUILD_ROOT_PATH . "/source-licenses/{$source}/{$index}.txt");
}
}
}
Expand All @@ -375,8 +375,17 @@ protected function isLibraryInstalled(): bool
return false;
}
}
$pkg_config_path = getenv('PKG_CONFIG_PATH') ?: '';
$search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path));
foreach (Config::getLib(static::NAME, 'pkg-configs', []) as $name) {
if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$name}.pc")) {
$found = false;
foreach ($search_paths as $path) {
if (file_exists($path . "/{$name}.pc")) {
$found = true;
break;
}
}
if (!$found) {
return false;
}
}
Expand Down
16 changes: 6 additions & 10 deletions src/SPC/builder/extension/grpc.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ public function patchBeforeBuildconf(): bool
if ($this->builder instanceof WindowsBuilder) {
throw new ValidationException('grpc extension does not support windows yet');
}
if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) {
return false;
}
// soft link to the grpc source code
if (is_dir($this->source_dir . '/src/php/ext/grpc')) {
shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
} else {
throw new ValidationException('Cannot find grpc source code in ' . $this->source_dir . '/src/php/ext/grpc');
}
FileSystem::replaceFileStr(
$this->source_dir . '/src/php/ext/grpc/call.c',
'zend_exception_get_default(TSRMLS_C),',
'zend_ce_exception,',
);
if (SPCTarget::getTargetOS() === 'Darwin') {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/ext/grpc/config.m4',
$this->source_dir . '/config.m4',
'/GRPC_LIBDIR=.*$/m',
'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"'
);
Expand Down
22 changes: 22 additions & 0 deletions src/SPC/builder/extension/mysqlnd_ed25519.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace SPC\builder\extension;

use SPC\builder\Extension;
use SPC\util\CustomExt;

#[CustomExt('mysqlnd_ed25519')]
class mysqlnd_ed25519 extends Extension
{
public function getConfigureArg(bool $shared = false): string
{
return '--with-mysqlnd_ed25519' . ($shared ? '=shared' : '');
}

public function getUnixConfigureArg(bool $shared = false): string
{
return $this->getConfigureArg();
}
}
22 changes: 22 additions & 0 deletions src/SPC/builder/extension/mysqlnd_parsec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace SPC\builder\extension;

use SPC\builder\Extension;
use SPC\util\CustomExt;

#[CustomExt('mysqlnd_parsec')]
class mysqlnd_parsec extends Extension
{
public function getConfigureArg(bool $shared = false): string
{
return '--enable-mysqlnd_parsec' . ($shared ? '=shared' : '');
}

public function getUnixConfigureArg(bool $shared = false): string
{
return $this->getConfigureArg();
}
}
1 change: 1 addition & 0 deletions src/SPC/builder/extension/swoole.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class swoole extends Extension
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
FileSystem::replaceFileStr($this->source_dir . '/ext-src/php_swoole_private.h', 'PHP_VERSION_ID > 80500', 'PHP_VERSION_ID >= 80600');
if ($this->builder instanceof MacOSBuilder) {
// Fix swoole with event extension <util.h> conflict bug
$util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h';
Expand Down
2 changes: 1 addition & 1 deletion src/SPC/builder/traits/UnixLibraryTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function patchPkgconfPrefix(array $files = [], int $patch_option = PKGCON
$files = array_map(fn ($x) => "{$x}.pc", $conf_pc);
}
foreach ($files as $name) {
$realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name);
$realpath = realpath(BUILD_LIB_PATH . '/pkgconfig/' . $name);
if ($realpath === false) {
throw new PatchException('pkg-config prefix patcher', 'Cannot find library [' . static::NAME . '] pkgconfig file [' . $name . '] in ' . BUILD_LIB_PATH . '/pkgconfig/ !');
}
Expand Down
7 changes: 3 additions & 4 deletions src/SPC/builder/unix/UnixBuilderBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,10 @@ public function deployBinary(string $src, string $dst, bool $executable = true):
throw new SPCInternalException("Deploy failed. Cannot find file after copy: {$dst}");
}

// extract debug info
$this->extractDebugInfo($dst);

// strip
if (!$this->getOption('no-strip')) {
// extract debug info
$this->extractDebugInfo($dst);
// extra strip
$this->stripBinary($dst);
}

Expand Down
4 changes: 3 additions & 1 deletion src/SPC/builder/unix/library/gmp.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ protected function build(): void
->appendEnv([
'CFLAGS' => '-std=c17',
])
->configure()
->configure(
'--enable-fat'
)
->make();
$this->patchPkgconfPrefix(['gmp.pc']);
}
Expand Down
5 changes: 3 additions & 2 deletions src/SPC/builder/unix/library/libwebp.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ trait libwebp
{
protected function build(): void
{
$code = 'int main() { return _mm256_cvtsi256_si32(_mm256_setzero_si256()); }';
$code = '#include <immintrin.h>
int main() { return _mm256_cvtsi256_si32(_mm256_setzero_si256()); }';
$cc = getenv('CC') ?: 'gcc';
[$ret] = shell()->execWithResult("echo '{$code}' | {$cc} -x c -mavx2 -o /dev/null - 2>&1");
[$ret] = shell()->execWithResult("printf '%s' '{$code}' | {$cc} -x c -mavx2 -o /dev/null - 2>&1");
$disableAvx2 = $ret !== 0 && GNU_ARCH === 'x86_64' && PHP_OS_FAMILY === 'Linux';

UnixCMakeExecutor::create($this)
Expand Down
6 changes: 5 additions & 1 deletion src/SPC/store/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ public static function getLatestBitbucketTag(string $name, array $source): array
public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array
{
logger()->debug("finding {$name} source from github {$type} tarball");
$source['query'] ??= '';
$data = json_decode(self::curlExec(
url: "https://api.github.com/repos/{$source['repo']}/{$type}",
url: "https://api.github.com/repos/{$source['repo']}/{$type}{$source['query']}",
hooks: [[CurlHook::class, 'setupGithubToken']],
retries: self::getRetryAttempts()
), true, 512, JSON_THROW_ON_ERROR);
Expand All @@ -108,6 +109,9 @@ public static function getLatestGithubTarball(string $name, array $source, strin
if (($rel['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) {
continue;
}
if (($rel['draft'] ?? false) === true && (($source['prefer-stable'] ?? false) || !$rel['tarball_url'])) {
continue;
}
if (!($source['match'] ?? null)) {
$url = $rel['tarball_url'] ?? null;
break;
Expand Down
10 changes: 5 additions & 5 deletions src/SPC/store/FileSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,13 +408,13 @@ public static function removeDir(string $dir): bool
continue;
}
$sub_file = self::convertPath($dir . '/' . $v);
if (is_dir($sub_file)) {
# 如果是 目录 且 递推 , 则递推添加下级文件
if (!self::removeDir($sub_file)) {
if (is_link($sub_file) || is_file($sub_file)) {
if (!unlink($sub_file)) {
return false;
}
} elseif (is_link($sub_file) || is_file($sub_file)) {
if (!unlink($sub_file)) {
} elseif (is_dir($sub_file)) {
# 如果是 目录 且 递推 , 则递推添加下级文件
if (!self::removeDir($sub_file)) {
return false;
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/SPC/store/pkg/Zig.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ public function fetch(string $name, bool $force = false, ?array $config = null):

$latest_version = null;
foreach ($index_json as $version => $data) {
$latest_version = $version;
break;
// Skip the master branch, get the latest stable release
if ($version !== 'master') {
$latest_version = $version;
break;
}
}

if (!$latest_version) {
Expand Down
4 changes: 3 additions & 1 deletion src/SPC/toolchain/ZigToolchain.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ public function afterInit(): void
GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}");
}
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: '';
GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_preserve_none=yes');
$has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4');
if (!$has_avx512) {
GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no');
$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}");
}
}

Expand Down
Loading
Loading