Compare commits

..

62 Commits

Author SHA1 Message Date
Jerry Ma
c5ae719b9c Update src/SPC/builder/unix/library/postgresql.php
Co-authored-by: Marc <m@pyc.ac>
2025-10-14 00:08:40 +08:00
crazywhalecc
7b6e707e24 Remove unnecessary macOS 15 bug CFLAGS from PostgreSQL build configuration 2025-10-14 00:08:40 +08:00
crazywhalecc
0114700dad Fix PostgreSQL build compatibility for aarch64 on glibc 2.17 and update test configurations 2025-10-14 00:08:40 +08:00
Marc
ec9364db69 use latest zip version for building shared (#917) 2025-10-13 11:28:16 +02:00
Marc
033e29985e Merge branch 'main' into feature/shared-exts 2025-10-13 11:20:54 +02:00
Marc
61cba2342d Drop macos-13 from actions runner (#922) 2025-10-13 11:20:37 +02:00
Marc
a3acad4ef3 Update config/source.json 2025-10-13 10:59:31 +02:00
Marc
1e13eb4abe Merge branch 'main' into ci/macos-15-intel 2025-10-13 10:38:48 +02:00
Jerry Ma
1bac06fe3c Remove more macOS 14 case from shared extensions switch
Yeah. Copied by tab.
2025-10-13 08:27:40 +08:00
Marc
762a768969 Merge branch 'main' into feature/shared-exts 2025-10-12 23:23:25 +02:00
Marc
d0a6e3a860 update pgsql version to 18.0, php 8.5 to RC2 (#916) 2025-10-12 22:59:09 +02:00
henderkes
995187d258 extra information for steps 2025-10-12 22:32:27 +02:00
henderkes
5333a04e1c clarification 2025-10-12 22:27:38 +02:00
henderkes
19be5263b5 add error 2025-10-12 22:26:06 +02:00
crazywhalecc
6e79401ab5 Add missing libs for suggested option defined 2025-10-13 00:21:13 +08:00
crazywhalecc
903036f1c0 Bump version 2025-10-13 00:00:31 +08:00
crazywhalecc
6bd3eea0fe Use SPCConfigUtil for postgresql build, refactor patches 2025-10-12 23:54:59 +08:00
crazywhalecc
f28a3cf5a3 Drop macos-13 from actions runner 2025-10-12 22:33:22 +08:00
Marc
c828c2c6e4 Merge branch 'main' into feature/shared-exts 2025-10-12 11:35:57 +02:00
Marc
ae1193ab16 Merge branch 'main' into pgsql-18 2025-10-12 11:28:49 +02:00
Marc
6d6a29368e fix building of shared extensions (grpc, simdjson, soap) (#905) 2025-10-12 11:24:32 +02:00
crazywhalecc
897cb00351 Use failsafe for SPC_EXTRA_PHP_VARS 2025-10-12 16:42:38 +08:00
henderkes
018dfae15e suggestions 2025-10-11 10:38:51 +02:00
henderkes
e437bf2ffe don't test macos 13 2025-10-06 21:05:05 +02:00
henderkes
a49ae05599 use latest zip version for building shared 2025-10-06 21:00:45 +02:00
henderkes
31a4498056 bring back simdjson tests, extension headers do not properly guard for avx512 symbols 2025-09-30 15:07:22 +02:00
henderkes
044275d909 typo 2025-09-30 14:22:38 +02:00
henderkes
c5316f9231 -v3 doesn't have avx512 yet 2025-09-30 14:21:31 +02:00
henderkes
2d1a61d184 explicitly disable avx512 if zig and march not high enough 2025-09-30 14:00:13 +02:00
henderkes
f7744188f4 fix configutil 2025-09-30 00:34:30 +02:00
henderkes
5b6c923eef snappy is a c++ lib 2025-09-30 00:31:20 +02:00
henderkes
b0b031afb2 add docstring 2025-09-30 00:18:32 +02:00
henderkes
fe09d8c03e add docstring 2025-09-30 00:17:53 +02:00
henderkes
c2813d5736 move hasCpp to SPCConfigUtil 2025-09-30 00:12:59 +02:00
Marc
8b2b658ced Merge branch 'main' into fix/grpc-shared 2025-09-29 23:27:58 +02:00
henderkes
e862bacc8a php 8.5 has beta 3 2025-09-29 21:15:56 +02:00
henderkes
cac57dbf79 php 8.5 has beta 3 2025-09-29 21:15:18 +02:00
henderkes
21149f6807 simdjson needs patch 2025-09-23 12:42:29 +02:00
henderkes
93e6dcab1d cflags and cxxflags separately 2025-09-23 12:01:09 +02:00
henderkes
59dedbea2c simdjson requires +evex512 (for zig-cc) 2025-09-23 12:00:15 +02:00
henderkes
8bda4fd31e simdjson requires +evex512 2025-09-23 11:59:30 +02:00
henderkes
8039ea49f9 soap requires libxml and session 2025-09-23 11:56:12 +02:00
henderkes
fc90b4ae23 allow appending pkg_config_path #910 2025-09-22 13:10:48 +02:00
henderkes
e02be69e40 update grpc from 1.68.x to 1.75.x 2025-09-21 21:01:07 +02:00
henderkes
35a90f2769 skip -mcpu or -march=armv8-... flags because zig doesn't support them (stupid grpc build thirdparty hardcode) 2025-09-21 20:32:47 +02:00
henderkes
fa87149631 shallow submodules as well (grpc download goes from 2.9 gb to 900mb) 2025-09-21 19:59:24 +02:00
Marc
8dfe722e14 Merge branch 'main' into fix/grpc-shared 2025-09-19 15:31:25 +02:00
henderkes
dcbfe1e0ab e-dant/watcher is a c++ library too 2025-09-19 15:29:49 +02:00
henderkes
44399cd185 remove -Wl,--as-needed for imagemagick build 2025-09-19 09:21:17 +02:00
henderkes
927d7f55ba make sure -ldl is not taken as needed, problem on EL8 2025-09-18 22:47:01 +02:00
henderkes
e323d7b155 revert to private 2025-09-18 21:41:35 +02:00
henderkes
d0b253c346 not required 2025-09-18 21:36:38 +02:00
henderkes
81430e6853 also use libstdc++.a for grpc? 2025-09-18 21:29:46 +02:00
henderkes
83696e92b7 remove whitespace 2025-09-18 20:12:41 +02:00
henderkes
b0538c09bf deduplicate those to make it more readable 2025-09-18 20:09:17 +02:00
henderkes
40f89d1dca is shared_libadd enough? 2025-09-18 19:44:48 +02:00
henderkes
fa2e041cc9 test shared grpc, imagick 2025-09-18 19:01:10 +02:00
Marc
3c614663a3 Merge branch 'main' into fix/grpc-shared 2025-09-18 18:22:42 +02:00
henderkes
b5c7185374 add comment for new method 2025-09-18 18:20:26 +02:00
henderkes
d0a9a3a594 fix return of array 2025-09-18 18:17:48 +02:00
henderkes
2e6329bb86 add cpp-library for imagemagick too 2025-09-18 18:16:42 +02:00
henderkes
d3ba04fc5b fix grpc shared build (ignores CXXFLAGS, needs CPPFLAGS) 2025-09-18 18:16:41 +02:00
27 changed files with 279 additions and 220 deletions

View File

@@ -136,12 +136,12 @@ jobs:
macos-x86_64)
DOWN_CMD="composer update --no-dev --classmap-authoritative && ./bin/spc doctor --auto-fix && ./bin/spc download"
BUILD_CMD="./bin/spc build"
RUNS_ON="macos-13"
RUNS_ON="macos-15-intel"
;;
macos-aarch64)
DOWN_CMD="composer update --no-dev --classmap-authoritative && ./bin/spc doctor --auto-fix && ./bin/spc download"
BUILD_CMD="./bin/spc build"
RUNS_ON="macos-14"
RUNS_ON="macos-15"
;;
esac
DOWN_CMD="$DOWN_CMD --with-php=${{ inputs.php-version }} --for-extensions=${{ inputs.extensions }} --ignore-cache-sources=php-src"

View File

@@ -85,9 +85,9 @@ jobs:
- "8.4"
operating-system:
- "ubuntu-latest"
#- "macos-13"
#- "macos-15-intel"
#- "debian-arm64-self-hosted"
- "macos-14"
- "macos-15"
steps:
- name: "Checkout"
@@ -99,11 +99,11 @@ jobs:
OS=""
if [ "${{ matrix.operating-system }}" = "ubuntu-latest" ]; then
OS="linux-x86_64"
elif [ "${{ matrix.operating-system }}" = "macos-13" ]; then
elif [ "${{ matrix.operating-system }}" = "macos-15-intel" ]; then
OS="macos-x86_64"
elif [ "${{ matrix.operating-system }}" = "debian-arm64-self-hosted" ]; then
OS="linux-aarch64"
elif [ "${{ matrix.operating-system }}" = "macos-14" ]; then
elif [ "${{ matrix.operating-system }}" = "macos-15" ]; then
OS="macos-aarch64"
fi
echo "OS=$OS" >> $GITHUB_ENV

View File

@@ -27,7 +27,7 @@ jobs:
os: "ubuntu-latest"
filename: "spc-linux-x86_64.tar.gz"
- name: "macos-x86_64"
os: "macos-13"
os: "macos-15-intel"
filename: "spc-macos-x86_64.tar.gz"
- name: "linux-aarch64"
os: "ubuntu-latest"
@@ -147,11 +147,11 @@ jobs:
- name: "linux-x86_64"
os: "ubuntu-latest"
- name: "macos-x86_64"
os: "macos-13"
os: "macos-15-intel"
- name: "linux-aarch64"
os: "ubuntu-24.04-arm"
- name: "macos-aarch64"
os: "macos-latest"
os: "macos-15"
- name: "windows-x64"
os: "windows-latest"
steps:

View File

@@ -34,6 +34,7 @@
; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`)
; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`)
; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`)
; SPC_EXTRA_PHP_VARS: the extra vars for building php, used in `configure` and `make` command.
[global]
; Build concurrency for make -jN, default is CPU_COUNT, this value are used in every libs.

View File

@@ -759,11 +759,9 @@
},
"type": "builtin",
"arg-type": "custom",
"lib-depends": [
"libxml2"
],
"ext-depends-windows": [
"xml"
"ext-depends": [
"libxml",
"session"
]
},
"sockets": {
@@ -1154,8 +1152,9 @@
"support": {
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-path",
"type": "external",
"source": "ext-zip",
"arg-type": "custom",
"arg-type-windows": "enable",
"lib-depends-unix": [
"libzip"

View File

@@ -202,6 +202,7 @@
"openssl",
"libcares"
],
"cpp-library": true,
"provide-pre-built": true,
"frameworks": [
"CoreFoundation"
@@ -230,6 +231,7 @@
},
"imagemagick": {
"source": "imagemagick",
"cpp-library": true,
"pkg-configs": [
"Magick++-7.Q16HDRI",
"MagickCore-7.Q16HDRI",
@@ -822,6 +824,7 @@
},
"snappy": {
"source": "snappy",
"cpp-library": true,
"static-libs-unix": [
"libsnappy.a"
],
@@ -867,6 +870,7 @@
},
"watcher": {
"source": "watcher",
"cpp-library": true,
"static-libs-unix": [
"libwatcher-c.a"
],

View File

@@ -263,6 +263,15 @@
"path": "LICENSE"
}
},
"ext-zip": {
"type": "url",
"url": "https://pecl.php.net/get/zip",
"filename": "ext-zip.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-zstd": {
"type": "git",
"path": "php-src/ext/zstd",
@@ -324,7 +333,7 @@
},
"grpc": {
"type": "git",
"rev": "v1.68.x",
"rev": "v1.75.x",
"url": "https://github.com/grpc/grpc.git",
"provide-pre-built": true,
"license": {

View File

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

View File

@@ -128,27 +128,6 @@ abstract class BuilderBase
return array_filter($this->exts, fn ($ext) => $ext->isBuildStatic());
}
/**
* Check if there is a cpp extensions or libraries.
*/
public function hasCpp(): bool
{
// judge cpp-extension
$exts = array_keys($this->getExts(false));
foreach ($exts as $ext) {
if (Config::getExt($ext, 'cpp-extension', false) === true) {
return true;
}
}
$libs = array_keys($this->getLibs());
foreach ($libs as $lib) {
if (Config::getLib($lib, 'cpp-library', false) === true) {
return true;
}
}
return false;
}
/**
* Set libs only mode.
*

View File

@@ -5,16 +5,11 @@ declare(strict_types=1);
namespace SPC\builder;
use SPC\exception\EnvironmentException;
use SPC\exception\FileSystemException;
use SPC\exception\SPCException;
use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\toolchain\ClangNativeToolchain;
use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
@@ -226,12 +221,24 @@ class Extension
public function patchBeforeSharedMake(): bool
{
$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)
);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') ? '-l:libstdc++.a' : null;
$lstdcpp ??= str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : '';
$makefileContent = file_get_contents($this->source_dir . '/Makefile');
if (preg_match('/^(.*_SHARED_LIBADD\s*=\s*)(.*)$/m', $makefileContent, $matches)) {
$prefix = $matches[1];
$currentLibs = trim($matches[2]);
$newLibs = trim("{$currentLibs} {$staticLibs} {$lstdcpp}");
$deduplicatedLibs = deduplicate_flags($newLibs);
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
$prefix . $deduplicatedLibs
);
}
if ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS')) {
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
@@ -405,42 +412,7 @@ class Extension
*/
public function buildUnixShared(): void
{
$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' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
if (str_contains($env['LIBS'], '-lstdc++') && SPCTarget::getTargetOS() === 'Linux') {
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
} elseif (ToolchainManager::getToolchainClass() === GccNativeToolchain::class || ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) {
try {
$content = FileSystem::readFile($this->source_dir . '/config.m4');
if ($content && !str_contains($content, 'PHP_ADD_LIBRARY(stdc++')) {
$pattern = '/(PHP_NEW_EXTENSION\(' . $this->name . ',.*\))/m';
$replacement = "$1\nPHP_ADD_LIBRARY(stdc++, 1, " . strtoupper($this->name) . '_SHARED_LIBADD)';
FileSystem::replaceFileRegex(
$this->source_dir . '/config.m4',
$pattern,
$replacement
);
}
} catch (FileSystemException) {
}
}
}
$env = $this->getSharedExtensionEnv();
if ($this->patchBeforeSharedPhpize()) {
logger()->info("Extension [{$this->getName()}] patched before shared phpize");
}
@@ -455,13 +427,15 @@ class Extension
logger()->info("Extension [{$this->getName()}] patched before shared configure");
}
$phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
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'
"--enable-shared --disable-static {$phpvars}"
);
if ($this->patchBeforeSharedMake()) {
@@ -512,6 +486,30 @@ class Extension
return $this->build_static;
}
/**
* 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'),
);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
return [
'CFLAGS' => $config['cflags'],
'CXXFLAGS' => $config['cflags'],
'LDFLAGS' => $config['ldflags'],
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
}
protected function addLibraryDependency(string $name, bool $optional = false): void
{
$depLib = $this->builder->getLib($name);
@@ -584,12 +582,12 @@ class Extension
$added = 0;
foreach ($ret as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!in_array($depdepName, array_keys($deps), true)) {
if (!array_key_exists($depdepName, $deps)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!in_array($depName, array_keys($deps), true)) {
if (!array_key_exists($depName, $deps)) {
$deps[$depName] = $dep;
}
}

View File

@@ -56,4 +56,11 @@ class grpc extends Extension
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true;
}
protected function getSharedExtensionEnv(): array
{
$env = parent::getSharedExtensionEnv();
$env['CPPFLAGS'] = $env['CXXFLAGS'] . ' -Wno-attributes';
return $env;
}
}

View File

@@ -6,6 +6,8 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\CustomExt;
#[CustomExt('simdjson')]
@@ -17,7 +19,7 @@ class simdjson extends Extension
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/ext/simdjson/config.m4',
'/php_version=(`.*`)$/m',
'php_version=' . strval($php_ver)
'php_version=' . $php_ver
);
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/simdjson/config.m4',
@@ -31,4 +33,18 @@ class simdjson extends Extension
);
return true;
}
public function getSharedExtensionEnv(): array
{
$env = parent::getSharedExtensionEnv();
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$extra = getenv('SPC_COMPILER_EXTRA');
if (!str_contains((string) $extra, '-lstdc++')) {
f_putenv('SPC_COMPILER_EXTRA=' . clean_spaces($extra . ' -lstdc++'));
}
$env['CFLAGS'] .= ' -Xclang -target-feature -Xclang +evex512';
$env['CXXFLAGS'] .= ' -Xclang -target-feature -Xclang +evex512';
}
return $env;
}
}

View File

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

View File

@@ -95,6 +95,8 @@ class LinuxBuilder extends UnixBuilderBase
// 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions!
]);
$phpvars = getenv('SPC_EXTRA_PHP_VARS') ?: '';
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
if ($embed_type !== 'static' && SPCTarget::isStatic()) {
throw new WrongUsageException(
@@ -116,6 +118,7 @@ class LinuxBuilder extends UnixBuilderBase
$json_74 .
$zts .
$maxExecutionTimers .
"{$phpvars} " .
$this->makeStaticExtensionArgs() . ' '
));
@@ -153,7 +156,14 @@ class LinuxBuilder extends UnixBuilderBase
}
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...');
if (SPCTarget::isStatic()) {
throw new WrongUsageException(
"You're building against musl libc statically (the default on Linux), but you're trying to build shared extensions.\n" .
'Static musl libc does not implement `dlopen`, so your php binary is not able to load shared extensions.' . "\n" .
'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, or use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc`.'
);
}
logger()->info('Building shared extensions...');
$this->buildSharedExts();
}
}

View File

@@ -291,11 +291,11 @@ abstract class UnixBuilderBase extends BuilderBase
}
}
$debugFlags = $this->getOption('no-strip') ? '-w -s ' : '';
$extLdFlags = "-extldflags '-pie{$dynamic_exports}'";
$extLdFlags = "-extldflags '-pie{$dynamic_exports} {$this->arch_ld_flags}'";
$muslTags = '';
$staticFlags = '';
if (SPCTarget::isStatic()) {
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports}'";
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports} {$this->arch_ld_flags}'";
$muslTags = 'static_build,';
$staticFlags = '-static-pie';
}
@@ -303,7 +303,6 @@ abstract class UnixBuilderBase extends BuilderBase
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list);
$cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS');
$libs = $config['libs'];
$libs .= PHP_OS_FAMILY === 'Linux' ? ' -lrt' : '';
// Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix.
if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') ||
str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), '-fprofile')) &&

View File

@@ -12,6 +12,11 @@ trait imagemagick
{
protected function build(): void
{
$original_ldflags = $this->builder->arch_ld_flags;
if (str_contains($this->builder->arch_ld_flags, '-Wl,--as-needed')) {
$this->builder->arch_ld_flags = str_replace('-Wl,--as-needed', '', $original_ldflags);
}
$ac = UnixAutoconfExecutor::create($this)
->optionalLib('libzip', ...ac_with_args('zip'))
->optionalLib('libjpeg', ...ac_with_args('jpeg'))
@@ -32,7 +37,7 @@ trait imagemagick
);
// special: linux-static target needs `-static`
$ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl';
$ldflags = SPCTarget::isStatic() ? '-static -ldl' : '-ldl';
// special: macOS needs -iconv
$libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : '';
@@ -45,6 +50,8 @@ trait imagemagick
$ac->configure()->make();
$this->builder->arch_ld_flags = $original_ldflags;
$filelist = [
'ImageMagick.pc',
'ImageMagick-7.Q16HDRI.pc',

View File

@@ -4,111 +4,77 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\exception\BuildFailureException;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\PkgConfigUtil;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
trait postgresql
{
public function patchBeforeBuild(): bool
{
// fix aarch64 build on glibc 2.17 (e.g. CentOS 7)
if (SPCTarget::getLibcVersion() === '2.17' && GNU_ARCH === 'aarch64') {
FileSystem::replaceFileStr(
$this->source_dir . '/src/port/pg_popcount_aarch64.c',
'HWCAP_SVE',
'0',
);
FileSystem::replaceFileStr(
$this->source_dir . '/src/port/pg_crc32c_armv8_choose.c',
'#if defined(__linux__) && !defined(__aarch64__) && !defined(HWCAP2_CRC32)',
'#if defined(__linux__) && !defined(HWCAP_CRC32)',
);
return true;
try {
FileSystem::replaceFileStr("{$this->source_dir}/src/port/pg_popcount_aarch64.c", 'HWCAP_SVE', '0');
FileSystem::replaceFileStr(
"{$this->source_dir}/src/port/pg_crc32c_armv8_choose.c",
'#if defined(__linux__) && !defined(__aarch64__) && !defined(HWCAP2_CRC32)',
'#if defined(__linux__) && !defined(HWCAP_CRC32)'
);
} catch (FileSystemException) {
// allow file not-existence to make it compatible with old and new version
}
}
return false;
// skip the test on platforms where libpq infrastructure may be provided by statically-linked libraries
FileSystem::replaceFileStr("{$this->source_dir}/src/interfaces/libpq/Makefile", 'invokes exit\'; exit 1;', 'invokes exit\';');
// disable shared libs build
FileSystem::replaceFileStr(
"{$this->source_dir}/src/Makefile.shlib",
[
'$(LINK.shared) -o $@ $(OBJS) $(LDFLAGS) $(LDFLAGS_SL) $(SHLIB_LINK)',
'$(INSTALL_SHLIB) $< \'$(DESTDIR)$(pkglibdir)/$(shlib)\'',
'$(INSTALL_SHLIB) $< \'$(DESTDIR)$(libdir)/$(shlib)\'',
'$(INSTALL_SHLIB) $< \'$(DESTDIR)$(bindir)/$(shlib)\'',
],
''
);
return true;
}
protected function build(): void
{
$builddir = BUILD_ROOT_PATH;
$envs = '';
$packages = 'zlib openssl readline libxml-2.0';
$optional_packages = [
'zstd' => 'libzstd',
'ldap' => 'ldap',
'libxslt' => 'libxslt',
'icu' => 'icu-i18n',
$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'));
$env_vars = [
'CFLAGS' => $config['cflags'],
'CPPFLAGS' => '-DPIC',
'LDFLAGS' => $config['ldflags'],
'LIBS' => $config['libs'],
];
$error_exec_cnt = 0;
foreach ($optional_packages as $lib => $pkg) {
if ($this->builder->getLib($lib)) {
$packages .= ' ' . $pkg;
$output = shell()->execWithResult("pkg-config --static {$pkg}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
logger()->info(var_export($output[1], true));
}
}
$output = shell()->execWithResult("pkg-config --cflags-only-I --static {$packages}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
$macos_15_bug_cflags = PHP_OS_FAMILY === 'Darwin' ? ' -Wno-unguarded-availability-new' : '';
$cflags = '';
if (!empty($output[1][0])) {
$cflags = $output[1][0];
$envs .= ' CPPFLAGS="-DPIC"';
$cflags = "{$cflags} -fno-ident{$macos_15_bug_cflags}";
}
$output = shell()->execWithResult("pkg-config --libs-only-L --static {$packages}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
if (!empty($output[1][0])) {
$ldflags = $output[1][0];
$envs .= " LDFLAGS=\"{$ldflags}\" ";
}
$output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
if (!empty($output[1][0])) {
$libs = $output[1][0];
$libcpp = '';
if ($this->builder->getLib('icu')) {
$libcpp = $this instanceof LinuxLibraryBase ? ' -lstdc++' : ' -lc++';
}
$envs .= " LIBS=\"{$libs}{$libcpp}\" ";
}
if ($error_exec_cnt > 0) {
throw new BuildFailureException('Failed to get pkg-config information!');
if ($ldLibraryPath = getenv('SPC_LD_LIBRARY_PATH')) {
$env_vars['LD_LIBRARY_PATH'] = $ldLibraryPath;
}
FileSystem::resetDir($this->source_dir . '/build');
# 有静态链接配置 参考文件: src/interfaces/libpq/Makefile
shell()->cd($this->source_dir . '/build')
->exec('sed -i.backup "s/invokes exit\'; exit 1;/invokes exit\';/" ../src/interfaces/libpq/Makefile')
->exec('sed -i.backup "278 s/^/# /" ../src/Makefile.shlib')
->exec('sed -i.backup "402 s/^/# /" ../src/Makefile.shlib');
// php source relies on the non-private encoding functions in libpgcommon.a
FileSystem::replaceFileStr(
$this->source_dir . '/src/common/Makefile',
"{$this->source_dir}/src/common/Makefile",
'$(OBJS_FRONTEND): CPPFLAGS += -DUSE_PRIVATE_ENCODING_FUNCS',
'$(OBJS_FRONTEND): CPPFLAGS += -UUSE_PRIVATE_ENCODING_FUNCS -DFRONTEND',
);
$env = [
'CFLAGS' => $cflags,
];
if ($ldLibraryPath = getenv('SPC_LD_LIBRARY_PATH')) {
$env['LD_LIBRARY_PATH'] = $ldLibraryPath;
}
// configure
$shell = shell()->cd($this->source_dir . '/build')->initializeEnv($this)
->appendEnv($env)
$shell = shell()->cd("{$this->source_dir}/build")->initializeEnv($this)
->appendEnv($env_vars)
->exec(
"{$envs} ../configure " .
"--prefix={$builddir} " .
'../configure ' .
"--prefix={$this->getBuildRootPath()} " .
'--enable-coverage=no ' .
'--with-ssl=openssl ' .
'--with-readline ' .
@@ -125,6 +91,7 @@ trait postgresql
'--without-tcl '
);
// patch ldap lib
if ($this->builder->getLib('ldap')) {
$libs = PkgConfigUtil::getLibsArray('ldap');
$libs = clean_spaces(implode(' ', $libs));
@@ -133,18 +100,18 @@ trait postgresql
}
$shell
->exec($envs . ' make -C src/bin/pg_config install')
->exec($envs . ' make -C src/include install')
->exec($envs . ' make -C src/common install')
->exec($envs . ' make -C src/port install')
->exec($envs . ' make -C src/interfaces/libpq install');
->exec('make -C src/bin/pg_config install')
->exec('make -C src/include install')
->exec('make -C src/common install')
->exec('make -C src/port install')
->exec('make -C src/interfaces/libpq install');
// remove dynamic libs
shell()->cd($this->source_dir . '/build')
->exec("rm -rf {$builddir}/lib/*.so.*")
->exec("rm -rf {$builddir}/lib/*.so")
->exec("rm -rf {$builddir}/lib/*.dylib");
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so.*")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.dylib");
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/pkgconfig/libpq.pc', '-lldap', '-lldap -llber');
FileSystem::replaceFileStr("{$this->getLibDir()}/pkgconfig/libpq.pc", '-lldap', '-lldap -llber');
}
}

View File

@@ -106,7 +106,7 @@ class DownloadCommand extends BaseCommand
}
// retry
$retry = intval($this->getOption('retry'));
$retry = (int) $this->getOption('retry');
f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry);
// Use shallow-clone can reduce git resource download
@@ -265,7 +265,7 @@ class DownloadCommand extends BaseCommand
f_passthru((PHP_OS_FAMILY === 'Windows' ? 'rmdir /s /q ' : 'rm -rf ') . DOWNLOAD_PATH);
}
// unzip command check
if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) {
if (PHP_OS_FAMILY !== 'Windows' && !self::findCommand('unzip')) {
$this->output->writeln('Missing unzip command, you need to install it first !');
$this->output->writeln('You can use "bin/spc doctor" command to check and install required tools');
return static::FAILURE;

View File

@@ -65,19 +65,19 @@ class Downloader
url: "https://api.github.com/repos/{$source['repo']}/{$type}",
hooks: [[CurlHook::class, 'setupGithubToken']],
retries: self::getRetryAttempts()
), true);
), true, 512, JSON_THROW_ON_ERROR);
$url = null;
for ($i = 0; $i < count($data); ++$i) {
if (($data[$i]['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) {
foreach ($data as $rel) {
if (($rel['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) {
continue;
}
if (!($source['match'] ?? null)) {
$url = $data[$i]['tarball_url'] ?? null;
$url = $rel['tarball_url'] ?? null;
break;
}
if (preg_match('|' . $source['match'] . '|', $data[$i]['tarball_url'])) {
$url = $data[$i]['tarball_url'];
if (preg_match('|' . $source['match'] . '|', $rel['tarball_url'])) {
$url = $rel['tarball_url'];
break;
}
}
@@ -232,7 +232,8 @@ class Downloader
$quiet = !defined('DEBUG_MODE') ? '-q --quiet' : '';
$git = SPC_GIT_EXEC;
$shallow = defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '';
$recursive = ($submodules === null) ? '--recursive' : '';
$recursive = ($submodules === null && defined('GIT_SHALLOW_CLONE')) ? '--recursive --shallow-submodules' : null;
$recursive ??= $submodules === null ? '--recursive' : '';
try {
self::registerCancelEvent(function () use ($download_path) {
@@ -243,8 +244,9 @@ class Downloader
});
f_passthru("{$git} clone {$quiet} --config core.autocrlf=false --branch \"{$branch}\" {$shallow} {$recursive} \"{$url}\" \"{$download_path}\"");
if ($submodules !== null) {
$depth_flag = defined('GIT_SHALLOW_CLONE') ? '--depth 1' : '';
foreach ($submodules as $submodule) {
f_passthru("cd \"{$download_path}\" && {$git} submodule update --init " . escapeshellarg($submodule));
f_passthru("cd \"{$download_path}\" && {$git} submodule update --init {$depth_flag} " . escapeshellarg($submodule));
}
}
} catch (SPCException $e) {
@@ -399,7 +401,7 @@ class Downloader
* Download source
*
* @param string $name source name
* @param null|array{
* @param null|array{
* type: string,
* repo: ?string,
* url: ?string,
@@ -414,7 +416,7 @@ class Downloader
* path: ?string,
* text: ?string
* }
* } $source source meta info: [type, path, rev, url, filename, regex, license]
* } $source source meta info: [type, path, rev, url, filename, regex, license]
* @param bool $force Whether to force download (default: false)
* @param int $download_as Lock source type (default: SPC_LOCK_SOURCE)
*/

View File

@@ -19,9 +19,15 @@ while [[ $# -gt 0 ]]; do
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
-march=*|-mcpu=*)
OPT_NAME="${1%%=*}"
OPT_VALUE="${1#*=}"
# Skip armv8- flags entirely as Zig doesn't support them
if [[ "$OPT_VALUE" == armv8-* ]]; then
shift
continue
fi
# replace -march=x86-64 with -march=x86_64
OPT_VALUE="${OPT_VALUE//-/_}"
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
shift

View File

@@ -20,7 +20,7 @@ class PhpSource extends CustomSourceBase
} elseif ($major === 'git') {
Downloader::downloadSource('php-src', ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force);
} else {
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force);
Downloader::downloadSource('php-src', $this->getLatestPHPInfo($major), $force);
}
}
@@ -33,7 +33,7 @@ class PhpSource extends CustomSourceBase
// 查找最新的小版本号
$info = json_decode(Downloader::curlExec(
url: "https://www.php.net/releases/index.php?json&version={$major_version}",
retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)
retries: (int) getenv('SPC_DOWNLOAD_RETRIES') ?: 0
), true);
if (!isset($info['version'])) {
throw new DownloaderException("Version {$major_version} not found.");

View File

@@ -64,6 +64,11 @@ class ZigToolchain implements ToolchainInterface
$extra_libs = trim($extra_libs . ' -lunwind');
GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}");
}
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: '';
$has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4');
if (!$has_avx512) {
GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no');
}
}
public function getCompilerInfo(): ?string

View File

@@ -40,7 +40,12 @@ class GlobalEnvManager
if (is_unix()) {
self::addPathIfNotExists(BUILD_BIN_PATH);
self::addPathIfNotExists(PKG_ROOT_PATH . '/bin');
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig');
$pkgConfigPath = getenv('PKG_CONFIG_PATH');
if ($pkgConfigPath !== false) {
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . "/pkgconfig:{$pkgConfigPath}");
} else {
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig');
}
}
$ini = self::readIniFile();

View File

@@ -6,6 +6,7 @@ namespace SPC\util;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
use SPC\builder\Extension;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use Symfony\Component\Console\Input\ArgvInput;
@@ -87,7 +88,7 @@ class SPCConfigUtil
if (SPCTarget::getTargetOS() === 'Darwin') {
$libs .= " {$this->getFrameworksString($extensions)}";
}
if ($this->builder->hasCpp()) {
if ($this->hasCpp($extensions, $libraries)) {
$libcpp = SPCTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++';
$libs = str_replace($libcpp, '', $libs) . " {$libcpp}";
}
@@ -123,6 +124,27 @@ class SPCConfigUtil
];
}
private function hasCpp(array $extensions, array $libraries): bool
{
// judge cpp-extension
$builderExtNames = array_keys($this->builder->getExts(false));
$exts = array_unique([...$builderExtNames, ...$extensions]);
foreach ($exts as $ext) {
if (Config::getExt($ext, 'cpp-extension', false) === true) {
return true;
}
}
$builderLibNames = array_keys($this->builder->getLibs());
$libs = array_unique([...$builderLibNames, ...$libraries]);
foreach ($libs as $lib) {
if (Config::getLib($lib, 'cpp-library', false) === true) {
return true;
}
}
return false;
}
private function getIncludesString(array $libraries): string
{
$base = BUILD_INCLUDE_PATH;

View File

@@ -245,6 +245,23 @@ function clean_spaces(string $string): string
return trim(preg_replace('/\s+/', ' ', $string));
}
/**
* Deduplicate flags in a string. Only the last occurence of each flag will be kept.
* E.g. `-lintl -lstdc++ -lphp -lstdc++` becomes `-lintl -lphp -lstdc++`
*
* @param string $flags the string containing flags to deduplicate
* @return string the deduplicated string with no duplicate flags
*/
function deduplicate_flags(string $flags): string
{
$tokens = preg_split('/\s+/', trim($flags));
// Reverse, unique, reverse back - keeps last occurrence of duplicates
$deduplicated = array_reverse(array_unique(array_reverse($tokens)));
return implode(' ', $deduplicated);
}
/**
* Register a callback function to handle keyboard interrupts (Ctrl+C).
*

View File

@@ -21,21 +21,20 @@ $test_php_version = [
// 'git',
];
// test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available)
// test os (macos-15-intel, macos-15, ubuntu-latest, windows-latest are available)
$test_os = [
// 'macos-13', // bin/spc for x86_64
// 'macos-14', // bin/spc for arm64
'macos-15-intel', // bin/spc for x86_64
'macos-15', // bin/spc for arm64
'ubuntu-latest', // bin/spc-alpine-docker for x86_64
'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
'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
// 'ubuntu-latest', // bin/spc-alpine-docker for x86_64
// 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
// '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
];
// whether enable thread safe
$zts = true;
$zts = false;
$no_strip = false;
@@ -50,7 +49,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' => 'pgsql',
'Linux', 'Darwin' => 'pdo_pgsql',
'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',
};
@@ -156,17 +155,13 @@ if ($shared_extensions) {
switch ($argv[2] ?? null) {
case 'ubuntu-22.04':
case 'ubuntu-22.04-arm':
case 'macos-15':
case 'macos-15-intel':
$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':
$shared_cmd = ' --build-shared=' . quote2($shared_extensions) . ' ';
$no_strip = true;
break;
default:
$shared_cmd = '';
break;

View File

@@ -62,12 +62,6 @@ class BuilderTest extends TestCase
$this->assertInstanceOf(Extension::class, $this->builder->getExt('mbregex'));
}
public function testHasCpp()
{
// mbregex doesn't have cpp
$this->assertFalse($this->builder->hasCpp());
}
public function testMakeExtensionArgs()
{
$this->assertStringContainsString('--enable-mbstring', $this->builder->makeStaticExtensionArgs());