diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 7648873a..a8fd17a1 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -197,5 +197,5 @@ jobs:
run: php src/globals/test-extensions.php build_cmd ${{ matrix.os }} ${{ matrix.php }}
- name: "Run Build Tests (build - embed for non-windows)"
- if: matrix.os != 'windows-latest'
+ if: ${{ !startsWith(matrix.os, 'windows-') }}
run: php src/globals/test-extensions.php build_embed_cmd ${{ matrix.os }} ${{ matrix.php }}
diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker
index e2e28dcd..60732908 100755
--- a/bin/spc-alpine-docker
+++ b/bin/spc-alpine-docker
@@ -122,6 +122,20 @@ MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"
# shellcheck disable=SC2086
# shellcheck disable=SC2090
if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
+ echo "* Debug mode enabled, run docker in interactive mode."
+ echo "* You can use 'exit' to exit the docker container."
+ echo "* You can use 'bin/spc' like normal builds."
+ echo "*"
+ echo "* Mounted directories:"
+ echo "* ./config: $(pwd)/config"
+ echo "* ./src: $(pwd)/src"
+ echo "* ./buildroot: $(pwd)/buildroot"
+ echo "* ./source: $(pwd)/source"
+ echo "* ./dist: $(pwd)/dist"
+ echo "* ./downloads: $(pwd)/downloads"
+ echo "* ./pkgroot: $(pwd)/pkgroot"
+ echo "*"
+
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-v2
else
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-v2 bin/spc $@
diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker
index 101994e4..e063f115 100755
--- a/bin/spc-gnu-docker
+++ b/bin/spc-gnu-docker
@@ -12,7 +12,7 @@ DOCKER_EXECUTABLE="docker"
# shellcheck disable=SC2046
if [ $(id -u) -ne 0 ]; then
if ! docker info > /dev/null 2>&1; then
- if [ "$SPC_USE_SUDO" != "yes" ]; then
+ if [ "$SPC_USE_SUDO" != "yes" ] && [ "$SPC_DOCKER_DEBUG" != "yes" ]; then
echo "Docker command requires sudo"
# shellcheck disable=SC2039
echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again'
@@ -86,7 +86,7 @@ COPY ./composer.* /app/
ADD ./bin/setup-runtime /app/bin/setup-runtime
ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime
-RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative
+RUN /app/bin/php /app/bin/composer install --no-dev
ENV PATH="/app/bin:/cmake/bin:$PATH"
ENV SPC_LIBC=glibc
@@ -146,6 +146,20 @@ echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"'
# shellcheck disable=SC2090
if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
+ echo "* Debug mode enabled, run docker in interactive mode."
+ echo "* You can use 'exit' to exit the docker container."
+ echo "* You can use 'bin/spc' like normal builds."
+ echo "*"
+ echo "* Mounted directories:"
+ echo "* ./config: $(pwd)/config"
+ echo "* ./src: $(pwd)/src"
+ echo "* ./buildroot: $(pwd)/buildroot"
+ echo "* ./source: $(pwd)/source"
+ echo "* ./dist: $(pwd)/dist"
+ echo "* ./downloads: $(pwd)/downloads"
+ echo "* ./pkgroot: $(pwd)/pkgroot"
+ echo "*"
+
$DOCKER_EXECUTABLE run --rm -it --privileged $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH
else
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@
diff --git a/config/env.ini b/config/env.ini
index 4c25823b..f70ad0a2 100644
--- a/config/env.ini
+++ b/config/env.ini
@@ -28,7 +28,6 @@
; PATH: static-php-cli will add `$BUILD_BIN_PATH` to PATH.
; PKG_CONFIG: static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG.
; PKG_CONFIG_PATH: static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH.
-; SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS: the default optimization CFLAGS for compiling php. (if --no-strip option is set: `-g -O0`, else: `-g -Os`)
;
; * These vars are only be defined in LinuxBuilder and cannot be changed anywhere:
; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`)
@@ -98,9 +97,9 @@ SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; LIBS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
; EXTRA_CFLAGS for `make` php
-SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS} -fno-ident -fPIE -fPIC"
+SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -fno-ident -fPIE -fPIC"
; EXTRA_LIBS for `make` php
-SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS=""
+SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm"
; EXTRA_LDFLAGS_PROGRAM for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie"
@@ -132,7 +131,7 @@ SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; EXTRA_CFLAGS for `make` php
-SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS}"
+SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv"
; embed type for php, static (libphp.a) or shared (libphp.dylib)
diff --git a/config/ext.json b/config/ext.json
index a906daf6..c0ead898 100644
--- a/config/ext.json
+++ b/config/ext.json
@@ -119,6 +119,10 @@
"Linux": "partial",
"BSD": "wip"
},
+ "target": [
+ "static",
+ "shared"
+ ],
"notes": true,
"arg-type": "custom",
"type": "builtin",
@@ -772,6 +776,9 @@
"Windows": "no",
"BSD": "wip"
},
+ "target": [
+ "static"
+ ],
"notes": true,
"type": "external",
"source": "swoole",
@@ -919,12 +926,16 @@
]
},
"xdebug": {
- "type": "builtin",
+ "type": "external",
+ "source": "xdebug",
+ "target": [
+ "shared"
+ ],
"support": {
"Windows": "wip",
"BSD": "no",
- "Darwin": "no",
- "Linux": "no"
+ "Darwin": "partial",
+ "Linux": "partial"
},
"notes": true
},
@@ -1040,6 +1051,9 @@
"support": {
"BSD": "wip"
},
+ "target": [
+ "static"
+ ],
"type": "builtin",
"arg-type": "with-prefix",
"arg-type-windows": "enable",
diff --git a/config/lib.json b/config/lib.json
index a68d242e..576f1087 100644
--- a/config/lib.json
+++ b/config/lib.json
@@ -281,8 +281,7 @@
"headers-unix": [
"ares.h",
"ares_dns.h",
- "ares_nameser.h",
- "ares_rules.h"
+ "ares_nameser.h"
]
},
"libde265": {
diff --git a/config/source.json b/config/source.json
index ab8bd630..db6cbe47 100644
--- a/config/source.json
+++ b/config/source.json
@@ -913,6 +913,15 @@
"path": "COPYING"
}
},
+ "xdebug": {
+ "type": "url",
+ "url": "https://pecl.php.net/get/xdebug",
+ "filename": "xdebug.tgz",
+ "license": {
+ "type": "file",
+ "path": "LICENSE"
+ }
+ },
"xhprof": {
"type": "url",
"url": "https://pecl.php.net/get/xhprof",
diff --git a/docs/en/guide/extension-notes.md b/docs/en/guide/extension-notes.md
index 734658ab..83c0b787 100644
--- a/docs/en/guide/extension-notes.md
+++ b/docs/en/guide/extension-notes.md
@@ -80,10 +80,9 @@ and this extension cannot be compiled into php by static linking, so it cannot b
## xdebug
-1. Xdebug is only buildable as a shared extension. On Linux, you need to use static-php-cli with SPC_LIBC=glibc and then compile php-xdebug from source with the option `--with-php-config=/path/to/buildroot/bin/php-config`.
-2. The macOS platform can compile an xdebug extension under PHP compiled on the same platform,
-extract the `xdebug.so` file, and then use the `--no-strip` parameter in static-php-cli to retain the debug symbol table and add the `ffi` extension.
-The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
+1. Xdebug is only buildable as a shared extension. On Linux, you need to use static-php-cli with SPC_LIBC=glibc.
+2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug".
+ The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
## xml
@@ -126,8 +125,8 @@ For details on the solution, see [FAQ - Unable to use ssl](../faq/#unable-to-use
## ffi
-1. Due to the limitation of Linux system, you cannot use it to load other `so` extensions in the purely static compiled state (spc defaults to pure static compilation).
- Linux supports loading so extensions only if they are non-statically compiled. If you need to use the ffi extension, see [Compile PHP with GNU libc](./build-with-glibc).
+1. Due to the limitation of musl libc's static linkage, you cannot use ffi because dynamic libraries cannot be loaded.
+ If you need to use the ffi extension, see [Compile PHP with GNU libc](./build-with-glibc).
2. macOS supports the ffi extension, but errors will occur when some kernels do not contain debugging symbols.
3. Windows x64 supports the ffi extension.
diff --git a/docs/en/guide/manual-build.md b/docs/en/guide/manual-build.md
index d38e2b85..c935f5c8 100644
--- a/docs/en/guide/manual-build.md
+++ b/docs/en/guide/manual-build.md
@@ -314,6 +314,7 @@ You can try to use the following commands:
- `--with-suggested-exts`: Add `ext-suggests` as dependencies when compiling
- `--with-suggested-libs`: Add `lib-suggests` as dependencies when compiling
- `--with-upx-pack`: Use UPX to reduce the size of the binary file after compilation (you need to use `bin/spc install-pkg upx` to install upx first)
+- `--build-shared=XXX,YYY`: compile the specified extension into a shared library (the default is to compile into a static library)
For hardcoding INI options, it works for cli, micro, embed sapi. Here is a simple example where we preset a larger `memory_limit` and disable the `system` function:
diff --git a/docs/zh/guide/extension-notes.md b/docs/zh/guide/extension-notes.md
index 6ff2dfc0..762d2df9 100644
--- a/docs/zh/guide/extension-notes.md
+++ b/docs/zh/guide/extension-notes.md
@@ -74,9 +74,9 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli
## xdebug
-1. Xdebug 只能作为共享扩展构建。在 Linux 上,您需要使用带有 `SPC_LIBC=glibc` 的 static-php-cli,然后使用选项 `--with-php-config=/path/to/buildroot/bin/php-config` 从源代码编译 php-xdebug。
-2. macOS 平台可以通过在相同平台编译的 PHP 下编译一个 xdebug 扩展,并提取其中的 `xdebug.so` 文件,再在 static-php-cli 中使用 `--no-strip` 参数保留调试符号表,同时加入 `ffi` 扩展。
- 编译的 `./php` 二进制可以通过指定 INI 配置并运行,例如`./php -d 'zend_extension=xdebug.so' your-code.php`。
+1. Xdebug 只能作为共享扩展进行构建。在 Linux 上,您需要使用 static-php-cli 并设置 SPC_LIBC=glibc。
+2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
+ 编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`。
## xml
@@ -117,9 +117,10 @@ pgsql 16.2 修复了这个 Bug,现在正常工作了。
## ffi
-1. 因为 Linux 系统的限制,纯静态编译的状态下(spc 默认编译结果为纯静态)无法使用它加载其他 `so` 扩展。Linux 支持加载 so 扩展的前提是非静态编译。如果你需要使用 ffi 扩展,请参见 [编译 GNU libc 的 PHP](./build-with-glibc)。
-2. macOS 支持 ffi 扩展,但是部分内核下不包含调试符号时会出现错误。
-3. Windows 支持 ffi 扩展。
+1. 由于 musl libc 静态链接的限制,无法加载动态库,因此无法使用 ffi。
+ 如果您需要使用 ffi 扩展,请参阅 [使用 GNU libc 编译 PHP](./build-with-glibc)。
+2. macOS 支持 ffi 扩展,但某些内核不包含调试符号时会出现错误。
+3. Windows x64 支持 ffi 扩展。
## xhprof
diff --git a/docs/zh/guide/manual-build.md b/docs/zh/guide/manual-build.md
index 7a573704..955d98c8 100644
--- a/docs/zh/guide/manual-build.md
+++ b/docs/zh/guide/manual-build.md
@@ -272,6 +272,7 @@ bin/spc build mysqlnd,pdo_mysql --build-all --debug
- `--with-suggested-exts`: 编译时将 `ext-suggests` 也作为编译依赖加入
- `--with-suggested-libs`: 编译时将 `lib-suggests` 也作为编译依赖加入
- `--with-upx-pack`: 编译后使用 UPX 减小二进制文件体积(需先使用 `bin/spc install-pkg upx` 安装 upx)
+- `--build-shared=XXX,YYY`: 编译时将指定的扩展编译为共享库(默认编译为静态库)
硬编码 INI 选项适用于 cli、micro、embed。有关硬编码 INI 选项,下面是一个简单的例子,我们预设一个更大的 `memory_limit`,并且禁用 `system` 函数:
diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php
index 5974bdf0..59f371ef 100644
--- a/src/SPC/builder/BuilderBase.php
+++ b/src/SPC/builder/BuilderBase.php
@@ -122,9 +122,12 @@ abstract class BuilderBase
*
* @return Extension[]
*/
- public function getExts(): array
+ public function getExts(bool $including_shared = true): array
{
- return $this->exts;
+ if ($including_shared) {
+ return $this->exts;
+ }
+ return array_filter($this->exts, fn ($ext) => !$ext->isBuildShared());
}
/**
@@ -136,7 +139,7 @@ abstract class BuilderBase
public function hasCpp(): bool
{
// judge cpp-extension
- $exts = array_keys($this->getExts());
+ $exts = array_keys($this->getExts(false));
foreach ($exts as $ext) {
if (Config::getExt($ext, 'cpp-extension', false) === true) {
return true;
@@ -170,23 +173,46 @@ abstract class BuilderBase
* @throws \Throwable|WrongUsageException
* @internal
*/
- public function proveExts(array $extensions, bool $skip_check_deps = false): void
+ public function proveExts(array $static_extensions, array $shared_extensions = [], bool $skip_check_deps = false, bool $skip_extract = false): void
{
CustomExt::loadCustomExt();
- $this->emitPatchPoint('before-php-extract');
- SourceManager::initSource(sources: ['php-src'], source_only: true);
- $this->emitPatchPoint('after-php-extract');
- if ($this->getPHPVersionID() >= 80000) {
- $this->emitPatchPoint('before-micro-extract');
- SourceManager::initSource(sources: ['micro'], source_only: true);
- $this->emitPatchPoint('after-micro-extract');
+ // judge ext
+ foreach ($static_extensions as $ext) {
+ // if extension does not support static build, throw exception
+ if (!in_array('static', Config::getExtTarget($ext))) {
+ throw new WrongUsageException('Extension [' . $ext . '] does not support static build!');
+ }
}
- $this->emitPatchPoint('before-exts-extract');
- SourceManager::initSource(exts: $extensions);
- $this->emitPatchPoint('after-exts-extract');
- foreach ($extensions as $extension) {
+ foreach ($shared_extensions as $ext) {
+ // if extension does not support shared build, throw exception
+ if (!in_array('shared', Config::getExtTarget($ext)) && !in_array($ext, $shared_extensions)) {
+ throw new WrongUsageException('Extension [' . $ext . '] does not support shared build!');
+ }
+ }
+ if (!$skip_extract) {
+ $this->emitPatchPoint('before-php-extract');
+ SourceManager::initSource(sources: ['php-src'], source_only: true);
+ $this->emitPatchPoint('after-php-extract');
+ if ($this->getPHPVersionID() >= 80000) {
+ $this->emitPatchPoint('before-micro-extract');
+ SourceManager::initSource(sources: ['micro'], source_only: true);
+ $this->emitPatchPoint('after-micro-extract');
+ }
+ $this->emitPatchPoint('before-exts-extract');
+ SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]);
+ $this->emitPatchPoint('after-exts-extract');
+ }
+
+ foreach ([...$static_extensions, ...$shared_extensions] as $extension) {
$class = CustomExt::getExtClass($extension);
+ /** @var Extension $ext */
$ext = new $class($extension, $this);
+ if (in_array($extension, $static_extensions)) {
+ $ext->setBuildStatic();
+ }
+ if (in_array($extension, $shared_extensions)) {
+ $ext->setBuildShared();
+ }
$this->addExt($ext);
}
@@ -194,10 +220,10 @@ abstract class BuilderBase
return;
}
- foreach ($this->exts as $ext) {
+ foreach ($this->getExts() as $ext) {
$ext->checkDependency();
}
- $this->ext_list = $extensions;
+ $this->ext_list = [...$static_extensions, ...$shared_extensions];
}
/**
@@ -207,6 +233,17 @@ abstract class BuilderBase
*/
abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE);
+ public function buildSharedExts(): void
+ {
+ foreach ($this->getExts() as $ext) {
+ if (!$ext->isBuildShared()) {
+ continue;
+ }
+ logger()->info('Building extension [' . $ext->getName() . '] as shared extension (' . $ext->getName() . '.so)');
+ $ext->buildShared();
+ }
+ }
+
/**
* Generate extension enable arguments for configure.
* e.g. --enable-mbstring
@@ -214,10 +251,10 @@ abstract class BuilderBase
* @throws FileSystemException
* @throws WrongUsageException
*/
- public function makeExtensionArgs(): string
+ public function makeStaticExtensionArgs(): string
{
$ret = [];
- foreach ($this->exts as $ext) {
+ foreach ($this->getExts(false) as $ext) {
logger()->info($ext->getName() . ' is using ' . $ext->getConfigureArg());
$ret[] = trim($ext->getConfigureArg());
}
@@ -396,7 +433,7 @@ abstract class BuilderBase
foreach ($this->libs as $lib) {
$lib->validate();
}
- foreach ($this->exts as $ext) {
+ foreach ($this->getExts() as $ext) {
$ext->validate();
}
}
@@ -441,7 +478,7 @@ abstract class BuilderBase
{
$php = "getExts() as $ext) {
+ foreach ($this->getExts(false) as $ext) {
$ext_name = $ext->getDistName();
if (!empty($ext_name)) {
$php .= "echo 'Running micro with {$ext_name} test' . PHP_EOL;\n";
diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php
index a7886cd8..b250097b 100644
--- a/src/SPC/builder/Extension.php
+++ b/src/SPC/builder/Extension.php
@@ -9,11 +9,18 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
+use SPC\util\SPCConfigUtil;
class Extension
{
protected array $dependencies = [];
+ protected bool $build_shared = false;
+
+ protected bool $build_static = false;
+
+ protected string $source_dir;
+
/**
* @throws FileSystemException
* @throws RuntimeException
@@ -30,6 +37,18 @@ class Extension
if (PHP_OS_FAMILY === 'Windows' && $unix_only) {
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Windows platform");
}
+ // set source_dir for builtin
+ if ($ext_type === 'builtin') {
+ $this->source_dir = SOURCE_PATH . '/php-src/ext/' . $this->name;
+ } else {
+ $source = Config::getExt($this->name, 'source');
+ if ($source === null) {
+ throw new RuntimeException("{$ext_type} extension {$name} source not found");
+ }
+ $source_path = Config::getSource($source)['path'] ?? null;
+ $source_path = $source_path === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $source_path;
+ $this->source_dir = $source_path;
+ }
}
/**
@@ -132,7 +151,7 @@ class Extension
// Windows is not supported yet
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '';
}
@@ -167,6 +186,21 @@ class Extension
return false;
}
+ /**
+ * Run shared extension check when cli is enabled
+ * @throws RuntimeException
+ */
+ public function runSharedExtensionCheckUnix(): void
+ {
+ [$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -d "extension=' . BUILD_LIB_PATH . '/' . $this->getName() . '.so" --ri ' . $this->getName());
+ if ($ret !== 0) {
+ throw new RuntimeException($this->getName() . '.so failed to load');
+ }
+ if ($this->isBuildStatic()) {
+ logger()->warning($this->getName() . '.so test succeeded, but has little significance since it is also compiled in statically.');
+ }
+ }
+
/**
* @throws RuntimeException
*/
@@ -231,6 +265,53 @@ class Extension
// do nothing, just throw wrong usage exception if not valid
}
+ /**
+ * Build shared extension
+ *
+ * @throws WrongUsageException
+ * @throws RuntimeException
+ */
+ public function buildShared(): void
+ {
+ match (PHP_OS_FAMILY) {
+ 'Darwin', 'Linux' => $this->buildUnixShared(),
+ default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'),
+ };
+ }
+
+ /**
+ * Build shared extension for Unix
+ *
+ * @throws FileSystemException
+ * @throws RuntimeException
+ * @throws WrongUsageException
+ * @throws \ReflectionException
+ * @throws \Throwable
+ */
+ public function buildUnixShared(): void
+ {
+ $config = (new SPCConfigUtil($this->builder))->config([$this->getName()]);
+ $env = [
+ 'CFLAGS' => $config['cflags'],
+ 'LDFLAGS' => $config['ldflags'],
+ 'LIBS' => $config['libs'],
+ ];
+ // prepare configure args
+ shell()->cd($this->source_dir)
+ ->setEnv($env)
+ ->execWithEnv(BUILD_BIN_PATH . '/phpize')
+ ->execWithEnv('./configure ' . $this->getUnixConfigureArg(true) . ' --with-php-config=' . BUILD_BIN_PATH . '/php-config --enable-shared --disable-static')
+ ->execWithEnv('make clean')
+ ->execWithEnv('make -j' . $this->builder->concurrency);
+
+ // copy shared library
+ copy($this->source_dir . '/modules/' . $this->getDistName() . '.so', BUILD_LIB_PATH . '/' . $this->getDistName() . '.so');
+ // check shared extension with php-cli
+ if (file_exists(BUILD_BIN_PATH . '/php')) {
+ $this->runSharedExtensionCheckUnix();
+ }
+ }
+
/**
* Get current extension version
*
@@ -241,6 +322,32 @@ class Extension
return null;
}
+ public function setBuildStatic(): void
+ {
+ if (!in_array('static', Config::getExtTarget($this->name))) {
+ throw new WrongUsageException("Extension [{$this->name}] does not support static build!");
+ }
+ $this->build_static = true;
+ }
+
+ public function setBuildShared(): void
+ {
+ if (!in_array('shared', Config::getExtTarget($this->name))) {
+ throw new WrongUsageException("Extension [{$this->name}] does not support shared build!");
+ }
+ $this->build_shared = true;
+ }
+
+ public function isBuildShared(): bool
+ {
+ return $this->build_shared;
+ }
+
+ public function isBuildStatic(): bool
+ {
+ return $this->build_static;
+ }
+
/**
* @throws RuntimeException
*/
diff --git a/src/SPC/builder/extension/amqp.php b/src/SPC/builder/extension/amqp.php
index d795f47d..8fbfea24 100644
--- a/src/SPC/builder/extension/amqp.php
+++ b/src/SPC/builder/extension/amqp.php
@@ -23,7 +23,7 @@ class amqp extends Extension
return false;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--with-amqp --with-librabbitmq-dir=' . BUILD_ROOT_PATH;
}
diff --git a/src/SPC/builder/extension/dba.php b/src/SPC/builder/extension/dba.php
index 011e4746..bd7388f3 100644
--- a/src/SPC/builder/extension/dba.php
+++ b/src/SPC/builder/extension/dba.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('dba')]
class dba extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . BUILD_ROOT_PATH) : '';
return '--enable-dba' . $qdbm;
diff --git a/src/SPC/builder/extension/enchant.php b/src/SPC/builder/extension/enchant.php
index 26578519..c3046409 100644
--- a/src/SPC/builder/extension/enchant.php
+++ b/src/SPC/builder/extension/enchant.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('enchant')]
class enchant extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$glibs = [
'/Users/jerry/project/git-project/static-php-cli/buildroot/lib/libgio-2.0.a',
diff --git a/src/SPC/builder/extension/event.php b/src/SPC/builder/extension/event.php
index a1cc7bcc..95d9aba6 100644
--- a/src/SPC/builder/extension/event.php
+++ b/src/SPC/builder/extension/event.php
@@ -13,7 +13,7 @@ use SPC\util\CustomExt;
#[CustomExt('event')]
class event extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--with-event-core --with-event-extra --with-event-libevent-dir=' . BUILD_ROOT_PATH;
if ($this->builder->getLib('openssl')) {
diff --git a/src/SPC/builder/extension/ffi.php b/src/SPC/builder/extension/ffi.php
index 6f294481..51c3efac 100644
--- a/src/SPC/builder/extension/ffi.php
+++ b/src/SPC/builder/extension/ffi.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('ffi')]
class ffi extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--with-ffi --enable-zend-signals';
}
diff --git a/src/SPC/builder/extension/gd.php b/src/SPC/builder/extension/gd.php
index 707a898f..f872733b 100644
--- a/src/SPC/builder/extension/gd.php
+++ b/src/SPC/builder/extension/gd.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('gd')]
class gd extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--enable-gd';
$arg .= $this->builder->getLib('freetype') ? ' --with-freetype' : '';
diff --git a/src/SPC/builder/extension/glfw.php b/src/SPC/builder/extension/glfw.php
index c9d0c4f7..444b5d93 100644
--- a/src/SPC/builder/extension/glfw.php
+++ b/src/SPC/builder/extension/glfw.php
@@ -30,7 +30,7 @@ class glfw extends Extension
return true;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH;
}
diff --git a/src/SPC/builder/extension/grpc.php b/src/SPC/builder/extension/grpc.php
index a63bad9d..852f2933 100644
--- a/src/SPC/builder/extension/grpc.php
+++ b/src/SPC/builder/extension/grpc.php
@@ -44,7 +44,7 @@ class grpc extends Extension
return true;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH;
}
diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php
index c3a96c3b..a4cb9386 100644
--- a/src/SPC/builder/extension/imagick.php
+++ b/src/SPC/builder/extension/imagick.php
@@ -21,7 +21,7 @@ class imagick extends Extension
return true;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$disable_omp = getenv('SPC_LIBC') === 'musl' ? '' : ' ac_cv_func_omp_pause_resource_all=no';
return '--with-imagick=' . BUILD_ROOT_PATH . $disable_omp;
diff --git a/src/SPC/builder/extension/imap.php b/src/SPC/builder/extension/imap.php
index 3aa2c284..9cc9a87f 100644
--- a/src/SPC/builder/extension/imap.php
+++ b/src/SPC/builder/extension/imap.php
@@ -33,7 +33,7 @@ class imap extends Extension
}
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--with-imap=' . BUILD_ROOT_PATH;
if ($this->builder->getLib('openssl') !== null) {
diff --git a/src/SPC/builder/extension/memcache.php b/src/SPC/builder/extension/memcache.php
index eb3e2969..4625cae2 100644
--- a/src/SPC/builder/extension/memcache.php
+++ b/src/SPC/builder/extension/memcache.php
@@ -12,7 +12,7 @@ use SPC\util\CustomExt;
#[CustomExt('memcache')]
class memcache extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH;
}
diff --git a/src/SPC/builder/extension/memcached.php b/src/SPC/builder/extension/memcached.php
index 1ef679d9..9c433954 100644
--- a/src/SPC/builder/extension/memcached.php
+++ b/src/SPC/builder/extension/memcached.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('memcached')]
class memcached extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$rootdir = BUILD_ROOT_PATH;
$zlib_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : "--with-zlib-dir={$rootdir}";
diff --git a/src/SPC/builder/extension/mongodb.php b/src/SPC/builder/extension/mongodb.php
index 2096de05..7ba1ea33 100644
--- a/src/SPC/builder/extension/mongodb.php
+++ b/src/SPC/builder/extension/mongodb.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('mongodb')]
class mongodb extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = ' --enable-mongodb ';
$arg .= ' --with-mongodb-system-libs=no --with-mongodb-client-side-encryption=no ';
diff --git a/src/SPC/builder/extension/odbc.php b/src/SPC/builder/extension/odbc.php
index 6234f4e8..ac5c3e8f 100644
--- a/src/SPC/builder/extension/odbc.php
+++ b/src/SPC/builder/extension/odbc.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('odbc')]
class odbc extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--with-unixODBC=' . BUILD_ROOT_PATH;
}
diff --git a/src/SPC/builder/extension/opcache.php b/src/SPC/builder/extension/opcache.php
index 5276261a..5d9dda0a 100644
--- a/src/SPC/builder/extension/opcache.php
+++ b/src/SPC/builder/extension/opcache.php
@@ -42,7 +42,7 @@ class opcache extends Extension
return file_put_contents(SOURCE_PATH . '/php-src/.opcache_patched', '1') !== false;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-opcache';
}
diff --git a/src/SPC/builder/extension/openssl.php b/src/SPC/builder/extension/openssl.php
index 2576b0b2..add1aede 100644
--- a/src/SPC/builder/extension/openssl.php
+++ b/src/SPC/builder/extension/openssl.php
@@ -23,7 +23,7 @@ class openssl extends Extension
return false;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$openssl_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-openssl-dir=' . BUILD_ROOT_PATH;
return '--with-openssl=' . BUILD_ROOT_PATH . $openssl_dir;
diff --git a/src/SPC/builder/extension/pdo_odbc.php b/src/SPC/builder/extension/pdo_odbc.php
index 4ab45904..e436f381 100644
--- a/src/SPC/builder/extension/pdo_odbc.php
+++ b/src/SPC/builder/extension/pdo_odbc.php
@@ -17,7 +17,7 @@ class pdo_odbc extends Extension
return true;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--with-pdo-odbc=unixODBC,' . BUILD_ROOT_PATH;
}
diff --git a/src/SPC/builder/extension/pgsql.php b/src/SPC/builder/extension/pgsql.php
index 74085b21..1c63f163 100644
--- a/src/SPC/builder/extension/pgsql.php
+++ b/src/SPC/builder/extension/pgsql.php
@@ -33,7 +33,7 @@ class pgsql extends Extension
* @throws WrongUsageException
* @throws RuntimeException
*/
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
if ($this->builder->getPHPVersionID() >= 80400) {
return '--with-pgsql PGSQL_CFLAGS=-I' . BUILD_INCLUDE_PATH . ' PGSQL_LIBS="-L' . BUILD_LIB_PATH . ' -lpq -lpgport -lpgcommon"';
diff --git a/src/SPC/builder/extension/redis.php b/src/SPC/builder/extension/redis.php
index bffc768e..0b60075c 100644
--- a/src/SPC/builder/extension/redis.php
+++ b/src/SPC/builder/extension/redis.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('redis')]
class redis extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--enable-redis';
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';
diff --git a/src/SPC/builder/extension/snappy.php b/src/SPC/builder/extension/snappy.php
index 2dc2a33a..896a9be2 100644
--- a/src/SPC/builder/extension/snappy.php
+++ b/src/SPC/builder/extension/snappy.php
@@ -26,7 +26,7 @@ class snappy extends Extension
return true;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-snappy --with-snappy-includedir="' . BUILD_ROOT_PATH . '"';
}
diff --git a/src/SPC/builder/extension/spx.php b/src/SPC/builder/extension/spx.php
index 6c2a6a19..dc341e39 100644
--- a/src/SPC/builder/extension/spx.php
+++ b/src/SPC/builder/extension/spx.php
@@ -21,7 +21,7 @@ class spx extends Extension
}
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--enable-spx';
if ($this->builder->getExt('zlib') === null) {
diff --git a/src/SPC/builder/extension/swoole.php b/src/SPC/builder/extension/swoole.php
index 78aaf339..6ce006b9 100644
--- a/src/SPC/builder/extension/swoole.php
+++ b/src/SPC/builder/extension/swoole.php
@@ -35,7 +35,7 @@ class swoole extends Extension
return null;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
// enable swoole
$arg = '--enable-swoole';
@@ -49,7 +49,9 @@ class swoole extends Extension
// additional feature: c-ares, brotli, nghttp2 (can be disabled, but we enable it by default in config to support full network feature)
$arg .= $this->builder->getLib('libcares') ? ' --enable-cares' : '';
- $arg .= $this->builder->getLib('brotli') ? (' --with-brotli-dir=' . BUILD_ROOT_PATH) : '';
+ if (!$shared) {
+ $arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : '';
+ }
$arg .= $this->builder->getLib('nghttp2') ? (' --with-nghttp2-dir=' . BUILD_ROOT_PATH) : '';
// additional feature: swoole-pgsql, it should depend on lib [postgresql], but it will lack of CFLAGS etc.
diff --git a/src/SPC/builder/extension/swoole_hook_mysql.php b/src/SPC/builder/extension/swoole_hook_mysql.php
index 6d1d6828..e9684872 100644
--- a/src/SPC/builder/extension/swoole_hook_mysql.php
+++ b/src/SPC/builder/extension/swoole_hook_mysql.php
@@ -16,7 +16,7 @@ class swoole_hook_mysql extends Extension
return 'swoole';
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
// pdo_mysql doesn't need to be disabled
// enable swoole-hook-mysql will enable mysqli, pdo, pdo_mysql, we don't need to add any additional options
diff --git a/src/SPC/builder/extension/swoole_hook_pgsql.php b/src/SPC/builder/extension/swoole_hook_pgsql.php
index 08e217be..113b8eb6 100644
--- a/src/SPC/builder/extension/swoole_hook_pgsql.php
+++ b/src/SPC/builder/extension/swoole_hook_pgsql.php
@@ -25,7 +25,7 @@ class swoole_hook_pgsql extends Extension
}
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
// enable swoole pgsql hook
return '--enable-swoole-pgsql';
diff --git a/src/SPC/builder/extension/swoole_hook_sqlite.php b/src/SPC/builder/extension/swoole_hook_sqlite.php
index 5de18351..7948dd29 100644
--- a/src/SPC/builder/extension/swoole_hook_sqlite.php
+++ b/src/SPC/builder/extension/swoole_hook_sqlite.php
@@ -25,7 +25,7 @@ class swoole_hook_sqlite extends Extension
}
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
// enable swoole pgsql hook
return '--enable-swoole-sqlite';
diff --git a/src/SPC/builder/extension/xdebug.php b/src/SPC/builder/extension/xdebug.php
new file mode 100644
index 00000000..ed592056
--- /dev/null
+++ b/src/SPC/builder/extension/xdebug.php
@@ -0,0 +1,21 @@
+execWithResult(BUILD_BIN_PATH . '/php -n -d "zend_extension=' . BUILD_LIB_PATH . '/xdebug.so" --ri xdebug');
+ if ($ret !== 0) {
+ throw new RuntimeException('xdebug.so failed to load.');
+ }
+ }
+}
diff --git a/src/SPC/builder/extension/xlswriter.php b/src/SPC/builder/extension/xlswriter.php
index 475a6c07..6319ce31 100644
--- a/src/SPC/builder/extension/xlswriter.php
+++ b/src/SPC/builder/extension/xlswriter.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('xlswriter')]
class xlswriter extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--with-xlswriter --enable-reader';
if ($this->builder->getLib('openssl')) {
diff --git a/src/SPC/builder/extension/xml.php b/src/SPC/builder/extension/xml.php
index 6242be6b..fc8bfc4d 100644
--- a/src/SPC/builder/extension/xml.php
+++ b/src/SPC/builder/extension/xml.php
@@ -20,7 +20,7 @@ class xml extends Extension
/**
* @throws RuntimeException
*/
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$arg = match ($this->name) {
'xml' => '--enable-xml',
diff --git a/src/SPC/builder/extension/yac.php b/src/SPC/builder/extension/yac.php
index a4152e42..bb085133 100644
--- a/src/SPC/builder/extension/yac.php
+++ b/src/SPC/builder/extension/yac.php
@@ -19,7 +19,7 @@ class yac extends Extension
return true;
}
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-yac --enable-igbinary --enable-json';
}
diff --git a/src/SPC/builder/extension/zlib.php b/src/SPC/builder/extension/zlib.php
index a9932999..86c45dbe 100644
--- a/src/SPC/builder/extension/zlib.php
+++ b/src/SPC/builder/extension/zlib.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('zlib')]
class zlib extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
$zlib_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-zlib-dir=' . BUILD_ROOT_PATH;
return '--with-zlib' . $zlib_dir;
diff --git a/src/SPC/builder/extension/zstd.php b/src/SPC/builder/extension/zstd.php
index 13311979..3a6df602 100644
--- a/src/SPC/builder/extension/zstd.php
+++ b/src/SPC/builder/extension/zstd.php
@@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('zstd')]
class zstd extends Extension
{
- public function getUnixConfigureArg(): string
+ public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
}
diff --git a/src/SPC/builder/freebsd/BSDBuilder.php b/src/SPC/builder/freebsd/BSDBuilder.php
index c1d41118..b2e246b3 100644
--- a/src/SPC/builder/freebsd/BSDBuilder.php
+++ b/src/SPC/builder/freebsd/BSDBuilder.php
@@ -118,7 +118,7 @@ class BSDBuilder extends UnixBuilderBase
$config_file_scan_dir .
$json_74 .
$zts .
- $this->makeExtensionArgs()
+ $this->makeStaticExtensionArgs()
);
$this->emitPatchPoint('before-php-make');
diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php
index cd48728f..e38714f1 100644
--- a/src/SPC/builder/linux/LinuxBuilder.php
+++ b/src/SPC/builder/linux/LinuxBuilder.php
@@ -182,7 +182,7 @@ class LinuxBuilder extends UnixBuilderBase
$json_74 .
$zts .
$maxExecutionTimers .
- $this->makeExtensionArgs() .
+ $this->makeStaticExtensionArgs() .
' ' . $envs_build_php . ' '
);
@@ -311,15 +311,7 @@ class LinuxBuilder extends UnixBuilderBase
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
- FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'");
- FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#');
- $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config');
- str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str);
- // move mimalloc to the beginning of libs
- $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str);
- // move lstdc++ to the end of libs
- $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
- FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
+ $this->patchPhpScripts();
}
private function getMakeExtraVars(): array
diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php
index f1022661..9f9ad684 100644
--- a/src/SPC/builder/macos/MacOSBuilder.php
+++ b/src/SPC/builder/macos/MacOSBuilder.php
@@ -176,7 +176,7 @@ class MacOSBuilder extends UnixBuilderBase
$config_file_scan_dir .
$json_74 .
$zts .
- $this->makeExtensionArgs() . ' ' .
+ $this->makeStaticExtensionArgs() . ' ' .
$envs_build_php
);
@@ -300,13 +300,7 @@ class MacOSBuilder extends UnixBuilderBase
->exec('rm ' . BUILD_ROOT_PATH . '/lib/libphp.a')
->exec('ar rcs ' . BUILD_ROOT_PATH . '/lib/libphp.a *.o')
->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o');
- FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'");
- FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#');
- $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config');
- str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str);
- // move mimalloc to the beginning of libs
- $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str);
- FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
+ $this->patchPhpScripts();
}
private function getMakeExtraVars(): array
diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php
index ed07a725..4c8df333 100644
--- a/src/SPC/builder/unix/UnixBuilderBase.php
+++ b/src/SPC/builder/unix/UnixBuilderBase.php
@@ -146,7 +146,7 @@ abstract class UnixBuilderBase extends BuilderBase
throw new RuntimeException("cli failed sanity check: ret[{$ret}]. out[{$raw_output}]");
}
- foreach ($this->exts as $ext) {
+ foreach ($this->getExts(false) as $ext) {
logger()->debug('testing ext: ' . $ext->getName());
$ext->runCliCheckUnix();
}
@@ -238,4 +238,29 @@ abstract class UnixBuilderBase extends BuilderBase
logger()->info('cleaning up');
shell()->cd(SOURCE_PATH . '/php-src')->exec('make clean');
}
+
+ /**
+ * Patch phpize and php-config if needed
+ * @throws FileSystemException
+ */
+ protected function patchPhpScripts(): void
+ {
+ // patch phpize
+ if (file_exists(BUILD_BIN_PATH . '/phpize')) {
+ 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#');
+ }
+ // patch php-config
+ if (file_exists(BUILD_BIN_PATH . '/php-config')) {
+ logger()->debug('Patching php-config prefix and libs order');
+ $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config');
+ $php_config_str = str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str);
+ // move mimalloc to the beginning of libs
+ $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str);
+ // move lstdc++ to the end of libs
+ $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
+ FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
+ }
+ }
}
diff --git a/src/SPC/builder/unix/library/libcares.php b/src/SPC/builder/unix/library/libcares.php
index 076a10c9..d99cc685 100644
--- a/src/SPC/builder/unix/library/libcares.php
+++ b/src/SPC/builder/unix/library/libcares.php
@@ -26,9 +26,9 @@ trait libcares
{
shell()->cd($this->source_dir)
->setEnv(['CFLAGS' => $this->getLibExtraCFlags(), 'LDFLAGS' => $this->getLibExtraLdFlags(), 'LIBS' => $this->getLibExtraLibs()])
- ->execWithEnv('./configure --prefix= --enable-static --disable-shared --disable-tests')
+ ->execWithEnv('./configure --prefix= --enable-static --disable-shared --disable-tests --with-pic')
->execWithEnv("make -j {$this->builder->concurrency}")
- ->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
+ ->execWithEnv('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libcares.pc'], PKGCONF_PATCH_PREFIX);
}
diff --git a/src/SPC/builder/windows/WindowsBuilder.php b/src/SPC/builder/windows/WindowsBuilder.php
index fb7eaa0f..7a604ca8 100644
--- a/src/SPC/builder/windows/WindowsBuilder.php
+++ b/src/SPC/builder/windows/WindowsBuilder.php
@@ -119,7 +119,7 @@ class WindowsBuilder extends BuilderBase
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
$config_file_scan_dir .
- "{$this->makeExtensionArgs()} " .
+ "{$this->makeStaticExtensionArgs()} " .
$zts .
'"'
);
@@ -286,7 +286,7 @@ class WindowsBuilder extends BuilderBase
throw new RuntimeException('cli failed sanity check');
}
- foreach ($this->exts as $ext) {
+ foreach ($this->getExts(false) as $ext) {
logger()->debug('testing ext: ' . $ext->getName());
$ext->runCliCheckWindows();
}
diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php
index 3688c249..a1610cae 100644
--- a/src/SPC/command/BaseCommand.php
+++ b/src/SPC/command/BaseCommand.php
@@ -165,7 +165,7 @@ abstract class BaseCommand extends Command
return SPC_EXTENSION_ALIAS[$lower];
}
return $lower;
- }, is_array($ext_list) ? $ext_list : explode(',', $ext_list));
+ }, is_array($ext_list) ? $ext_list : array_filter(explode(',', $ext_list)));
// filter internals
return array_values(array_filter($ls, function ($x) {
diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php
index 6bafce54..49067b0b 100644
--- a/src/SPC/command/BuildPHPCommand.php
+++ b/src/SPC/command/BuildPHPCommand.php
@@ -27,6 +27,7 @@ class BuildPHPCommand extends BuildCommand
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
+ $this->addOption('build-shared', 'D', InputOption::VALUE_REQUIRED, 'Shared extensions to build, comma separated', '');
$this->addOption('build-micro', null, null, 'Build micro SAPI');
$this->addOption('build-cli', null, null, 'Build cli SAPI');
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
@@ -52,13 +53,31 @@ class BuildPHPCommand extends BuildCommand
// transform string to array
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs'))));
// transform string to array
- $extensions = $this->parseExtensionList($this->getArgument('extensions'));
+ $shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
+ // transform string to array
+ $static_extensions = $this->parseExtensionList($this->getArgument('extensions'));
// parse rule with options
- $rule = $this->parseRules();
+ $rule = $this->parseRules($shared_extensions);
+
+ // check dynamic extension build env
+ // macOS must use --no-strip option
+ if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Darwin' && !$this->getOption('no-strip')) {
+ $this->output->writeln('MacOS does not support dynamic extension loading with stripped binary, please use --no-strip option!');
+ return static::FAILURE;
+ }
+ // linux must build with glibc
+ if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') {
+ $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!');
+ return static::FAILURE;
+ }
+ $static_and_shared = array_intersect($static_extensions, $shared_extensions);
+ if (!empty($static_and_shared)) {
+ $this->output->writeln('Building extensions [' . implode(',', $static_and_shared) . '] as both static and shared\, tests may not be accurate or fail.');
+ }
if ($rule === BUILD_TARGET_NONE) {
- $this->output->writeln('Please add at least one build target!');
+ $this->output->writeln('Please add at least one build SAPI!');
$this->output->writeln("\t--build-cli\tBuild php-cli SAPI");
$this->output->writeln("\t--build-micro\tBuild phpmicro SAPI");
$this->output->writeln("\t--build-fpm\tBuild php-fpm SAPI");
@@ -107,18 +126,26 @@ class BuildPHPCommand extends BuildCommand
$builder = BuilderProvider::makeBuilderByInput($this->input);
$include_suggest_ext = $this->getOption('with-suggested-exts');
$include_suggest_lib = $this->getOption('with-suggested-libs');
- [$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
+ [$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs(array_merge($static_extensions, $shared_extensions), $libraries, $include_suggest_ext, $include_suggest_lib);
$display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package']));
+ $display_extensions = array_map(fn ($ext) => in_array($ext, $shared_extensions) ? "*{$ext}" : $ext, $extensions);
+
+ // separate static and shared extensions from $extensions
+ // filter rule: including shared extensions if they are in $static_extensions or $shared_extensions
+ $static_extensions = array_filter($extensions, fn ($ext) => !in_array($ext, $shared_extensions) || in_array($ext, $static_extensions));
// print info
$indent_texts = [
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
'Build SAPI' => $builder->getBuildTypeName($rule),
- 'Extensions (' . count($extensions) . ')' => implode(',', $extensions),
+ 'Extensions (' . count($extensions) . ')' => implode(',', $display_extensions),
'Libraries (' . count($libraries) . ')' => implode(',', $display_libs),
'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes',
'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no',
];
+ if (!empty($shared_extensions) || ($rule & BUILD_TARGET_EMBED)) {
+ $indent_texts['Build Dev'] = 'yes';
+ }
if (!empty($this->input->getOption('with-config-file-path'))) {
$indent_texts['Config File Path'] = $this->input->getOption('with-config-file-path');
}
@@ -152,8 +179,8 @@ class BuildPHPCommand extends BuildCommand
// compile libraries
$builder->proveLibs($libraries);
// check extensions
- $builder->proveExts($extensions);
- // validate libs and exts
+ $builder->proveExts($static_extensions, $shared_extensions);
+ // validate libs and extensions
$builder->validateLibsAndExts();
// clean builds and sources
@@ -183,6 +210,12 @@ class BuildPHPCommand extends BuildCommand
// start to build
$builder->buildPHP($rule);
+ // build dynamic extensions if needed
+ if (!empty($shared_extensions)) {
+ logger()->info('Building shared extensions ...');
+ $builder->buildSharedExts();
+ }
+
// compile stopwatch :P
$time = round(microtime(true) - START_TIME, 3);
logger()->info('');
@@ -211,6 +244,12 @@ class BuildPHPCommand extends BuildCommand
$path = FileSystem::convertPath("{$build_root_path}/bin/php-fpm");
logger()->info("Static php-fpm binary path{$fixed}: {$path}");
}
+ if (!empty($shared_extensions)) {
+ foreach ($shared_extensions as $ext) {
+ $path = FileSystem::convertPath("{$build_root_path}/lib/{$ext}.so");
+ logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}");
+ }
+ }
// export metadata
file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
@@ -239,13 +278,13 @@ class BuildPHPCommand extends BuildCommand
/**
* Parse build options to rule int.
*/
- private function parseRules(): int
+ private function parseRules(array $shared_extensions = []): int
{
$rule = BUILD_TARGET_NONE;
$rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
- $rule |= ($this->getOption('build-embed') ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE);
+ $rule |= ($this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
return $rule;
}
diff --git a/src/SPC/command/SPCConfigCommand.php b/src/SPC/command/SPCConfigCommand.php
index 0377ace9..d3c7a0b2 100644
--- a/src/SPC/command/SPCConfigCommand.php
+++ b/src/SPC/command/SPCConfigCommand.php
@@ -37,7 +37,7 @@ class SPCConfigCommand extends BuildCommand
$include_suggest_ext = $this->getOption('with-suggested-exts');
$include_suggest_lib = $this->getOption('with-suggested-libs');
- $util = new SPCConfigUtil(null, $this->input);
+ $util = new SPCConfigUtil();
$config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
if ($this->getOption('includes')) {
diff --git a/src/SPC/command/dev/ExtVerCommand.php b/src/SPC/command/dev/ExtVerCommand.php
index 0f08fa9a..3154a6bb 100644
--- a/src/SPC/command/dev/ExtVerCommand.php
+++ b/src/SPC/command/dev/ExtVerCommand.php
@@ -6,7 +6,6 @@ namespace SPC\command\dev;
use SPC\builder\BuilderProvider;
use SPC\command\BaseCommand;
-use SPC\store\Config;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -31,8 +30,7 @@ class ExtVerCommand extends BaseCommand
// Get lib object
$builder = BuilderProvider::makeBuilderByInput($this->input);
- $ext_conf = Config::getExt($this->getArgument('extension'));
- $builder->proveExts([$this->getArgument('extension')], true);
+ $builder->proveExts([$this->getArgument('extension')], [], true);
// Check whether lib is extracted
// if (!is_dir(SOURCE_PATH . '/' . $this->getArgument('library'))) {
diff --git a/src/SPC/store/Config.php b/src/SPC/store/Config.php
index 68ba4399..16f925c6 100644
--- a/src/SPC/store/Config.php
+++ b/src/SPC/store/Config.php
@@ -125,6 +125,21 @@ class Config
return self::$lib;
}
+ /**
+ * @throws WrongUsageException
+ * @throws FileSystemException
+ */
+ public static function getExtTarget(string $name): ?array
+ {
+ if (self::$ext === null) {
+ self::$ext = FileSystem::loadConfigArray('ext');
+ }
+ if (!isset(self::$ext[$name])) {
+ throw new WrongUsageException('ext [' . $name . '] is not supported yet');
+ }
+ return self::$ext[$name]['target'] ?? ['static', 'shared'];
+ }
+
/**
* @throws FileSystemException
* @throws WrongUsageException
diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php
index d3ffc302..2db3498e 100644
--- a/src/SPC/store/SourcePatcher.php
+++ b/src/SPC/store/SourcePatcher.php
@@ -43,7 +43,7 @@ class SourcePatcher
*/
public static function patchBeforeBuildconf(BuilderBase $builder): void
{
- foreach ($builder->getExts() as $ext) {
+ foreach ($builder->getExts(false) as $ext) {
if ($ext->patchBeforeBuildconf() === true) {
logger()->info('Extension [' . $ext->getName() . '] patched before buildconf');
}
@@ -86,7 +86,7 @@ class SourcePatcher
*/
public static function patchBeforeConfigure(BuilderBase $builder): void
{
- foreach ($builder->getExts() as $ext) {
+ foreach ($builder->getExts(false) as $ext) {
if ($ext->patchBeforeConfigure() === true) {
logger()->info('Extension [' . $ext->getName() . '] patched before configure');
}
@@ -253,7 +253,7 @@ class SourcePatcher
// }
// call extension patch before make
- foreach ($builder->getExts() as $ext) {
+ foreach ($builder->getExts(false) as $ext) {
if ($ext->patchBeforeMake() === true) {
logger()->info('Extension [' . $ext->getName() . '] patched before make');
}
diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php
index 7988244f..8ffd5d05 100644
--- a/src/SPC/util/GlobalEnvManager.php
+++ b/src/SPC/util/GlobalEnvManager.php
@@ -40,9 +40,6 @@ class GlobalEnvManager
self::putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
- if ($builder instanceof BuilderBase) {
- self::putenv('SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS=' . ($builder->getOption('no-strip') ? '-g -O0' : '-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'));
- }
}
// Define env vars for linux
diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php
index 8f676d3f..e0b92a2e 100644
--- a/src/SPC/util/SPCConfigUtil.php
+++ b/src/SPC/util/SPCConfigUtil.php
@@ -7,26 +7,51 @@ namespace SPC\util;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
use SPC\builder\macos\MacOSBuilder;
+use SPC\exception\FileSystemException;
+use SPC\exception\RuntimeException;
+use SPC\exception\WrongUsageException;
use SPC\store\Config;
use Symfony\Component\Console\Input\ArgvInput;
-use Symfony\Component\Console\Input\InputInterface;
class SPCConfigUtil
{
- public function __construct(private ?BuilderBase $builder = null, ?InputInterface $input = null)
+ private ?BuilderBase $builder = null;
+
+ public function __construct(?BuilderBase $builder = null)
{
- if ($builder === null) {
- $this->builder = BuilderProvider::makeBuilderByInput($input ?? new ArgvInput());
+ if ($builder !== null) {
+ $this->builder = $builder; // BuilderProvider::makeBuilderByInput($input ?? new ArgvInput());
}
}
+ /**
+ * Generate configuration for building PHP extensions.
+ *
+ * @param array $extensions Extension name list
+ * @param array $libraries Additional library name list
+ * @param bool $include_suggest_ext Include suggested extensions
+ * @param bool $include_suggest_lib Include suggested libraries
+ * @return array{
+ * cflags: string,
+ * ldflags: string,
+ * libs: string
+ * }
+ * @throws \ReflectionException
+ * @throws FileSystemException
+ * @throws RuntimeException
+ * @throws WrongUsageException
+ * @throws \Throwable
+ */
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);
ob_start();
- $this->builder->proveLibs($libraries);
- $this->builder->proveExts($extensions);
+ if ($this->builder === null) {
+ $this->builder = BuilderProvider::makeBuilderByInput(new ArgvInput());
+ $this->builder->proveLibs($libraries);
+ $this->builder->proveExts($extensions, skip_extract: true);
+ }
ob_get_clean();
$ldflags = $this->getLdflagsString();
$libs = $this->getLibsString($libraries);
@@ -47,9 +72,9 @@ class SPCConfigUtil
$libs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $libs);
}
return [
- 'cflags' => $cflags,
- 'ldflags' => $ldflags,
- 'libs' => $libs,
+ 'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags),
+ 'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags),
+ 'libs' => trim(getenv('LIBS') . ' ' . $libs),
];
}
diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php
index e0904d40..1553443e 100644
--- a/src/globals/test-extensions.php
+++ b/src/globals/test-extensions.php
@@ -13,9 +13,9 @@ declare(strict_types=1);
// test php version (8.1 ~ 8.4 available, multiple for matrix)
$test_php_version = [
- // '8.1',
- // '8.2',
- // '8.3',
+ '8.1',
+ '8.2',
+ '8.3',
'8.4',
];
@@ -25,12 +25,13 @@ $test_os = [
'macos-14',
'ubuntu-latest',
'ubuntu-22.04',
+ 'ubuntu-24.04',
'ubuntu-22.04-arm',
'ubuntu-24.04-arm',
];
// whether enable thread safe
-$zts = false;
+$zts = true;
$no_strip = false;
@@ -42,10 +43,16 @@ $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' => 'imagick',
+ 'Linux', 'Darwin' => 'phar',
'Windows' => 'pgsql,pdo_pgsql',
};
+// If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`).
+$shared_extensions = match (PHP_OS_FAMILY) {
+ 'Linux' => 'xdebug',
+ 'Windows', 'Darwin' => '',
+};
+
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
$with_libs = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => '',
@@ -56,7 +63,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' => 'common',
'Windows' => 'none',
};
@@ -87,6 +94,7 @@ if (!isset($argv[1])) {
$trim_value = "\r\n \t,";
$final_extensions = trim(trim($extensions, $trim_value) . ',' . _getCombination($base_combination), $trim_value);
+$download_extensions = trim($final_extensions . ',' . $shared_extensions, $trim_value);
$final_libs = trim($with_libs, $trim_value);
if (PHP_OS_FAMILY === 'Windows') {
@@ -107,7 +115,7 @@ function quote2(string $param): string
// generate download command
if ($argv[1] === 'download_cmd') {
$down_cmd = 'download ';
- $down_cmd .= '--for-extensions=' . quote2($final_extensions) . ' ';
+ $down_cmd .= '--for-extensions=' . quote2($download_extensions) . ' ';
$down_cmd .= '--for-libs=' . quote2($final_libs) . ' ';
$down_cmd .= '--with-php=' . quote2($argv[3]) . ' ';
$down_cmd .= '--ignore-cache-sources=php-src ';
@@ -124,10 +132,39 @@ if ($argv[1] === 'install_upx_cmd') {
$install_upx_cmd = 'install-pkg upx';
}
+$prefix = match ($argv[2] ?? null) {
+ 'windows-latest', 'windows-2022', 'windows-2019', 'windows-2025' => 'powershell.exe -file .\bin\spc.ps1 ',
+ 'ubuntu-latest' => 'bin/spc-alpine-docker ',
+ 'ubuntu-24.04', 'ubuntu-24.04-arm' => './bin/spc ',
+ 'ubuntu-22.04', 'ubuntu-22.04-arm' => 'bin/spc-gnu-docker ',
+ default => 'bin/spc ',
+};
+
+// shared_extension build
+if ($shared_extensions) {
+ switch ($argv[2] ?? null) {
+ case 'ubuntu-22.04':
+ case 'ubuntu-22.04-arm':
+ $shared_cmd = ' --build-shared=' . quote2($shared_extensions) . ' ';
+ break;
+ case 'macos-13':
+ case 'macos-14':
+ $shared_cmd = ' --build-shared=' . quote2($shared_extensions) . ' ';
+ $no_strip = true;
+ break;
+ default:
+ $shared_cmd = '';
+ break;
+ }
+} else {
+ $shared_cmd = '';
+}
+
// generate build command
if ($argv[1] === 'build_cmd' || $argv[1] === 'build_embed_cmd') {
$build_cmd = 'build ';
$build_cmd .= quote2($final_extensions) . ' ';
+ $build_cmd .= $shared_cmd;
$build_cmd .= $zts ? '--enable-zts ' : '';
$build_cmd .= $no_strip ? '--no-strip ' : '';
$build_cmd .= $upx ? '--with-upx-pack ' : '';
@@ -155,31 +192,25 @@ echo match ($argv[1]) {
default => '',
};
-$prefix = match ($argv[2] ?? null) {
- 'windows-latest', 'windows-2022', 'windows-2019', 'windows-2025' => 'powershell.exe -file .\bin\spc.ps1 ',
- 'ubuntu-latest', 'ubuntu-24.04', 'ubuntu-24.04-arm' => './bin/spc ',
- 'ubuntu-22.04', 'ubuntu-22.04-arm' => 'bin/spc-gnu-docker ',
- 'ubuntu-20.04' => 'bin/spc-alpine-docker ',
- default => 'bin/spc ',
-};
-
-if ($argv[1] === 'download_cmd') {
- passthru($prefix . $down_cmd, $retcode);
-} elseif ($argv[1] === 'build_cmd') {
- passthru($prefix . $build_cmd . ' --build-cli --build-micro', $retcode);
-} elseif ($argv[1] === 'build_embed_cmd') {
- if (str_starts_with($argv[2], 'windows-')) {
- // windows does not accept embed SAPI
- passthru($prefix . $build_cmd . ' --build-cli', $retcode);
- } else {
- passthru($prefix . $build_cmd . ' --build-embed', $retcode);
- }
-} elseif ($argv[1] === 'doctor_cmd') {
- passthru($prefix . $doctor_cmd, $retcode);
-} elseif ($argv[1] === 'install_upx_cmd') {
- passthru($prefix . $install_upx_cmd, $retcode);
-} else {
- $retcode = 0;
+switch ($argv[1] ?? null) {
+ case 'download_cmd':
+ passthru($prefix . $down_cmd, $retcode);
+ break;
+ case 'build_cmd':
+ passthru($prefix . $build_cmd . ' --build-cli --build-micro', $retcode);
+ break;
+ case 'build_embed_cmd':
+ passthru($prefix . $build_cmd . (str_starts_with($argv[2], 'windows-') ? ' --build-cli' : ' --build-embed'), $retcode);
+ break;
+ case 'doctor_cmd':
+ passthru($prefix . $doctor_cmd, $retcode);
+ break;
+ case 'install_upx_cmd':
+ passthru($prefix . $install_upx_cmd, $retcode);
+ break;
+ default:
+ $retcode = 0;
+ break;
}
exit($retcode);
diff --git a/tests/SPC/builder/BuilderTest.php b/tests/SPC/builder/BuilderTest.php
index 5796f5df..17f1e799 100644
--- a/tests/SPC/builder/BuilderTest.php
+++ b/tests/SPC/builder/BuilderTest.php
@@ -72,7 +72,7 @@ class BuilderTest extends TestCase
public function testMakeExtensionArgs()
{
- $this->assertStringContainsString('--enable-mbstring', $this->builder->makeExtensionArgs());
+ $this->assertStringContainsString('--enable-mbstring', $this->builder->makeStaticExtensionArgs());
}
public function testIsLibsOnly()