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()