Merge branch 'main' into frankenphp/mbed

This commit is contained in:
Marc
2025-11-07 10:51:55 +01:00
committed by GitHub
37 changed files with 1252 additions and 131 deletions

View File

@@ -34,7 +34,7 @@ use Symfony\Component\Console\Application;
*/
final class ConsoleApplication extends Application
{
public const string VERSION = '2.7.5';
public const string VERSION = '2.7.6';
public function __construct()
{

View File

@@ -220,7 +220,7 @@ class Extension
*/
public function patchBeforeSharedMake(): bool
{
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], array_map(fn ($l) => $l->getName(), $this->builder->getLibs()));
$config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') ? '-l:libstdc++.a' : null;
$lstdcpp ??= str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : '';
@@ -486,18 +486,46 @@ class Extension
return $this->build_static;
}
/**
* Get the library dependencies that current extension depends on.
*
* @param bool $recursive Whether it includes dependencies recursively
*/
public function getLibraryDependencies(bool $recursive = false): array
{
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
if (!$recursive) {
return $ret;
}
$deps = [];
$added = 1;
while ($added !== 0) {
$added = 0;
foreach ($ret as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!array_key_exists($depdepName, $deps)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!array_key_exists($depName, $deps)) {
$deps[$depName] = $dep;
}
}
}
return $deps;
}
/**
* Returns the environment variables a shared extension needs to be built.
* CFLAGS, CXXFLAGS, LDFLAGS and so on.
*/
protected function getSharedExtensionEnv(): array
{
$config = (new SPCConfigUtil($this->builder))->config(
[$this->getName()],
array_map(fn ($l) => $l->getName(), $this->getLibraryDependencies(recursive: true)),
$this->builder->getOption('with-suggested-exts'),
$this->builder->getOption('with-suggested-libs'),
);
$config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
@@ -519,7 +547,7 @@ class Extension
}
logger()->info("enabling {$this->name} without library {$name}");
} else {
$this->dependencies[] = $depLib;
$this->dependencies[$name] = $depLib;
}
}
@@ -532,7 +560,7 @@ class Extension
}
logger()->info("enabling {$this->name} without extension {$name}");
} else {
$this->dependencies[] = $depExt;
$this->dependencies[$name] = $depExt;
}
}
@@ -567,37 +595,4 @@ class Extension
}
return [trim($staticLibString), trim($sharedLibString)];
}
private function getLibraryDependencies(bool $recursive = false): array
{
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
if (!$recursive) {
return $ret;
}
$deps = [];
$added = 1;
while ($added !== 0) {
$added = 0;
foreach ($ret as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!array_key_exists($depdepName, $deps)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!array_key_exists($depName, $deps)) {
$deps[$depName] = $dep;
}
}
}
if (array_key_exists(0, $deps)) {
$zero = [0 => $deps[0]];
unset($deps[0]);
return $zero + $deps;
}
return $deps;
}
}

View File

@@ -43,7 +43,7 @@ class grpc extends Extension
public function patchBeforeConfigure(): bool
{
$util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]);
$config = $util->config(['grpc']);
$config = $util->getExtensionConfig($this);
$libs = $config['libs'];
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs);
return true;

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\SPCConfigUtil;
#[CustomExt('rdkafka')]
class rdkafka extends Extension
@@ -15,6 +16,7 @@ class rdkafka extends Extension
{
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n");
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"');
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", 'PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,', 'AC_CHECK_LIB([$LIBNAME], [$LIBSYMBOL],');
return true;
}
@@ -37,8 +39,7 @@ class rdkafka extends Extension
public function getUnixConfigureArg(bool $shared = false): string
{
$pkgconf_libs = shell()->execWithResult('pkg-config --libs --static rdkafka')[1];
$pkgconf_libs = trim(implode('', $pkgconf_libs));
return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . ' RDKAFKA_LIBS="' . $pkgconf_libs . '"';
$pkgconf_libs = (new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]))->getExtensionConfig($this);
return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . " RDKAFKA_LIBS=\"{$pkgconf_libs['libs']}\"";
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\PkgConfigUtil;
#[CustomExt('snmp')]
class snmp extends Extension
{
public function patchBeforeBuildconf(): bool
{
// Overwrite m4 config using newer PHP version
if ($this->builder->getPHPVersionID() < 80400) {
FileSystem::copy(ROOT_DIR . '/src/globals/extra/snmp-ext-config-old.m4', "{$this->source_dir}/config.m4");
}
$libs = implode(' ', PkgConfigUtil::getLibsArray('netsnmp'));
FileSystem::replaceFileStr(
"{$this->source_dir}/config.m4",
'PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])',
"SNMP_LIBS=\"{$libs}\"\nPHP_EVAL_LIBLINE([\$SNMP_LIBS], [SNMP_SHARED_LIBADD])"
);
return true;
}
}

View File

@@ -70,7 +70,7 @@ class swoole extends Extension
$arg .= $this->builder->getExt('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd';
$arg .= $this->builder->getExt('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite';
if ($this->builder->getExt('swoole-hook-odbc')) {
$config = (new SPCConfigUtil($this->builder, ['libs_only_deps' => true]))->config([], ['unixodbc']);
$config = (new SPCConfigUtil($this->builder))->getLibraryConfig($this->builder->getLib('unixodbc'));
$arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"';
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class net_snmp extends LinuxLibraryBase
{
use \SPC\builder\unix\library\net_snmp;
public const NAME = 'net-snmp';
}

View File

@@ -78,7 +78,7 @@ class openssl extends LinuxLibraryBase
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
}
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class net_snmp extends MacOSLibraryBase
{
use \SPC\builder\unix\library\net_snmp;
public const NAME = 'net-snmp';
}

View File

@@ -63,7 +63,7 @@ class openssl extends MacOSLibraryBase
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
}
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
}
}

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\executor\UnixCMakeExecutor;
trait librdkafka
{
@@ -26,34 +26,18 @@ trait librdkafka
protected function build(): void
{
UnixAutoconfExecutor::create($this)
->appendEnv(['CFLAGS' => '-Wno-int-conversion -Wno-unused-but-set-variable -Wno-unused-variable'])
->optionalLib(
'zstd',
function ($lib) {
putenv("STATIC_LIB_libzstd={$lib->getLibDir()}/libzstd.a");
return '';
},
'--disable-zstd'
UnixCMakeExecutor::create($this)
->optionalLib('zstd', ...cmake_boolean_args('WITH_ZSTD'))
->optionalLib('curl', ...cmake_boolean_args('WITH_CURL'))
->optionalLib('openssl', ...cmake_boolean_args('WITH_SSL'))
->optionalLib('zlib', ...cmake_boolean_args('WITH_ZLIB'))
->optionalLib('liblz4', ...cmake_boolean_args('ENABLE_LZ4_EXT'))
->addConfigureArgs(
'-DWITH_SASL=OFF',
'-DRDKAFKA_BUILD_STATIC=ON',
'-DRDKAFKA_BUILD_EXAMPLES=OFF',
'-DRDKAFKA_BUILD_TESTS=OFF',
)
->removeConfigureArgs(
'--with-pic',
'--enable-pic',
)
->configure(
'--disable-curl',
'--disable-sasl',
'--disable-valgrind',
'--disable-zlib',
'--disable-ssl',
)
->make();
$this->patchPkgconfPrefix(['rdkafka.pc', 'rdkafka-static.pc', 'rdkafka++.pc', 'rdkafka++-static.pc']);
// remove dynamic libs
shell()
->exec("rm -rf {$this->getLibDir()}/*.so.*")
->exec("rm -rf {$this->getLibDir()}/*.so")
->exec("rm -rf {$this->getLibDir()}/*.dylib");
->build();
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait net_snmp
{
public function patchBeforeBuild(): bool
{
if (PHP_OS_FAMILY === 'Linux') {
FileSystem::replaceFileStr("{$this->source_dir}/configure", 'LIBS="-lssl ${OPENSSL_LIBS}"', 'LIBS="-lssl ${OPENSSL_LIBS} -lpthread -ldl"');
return true;
}
return false;
}
protected function build(): void
{
// use --static for PKG_CONFIG
UnixAutoconfExecutor::create($this)
->setEnv(['PKG_CONFIG' => getenv('PKG_CONFIG') . ' --static'])
->configure(
'--disable-mibs',
'--without-nl',
'--disable-agent',
'--disable-applications',
'--disable-manuals',
'--disable-scripts',
'--disable-embedded-perl',
'--without-perl-modules',
'--with-out-mib-modules="if-mib host disman/event-mib ucd-snmp/diskio mibII"',
'--with-out-transports="Unix"',
'--with-mib-modules=""',
'--enable-mini-agent',
'--with-default-snmp-version="3"',
'--with-sys-contact="@@no.where"',
'--with-sys-location="Unknown"',
'--with-logfile="/var/log/snmpd.log"',
'--with-persistent-directory="/var/lib/net-snmp"',
'--with-openssl=' . BUILD_ROOT_PATH,
'--with-zlib=' . BUILD_ROOT_PATH,
)->make(with_install: 'installheaders installlibs install_pkgconfig');
$this->patchPkgconfPrefix();
}
}

View File

@@ -45,9 +45,9 @@ trait postgresql
protected function build(): void
{
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies());
$spc = new SPCConfigUtil($this->getBuilder(), ['no_php' => true, 'libs_only_deps' => true]);
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs'));
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies(true));
$spc = new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]);
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false));
$env_vars = [
'CFLAGS' => $config['cflags'],

View File

@@ -57,6 +57,7 @@ class WindowsBuilder extends BuilderBase
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
SourcePatcher::patchBeforeBuildconf($this);
@@ -102,13 +103,13 @@ class WindowsBuilder extends BuilderBase
->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" .
'--disable-all ' .
'--disable-cgi ' .
'--with-php-build=' . BUILD_ROOT_PATH . ' ' .
'--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' .
'--with-extra-libs=' . BUILD_LIB_PATH . ' ' .
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') .
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableMicro ? ('--enable-micro ' . $micro_logo . $micro_w32) : '--disable-micro ') .
($enableEmbed ? '--enable-embed ' : '--disable-embed ') .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_scan_dir .
$opcache_jit_arg .
"{$this->makeStaticExtensionArgs()} " .
@@ -127,6 +128,10 @@ class WindowsBuilder extends BuilderBase
if ($enableFpm) {
logger()->warning('Windows does not support fpm SAPI, I will skip it.');
}
if ($enableCgi) {
logger()->info('building cgi');
$this->buildCgi();
}
if ($enableMicro) {
logger()->info('building micro');
$this->buildMicro();
@@ -159,6 +164,20 @@ class WindowsBuilder extends BuilderBase
$this->deployBinary(BUILD_TARGET_CLI);
}
public function buildCgi(): void
{
SourcePatcher::patchWindowsCGITarget();
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// add nmake wrapper
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cgi_wrapper.bat', "nmake /nologo LIBS_CGI=\"ws2_32.lib kernel32.lib advapi32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cgi_wrapper.bat --task-args php-cgi.exe");
$this->deployBinary(BUILD_TARGET_CGI);
}
public function buildEmbed(): void
{
// TODO: add embed support for windows
@@ -265,7 +284,7 @@ class WindowsBuilder extends BuilderBase
// sanity check for php-cli
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('running cli sanity check');
[$ret, $output] = cmd()->execWithResult(BUILD_ROOT_PATH . '\bin\php.exe -n -r "echo \"hello\";"');
[$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php.exe -n -r "echo \"hello\";"');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check');
}
@@ -284,7 +303,7 @@ class WindowsBuilder extends BuilderBase
if (file_exists($test_file)) {
@unlink($test_file);
}
file_put_contents($test_file, file_get_contents(BUILD_ROOT_PATH . '\bin\micro.sfx') . $task['content']);
file_put_contents($test_file, file_get_contents(BUILD_BIN_PATH . '\micro.sfx') . $task['content']);
chmod($test_file, 0755);
[$ret, $out] = cmd()->execWithResult($test_file);
foreach ($task['conditions'] as $condition => $closure) {
@@ -298,6 +317,17 @@ class WindowsBuilder extends BuilderBase
}
}
}
// sanity check for php-cgi
if (($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
logger()->info('running cgi sanity check');
FileSystem::writeFile(SOURCE_PATH . '\php-cgi-test.php', '<?php echo "<h1>Hello, World!</h1>"; ?>');
[$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php-cgi.exe -n -f ' . SOURCE_PATH . '\php-cgi-test.php');
$raw_output = implode("\n", $output);
if ($ret !== 0 || !str_contains($raw_output, 'Hello, World!')) {
throw new ValidationException("cgi failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cgi sanity check');
}
}
}
/**
@@ -311,20 +341,21 @@ class WindowsBuilder extends BuilderBase
$src = match ($type) {
BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe",
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx",
BUILD_TARGET_CGI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php-cgi.exe",
default => throw new SPCInternalException("Deployment does not accept type {$type}"),
};
// with-upx-pack for cli and micro
if ($this->getOption('with-upx-pack', false)) {
if ($type === BUILD_TARGET_CLI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
if ($type === BUILD_TARGET_CLI || $type === BUILD_TARGET_CGI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($src));
}
}
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
FileSystem::createDir(BUILD_ROOT_PATH . '\bin');
FileSystem::createDir(BUILD_BIN_PATH);
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '\bin\\'));
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_BIN_PATH . '\\'));
return true;
}

View File

@@ -33,7 +33,7 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
$this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
$this->addOption('build-cgi', null, null, 'Build cgi SAPI (not available on Windows)');
$this->addOption('build-cgi', null, null, 'Build cgi SAPI');
$this->addOption('build-all', null, null, 'Build all SAPI');
$this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');

View File

@@ -40,7 +40,7 @@ class LinuxToolCheckList
'tar', 'unzip', 'gzip', 'gcc', 'g++',
'bzip2', 'cmake', 'patch', 'which',
'xz', 'libtool', 'gettext-devel',
'patchelf',
'patchelf', 'file',
];
public const TOOLS_ARCH = [

View File

@@ -159,7 +159,9 @@ class ExceptionHandler
public static function handleDefaultException(\Throwable $e): void
{
$class = get_class($e);
self::logError("✗ Unhandled exception {$class}:\n\t{$e->getMessage()}\n");
$file = $e->getFile();
$line = $e->getLine();
self::logError("✗ Unhandled exception {$class} on {$file} line {$line}:\n\t{$e->getMessage()}\n");
self::logError('Stack trace:');
self::logError(ConsoleColor::gray($e->getTraceAsString()) . PHP_EOL, 4);
self::logError('⚠ Please report this exception to: https://github.com/crazywhalecc/static-php-cli/issues');

View File

@@ -549,6 +549,39 @@ class SourcePatcher
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
}
/**
* Patch cgi SAPI Makefile for Windows.
*/
public static function patchWindowsCGITarget(): void
{
// search Makefile code line contains "$(BUILD_DIR)\php-cgi.exe:"
$content = FileSystem::readFile(SOURCE_PATH . '/php-src/Makefile');
$lines = explode("\r\n", $content);
$line_num = 0;
$found = false;
foreach ($lines as $v) {
if (str_contains($v, '$(BUILD_DIR)\php-cgi.exe:')) {
$found = $line_num;
break;
}
++$line_num;
}
if ($found === false) {
throw new PatchException('Windows Makefile patching for php-cgi.exe target', 'Cannot patch windows CGI Makefile, Makefile does not contain "$(BUILD_DIR)\php-cgi.exe:" line');
}
// cli: $(BUILD_DIR)\php.exe: $(DEPS_CLI) $(CLI_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest
// $lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(CLI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest';
// cgi: $(BUILD_DIR)\php-cgi.exe: $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest
$lines[$line_num] = '$(BUILD_DIR)\php-cgi.exe: $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest';
// cli: @"$(LINK)" /nologo $(CGI_GLOBAL_OBJS_RESP) $(BUILD_DIR)\$(PHPLIB) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI)
$lines[$line_num + 1] = "\t" . '@"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CGI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
// Patch cgi-static, comment ZEND_TSRMLS_CACHE_DEFINE()
FileSystem::replaceFileRegex(SOURCE_PATH . '\php-src\sapi\cgi\cgi_main.c', '/^ZEND_TSRMLS_CACHE_DEFINE\(\)/m', '// ZEND_TSRMLS_CACHE_DEFINE()');
}
public static function patchPhpLibxml212(): bool
{
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');

View File

@@ -7,6 +7,7 @@ namespace SPC\util;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
use SPC\builder\Extension;
use SPC\builder\LibraryBase;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use Symfony\Component\Console\Input\ArgvInput;
@@ -53,6 +54,9 @@ class SPCConfigUtil
*/
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
{
logger()->debug('config extensions: ' . implode(',', $extensions));
logger()->debug('config libs: ' . implode(',', $libraries));
logger()->debug('config suggest for [ext, lib]: ' . ($include_suggest_ext ? 'true' : 'false') . ',' . ($include_suggest_lib ? 'true' : 'false'));
$extra_exts = [];
foreach ($extensions as $ext) {
$extra_exts = array_merge($extra_exts, Config::getExt($ext, 'ext-suggests', []));
@@ -124,6 +128,63 @@ class SPCConfigUtil
];
}
/**
* [Helper function]
* Get configuration for a specific extension(s) dependencies.
*
* @param Extension|Extension[] $extension Extension instance or list
* @param bool $include_suggest_ext Whether to include suggested extensions
* @param bool $include_suggest_lib Whether to include suggested libraries
* @return array{
* cflags: string,
* ldflags: string,
* libs: string
* }
*/
public function getExtensionConfig(array|Extension $extension, bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
{
if (!is_array($extension)) {
$extension = [$extension];
}
$libs = array_map(fn ($y) => $y->getName(), array_merge(...array_map(fn ($x) => $x->getLibraryDependencies(true), $extension)));
return $this->config(
extensions: array_map(fn ($x) => $x->getName(), $extension),
libraries: $libs,
include_suggest_ext: $include_suggest_ext ?: $this->builder?->getOption('with-suggested-exts') ?? false,
include_suggest_lib: $include_suggest_lib ?: $this->builder?->getOption('with-suggested-libs') ?? false,
);
}
/**
* [Helper function]
* Get configuration for a specific library(s) dependencies.
*
* @param LibraryBase|LibraryBase[] $lib Library instance or list
* @param bool $include_suggest_lib Whether to include suggested libraries
* @return array{
* cflags: string,
* ldflags: string,
* libs: string
* }
*/
public function getLibraryConfig(array|LibraryBase $lib, bool $include_suggest_lib = false): array
{
if (!is_array($lib)) {
$lib = [$lib];
}
$save_no_php = $this->no_php;
$this->no_php = true;
$save_libs_only_deps = $this->libs_only_deps;
$this->libs_only_deps = true;
$ret = $this->config(
libraries: array_map(fn ($x) => $x->getName(), $lib),
include_suggest_lib: $include_suggest_lib ?: $this->builder?->getOption('with-suggested-libs') ?? false,
);
$this->no_php = $save_no_php;
$this->libs_only_deps = $save_libs_only_deps;
return $ret;
}
private function hasCpp(array $extensions, array $libraries): bool
{
// judge cpp-extension

View File

@@ -115,6 +115,12 @@ class UnixAutoconfExecutor extends Executor
return $this;
}
public function setEnv(array $env): static
{
$this->shell->setEnv($env);
return $this;
}
public function appendEnv(array $env): static
{
$this->shell->appendEnv($env);

View File

@@ -0,0 +1,92 @@
PHP_ARG_WITH([snmp],
[for SNMP support],
[AS_HELP_STRING([[--with-snmp[=DIR]]],
[Include SNMP support. Use PKG_CONFIG_PATH (or SNMP_CFLAGS and SNMP_LIBS)
environment variables, or alternatively the optional DIR argument to
customize where to look for the net-snmp-config utility of the NET-SNMP
library.])])
if test "$PHP_SNMP" != "no"; then
snmp_found=no
AS_VAR_IF([PHP_SNMP], [yes],
[PKG_CHECK_MODULES([SNMP], [netsnmp >= 5.3], [snmp_found=yes], [:])])
AS_VAR_IF([snmp_found], [no], [
AS_VAR_IF([PHP_SNMP], [yes],
[AC_PATH_PROG([SNMP_CONFIG], [net-snmp-config],, [/usr/local/bin:$PATH])],
[SNMP_CONFIG="$PHP_SNMP/bin/net-snmp-config"])
AS_IF([test ! -x "$SNMP_CONFIG"],
[AC_MSG_ERROR(m4_text_wrap([
Could not find net-snmp-config binary. Please check your net-snmp
installation.
]))])
snmp_version=$($SNMP_CONFIG --version)
AS_VERSION_COMPARE([$snmp_version], [5.3],
[AC_MSG_ERROR(m4_text_wrap([
Net-SNMP version 5.3 or greater required (detected $snmp_version).
]))])
SNMP_PREFIX=$($SNMP_CONFIG --prefix)
SNMP_CFLAGS="-I${SNMP_PREFIX}/include"
SNMP_LIBS=$($SNMP_CONFIG --netsnmp-libs)
SNMP_LIBS="$SNMP_LIBS $($SNMP_CONFIG --external-libs)"
AS_IF([test -z "$SNMP_LIBS" || test -z "$SNMP_PREFIX"],
[AC_MSG_ERROR(m4_text_wrap([
Could not find the required paths. Please check your net-snmp
installation.
]))])
])
PHP_EVAL_INCLINE([$SNMP_CFLAGS])
PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])
SNMP_LIBNAME=netsnmp
dnl Test build.
PHP_CHECK_LIBRARY([$SNMP_LIBNAME], [init_snmp],
[AC_DEFINE([HAVE_SNMP], [1],
[Define to 1 if the PHP extension 'snmp' is available.])],
[AC_MSG_FAILURE([SNMP sanity check failed.])],
[$SNMP_SHARED_LIBADD])
dnl Check whether shutdown_snmp_logging() exists.
PHP_CHECK_LIBRARY([$SNMP_LIBNAME], [shutdown_snmp_logging],
[AC_DEFINE([HAVE_SHUTDOWN_SNMP_LOGGING], [1],
[Define to 1 if SNMP library has the 'shutdown_snmp_logging' function.])],
[],
[$SNMP_SHARED_LIBADD])
CFLAGS_SAVE=$CFLAGS
LIBS_SAVE=$LIBS
CFLAGS="$CFLAGS $SNMP_CFLAGS"
LIBS="$LIBS $SNMP_LIBS"
AC_CHECK_DECL([usmHMAC192SHA256AuthProtocol],
[AC_DEFINE([HAVE_SNMP_SHA256], [1],
[Define to 1 if SNMP library has the 'usmHMAC192SHA256AuthProtocol'
array.])],
[],
[
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
])
AC_CHECK_DECL([usmHMAC384SHA512AuthProtocol],
[AC_DEFINE([HAVE_SNMP_SHA512], [1],
[Define to 1 if SNMP library has the 'usmHMAC384SHA512AuthProtocol'
array.])],
[],
[
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
])
CFLAGS=$CFLAGS_SAVE
LIBS=$LIBS_SAVE
PHP_NEW_EXTENSION([snmp], [snmp.c], [$ext_shared])
PHP_ADD_EXTENSION_DEP(snmp, spl)
PHP_SUBST([SNMP_SHARED_LIBADD])
fi

View File

@@ -13,11 +13,11 @@ declare(strict_types=1);
// test php version (8.1 ~ 8.4 available, multiple for matrix)
$test_php_version = [
'8.1',
// '8.1',
// '8.2',
// '8.3',
// '8.4',
'8.5',
'8.4',
// '8.5',
// 'git',
];
@@ -30,11 +30,12 @@ $test_os = [
'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
'ubuntu-24.04-arm', // bin/spc for arm64
// 'windows-latest', // .\bin\spc.ps1
// 'windows-2022', // .\bin\spc.ps1
// 'windows-2025',
];
// whether enable thread safe
$zts = false;
$zts = true;
$no_strip = false;
@@ -49,7 +50,7 @@ $prefer_pre_built = false;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'gettext',
'Linux', 'Darwin' => 'rdkafka',
'Windows' => 'bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pdo,pdo_mysql,pdo_sqlite,phar,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip',
};
@@ -208,7 +209,7 @@ switch ($argv[1] ?? null) {
passthru($prefix . $down_cmd, $retcode);
break;
case 'build_cmd':
passthru($prefix . $build_cmd . ' --build-cli --build-micro', $retcode);
passthru($prefix . $build_cmd . ' --build-cli --build-micro --build-cgi', $retcode);
break;
case 'build_embed_cmd':
if ($frankenphp) {