From 12aadf18cc2a7b30a2f993896c9e2d5555e29bef Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 16:36:05 +0800 Subject: [PATCH 01/23] 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 5ed6d59dc..ee4a17fb9 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 10a693a78..bc5cdb33d 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 6cc36bfa6..523905c5c 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 d80bc3e7c..a8e7a8a66 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 aa11c3772..5a022b590 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 10e62eb06..ad787dd95 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 b69dcf084..a24eada9a 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -9,6 +9,7 @@ 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 @@ public function __construct() 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 7951ea69d..bcdbffab9 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -6,6 +6,7 @@ use SPC\builder\Extension; use SPC\util\CustomExt; +use SPC\util\SPCTarget; #[CustomExt('imagick')] class imagick extends Extension @@ -15,7 +16,7 @@ public function patchBeforeMake(): bool 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 @@ public function patchBeforeMake(): bool 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 @@ 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 (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 685486154..94d8a41ee 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -25,22 +25,8 @@ public function __construct(array $options = []) { $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 a85f3dec6..1e405768b 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -6,6 +6,7 @@ use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\RuntimeException; +use SPC\util\SPCTarget; class SystemUtil { @@ -193,7 +194,7 @@ public static function getLibcVersionIfExists(): ?string 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 @@ public static function getLibcVersionIfExists(): ?string } 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 aa2825b7d..6fb7e1fc8 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -5,6 +5,7 @@ namespace SPC\builder\linux\library; use SPC\store\FileSystem; +use SPC\util\SPCTarget; class icu extends LinuxLibraryBase { @@ -16,7 +17,7 @@ protected function build(): void { $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 0d56b0eb3..886eec683 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -17,6 +17,7 @@ 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 @@ protected function sanityCheck(int $build_target): void $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 @@ protected function buildFrankenphp(): void $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 ead786a22..88ae9f7d4 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -4,12 +4,11 @@ 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 @@ protected function build(): void ->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 62f7bcd75..e87310cb3 100644 --- a/src/SPC/builder/unix/library/ldap.php +++ b/src/SPC/builder/unix/library/ldap.php @@ -6,12 +6,13 @@ 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 c820f8f69..aabc277d9 100644 --- a/src/SPC/builder/unix/library/libxslt.php +++ b/src/SPC/builder/unix/library/libxslt.php @@ -33,7 +33,13 @@ protected function build(): void '--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 788688565..0808516cf 100644 --- a/src/SPC/builder/unix/library/mimalloc.php +++ b/src/SPC/builder/unix/library/mimalloc.php @@ -5,6 +5,7 @@ namespace SPC\builder\unix\library; use SPC\util\executor\UnixCMakeExecutor; +use SPC\util\SPCTarget; trait mimalloc { @@ -15,7 +16,7 @@ protected function build(): void '-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 05727f96a..3522c52a7 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -4,8 +4,8 @@ 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 @@ protected function build(): void 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 1de3e4c28..efcfc089a 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -8,6 +8,7 @@ use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\SPCTarget; trait postgresql { @@ -50,7 +51,7 @@ protected function build(): void $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 21a848a5e..f9938957b 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -13,6 +13,7 @@ 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 @@ public function handle(): int // 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 @@ public function handle(): int // 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 d5054aa95..35529f5ea 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -14,6 +14,7 @@ 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 @@ public function handle(): int '{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 000000000..d5a0cec32 --- /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 d0d9797e5..29a22353c 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -16,6 +16,7 @@ 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 @@ public function handle(): int '{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 23005dfda..cac17ce3b 100644 --- a/src/SPC/doctor/CheckListHandler.php +++ b/src/SPC/doctor/CheckListHandler.php @@ -72,6 +72,14 @@ private function loadCheckList(bool $include_manual = false): array { 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 000000000..4dae938b4 --- /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 fa5c9cf54..a526dd5ac 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -40,24 +40,6 @@ public static function init(): void 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 @@ public static function init(): void 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 @@ public static function putenv(string $val): void 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 8e69e89ce..bf441efea 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -55,7 +55,7 @@ public function config(array $extensions = [], array $libraries = [], bool $incl 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 @@ private function getLibsString(array $libraries, bool $withDependencies = false) } } // 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 000000000..2d81fc4e7 --- /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 000000000..863636ece --- /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 000000000..c845d11d2 --- /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 000000000..2ec8111c2 --- /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/23] 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 70758f855..a1cef3ff7 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -23,13 +23,13 @@ $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 @@ // 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/23] 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 c845d11d2..b9882a9a1 100644 --- a/src/SPC/util/toolchain/GccNativeToolchain.php +++ b/src/SPC/util/toolchain/GccNativeToolchain.php @@ -7,6 +7,7 @@ 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 @@ public function afterInit(string $target): void { // 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 3357f286ab0d20bafcaa6547d5b81ddaf843d520 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 17:13:22 +0800 Subject: [PATCH 04/23] 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 bcdbffab9..8332f832b 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -11,23 +11,9 @@ #[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 bf441efea..9335e88d0 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -145,10 +145,6 @@ private function getLibsString(array $libraries, bool $withDependencies = false) } } } - // 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 05/23] 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 a526dd5ac..488a2b6e2 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -80,7 +80,8 @@ public static function init(): void 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 e41d7899c7d89b393de65331224fd904b51e170d Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 28 Jun 2025 18:14:10 +0800 Subject: [PATCH 06/23] 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 a1cef3ff7..86b53b8dc 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -21,15 +21,15 @@ // 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 07/23] 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 523905c5c..7292bfd8a 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 08/23] 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 488a2b6e2..aaab1ae17 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -68,7 +68,9 @@ public static function init(): void 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 09/23] 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 88ae9f7d4..f126bf300 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -31,8 +31,7 @@ protected function build(): void ->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 10/23] 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 2d81fc4e7..ec7308bfe 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -43,6 +43,8 @@ public static function isTarget(string $target): bool return false; } $env = strtolower($env); + // ver + $env = explode('@', $env)[0]; return $env === $target; } @@ -53,7 +55,9 @@ public static function isStaticTarget(): bool 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 @@ public static function afterInitTargetForToolchain() $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 863636ece..ac93513a8 100644 --- a/src/SPC/util/toolchain/ClangNativeToolchain.php +++ b/src/SPC/util/toolchain/ClangNativeToolchain.php @@ -9,11 +9,16 @@ 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 b9882a9a1..2547334a3 100644 --- a/src/SPC/util/toolchain/GccNativeToolchain.php +++ b/src/SPC/util/toolchain/GccNativeToolchain.php @@ -9,11 +9,16 @@ 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 11/23] 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 aaab1ae17..a6fe29872 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -78,7 +78,7 @@ public static function init(): void // 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 ec7308bfe..265c3d516 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -85,6 +85,14 @@ public static function afterInitTargetForToolchain() (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 12/23] 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 | 120 ++++++++---------- src/SPC/util/toolchain/MSVCToolchain.php | 12 -- src/SPC/util/toolchain/ZigToolchain.php | 18 --- 30 files changed, 188 insertions(+), 207 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 a8e7a8a66..bf322adb8 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 3f63c84b8..d00c8de0c 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -10,6 +10,7 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\util\SPCConfigUtil; +use SPC\util\SPCTarget; class Extension { @@ -532,6 +533,11 @@ protected function getStaticAndSharedLibs(): array $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 8332f832b..514e79b32 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -6,7 +6,6 @@ use SPC\builder\Extension; use SPC\util\CustomExt; -use SPC\util\SPCTarget; #[CustomExt('imagick')] class imagick extends Extension @@ -16,15 +15,4 @@ public function getUnixConfigureArg(bool $shared = false): string $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 1e405768b..0f6d35781 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -6,7 +6,6 @@ use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\RuntimeException; -use SPC\util\SPCTarget; class SystemUtil { @@ -189,12 +188,12 @@ public static function getSupportedDistros(): array /** * 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 @@ public static function getLibcVersionIfExists(): ?string } 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 6fb7e1fc8..26b988999 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -17,7 +17,7 @@ protected function build(): void { $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 173ec954b..362a68fe7 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,6 +29,7 @@ public function __construct(array $options = []) // 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 886eec683..f0e2dba5e 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -201,7 +201,7 @@ protected function sanityCheck(int $build_target): void $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 @@ protected function buildFrankenphp(): void $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 f126bf300..d8d7e0c98 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -6,6 +6,7 @@ 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 @@ protected function build(): void '--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 e87310cb3..18393e248 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 0808516cf..6d89e6dfc 100644 --- a/src/SPC/builder/unix/library/mimalloc.php +++ b/src/SPC/builder/unix/library/mimalloc.php @@ -14,9 +14,9 @@ protected function build(): void $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 efcfc089a..7ca324c28 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -51,7 +51,7 @@ protected function build(): void $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 c7450050a..25a503ebf 100644 --- a/src/SPC/builder/windows/WindowsBuilder.php +++ b/src/SPC/builder/windows/WindowsBuilder.php @@ -34,6 +34,7 @@ public function __construct(array $options = []) $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 f9938957b..62f4260e8 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -64,8 +64,8 @@ public function handle(): int // 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 35529f5ea..1e895bdb6 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -4,7 +4,6 @@ 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 @@ public function handle(): int '{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 29a22353c..e51cdb174 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -6,7 +6,6 @@ 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 @@ public function handle(): int '{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 050cd88d2..f031d73a8 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -4,7 +4,6 @@ namespace SPC\store; -use SPC\builder\linux\SystemUtil; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; @@ -594,12 +593,8 @@ public static function getPreBuiltLockName(string $source): string { $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 69d3a6495..fe7d53330 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -11,6 +11,7 @@ use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\SPCTarget; class SourcePatcher { @@ -456,7 +457,7 @@ public static function patchImagickWith84(): bool 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 e0c7c5b96..8dc1cf2d6 100644 --- a/src/SPC/store/pkg/GoXcaddy.php +++ b/src/SPC/store/pkg/GoXcaddy.php @@ -7,7 +7,6 @@ use SPC\store\Downloader; use SPC\store\FileSystem; use SPC\store\LockFile; -use SPC\util\GlobalEnvManager; class GoXcaddy extends CustomPackage { @@ -62,7 +61,6 @@ public function extract(string $name): void 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 ac93513a8..c5186521c 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 2547334a3..bd8cc8688 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 000000000..567cff38d --- /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 000000000..3922fecbb --- /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 @@ public static function addPathIfNotExists(string $path): void } } + /** + * 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 9335e88d0..fec3c0895 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -55,7 +55,7 @@ public function config(array $extensions = [], array $libraries = [], bool $incl 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 265c3d516..5f091a8e3 100644 --- a/src/SPC/util/SPCTarget.php +++ b/src/SPC/util/SPCTarget.php @@ -4,13 +4,8 @@ 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 @@ */ 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 { - $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); + $env = getenv('SPC_TARGET'); + $libc = getenv('SPC_LIBC'); + if ($libc !== false) { + return $libc; } - GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}"); + // TODO: zig target parser + return null; } - public static function afterInitTargetForToolchain() + /** + * Returns the libc version if set, for other OS, it will always return null. + */ + public static function getLibcVersion(): ?string { - 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}"); + $env = getenv('SPC_TARGET'); + $libc = getenv('SPC_LIBC'); + if ($libc !== false) { + // legacy method: get a version from system + return SystemUtil::getLibcVersionIfExists($libc); } - $toolchainClass = self::TOOLCHAIN_LIST[$toolchain]; - (new $toolchainClass())->afterInit(getenv('SPC_TARGET')); - } + // TODO: zig target parser - public static function getTargetName(): ?string - { - $target = getenv('SPC_TARGET'); - $target = strtolower($target); - // ver - return explode('@', $target)[0]; + return null; } - public static function getTargetSuffix(): ?string + /** + * 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'); - $target = strtolower($target); - // ver - return explode('@', $target)[1] ?? null; + if ($target === false) { + return PHP_OS_FAMILY; + } + // 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 2ec8111c2..000000000 --- a/src/SPC/util/toolchain/MSVCToolchain.php +++ /dev/null @@ -1,12 +0,0 @@ - Date: Sun, 29 Jun 2025 16:03:38 +0800 Subject: [PATCH 13/23] phpunit fix --- config/env.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/env.ini b/config/env.ini index bf322adb8..7a41eb355 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 14/23] 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 362a68fe7..319047a49 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,7 +29,10 @@ public function __construct(array $options = []) // 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 832ced971..251fcdb52 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -32,6 +32,7 @@ public function __construct(?string $name = null) 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 @@ protected function execute(InputInterface $input, OutputInterface $output): int // 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 c8003a3e1..fbf3a5bed 100644 --- a/src/SPC/command/CraftCommand.php +++ b/src/SPC/command/CraftCommand.php @@ -10,7 +10,7 @@ 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 d3c7a0b25..a1886ed1c 100644 --- a/src/SPC/command/SPCConfigCommand.php +++ b/src/SPC/command/SPCConfigCommand.php @@ -11,7 +11,7 @@ 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 15/23] 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 dc58130d9..2f0c2934b 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 319047a49..362a68fe7 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,10 +29,7 @@ public function __construct(array $options = []) // 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 251fcdb52..9c07b074e 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -32,7 +32,6 @@ public function __construct(?string $name = null) 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 @@ protected function execute(InputInterface $input, OutputInterface $output): int // 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 6214476b5..23ae242dc 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -108,7 +108,9 @@ public static function addPathIfNotExists(string $path): void */ 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 16/23] 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 ee4a17fb9..5ed6d59dc 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 bc5cdb33d..10a693a78 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 7292bfd8a..51f3d29ab 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 17/23] 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 d00c8de0c..76fc76d62 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -534,7 +534,7 @@ protected function getStaticAndSharedLibs(): array } } // 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 26b988999..c911a2fe4 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -17,7 +17,7 @@ protected function build(): void { $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 f0e2dba5e..6f0d5045e 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -201,7 +201,7 @@ protected function sanityCheck(int $build_target): void $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 @@ protected function buildFrankenphp(): void $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 d8d7e0c98..d7cfa8876 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -39,7 +39,7 @@ protected function build(): void ); // 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 3522c52a7..1817ab0c9 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -14,7 +14,7 @@ protected function build(): void 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 7ca324c28..c2cc7165e 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -51,7 +51,7 @@ protected function build(): void $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 62f4260e8..8bfa4aafe 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -64,7 +64,7 @@ public function handle(): int // 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 5f091a8e3..071f26204 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 18/23] 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 51f3d29ab..bbdf957f4 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 19/23] 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 bbdf957f4..38d000ae4 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 20/23] 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 38d000ae4..20a26389f 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 21/23] 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 c219b83cb..a4e57eba2 100644 --- a/src/SPC/doctor/item/LinuxMuslCheck.php +++ b/src/SPC/doctor/item/LinuxMuslCheck.php @@ -16,13 +16,14 @@ 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 22/23] 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 7a41eb355..a6b3fc430 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 23/23] 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 56235b0cf..51e191bfa 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 */