From 12aadf18cc2a7b30a2f993896c9e2d5555e29bef Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 16:36:05 +0800 Subject: [PATCH 01/30] refactor: replace SPC_LIBC with SPC_TARGET and update related logic --- .github/workflows/tests.yml | 2 +- bin/build-static-frankenphp | 4 +- bin/spc-gnu-docker | 4 +- config/env.ini | 18 +++- docs/en/guide/extension-notes.md | 2 +- docs/zh/guide/extension-notes.md | 2 +- src/SPC/ConsoleApplication.php | 2 + src/SPC/builder/extension/imagick.php | 7 +- src/SPC/builder/linux/LinuxBuilder.php | 16 +--- src/SPC/builder/linux/SystemUtil.php | 5 +- src/SPC/builder/linux/library/icu.php | 3 +- src/SPC/builder/unix/UnixBuilderBase.php | 5 +- src/SPC/builder/unix/library/imagemagick.php | 11 ++- src/SPC/builder/unix/library/ldap.php | 3 +- src/SPC/builder/unix/library/libxslt.php | 8 +- src/SPC/builder/unix/library/mimalloc.php | 3 +- src/SPC/builder/unix/library/pkgconfig.php | 4 +- src/SPC/builder/unix/library/postgresql.php | 3 +- src/SPC/command/BuildPHPCommand.php | 5 +- src/SPC/command/DownloadCommand.php | 7 +- src/SPC/command/dev/EnvCommand.php | 37 +++++++++ src/SPC/command/dev/PackLibCommand.php | 9 +- src/SPC/doctor/CheckListHandler.php | 8 ++ src/SPC/doctor/OptionalCheck.php | 11 +++ src/SPC/doctor/item/LinuxMuslCheck.php | 22 ++--- src/SPC/store/Downloader.php | 12 ++- src/SPC/util/GlobalEnvManager.php | 48 ++++++----- src/SPC/util/SPCConfigUtil.php | 4 +- src/SPC/util/SPCTarget.php | 83 +++++++++++++++++++ .../util/toolchain/ClangNativeToolchain.php | 33 ++++++++ src/SPC/util/toolchain/GccNativeToolchain.php | 32 +++++++ src/SPC/util/toolchain/MSVCToolchain.php | 12 +++ src/SPC/util/toolchain/MuslToolchain.php | 44 ++++++++++ src/SPC/util/toolchain/ToolchainInterface.php | 18 ++++ src/SPC/util/toolchain/ZigToolchain.php | 18 ++++ 35 files changed, 420 insertions(+), 85 deletions(-) create mode 100644 src/SPC/command/dev/EnvCommand.php create mode 100644 src/SPC/doctor/OptionalCheck.php create mode 100644 src/SPC/util/SPCTarget.php create mode 100644 src/SPC/util/toolchain/ClangNativeToolchain.php create mode 100644 src/SPC/util/toolchain/GccNativeToolchain.php create mode 100644 src/SPC/util/toolchain/MSVCToolchain.php create mode 100644 src/SPC/util/toolchain/MuslToolchain.php create mode 100644 src/SPC/util/toolchain/ToolchainInterface.php create mode 100644 src/SPC/util/toolchain/ZigToolchain.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5ed6d59d..ee4a17fb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -105,7 +105,7 @@ jobs: run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - name: "Run PHPUnit Tests" - run: SPC_LIBC=glibc vendor/bin/phpunit tests/ --no-coverage + run: SPC_TARGET=glibc vendor/bin/phpunit tests/ --no-coverage define-matrix: name: "Define Matrix" diff --git a/bin/build-static-frankenphp b/bin/build-static-frankenphp index 10a693a7..bc5cdb33 100755 --- a/bin/build-static-frankenphp +++ b/bin/build-static-frankenphp @@ -92,7 +92,7 @@ ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative ENV PATH="/app/bin:/cmake/bin:/usr/local/go/bin:$PATH" -ENV SPC_LIBC=glibc +ENV SPC_TARGET=glibc ADD ./config/env.ini /app/config/env.ini RUN bin/spc doctor --auto-fix --debug @@ -146,7 +146,7 @@ echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env echo 'SPC_DEFAULT_C_FLAGS=-fPIE -fPIC' >> /tmp/spc-gnu-docker.env -echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env +echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 6cc36bfa..523905c5 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -108,7 +108,7 @@ ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime RUN /app/bin/php /app/bin/composer install --no-dev ENV PATH="/app/bin:/cmake/bin:$PATH" -ENV SPC_LIBC=glibc +ENV SPC_TARGET=glibc ADD ./config/env.ini /app/config/env.ini RUN CC=gcc bin/spc doctor --auto-fix --debug @@ -159,7 +159,7 @@ echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env echo 'SPC_DEFAULT_C_FLAGS=-fPIC' >> /tmp/spc-gnu-docker.env -echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env +echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env diff --git a/config/env.ini b/config/env.ini index d80bc3e7..a8e7a8a6 100644 --- a/config/env.ini +++ b/config/env.ini @@ -55,6 +55,8 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd ; EXTENSION_DIR= [windows] +; build target: win7-static +SPC_TARGET=msvc-static ; php-sdk-binary-tools path PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" ; upx executable path @@ -63,8 +65,17 @@ UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe" SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static [linux] -; include PATH for musl libc. -SPC_LIBC=musl +; Linux can use different build toolchain, but the toolchain can not be changed in this file: +; - musl (default): used for general linux distros, can build `musl-static` target only. +; - zig (WIP): used for general linux distros, can build `musl` and `glibc` targets. +; - musl-native: used for alpine linux, can build `musl-static` and `musl`(WIP) target. +; - gnu-native (assume): used for general linux distros, can build `glibc` target only and have portability issues. + +; build target: +; - musl-static (default): pure static linking, using musl-libc, can run on any linux distro. +; - musl: static linking with dynamic linking to musl-libc, can run on musl-based linux distro. +; - glibc: static linking with dynamic linking to glibc, can run on glibc-based linux distro. +SPC_TARGET=musl-static ; compiler environments CC=${SPC_LINUX_DEFAULT_CC} CXX=${SPC_LINUX_DEFAULT_CXX} @@ -109,6 +120,9 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" [macos] +; build target: macho or macho (possibly we could support macho-universal in the future) +; Currently we do not support universal and cross-compilation for macOS. +SPC_TARGET=macho ; compiler environments CC=clang CXX=clang++ diff --git a/docs/en/guide/extension-notes.md b/docs/en/guide/extension-notes.md index aa11c377..5a022b59 100644 --- a/docs/en/guide/extension-notes.md +++ b/docs/en/guide/extension-notes.md @@ -82,7 +82,7 @@ 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. +1. Xdebug is only buildable as a shared extension. You need to use a build target other than `musl-static` for SPC_TARGET. 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`. diff --git a/docs/zh/guide/extension-notes.md b/docs/zh/guide/extension-notes.md index 10e62eb0..ad787dd9 100644 --- a/docs/zh/guide/extension-notes.md +++ b/docs/zh/guide/extension-notes.md @@ -76,7 +76,7 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli ## xdebug -1. Xdebug 只能作为共享扩展进行构建。在 Linux 上,您需要使用 static-php-cli 并设置 SPC_LIBC=glibc。 +1. Xdebug 只能作为共享扩展进行构建。您需要使用除了 `musl-static` 外的其他 `SPC_TARGET` 构建目标。 2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。 编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`。 diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index b69dcf08..a24eada9 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -9,6 +9,7 @@ use SPC\command\BuildPHPCommand; use SPC\command\CraftCommand; use SPC\command\DeleteDownloadCommand; use SPC\command\dev\AllExtCommand; +use SPC\command\dev\EnvCommand; use SPC\command\dev\ExtVerCommand; use SPC\command\dev\GenerateExtDepDocsCommand; use SPC\command\dev\GenerateExtDocCommand; @@ -70,6 +71,7 @@ final class ConsoleApplication extends Application new GenerateExtDepDocsCommand(), new GenerateLibDepDocsCommand(), new PackLibCommand(), + new EnvCommand(), ] ); } diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index 7951ea69..bcdbffab 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -6,6 +6,7 @@ namespace SPC\builder\extension; use SPC\builder\Extension; use SPC\util\CustomExt; +use SPC\util\SPCTarget; #[CustomExt('imagick')] class imagick extends Extension @@ -15,7 +16,7 @@ class imagick extends Extension if (PHP_OS_FAMILY !== 'Linux') { return false; } - if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) { + if (SPCTarget::isTarget(SPCTarget::GLIBC)) { return false; } // imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp @@ -26,7 +27,7 @@ class imagick extends Extension public function getUnixConfigureArg(bool $shared = false): string { - $disable_omp = !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) ? '' : ' ac_cv_func_omp_pause_resource_all=no'; + $disable_omp = SPCTarget::isTarget(SPCTarget::GLIBC) ? ' ac_cv_func_omp_pause_resource_all=no' : ''; return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; } @@ -34,7 +35,7 @@ class imagick extends Extension { // on centos 7, it will use the symbol _ZTINSt6thread6_StateE, which is not defined in system libstdc++.so.6 [$static, $shared] = parent::getStaticAndSharedLibs(); - if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) { + if (SPCTarget::isTarget(SPCTarget::GLIBC)) { $static .= ' -lstdc++'; $shared = str_replace('-lstdc++', '', $shared); } diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 68548615..94d8a41e 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -25,22 +25,8 @@ class LinuxBuilder extends UnixBuilderBase { $this->options = $options; - // check musl-cross make installed if we use musl-cross-make - $arch = arch2gnu(php_uname('m')); - GlobalEnvManager::init(); - - if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) { - $this->setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\""); - $this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\""); - $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE'); - $configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure; - GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$configure}"); - - if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) { - throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)'); - } - } + GlobalEnvManager::afterInit(); // concurrency $this->concurrency = intval(getenv('SPC_CONCURRENCY')); diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php index a85f3dec..1e405768 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -6,6 +6,7 @@ namespace SPC\builder\linux; use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\RuntimeException; +use SPC\util\SPCTarget; class SystemUtil { @@ -193,7 +194,7 @@ class SystemUtil if (self::$libc_version !== null) { return self::$libc_version; } - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') { + if (SPCTarget::isTarget(SPCTarget::GLIBC)) { $result = shell()->execWithResult('ldd --version', false); if ($result[0] !== 0) { return null; @@ -208,7 +209,7 @@ class SystemUtil } return null; } - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { + if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { if (self::isMuslDist()) { $result = shell()->execWithResult('ldd 2>&1', false); } else { diff --git a/src/SPC/builder/linux/library/icu.php b/src/SPC/builder/linux/library/icu.php index aa2825b7..6fb7e1fc 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\linux\library; use SPC\store\FileSystem; +use SPC\util\SPCTarget; class icu extends LinuxLibraryBase { @@ -16,7 +17,7 @@ class icu extends LinuxLibraryBase { $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"'; $cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"'; - $ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : ''; + $ldflags = SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? 'LDFLAGS="-static"' : ''; shell()->cd($this->source_dir . '/source')->initializeEnv($this) ->exec( "{$cppflags} {$cxxflags} {$ldflags} " . diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index 0d56b0eb..886eec68 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -17,6 +17,7 @@ use SPC\store\Downloader; use SPC\store\FileSystem; use SPC\util\DependencyUtil; use SPC\util\SPCConfigUtil; +use SPC\util\SPCTarget; abstract class UnixBuilderBase extends BuilderBase { @@ -200,7 +201,7 @@ abstract class UnixBuilderBase extends BuilderBase $util = new SPCConfigUtil($this); $config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}"; - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { + if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { $lens .= ' -static'; } [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); @@ -334,7 +335,7 @@ abstract class UnixBuilderBase extends BuilderBase $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $extLdFlags = "-extldflags '-pie'"; $muslTags = ''; - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { + if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; $muslTags = 'static_build,'; } diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index ead786a2..88ae9f7d 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -4,12 +4,11 @@ declare(strict_types=1); namespace SPC\builder\unix\library; -use SPC\builder\linux\library\LinuxLibraryBase; -use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; use SPC\util\executor\UnixAutoconfExecutor; +use SPC\util\SPCTarget; trait imagemagick { @@ -33,16 +32,16 @@ trait imagemagick ->optionalLib('bzip2', ...ac_with_args('bzlib')) ->addConfigureArgs( // TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so - getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10') ? '--disable-openmp' : '--enable-openmp', + SPCTarget::isTarget(SPCTarget::GLIBC) ? '--disable-openmp' : '--enable-openmp', '--without-jxl', '--without-x', ); - // special: linux musl needs `-static` - $ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl'; + // special: linux musl-static needs `-static` + $ldflags = !SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? ('-static -ldl') : '-ldl'; // special: macOS needs -iconv - $libs = $this instanceof MacOSLibraryBase ? '-liconv' : ''; + $libs = SPCTarget::isTarget(SPCTarget::MACHO) ? '-liconv' : ''; $ac->appendEnv([ 'LDFLAGS' => $ldflags, diff --git a/src/SPC/builder/unix/library/ldap.php b/src/SPC/builder/unix/library/ldap.php index 62f7bcd7..e87310cb 100644 --- a/src/SPC/builder/unix/library/ldap.php +++ b/src/SPC/builder/unix/library/ldap.php @@ -6,12 +6,13 @@ namespace SPC\builder\unix\library; use SPC\store\FileSystem; use SPC\util\executor\UnixAutoconfExecutor; +use SPC\util\SPCTarget; trait ldap { public function patchBeforeBuild(): bool { - $extra = getenv('SPC_LIBC') === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : ''; + $extra = SPCTarget::isTarget(SPCTarget::GLIBC) ? '-ldl -lpthread -lm -lresolv -lutil' : ''; FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra); return true; } diff --git a/src/SPC/builder/unix/library/libxslt.php b/src/SPC/builder/unix/library/libxslt.php index c820f8f6..aabc277d 100644 --- a/src/SPC/builder/unix/library/libxslt.php +++ b/src/SPC/builder/unix/library/libxslt.php @@ -33,7 +33,13 @@ trait libxslt '--without-debugger', "--with-libxml-prefix={$this->getBuildRootPath()}", ); - $ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make(); + if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) { + $ac->appendEnv([ + 'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'), + 'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'), + ]); + } + $ac->configure()->make(); $this->patchPkgconfPrefix(['libexslt.pc']); $this->patchLaDependencyPrefix(); diff --git a/src/SPC/builder/unix/library/mimalloc.php b/src/SPC/builder/unix/library/mimalloc.php index 78868856..0808516c 100644 --- a/src/SPC/builder/unix/library/mimalloc.php +++ b/src/SPC/builder/unix/library/mimalloc.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\util\executor\UnixCMakeExecutor; +use SPC\util\SPCTarget; trait mimalloc { @@ -15,7 +16,7 @@ trait mimalloc '-DMI_BUILD_SHARED=OFF', '-DMI_INSTALL_TOPLEVEL=ON' ); - if (getenv('SPC_LIBC') === 'musl') { + if (SPCTarget::isTarget(SPCTarget::MUSL) || SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { $cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON'); } $cmake->build(); diff --git a/src/SPC/builder/unix/library/pkgconfig.php b/src/SPC/builder/unix/library/pkgconfig.php index 05727f96..3522c52a 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -4,8 +4,8 @@ declare(strict_types=1); namespace SPC\builder\unix\library; -use SPC\builder\linux\library\LinuxLibraryBase; use SPC\util\executor\UnixAutoconfExecutor; +use SPC\util\SPCTarget; trait pkgconfig { @@ -14,7 +14,7 @@ trait pkgconfig UnixAutoconfExecutor::create($this) ->appendEnv([ 'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '', - 'LDFLAGS' => !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--static', + 'LDFLAGS' => SPCTarget::isStaticTarget() ? '--static' : '', ]) ->configure( '--with-internal-glib', diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php index 1de3e4c2..efcfc089 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\SPCTarget; trait postgresql { @@ -50,7 +51,7 @@ trait postgresql $error_exec_cnt += $output[0] === 0 ? 0 : 1; if (!empty($output[1][0])) { $ldflags = $output[1][0]; - $envs .= !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" "; + $envs .= SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" "; } $output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}"); $error_exec_cnt += $output[0] === 0 ? 0 : 1; diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index 21a848a5..f9938957 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -13,6 +13,7 @@ use SPC\store\SourcePatcher; use SPC\util\DependencyUtil; use SPC\util\GlobalEnvManager; use SPC\util\LicenseDumper; +use SPC\util\SPCTarget; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -63,7 +64,7 @@ class BuildPHPCommand extends BuildCommand // check dynamic extension build env // linux must build with glibc - if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') { + if (!empty($shared_extensions) && SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!'); return static::FAILURE; } @@ -133,6 +134,8 @@ class BuildPHPCommand extends BuildCommand // print info $indent_texts = [ 'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')', + 'Build Target' => getenv('SPC_TARGET'), + 'Build Toolchain' => getenv('SPC_TOOLCHAIN'), 'Build SAPI' => $builder->getBuildTypeName($rule), 'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions), 'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions), diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index d5054aa9..35529f5e 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -14,6 +14,7 @@ use SPC\store\Config; use SPC\store\Downloader; use SPC\store\LockFile; use SPC\util\DependencyUtil; +use SPC\util\SPCTarget; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -223,7 +224,11 @@ class DownloadCommand extends BaseCommand '{name}' => $source, '{arch}' => arch2gnu(php_uname('m')), '{os}' => strtolower(PHP_OS_FAMILY), - '{libc}' => getenv('SPC_LIBC') ?: 'default', + '{libc}' => match (getenv('SPC_TARGET')) { + SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl', + SPCTarget::GLIBC => 'glibc', + default => 'default', + }, '{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', ]; $find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern')); diff --git a/src/SPC/command/dev/EnvCommand.php b/src/SPC/command/dev/EnvCommand.php new file mode 100644 index 00000000..d5a0cec3 --- /dev/null +++ b/src/SPC/command/dev/EnvCommand.php @@ -0,0 +1,37 @@ +addArgument('env', InputArgument::REQUIRED, 'The environment variable to show, if not set, all will be shown'); + } + + public function initialize(InputInterface $input, OutputInterface $output): void + { + $this->no_motd = true; + parent::initialize($input, $output); + } + + public function handle(): int + { + $env = $this->getArgument('env'); + if (($val = getenv($env)) === false) { + $this->output->writeln("Environment variable '{$env}' is not set."); + return static::FAILURE; + } + $this->output->writeln("{$val}"); + return static::SUCCESS; + } +} diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php index d0d9797e..29a22353 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -16,6 +16,7 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\LockFile; use SPC\util\DependencyUtil; +use SPC\util\SPCTarget; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; @@ -76,8 +77,12 @@ class PackLibCommand extends BuildCommand '{name}' => $lib->getName(), '{arch}' => arch2gnu(php_uname('m')), '{os}' => strtolower(PHP_OS_FAMILY), - '{libc}' => getenv('SPC_LIBC') ?: 'default', - '{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', + '{libc}' => match (getenv('SPC_TARGET')) { + SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl', + SPCTarget::GLIBC => 'glibc', + default => 'default', + }, + '{libcver}' => SystemUtil::getLibcVersionIfExists() ?? 'default', ]; // detect suffix, for proper tar option $tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern')); diff --git a/src/SPC/doctor/CheckListHandler.php b/src/SPC/doctor/CheckListHandler.php index 23005dfd..cac17ce3 100644 --- a/src/SPC/doctor/CheckListHandler.php +++ b/src/SPC/doctor/CheckListHandler.php @@ -72,6 +72,14 @@ final class CheckListHandler { foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) { $ref = new \ReflectionClass($class); + $optional = $ref->getAttributes(OptionalCheck::class)[0] ?? null; + if ($optional !== null) { + /** @var OptionalCheck $instance */ + $instance = $optional->newInstance(); + if (is_callable($instance->check) && !call_user_func($instance->check)) { + continue; // skip this class if optional check is false + } + } foreach ($ref->getMethods() as $method) { foreach ($method->getAttributes() as $a) { if (is_a($a->getName(), AsCheckItem::class, true)) { diff --git a/src/SPC/doctor/OptionalCheck.php b/src/SPC/doctor/OptionalCheck.php new file mode 100644 index 00000000..4dae938b --- /dev/null +++ b/src/SPC/doctor/OptionalCheck.php @@ -0,0 +1,11 @@ + 'musl', + SPCTarget::GLIBC => 'glibc', + default => 'default', + }; + $libc_version = SystemUtil::getLibcVersionIfExists() ?? 'default'; + + return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}"; } /** diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index fa5c9cf5..a526dd5a 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -40,24 +40,6 @@ class GlobalEnvManager self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig'); } - // Define env vars for linux - if (PHP_OS_FAMILY === 'Linux') { - $arch = getenv('GNU_ARCH'); - if (SystemUtil::isMuslDist() || getenv('SPC_LIBC') === 'glibc') { - self::putenv('SPC_LINUX_DEFAULT_CC=gcc'); - self::putenv('SPC_LINUX_DEFAULT_CXX=g++'); - self::putenv('SPC_LINUX_DEFAULT_AR=ar'); - self::putenv('SPC_LINUX_DEFAULT_LD=ld.gold'); - } else { - self::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc"); - self::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++"); - self::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar"); - self::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld"); - self::addPathIfNotExists('/usr/local/musl/bin'); - self::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin"); - } - } - $ini = self::readIniFile(); $default_put_list = []; @@ -80,6 +62,29 @@ class GlobalEnvManager self::putenv("{$k}={$v}"); } } + + // deprecated: convert SPC_LIBC to SPC_TARGET + if (getenv('SPC_LIBC') !== false) { + logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.'); + $target = match (getenv('SPC_LIBC')) { + 'musl' => SPCTarget::MUSL_STATIC, + default => SPCTarget::GLIBC, + }; + self::putenv("SPC_TARGET={$target}"); + self::putenv('SPC_LIBC'); + } + + // auto-select toolchain based on target and OS temporarily + // TODO: use 'zig' instead of 'gcc-native' when ZigToolchain is implemented + $toolchain = match (getenv('SPC_TARGET')) { + SPCTarget::MUSL_STATIC, SPCTarget::MUSL => SystemUtil::isMuslDist() ? 'gcc-native' : 'musl', + SPCTarget::MACHO => 'clang-native', + SPCTarget::MSVC_STATIC => 'msvc', + default => 'gcc-native', + }; + + SPCTarget::initTargetForToolchain($toolchain); + // apply second time $ini2 = self::readIniFile(); @@ -108,13 +113,18 @@ class GlobalEnvManager self::$env_cache[] = $val; } - private static function addPathIfNotExists(string $path): void + public static function addPathIfNotExists(string $path): void { if (is_unix() && !str_contains(getenv('PATH'), $path)) { self::putenv("PATH={$path}:" . getenv('PATH')); } } + public static function afterInit(): void + { + SPCTarget::afterInitTargetForToolchain(); + } + /** * @throws WrongUsageException */ diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index 8e69e89c..bf441efe 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -55,7 +55,7 @@ class SPCConfigUtil ob_get_clean(); $ldflags = $this->getLdflagsString(); $libs = $this->getLibsString($libraries, $with_dependencies); - if (PHP_OS_FAMILY === 'Darwin') { + if (SPCTarget::isTarget(SPCTarget::MACHO)) { $libs .= " {$this->getFrameworksString($extensions)}"; } $cflags = $this->getIncludesString(); @@ -146,7 +146,7 @@ class SPCConfigUtil } } // patch: imagick (imagemagick wrapper) for linux needs libgomp - if (in_array('imagemagick', $libraries) && PHP_OS_FAMILY === 'Linux' && !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10'))) { + if (in_array('imagemagick', $libraries) && !SPCTarget::isTarget(SPCTarget::GLIBC)) { $short_name[] = '-lgomp'; } return implode(' ', $short_name); diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php new file mode 100644 index 00000000..2d81fc4e --- /dev/null +++ b/src/SPC/util/SPCTarget.php @@ -0,0 +1,83 @@ + MuslToolchain::class, + 'gcc-native' => GccNativeToolchain::class, + 'clang-native' => ClangNativeToolchain::class, + 'msvc' => MSVCToolchain::class, + 'zig' => ZigToolchain::class, + ]; + + public static function isTarget(string $target): bool + { + $env = getenv('SPC_TARGET'); + if ($env === false) { + return false; + } + $env = strtolower($env); + return $env === $target; + } + + public static function isStaticTarget(): bool + { + $env = getenv('SPC_TARGET'); + if ($env === false) { + return false; + } + $env = strtolower($env); + return str_ends_with($env, '-static') || $env === self::MUSL_STATIC; + } + + public static function initTargetForToolchain(string $toolchain): void + { + $target = getenv('SPC_TARGET'); + $toolchain = strtolower($toolchain); + if (isset(self::TOOLCHAIN_LIST[$toolchain])) { + $toolchainClass = self::TOOLCHAIN_LIST[$toolchain]; + /* @var ToolchainInterface $toolchainClass */ + (new $toolchainClass())->initEnv($target); + } + GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}"); + } + + public static function afterInitTargetForToolchain() + { + if (!getenv('SPC_TOOLCHAIN')) { + throw new WrongUsageException('SPC_TOOLCHAIN not set'); + } + $toolchain = getenv('SPC_TOOLCHAIN'); + if (!isset(self::TOOLCHAIN_LIST[$toolchain])) { + throw new WrongUsageException("Unknown toolchain: {$toolchain}"); + } + $toolchainClass = self::TOOLCHAIN_LIST[$toolchain]; + (new $toolchainClass())->afterInit(getenv('SPC_TARGET')); + } +} diff --git a/src/SPC/util/toolchain/ClangNativeToolchain.php b/src/SPC/util/toolchain/ClangNativeToolchain.php new file mode 100644 index 00000000..863636ec --- /dev/null +++ b/src/SPC/util/toolchain/ClangNativeToolchain.php @@ -0,0 +1,33 @@ + LinuxSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or manually set CC/CXX to a valid path.'), + 'Darwin' => MacOSSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or set CC/CXX to a valid path.'), + 'BSD' => FreeBSDSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or set CC/CXX to a valid path.'), + default => throw new WrongUsageException('Clang is not supported on ' . PHP_OS_FAMILY . '.'), + }; + } +} diff --git a/src/SPC/util/toolchain/GccNativeToolchain.php b/src/SPC/util/toolchain/GccNativeToolchain.php new file mode 100644 index 00000000..c845d11d --- /dev/null +++ b/src/SPC/util/toolchain/GccNativeToolchain.php @@ -0,0 +1,32 @@ + LinuxSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'), + 'Darwin' => MacOSSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'), + 'BSD' => FreeBSDSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'), + default => throw new \RuntimeException('GCC is not supported on ' . PHP_OS_FAMILY . '.'), + }; + } +} diff --git a/src/SPC/util/toolchain/MSVCToolchain.php b/src/SPC/util/toolchain/MSVCToolchain.php new file mode 100644 index 00000000..2ec8111c --- /dev/null +++ b/src/SPC/util/toolchain/MSVCToolchain.php @@ -0,0 +1,12 @@ + Date: Sat, 28 Jun 2025 16:39:02 +0800 Subject: [PATCH 02/30] extension test --- src/globals/test-extensions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index 70758f85..a1cef3ff 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -23,13 +23,13 @@ $test_php_version = [ $test_os = [ // 'macos-13', // 'macos-14', - // 'macos-15', - // 'ubuntu-latest', - // 'ubuntu-22.04', - // 'ubuntu-24.04', + 'macos-15', + 'ubuntu-latest', + 'ubuntu-22.04', + 'ubuntu-24.04', // 'ubuntu-22.04-arm', // 'ubuntu-24.04-arm', - 'windows-latest', + // 'windows-latest', ]; // whether enable thread safe @@ -72,7 +72,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', }; From 604131b31db33602291b35b47eadf529d412a715 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 16:45:20 +0800 Subject: [PATCH 03/30] Use own exception --- src/SPC/util/toolchain/GccNativeToolchain.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/SPC/util/toolchain/GccNativeToolchain.php b/src/SPC/util/toolchain/GccNativeToolchain.php index c845d11d..b9882a9a 100644 --- a/src/SPC/util/toolchain/GccNativeToolchain.php +++ b/src/SPC/util/toolchain/GccNativeToolchain.php @@ -7,6 +7,7 @@ namespace SPC\util\toolchain; use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; +use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; class GccNativeToolchain implements ToolchainInterface @@ -23,9 +24,9 @@ class GccNativeToolchain implements ToolchainInterface { // check gcc exists match (PHP_OS_FAMILY) { - 'Linux' => LinuxSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'), - 'Darwin' => MacOSSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'), - 'BSD' => FreeBSDSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'), + 'Linux' => LinuxSystemUtil::findCommand('g++') ?? throw new WrongUsageException('g++ not found, please install it or set CC/CXX to a valid path.'), + 'Darwin' => MacOSSystemUtil::findCommand('g++') ?? throw new WrongUsageException('g++ not found, please install it or set CC/CXX to a valid path.'), + 'BSD' => FreeBSDSystemUtil::findCommand('g++') ?? throw new WrongUsageException('g++ not found, please install it or set CC/CXX to a valid path.'), default => throw new \RuntimeException('GCC is not supported on ' . PHP_OS_FAMILY . '.'), }; } From 25c2def710bba4310ab62269e502241e96430664 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 16:47:19 +0800 Subject: [PATCH 04/30] Remove unnecessary SPC_MICRO_PATCHES --- config/env.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/env.ini b/config/env.ini index d80bc3e7..60f4a6d4 100644 --- a/config/env.ini +++ b/config/env.ini @@ -78,7 +78,7 @@ SPC_EXTRA_LIBS= ; upx executable path UPX_EXEC=${PKG_ROOT_PATH}/bin/upx ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches -SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream +SPC_MICRO_PATCHES=cli_checks,disable_huge_page ; *** default build command for building php *** ; buildconf command @@ -118,7 +118,7 @@ SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_EXTRA_LIBS= ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches -SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv +SPC_MICRO_PATCHES=cli_checks,macos_iconv ; *** default build command for building php *** ; buildconf command From ad080da02669d972b4a0dda38466a0661d0db884 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 17:06:51 +0800 Subject: [PATCH 05/30] Refactor micro patching logic in SourcePatcher --- src/SPC/builder/BuilderBase.php | 3 +++ src/SPC/store/SourcePatcher.php | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 88e827c5..184d88dc 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -14,6 +14,7 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\LockFile; use SPC\store\SourceManager; +use SPC\store\SourcePatcher; use SPC\util\CustomExt; abstract class BuilderBase @@ -203,6 +204,8 @@ abstract class BuilderBase $this->emitPatchPoint('before-exts-extract'); SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]); $this->emitPatchPoint('after-exts-extract'); + // patch micro + SourcePatcher::patchMicro(); } foreach ([...$static_extensions, ...$shared_extensions] as $extension) { diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index 69d3a649..5ea10577 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -17,7 +17,6 @@ class SourcePatcher public static function init(): void { // FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']); - FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']); FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']); FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']); FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']); @@ -105,7 +104,7 @@ class SourcePatcher * @throws RuntimeException * @throws FileSystemException */ - public static function patchMicro(string $name = '', string $target = '', ?array $items = null): bool + public static function patchMicro(?array $items = null): bool { if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) { return false; @@ -152,11 +151,7 @@ class SourcePatcher foreach ($patches as $patch) { logger()->info("Patching micro with {$patch}"); - $patchesStr = str_replace('/', DIRECTORY_SEPARATOR, $patch); - f_passthru( - 'cd ' . SOURCE_PATH . '/php-src && ' . - (PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1 ' - ); + self::patchFile(SOURCE_PATH . "/php-src{$patch}", SOURCE_PATH . '/php-src'); } return true; @@ -165,24 +160,29 @@ class SourcePatcher /** * Use existing patch file for patching * - * @param string $patch_name Patch file name in src/globals/patch/ + * @param string $patch_name Patch file name in src/globals/patch/ or absolute path * @param string $cwd Working directory for patch command * @param bool $reverse Reverse patches (default: False) * @throws RuntimeException */ public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool { - if (!file_exists(ROOT_DIR . "/src/globals/patch/{$patch_name}")) { + if (FileSystem::isRelativePath($patch_name)) { + $patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}"; + } else { + $patch_file = $patch_name; + } + if (!file_exists($patch_file)) { return false; } - $patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}"; - $patch_str = str_replace('/', DIRECTORY_SEPARATOR, $patch_file); + $patch_str = FileSystem::convertPath($patch_file); // Copy patch from phar - if (\Phar::running() !== '') { - file_put_contents(SOURCE_PATH . '/' . $patch_name, file_get_contents($patch_file)); - $patch_str = str_replace('/', DIRECTORY_SEPARATOR, SOURCE_PATH . '/' . $patch_name); + if (str_starts_with($patch_str, 'phar://')) { + $filename = pathinfo($patch_file, PATHINFO_BASENAME); + file_put_contents(SOURCE_PATH . "{$filename}", file_get_contents($patch_file)); + $patch_str = FileSystem::convertPath(SOURCE_PATH . "/{$filename}"); } // detect From 3357f286ab0d20bafcaa6547d5b81ddaf843d520 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 17:13:22 +0800 Subject: [PATCH 06/30] Remove libgomp deps --- src/SPC/builder/extension/imagick.php | 16 +--------------- src/SPC/util/SPCConfigUtil.php | 4 ---- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index bcdbffab..8332f832 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -11,23 +11,9 @@ use SPC\util\SPCTarget; #[CustomExt('imagick')] class imagick extends Extension { - public function patchBeforeMake(): bool - { - if (PHP_OS_FAMILY !== 'Linux') { - return false; - } - if (SPCTarget::isTarget(SPCTarget::GLIBC)) { - return false; - } - // imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp - $extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lgomp'); - f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); - return true; - } - public function getUnixConfigureArg(bool $shared = false): string { - $disable_omp = SPCTarget::isTarget(SPCTarget::GLIBC) ? ' ac_cv_func_omp_pause_resource_all=no' : ''; + $disable_omp = ' ac_cv_func_omp_pause_resource_all=no'; return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; } diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index bf441efe..9335e88d 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -145,10 +145,6 @@ class SPCConfigUtil } } } - // patch: imagick (imagemagick wrapper) for linux needs libgomp - if (in_array('imagemagick', $libraries) && !SPCTarget::isTarget(SPCTarget::GLIBC)) { - $short_name[] = '-lgomp'; - } return implode(' ', $short_name); } From 90823e3b17f23503378d6e5f68b4f25f9e924062 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 17:28:02 +0800 Subject: [PATCH 07/30] Use explict glibc toolchain target --- src/SPC/util/GlobalEnvManager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index a526dd5a..488a2b6e 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -80,7 +80,8 @@ class GlobalEnvManager SPCTarget::MUSL_STATIC, SPCTarget::MUSL => SystemUtil::isMuslDist() ? 'gcc-native' : 'musl', SPCTarget::MACHO => 'clang-native', SPCTarget::MSVC_STATIC => 'msvc', - default => 'gcc-native', + SPCTarget::GLIBC => !SystemUtil::isMuslDist() ? 'gcc-native' : throw new WrongUsageException('SPC_TARGET must be musl-static or musl for musl dist.'), + default => throw new WrongUsageException('Unknown SPC_TARGET: ' . getenv('SPC_TARGET')), }; SPCTarget::initTargetForToolchain($toolchain); From 88d99a7dea2eb99fcc764690c9a40e190c2479e0 Mon Sep 17 00:00:00 2001 From: Jerry Ma Date: Sat, 28 Jun 2025 18:04:45 +0800 Subject: [PATCH 08/30] Update src/SPC/store/SourcePatcher.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/SPC/store/SourcePatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index 5ea10577..029f77a8 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -181,7 +181,7 @@ class SourcePatcher // Copy patch from phar if (str_starts_with($patch_str, 'phar://')) { $filename = pathinfo($patch_file, PATHINFO_BASENAME); - file_put_contents(SOURCE_PATH . "{$filename}", file_get_contents($patch_file)); + file_put_contents(SOURCE_PATH . "/{$filename}", file_get_contents($patch_file)); $patch_str = FileSystem::convertPath(SOURCE_PATH . "/{$filename}"); } From e41d7899c7d89b393de65331224fd904b51e170d Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 18:14:10 +0800 Subject: [PATCH 09/30] Add comments, re-trigger tests --- src/globals/test-extensions.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index a1cef3ff..86b53b8d 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -21,15 +21,15 @@ $test_php_version = [ // test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available) $test_os = [ - // 'macos-13', - // 'macos-14', - 'macos-15', - 'ubuntu-latest', - 'ubuntu-22.04', - 'ubuntu-24.04', - // 'ubuntu-22.04-arm', - // 'ubuntu-24.04-arm', - // 'windows-latest', + // 'macos-13', // bin/spc for x86_64 + // 'macos-14', // bin/spc for arm64 + 'macos-15', // bin/spc for arm64 + 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 + 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 + 'ubuntu-24.04', // bin/spc for x86_64 + // 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64 + // 'ubuntu-24.04-arm', // bin/spc for arm64 + // 'windows-latest', // .\bin\spc.ps1 ]; // whether enable thread safe From 8145a7536b44881ff3afc78d5ce6da7a051c1e80 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 19:05:52 +0800 Subject: [PATCH 10/30] Fix spc-gnu-docker path --- bin/spc-gnu-docker | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 523905c5..7292bfd8 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -64,7 +64,7 @@ fi # Detect docker env is setup if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then echo "Docker container does not exist. Building docker image ..." - $DOCKER_EXECUTABLE buildx build $PLATFORM_ARG --no-cache -t cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . < /tmp/spc-gnu-docker.env -echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env -echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env -echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env -echo 'SPC_DEFAULT_C_FLAGS=-fPIC' >> /tmp/spc-gnu-docker.env +echo 'SPC_DEFAULT_C_FLAGS=-fPIC' > /tmp/spc-gnu-docker.env echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env @@ -192,5 +187,5 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then set -ex $DOCKER_EXECUTABLE run $PLATFORM_ARG --privileged --rm -it $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash else - $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@ + $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /app/bin/php bin/spc $@ fi From 454b5a77adcaca6abf008d956b4700566e9403d1 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 22:59:02 +0800 Subject: [PATCH 11/30] Add SPC_LIBC mapping to SPC_TARGET --- src/SPC/util/GlobalEnvManager.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index 488a2b6e..aaab1ae1 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -68,7 +68,9 @@ class GlobalEnvManager logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.'); $target = match (getenv('SPC_LIBC')) { 'musl' => SPCTarget::MUSL_STATIC, - default => SPCTarget::GLIBC, + 'musl-shared' => SPCTarget::MUSL, + 'glibc' => SPCTarget::GLIBC, + default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . getenv('SPC_LIBC')), }; self::putenv("SPC_TARGET={$target}"); self::putenv('SPC_LIBC'); From c23c5ae6143c862d255e71effcb8accdb7453dd8 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 23:08:17 +0800 Subject: [PATCH 12/30] Disable openmp for imagemagick --- src/SPC/builder/unix/library/imagemagick.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index 88ae9f7d..f126bf30 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -31,8 +31,7 @@ trait imagemagick ->optionalLib('freetype', ...ac_with_args('freetype')) ->optionalLib('bzip2', ...ac_with_args('bzlib')) ->addConfigureArgs( - // TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so - SPCTarget::isTarget(SPCTarget::GLIBC) ? '--disable-openmp' : '--enable-openmp', + '--disable-openmp', '--without-jxl', '--without-x', ); From a6364389baf275228498fc3cd75dd0a3bf15983d Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 23:11:26 +0800 Subject: [PATCH 13/30] Add suffix support for SPC_TARGET --- src/SPC/util/SPCTarget.php | 14 +++++++++++++- src/SPC/util/toolchain/ClangNativeToolchain.php | 5 +++++ src/SPC/util/toolchain/GccNativeToolchain.php | 5 +++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index 2d81fc4e..ec7308bf 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -43,6 +43,8 @@ class SPCTarget return false; } $env = strtolower($env); + // ver + $env = explode('@', $env)[0]; return $env === $target; } @@ -53,7 +55,9 @@ class SPCTarget return false; } $env = strtolower($env); - return str_ends_with($env, '-static') || $env === self::MUSL_STATIC; + // ver + $env = explode('@', $env)[0]; + return str_ends_with($env, '-static'); } public static function initTargetForToolchain(string $toolchain): void @@ -80,4 +84,12 @@ class SPCTarget $toolchainClass = self::TOOLCHAIN_LIST[$toolchain]; (new $toolchainClass())->afterInit(getenv('SPC_TARGET')); } + + public static function getTargetSuffix(): ?string + { + $target = getenv('SPC_TARGET'); + $target = strtolower($target); + // ver + return explode('@', $target)[1] ?? null; + } } diff --git a/src/SPC/util/toolchain/ClangNativeToolchain.php b/src/SPC/util/toolchain/ClangNativeToolchain.php index 863636ec..ac93513a 100644 --- a/src/SPC/util/toolchain/ClangNativeToolchain.php +++ b/src/SPC/util/toolchain/ClangNativeToolchain.php @@ -9,11 +9,16 @@ use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; +use SPC\util\SPCTarget; class ClangNativeToolchain implements ToolchainInterface { public function initEnv(string $target): void { + // native toolchain does not support versioning + if (SPCTarget::getTargetSuffix() !== null) { + throw new WrongUsageException('Clang native toolchain does not support versioning.'); + } GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=clang'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=clang++'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); diff --git a/src/SPC/util/toolchain/GccNativeToolchain.php b/src/SPC/util/toolchain/GccNativeToolchain.php index b9882a9a..2547334a 100644 --- a/src/SPC/util/toolchain/GccNativeToolchain.php +++ b/src/SPC/util/toolchain/GccNativeToolchain.php @@ -9,11 +9,16 @@ use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; +use SPC\util\SPCTarget; class GccNativeToolchain implements ToolchainInterface { public function initEnv(string $target): void { + // native toolchain does not support versioning + if (SPCTarget::getTargetSuffix() !== null) { + throw new WrongUsageException('gcc native toolchain does not support versioning.'); + } GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); From 0598eff9c5fce904dbb8052c79708ba4dcedefe6 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 23:13:40 +0800 Subject: [PATCH 14/30] Add suffix support for SPC_TARGET --- src/SPC/util/GlobalEnvManager.php | 2 +- src/SPC/util/SPCTarget.php | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index aaab1ae1..a6fe2987 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -78,7 +78,7 @@ class GlobalEnvManager // auto-select toolchain based on target and OS temporarily // TODO: use 'zig' instead of 'gcc-native' when ZigToolchain is implemented - $toolchain = match (getenv('SPC_TARGET')) { + $toolchain = match (SPCTarget::getTargetName()) { SPCTarget::MUSL_STATIC, SPCTarget::MUSL => SystemUtil::isMuslDist() ? 'gcc-native' : 'musl', SPCTarget::MACHO => 'clang-native', SPCTarget::MSVC_STATIC => 'msvc', diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index ec7308bf..265c3d51 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -85,6 +85,14 @@ class SPCTarget (new $toolchainClass())->afterInit(getenv('SPC_TARGET')); } + public static function getTargetName(): ?string + { + $target = getenv('SPC_TARGET'); + $target = strtolower($target); + // ver + return explode('@', $target)[0]; + } + public static function getTargetSuffix(): ?string { $target = getenv('SPC_TARGET'); From 977fbaa8ef497340d04568ebbc5be3880311bca6 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 29 Jun 2025 16:00:17 +0800 Subject: [PATCH 15/30] Suggestions --- config/env.ini | 8 +- src/SPC/builder/Extension.php | 6 + src/SPC/builder/extension/imagick.php | 12 -- src/SPC/builder/linux/SystemUtil.php | 7 +- src/SPC/builder/linux/library/icu.php | 2 +- src/SPC/builder/macos/MacOSBuilder.php | 1 + src/SPC/builder/unix/UnixBuilderBase.php | 4 +- src/SPC/builder/unix/library/imagemagick.php | 8 +- src/SPC/builder/unix/library/ldap.php | 2 +- src/SPC/builder/unix/library/mimalloc.php | 4 +- src/SPC/builder/unix/library/postgresql.php | 2 +- src/SPC/builder/windows/WindowsBuilder.php | 1 + src/SPC/command/BuildPHPCommand.php | 4 +- src/SPC/command/DownloadCommand.php | 9 +- src/SPC/command/dev/PackLibCommand.php | 9 +- src/SPC/store/Downloader.php | 9 +- src/SPC/store/SourcePatcher.php | 3 +- src/SPC/store/pkg/GoXcaddy.php | 2 - .../toolchain/ClangNativeToolchain.php | 11 +- .../toolchain/GccNativeToolchain.php | 11 +- src/SPC/toolchain/MSVCToolchain.php | 12 ++ .../{util => }/toolchain/MuslToolchain.php | 11 +- .../toolchain/ToolchainInterface.php | 6 +- src/SPC/toolchain/ToolchainManager.php | 52 +++++++ src/SPC/toolchain/ZigToolchain.php | 12 ++ src/SPC/util/GlobalEnvManager.php | 35 ++--- src/SPC/util/SPCConfigUtil.php | 2 +- src/SPC/util/SPCTarget.php | 136 ++++++++---------- src/SPC/util/toolchain/MSVCToolchain.php | 12 -- src/SPC/util/toolchain/ZigToolchain.php | 18 --- 30 files changed, 196 insertions(+), 215 deletions(-) rename src/SPC/{util => }/toolchain/ClangNativeToolchain.php (77%) rename src/SPC/{util => }/toolchain/GccNativeToolchain.php (77%) create mode 100644 src/SPC/toolchain/MSVCToolchain.php rename src/SPC/{util => }/toolchain/MuslToolchain.php (78%) rename src/SPC/{util => }/toolchain/ToolchainInterface.php (64%) create mode 100644 src/SPC/toolchain/ToolchainManager.php create mode 100644 src/SPC/toolchain/ZigToolchain.php delete mode 100644 src/SPC/util/toolchain/MSVCToolchain.php delete mode 100644 src/SPC/util/toolchain/ZigToolchain.php diff --git a/config/env.ini b/config/env.ini index a8e7a8a6..bf322adb 100644 --- a/config/env.ini +++ b/config/env.ini @@ -56,7 +56,7 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd [windows] ; build target: win7-static -SPC_TARGET=msvc-static +SPC_TARGET=native ; php-sdk-binary-tools path PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" ; upx executable path @@ -75,7 +75,9 @@ SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime ; - musl-static (default): pure static linking, using musl-libc, can run on any linux distro. ; - musl: static linking with dynamic linking to musl-libc, can run on musl-based linux distro. ; - glibc: static linking with dynamic linking to glibc, can run on glibc-based linux distro. -SPC_TARGET=musl-static + +; include PATH for musl libc. +SPC_LIBC=musl ; compiler environments CC=${SPC_LINUX_DEFAULT_CC} CXX=${SPC_LINUX_DEFAULT_CXX} @@ -122,7 +124,7 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" [macos] ; build target: macho or macho (possibly we could support macho-universal in the future) ; Currently we do not support universal and cross-compilation for macOS. -SPC_TARGET=macho +SPC_TARGET=native ; compiler environments CC=clang CXX=clang++ diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 3f63c84b..d00c8de0 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; use SPC\util\SPCConfigUtil; +use SPC\util\SPCTarget; class Extension { @@ -532,6 +533,11 @@ class Extension $sharedLibString .= '-l' . $lib . ' '; } } + // move static libstdc++ to shared if we are on non-full-static build target + if (!SPCTarget::isStaticTarget() && in_array(SPCTarget::getLibc(), SPCTarget::LIBC_LIST)) { + $staticLibString .= ' -lstdc++'; + $sharedLibString = str_replace('-lstdc++', '', $sharedLibString); + } return [trim($staticLibString), trim($sharedLibString)]; } diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index 8332f832..514e79b3 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -6,7 +6,6 @@ namespace SPC\builder\extension; use SPC\builder\Extension; use SPC\util\CustomExt; -use SPC\util\SPCTarget; #[CustomExt('imagick')] class imagick extends Extension @@ -16,15 +15,4 @@ class imagick extends Extension $disable_omp = ' ac_cv_func_omp_pause_resource_all=no'; return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; } - - protected function getStaticAndSharedLibs(): array - { - // on centos 7, it will use the symbol _ZTINSt6thread6_StateE, which is not defined in system libstdc++.so.6 - [$static, $shared] = parent::getStaticAndSharedLibs(); - if (SPCTarget::isTarget(SPCTarget::GLIBC)) { - $static .= ' -lstdc++'; - $shared = str_replace('-lstdc++', '', $shared); - } - return [$static, $shared]; - } } diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php index 1e405768..0f6d3578 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -6,7 +6,6 @@ namespace SPC\builder\linux; use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\RuntimeException; -use SPC\util\SPCTarget; class SystemUtil { @@ -189,12 +188,12 @@ class SystemUtil /** * Get libc version string from ldd */ - public static function getLibcVersionIfExists(): ?string + public static function getLibcVersionIfExists(string $libc): ?string { if (self::$libc_version !== null) { return self::$libc_version; } - if (SPCTarget::isTarget(SPCTarget::GLIBC)) { + if ($libc === 'glibc') { $result = shell()->execWithResult('ldd --version', false); if ($result[0] !== 0) { return null; @@ -209,7 +208,7 @@ class SystemUtil } return null; } - if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { + if ($libc === 'musl') { if (self::isMuslDist()) { $result = shell()->execWithResult('ldd 2>&1', false); } else { diff --git a/src/SPC/builder/linux/library/icu.php b/src/SPC/builder/linux/library/icu.php index 6fb7e1fc..26b98899 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -17,7 +17,7 @@ class icu extends LinuxLibraryBase { $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"'; $cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"'; - $ldflags = SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? 'LDFLAGS="-static"' : ''; + $ldflags = SPCTarget::isStaticTarget() ? 'LDFLAGS="-static"' : ''; shell()->cd($this->source_dir . '/source')->initializeEnv($this) ->exec( "{$cppflags} {$cxxflags} {$ldflags} " . diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 173ec954..362a68fe 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,6 +29,7 @@ class MacOSBuilder extends UnixBuilderBase // apply global environment variables GlobalEnvManager::init(); + GlobalEnvManager::afterInit(); // ---------- set necessary compile vars ---------- // concurrency diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index 886eec68..f0e2dba5 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -201,7 +201,7 @@ abstract class UnixBuilderBase extends BuilderBase $util = new SPCConfigUtil($this); $config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}"; - if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { + if (SPCTarget::isStaticTarget()) { $lens .= ' -static'; } [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); @@ -335,7 +335,7 @@ abstract class UnixBuilderBase extends BuilderBase $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $extLdFlags = "-extldflags '-pie'"; $muslTags = ''; - if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { + if (SPCTarget::isStaticTarget()) { $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; $muslTags = 'static_build,'; } diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index f126bf30..d8d7e0c9 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\FileSystem; use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\SPCTarget; @@ -15,6 +16,7 @@ trait imagemagick /** * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ protected function build(): void { @@ -36,11 +38,11 @@ trait imagemagick '--without-x', ); - // special: linux musl-static needs `-static` - $ldflags = !SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? ('-static -ldl') : '-ldl'; + // special: linux-static target needs `-static` + $ldflags = SPCTarget::isStaticTarget() ? ('-static -ldl') : '-ldl'; // special: macOS needs -iconv - $libs = SPCTarget::isTarget(SPCTarget::MACHO) ? '-liconv' : ''; + $libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : ''; $ac->appendEnv([ 'LDFLAGS' => $ldflags, diff --git a/src/SPC/builder/unix/library/ldap.php b/src/SPC/builder/unix/library/ldap.php index e87310cb..18393e24 100644 --- a/src/SPC/builder/unix/library/ldap.php +++ b/src/SPC/builder/unix/library/ldap.php @@ -12,7 +12,7 @@ trait ldap { public function patchBeforeBuild(): bool { - $extra = SPCTarget::isTarget(SPCTarget::GLIBC) ? '-ldl -lpthread -lm -lresolv -lutil' : ''; + $extra = SPCTarget::getLibc() === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : ''; FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra); return true; } diff --git a/src/SPC/builder/unix/library/mimalloc.php b/src/SPC/builder/unix/library/mimalloc.php index 0808516c..6d89e6df 100644 --- a/src/SPC/builder/unix/library/mimalloc.php +++ b/src/SPC/builder/unix/library/mimalloc.php @@ -14,9 +14,9 @@ trait mimalloc $cmake = UnixCMakeExecutor::create($this) ->addConfigureArgs( '-DMI_BUILD_SHARED=OFF', - '-DMI_INSTALL_TOPLEVEL=ON' + '-DMI_INSTALL_TOPLEVEL=ON', ); - if (SPCTarget::isTarget(SPCTarget::MUSL) || SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { + if (SPCTarget::getLibc() === 'musl') { $cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON'); } $cmake->build(); diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php index efcfc089..7ca324c2 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -51,7 +51,7 @@ trait postgresql $error_exec_cnt += $output[0] === 0 ? 0 : 1; if (!empty($output[1][0])) { $ldflags = $output[1][0]; - $envs .= SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" "; + $envs .= SPCTarget::isStaticTarget() ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" "; } $output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}"); $error_exec_cnt += $output[0] === 0 ? 0 : 1; diff --git a/src/SPC/builder/windows/WindowsBuilder.php b/src/SPC/builder/windows/WindowsBuilder.php index c7450050..25a503eb 100644 --- a/src/SPC/builder/windows/WindowsBuilder.php +++ b/src/SPC/builder/windows/WindowsBuilder.php @@ -34,6 +34,7 @@ class WindowsBuilder extends BuilderBase $this->options = $options; GlobalEnvManager::init(); + GlobalEnvManager::afterInit(); // ---------- set necessary options ---------- // set sdk (require visual studio 16 or 17) diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index f9938957..62f4260e 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -64,8 +64,8 @@ class BuildPHPCommand extends BuildCommand // check dynamic extension build env // linux must build with glibc - if (!empty($shared_extensions) && SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) { - $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!'); + if (!empty($shared_extensions) && SPCTarget::isStaticTarget()) { + $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with shared target!'); return static::FAILURE; } $static_and_shared = array_intersect($static_extensions, $shared_extensions); diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index 35529f5e..1e895bdb 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace SPC\command; -use SPC\builder\linux\SystemUtil; use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; @@ -224,12 +223,8 @@ class DownloadCommand extends BaseCommand '{name}' => $source, '{arch}' => arch2gnu(php_uname('m')), '{os}' => strtolower(PHP_OS_FAMILY), - '{libc}' => match (getenv('SPC_TARGET')) { - SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl', - SPCTarget::GLIBC => 'glibc', - default => 'default', - }, - '{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', + '{libc}' => SPCTarget::getLibc() ?? 'default', + '{libcver}' => SPCTarget::getLibcVersion() ?? 'default', ]; $find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern')); // find filename in asset list diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php index 29a22353..e51cdb17 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -6,7 +6,6 @@ namespace SPC\command\dev; use SPC\builder\BuilderProvider; use SPC\builder\LibraryBase; -use SPC\builder\linux\SystemUtil; use SPC\command\BuildCommand; use SPC\exception\ExceptionHandler; use SPC\exception\FileSystemException; @@ -77,12 +76,8 @@ class PackLibCommand extends BuildCommand '{name}' => $lib->getName(), '{arch}' => arch2gnu(php_uname('m')), '{os}' => strtolower(PHP_OS_FAMILY), - '{libc}' => match (getenv('SPC_TARGET')) { - SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl', - SPCTarget::GLIBC => 'glibc', - default => 'default', - }, - '{libcver}' => SystemUtil::getLibcVersionIfExists() ?? 'default', + '{libc}' => SPCTarget::getLibc() ?? 'default', + '{libcver}' => SPCTarget::getLibcVersion() ?? 'default', ]; // detect suffix, for proper tar option $tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern')); diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index 050cd88d..f031d73a 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace SPC\store; -use SPC\builder\linux\SystemUtil; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; @@ -594,12 +593,8 @@ class Downloader { $os_family = PHP_OS_FAMILY; $gnu_arch = getenv('GNU_ARCH') ?: 'unknown'; - $libc = match (getenv('SPC_TARGET')) { - SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl', - SPCTarget::GLIBC => 'glibc', - default => 'default', - }; - $libc_version = SystemUtil::getLibcVersionIfExists() ?? 'default'; + $libc = SPCTarget::getLibc(); + $libc_version = SPCTarget::getLibcVersion() ?? 'default'; return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}"; } diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index 69d3a649..fe7d5333 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -11,6 +11,7 @@ use SPC\builder\unix\UnixBuilderBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\SPCTarget; class SourcePatcher { @@ -456,7 +457,7 @@ class SourcePatcher public static function patchFfiCentos7FixO3strncmp(): bool { - if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::getLibcVersionIfExists() > '2.17') { + if (!($ver = SPCTarget::getLibcVersion()) || version_compare($ver, '2.17', '>')) { return false; } if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) { diff --git a/src/SPC/store/pkg/GoXcaddy.php b/src/SPC/store/pkg/GoXcaddy.php index e0c7c5b9..8dc1cf2d 100644 --- a/src/SPC/store/pkg/GoXcaddy.php +++ b/src/SPC/store/pkg/GoXcaddy.php @@ -7,7 +7,6 @@ namespace SPC\store\pkg; use SPC\store\Downloader; use SPC\store\FileSystem; use SPC\store\LockFile; -use SPC\util\GlobalEnvManager; class GoXcaddy extends CustomPackage { @@ -62,7 +61,6 @@ class GoXcaddy extends CustomPackage FileSystem::extractPackage($name, $source_type, $filename, $extract); - GlobalEnvManager::init(); // install xcaddy shell() ->appendEnv([ diff --git a/src/SPC/util/toolchain/ClangNativeToolchain.php b/src/SPC/toolchain/ClangNativeToolchain.php similarity index 77% rename from src/SPC/util/toolchain/ClangNativeToolchain.php rename to src/SPC/toolchain/ClangNativeToolchain.php index ac93513a..c5186521 100644 --- a/src/SPC/util/toolchain/ClangNativeToolchain.php +++ b/src/SPC/toolchain/ClangNativeToolchain.php @@ -2,30 +2,25 @@ declare(strict_types=1); -namespace SPC\util\toolchain; +namespace SPC\toolchain; use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; -use SPC\util\SPCTarget; class ClangNativeToolchain implements ToolchainInterface { - public function initEnv(string $target): void + public function initEnv(): void { - // native toolchain does not support versioning - if (SPCTarget::getTargetSuffix() !== null) { - throw new WrongUsageException('Clang native toolchain does not support versioning.'); - } GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=clang'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=clang++'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld'); } - public function afterInit(string $target): void + public function afterInit(): void { // check clang exists match (PHP_OS_FAMILY) { diff --git a/src/SPC/util/toolchain/GccNativeToolchain.php b/src/SPC/toolchain/GccNativeToolchain.php similarity index 77% rename from src/SPC/util/toolchain/GccNativeToolchain.php rename to src/SPC/toolchain/GccNativeToolchain.php index 2547334a..bd8cc868 100644 --- a/src/SPC/util/toolchain/GccNativeToolchain.php +++ b/src/SPC/toolchain/GccNativeToolchain.php @@ -2,30 +2,25 @@ declare(strict_types=1); -namespace SPC\util\toolchain; +namespace SPC\toolchain; use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\exception\WrongUsageException; use SPC\util\GlobalEnvManager; -use SPC\util\SPCTarget; class GccNativeToolchain implements ToolchainInterface { - public function initEnv(string $target): void + public function initEnv(): void { - // native toolchain does not support versioning - if (SPCTarget::getTargetSuffix() !== null) { - throw new WrongUsageException('gcc native toolchain does not support versioning.'); - } GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld.gold'); } - public function afterInit(string $target): void + public function afterInit(): void { // check gcc exists match (PHP_OS_FAMILY) { diff --git a/src/SPC/toolchain/MSVCToolchain.php b/src/SPC/toolchain/MSVCToolchain.php new file mode 100644 index 00000000..567cff38 --- /dev/null +++ b/src/SPC/toolchain/MSVCToolchain.php @@ -0,0 +1,12 @@ + MuslToolchain::class, // use musl toolchain by default, after zig pr merged, change this to ZigToolchain::class + 'Windows' => MSVCToolchain::class, + 'Darwin' => ClangNativeToolchain::class, + 'BSD' => ClangNativeToolchain::class, + ]; + + /** + * @throws WrongUsageException + */ + public static function initToolchain(): void + { + $libc = getenv('SPC_LIBC'); + if ($libc !== false) { + // uncomment this when zig pr is merged + // logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.'); + $toolchain = match ($libc) { + 'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class, + 'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_TARGET must be musl-static or musl for musl dist.'), + default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc), + }; + } else { + $toolchain = self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY]; + } + $toolchainClass = $toolchain; + /* @var ToolchainInterface $toolchainClass */ + (new $toolchainClass())->initEnv(); + GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}"); + } + + public static function afterInitToolchain(): void + { + if (!getenv('SPC_TOOLCHAIN')) { + throw new WrongUsageException('SPC_TOOLCHAIN not set'); + } + $toolchain = getenv('SPC_TOOLCHAIN'); + /* @var ToolchainInterface $toolchain */ + (new $toolchain())->afterInit(); + } +} diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php new file mode 100644 index 00000000..3922fecb --- /dev/null +++ b/src/SPC/toolchain/ZigToolchain.php @@ -0,0 +1,12 @@ +warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.'); - $target = match (getenv('SPC_LIBC')) { - 'musl' => SPCTarget::MUSL_STATIC, - 'musl-shared' => SPCTarget::MUSL, - 'glibc' => SPCTarget::GLIBC, - default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . getenv('SPC_LIBC')), - }; - self::putenv("SPC_TARGET={$target}"); - self::putenv('SPC_LIBC'); - } - - // auto-select toolchain based on target and OS temporarily - // TODO: use 'zig' instead of 'gcc-native' when ZigToolchain is implemented - $toolchain = match (SPCTarget::getTargetName()) { - SPCTarget::MUSL_STATIC, SPCTarget::MUSL => SystemUtil::isMuslDist() ? 'gcc-native' : 'musl', - SPCTarget::MACHO => 'clang-native', - SPCTarget::MSVC_STATIC => 'msvc', - SPCTarget::GLIBC => !SystemUtil::isMuslDist() ? 'gcc-native' : throw new WrongUsageException('SPC_TARGET must be musl-static or musl for musl dist.'), - default => throw new WrongUsageException('Unknown SPC_TARGET: ' . getenv('SPC_TARGET')), - }; - - SPCTarget::initTargetForToolchain($toolchain); + ToolchainManager::initToolchain(); // apply second time $ini2 = self::readIniFile(); @@ -123,9 +100,15 @@ class GlobalEnvManager } } + /** + * Initialize the toolchain after the environment variables are set. + * The toolchain or environment availability check is done here. + * + * @throws WrongUsageException + */ public static function afterInit(): void { - SPCTarget::afterInitTargetForToolchain(); + ToolchainManager::afterInitToolchain(); } /** diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index 9335e88d..fec3c089 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -55,7 +55,7 @@ class SPCConfigUtil ob_get_clean(); $ldflags = $this->getLdflagsString(); $libs = $this->getLibsString($libraries, $with_dependencies); - if (SPCTarget::isTarget(SPCTarget::MACHO)) { + if (SPCTarget::getTargetOS() === 'Darwin') { $libs .= " {$this->getFrameworksString($extensions)}"; } $cflags = $this->getIncludesString(); diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index 265c3d51..5f091a8e 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -4,13 +4,8 @@ declare(strict_types=1); namespace SPC\util; +use SPC\builder\linux\SystemUtil; use SPC\exception\WrongUsageException; -use SPC\util\toolchain\ClangNativeToolchain; -use SPC\util\toolchain\GccNativeToolchain; -use SPC\util\toolchain\MSVCToolchain; -use SPC\util\toolchain\MuslToolchain; -use SPC\util\toolchain\ToolchainInterface; -use SPC\util\toolchain\ZigToolchain; /** * SPC build target constants and toolchain initialization. @@ -18,86 +13,75 @@ use SPC\util\toolchain\ZigToolchain; */ class SPCTarget { - public const MUSL = 'musl'; - - public const MUSL_STATIC = 'musl-static'; - - public const GLIBC = 'glibc'; - - public const MACHO = 'macho'; - - public const MSVC_STATIC = 'msvc-static'; - - public const TOOLCHAIN_LIST = [ - 'musl' => MuslToolchain::class, - 'gcc-native' => GccNativeToolchain::class, - 'clang-native' => ClangNativeToolchain::class, - 'msvc' => MSVCToolchain::class, - 'zig' => ZigToolchain::class, + public const array LIBC_LIST = [ + 'musl', + 'glibc', ]; - public static function isTarget(string $target): bool - { - $env = getenv('SPC_TARGET'); - if ($env === false) { - return false; - } - $env = strtolower($env); - // ver - $env = explode('@', $env)[0]; - return $env === $target; - } - + /** + * Returns whether the target is a full-static target. + */ public static function isStaticTarget(): bool { $env = getenv('SPC_TARGET'); - if ($env === false) { - return false; + $libc = getenv('SPC_LIBC'); + // if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released + if ($libc === 'musl') { + return true; } - $env = strtolower($env); - // ver - $env = explode('@', $env)[0]; - return str_ends_with($env, '-static'); + // TODO: add zig target parser here + return false; } - public static function initTargetForToolchain(string $toolchain): void + /** + * Returns the libc type if set, for other OS, it will always return null. + */ + public static function getLibc(): ?string + { + $env = getenv('SPC_TARGET'); + $libc = getenv('SPC_LIBC'); + if ($libc !== false) { + return $libc; + } + // TODO: zig target parser + return null; + } + + /** + * Returns the libc version if set, for other OS, it will always return null. + */ + public static function getLibcVersion(): ?string + { + $env = getenv('SPC_TARGET'); + $libc = getenv('SPC_LIBC'); + if ($libc !== false) { + // legacy method: get a version from system + return SystemUtil::getLibcVersionIfExists($libc); + } + // TODO: zig target parser + + return null; + } + + /** + * Returns the target OS family, e.g. Linux, Darwin, Windows, BSD. + * Currently, we only support native building. + * + * @return 'BSD'|'Darwin'|'Linux'|'Windows' + * @throws WrongUsageException + */ + public static function getTargetOS(): string { $target = getenv('SPC_TARGET'); - $toolchain = strtolower($toolchain); - if (isset(self::TOOLCHAIN_LIST[$toolchain])) { - $toolchainClass = self::TOOLCHAIN_LIST[$toolchain]; - /* @var ToolchainInterface $toolchainClass */ - (new $toolchainClass())->initEnv($target); + if ($target === false) { + return PHP_OS_FAMILY; } - GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}"); - } - - public static function afterInitTargetForToolchain() - { - if (!getenv('SPC_TOOLCHAIN')) { - throw new WrongUsageException('SPC_TOOLCHAIN not set'); - } - $toolchain = getenv('SPC_TOOLCHAIN'); - if (!isset(self::TOOLCHAIN_LIST[$toolchain])) { - throw new WrongUsageException("Unknown toolchain: {$toolchain}"); - } - $toolchainClass = self::TOOLCHAIN_LIST[$toolchain]; - (new $toolchainClass())->afterInit(getenv('SPC_TARGET')); - } - - public static function getTargetName(): ?string - { - $target = getenv('SPC_TARGET'); - $target = strtolower($target); - // ver - return explode('@', $target)[0]; - } - - public static function getTargetSuffix(): ?string - { - $target = getenv('SPC_TARGET'); - $target = strtolower($target); - // ver - return explode('@', $target)[1] ?? null; + // TODO: zig target parser like below? + return match (true) { + str_contains($target, 'linux') => 'Linux', + str_contains($target, 'macos') => 'Darwin', + str_contains($target, 'windows') => 'Windows', + default => throw new WrongUsageException('Cannot parse target.'), + }; } } diff --git a/src/SPC/util/toolchain/MSVCToolchain.php b/src/SPC/util/toolchain/MSVCToolchain.php deleted file mode 100644 index 2ec8111c..00000000 --- a/src/SPC/util/toolchain/MSVCToolchain.php +++ /dev/null @@ -1,12 +0,0 @@ - Date: Sun, 29 Jun 2025 16:03:38 +0800 Subject: [PATCH 16/30] phpunit fix --- config/env.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/env.ini b/config/env.ini index bf322adb..7a41eb35 100644 --- a/config/env.ini +++ b/config/env.ini @@ -56,7 +56,7 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd [windows] ; build target: win7-static -SPC_TARGET=native +SPC_TARGET=native-windows ; php-sdk-binary-tools path PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" ; upx executable path @@ -124,7 +124,7 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" [macos] ; build target: macho or macho (possibly we could support macho-universal in the future) ; Currently we do not support universal and cross-compilation for macOS. -SPC_TARGET=native +SPC_TARGET=native-macos ; compiler environments CC=clang CXX=clang++ From fcaa7c5f429c486dc8dc9ca9f97034610d0171be Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 29 Jun 2025 16:21:22 +0800 Subject: [PATCH 17/30] Add no-env-check for builder --- src/SPC/builder/macos/MacOSBuilder.php | 5 ++++- src/SPC/command/BaseCommand.php | 2 ++ src/SPC/command/CraftCommand.php | 2 +- src/SPC/command/SPCConfigCommand.php | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 362a68fe..319047a4 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,7 +29,10 @@ class MacOSBuilder extends UnixBuilderBase // apply global environment variables GlobalEnvManager::init(); - GlobalEnvManager::afterInit(); + + if (!$this->getOption('no-env-check')) { + GlobalEnvManager::afterInit(); + } // ---------- set necessary compile vars ---------- // concurrency diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index 832ced97..251fcdb5 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -32,6 +32,7 @@ abstract class BaseCommand extends Command parent::__construct($name); $this->addOption('debug', null, null, 'Enable debug mode'); $this->addOption('no-motd', null, null, 'Disable motd'); + $this->addOption('no-env-check', null, null, 'Disable env check for builder'); } public function initialize(InputInterface $input, OutputInterface $output): void @@ -99,6 +100,7 @@ abstract class BaseCommand extends Command // init GlobalEnv if (!$this instanceof BuildCommand) { GlobalEnvManager::init(); + $this->input->setOption('no-env-check', true); } if ($this->shouldExecute()) { try { diff --git a/src/SPC/command/CraftCommand.php b/src/SPC/command/CraftCommand.php index c8003a3e..fbf3a5be 100644 --- a/src/SPC/command/CraftCommand.php +++ b/src/SPC/command/CraftCommand.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Process\Process; #[AsCommand('craft', 'Build static-php from craft.yml')] -class CraftCommand extends BaseCommand +class CraftCommand extends BuildCommand { public function configure(): void { diff --git a/src/SPC/command/SPCConfigCommand.php b/src/SPC/command/SPCConfigCommand.php index d3c7a0b2..a1886ed1 100644 --- a/src/SPC/command/SPCConfigCommand.php +++ b/src/SPC/command/SPCConfigCommand.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; #[AsCommand('spc-config', 'Build dependencies')] -class SPCConfigCommand extends BuildCommand +class SPCConfigCommand extends BaseCommand { protected bool $no_motd = true; From 7f45f4aeec9e839e11e77c2c003d27f3db4a0e62 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 29 Jun 2025 19:48:09 +0800 Subject: [PATCH 18/30] Fix phpunit, add SPC_SKIP_TOOLCHAIN_CHECK --- phpunit.xml.dist | 1 + src/SPC/builder/macos/MacOSBuilder.php | 5 +---- src/SPC/command/BaseCommand.php | 3 +-- src/SPC/util/GlobalEnvManager.php | 4 +++- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index dc58130d..2f0c2934 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,5 +4,6 @@ > + diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 319047a4..362a68fe 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,10 +29,7 @@ class MacOSBuilder extends UnixBuilderBase // apply global environment variables GlobalEnvManager::init(); - - if (!$this->getOption('no-env-check')) { - GlobalEnvManager::afterInit(); - } + GlobalEnvManager::afterInit(); // ---------- set necessary compile vars ---------- // concurrency diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index 251fcdb5..9c07b074 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -32,7 +32,6 @@ abstract class BaseCommand extends Command parent::__construct($name); $this->addOption('debug', null, null, 'Enable debug mode'); $this->addOption('no-motd', null, null, 'Disable motd'); - $this->addOption('no-env-check', null, null, 'Disable env check for builder'); } public function initialize(InputInterface $input, OutputInterface $output): void @@ -100,7 +99,7 @@ abstract class BaseCommand extends Command // init GlobalEnv if (!$this instanceof BuildCommand) { GlobalEnvManager::init(); - $this->input->setOption('no-env-check', true); + f_putenv('SPC_SKIP_TOOLCHAIN_CHECK=yes'); } if ($this->shouldExecute()) { try { diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index 6214476b..23ae242d 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -108,7 +108,9 @@ class GlobalEnvManager */ public static function afterInit(): void { - ToolchainManager::afterInitToolchain(); + if (!filter_var(getenv('SPC_SKIP_TOOLCHAIN_CHECK'), FILTER_VALIDATE_BOOL)) { + ToolchainManager::afterInitToolchain(); + } } /** From 956688d9f4021229bd0e7443a74a3223d93ac6ab Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 29 Jun 2025 19:53:22 +0800 Subject: [PATCH 19/30] Fix CI again --- .github/workflows/tests.yml | 2 +- bin/build-static-frankenphp | 4 ++-- bin/spc-gnu-docker | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ee4a17fb..5ed6d59d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -105,7 +105,7 @@ jobs: run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - name: "Run PHPUnit Tests" - run: SPC_TARGET=glibc vendor/bin/phpunit tests/ --no-coverage + run: SPC_LIBC=glibc vendor/bin/phpunit tests/ --no-coverage define-matrix: name: "Define Matrix" diff --git a/bin/build-static-frankenphp b/bin/build-static-frankenphp index bc5cdb33..10a693a7 100755 --- a/bin/build-static-frankenphp +++ b/bin/build-static-frankenphp @@ -92,7 +92,7 @@ ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative ENV PATH="/app/bin:/cmake/bin:/usr/local/go/bin:$PATH" -ENV SPC_TARGET=glibc +ENV SPC_LIBC=glibc ADD ./config/env.ini /app/config/env.ini RUN bin/spc doctor --auto-fix --debug @@ -146,7 +146,7 @@ echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env echo 'SPC_DEFAULT_C_FLAGS=-fPIE -fPIC' >> /tmp/spc-gnu-docker.env -echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env +echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 7292bfd8..51f3d29a 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -107,7 +107,7 @@ 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 -ENV SPC_TARGET=glibc +ENV SPC_LIBC=glibc ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH" ADD ./config/env.ini /app/config/env.ini @@ -154,7 +154,7 @@ fi # Apply env in temp env file echo 'SPC_DEFAULT_C_FLAGS=-fPIC' > /tmp/spc-gnu-docker.env -echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env +echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env From e1e48922de90c6e61f1c57fffd30c187cb66a36c Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 29 Jun 2025 22:49:48 +0800 Subject: [PATCH 20/30] Use isStatic instead of isStaticTarget --- src/SPC/builder/Extension.php | 2 +- src/SPC/builder/linux/library/icu.php | 2 +- src/SPC/builder/unix/UnixBuilderBase.php | 4 ++-- src/SPC/builder/unix/library/imagemagick.php | 2 +- src/SPC/builder/unix/library/pkgconfig.php | 2 +- src/SPC/builder/unix/library/postgresql.php | 2 +- src/SPC/command/BuildPHPCommand.php | 2 +- src/SPC/util/SPCTarget.php | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index d00c8de0..76fc76d6 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -534,7 +534,7 @@ class Extension } } // move static libstdc++ to shared if we are on non-full-static build target - if (!SPCTarget::isStaticTarget() && in_array(SPCTarget::getLibc(), SPCTarget::LIBC_LIST)) { + if (!SPCTarget::isStatic() && in_array(SPCTarget::getLibc(), SPCTarget::LIBC_LIST)) { $staticLibString .= ' -lstdc++'; $sharedLibString = str_replace('-lstdc++', '', $sharedLibString); } diff --git a/src/SPC/builder/linux/library/icu.php b/src/SPC/builder/linux/library/icu.php index 26b98899..c911a2fe 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -17,7 +17,7 @@ class icu extends LinuxLibraryBase { $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"'; $cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"'; - $ldflags = SPCTarget::isStaticTarget() ? 'LDFLAGS="-static"' : ''; + $ldflags = SPCTarget::isStatic() ? 'LDFLAGS="-static"' : ''; shell()->cd($this->source_dir . '/source')->initializeEnv($this) ->exec( "{$cppflags} {$cxxflags} {$ldflags} " . diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index f0e2dba5..6f0d5045 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -201,7 +201,7 @@ abstract class UnixBuilderBase extends BuilderBase $util = new SPCConfigUtil($this); $config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}"; - if (SPCTarget::isStaticTarget()) { + if (SPCTarget::isStatic()) { $lens .= ' -static'; } [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); @@ -335,7 +335,7 @@ abstract class UnixBuilderBase extends BuilderBase $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $extLdFlags = "-extldflags '-pie'"; $muslTags = ''; - if (SPCTarget::isStaticTarget()) { + if (SPCTarget::isStatic()) { $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; $muslTags = 'static_build,'; } diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index d8d7e0c9..d7cfa887 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -39,7 +39,7 @@ trait imagemagick ); // special: linux-static target needs `-static` - $ldflags = SPCTarget::isStaticTarget() ? ('-static -ldl') : '-ldl'; + $ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl'; // special: macOS needs -iconv $libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : ''; diff --git a/src/SPC/builder/unix/library/pkgconfig.php b/src/SPC/builder/unix/library/pkgconfig.php index 3522c52a..1817ab0c 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -14,7 +14,7 @@ trait pkgconfig UnixAutoconfExecutor::create($this) ->appendEnv([ 'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '', - 'LDFLAGS' => SPCTarget::isStaticTarget() ? '--static' : '', + 'LDFLAGS' => SPCTarget::isStatic() ? '--static' : '', ]) ->configure( '--with-internal-glib', diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php index 7ca324c2..c2cc7165 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -51,7 +51,7 @@ trait postgresql $error_exec_cnt += $output[0] === 0 ? 0 : 1; if (!empty($output[1][0])) { $ldflags = $output[1][0]; - $envs .= SPCTarget::isStaticTarget() ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" "; + $envs .= SPCTarget::isStatic() ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" "; } $output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}"); $error_exec_cnt += $output[0] === 0 ? 0 : 1; diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index 62f4260e..8bfa4aaf 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -64,7 +64,7 @@ class BuildPHPCommand extends BuildCommand // check dynamic extension build env // linux must build with glibc - if (!empty($shared_extensions) && SPCTarget::isStaticTarget()) { + if (!empty($shared_extensions) && SPCTarget::isStatic()) { $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with shared target!'); return static::FAILURE; } diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php index 5f091a8e..071f2620 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -21,7 +21,7 @@ class SPCTarget /** * Returns whether the target is a full-static target. */ - public static function isStaticTarget(): bool + public static function isStatic(): bool { $env = getenv('SPC_TARGET'); $libc = getenv('SPC_LIBC'); From ab5828a56048f5350df2d3fcbc531d720287277c Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 30 Jun 2025 09:05:56 +0800 Subject: [PATCH 21/30] Remove redundant path in gnu docker --- bin/spc-gnu-docker | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 51f3d29a..bbdf957f 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -108,10 +108,10 @@ ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime RUN /app/bin/php /app/bin/composer install --no-dev ENV SPC_LIBC=glibc -ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH" +ENV PATH="/app/bin:/cmake/bin:\$PATH" ADD ./config/env.ini /app/config/env.ini -RUN CC=gcc bin/php bin/spc doctor --auto-fix --debug +RUN CC=gcc bin/spc doctor --auto-fix --debug RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \ tar -zxvf make.tgz && \ From 138e5588e949835822921094b0b30ea2e615f9da Mon Sep 17 00:00:00 2001 From: Jerry Ma Date: Mon, 30 Jun 2025 12:18:49 +0800 Subject: [PATCH 22/30] Update bin/spc-gnu-docker Co-authored-by: Marc --- bin/spc-gnu-docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index bbdf957f..38d000ae 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -187,5 +187,5 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then set -ex $DOCKER_EXECUTABLE run $PLATFORM_ARG --privileged --rm -it $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash else - $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /app/bin/php bin/spc $@ + $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@ fi From e5848086c373d1dc3cafe04c279bede7b90dba1b Mon Sep 17 00:00:00 2001 From: Jerry Ma Date: Mon, 30 Jun 2025 12:19:17 +0800 Subject: [PATCH 23/30] Update bin/spc-gnu-docker Co-authored-by: Marc --- bin/spc-gnu-docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 38d000ae..20a26389 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -108,7 +108,7 @@ ADD ./bin/spc /app/bin/spc RUN /app/bin/setup-runtime RUN /app/bin/php /app/bin/composer install --no-dev ENV SPC_LIBC=glibc -ENV PATH="/app/bin:/cmake/bin:\$PATH" +ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH" ADD ./config/env.ini /app/config/env.ini RUN CC=gcc bin/spc doctor --auto-fix --debug From 44c6d6288eaf2276ffcd1918667a7b9f60c88108 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 30 Jun 2025 13:04:49 +0800 Subject: [PATCH 24/30] Refactor LinuxMuslCheck to use MuslToolchain class for toolchain check --- src/SPC/doctor/item/LinuxMuslCheck.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SPC/doctor/item/LinuxMuslCheck.php b/src/SPC/doctor/item/LinuxMuslCheck.php index c219b83c..a4e57eba 100644 --- a/src/SPC/doctor/item/LinuxMuslCheck.php +++ b/src/SPC/doctor/item/LinuxMuslCheck.php @@ -16,13 +16,14 @@ use SPC\store\Downloader; use SPC\store\FileSystem; use SPC\store\PackageManager; use SPC\store\SourcePatcher; +use SPC\toolchain\MuslToolchain; #[OptionalCheck([self::class, 'optionalCheck'])] class LinuxMuslCheck { public static function optionalCheck(): bool { - return getenv('SPC_TOOLCHAIN') === 'musl'; + return getenv('SPC_TOOLCHAIN') === MuslToolchain::class; } /** @noinspection PhpUnused */ From 51e23d21cd6c7e72d71d64017da7563042ce9baf Mon Sep 17 00:00:00 2001 From: DubbleClick Date: Mon, 30 Jun 2025 12:56:12 +0700 Subject: [PATCH 25/30] remove w32 patches (other branch isn't merged yet) --- config/env.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/env.ini b/config/env.ini index 7a41eb35..a6b3fc43 100644 --- a/config/env.ini +++ b/config/env.ini @@ -91,7 +91,7 @@ SPC_EXTRA_LIBS= ; upx executable path UPX_EXEC=${PKG_ROOT_PATH}/bin/upx ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches -SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream +SPC_MICRO_PATCHES=cli_checks,disable_huge_page ; *** default build command for building php *** ; buildconf command @@ -134,7 +134,7 @@ SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_EXTRA_LIBS= ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches -SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv +SPC_MICRO_PATCHES=cli_checks,disable_huge_page ; *** default build command for building php *** ; buildconf command From 956667bf081c17dfc20fe061dbf9c48a5f6c562c Mon Sep 17 00:00:00 2001 From: DubbleClick Date: Mon, 30 Jun 2025 13:40:51 +0700 Subject: [PATCH 26/30] fix alpine doctor saying gettext-dev isn't installed --- src/SPC/doctor/item/LinuxToolCheckList.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index 56235b0c..51e191bf 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -51,6 +51,7 @@ class LinuxToolCheckList 'binutils-gold' => 'ld.gold', 'base-devel' => 'automake', 'gettext-devel' => 'gettextize', + 'gettext-dev' => 'gettextize', ]; /** @noinspection PhpUnused */ From 3965a899c7adc874901495987ee36ecee66feebf Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 30 Jun 2025 19:41:32 +0800 Subject: [PATCH 27/30] Add missing directory separator --- src/SPC/store/SourcePatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index 029f77a8..92af572c 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -151,7 +151,7 @@ class SourcePatcher foreach ($patches as $patch) { logger()->info("Patching micro with {$patch}"); - self::patchFile(SOURCE_PATH . "/php-src{$patch}", SOURCE_PATH . '/php-src'); + self::patchFile(SOURCE_PATH . "/php-src/{$patch}", SOURCE_PATH . '/php-src'); } return true; From d00a5223d3ec24f0d482792d565c8ef232d4f61e Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 30 Jun 2025 19:45:26 +0800 Subject: [PATCH 28/30] Add exception for checking patch file exist --- src/SPC/store/SourcePatcher.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index 92af572c..1e8be792 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -177,6 +177,9 @@ class SourcePatcher } $patch_str = FileSystem::convertPath($patch_file); + if (!file_exists($patch_str)) { + throw new RuntimeException("Patch file [{$patch_str}] does not exist"); + } // Copy patch from phar if (str_starts_with($patch_str, 'phar://')) { From 6de7b8e63b80fbf8eb23b27c5389b2034ab41827 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 30 Jun 2025 21:41:48 +0800 Subject: [PATCH 29/30] Add special sponsors --- README-zh.md | 7 +++++++ README.md | 6 ++++++ docs/.vitepress/config.ts | 6 +++++- docs/.vitepress/theme/index.ts | 3 ++- docs/.vitepress/theme/style.css | 6 ++++++ docs/index.md | 13 +++++++++++++ docs/public/images/beyondcode-seeklogo.png | Bin 0 -> 29354 bytes docs/public/images/nativephp-logo.svg | 6 ++++++ 8 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 docs/.vitepress/theme/style.css create mode 100644 docs/public/images/beyondcode-seeklogo.png create mode 100644 docs/public/images/nativephp-logo.svg diff --git a/README-zh.md b/README-zh.md index 03fa7160..bd155da5 100755 --- a/README-zh.md +++ b/README-zh.md @@ -292,6 +292,13 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys 你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。 +**特别赞助商**: + +Beyond Code Logo + +NativePHP Logo + + ## 开源协议 本项目采用 MIT License 许可开源,下面是类似的项目: diff --git a/README.md b/README.md index 94c10d58..8316a751 100755 --- a/README.md +++ b/README.md @@ -317,6 +317,12 @@ Now there is a [static-php](https://github.com/static-php) organization, which i You can sponsor me or my project from [GitHub Sponsor](https://github.com/crazywhalecc). A portion of your donation will be used to maintain the **static-php.dev** server. +**Special thanks to sponsors below**: + +Beyond Code Logo + +NativePHP Logo + ## Open-Source License This project itself is based on MIT License, diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 7205a606..d9bc48c8 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -47,6 +47,10 @@ export default { nav: [], socialLinks: [ {icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'} - ] + ], + footer: { + message: 'Released under the MIT License.', + copyright: 'Copyright © 2023-present crazywhalecc' + } } } diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 8dd36ba8..06771bcf 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -2,6 +2,7 @@ import DefaultTheme from 'vitepress/theme' import {inBrowser, useData} from "vitepress"; import {watchEffect} from "vue"; +import './style.css'; export default { ...DefaultTheme, @@ -13,4 +14,4 @@ export default { } }) } -} \ No newline at end of file +} diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 00000000..b09fc682 --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,6 @@ +/** override default styles */ +.vp-sponsor-grid-image { + max-height:36px !important; + max-width: 1000px !important; +} + diff --git a/docs/index.md b/docs/index.md index 76c06407..f667ce44 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,3 +21,16 @@ features: - title: Dependency Management details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions. --- + + + +## Special Sponsors + + + diff --git a/docs/public/images/beyondcode-seeklogo.png b/docs/public/images/beyondcode-seeklogo.png new file mode 100644 index 0000000000000000000000000000000000000000..cacfd720d8c99113aa3aaabf7d13d33bd9dd3cb5 GIT binary patch literal 29354 zcmc$_byOVB(>}Vu!s703!8JGpSu}X?;FbVELvV-1odAIlY=Pho!QBHvHo+4t=;F4x z-Ti#!{k`|zfA2YW&zw0uHQhB&Jyl&((|tDfm6i$~HZ?W?0Kijysi+G8fM5UsN(m+! zQbMA6TnzxAB3^0eE2+HKAf^#P3jcrn(LPhAVO9FqS^0mp|0AJcRsR13oAj$ZaCI24vFAe|5{~w3ce+T{>CoUn=|0jU)N!XFL zBMJF8Gt~c!BBX?x<$w73r}01C{%No()BJbTzvalW|63JO`0q<3?bOWw4F9*be|P?m zFaD2CB^&~Jq{9FD=)bD|h3$XTA~pQGjkNv$&e(rB|9>%$r13xQ_~%x>wDy0vmDKuo zy^scG4F>WoMqlIKHzE z+S{pX9*|W&^sx;3Z>W64{)G3HAP;;(-BkEfU`DusvWBb@-RbT=4JRHeIT{pUd*KuE zHTU5QUS>vk5W3Ws;@TJ7W?I9t9o-}2yQjy?_gtJeUce7mK{gb1>YM4<$#10MLjS(n zamdS;&Z#7EI{RkTBEPGQ`5CYDaY1Y;@@AYD&5f~-#{0tT!1$zyP`_*P2hXI~f@Ckt z2*&_JCzP;^xQV9q1FHQWnN;u`Cxs7*1WVbeBBdPY$-r(>MVIeb9F-~HX5=9)B0 zdLFh3exnhYX%y&_-D*vaaDUOz-kld{6$RRZfAyz{HL5g*rbFzHCGRr;;>YFkh#~Pn z+NToByDWUbgU+H`ODc@2?Jm2M3_UbYDj=YaT#~)H`G__dEp&yt!>oD;rc=L^_qnM7 z0F(NYuT5RBk=k@a<%9!})?Ji!jH-EQ8Tfum3V;~gJu`uS`DNJGEd}s24|aWyxBRVD z*vl>p1)A+T-?J&7Sv;$)y9NRlBvxn{8h*&#w?cuc+Zk30A{2R^0;XN+05J3x zh7ykgdzri^bb3>@L<)c_>xJfEd z0R+08H=l8+NZ4yT`!N6nu@y}}n8LqM{G%pcq_I6C%Gt3gnHw7jkj)5@&W##Zn=>q( z!2|?bHP;znXJvHzaw3PVdWbfCqf{6knmg(a@&R0{WO~dB{Bu=aPN4**@R)`V9>2Js|>ciHM%rHMoVQLvj*M`Godx)$WtXjTif!QE~VIzoP4HD>_akBcoT^VYyE zK39ALhEOI!Q9VnIeg{W7IwMa2V1V&yn-!0R+t`c@Iq<{)8)rh`*RS1Nmmol|oRpOG z>jdk;p;a7}(gh46ZuRQaDKtORyf^?~X`gi2|M%o%#K8y(=30%|5b>@;&CqC#UoK zk2dAf#o>VOkl5uJtKd%0+cp3VAV{n^WLX_%F;HBq=0+SNDTCv?t!f~rbfeHNq;T5$={qpg9K$mkHx4?q>Xu7WS zi39v?s@CMpt_IIS7CN4wB~xfg8`>}a6l7SqxOG%dCe9^U%CGcXTPkOfn>TEJXMPV`t1_E@tLK6F$LB$GAvOR2W8_uF1{$37UFL?MBmnTz%JK%2 z94>6|X~VPv0FcYQzo0R(T$Pg2?78rR%nO9#IYQ-H~gsp0O&h{n)Hu~L3EQtD9GwWd$_RaSJ`lz zIP#9NHb5las$8%LTf25_r`cGf=%E z2-8>uXGgR7Jm5OnI;q?TTQi(>pK)v0c*D9tH!KBDt*9MvLk}&6s;H5A`l%nEOf(yY zPY5a(yy7@NJMi;UE0+KJzRWpeTYWKT4rnW>Jj#$t#zch>(=Mt{{g61fK7twwor>vn zSKr?D5Ig9!=Tg|e%=sIEpP>I44JZZ6aI(n#Nh_{g4 z$#auFSyzY`#FF#Hc%YZp4p6^h`;n5HPc-Sezk8qtmzy4Nj;x9I($yDjf0|i+Lg+Yu zB7t0g$U;9C>E^I7_}0Pte7(g3+o#5loCJjt9-LOZ+Hey$FgX=vZrzcSlaefmQ%ZKb z;?K@o6J=X#lqx9F-tL(5wMVI=FsdAi6K84CCA!DAl!J!ZZu!P|di~7Tav`AE^<95i zq$X@CaP#Ys*U5w0bG~hnC1rM4x520x4E>o0gWnB-gCrwdCbfycvf-p#Ph_XrYDZe5 zdCBXomdV)4`!v&x0(gt?U>&6=_|4$dP7oalDkHqnUi6{~-@Q}zJ-$K-{c%I&0QPcM zritDDRoUoa(i4D*!N_F94u)PS`a<_QNCOM$3*G5i5B#K!2PJpuYNE`^ItE10)2!s~ z`^0XQqcxdWw{J<)^b%u^NKgd)eOz>eh(!U5F8JERc&c|W;lPM5%!#0#)ih#wjYa@M zB6xsd{_14I{)CxkO&XEqc6t@f0T&`1I-1V zD14Lf#Yx3-la`p0GSgjXv^pBe5d>;T6+gRqaiXC(M-~xOsDy}esul^~a!!6>8{w?q zAAFA8nc-oD(tg5K5DVJGt(mrGwVB-mKq{^1w1bWQ3xTCbZJuY(Jp8+t zW*X}2WhIKI*Iz9RQAh^^lk<~ULn#H-Qhf^_l}a+W-1ZLJ-?aW+^L6)szmQu1^S9&%)lr;QN;+adh8#6KBt67b}jsG{U_DqX-E#5`rB^zDbiWVNb@9P>|LT%{b8fWM<4kTk3cy*?O9;PQGVCBz%7RLq%&&hW>Mp z@>?@Osblh-1lu=19u6=kpg#ybllMZtzk;FfXDk_QT2DW`HABHc`ao>bX^L#8U89lr z2zt6bg1v%MV-Lub5v-TD1Pj57Fx%~m^G9D}w_8n2VGB?Dld5~l!A5qo4?8KMoTX2d z)LnC~u6|D0zBb0Z{Y&KB2GW{U6%E&#Q<9c`AeOGcAVFt@TR!*e&>oga|K-5eO{MYj zZg4W-joT1!9S`t<6hbe@vXy|5J432viMY$_S46?QrTDE!YC)gpW1<&(tXo~pwZfv5 zO^Zfz3tkO}f>JIST(=>7q!067-YJsX6e7b%LpN#cN8=%F$Xmme*@1w~?^+|@qXJ?3 z-;fkL5D3zE7IwGD2{jq?=-HM4I-8Z>JQ(?Er)O8$t@A&3s!Y8aY({6HIRii8Mz5oO zzaZ}bvrj!%C<-;*_*j&lb#HR$H)4G3mvX(_a{6mfLc&2P<{M5?#zYf{oV>6jrmJS18Ih0GM-I}Lrj;Ur zo~7}rh1EHJHh7@KQ@XX@W#OzHA9T%YsSq zp7{EPqUaNB-q-&N!mrv!bFR@37<%Y0A91DKuB0gRFd*ZYK1W<;JQrxInSI!FU=`TA zrdgR!XVg1O;7hXGF)oVfk)m-*ZtKW+bO@z+)jyWx8qOA{vbj&vZQ{585gCkpQG+R@ zhWOYY8O3&TL=8!TvcSKUkmswFmk*ht=z@a8h9W(UVg%9RYdi|_rCQa{Oqp$sXc^$Z1% z7*q(SDFNfqD)4Ho$Cqsh$~W8ZSLa3YiibwR1rvDwMUl!oe^rdGcDgk5m+f0C{DE&Z zcd-N;5iSmkIr|L1t_yLSm;Q+w%1O~ry3zZ_a&yq+VsT!g*EZXWOJf!XmxNbUOi0@x z!BvrJpKRJ9Mo~9${Shn_F{N3t7_tH127(?>*u4MLZ~fyB_BA6;w|aSyM1H|ev2@P1 zSC(nqV_l|4%@TdQ)ysc{2O_PmJl#eiT;_26JQAE6ra7E?S;s_DsTgA>f@3M=-?a5 z^#a0xIOWvxgo7zTspEs4)ck%RQe23f1khHlL0vqHWYUw;buT?$@{D6~!jtX&=p;}0bo0HpzrAg3$3vr* zl25I_+rS?8K*uM~UvF)tv$Y~H!P2*!N0uXDd2uHLLNQSIi~9a@l$LqN`)W_8uA9k;~)T=l*J zq*nKS=4`9U?5j2Qf=C(c5IfiyunK+9Q~9$nSEcrN3*K&jpH>)n=5#&e;&W*+ zWu=;sX9MvyUPHv0h64B*bTorqWt0YVQ2CK%dFHuPz>15yi{CH~6H2mBJc(k_4&Ir4 zsPq&rf3lQO)N8g7nJ+qTAgioZgt^I;eSmrAcY>KwwimWQMV3LTRCTmPlZdryO(cDIDA&bG?zTQMv9~E8<>qOwxubJEKXnB_98i}Edq77k4jkEuQ z>$|b0VdXUfY|=~q;$p9EL8F(1aQiEh0(Mu)D&goWE)2l_ zi3ydi78Mjvn=^eFyZYIQl!jsBBfD)No>i=wjkK1}*1{^2AorXzguO2AS(_N3^bEsd zJK0#9mYJs~J#WMjcBN0MyShP6c#$q-F_iviT3L5h(Y3iEP@U%D$Hxl4hv6b~ZH9}W zYOa8tNe%Ug@Re_K20NF|sWVq8Uq_^`-c9s@atUr1gY?Xvo65cz;z)dE6;nl`t3aLm z{1Kp|7Udl35{m^N(ta0Wkfp=b&<|ssWgB5Y6-~e$*oedKsd$`SXQresMA%@jVUzpl zob>}0^jPZ$t*g+@8`X4Q?q4wep131xq~~G{s_8hUkwu-9xPPb-Bf}K&5&0MQyY1j{T+UNBAWpdlnYqW(7 zyVfG1pWQl27go<`z&_qhx-T!435V+59-C)I7sf7G_#Qi;(CTQ(Hc#i1`c6*|2Ej7J zD^XGlE8V(6Oh*5~z==UOB@71Y7+*_hXU1WDkC7-np&ln8wwI0u8yyK(a%}Hi(&~^S!i`iO{V(>` z*4E%cGH@Mg1h;&Q)vX_G#51WIe=J<}13d}MNF5BFC>21wKoMCXrdycMh= zhMo~^@2O>$cJr6=0f`ij7>!q@*?idgCn&_d&U$@VG4Zw5GZSQxLh{biS%Wt^gfp>r zRQ!pkXDOoq?ll?)c!|~f*}w1t$t|50)ru$Qr{e^RqZ_YxX~B;@S9wc+5CH+M99@PVbgxUn5<8JU1^fwBmd| z!yD~W&BoXlac*>TZ!f==urqgY1t`zcej)&oBnoA z1kN=v=kU($ZF>#0pr)oku2j92#|yYj4K~u!CA_4NCU^@m0{$&l)ha{%VFsBQuWlIy zOl7y}S!_F!O7Q zftrJVdil@uUJ|O+3F$(zkA_&cUGQXEPWFMc*=lbhL{uErj-}a)w9HQi2!gjdhK8!6 zE9F!-(OMH_9CEZ7M=yuIQQckU%LIIru^k)D@nAT*xrF&-u1M+bVoG|$NK!G?UopP_ zEWoa#d0ige`4^RZbod>XdMM6X)(Bss9Vn&{_iBb1C=b){;RXh^X7-Bl9LufkJRdg( zw-zUz?O@#|dXLStk{+Z6yQlw@yBQLdNk(I>4DNaNbtyr|=ro+KBg&&a;mXbPw=Ybc z8@rJpJriJR=ojp+Nv9?+#D6$7#aYXDJI^$^nZqk%k#U46X#-o{!HED9rDCLxE}?z{ zMhBkHTYWrc>`#>>g>kUg$L|Oc4W6v#DyEc)x+oFczGPTQ!Cn~%oBu8#(Fet&;gg2n zTH0``k2^{)6m7%z=}4pBDUr;jgT;pG9u1AyKfuIlj1@ncg}|IUz+zW(TlC_uae^wo zF7Y7K^fT1+`7==x6w-DPiQtyNswM1h*^JDOG4j}u_?EGeCc+V zqBpf_FJT=!mjzA4^cj~>nD!|yB(HjsDJ5u{Bi3n-HjqZcA64mO{otUR>pJ@NXZ2{XV0 zC`!Ktyw6iHAWD2%A0S#J4TLUZL7$`6QjWbrv1Z-<-8eb>JiPEz<*_pa^+ImqDO((0 z)GWs-Vv0h?EW`}ycY7mS(CuQJV?#AGGw*v|D*A&_KvhYDZ1SX6B*i(jCk{VPW@#T& zQUwOOC20!`Pg;wP5B7IauVg zJSoEistPbw9DjY#+t|2oJ$bdlI@~cg-(rh)dYW_sIl%L>!7opTniYS;(mUE1Ge3H` zSA_78NV$64f_5$;q)K{?$?1|1J`s3$8hZa%YA*NWZj1GRnpcEBG1|bZb~i-i43@nq z{YRRLkgb@gcd-TzXs9|6zVGOeVIjh=@2M)8M2Qh|%45HXYC%$so;N0rWc62W>PLRs zVog7x`wT5qWgHmc)eFXP_`b`Q{Co*4D%Q)Wu|!CuPdbDitIARngD9W-#!AH= zo3*L+yhkcYFl~rZSTJ>v(w4jrk4Q<8MyW#^jSCx2_x?72HMi|2d%MOyq0s~vo+Nwm&USlLrI7mMxJCV5 z|I&{gt(*!oqxAwKc=y49KewO_fh@uXB4z=ySVwbJq_(|OKtgTnSHw06j zxmPR)-lz;+5sY8>dwRZ3vAlxB*3-=W4&icSjrl9Oi^ax_nV#JoX+_-Mn-r$!$H{vY zJ4VwWE#Ry7`gJhaYW4Ay=EaD5?BNMK3Aeq^e&#dNL(3KBBmp<(!JWLbMS61HLZclu zD2eOnqa5o`O=`gxZjOP@&Vd%+>&iBquJJK%bxDSDIMC;HFFMon=4bP=r9zA+5=(Bg zc{SB{cr7LF2BkSir7iJ4CO5VH_8MQ@Jy!08t{Te3f8;p6H(R8-vz}Dzk_tFYloG1oo~A)bz~QR=p;p^u zX6#kN#QWo8RqdqRO=;A#!@Y{)(zHH9f4_&+R7o5%kII!BaLCEeo#?QzF4-e~;-Is@ zc_ZjT>x0q}R7yFG<%8l@)cX{LX-jQIxOBTGK@%k96XqU7Nx} zT_awA%Cm{4g$@Z9$&PzFKV6K4vMLT2W9jAeIj|A@t-YehN?=>bH{ysl`aII?!CApI zg}JB-F;iN5IC3{@V~F`@z@0L0%rDQ} zb(Rdo#hEI;D)ePmI~IIqA0&ubKb(M%Z%i}F0iVs}D7nFCC9Zj_aS+$EKh7)( z$FzO>5NE?w;zt0&Qu6sK0%wUC;F3VF_T{6bc!kp+_1$*g&h^Qq0JA(3(T^{i(( z_#k`2GL$y|cWiPs*~12wmE96w0oiF`=_)6X96t!T)a40oGasDnJz1_mPyedY=l*eo zU0Ge#exAum$waUzA5L&33=N|04|~p^*sFjnVKmPPC;VR4MIhd>)t<94vqje9w`+&U z;wuhq5?qnpSV;F0fxW${;6=^K9#+xKcZAxaV18+^U{ho7r*TQ9-|OlkvsOmKO*cm6 zF{YZz?-hs1NXTrnx=%b=7WzI}O5~5efbafLvX~YwRT@(_XdbU2@%u=lg?C%uBZH26 z=zE75EWUTAUFRQN`!o@bPwXD~GrJwvQe6>lvmuuR*Xe(D>GxCpEbH%pmtstJEt#U{ z5Zc}%h_wC^Mgo)-r&d%_iS^=Qm8E5k-KFn{#N1y_ds)A@stT<>iRW zED2GRPS{UICHEG-G+zvGkDen2L8M(-b7kEjn-Uf)I$MLO3 zYH(b`msH_luv3;q%(j4uic4i4nA+c2{zhtp0C??n*&II2J&CAM7Bi13YRZXvN|gD*U1Th7zG_IsnE#++69j5sX-b9jO*KzO_PU*> zm>!Bxt$z5M#8UfGU6k@lp&TEApb;%A=hfBZMQ87jxAWFkZ+B>(R>1SgnKtT>+OP$p zHYVyfoG=m;r3b`6s6_g zLT#QmWS-qujH9(%BH%i>?YdmW31J@Qb4ATL2-+M4;uk00EgnuV!3<_i(WqkN*SwA_ zRQxrrJhu}0J@R7pDjTPie*;qh+uIs9Pv?9JuUu{gglk==*-0ZR``?e{CwauMYDPkW|T=r=aZ zzL3~N6hV~m*pvuPnm(}mA9f<9v=TvVN!bIzl%skJ#op~2#*fXl{6se*i9k?d&T|W6 zSmFpYKnNBV=%)Q{V|MJ5gicgG!k1RON?=v*1YITaIhOyMJO=T?4p85&1qYBe zc#qe0VlH^C?z0tvg(_^CLRZ5Mr#0cgpj=;Wpn)sJ{mtkiPh4XTC+J-&e~AuLR^?c) z_3(7#^tcAZLULL|XW~P!DJO3!$K;HSz7q`R>hN)of3^GG(yixoG&{fNcFSoac)=L+ z^5F4!&hj#F9>321m_Q2u{`#ykzo*`5zDBJ3<}`L$_5dA4o7M}I8+Q6|7xv;N8%wRl zRrq_odqUE8%1Lrk$kQvn?{?;;13pWjFia&w34%errIkB(n4pL`8jUR@^FNP=R~f?cU>eGhMHeOy{??);Bjd7Y?#q z^gzTPTRxtYFEhF>$p-&C?+Jce`xG5~zZvfn5}a9BIA@eZ8}N`NW=YdRKseXa(jH!u z;{zopOjYaKD38?!`Y99aU~#P*j~s8F+X^!pwe$fSivfezeuT8El~Ug~Tfk-TaF2A5 zIfdY_Q=zv(SYxsv)=_Q3gPRyy{7uz_`5y%Svr2J-2BAWNR3B~ff^*UQxcKIbinE=G5)3UL$otA`sycM=(K=0C(2KF_QWfJ?3TZH!7QDG|`$xBDq6)h0Y3 zexOA%Zg0)0NI&fo#{8teKEKLuBA0eBngc0;^6xjVv=1UKyKf7>(|Hynir7rSdGcZV zI_4`cQ+_xB!7fC2>u~Gq_gN!yCF^x(>XzCk^kvDTErKNMWIH#yj{RH)IoRJGnR{v_ zZ5b41@4!0-+sNo2bhLN70nvYrF1!5dCg?mParKJ8H9uK^%tUx+nwM;p=7EBkjjk6% z+R12H_iSs{a2%U z7Vb$lc5Gqs9Mxp)GO~Es`s12p?Ly;W=0c9;p_Q7PjFua}?25rX*2JH~TjWM15MHFy zu)+%Hap3wyy$b!eSpO@6+C#LZ6(fd;c&S@_%ysXl)BPfXL7a}A-79?4zaP6S=>yjA z+jZNUY|p~QQwG`%O5bl|bwjg5OG~pILIZ^8_y+zu5p>!!$G|WT0hCL0q`TfZYs?T< zkon4M{~SXrj+*GqZ#|z#87`x75@XV75RIfBd{kx%V}(3%?Or6LoKp7W3lv6gdpar` zIF432NnbJB8kaOcjVwz(x8Hn%%KdUi;MkkLhUP&j7#1#ED<53R>Fy8%U7-Lj(VjHF_xktjg22tX~S=V2)J@Q zDxk%Ilfryaz1IK0Ff^IVCgKR}8NL}Z>RZP_=&2Zh4W8$$pHH*D;;sn^P};}GC)or< z26tnd4OE%n3<>kQ0N-kDOvPqtiV(|?4$bnaqP%UOnB&BfoR4^H^>cmB@!Scy`e&i1 z3G?aXxsYotOqO8)HRC@aPIgS6-cv?&UUbcf_nb`!xCJ%cowTAo-9#JxUIN>PU*UaB z^9=MaJ@)41IM;Q9?2_RzcXnd2aZ$fn!?+<%XkmznF&)_up@O0Vs&6xU*;kDXdkBZT;f5$QKxs9WP0kqM~hI=nk+ayjnxuRQZBQHv}1 zOSmxoXZ_-`ByiIII>Kt5jq?hUpdKOaU%!BQf=elX|9uSJv_83F0HAbO((lXJn_fL3 z9zjzFZZ1w9pxoSuzhQsZJ}2rp*vMl*bfpnV3^3yaS)yZ_@vse>GnnwXf>N503m*M) z%==e7uDHoY0+wLd1-~PhjsLzwO}tTbJQM19`XE=o4iRa9SOg6hXD(^y6_%ZoL0@0q z3x|2Aym56G%S?yZnEAyT2g6RmJtcme&{t>AaFlW?Un#+S9)m-&6)c_t#qNKH)e~gy zCqJ-*IF4YY0h>!WKA_y{h*iyL2n#9+EOAQ$`kIg)%30WiNjc@(s?lo)$x26KwkaW; z`2w(F>{VE+MNd3{o{y-6n|&d)Soya6xg5Cj7aiB39sU;m;l)sTt1^rx^Q-RovcD(^ z{Df~xwIQG>&avAXh_QD(itm4$!{f-kZ}W^}Xc;VPuzit4aH~zb;C`(c?WW6ck08AN zwIXK9uzgx9g;E@}6F=81>I2KA_w08&>LL1yGqv%vNc|g>HkfSwkM=!vhE*Utn%eNJ zOb;KvqI0&Q!0lmVZ>U#Cp4Hs;7=C*<==(4_4Q9U--~hfo94EyvVxyoqaT=QgO{V2Kn=h`BA*vi;kI$n0=YzBQ2%csx8)fMLGb>jsypA5AWh}4?W$;e#>*ROYSp5S8l$7GMc&{j9u>Qys ze$*3L=!g05 zShsKEB({jbVsl@0BeoRL1wKlTK)N3*oPRos>LuLF@K_bCH;hBY_%se%!+rN?%6QdP z4cPOdlLGdF{EiK7Emq8BLtcSi^gGG! zZF>4JdvO5mMIk+&m7**m(MZQ`jb8W)pJVd1^SFA3bu@aUk}U2RhUkquo(T?MDIGFRhr2_#rZ_RJCm{7 z-dGDGc+h8U4A1Yfjm)RJhX0Mco8O2?Hu3_QOM}@rqNBoDV@VEXBOlIH=+WqU%C$Y~ z4cXkZh#_E&NAmmH(DznfR|Oz~q~CGdTPQfJ4*N(34Y^~+uGl|+Ul6xqb*;#OPI>=T zb!X4!><{dwrYsM{mo>X=CIUX2_*#}l);rdz?+11h)EVr@wOWtD>bm>82~EC>zK=Y4 zsC7T#|7}jt)BDp+Lhj(fPTMqF3hsz4DfsfmORq2V@jEaZE69E=OLHJYW8-1os4KTq z9X*2(J&-wWUWs^cJJ5=4+-O^H(-*dpkN&JAkv*n>TB?JDnF_bcmI1LTQ6u$ZxmBEO zHF6S#d!FWmeV#lNdaDtJ_AYS}IUAUTnwQ=aFT9kr?h7{H)WVc}USp6{SkKVk^WxP1 z=hV{_r~^gkex!(C32bBx!(vzmU6K_x_6L3l{YdL(^8*%+CV1T~_@Kxw&io?63OCt< zk-1&$kfJ7$8YVLLvyPb)Ubl1ad*b zR@MO-_vPS`cp2gY|4Z@Y`!{(Qls}!<>J^OR!Q&1Dd42D*MzMe!g4Bbg&d7~&KdS~$ zD9-jKFi4$}gBZCpJ}>~*_$=F}F^0Z_A${i;H~Io=&!;quS)(P4>E3y+{f?3KCL)XZ zSIt*(X}FkBdG$7%)z;V8-;QNIHn65u+IMi|ZNP^=BeCF+Lx*g6knAeX$>Bhz#I*{(Gu@*?r4$@HT*m8* zVw*L3n5pG!ieFB3JrNcAb4+xpb?t|Ac;g!Cy@r`xa{Boq!1IpxDK(X{)QG-ix$*Bh zz=C}z^r)(92A-AJ1_(XI7-BQFdV^+&L6MPiHSqlhWYX2sFvkNqW(JKW1f31_&V15{ z6Zawpt`WqRPKq_)wpW=S;nekL{>l?W<=#Cn=lu~moP;k4Le29xxlz0Bko^#_>f2g- z6hC04e&^?eoCMh6E0JiR_q3&p8LJ+95h5QIa~>lp2@P^aHZtSJBkmZF>xuz#!*!yo z;2ea*y2_Vx1sgdHpAg8x66o`oxjNy-c`h+xg5RN=)CJrLJGb__5%JRkx%ln z>4sInAb4KGB`X7a(#F>FlID&Z(SQ*+luK3f3@5jLzls}?>qhIWcQmRHO6lL8(}MaX!X@gJlA%=(R85)d6K-s|NndK|F!)Sz;|R=MLEXSQm=?$+Nuq44qmvddWX{}ROagP~L%HL*d zyUt@j9hTeBIAiG1l?LhFsC8BgI9XQFTk;9VJKB;)&VWYDcC&pC7SZ?>I`g-k9yu(V+Kk-8B`K&OS zkuLQE@%F7C(3VX|BG z5jwnf2AS*@khmS0^gL$j~4a zlD$kM}Sz^7;pOhtd0Br}%CUxS9Vq3Z*U$A)K==B_qPWU?B!5%ng zK#cNW4c1Jo03|z^9y|vY1avNF+1q+utBE_Abtf^d&v(ef+PeKAl=YhE;Aka>S{`m{ z@T~5UUPL+l=Z<%UF^i+2pS|Llku~>u=F;)J&W^b0cEYnO23bejgQAw+;lJ!i%6;Tv zf)+Z8p?t3dY}+0OC9bQjw7-G+aA`0vy-dR~9^Ed*k>Jh1#1~bfXS1QLd1^#H>IQ^}8}dEcQZ=b-Y`Px?r|ea$ z4Gsscb*Ga=KCoG0PfWNbQ1Jo{!wd?XAI0d^IK**MmQR~Sr`#0b6ZMXt5SwMKU9A$KV%-v`5l3}3Ogi40(Y zL-uw>hvT~EIuZ+6;CQ$3SKy2gTH<*7 zYRZQM^;*0Ff)nz$vB8$ds)Sf!xkQMOzN7;WyEuUL8g2Q!B-HfQ7?loC z?P-n!68&YQHy%`U9&TYGpUZk^w_{lla^XeelplmHD)SB*sG8vD8X2HSaM~JF&mQ?d z81JF2Q}4NAcl7I5S(tbWB%<`P8X3}u@xZ>FN;dc(xs-eYurElQ1%B;kbL}Z4!aXtg zyB=4vw}A(d0o2_+AS2a7TSJV@*-dzwz_O9~43tzKl1&F&NeX%K9%OC+p4XZoN^r%5 zuFnzKV_~7+iMlXhhUfl$4)z0Xa5Dv>FVL_7(E^qXAz0p#+@AMUBKfX+n72)Kaf1A# z05YjW^sXap&rmJy$IQAiE1ZIpSsf27E0HFtI@h4p6a9Ds^vJN7l@I8VUB_=9gSgT; zzJC+qGfs+Jr^6wqMiGM#R-RxB?X7(GM(@wCjHf@VkPMoE4x&?4F!!nlZv->4AR*b?Lxje40g{ zzZoP#ErjZz*^W@OnbpE(9uJ{j<=b=8kBfy6ZUhy3S=v|gU4-#b$5l(c_ol6b+YURD zK0f$$0Yk)UO*2#_A{;mr#6s6aH;JdDUjD}f^Ahh&C36XhvVPy35f1i+73C6n-iN+Q zxY4I7Gm`L+45pFn)f9#|)^wiK?zqI!6j}$sA^uzW{pBb^`}Q=Xrw6gTTi-v2M7@ z88WYj?XQm|m!dQoHIoRy@O2!NOz{M(IT;LHAT<1KvjPZ{^>YEW5#@tq7~|ZwxV0|r z-lTLKveK^ad%pdwmp=H!VQ!4H?q_R)jpI!FW}e@X?ue<*+%7mIItV5!WkrK{KzfJ+ zz8=sS)cI9gj4E~ERI~>4PWbdA{E7(MGZ^&|6iWUTE`;Uu=ap5huT-Bizh@+rU?;X( zwapRBe~2osp{l~O{%37G6pcrT!PGPYm|I!!nVSpK|3<=be`R7XDRX9_PyNVhV}!+} z>M#WLcfh)k56pnn^VH=H7D{Mcp}{VyV`>K#F9OH+22w&ip7D-xbhv4doPpqA2$$)z zT&B2-iw5hZ1h#&|CjPKeIjPq^b(z60tT9ZCiI|~%s_1-4WUn69hWu-CzLJz*GBcl%lq`JFgR_uc zQek98A!Ivxa)I<}T-@QfTKZp4v~Yq!DFAF5$;rk>X(!e|Aub~4E@3RZ+bG2pWty;u zPJ0F`*RKR%jWKs*F64lb41Tt0GAkJPB#HFgcYv2#zBZAOw$Gd&+zT%CDX%bQ_w6J> z`zfjt&z6}iT$|zl>gXy1+G>_)(BSS~io3go7HIKe#T|+}#ofKdo#0m79f}nXuEiy| zL-CjI2K(ZSuEh$A9msDJdN^D1i zg5WYgaVv-u+6@HG5GYUM>e>3EkBgTK=cRf?fj#c&BQf(UP`!1k!c502{zBs&nWlaL z_eZDHKDxHus1GYOWK+APJLWg1nzz0tJ`p#U`)zB(?z;29I|&XKpg|ajdKn#4q}%VY zu5Vyr1)!P&W%6wu_qKApaUPjE+4{Abu|YYcgW!(HZPy3`2St(~PqC6r@VD_6)wL5k zsNFIvjkJ4GWIvY{SNQ7dc!7W--kD*RXdgX$f@--lP;>yrwq_^0|l| zyxFcxc!Y2L2-&%UsPz2R0S!A6?37Fa_D?4Gcm;oP2nM&3@-i0`!ISh4x6OnddfLL+ zoIT&>*&4`Y)Xn`UY+wT(Yw?x!Z2d@ASbdi%-T0Bk$&6gEiAX+t&}f;U3{XVdq6GA$ z_8?)tC7Vipmi#m>3M5%=4yW-&FnAAL$rS^|9+qZ;T?KiVIczNi+)7STtdnQvG8B?P z8n+hFTsg=?iPQIWY=qfQ@m6Kff8d%)Xhd^&!g+&`)}G=et~s(Kf8x)HZ#Dd+B%$T%eN zBys<{aA3lwq97Q*crG0(nZbKt=;-E~<+Ji4Ym6=#Q~e+Uwp1*Q9A|(*manO*dgbVG z9Mm@<8&W=l=XF?*uPxv%>U9sGK^$l}0pY+Ejxg3>?#NZjpz%@!wco-;03VT>8exVI zizdDNC9wRrSYsqUp1}?pW%~nuC`TTQ`!>mq+GfjXhx(tgykxX}|4bU$8fP)#VhP7( z@o^(OH5tC*2v4R+d{|fxS9@U+=Kl5G;kGvZ@uGY67>l z(kF12vr!|r=FJ;mS^gZWoh&52CfwTxj>Q+gZcrr>!QD$B!MH#GUJS1*l`*o@$tjdL zQ`dkC$cDs#6Y6N>^b@=jAa)soKacO0o@QOr+P=pO5w8FG)~;ESQY_ndmnS26>}OnY z!_i%j4ezkYdZRRn?cEi%GOEA7t#I~lJdOH$6L-_W)DlbJ|7Vmt89rP50ysg!6VchF;AaC^_>BY6ApyHLm=y>14> z^`R=-d=J)(h_gyrqE!(4WPC6#1Lz&Ygr1_KD>xnZ`p-(;`h6T0Q5Or3rjoZ%1$)o?3nsX+3tf?-kzb!1A%1-IyO1N}a@ zg2q(S+%e?unVHPA?)*S_AzwECqk7qIKyizxu$58W?5!i1oT$y-!NY%)rv>;n1oG(^ zmVg23FwpQ~$@`Yzz}9uKI*TPkj3Yd8xEDjS#*2X85rqxPVsgPDkC^Y%`wW5(Tm7EL zz0=yHBjCJ)BV*?)7@Ts0yLpta(^C^uQ!Bw%#M2&BYK^dcjP(8lI7>WIG zVZHqmaDR?Sv=D!PSn8TH^$CF7sJ^rRWm%~9^0MPUn9T{*dWPeC+Je{W<{X}qy)ScfXabpbEd!-Eh>+zckcO7wnLUg+aLBpsGs zYYkmfYgA30U+x{9Kz;^wo8xGB<(kgyljgt(QWztlle>KcdV}IDdD_^Ki#nbol5bEr z|L28-JDT+MRSG}aUvlqiuV!}MgM@qf5KW+JC~pKU@x}`d2odfEA*Cz;lEa-3s(R=K zOi*s}|0E0Qu;Dn#&Oj^ITdtGg6wi}EN*V8+iY!bIKkofBuP-(kLk?2iu@5SJ7=w|t zNHI+N8p2%a8_6afBRO3Q=XS2D&kP;+zAqnYzdT#Rf&vCuS`1Whd~=aQL+@xwrmT$V zEX&Tz&IZ96+!AlMt!baa9+=6T(k+D&dbB9|LCyb+FgOU@ptdyW6SILoFTO;FU@0-M zl%2KNe7o)RD#vA?yC&fGfUA_qxJYot`#9dNz+HYXY~0_vM#~C?n8MgJnYi9hhs!?b zm9TBBfo`oc)Ie*H{qdaoM_NEVEYWz2BrXzdjKLXwQ_0u|4Yvx6Tbl^|b-Idej-RL8 z+(ZIsH1KTXfZG(R2B@UG(HNU%V)tEKPK{zbI>M+#D8KJ8);#)+Ce;lRt4w{SNDGF@ zXsj!(V|Ftwx$12OJo-)V$1BnX=x5v3iUX#h-zS#0zcMroA!AdS)^ZMRDn`jR)uI7a zbq#52F%qH}0(bsbf%DO?3C73wJ}`pa2-4(ti`ZaUuWkH%>Awe$AJy+E9eJMa09G9r z+1lcnmO#YkWaWq&zTB3LLq?YVaDfxQfym(=Rvksp%2} zYnaT1mwgW~jEVtPF&He!xoz1M|NZrQYC)zg8Ax~s6F-H(%~zLW$c-L>q)bTejo#Iw zd>7nc!vPeydnL~D@OuF)d7HlV&j-Ey=RF0;&Ja$r32o$ZG^6YWJu<=0)uPy_ZQ4gw zIuz>d=66`qm$QQr5g}}bRET>`66**yzvV!;}1y}>^FgzqD5^wk(!XTWQ*85-Xq4O4nn5#om} z#_nVoe#kL0HE7$sNCoTS9gF}sNd?j=2hh6~QI=tL8VvhRZGFG%2WgMS0P9IT5xw%KYd z4kCTD)wbjNLGAWTp{Z}LBv7mA{zgS;B5Z^l+-i|Qpy>l7kT=Yb|MG3@RN$UPMu+2T zh}wKZ!!D3A)lzM)4NaA0FFH!XR9BzfC2Vp%mlN*t?-%5%mtm9IrgLn2$*03gY$F(; zpae}BC^&gJ)7X>+fyGLKqzeQ1Rb%h{#Xc>cdIm${`WzLmyQD!S)l`Us30DX*c<Sn{CCM5Vzzg7T4XNegX5`&IeDzqE!}T| zf&V6^BUsNIiEWI;p|G_-n2Ia7F@(Y-`0DyyD=r-P)4%lZFIRE*@JJW*D0%N-dC^+J zU6zvq#@2EKP!O2eJD8jhjRjlewFcopT<35j=w~QQgT>FlGUVHA4Og3Jz?&1z^Vb0p zca1RUtYo5Eb*_{wDWgF7ljmPj59UcXNy_9;J&pzP!491{GG8)ck_mGI**T2Fr)rmO!(v1Jx1$x&LS#Nv~QCrB_GZ+}ViRz$D8` zHGsVH@h2m6chn>WvXLlork9nq%PjLGIM?`8OOH%8=n;JVif~uV)~KrQs)YO}ji)99 z#;PT!sOs-0%Bvcuo*rj%B)dv~g`W7elj8shu=E}M%&2MBy<0~OZ~7zvLKj7%&`GRz zhVc*-2++kiB2TbSMiwd1ZVGGK?8WOH!R|K0QKW{i3i}_+zYa7)AaQe_`G*WAa5(lriv!L48pf74c~e)KN{29P5L)`hA8VAqVS{&Ds^4WpT%L1*B{h zcvQ>m0wLG_8QCAKFW(U~N!)f)MDhzbtKpX|ja6Dpl^j14w@QC=oEoNB+FpN{MWAPX z?#Gfk@!Q$D7rqkwSNi5h3`^LP-CUf$is5qE@GTW)SkVZ*{xiqgg=H^e7@a-i*XFhZ zyf!<#q=E*RXti%tf9x&8p?>Tw8re()1$-C6?Hzkl;PSSiGr@CvPAAacA5D@~Wq4T$ zQ?wawbE##t<{

Js;luvvs>|NvjY0jG>86iSe{!QJ`4L{rQ(-P((eihSc99v>%7N z!Y~aATlv{9FtJ}dF-};((tQw#N4l?oqt7c9Q$F%93kDekt(;K(5Dt%R)8 zj&*=~4DP?JoWr^vf3g5e?TpAYS$3Q|W1e)VvufW6H<*WAkFxU)7IP$~DphaCW}vmY zi<0ktGBht3g<yA0Z&ByO{r zJd*alhKDXJ>#_7s^`?+V^xxpHmXs5`hWvNC~<;h<8xT9`{go>tbNWYrFk_<#ozh)K36*5lCli=9?K_&c(s4m z5z3wr6dI{%7?lp2o5(Oh9A+!y{9LFQjKz4a_HIAiLUR*bKhMBYbl3hz-0g0vfOtpd zZEA6^u^CDY1T;NOFFd>tXndu;o`P}5U9%xn*V-Fjyz2%Ii?AM`NAK0-rg~MKLXGU; zaevFjAtKclJNfdot7Q%0gB~N#bbnOqoeDMWsq#v5bzcjP?8HM${+V5yuStx zQ!#19``?`rk3k>v{SxqT`|^~uvBE2v^u zRAF-d3-g)5XJ9Cli0_%!ZwmvfsJzpeAq7oTeIKIy;G>~^{p|9C&Ds#P$=1DjH!Vvk zEW?&KO{5-on}aY}fDu)M7h2_bhM=((VI=wp7vI%|;1n@LB(9J5j~Cc;GtUSE9BJfk ztc*XKT2(8fbR@lXjngngZU?Z=H(U}G+Bgy;wRzoDDPU@eh`YEe`rkKtRW#WCpCf01 z!7ll@KR>4Txk?ofo*Xm!D};I?16oHDMI|`niP0~@gN3l@n6UnymA|(Oj)w)c9R24? z@rDa!O*G&(*=(iWeqQB7K_VaL81hJkR&JV(bKkBd5WhjiM}iedwlQO__J9?yI{GeCakofi!0!IXqco=Yoc*c6{PzXhzL zxB7MHQdf<%IKFmoB(S@bHTuL;iEm>)N`?`9B6$9H_o{v+i)qNa{#6cE>PZhJ@iuBZ z3dMh{Y!gZX%QQDvpJxzqOFX5yJpR_&B zGFu}etyhgB);@mv>iIj%Q!;FQdWX{-@AxZ4PFq96-$`Z9^_@QekeOhkL6)9kNB2#9 zPSw6j%VqoAaC<$%_@fkWN@=e>Y}-yvc3aK`RlA}0k{+> z^07**V*|sPmt+)~hvNh0r^k=tkdgV6Pfk^lUhLP7X+G;+4J=$tPV=LL=wpsV5}1Qc z?>XjP9UAVC^)b($3CA+KKF`FdQ)^d$N4vx9kUNb#+FcR*Y)BL7qbl`W+e#q(b;NL>XSVeMCHu z;FHrf3FhP0rVWy~B&2U$6D$!mto>_U=mwpUQ6TFLZ`HbbM@(Th$_%3p3j;~SNzBi= zBrJ7KBd9r(Y~IL<=xHI94b6ASKSo{mkn&73y9exbcqO_=MO*inDK-XhV~UQb?62zT zGPhQ8_LOp}ol@w7^G`uKof1Dt(ub2Gg#Iw1>znu4|MF__XJ#Mi!NPFM-+#*xok|kz z;i|8OUG4Uz{mr{anU@fHb^Q(0)>))3ry+MR)x2TBDpl?ZRmd29wK{SXV5wOe zQ%~p=@}qeDEs~Wg{|y{4$BOXRWZx?F&JiJIn2#2xJ!{~XPQ#f8_xCetpdvnZYDQq- zI*I$WF(TMhjM4Bqe87!w_nM!ZAq#{5Np;Oz4hl)C&QEg9CW$B~K6ki!00;PXVA7*A&avnF%WfBlO_Jp%J=z6~Ibf>FL_&HnX0P| zz!`)BKRe(0%1q@FrE)&HXd0_Vq+pBbl6_Q5tj9qXS!-vWE;BGKFRRKgG-{&MC0pV9 zrJ$yPR@27vo+Q!xfwt_2%lDeosZgGSgdAn_AeZN!1H76)@fIO!(I*ByOS?GoyZTE$ z40{t3waU9VH$Ukcyv@K4uUu4xO0mM!s!m4-U=jWI%Yoy>Kc!A& zAEhEJvALDA3Vc2PnvnJpJe0GkS|0`6^(@VL(M*04y*thD14nPKkS!zX0Pmhpe?U zz2%=tu2+z~!n8@nWuWYI z>YF%6@}}F5WVS#-oi`~iMza94GIx$W&%3$v8o!now?ua>)Aepd)v*;>Ed2FSx<))BBlAcJG&h@eDhroGHxOFWh7r372pAD^ygptm;& zu7kpu-XS$~pu`UDh}kwTwPCGwS;@h4C38#Ff#_ z1s^AO)wy=u%5iX)WL^xRRW$T<40eG@;V^AZKf_nzv)7>cPYK8kZR%m3r}zbgd9F=;nRvCzcYG5|>!&ozPr5{3rM`K|p;}rzvy$Ve(G`zGP3|U5u>j&a zjZoVEVAJ0fND)sy&!IAz#ksi-|CrtoBf?QnM^BdN6F`FW4FZ-`h|TUK05>P7ITSuS zG;ra8KD%^Mq|jZKG_TO@n@u8tb%c-4N8)rsWK-~C092xHxY!c~eVHs$Liq@YjcHEK z8by2jN>af+Nyh0X!!d#5ZIuUm^0{WN?D#+abW812J$Q97KqzJH8sO=&*&L0nP=;lg z0K$d1ByAvsAgW)HmzP#VK0iC7nt{sD-J3NWjKgDr4gf+Bmga*=lPQIGT||ICAx;1G z8sQeoCx_-o0L%Iqn)B#K6AM6~i!{(U1xaAjs*%ZHwpBrvOHl&$fQA*t+g;?<;r&N6 z@WspLNB9?L_c|WbOe+D={IP?~6a{115{Yj!fPp4KL0tkec*N=#%hMq&3vhGBLS*b=)5afDI0Q4y#k*}II#QGY0W5w&{`C1@5!;Oodh$^NA=<7-2P$E_$gF`x zsnS~l;jdb_8yOmeIKxjN)0uq5FMkO&A1ri zVx^fj%iB|q7kA8_RN|qKOu$q`7Loc_RjXS~GOgBn^y3)-{_tBYa?qR7BwZQ!=f=np4XVFnP=$#HGT~;meL)mT@(U+t%K@=1NaqR@a>^ z57{?=)T4&}=H?wgB@CnrD7z*>JH8gss|#enwdLZ;T7d7;zC$L;avQJES!G}j{HS6` zAm>os$M&uSp)bScmZ9dp!c zO4KRs{Dq<|S_|#xtJ`1_Ks78tL)k5Ct?&>HVz3@$%_H5F{r9sknC8Ws1(b)bzHufc zNO5L|vjxw$CHISpD&f$0R_Gd4ioBUQth8Irobb3Ac{X!oaX(%P>AG~S+=-&(C7FYy z+nx>(CRGYkn2l@{cDmkX3aAA`AORT#j;zOct z3BCC73xi0Xp_tWXYy2k>uNVq)0$?2bAoqP`eh8zE-4WXmN?G*Gs(`Cf=a6{xWT;@P zZ=5@kB!-o`dqTsnPOYL_t97j|C+^ZVV6qWHx*Y_GM|4h)dU{$#Q-wnvJYt!$LqP##d9OD0zQ z)vBh3c(eXu&eNZyf!<#8LuDxr#=%4L6JE+a9vCI}EvDDJI=#t?PbyDU)L>BXa-7rO zU&AFGwNQ{V3F;QtzQ}L+bmKz-Q~Z+fCz`Ja?{;$}~lCIwmn?r5cv zJynauWyjNjq**QB&v2g%>R$o`AmuDVZ(p>(@MA9$)YSTZw#`r>2>k z)w7y@-VZNZ>l;nHk;AE`p4iwdRy0)OT9bLsadh$pP%%nX)oE}X14GvAx-)0- zQsmuoV#iiFS)^ROK95A~icknWTVm>d`t-IS5CYDM(}dja4%r@ z1@N-YKk&!5mXM^FYvsal_mNvVHd!^mboXnjH!7rqbc%ezaR@~;cC7?keDaoR;%beZcy?Dvys#<=9t_ngc|H!V00=v7XMq+m%H+ROm0% z^IjhFoGbsP3W%7iL(gXuWnN>NI{4~%L!i=Elma8))Cs_-{tLwc=Rfh=|I~h#$o{j) zHi2LBn$GYZ+9F!R89nV~D6$Sn4tVby}70Cp$tglJX&nQ6! zz>u&Ew!Urmiv0@>{#M=zvUY1m#?+Z4bA@BVL(XsivCXBa?BO_VGp6T|{E;|k*XBVi z0iJM&vQgHa68DGb0vlWvmc7St`B#FYu@reP`yLLoikcly5HVWW?%aGt=-;}x0HdWoU)6)(Suv}2SZJRm8$LF_PdBz)=}{l z&lyP@pMB?5Ub}~xeZAuK6{343j_D@zwlo!eMKK@?^Hu#6UEb7Be38V`edYD6Y~(`z2>(6(G$QzoCw=?#|cdt@@Df7>s0uFGh~%D+TqE&oVCY3|mVwOZ%T}4Hvui>o5;E}C7 z^(%;Un(_};*Qa%_$CG3NPbJImk4*}|fKI1Gsl5nmxq#Ia^PGu@#Uk@!Y6)kjdoKo2 z6Q54U4WHF<-o8KSv1hhQc@lmkKa)Xj#8*iXcGc&vR_7{Tl_^>quZ^z~O6_Lj&Hwy# zw3sqqeQWkOSFxx33K#|sPgE8s3G8@_C^Jm7x|1);(rhVjf3+P>5E-FXS6VINb*C^| zVu=4X#)e|gPJ%`yX7Yc` zC6uvWyI)C|!>+e+c~k^z)&w*}YVgPalaXS&V z#b?->0e&;PQ6|h?%PG#$>u>h+lB)3Idzh*4E3b~tuR<5EdS%`NP7;mMmIh&BnLrNK0YQzYA)d`6|O~ z?Hy7L-AWZ}VYb66kC-XyJv0IU3c-xcf)3sj#aOr5@jXF|lu7FU91~MjE8`(cOzw92 z44zee*t3>K0Hci&mVSu=T)5VclTl17y#g>T@uP`0GVvU+67kv;rdL8Ml0oh`Ricnm(V4ERec+5;2huo zajdjVOO{Od(qvLa5U;Wa+Vec9CASQ zCUvn_RdqeDt9eR%_kDf7%abcCCg>9p2wA*emgaGUiwjpSJHn+mj67430&NEtM(4J? zN7SbH-5*-Qi<_OFdtB~Rzo82C34vYW8l7fkmRUWdO_TyAL&wPKf6-iuepbWhRroAa^WG8$lG~eG z8HqBH8Xdk1xpCiS|FAcj2ebYv?{Lt1;!iU}X1rE8wC+Ew+TM#y8K~z~L0@ql(?CpQ zZ8)>s@zz@I5`!rMxX<)y97R9m{F6<_mME;tx+Zkz6Gr|Vi?C@5r;;*##wm?VOq8^X z3Fvh@&>w&n>YF~)_X5TLIA`DK4uB z%x!$s#P)rY;!7r7aDbIepWUXgS2o>w03h?2G_N*nVSF^Bp|b)R05Ra{Qu`^bS3lP= zW({)*<-qqv1!;B5Ma#&JQ$V1=7vB~3u<+eEuK&6(FtbJaIbB+?&xvkm1s<>>uh>E) zka0?dR#y!OY;JJnNF!YNp*8f?>ESg|DUp4sh{?Hj6!05N3Ygtp1A(0NAK+ z=t@f|p#S9RUiBv$=J7`ETALUm8vOpo|EzP+#K*oc>nm&Rr&(zt05)|Je=a9F@ps%) zxEjI%JaZvG7Ky4Ylw;xpuGm5Qa001bYT)6ddtAE_a|GzP4 zBImeC=30l8+7=Gb`|buqIlUmGNGBr$s}=`lEYD+5^=X=~zENTXHbj7kv=u)!7=P-4 z8LACC;pNEGtU*tHON^9q!~@44_I{8zl`BWmK5tJGcGTe!OEXEl!WK=&gm~`zGXP+2 zc~X^wmEwJsVx24mw%d_38cO_)%X7KMw?_d8970i?(#TFnCfDEmuQ8d`x2w;peCb{7 zLzOlF0H0RbbWN>Hgb!LMDIiexI#w|V(}~g_ae1dZr22z`o>cyQ#H{e*AN0e^~)*I&mbQ z$GNITk;=9wdng73fW6dWzOkuC_y15w*L%0$2@im1yxyd@&|%4E8t^8?dLaRNk-VEz z*`EMUZnMhc5}$@qXaIn-AEVe|h4kttdC7fOkCvI-JODuJXZAgE-NTjhDi58yec)9& zz!?EH$Dnnej@xluu9>b=Qjy+X0MKErQ%2>e%qxzyMw*pYp9M|)Ctl^bjtofkvTmFp zSb)GM`h)0)rw)Yo4P6FW1&RC$QBj(Xo}R9@(>v(SKr8?x_Z6v`0vg{YKLY{4UY})@ KKUPZ{2mcQrDPi6K literal 0 HcmV?d00001 diff --git a/docs/public/images/nativephp-logo.svg b/docs/public/images/nativephp-logo.svg new file mode 100644 index 00000000..2b60ec2a --- /dev/null +++ b/docs/public/images/nativephp-logo.svg @@ -0,0 +1,6 @@ + + + + + + From 4e244789a2d32c482d3460a9074a1f39db108f7e Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Mon, 30 Jun 2025 21:45:45 +0800 Subject: [PATCH 30/30] Add frankenphp SAPI --- README-zh.md | 3 +-- README.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README-zh.md b/README-zh.md index bd155da5..0a0c4578 100755 --- a/README-zh.md +++ b/README-zh.md @@ -9,7 +9,7 @@ **static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。 -目前 static-php-cli 支持 `cli`、`fpm`、`embed` 和 `micro` SAPI。 +目前 static-php-cli 支持 `cli`、`fpm`、`embed`、`micro` 和 `frankenphp` SAPI。 **static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。 @@ -298,7 +298,6 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys NativePHP Logo - ## 开源协议 本项目采用 MIT License 许可开源,下面是类似的项目: diff --git a/README.md b/README.md index 8316a751..46200f09 100755 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ **static-php-cli** is a powerful tool designed for building static, standalone PHP runtime with popular extensions. -Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed` and `micro` SAPI. +Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed`, `micro` and `frankenphp` SAPI. **static-php-cli** also has the ability to package PHP projects along with the PHP interpreter into one single executable file.