Merge branch 'main' into v3-refactor/win-exts

# Conflicts:
#	config/env.ini
This commit is contained in:
crazywhalecc
2026-03-31 15:13:23 +08:00
30 changed files with 414 additions and 64 deletions

View File

@@ -96,7 +96,8 @@ class Extension
fn ($x) => $x->getStaticLibFiles(),
$this->getLibraryDependencies(recursive: true)
);
return implode(' ', $ret);
$libs = implode(' ', $ret);
return deduplicate_flags($libs);
}
/**

View File

@@ -365,6 +365,27 @@ abstract class LibraryBase
protected function isLibraryInstalled(): bool
{
if ($pkg_configs = Config::getLib(static::NAME, 'pkg-configs', [])) {
$pkg_config_path = getenv('PKG_CONFIG_PATH') ?: '';
$search_paths = array_unique(array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)));
foreach ($pkg_configs as $name) {
$found = false;
foreach ($search_paths as $path) {
if (file_exists($path . "/{$name}.pc")) {
$found = true;
break;
}
}
if (!$found) {
return false;
}
}
// allow using system dependencies if pkg_config_path is explicitly defined
if (count($search_paths) > 1) {
return true;
}
}
foreach (Config::getLib(static::NAME, 'static-libs', []) as $name) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
return false;

View File

@@ -11,7 +11,6 @@ use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
#[CustomExt('grpc')]
class grpc extends Extension
@@ -21,18 +20,50 @@ class grpc extends Extension
if ($this->builder instanceof WindowsBuilder) {
throw new ValidationException('grpc extension does not support windows yet');
}
// Fix deprecated PHP API usage in call.c
FileSystem::replaceFileStr(
$this->source_dir . '/src/php/ext/grpc/call.c',
"{$this->source_dir}/src/php/ext/grpc/call.c",
'zend_exception_get_default(TSRMLS_C),',
'zend_ce_exception,',
);
if (SPCTarget::getTargetOS() === 'Darwin') {
FileSystem::replaceFileRegex(
$this->source_dir . '/config.m4',
'/GRPC_LIBDIR=.*$/m',
'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"'
);
}
$config_m4 = <<<'M4'
PHP_ARG_ENABLE(grpc, [whether to enable grpc support], [AS_HELP_STRING([--enable-grpc], [Enable grpc support])])
if test "$PHP_GRPC" != "no"; then
PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/include)
PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
GRPC_LIBDIR=@@build_lib_path@@
PHP_ADD_LIBPATH($GRPC_LIBDIR)
PHP_ADD_LIBRARY(grpc,,GRPC_SHARED_LIBADD)
LIBS="-lpthread $LIBS"
PHP_ADD_LIBRARY(pthread)
case $host in
*darwin*)
PHP_ADD_LIBRARY(c++,1,GRPC_SHARED_LIBADD)
;;
*)
PHP_ADD_LIBRARY(stdc++,1,GRPC_SHARED_LIBADD)
PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD)
PHP_ADD_LIBRARY(rt)
;;
esac
PHP_NEW_EXTENSION(grpc, @grpc_c_files@, $ext_shared, , -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
PHP_SUBST(GRPC_SHARED_LIBADD)
PHP_INSTALL_HEADERS([ext/grpc], [php_grpc.h])
fi
M4;
$replace = get_pack_replace();
// load grpc c files from src/php/ext/grpc
$c_files = glob($this->source_dir . '/src/php/ext/grpc/*.c');
$replace['@grpc_c_files@'] = implode(" \\\n ", array_map(fn ($f) => 'src/php/ext/grpc/' . basename($f), $c_files));
$config_m4 = str_replace(array_keys($replace), array_values($replace), $config_m4);
file_put_contents($this->source_dir . '/config.m4', $config_m4);
copy($this->source_dir . '/src/php/ext/grpc/php_grpc.h', $this->source_dir . '/php_grpc.h');
return true;
}
@@ -48,7 +79,6 @@ class grpc extends Extension
public function patchBeforeMake(): bool
{
parent::patchBeforeMake();
// add -Wno-strict-prototypes
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true;
}

View File

@@ -50,19 +50,16 @@ class swoole extends Extension
// commonly used feature: coroutine-time
$arg .= ' --enable-swoole-coro-time --with-pic';
$arg .= ' --enable-swoole-ssh --enable-swoole-curl';
$arg .= $this->builder->getOption('enable-zts') ? ' --enable-swoole-thread --disable-thread-context' : ' --disable-swoole-thread --enable-thread-context';
// required features: curl, openssl (but curl hook is buggy for php 8.0)
$arg .= $this->builder->getPHPVersionID() >= 80100 ? ' --enable-swoole-curl' : ' --disable-swoole-curl';
$arg .= ' --enable-openssl';
// additional features that only require libraries
$arg .= $this->builder->getLib('libcares') ? ' --enable-cares' : '';
$arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : '';
$arg .= $this->builder->getLib('nghttp2') ? (' --with-nghttp2-dir=' . BUILD_ROOT_PATH) : '';
$arg .= $this->builder->getLib('zstd') ? ' --enable-zstd' : '';
$arg .= $this->builder->getLib('liburing') ? ' --enable-iouring' : '';
$arg .= $this->builder->getLib('liburing') ? ' --enable-iouring --enable-uring-socket' : '';
$arg .= $this->builder->getExt('sockets') ? ' --enable-sockets' : '';
// enable additional features that require the pdo extension, but conflict with pdo_* extensions
@@ -74,6 +71,7 @@ class swoole extends Extension
$config = (new SPCConfigUtil($this->builder))->getLibraryConfig($this->builder->getLib('unixodbc'));
$arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"';
}
$arg .= $this->builder->getExt('ftp')?->isBuildStatic() ? ' --disable-swoole-ftp' : ' --enable-swoole-ftp';
if ($this->getExtVersion() >= '6.1.0') {
$arg .= ' --enable-swoole-stdext';

View File

@@ -162,7 +162,7 @@ class LinuxBuilder extends UnixBuilderBase
throw new WrongUsageException(
"You're building against musl libc statically (the default on Linux), but you're trying to build shared extensions.\n" .
'Static musl libc does not implement `dlopen`, so your php binary is not able to load shared extensions.' . "\n" .
'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, or use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc`.'
'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc` or use SPC_MUSL_DYNAMIC=true on alpine.'
);
}
logger()->info('Building shared extensions...');

View File

@@ -72,6 +72,10 @@ trait UnixSystemUtilTrait
if (!is_file($symbol_file)) {
throw new SPCInternalException("The symbol file {$symbol_file} does not exist, please check if nm command is available.");
}
// https://github.com/ziglang/zig/issues/24662
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
return '-Wl,--export-dynamic'; // needs release 0.16, can be removed then
}
// macOS/zig
if (SPCTarget::getTargetOS() !== 'Linux' || ToolchainManager::getToolchainClass() === ZigToolchain::class) {
return "-Wl,-exported_symbols_list,{$symbol_file}";

View File

@@ -457,6 +457,7 @@ abstract class UnixBuilderBase extends BuilderBase
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' .
'-X \'github.com/caddyserver/caddy/v2/modules/caddyhttp.ServerHeader=FrankenPHP Caddy\' ' .
'-X \'github.com/caddyserver/caddy/v2.CustomBinaryName=frankenphp\' ' .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
"v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",

View File

@@ -13,8 +13,8 @@ trait freetype
{
$cmake = UnixCMakeExecutor::create($this)
->optionalLib('libpng', ...cmake_boolean_args('FT_DISABLE_PNG', true))
->optionalLib('bzip2', ...cmake_boolean_args('FT_DISABLE_BZIP2', true))
->optionalLib('brotli', ...cmake_boolean_args('FT_DISABLE_BROTLI', true))
->addConfigureArgs('-DFT_DISABLE_BZIP2=ON')
->addConfigureArgs('-DFT_DISABLE_BROTLI=ON')
->addConfigureArgs('-DFT_DISABLE_HARFBUZZ=ON');
// fix cmake 4.0 compatibility

View File

@@ -11,7 +11,10 @@ trait libde265
protected function build(): void
{
UnixCMakeExecutor::create($this)
->addConfigureArgs('-DENABLE_SDL=OFF')
->addConfigureArgs(
'-DENABLE_SDL=OFF',
'-DENABLE_DECODER=OFF'
)
->build();
$this->patchPkgconfPrefix(['libde265.pc']);
}

View File

@@ -14,6 +14,7 @@ trait libjpeg
->addConfigureArgs(
'-DENABLE_STATIC=ON',
'-DENABLE_SHARED=OFF',
'-DWITH_SYSTEM_ZLIB=ON'
)
->build();
// patch pkgconfig

View File

@@ -93,8 +93,7 @@ trait postgresql
// remove dynamic libs
shell()->cd($this->source_dir . '/build')
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so.*")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so*")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.dylib");
FileSystem::replaceFileStr("{$this->getLibDir()}/pkgconfig/libpq.pc", '-lldap', '-lldap -llber');

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait unixodbc
@@ -30,7 +31,15 @@ trait unixodbc
'--enable-gui=no',
)
->make();
$this->patchPkgconfPrefix(['odbc.pc', 'odbccr.pc', 'odbcinst.pc']);
$pkgConfigs = ['odbc.pc', 'odbccr.pc', 'odbcinst.pc'];
$this->patchPkgconfPrefix($pkgConfigs);
foreach ($pkgConfigs as $file) {
FileSystem::replaceFileStr(
BUILD_LIB_PATH . "/pkgconfig/{$file}",
'$(top_build_prefix)libltdl/libltdlc.la',
''
);
}
$this->patchLaDependencyPrefix();
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class brotli extends WindowsLibraryBase
{
public const NAME = 'brotli';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBROTLI_BUILD_TOOLS=OFF ' .
'-DBROTLI_BUNDLED_MODE=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -24,6 +24,8 @@ class freetype extends WindowsLibraryBase
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DFT_DISABLE_BROTLI=TRUE ' .
'-DFT_DISABLE_BZIP2=TRUE ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class libaom extends WindowsLibraryBase
{
public const NAME = 'libaom';
protected function build(): void
{
// libaom source tree contains a build/cmake/ directory with its own
// cmake modules, so we must use a different name for the build dir.
FileSystem::resetDir($this->source_dir . '\builddir');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-S . -B builddir ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DAOM_TARGET_CPU=generic ' .
'-DENABLE_DOCS=OFF ' .
'-DENABLE_EXAMPLES=OFF ' .
'-DENABLE_TESTDATA=OFF ' .
'-DENABLE_TESTS=OFF ' .
'-DENABLE_TOOLS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build builddir --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -31,8 +31,24 @@ class zlib extends WindowsLibraryBase
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
copy(BUILD_LIB_PATH . '\zlibstatic.lib', BUILD_LIB_PATH . '\zlib_a.lib');
unlink(BUILD_ROOT_PATH . '\bin\zlib.dll');
unlink(BUILD_LIB_PATH . '\zlib.lib');
$detect_list = [
'zlibstatic.lib',
'zs.lib',
'libzs.lib',
'libz.lib',
];
foreach ($detect_list as $item) {
if (file_exists(BUILD_LIB_PATH . '\\' . $item)) {
FileSystem::copy(BUILD_LIB_PATH . '\\' . $item, BUILD_LIB_PATH . '\zlib_a.lib');
FileSystem::copy(BUILD_LIB_PATH . '\\' . $item, BUILD_LIB_PATH . '\zlibstatic.lib');
break;
}
}
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '\bin\zlib.dll');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\zlib.lib');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\libz.dll');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\libz.lib');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\z.lib');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\z.dll');
}
}