Merge branch 'main' into php-85

# Conflicts:
#	src/SPC/builder/linux/LinuxBuilder.php
#	src/SPC/store/source/PhpSource.php
#	src/globals/test-extensions.php
This commit is contained in:
crazywhalecc
2025-07-31 23:46:34 +08:00
88 changed files with 1129 additions and 678 deletions

View File

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

View File

@@ -264,6 +264,7 @@ abstract class BuilderBase
}
}
file_put_contents(BUILD_BIN_PATH . '/php-config', implode('', $lines));
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', 'test "[$]$1" = "no" && $1=yes', '# test "[$]$1" = "no" && $1=yes');
FileSystem::createDir(BUILD_MODULES_PATH);
try {
foreach ($this->getExts() as $ext) {
@@ -277,6 +278,7 @@ abstract class BuilderBase
throw $e;
}
FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line);
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', '# test "[$]$1" = "no" && $1=yes', 'test "[$]$1" = "no" && $1=yes');
}
/**

View File

@@ -9,7 +9,10 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
class Extension
{
@@ -187,6 +190,14 @@ class Extension
*/
public function patchBeforeMake(): bool
{
if (SPCTarget::getTargetOS() === 'Linux' && $this->isBuildShared() && ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS'))) {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/Makefile',
"/^(shared_objects_{$this->getName()}\\s*=.*)$/m",
"$1 {$objs}",
);
return true;
}
return false;
}
@@ -217,7 +228,21 @@ class Extension
*/
public function patchBeforeSharedMake(): bool
{
return false;
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], array_map(fn ($l) => $l->getName(), $this->builder->getLibs()));
[$staticLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
'$1 ' . trim($staticLibs)
);
if ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS')) {
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
"/^(shared_objects_{$this->getName()}\\s*=.*)$/m",
"$1 {$objs}",
);
}
return true;
}
/**
@@ -251,7 +276,7 @@ class Extension
$ret = '';
foreach ($order as $ext) {
if ($ext instanceof Extension && $ext->isBuildShared()) {
if ($ext instanceof self && $ext->isBuildShared()) {
if (Config::getExt($ext->getName(), 'zend-extension', false) === true) {
$ret .= " -d \"zend_extension={$ext->getName()}\"";
} else {
@@ -294,9 +319,7 @@ class Extension
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' -r "' . trim($test) . '"');
if ($ret !== 0) {
if ($this->builder->getOption('debug')) {
var_dump($out);
}
var_dump($out);
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check');
}
}
@@ -384,20 +407,25 @@ class Extension
*/
public function buildUnixShared(): void
{
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
[$staticLibString, $sharedLibString] = $this->getStaticAndSharedLibs();
// macOS ld64 doesn't understand these, while Linux and BSD do
// use them to make sure that all symbols are picked up, even if a library has already been visited before
$preStatic = PHP_OS_FAMILY !== 'Darwin' ? '-Wl,-Bstatic -Wl,--start-group ' : '';
$postStatic = PHP_OS_FAMILY !== 'Darwin' ? ' -Wl,--end-group -Wl,-Bdynamic ' : ' ';
$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'),
);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
$env = [
'CFLAGS' => $config['cflags'],
'CXXFLAGS' => $config['cflags'],
'LDFLAGS' => $config['ldflags'],
'LIBS' => $preStatic . $staticLibString . $postStatic . $sharedLibString,
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && SPCTarget::getTargetOS() === 'Linux') {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
}
if ($this->patchBeforeSharedPhpize()) {
logger()->info("Extension [{$this->getName()}] patched before shared phpize");
@@ -406,6 +434,7 @@ class Extension
// prepare configure args
shell()->cd($this->source_dir)
->setEnv($env)
->appendEnv($this->getExtraEnv())
->exec(BUILD_BIN_PATH . '/phpize');
if ($this->patchBeforeSharedConfigure()) {
@@ -414,25 +443,20 @@ class Extension
shell()->cd($this->source_dir)
->setEnv($env)
->appendEnv($this->getExtraEnv())
->exec(
'./configure ' . $this->getUnixConfigureArg(true) .
' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' .
'--enable-shared --disable-static'
);
// some extensions don't define their dependencies well, this patch is only needed for a few
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
'$1 ' . $staticLibString
);
if ($this->patchBeforeSharedMake()) {
logger()->info("Extension [{$this->getName()}] patched before shared make");
}
shell()->cd($this->source_dir)
->setEnv($env)
->appendEnv($this->getExtraEnv())
->exec('make clean')
->exec('make -j' . $this->builder->concurrency)
->exec('make install');
@@ -506,38 +530,35 @@ class Extension
}
}
protected function getExtraEnv(): array
{
return [];
}
/**
* Get required static and shared libraries as a pair of strings in format -l{libname} -l{libname2}
* Splits a given string of library flags into static and shared libraries.
*
* @return array [staticLibString, sharedLibString]
* @param string $allLibs A space-separated string of library flags (e.g., -lxyz).
* @return array an array containing two elements: the first is a space-separated string
* of static library flags, and the second is a space-separated string
* of shared library flags
*/
protected function getStaticAndSharedLibs(): array
protected function splitLibsIntoStaticAndShared(string $allLibs): array
{
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
$sharedLibString = '';
$staticLibString = '';
$staticLibs = $this->getLibFilesString();
$staticLibs = str_replace([BUILD_LIB_PATH . '/lib', '.a'], ['-l', ''], $staticLibs);
$staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']);
foreach ($staticLibs as $lib) {
$lib = trim($lib);
if ($lib === '') {
continue;
$sharedLibString = '';
$libs = explode(' ', $allLibs);
foreach ($libs as $lib) {
$staticLib = BUILD_LIB_PATH . '/lib' . str_replace('-l', '', $lib) . '.a';
if (str_starts_with($lib, BUILD_LIB_PATH . '/lib') && str_ends_with($lib, '.a')) {
$staticLib = $lib;
}
$static_lib = 'lib' . $lib . '.a';
if (file_exists(BUILD_LIB_PATH . '/' . $static_lib) && !str_contains($static_lib, 'libphp')) {
if (!str_contains($staticLibString, '-l' . $lib . ' ')) {
$staticLibString .= '-l' . $lib . ' ';
}
} elseif (!str_contains($sharedLibString, '-l' . $lib . ' ')) {
$sharedLibString .= '-l' . $lib . ' ';
if ($lib === '-lphp' || !file_exists($staticLib)) {
$sharedLibString .= " {$lib}";
} else {
$staticLibString .= " {$lib}";
}
}
// move -lstdc++ to static libraries because centos 7 the shared libstdc++ is incomplete
if (str_contains((string) getenv('PATH'), 'rh/devtoolset-10')) {
$staticLibString .= ' -lstdc++';
$sharedLibString = str_replace('-lstdc++', '', $sharedLibString);
}
return [trim($staticLibString), trim($sharedLibString)];
}

View File

@@ -13,6 +13,7 @@ class amqp extends Extension
{
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
if (PHP_OS_FAMILY === 'Windows') {
FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp.h', '/^#warning.*/m', '');
FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp_framing.h', '/^#warning.*/m', '');
@@ -20,7 +21,7 @@ class amqp extends Extension
FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp_tcp_socket.h', '/^#warning.*/m', '');
return true;
}
return false;
return $patched;
}
public function getUnixConfigureArg(bool $shared = false): string

View File

@@ -60,13 +60,14 @@ class curl extends Extension
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
$extra_libs = getenv('SPC_EXTRA_LIBS');
if ($this->builder instanceof WindowsBuilder && !str_contains($extra_libs, 'secur32.lib')) {
$extra_libs .= ' secur32.lib';
putenv('SPC_EXTRA_LIBS=' . trim($extra_libs));
return true;
}
return false;
return $patched;
}
public function patchBeforeSharedConfigure(): bool

View File

@@ -41,10 +41,12 @@ class event extends Extension
*/
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
// 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;
}
return true;
return $patched;
}
}

View File

@@ -50,6 +50,7 @@ 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

@@ -15,4 +15,14 @@ class imagick extends Extension
$disable_omp = ' ac_cv_func_omp_pause_resource_all=no';
return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp;
}
protected function splitLibsIntoStaticAndShared(string $allLibs): array
{
[$static, $shared] = parent::splitLibsIntoStaticAndShared($allLibs);
if (str_contains(getenv('PATH'), 'rh/devtoolset-10')) {
$static .= ' -l:libstdc++.a';
$shared = str_replace('-lstdc++', '', $shared);
}
return [clean_spaces($static), clean_spaces($shared)];
}
}

View File

@@ -45,8 +45,9 @@ class imap extends Extension
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::isMuslDist()) {
return false;
return $patched;
}
$extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lcrypt');
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);

View File

@@ -12,6 +12,7 @@ class openssl extends Extension
{
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
// patch openssl3 with php8.0 bug
if ($this->builder->getPHPVersionID() < 80100) {
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
@@ -20,7 +21,7 @@ class openssl extends Extension
return true;
}
return false;
return $patched;
}
public function getUnixConfigureArg(bool $shared = false): string

View File

@@ -34,6 +34,7 @@ class opentelemetry 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

@@ -23,4 +23,23 @@ class password_argon2 extends Extension
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check');
}
}
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
if ($this->builder->getLib('libsodium') !== null) {
$extraLibs = getenv('SPC_EXTRA_LIBS');
if ($extraLibs !== false) {
$extraLibs = str_replace(
[BUILD_LIB_PATH . '/libargon2.a', BUILD_LIB_PATH . '/libsodium.a'],
['', BUILD_LIB_PATH . '/libargon2.a ' . BUILD_LIB_PATH . '/libsodium.a'],
$extraLibs,
);
$extraLibs = trim(preg_replace('/\s+/', ' ', $extraLibs)); // normalize spacing
f_putenv('SPC_EXTRA_LIBS=' . $extraLibs);
return true;
}
}
return $patched;
}
}

View File

@@ -57,4 +57,11 @@ class pgsql extends Extension
}
return '--with-pgsql=' . BUILD_ROOT_PATH;
}
protected function getExtraEnv(): array
{
return [
'CFLAGS' => '-Wno-int-conversion',
];
}
}

View File

@@ -20,6 +20,7 @@ class rdkafka extends Extension
public function patchBeforeMake(): bool
{
parent::patchBeforeMake();
// when compiling rdkafka with inline builds, it shows some errors, I don't know why.
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/rdkafka/rdkafka.c',

View File

@@ -27,7 +27,12 @@ class readline extends Extension
public function getUnixConfigureArg(bool $shared = false): string
{
return '--without-libedit --with-readline=' . BUILD_ROOT_PATH;
$enable = '--without-libedit --with-readline=' . BUILD_ROOT_PATH;
if ($this->builder->getPHPVersionID() < 84000) {
// the check uses `char rl_pending_input()` instead of `extern int rl_pending_input`, which makes LTO fail
$enable .= ' ac_cv_lib_readline_rl_pending_input=yes';
}
return $enable;
}
public function buildUnixShared(): void

View File

@@ -13,8 +13,13 @@ class redis extends Extension
public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--enable-redis';
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';
$arg .= $this->builder->getExt('igbinary') ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
if ($this->isBuildStatic()) {
$arg .= $this->builder->getExt('session')?->isBuildStatic() ? ' --enable-redis-session' : ' --disable-redis-session';
$arg .= $this->builder->getExt('igbinary')?->isBuildStatic() ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
} else {
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';
$arg .= $this->builder->getExt('igbinary') ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
}
if ($this->builder->getLib('zstd')) {
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
}

View File

@@ -14,13 +14,18 @@ class swoole extends Extension
{
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
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';
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/swoole/thirdparty/php/standard/proc_open.cc', 'include <util.h>', 'include "' . $util_path . '"');
FileSystem::replaceFileStr(
"{$this->source_dir}/thirdparty/php/standard/proc_open.cc",
'include <util.h>',
'include "' . $util_path . '"',
);
return true;
}
return false;
return $patched;
}
public function getExtVersion(): ?string

View File

@@ -20,6 +20,7 @@ class uv extends Extension
public function patchBeforeSharedMake(): bool
{
parent::patchBeforeSharedMake();
if (PHP_OS_FAMILY !== 'Linux' || arch2gnu(php_uname('m')) !== 'aarch64') {
return false;
}

View File

@@ -27,16 +27,17 @@ class xlswriter extends Extension
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
if (PHP_OS_FAMILY === 'Windows') {
// fix windows build with openssl extension duplicate symbol bug
SourcePatcher::patchFile('spc_fix_xlswriter_win32.patch', $this->source_dir);
$content = file_get_contents($this->source_dir . '/library/libxlsxwriter/src/theme.c');
$bom = pack('CCC', 0xEF, 0xBB, 0xBF);
if (substr($content, 0, 3) !== $bom) {
if (!str_starts_with($content, $bom)) {
file_put_contents($this->source_dir . '/library/libxlsxwriter/src/theme.c', $bom . $content);
}
return true;
}
return false;
return $patched;
}
}

View File

@@ -198,7 +198,7 @@ class BSDBuilder extends UnixBuilderBase
->exec("make -j{$this->concurrency} {$vars} micro");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx');
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-unneeded micro.sfx');
}
$this->deployBinary(BUILD_TARGET_MICRO);

View File

@@ -31,17 +31,11 @@ class LinuxBuilder extends UnixBuilderBase
GlobalEnvManager::afterInit();
// concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY'));
$this->concurrency = (int) getenv('SPC_CONCURRENCY');
// cflags
$this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS');
$this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS');
// cross-compiling is not supported yet
/*if (php_uname('m') !== $this->arch) {
$this->cross_compile_prefix = SystemUtil::getCrossCompilePrefix($this->cc, $this->arch);
logger()->info('using cross compile prefix: ' . $this->cross_compile_prefix);
$this->configure_env .= " CROSS_COMPILE='{$this->cross_compile_prefix}'";
}*/
$this->arch_ld_flags = getenv('SPC_DEFAULT_LD_FLAGS');
// create pkgconfig and include dir (some libs cannot create them automatically)
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
@@ -79,7 +73,10 @@ class LinuxBuilder extends UnixBuilderBase
$maxExecutionTimers = '';
$zts = '';
}
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
if (!$disable_jit && $this->getExt('opcache')) {
f_putenv('SPC_COMPILER_EXTRA=-fno-sanitize=undefined');
}
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
@@ -91,27 +88,25 @@ class LinuxBuilder extends UnixBuilderBase
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
// prepare build php envs
$envs_build_php = SystemUtil::makeEnvVarString([
$php_configure_env = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
// 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions!
]);
// process micro upx patch if micro sapi enabled
if ($enableMicro) {
if (version_compare($this->getMicroVersion(), '0.2.0') < 0) {
// for phpmicro 0.1.x
$this->processMicroUPXLegacy();
}
// micro latest needs do strip and upx pack later (strip, upx, cut binary manually supported)
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
if ($embed_type !== 'static' && SPCTarget::isStatic()) {
throw new WrongUsageException(
'Linux does not support loading shared libraries when linking libc statically. ' .
'Change SPC_CMD_VAR_PHP_EMBED_TYPE to static.'
);
}
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
$php_configure_env . ' ' .
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
@@ -119,11 +114,11 @@ class LinuxBuilder extends UnixBuilderBase
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$disable_jit .
$json_74 .
$zts .
$maxExecutionTimers .
$this->makeStaticExtensionArgs() .
' ' . $envs_build_php . ' '
$this->makeStaticExtensionArgs() . ' '
);
$this->emitPatchPoint('before-php-make');
@@ -176,12 +171,12 @@ class LinuxBuilder extends UnixBuilderBase
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-unneeded php');
}
if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
->exec('strip --strip-all php')
->exec(getenv('UPX_EXEC') . ' --best php');
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php');
}
$this->deployBinary(BUILD_TARGET_CLI);
@@ -239,12 +234,12 @@ class LinuxBuilder extends UnixBuilderBase
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} fpm");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-unneeded php-fpm');
}
if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
->exec('strip --strip-all php-fpm')
->exec(getenv('UPX_EXEC') . ' --best php-fpm');
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm');
}
$this->deployBinary(BUILD_TARGET_FPM);
}
@@ -264,34 +259,79 @@ class LinuxBuilder extends UnixBuilderBase
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
$libDir = BUILD_LIB_PATH;
$modulesDir = BUILD_MODULES_PATH;
$libphpSo = "{$libDir}/libphp.so";
$realLibName = 'libphp.so';
$cwd = getcwd();
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
$release = $matches[1];
$realLibName = 'libphp-' . $release . '.so';
$realLib = BUILD_LIB_PATH . '/' . $realLibName;
rename(BUILD_LIB_PATH . '/libphp.so', $realLib);
$cwd = getcwd();
chdir(BUILD_LIB_PATH);
symlink($realLibName, 'libphp.so');
chdir(BUILD_MODULES_PATH);
foreach ($this->getExts() as $ext) {
if (!$ext->isBuildShared()) {
continue;
$realLibName = "libphp-{$release}.so";
$libphpRelease = "{$libDir}/{$realLibName}";
if (!file_exists($libphpRelease) && file_exists($libphpSo)) {
rename($libphpSo, $libphpRelease);
}
if (file_exists($libphpRelease)) {
chdir($libDir);
if (file_exists($libphpSo)) {
unlink($libphpSo);
}
$name = $ext->getName();
$versioned = "{$name}-{$release}.so";
$unversioned = "{$name}.so";
if (is_file(BUILD_MODULES_PATH . "/{$versioned}")) {
rename(BUILD_MODULES_PATH . "/{$versioned}", BUILD_MODULES_PATH . "/{$unversioned}");
shell()->cd(BUILD_MODULES_PATH)
->exec(sprintf(
symlink($realLibName, 'libphp.so');
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg($realLibName),
escapeshellarg($libphpRelease)
));
}
if (is_dir($modulesDir)) {
chdir($modulesDir);
foreach ($this->getExts() as $ext) {
if (!$ext->isBuildShared()) {
continue;
}
$name = $ext->getName();
$versioned = "{$name}-{$release}.so";
$unversioned = "{$name}.so";
$src = "{$modulesDir}/{$versioned}";
$dst = "{$modulesDir}/{$unversioned}";
if (is_file($src)) {
rename($src, $dst);
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg($unversioned),
escapeshellarg($unversioned)
escapeshellarg($dst)
));
}
}
}
chdir($cwd);
}
$target = "{$libDir}/{$realLibName}";
if (file_exists($target)) {
[, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target));
$output = join("\n", $output);
if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) {
$currentSoname = $sonameMatch[1];
if ($currentSoname !== basename($target)) {
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg(basename($target)),
escapeshellarg($target)
));
}
}
}
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
$AR = getenv('AR') ?: 'ar';
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
}
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {
shell()->cd(BUILD_LIB_PATH)->exec("strip --strip-unneeded {$realLibName}");
}
$this->patchPhpScripts();
}
@@ -303,58 +343,22 @@ class LinuxBuilder extends UnixBuilderBase
return [
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LIBS' => $config['libs'],
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie",
];
}
/**
* Apply option --no-strip and --with-upx-pack for micro sapi (only for phpmicro 0.1.x)
* Strip micro.sfx for Linux.
* The micro.sfx does not support UPX directly, but we can remove UPX-info segment to adapt.
* This will also make micro.sfx with upx-packed more like a malware fore antivirus :(
*
* @throws FileSystemException
* @throws RuntimeException
*/
private function processMicroUPXLegacy(): void
{
// upx pack and strip for micro
// but always restore Makefile.frag.bak first
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.bak')) {
copy(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.bak', SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag');
}
if ($this->getOption('with-upx-pack', false)) {
// judge $(MAKE) micro_2s_objs SFX_FILESIZE=`$(STAT_SIZE) $(SAPI_MICRO_PATH)` count
// if 2, replace src/globals/extra/micro-triple-Makefile.frag file content
if (substr_count(FileSystem::readFile(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag'), '$(MAKE) micro_2s_objs SFX_FILESIZE=`$(STAT_SIZE) $(SAPI_MICRO_PATH)`') === 2) {
// bak first
copy(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag', SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.bak');
// replace Makefile.frag content
FileSystem::writeFile(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag', FileSystem::readFile(ROOT_DIR . '/src/globals/extra/micro-triple-Makefile.frag'));
}
// with upx pack always need strip
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH) && ' . getenv('UPX_EXEC') . ' --best \$(SAPI_MICRO_PATH)',
);
} elseif (!$this->getOption('no-strip', false)) {
// not-no-strip means strip (default behavior)
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH)',
);
} else {
// just no strip
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=true',
);
}
}
private function processMicroUPX(): void
{
if (version_compare($this->getMicroVersion(), '0.2.0') >= 0 && !$this->getOption('no-strip', false)) {
shell()->exec('strip --strip-all ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
shell()->exec('strip --strip-unneeded ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
if ($this->getOption('with-upx-pack')) {
// strip first

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace SPC\builder\linux;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
class SystemUtil
{
@@ -75,37 +74,6 @@ class SystemUtil
return $ncpu;
}
/**
* @throws RuntimeException
*/
public static function getCCType(string $cc): string
{
return match (true) {
str_ends_with($cc, 'c++'), str_ends_with($cc, 'cc'), str_ends_with($cc, 'g++'), str_ends_with($cc, 'gcc') => 'gcc',
$cc === 'clang++', $cc === 'clang', str_starts_with($cc, 'musl-clang') => 'clang',
default => throw new RuntimeException("unknown cc type: {$cc}"),
};
}
/**
* @throws RuntimeException
* @noinspection PhpUnused
*/
public static function getCrossCompilePrefix(string $cc, string $arch): string
{
return match (static::getCCType($cc)) {
// guessing clang toolchains
'clang' => match ($arch) {
'x86_64' => 'x86_64-linux-gnu-',
'arm64', 'aarch64' => 'aarch64-linux-gnu-',
default => throw new RuntimeException('unsupported arch: ' . $arch),
},
// remove gcc postfix
'gcc' => str_replace('-cc', '', str_replace('-gcc', '', $cc)) . '-',
default => throw new RuntimeException('unsupported cc'),
};
}
public static function findStaticLib(string $name): ?array
{
$paths = getenv('LIBPATH');
@@ -188,7 +156,7 @@ class SystemUtil
/**
* Get libc version string from ldd
*/
public static function getLibcVersionIfExists(string $libc): ?string
public static function getLibcVersionIfExists(?string $libc = null): ?string
{
if (self::$libc_version !== null) {
return self::$libc_version;
@@ -211,8 +179,11 @@ class SystemUtil
if ($libc === 'musl') {
if (self::isMuslDist()) {
$result = shell()->execWithResult('ldd 2>&1', false);
} else {
} elseif (is_file('/usr/local/musl/lib/libc.so')) {
$result = shell()->execWithResult('/usr/local/musl/lib/libc.so 2>&1', false);
} else {
$arch = php_uname('m');
$result = shell()->execWithResult("/lib/ld-musl-{$arch}.so.1 2>&1", false);
}
// Match Version * line
// match ldd version: "Version 1.2.3" match 1.2.3

View File

@@ -18,14 +18,8 @@ class libffi extends LinuxLibraryBase
*/
public function build(): void
{
$arch = getenv('SPC_ARCH');
UnixAutoconfExecutor::create($this)
->configure(
"--host={$arch}-unknown-linux",
"--target={$arch}-unknown-linux",
"--libdir={$this->getLibDir()}"
)
->make();
->configure()->make();
if (is_file(BUILD_ROOT_PATH . '/lib64/libffi.a')) {
copy(BUILD_ROOT_PATH . '/lib64/libffi.a', BUILD_ROOT_PATH . '/lib/libffi.a');

View File

@@ -12,6 +12,8 @@ class libmemcached extends LinuxLibraryBase
public function build(): void
{
UnixCMakeExecutor::create($this)->build();
UnixCMakeExecutor::create($this)
->addConfigureArgs('-DCMAKE_INSTALL_RPATH=""')
->build();
}
}

View File

@@ -21,7 +21,6 @@ declare(strict_types=1);
namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
@@ -62,8 +61,6 @@ class openssl extends LinuxLibraryBase
$ex_lib = trim($ex_lib);
$clang_postfix = SystemUtil::getCCType(getenv('CC')) === 'clang' ? '-clang' : '';
shell()->cd($this->source_dir)->initializeEnv($this)
->exec(
"{$env} ./Configure no-shared {$extra} " .
@@ -71,8 +68,9 @@ class openssl extends LinuxLibraryBase
'--libdir=' . BUILD_LIB_PATH . ' ' .
'--openssldir=/etc/ssl ' .
"{$zlib_extra}" .
'enable-pie ' .
'no-legacy ' .
"linux-{$arch}{$clang_postfix}"
"linux-{$arch}"
)
->exec('make clean')
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")

View File

@@ -38,6 +38,7 @@ class MacOSBuilder extends UnixBuilderBase
// cflags
$this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS');
$this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS');
$this->arch_ld_flags = getenv('SPC_DEFAULT_LD_FLAGS');
// create pkgconfig and include dir (some libs cannot create them automatically)
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
@@ -113,12 +114,10 @@ class MacOSBuilder extends UnixBuilderBase
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
// prepare build php envs
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
$envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
]);
if ($this->getLib('postgresql')) {
@@ -270,8 +269,8 @@ class MacOSBuilder extends UnixBuilderBase
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
shell()->cd(SOURCE_PATH . '/php-src')
->exec('ar -t ' . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 ar d " . BUILD_LIB_PATH . '/libphp.a');
$AR = getenv('AR') ?: 'ar';
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
}
$this->patchPhpScripts();
}

View File

@@ -92,27 +92,31 @@ trait UnixLibraryTrait
{
$env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: '';
if (!str_contains($env, $this->builder->arch_c_flags)) {
$env .= $this->builder->arch_c_flags;
$env .= ' ' . $this->builder->arch_c_flags;
}
return $env;
}
public function getLibExtraLdFlags(): string
{
return getenv($this->getSnakeCaseName() . '_LDFLAGS') ?: '';
}
public function getLibExtraLibs(): string
{
return getenv($this->getSnakeCaseName() . '_LIBS') ?: '';
return trim($env);
}
public function getLibExtraCXXFlags(): string
{
$env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: '';
if (!str_contains($env, $this->builder->arch_cxx_flags)) {
$env .= $this->builder->arch_cxx_flags;
$env .= ' ' . $this->builder->arch_cxx_flags;
}
return $env;
return trim($env);
}
public function getLibExtraLdFlags(): string
{
$env = getenv($this->getSnakeCaseName() . '_LDFLAGS') ?: '';
if (!str_contains($env, $this->builder->arch_ld_flags)) {
$env .= ' ' . $this->builder->arch_ld_flags;
}
return trim($env);
}
public function getLibExtraLibs(): string
{
return getenv($this->getSnakeCaseName() . '_LIBS') ?: '';
}
}

View File

@@ -12,7 +12,9 @@ use SPC\store\Config;
use SPC\store\CurlHook;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\pkg\GoXcaddy;
use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
@@ -24,6 +26,9 @@ abstract class UnixBuilderBase extends BuilderBase
/** @var string C++ flags */
public string $arch_cxx_flags;
/** @var string LD flags */
public string $arch_ld_flags;
public function proveLibs(array $sorted_libraries): void
{
// search all supported libs
@@ -122,11 +127,7 @@ abstract class UnixBuilderBase extends BuilderBase
if (SPCTarget::isStatic()) {
$lens .= ' -static';
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
}
// if someone changed to --enable-embed=shared, we need to add LD_LIBRARY_PATH
// if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
if (PHP_OS_FAMILY === 'Darwin') {
$ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH ';
@@ -211,7 +212,6 @@ abstract class UnixBuilderBase extends BuilderBase
logger()->debug('Patching phpize prefix');
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'");
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#');
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', 'test "[$]$1" = "no" && $1=yes', '# test "[$]$1" = "no" && $1=yes');
}
// patch php-config
if (file_exists(BUILD_BIN_PATH . '/php-config')) {
@@ -237,18 +237,6 @@ abstract class UnixBuilderBase extends BuilderBase
*/
protected function buildFrankenphp(): void
{
$os = match (PHP_OS_FAMILY) {
'Linux' => 'linux',
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => throw new RuntimeException('Unsupported OS: ' . PHP_OS_FAMILY),
};
$arch = arch2gnu(php_uname('m'));
// define executables for go and xcaddy
$xcaddy_exec = PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}/bin/xcaddy";
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
@@ -261,30 +249,30 @@ abstract class UnixBuilderBase extends BuilderBase
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
}
$lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : '';
$releaseInfo = json_decode(Downloader::curlExec('https://api.github.com/repos/php/frankenphp/releases/latest', retries: 3, hooks: [[CurlHook::class, 'setupGithubToken']]), true);
$releaseInfo = json_decode(Downloader::curlExec(
'https://api.github.com/repos/php/frankenphp/releases/latest',
hooks: [[CurlHook::class, 'setupGithubToken']],
), true);
$frankenPhpVersion = $releaseInfo['tag_name'];
$libphpVersion = $this->getPHPVersion();
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion);
}
$debugFlags = $this->getOption('no-strip') ? "'-w -s' " : '';
$debugFlags = $this->getOption('no-strip') ? '-w -s ' : '';
$extLdFlags = "-extldflags '-pie'";
$muslTags = '';
$staticFlags = '';
if (SPCTarget::isStatic()) {
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
$muslTags = 'static_build,';
$staticFlags = '-static-pie';
}
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list, with_dependencies: true);
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list);
$env = [
'PATH' => PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}/bin:" . getenv('PATH'),
'GOROOT' => PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}",
'GOBIN' => PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}/bin",
'GOPATH' => PKG_ROOT_PATH . '/go',
'CGO_ENABLED' => '1',
'CGO_CFLAGS' => $config['cflags'],
'CGO_LDFLAGS' => "{$config['ldflags']} {$config['libs']} {$lrt}",
'CGO_LDFLAGS' => "{$staticFlags} {$config['ldflags']} {$config['libs']} {$lrt}",
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
@@ -292,8 +280,23 @@ abstract class UnixBuilderBase extends BuilderBase
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
foreach (GoXcaddy::getEnvironment() as $key => $value) {
if ($key === 'PATH') {
GlobalEnvManager::addPathIfNotExists($value);
} else {
$env[$key] = $value;
}
}
shell()->cd(BUILD_BIN_PATH)
->setEnv($env)
->exec("{$xcaddy_exec} build --output frankenphp {$xcaddyModules}");
->exec("xcaddy build --output frankenphp {$xcaddyModules}");
if (!$this->getOption('no-strip', false) && file_exists(BUILD_BIN_PATH . '/frankenphp')) {
if (PHP_OS_FAMILY === 'Linux') {
shell()->cd(BUILD_BIN_PATH)->exec('strip --strip-unneeded frankenphp');
} else { // macOS doesn't understand strip-unneeded
shell()->cd(BUILD_BIN_PATH)->exec('strip -S frankenphp');
}
}
}
}

View File

@@ -15,6 +15,9 @@ trait attr
protected function build(): void
{
UnixAutoconfExecutor::create($this)
->appendEnv([
'CFLAGS' => '-Wno-int-conversion -Wno-implicit-function-declaration',
])
->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls')

View File

@@ -31,7 +31,7 @@ trait gettext
$autoconf->addConfigureArgs('--disable-threads');
}
$autoconf->configure()->make(with_clean: true);
$autoconf->configure()->make();
$this->patchLaDependencyPrefix();
}
}

View File

@@ -33,6 +33,7 @@ trait imagemagick
->optionalLib('freetype', ...ac_with_args('freetype'))
->optionalLib('bzip2', ...ac_with_args('bzlib'))
->optionalLib('libjxl', ...ac_with_args('jxl'))
->optionalLib('jbig', ...ac_with_args('jbig'))
->addConfigureArgs(
'--disable-openmp',
'--without-x',

View File

@@ -27,8 +27,10 @@ trait ldap
'--disable-slapd',
'--without-systemd',
'--without-cyrus-sasl',
'ac_cv_func_pthread_kill_other_threads_np=no'
)
->appendEnv([
'CFLAGS' => '-Wno-date-time',
'LDFLAGS' => "-L{$this->getLibDir()}",
'CPPFLAGS' => "-I{$this->getIncludeDir()}",
])

View File

@@ -4,22 +4,24 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\executor\UnixCMakeExecutor;
trait libaom
{
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
$extra = getenv('SPC_COMPILER_EXTRA');
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$new = trim($extra . ' -D_GNU_SOURCE');
f_putenv("SPC_COMPILER_EXTRA={$new}");
}
UnixCMakeExecutor::create($this)
->setBuildDir("{$this->source_dir}/builddir")
->addConfigureArgs('-DAOM_TARGET_CPU=generic')
->build();
f_putenv("SPC_COMPILER_EXTRA={$extra}");
$this->patchPkgconfPrefix(['aom.pc']);
}
}

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget;
@@ -11,18 +13,31 @@ trait libjxl
{
protected function build(): void
{
UnixCMakeExecutor::create($this)
->addConfigureArgs('-DJPEGXL_ENABLE_TOOLS=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_EXAMPLES=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_MANPAGES=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_BENCHMARK=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_PLUGINS=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_SJPOEG=ON')
->addConfigureArgs('-DJPEGXL_ENABLE_JNI=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_TRANSCODE_JPEG=ON')
->addConfigureArgs('-DJPEGXL_STATIC=' . (SPCTarget::isStatic() ? 'ON' : 'OFF'))
->addConfigureArgs('-DJPEGXL_FORCE_SYSTEM_BROTLI=ON')
->addConfigureArgs('-DBUILD_TESTING=OFF')
->build();
$cmake = UnixCMakeExecutor::create($this)
->addConfigureArgs(
'-DJPEGXL_ENABLE_TOOLS=OFF',
'-DJPEGXL_ENABLE_EXAMPLES=OFF',
'-DJPEGXL_ENABLE_MANPAGES=OFF',
'-DJPEGXL_ENABLE_BENCHMARK=OFF',
'-DJPEGXL_ENABLE_PLUGINS=OFF',
'-DJPEGXL_ENABLE_SJPEG=ON',
'-DJPEGXL_ENABLE_JNI=OFF',
'-DJPEGXL_ENABLE_TRANSCODE_JPEG=ON',
'-DJPEGXL_STATIC=' . (SPCTarget::isStatic() ? 'ON' : 'OFF'),
'-DJPEGXL_FORCE_SYSTEM_BROTLI=ON',
'-DBUILD_TESTING=OFF'
);
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$cmake->addConfigureArgs(
'-DCXX_MAVX512F_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512DQ_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512CD_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512BW_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512VL_SUPPORTED:BOOL=FALSE'
);
}
$cmake->build();
}
}

View File

@@ -19,7 +19,11 @@ trait liblz4
{
shell()->cd($this->source_dir)->initializeEnv($this)
->exec("make PREFIX='' clean")
->exec("make -j{$this->builder->concurrency} PREFIX=''")
->exec("make lib -j{$this->builder->concurrency} PREFIX=''");
FileSystem::replaceFileStr($this->source_dir . '/Makefile', '$(MAKE) -C $(PRGDIR) $@', '');
shell()->cd($this->source_dir)
->exec("make install PREFIX='' DESTDIR=" . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['liblz4.pc']);

View File

@@ -6,10 +6,26 @@ namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait librdkafka
{
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileStr(
$this->source_dir . '/lds-gen.py',
"funcs.append('rd_ut_coverage_check')",
''
);
FileSystem::replaceFileStr(
$this->source_dir . '/src/rd.h',
'#error "IOV_MAX not defined"',
"#define IOV_MAX 1024\n#define __GNU__"
);
return true;
}
/**
* @throws FileSystemException
* @throws RuntimeException
@@ -17,6 +33,7 @@ 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) {

View File

@@ -35,18 +35,19 @@ trait libxslt
'--without-debugger',
"--with-libxml-prefix={$this->getBuildRootPath()}",
);
if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) {
if (getenv('SPC_LD_LIBRARY_PATH') && getenv('SPC_LIBRARY_PATH')) {
$ac->appendEnv([
'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'),
'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'),
'LD_LIBRARY_PATH' => getenv('SPC_LD_LIBRARY_PATH'),
'LIBRARY_PATH' => getenv('SPC_LIBRARY_PATH'),
]);
}
$ac->configure()->make();
$this->patchPkgconfPrefix(['libexslt.pc', 'libxslt.pc']);
$this->patchLaDependencyPrefix();
$AR = getenv('AR') ?: 'ar';
shell()->cd(BUILD_LIB_PATH)
->exec("ar -t libxslt.a | grep '\\.a$' | xargs -n1 ar d libxslt.a")
->exec("ar -t libexslt.a | grep '\\.a$' | xargs -n1 ar d libexslt.a");
->exec("{$AR} -t libxslt.a | grep '\\.a$' | xargs -n1 {$AR} d libxslt.a")
->exec("{$AR} -t libexslt.a | grep '\\.a$' | xargs -n1 {$AR} d libexslt.a");
}
}

View File

@@ -14,6 +14,7 @@ trait mimalloc
$cmake = UnixCMakeExecutor::create($this)
->addConfigureArgs(
'-DMI_BUILD_SHARED=OFF',
'-DMI_BUILD_OBJECT=OFF',
'-DMI_INSTALL_TOPLEVEL=ON',
);
if (SPCTarget::getLibc() === 'musl') {

View File

@@ -6,6 +6,7 @@ namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait ncurses
{
@@ -14,6 +15,9 @@ trait ncurses
$filelist = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true);
UnixAutoconfExecutor::create($this)
->appendEnv([
'LDFLAGS' => SPCTarget::isStatic() ? '-static' : '',
])
->configure(
'--enable-overwrite',
'--with-curses-h',

View File

@@ -48,6 +48,7 @@ trait ngtcp2
// on macOS, the static library may contain other static libraries?
// ld: archive member 'libssl.a' not a mach-o file in libngtcp2_crypto_ossl.a
shell()->cd(BUILD_LIB_PATH)->exec("ar -t libngtcp2_crypto_ossl.a | grep '\\.a$' | xargs -n1 ar d libngtcp2_crypto_ossl.a");
$AR = getenv('AR') ?: 'ar';
shell()->cd(BUILD_LIB_PATH)->exec("{$AR} -t libngtcp2_crypto_ossl.a | grep '\\.a$' | xargs -n1 {$AR} d libngtcp2_crypto_ossl.a");
}
}

View File

@@ -13,7 +13,7 @@ trait pkgconfig
{
UnixAutoconfExecutor::create($this)
->appendEnv([
'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '',
'CFLAGS' => '-Wimplicit-function-declaration -Wno-int-conversion',
'LDFLAGS' => SPCTarget::isStatic() ? '--static' : '',
])
->configure(

View File

@@ -16,9 +16,13 @@ trait watcher
*/
protected function build(): void
{
$cflags = $this->getLibExtraCXXFlags();
if (stripos($cflags, '-fpic') === false) {
$cflags .= ' -fPIC';
}
$ldflags = $this->getLibExtraLdFlags() ? ' ' . $this->getLibExtraLdFlags() : '';
shell()->cd($this->source_dir . '/watcher-c')
->initializeEnv($this)
->exec(getenv('CC') . ' -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC')
->exec(getenv('CXX') . " -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra {$cflags}{$ldflags}")
->exec(getenv('AR') . ' rcs libwatcher-c.a libwatcher-c.o');
copy($this->source_dir . '/watcher-c/libwatcher-c.a', BUILD_LIB_PATH . '/libwatcher-c.a');

View File

@@ -45,7 +45,7 @@ class WindowsBuilder extends BuilderBase
$this->zts = $this->getOption('enable-zts', false);
// set concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY'));
$this->concurrency = (int) getenv('SPC_CONCURRENCY');
// make cmake toolchain
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile();

View File

@@ -65,7 +65,7 @@ class BuildPHPCommand extends BuildCommand
// check dynamic extension build env
// linux must build with glibc
if (!empty($shared_extensions) && SPCTarget::isStatic()) {
$this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with shared target!');
$this->output->writeln('Linux does not support dynamic extension loading with fully static builds, please build with a shared C runtime target!');
return static::FAILURE;
}
$static_and_shared = array_intersect($static_extensions, $shared_extensions);

View File

@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace SPC\command;
use SPC\exception\ValidationException;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\ConfigValidator;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Process\Process;
@@ -71,7 +73,16 @@ class CraftCommand extends BuildCommand
$retcode = $this->runCommand('install-pkg', 'go-xcaddy');
if ($retcode !== 0) {
$this->output->writeln('<error>craft go-xcaddy failed</error>');
$this->log("craft go-xcaddy failed with code: {$retcode}", true);
$this->log("craft: spc install-pkg go-xcaddy failed with code: {$retcode}", true);
return static::FAILURE;
}
}
// install zig if requested
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$retcode = $this->runCommand('install-pkg', 'zig');
if ($retcode !== 0) {
$this->output->writeln('<error>craft zig failed</error>');
$this->log("craft: spc install-pkg zig failed with code: {$retcode}", true);
return static::FAILURE;
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\doctor\item;
use SPC\builder\linux\SystemUtil;
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult;
@@ -17,13 +18,15 @@ use SPC\store\FileSystem;
use SPC\store\PackageManager;
use SPC\store\SourcePatcher;
use SPC\toolchain\MuslToolchain;
use SPC\toolchain\ZigToolchain;
#[OptionalCheck([self::class, 'optionalCheck'])]
class LinuxMuslCheck
{
public static function optionalCheck(): bool
{
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class;
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class ||
(getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !SystemUtil::isMuslDist());
}
/** @noinspection PhpUnused */
@@ -31,7 +34,7 @@ class LinuxMuslCheck
public function checkMusl(): CheckResult
{
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) {
if (file_exists($musl_wrapper_lib) && (file_exists('/usr/local/musl/lib/libc.a') || getenv('SPC_TOOLCHAIN') === ZigToolchain::class)) {
return CheckResult::ok();
}
return CheckResult::fail('musl-wrapper is not installed on your system', 'fix-musl-wrapper');
@@ -40,6 +43,9 @@ class LinuxMuslCheck
#[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)]
public function checkMuslCrossMake(): CheckResult
{
if (getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !SystemUtil::isMuslDist()) {
return CheckResult::ok();
}
$arch = arch2gnu(php_uname('m'));
$cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a";
$cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc";

View File

@@ -69,7 +69,7 @@ class LinuxToolCheckList
};
$missing = [];
foreach ($required as $package) {
if ($this->findCommand(self::PROVIDED_COMMAND[$package] ?? $package) === null) {
if (self::findCommand(self::PROVIDED_COMMAND[$package] ?? $package) === null) {
$missing[] = $package;
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace SPC\doctor\item;
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult;
use SPC\doctor\OptionalCheck;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\PackageManager;
use SPC\store\pkg\Zig;
use SPC\toolchain\ZigToolchain;
#[OptionalCheck([self::class, 'optionalCheck'])]
class ZigCheck
{
public static function optionalCheck(): bool
{
return getenv('SPC_TOOLCHAIN') === ZigToolchain::class;
}
/** @noinspection PhpUnused */
#[AsCheckItem('if zig is installed', level: 800)]
public function checkZig(): CheckResult
{
if (Zig::isInstalled()) {
return CheckResult::ok();
}
return CheckResult::fail('zig is not installed', 'install-zig');
}
/** @noinspection PhpUnused */
/**
* @throws FileSystemException
* @throws WrongUsageException
*/
#[AsFixItem('install-zig')]
public function installZig(): bool
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
PackageManager::installPackage("zig-{$arch}-{$os}");
return Zig::isInstalled();
}
}

View File

@@ -389,8 +389,8 @@ class Downloader
$cls = new $class();
if (in_array($name, $cls->getSupportName())) {
(new $class())->fetch($name, $force, $pkg);
break;
}
break;
}
}
break;

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace SPC\store;
use SPC\builder\BuilderBase;
use SPC\builder\linux\LinuxBuilder;
use SPC\builder\linux\SystemUtil;
use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\FileSystemException;
@@ -17,21 +16,21 @@ class SourcePatcher
{
public static function init(): void
{
// FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']);
FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchGDWin32']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchFfiCentos7FixO3strncmp']);
FileSystem::addSourceExtractHook('sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('pdo_sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('yaml', [SourcePatcher::class, 'patchYamlWin32']);
FileSystem::addSourceExtractHook('libyaml', [SourcePatcher::class, 'patchLibYaml']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchImapLicense']);
FileSystem::addSourceExtractHook('ext-imagick', [SourcePatcher::class, 'patchImagickWith84']);
FileSystem::addSourceExtractHook('libaom', [SourcePatcher::class, 'patchLibaomForAlpine']);
FileSystem::addSourceExtractHook('attr', [SourcePatcher::class, 'patchAttrForAlpine']);
FileSystem::addSourceExtractHook('gmssl', [SourcePatcher::class, 'patchGMSSL']);
// FileSystem::addSourceExtractHook('swow', [__CLASS__, 'patchSwow']);
FileSystem::addSourceExtractHook('openssl', [__CLASS__, 'patchOpenssl11Darwin']);
FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchGDWin32']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']);
FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('yaml', [__CLASS__, 'patchYamlWin32']);
FileSystem::addSourceExtractHook('libyaml', [__CLASS__, 'patchLibYaml']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']);
FileSystem::addSourceExtractHook('ext-imagick', [__CLASS__, 'patchImagickWith84']);
FileSystem::addSourceExtractHook('libaom', [__CLASS__, 'patchLibaomForAlpine']);
FileSystem::addSourceExtractHook('attr', [__CLASS__, 'patchAttrForAlpine']);
FileSystem::addSourceExtractHook('gmssl', [__CLASS__, 'patchGMSSL']);
}
/**
@@ -73,9 +72,9 @@ class SourcePatcher
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
if ($builder->getOption('enable-micro-win32')) {
SourcePatcher::patchMicroWin32();
self::patchMicroWin32();
} else {
SourcePatcher::unpatchMicroWin32();
self::unpatchMicroWin32();
}
}
@@ -235,6 +234,7 @@ class SourcePatcher
'PHP_ADD_INCLUDE([$ext_srcdir])',
"PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])"
);
// swoole 5.1.3 build fix
// get swoole version first
$file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h';
@@ -248,6 +248,7 @@ class SourcePatcher
if ($version === '5.1.3') {
self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole');
}
self::patchFile('swoole_fix_date_time.patch', SOURCE_PATH . '/php-src/ext/swoole');
return true;
}
@@ -256,14 +257,23 @@ class SourcePatcher
*/
public static function patchBeforeMake(BuilderBase $builder): void
{
// Try to fix debian environment build lack HAVE_STRLCAT problem
if ($builder instanceof LinuxBuilder) {
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', '');
}
if ($builder instanceof UnixBuilderBase) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
}
if (!SPCTarget::isStatic() && SPCTarget::getLibc() === 'musl') {
// we need to patch the symbol to global visibility, otherwise extensions with `initial-exec` TLS model will fail to load
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
);
} else {
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
);
}
// no asan
// if (strpos(file_get_contents(SOURCE_PATH . '/php-src/Makefile'), 'CFLAGS_CLEAN = -g') === false) {
@@ -271,7 +281,7 @@ class SourcePatcher
// }
// call extension patch before make
foreach ($builder->getExts(false) as $ext) {
foreach ($builder->getExts() as $ext) {
if ($ext->patchBeforeMake() === true) {
logger()->info("Extension [{$ext->getName()}] patched before make");
}
@@ -454,7 +464,7 @@ class SourcePatcher
}
$extnum = intval($match[1]);
if ($extnum < 30800) {
SourcePatcher::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
self::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
return true;
}
return false;
@@ -472,14 +482,14 @@ class SourcePatcher
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) {
return false;
}
SourcePatcher::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src');
self::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src');
return true;
}
public static function patchLibaomForAlpine(): bool
{
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) {
SourcePatcher::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom');
self::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom');
return true;
}
return false;
@@ -488,7 +498,7 @@ class SourcePatcher
public static function patchAttrForAlpine(): bool
{
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() || PHP_OS_FAMILY === 'Darwin') {
SourcePatcher::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr');
self::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr');
return true;
}
return false;

View File

@@ -28,14 +28,18 @@ abstract class CustomPackage
*/
abstract public function fetch(string $name, bool $force = false, ?array $config = null): void;
/**
* Get the environment variables this package needs to be usable.
* PATH needs to be appended, rather than replaced.
*/
abstract public static function getEnvironment(): array;
abstract public static function isInstalled(): bool;
/**
* Extract the downloaded package
*
* @param string $name Package name
* @throws \RuntimeException If extraction is not implemented
* @param string $name Package name
*/
public function extract(string $name): void
{
throw new \RuntimeException("Extract method not implemented for package: {$name}");
}
abstract public function extract(string $name): void;
}

View File

@@ -4,12 +4,30 @@ declare(strict_types=1);
namespace SPC\store\pkg;
use SPC\builder\linux\SystemUtil;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\LockFile;
class GoXcaddy extends CustomPackage
{
public static function isInstalled(): bool
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "go-xcaddy-{$arch}-{$os}";
$pkgroot = PKG_ROOT_PATH;
$folder = "{$pkgroot}/{$packageName}";
return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy");
}
public function getSupportName(): array
{
return [
@@ -25,6 +43,9 @@ class GoXcaddy extends CustomPackage
$pkgroot = PKG_ROOT_PATH;
$go_exec = "{$pkgroot}/{$name}/bin/go";
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy";
if ($force) {
FileSystem::removeDir("{$pkgroot}/{$name}");
}
if (file_exists($go_exec) && file_exists($xcaddy_exec)) {
return;
}
@@ -57,18 +78,45 @@ class GoXcaddy extends CustomPackage
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = $lock[$name]['move_path'] === null ? "{$pkgroot}/{$name}" : $lock[$name]['move_path'];
$extract = $lock[$name]['move_path'] ?? "{$pkgroot}/{$name}";
FileSystem::extractPackage($name, $source_type, $filename, $extract);
// install xcaddy
$sanitizedPath = getenv('PATH');
if (PHP_OS_FAMILY === 'Linux' && !SystemUtil::isMuslDist()) {
$sanitizedPath = preg_replace('#(:?/?[^:]*musl[^:]*)#', '', $sanitizedPath);
$sanitizedPath = preg_replace('#^:|:$|::#', ':', $sanitizedPath); // clean up colons
}
// install xcaddy without using musl tools, xcaddy build requires dynamic linking
shell()
->appendEnv([
'PATH' => "{$pkgroot}/{$name}/bin:" . getenv('PATH'),
'PATH' => "{$pkgroot}/{$name}/bin:" . $sanitizedPath,
'GOROOT' => "{$pkgroot}/{$name}",
'GOBIN' => "{$pkgroot}/{$name}/bin",
'GOPATH' => "{$pkgroot}/go",
])
->exec("{$go_exec} install github.com/caddyserver/xcaddy/cmd/xcaddy@latest");
->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
}
public static function getEnvironment(): array
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "go-xcaddy-{$arch}-{$os}";
$pkgroot = PKG_ROOT_PATH;
return [
'PATH' => "{$pkgroot}/{$packageName}/bin",
'GOROOT' => "{$pkgroot}/{$packageName}",
'GOBIN' => "{$pkgroot}/{$packageName}/bin",
'GOPATH' => "{$pkgroot}/go",
];
}
}

185
src/SPC/store/pkg/Zig.php Normal file
View File

@@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace SPC\store\pkg;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\CurlHook;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\LockFile;
class Zig extends CustomPackage
{
public static function isInstalled(): bool
{
$path = self::getPath();
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
foreach ($files as $file) {
if (!file_exists("{$path}/{$file}")) {
return false;
}
}
return true;
}
public function getSupportName(): array
{
return [
'zig-x86_64-linux',
'zig-aarch64-linux',
'zig-x86_64-macos',
'zig-aarch64-macos',
'zig-x86_64-win',
];
}
public function fetch(string $name, bool $force = false, ?array $config = null): void
{
$pkgroot = PKG_ROOT_PATH;
$zig_exec = match (PHP_OS_FAMILY) {
'Windows' => "{$pkgroot}/{$name}/zig.exe",
default => "{$pkgroot}/{$name}/zig",
};
if ($force) {
FileSystem::removeDir("{$pkgroot}/{$name}");
}
if (file_exists($zig_exec)) {
return;
}
$parts = explode('-', $name);
$arch = $parts[1];
$os = $parts[2];
$zig_arch = match ($arch) {
'x86_64', 'aarch64' => $arch,
default => throw new WrongUsageException('Unsupported architecture: ' . $arch),
};
$zig_os = match ($os) {
'linux' => 'linux',
'macos' => 'macos',
'win' => 'windows',
default => throw new WrongUsageException('Unsupported OS: ' . $os),
};
$index_json = json_decode(Downloader::curlExec('https://ziglang.org/download/index.json', hooks: [[CurlHook::class, 'setupGithubToken']]), true);
$latest_version = null;
foreach ($index_json as $version => $data) {
$latest_version = $version;
break;
}
if (!$latest_version) {
throw new RuntimeException('Could not determine latest Zig version');
}
logger()->info("Installing Zig version {$latest_version}");
$platform_key = "{$zig_arch}-{$zig_os}";
if (!isset($index_json[$latest_version][$platform_key])) {
throw new RuntimeException("No download available for {$platform_key} in Zig version {$latest_version}");
}
$download_info = $index_json[$latest_version][$platform_key];
$url = $download_info['tarball'];
$filename = basename($url);
$pkg = [
'type' => 'url',
'url' => $url,
'filename' => $filename,
];
Downloader::downloadPackage($name, $pkg, $force);
}
public function extract(string $name): void
{
$pkgroot = PKG_ROOT_PATH;
$zig_bin_dir = "{$pkgroot}/{$name}";
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
$all_exist = true;
foreach ($files as $file) {
if (!file_exists("{$zig_bin_dir}/{$file}")) {
$all_exist = false;
break;
}
}
if ($all_exist) {
return;
}
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = "{$pkgroot}/{$name}";
FileSystem::extractPackage($name, $source_type, $filename, $extract);
$this->createZigCcScript($zig_bin_dir);
}
public static function getEnvironment(): array
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "zig-{$arch}-{$os}";
$path = PKG_ROOT_PATH . "/{$packageName}";
return [
'PATH' => $path,
];
}
/**
* @throws WrongUsageException
*/
private static function getPath(): string
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "zig-{$arch}-{$os}";
return PKG_ROOT_PATH . "/{$packageName}";
}
private function createZigCcScript(string $bin_dir): void
{
$script_path = __DIR__ . '/../scripts/zig-cc.sh';
$script_content = file_get_contents($script_path);
file_put_contents("{$bin_dir}/zig-cc", $script_content);
chmod("{$bin_dir}/zig-cc", 0755);
$script_content = str_replace('zig cc', 'zig c++', $script_content);
file_put_contents("{$bin_dir}/zig-c++", $script_content);
file_put_contents("{$bin_dir}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@");
file_put_contents("{$bin_dir}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@");
file_put_contents("{$bin_dir}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@");
file_put_contents("{$bin_dir}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@");
chmod("{$bin_dir}/zig-c++", 0755);
chmod("{$bin_dir}/zig-ar", 0755);
chmod("{$bin_dir}/zig-ld.lld", 0755);
chmod("{$bin_dir}/zig-ranlib", 0755);
chmod("{$bin_dir}/zig-objcopy", 0755);
}
}

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../buildroot/include" 2>/dev/null || true)"
PARSED_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
-isystem)
shift
ARG="$1"
shift
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG")
;;
-isystem*)
ARG="${1#-isystem}"
shift
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG")
;;
-march=*|-mcpu=*) # replace -march=x86-64 with -march=x86_64
OPT_NAME="${1%%=*}"
OPT_VALUE="${1#*=}"
OPT_VALUE="${OPT_VALUE//-/_}"
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
shift
;;
*)
PARSED_ARGS+=("$1")
shift
;;
esac
done
[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET=""
if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then
output=$(zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}" 2>&1)
status=$?
if [[ $status -eq 0 ]]; then
echo "$output"
exit 0
fi
if echo "$output" | grep -qE "version '.*' in target triple"; then
filtered_output=$(echo "$output" | grep -vE "version '.*' in target triple")
echo "$filtered_output"
exit 0
fi
fi
exec zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}"

View File

@@ -7,6 +7,7 @@ namespace SPC\store\source;
use JetBrains\PhpStorm\ArrayShape;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\Downloader;
class PhpSource extends CustomSourceBase
@@ -16,6 +17,7 @@ class PhpSource extends CustomSourceBase
/**
* @throws DownloaderException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void
{

View File

@@ -10,6 +10,9 @@ use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
/**
* Toolchain implementation for system clang compiler.
*/
class ClangNativeToolchain implements ToolchainInterface
{
public function initEnv(): void
@@ -20,6 +23,9 @@ class ClangNativeToolchain implements ToolchainInterface
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
}
/**
* @throws WrongUsageException
*/
public function afterInit(): void
{
foreach (['CC', 'CXX', 'AR', 'LD'] as $env) {

View File

@@ -17,7 +17,7 @@ class GccNativeToolchain implements ToolchainInterface
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld.gold');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
}
public function afterInit(): void

View File

@@ -20,8 +20,8 @@ class MuslToolchain implements ToolchainInterface
GlobalEnvManager::addPathIfNotExists('/usr/local/musl/bin');
GlobalEnvManager::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
GlobalEnvManager::putenv("SPC_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
GlobalEnvManager::putenv("SPC_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
}
public function afterInit(): void
@@ -29,7 +29,7 @@ class MuslToolchain implements ToolchainInterface
$arch = getenv('GNU_ARCH');
// append LD_LIBRARY_PATH to $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$ld_library_path = getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH');
$ld_library_path = getenv('SPC_LD_LIBRARY_PATH');
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$configure}");
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {

View File

@@ -7,43 +7,57 @@ namespace SPC\toolchain;
use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCTarget;
class ToolchainManager
{
public const array OS_DEFAULT_TOOLCHAIN = [
'Linux' => MuslToolchain::class, // use musl toolchain by default, after zig pr merged, change this to ZigToolchain::class
'Linux' => ZigToolchain::class,
'Windows' => MSVCToolchain::class,
'Darwin' => ClangNativeToolchain::class,
'BSD' => ClangNativeToolchain::class,
];
public static function getToolchainClass(): string
{
if ($tc = getenv('SPC_TOOLCHAIN')) {
return $tc;
}
$libc = getenv('SPC_LIBC');
if ($libc && !getenv('SPC_TARGET')) {
// trigger_error('Setting SPC_LIBC is deprecated, please use SPC_TARGET instead.', E_USER_DEPRECATED);
return match ($libc) {
'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class,
'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_LIBC must be musl for musl dist.'),
default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc),
};
}
return self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY];
}
/**
* @throws WrongUsageException
*/
public static function initToolchain(): void
{
$libc = getenv('SPC_LIBC');
if ($libc !== false) {
// uncomment this when zig pr is merged
// logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.');
$toolchain = match ($libc) {
'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class,
'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_TARGET must be musl-static or musl for musl dist.'),
default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc),
};
} else {
$toolchain = self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY];
}
$toolchainClass = $toolchain;
$toolchainClass = self::getToolchainClass();
/* @var ToolchainInterface $toolchainClass */
(new $toolchainClass())->initEnv();
GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}");
GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchainClass}");
}
public static function afterInitToolchain(): void
{
if (!getenv('SPC_TOOLCHAIN')) {
throw new WrongUsageException('SPC_TOOLCHAIN not set');
throw new WrongUsageException('SPC_TOOLCHAIN was not properly set. Please contact the developers.');
}
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
if (SPCTarget::getLibc() === 'musl' && !SPCTarget::isStatic() && !file_exists($musl_wrapper_lib)) {
throw new WrongUsageException('You are linking against musl libc dynamically, but musl libc is not installed. Please use `bin/spc doctor` to install it.');
}
if (SPCTarget::getLibc() === 'glibc' && SystemUtil::isMuslDist()) {
throw new WrongUsageException('You are linking against glibc dynamically, which is only supported on glibc distros.');
}
$toolchain = getenv('SPC_TOOLCHAIN');
/* @var ToolchainInterface $toolchain */

View File

@@ -4,9 +4,68 @@ declare(strict_types=1);
namespace SPC\toolchain;
use SPC\exception\WrongUsageException;
use SPC\store\pkg\Zig;
use SPC\util\GlobalEnvManager;
class ZigToolchain implements ToolchainInterface
{
public function initEnv(): void {}
public function initEnv(): void
{
// Set environment variables for zig toolchain
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=zig-cc');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=zig-c++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=zig-ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=zig-ld.lld');
public function afterInit(): void {}
// Generate additional objects needed for zig toolchain
$paths = ['/usr/lib/gcc', '/usr/local/lib/gcc'];
$objects = ['crtbeginS.o', 'crtendS.o'];
$found = [];
foreach ($objects as $obj) {
$located = null;
foreach ($paths as $base) {
$output = shell_exec("find {$base} -name {$obj} 2>/dev/null | grep -v '/32/' | head -n 1");
$line = trim((string) $output);
if ($line !== '') {
$located = $line;
break;
}
}
if ($located) {
$found[] = $located;
}
}
GlobalEnvManager::putenv('SPC_EXTRA_RUNTIME_OBJECTS=' . implode(' ', $found));
}
/**
* @throws WrongUsageException
*/
public function afterInit(): void
{
if (!is_dir(Zig::getEnvironment()['PATH'])) {
throw new WrongUsageException('You are building with zig, but zig is not installed, please install zig first. (You can use `doctor` command to install it)');
}
GlobalEnvManager::addPathIfNotExists(Zig::getEnvironment()['PATH']);
f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: '';
$cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: '';
$extraCflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') ?: '';
$cflags = trim($cflags . ' -Wno-date-time');
$cxxflags = trim($cxxflags . ' -Wno-date-time');
$extraCflags = trim($extraCflags . ' -Wno-date-time');
GlobalEnvManager::putenv("SPC_DEFAULT_C_FLAGS={$cflags}");
GlobalEnvManager::putenv("SPC_DEFAULT_CXX_FLAGS={$cxxflags}");
GlobalEnvManager::putenv("SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS={$extraCflags}");
GlobalEnvManager::putenv('RANLIB=zig-ranlib');
GlobalEnvManager::putenv('OBJCOPY=zig-objcopy');
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
if (!str_contains($extra_libs, '-lunwind')) {
// Add unwind library if not already present
$extra_libs = trim($extra_libs . ' -lunwind');
GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}");
}
}
}

View File

@@ -16,6 +16,8 @@ class GlobalEnvManager
{
private static array $env_cache = [];
private static bool $initialized = false;
public static function getInitializedEnv(): array
{
return self::$env_cache;
@@ -29,6 +31,9 @@ class GlobalEnvManager
*/
public static function init(): void
{
if (self::$initialized) {
return;
}
// Check pre-defined env vars exists
if (getenv('BUILD_ROOT_PATH') === false) {
throw new RuntimeException('You must include src/globals/internal-env.php before using GlobalEnvManager');
@@ -86,6 +91,7 @@ class GlobalEnvManager
self::putenv("{$k}={$v}");
}
}
self::$initialized = true;
}
public static function putenv(string $val): void

View File

@@ -57,7 +57,7 @@ class SPCConfigUtil
* @throws WrongUsageException
* @throws \Throwable
*/
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false, bool $with_dependencies = false): array
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
{
[$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
@@ -73,9 +73,9 @@ class SPCConfigUtil
$libs = $this->getLibsString($libraries, !$this->absolute_libs);
// additional OS-specific libraries (e.g. macOS -lresolv)
$extra_env = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS');
if (is_string($extra_env)) {
$libs .= ' ' . trim($extra_env, '"');
// embed
if ($extra_libs = SPCTarget::getRuntimeLibs()) {
$libs .= " {$extra_libs}";
}
$extra_env = getenv('SPC_EXTRA_LIBS');
if (is_string($extra_env) && !empty($extra_env)) {
@@ -86,14 +86,21 @@ class SPCConfigUtil
$libs .= " {$this->getFrameworksString($extensions)}";
}
if ($this->builder->hasCpp()) {
$libs .= SPCTarget::getTargetOS() === 'Darwin' ? ' -lc++' : ' -lstdc++';
$libcpp = SPCTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++';
if (!str_contains($libs, $libcpp)) {
$libs .= " {$libcpp}";
}
}
if ($this->libs_only_deps) {
// mimalloc must come first
if ($this->builder->getLib('mimalloc') && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
$libs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $libs);
}
return [
'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags),
'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags),
'libs' => trim(getenv('LIBS') . ' ' . $libs),
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
'libs' => clean_spaces(getenv('LIBS') . ' ' . $libs),
];
}
@@ -105,14 +112,14 @@ class SPCConfigUtil
$allLibs = getenv('LIBS') . ' ' . $libs;
// mimalloc must come first
if (str_contains($libs, BUILD_LIB_PATH . '/mimalloc.o')) {
$allLibs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $allLibs);
if ($this->builder->getLib('mimalloc') && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
$allLibs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $allLibs);
}
return [
'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags),
'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags),
'libs' => trim($allLibs),
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
'libs' => clean_spaces($allLibs),
];
}
@@ -179,7 +186,7 @@ class SPCConfigUtil
$lib_names = [...$lib_names, ...$pc_libs];
}
// convert all static-libs to short names
$libs = Config::getLib($library, 'static-libs', []);
$libs = array_reverse(Config::getLib($library, 'static-libs', []));
foreach ($libs as $lib) {
// check file existence
if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) {

View File

@@ -5,7 +5,10 @@ declare(strict_types=1);
namespace SPC\util;
use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException;
use SPC\toolchain\ClangNativeToolchain;
use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\MuslToolchain;
use SPC\toolchain\ToolchainManager;
/**
* SPC build target constants and toolchain initialization.
@@ -13,23 +16,44 @@ use SPC\exception\WrongUsageException;
*/
class SPCTarget
{
public const array LIBC_LIST = [
'musl',
'glibc',
];
public const array LIBC_LIST = ['musl', 'glibc'];
/**
* Returns whether the target is a full-static target.
* Returns whether we link the C runtime in statically.
*/
public static function isStatic(): bool
{
$env = getenv('SPC_TARGET');
$libc = getenv('SPC_LIBC');
if (ToolchainManager::getToolchainClass() === MuslToolchain::class) {
return true;
}
if (ToolchainManager::getToolchainClass() === GccNativeToolchain::class) {
return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist();
}
if (ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) {
return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist();
}
// if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released
if ($libc === 'musl') {
if ($target = getenv('SPC_TARGET')) {
if (str_contains($target, '-macos') || str_contains($target, '-native') && PHP_OS_FAMILY === 'Darwin') {
return false;
}
if (str_contains($target, '-gnu')) {
return false;
}
if (str_contains($target, '-dynamic')) {
return false;
}
if (str_contains($target, '-musl')) {
return true;
}
if (PHP_OS_FAMILY === 'Linux') {
return SystemUtil::isMuslDist();
}
return true;
}
if (getenv('SPC_LIBC') === 'musl') {
return true;
}
// TODO: add zig target parser here
return false;
}
@@ -38,29 +62,45 @@ class SPCTarget
*/
public static function getLibc(): ?string
{
$env = getenv('SPC_TARGET');
if ($target = getenv('SPC_TARGET')) {
if (str_contains($target, '-gnu')) {
return 'glibc';
}
if (str_contains($target, '-musl')) {
return 'musl';
}
if (PHP_OS_FAMILY === 'Linux') {
return SystemUtil::isMuslDist() ? 'musl' : 'glibc';
}
}
$libc = getenv('SPC_LIBC');
if ($libc !== false) {
return $libc;
}
// TODO: zig target parser
if (PHP_OS_FAMILY === 'Linux') {
return SystemUtil::isMuslDist() ? 'musl' : 'glibc';
}
return null;
}
public static function getRuntimeLibs(): string
{
if (PHP_OS_FAMILY === 'Linux') {
return self::getLibc() === 'musl' ? '-ldl -lpthread -lm' : '-ldl -lrt -lpthread -lm -lresolv -lutil';
}
if (PHP_OS_FAMILY === 'Darwin') {
return '-lresolv';
}
return '';
}
/**
* Returns the libc version if set, for other OS, it will always return null.
*/
public static function getLibcVersion(): ?string
{
$env = getenv('SPC_TARGET');
$libc = getenv('SPC_LIBC');
if ($libc !== false) {
// legacy method: get a version from system
return SystemUtil::getLibcVersionIfExists($libc);
}
// TODO: zig target parser
return null;
$libc = self::getLibc();
return SystemUtil::getLibcVersionIfExists($libc);
}
/**
@@ -68,20 +108,16 @@ class SPCTarget
* Currently, we only support native building.
*
* @return 'BSD'|'Darwin'|'Linux'|'Windows'
* @throws WrongUsageException
*/
public static function getTargetOS(): string
{
$target = getenv('SPC_TARGET');
if ($target === false || $target === '') {
return PHP_OS_FAMILY;
}
// TODO: zig target parser like below?
$target = (string) getenv('SPC_TARGET');
return match (true) {
str_contains($target, 'linux') => 'Linux',
str_contains($target, 'macos') => 'Darwin',
str_contains($target, 'windows') => 'Windows',
default => throw new WrongUsageException('Cannot parse target.'),
str_contains($target, '-linux') => 'Linux',
str_contains($target, '-macos') => 'Darwin',
str_contains($target, '-windows') => 'Windows',
str_contains($target, '-native') => PHP_OS_FAMILY,
default => PHP_OS_FAMILY,
};
}
}

View File

@@ -61,9 +61,9 @@ class UnixShell
{
$this->setEnv([
'CFLAGS' => $library->getLibExtraCFlags(),
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
'LDFLAGS' => $library->getLibExtraLdFlags(),
'LIBS' => $library->getLibExtraLibs(),
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
]);
return $this;
}

View File

@@ -135,6 +135,7 @@ class UnixAutoconfExecutor extends Executor
{
$this->shell = shell()->cd($this->library->getSourceDir())->initializeEnv($this->library)->appendEnv([
'CFLAGS' => "-I{$this->library->getIncludeDir()}",
'CXXFLAGS' => "-I{$this->library->getIncludeDir()}",
'LDFLAGS' => "-L{$this->library->getLibDir()}",
]);
}

View File

@@ -8,10 +8,10 @@ int main(int argc,char **argv){
zend_stream_init_filename(&file_handle,"embed.php");
if(php_execute_script(&file_handle) == FAILURE){
if(!php_execute_script(&file_handle)){
php_printf("Failed to execute PHP script.\n");
}
PHP_EMBED_END_BLOCK()
return 0;
}
}

View File

@@ -242,3 +242,14 @@ function get_pack_replace(): array
BUILD_ROOT_PATH => '@build_root_path@',
];
}
/**
* Remove duplicate spaces from a string.
*
* @param string $string Input string that may contain unnecessary spaces (e.g., " -la -lb").
* @return string The trimmed string with only single spaces (e.g., "-la -lb").
*/
function clean_spaces(string $string): string
{
return trim(preg_replace('/\s+/', ' ', $string));
}

View File

@@ -0,0 +1,19 @@
--- a/config.m4
+++ b/config.m4
@@ -426,6 +426,7 @@
AX_CHECK_COMPILE_FLAG(-Wlogical-op-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-op-parentheses")
AX_CHECK_COMPILE_FLAG(-Wloop-analysis, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wloop-analysis")
AX_CHECK_COMPILE_FLAG(-Wuninitialized, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wuninitialized")
+ AX_CHECK_COMPILE_FLAG(-Wno-date-time, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-date-time")
AX_CHECK_COMPILE_FLAG(-Wno-missing-field-initializers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-missing-field-initializers")
AX_CHECK_COMPILE_FLAG(-Wno-sign-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-sign-compare")
AX_CHECK_COMPILE_FLAG(-Wno-unused-const-variable, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-const-variable")
@@ -1307,7 +1308,7 @@
PHP_REQUIRE_CXX()
- CXXFLAGS="$CXXFLAGS -Wall -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations"
+ CXXFLAGS="$CXXFLAGS -Wall -Wno-date-time -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations"
if test "$SW_OS" = "CYGWIN" || test "$SW_OS" = "MINGW"; then
CXXFLAGS="$CXXFLAGS -std=gnu++14"

View File

@@ -73,7 +73,7 @@ $with_libs = match (PHP_OS_FAMILY) {
// You can use `common`, `bulk`, `minimal` or `none`.
// note: combination is only available for *nix platform. Windows must use `none` combination
$base_combination = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'minimal',
'Linux', 'Darwin' => 'none',
'Windows' => 'none',
};
@@ -157,6 +157,9 @@ if ($shared_extensions) {
case 'ubuntu-22.04-arm':
$shared_cmd = ' --build-shared=' . quote2($shared_extensions) . ' ';
break;
case 'ubuntu-24.04':
case 'ubuntu-24.04-arm':
break;
case 'macos-13':
case 'macos-14':
case 'macos-15':