diff --git a/README-zh.md b/README-zh.md index 03fa7160..0a0c4578 100755 --- a/README-zh.md +++ b/README-zh.md @@ -9,7 +9,7 @@ **static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。 -目前 static-php-cli 支持 `cli`、`fpm`、`embed` 和 `micro` SAPI。 +目前 static-php-cli 支持 `cli`、`fpm`、`embed`、`micro` 和 `frankenphp` SAPI。 **static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。 @@ -292,6 +292,12 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys 你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。 +**特别赞助商**: + +Beyond Code Logo + +NativePHP Logo + ## 开源协议 本项目采用 MIT License 许可开源,下面是类似的项目: diff --git a/README.md b/README.md index 94c10d58..46200f09 100755 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ **static-php-cli** is a powerful tool designed for building static, standalone PHP runtime with popular extensions. -Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed` and `micro` SAPI. +Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed`, `micro` and `frankenphp` SAPI. **static-php-cli** also has the ability to package PHP projects along with the PHP interpreter into one single executable file. @@ -317,6 +317,12 @@ Now there is a [static-php](https://github.com/static-php) organization, which i You can sponsor me or my project from [GitHub Sponsor](https://github.com/crazywhalecc). A portion of your donation will be used to maintain the **static-php.dev** server. +**Special thanks to sponsors below**: + +Beyond Code Logo + +NativePHP Logo + ## Open-Source License This project itself is based on MIT License, diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 6cc36bfa..20a26389 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_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/config/env.ini b/config/env.ini index 54996148..c49b9334 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=native-windows ; php-sdk-binary-tools path PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" ; upx executable path @@ -63,6 +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] +; 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. + ; include PATH for musl libc. SPC_LIBC=musl SPC_LIBC_VERSION= @@ -80,7 +93,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 @@ -111,6 +124,9 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -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-macos ; compiler environments CC=clang CXX=clang++ @@ -120,7 +136,7 @@ SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_EXTRA_LIBS= ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches -SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv +SPC_MICRO_PATCHES=cli_checks,macos_iconv ; *** default build command for building php *** ; buildconf command diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 7205a606..d9bc48c8 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -47,6 +47,10 @@ export default { nav: [], socialLinks: [ {icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'} - ] + ], + footer: { + message: 'Released under the MIT License.', + copyright: 'Copyright © 2023-present crazywhalecc' + } } } diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 8dd36ba8..06771bcf 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -2,6 +2,7 @@ import DefaultTheme from 'vitepress/theme' import {inBrowser, useData} from "vitepress"; import {watchEffect} from "vue"; +import './style.css'; export default { ...DefaultTheme, @@ -13,4 +14,4 @@ export default { } }) } -} \ No newline at end of file +} diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 00000000..b09fc682 --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,6 @@ +/** override default styles */ +.vp-sponsor-grid-image { + max-height:36px !important; + max-width: 1000px !important; +} + diff --git a/docs/en/guide/extension-notes.md b/docs/en/guide/extension-notes.md index 7b0f91aa..4dad0cfd 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/index.md b/docs/index.md index 76c06407..f667ce44 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,3 +21,16 @@ features: - title: Dependency Management details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions. --- + + + +## Special Sponsors + + + diff --git a/docs/public/images/beyondcode-seeklogo.png b/docs/public/images/beyondcode-seeklogo.png new file mode 100644 index 00000000..cacfd720 Binary files /dev/null and b/docs/public/images/beyondcode-seeklogo.png differ diff --git a/docs/public/images/nativephp-logo.svg b/docs/public/images/nativephp-logo.svg new file mode 100644 index 00000000..2b60ec2a --- /dev/null +++ b/docs/public/images/nativephp-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/zh/guide/extension-notes.md b/docs/zh/guide/extension-notes.md index dd153817..8313439a 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/phpunit.xml.dist b/phpunit.xml.dist index dc58130d..2f0c2934 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,5 +4,6 @@ > + diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index b69dcf08..a24eada9 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -9,6 +9,7 @@ use SPC\command\BuildPHPCommand; use SPC\command\CraftCommand; use SPC\command\DeleteDownloadCommand; use SPC\command\dev\AllExtCommand; +use SPC\command\dev\EnvCommand; use SPC\command\dev\ExtVerCommand; use SPC\command\dev\GenerateExtDepDocsCommand; use SPC\command\dev\GenerateExtDocCommand; @@ -70,6 +71,7 @@ final class ConsoleApplication extends Application new GenerateExtDepDocsCommand(), new GenerateLibDepDocsCommand(), new PackLibCommand(), + new EnvCommand(), ] ); } diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 88e827c5..184d88dc 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -14,6 +14,7 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\LockFile; use SPC\store\SourceManager; +use SPC\store\SourcePatcher; use SPC\util\CustomExt; abstract class BuilderBase @@ -203,6 +204,8 @@ abstract class BuilderBase $this->emitPatchPoint('before-exts-extract'); SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]); $this->emitPatchPoint('after-exts-extract'); + // patch micro + SourcePatcher::patchMicro(); } foreach ([...$static_extensions, ...$shared_extensions] as $extension) { diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index ec162461..e71376dd 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -11,6 +11,7 @@ use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; use SPC\util\SPCConfigUtil; +use SPC\util\SPCTarget; class Extension { @@ -545,6 +546,11 @@ class Extension $sharedLibString .= '-l' . $lib . ' '; } } + // move static libstdc++ to shared if we are on non-full-static build target + if (!SPCTarget::isStatic() && 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 8623b8f3..514e79b3 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -12,17 +12,7 @@ class imagick extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . ' ac_cv_func_omp_pause_resource_all=no'; - } - - 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 (str_contains(getenv('CC'), 'devtoolset-10')) { - $static .= ' -lstdc++'; - $shared = str_replace('-lstdc++', '', $shared); - } - return [$static, $shared]; + $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/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 4e30460c..42636d66 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -25,22 +25,8 @@ class LinuxBuilder extends UnixBuilderBase { $this->options = $options; - // check musl-cross make installed if we use musl-cross-make - $arch = arch2gnu(php_uname('m')); - GlobalEnvManager::init(); - - if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist() && !str_contains((string) getenv('CC'), 'zig')) { - $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')); @@ -104,10 +90,6 @@ class LinuxBuilder extends UnixBuilderBase $zts = ''; } $disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : ''; - $cc = trim(getenv('CC')); - if (!$disable_jit && $this->getExt('opcache') && str_contains($cc, 'zig')) { - f_putenv("CC={$cc} -fno-sanitize=undefined"); - } $config_file_path = $this->getOption('with-config-file-path', false) ? ('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : ''; @@ -139,9 +121,6 @@ class LinuxBuilder extends UnixBuilderBase } $embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static'; - if ($embed_type !== 'static' && getenv('SPC_LIBC') === 'musl' && getenv('SPC_LIBC_LINKAGE') === '-static') { - throw new WrongUsageException('Musl libc does not support dynamic linking of PHP embed!'); - } shell()->cd(SOURCE_PATH . '/php-src') ->exec( getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' . @@ -183,9 +162,6 @@ class LinuxBuilder extends UnixBuilderBase } $this->buildEmbed(); } - if (!$disable_jit && $this->getExt('opcache') && str_contains($cc, 'zig')) { - f_putenv("CC={$cc}"); - } if ($enableFrankenphp) { logger()->info('building frankenphp'); $this->buildFrankenphp(); diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php index 018a22c5..1ccc39da 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -192,12 +192,12 @@ class SystemUtil /** * Get libc version string from ldd */ - public static function getLibcVersionIfExists(): ?string + public static function getLibcVersionIfExists(string $libc): ?string { if (self::$libc_version !== null) { return self::$libc_version; } - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') { + if ($libc === 'glibc') { $result = shell()->execWithResult('ldd --version', false); if ($result[0] !== 0) { return null; @@ -212,7 +212,7 @@ class SystemUtil } return null; } - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { + 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 aa2825b7..c911a2fe 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\linux\library; use SPC\store\FileSystem; +use SPC\util\SPCTarget; class icu extends LinuxLibraryBase { @@ -16,7 +17,7 @@ class icu extends LinuxLibraryBase { $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"'; $cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"'; - $ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : ''; + $ldflags = SPCTarget::isStatic() ? 'LDFLAGS="-static"' : ''; shell()->cd($this->source_dir . '/source')->initializeEnv($this) ->exec( "{$cppflags} {$cxxflags} {$ldflags} " . diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 173ec954..362a68fe 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -29,6 +29,7 @@ class MacOSBuilder extends UnixBuilderBase // apply global environment variables GlobalEnvManager::init(); + GlobalEnvManager::afterInit(); // ---------- set necessary compile vars ---------- // concurrency diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index d0d1f680..b65a9077 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -19,6 +19,7 @@ use SPC\store\pkg\GoXcaddy; use SPC\util\DependencyUtil; use SPC\util\GlobalEnvManager; use SPC\util\SPCConfigUtil; +use SPC\util\SPCTarget; abstract class UnixBuilderBase extends BuilderBase { @@ -202,16 +203,8 @@ abstract class UnixBuilderBase extends BuilderBase $util = new SPCConfigUtil($this); $config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}"; - $lens .= ' ' . getenv('SPC_LIBC_LINKAGE'); - // if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH - if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { - $ext_path = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$LD_LIBRARY_PATH '; - FileSystem::removeFileIfExists(BUILD_LIB_PATH . '/libphp.a'); - } else { - $ext_path = ''; - foreach (glob(BUILD_LIB_PATH . '/libphp*.so') as $file) { - unlink($file); - } + if (SPCTarget::isStatic()) { + $lens .= ' -static'; } [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); if ($ret !== 0) { @@ -327,8 +320,7 @@ abstract class UnixBuilderBase extends BuilderBase $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $extLdFlags = "-extldflags '-pie'"; $muslTags = ''; - $staticFlags = ''; - if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl' && getenv('SPC_LIBC_LINKAGE') === 'static') { + if (SPCTarget::isStatic()) { $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; $muslTags = 'static_build,'; $staticFlags = '-static -static-pie'; diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index 8f4a7dad..d7cfa887 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -4,18 +4,19 @@ declare(strict_types=1); namespace SPC\builder\unix\library; -use SPC\builder\linux\library\LinuxLibraryBase; -use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\FileSystem; use SPC\util\executor\UnixAutoconfExecutor; +use SPC\util\SPCTarget; trait imagemagick { /** * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ protected function build(): void { @@ -37,11 +38,11 @@ trait imagemagick '--without-x', ); - // special: linux musl needs `-static` - $ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl'; + // special: linux-static target needs `-static` + $ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl'; // special: macOS needs -iconv - $libs = $this instanceof MacOSLibraryBase ? '-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 d19b2e89..3ff72dcb 100644 --- a/src/SPC/builder/unix/library/ldap.php +++ b/src/SPC/builder/unix/library/ldap.php @@ -6,12 +6,13 @@ namespace SPC\builder\unix\library; use SPC\store\FileSystem; use SPC\util\executor\UnixAutoconfExecutor; +use SPC\util\SPCTarget; trait ldap { public function patchBeforeBuild(): bool { - $extra = getenv('SPC_LIBC') === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : ''; + $extra = SPCTarget::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/libxslt.php b/src/SPC/builder/unix/library/libxslt.php index c820f8f6..aabc277d 100644 --- a/src/SPC/builder/unix/library/libxslt.php +++ b/src/SPC/builder/unix/library/libxslt.php @@ -33,7 +33,13 @@ trait libxslt '--without-debugger', "--with-libxml-prefix={$this->getBuildRootPath()}", ); - $ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make(); + if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) { + $ac->appendEnv([ + 'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'), + 'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'), + ]); + } + $ac->configure()->make(); $this->patchPkgconfPrefix(['libexslt.pc']); $this->patchLaDependencyPrefix(); diff --git a/src/SPC/builder/unix/library/mimalloc.php b/src/SPC/builder/unix/library/mimalloc.php index 78868856..6d89e6df 100644 --- a/src/SPC/builder/unix/library/mimalloc.php +++ b/src/SPC/builder/unix/library/mimalloc.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\util\executor\UnixCMakeExecutor; +use SPC\util\SPCTarget; trait mimalloc { @@ -13,9 +14,9 @@ trait mimalloc $cmake = UnixCMakeExecutor::create($this) ->addConfigureArgs( '-DMI_BUILD_SHARED=OFF', - '-DMI_INSTALL_TOPLEVEL=ON' + '-DMI_INSTALL_TOPLEVEL=ON', ); - if (getenv('SPC_LIBC') === 'musl') { + if (SPCTarget::getLibc() === 'musl') { $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 462e7344..b5392f4d 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -4,17 +4,17 @@ declare(strict_types=1); namespace SPC\builder\unix\library; -use SPC\builder\linux\library\LinuxLibraryBase; use SPC\util\executor\UnixAutoconfExecutor; +use SPC\util\SPCTarget; trait pkgconfig { protected function build(): void { UnixAutoconfExecutor::create($this) - ->appendEnv([ + ->appendEnv([[ 'CFLAGS' => '-Wimplicit-function-declaration -Wno-int-conversion', - 'LDFLAGS' => !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--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 1de3e4c2..c2cc7165 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\SPCTarget; trait postgresql { @@ -50,7 +51,7 @@ trait postgresql $error_exec_cnt += $output[0] === 0 ? 0 : 1; if (!empty($output[1][0])) { $ldflags = $output[1][0]; - $envs .= !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" "; + $envs .= SPCTarget::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/builder/windows/WindowsBuilder.php b/src/SPC/builder/windows/WindowsBuilder.php index c7450050..25a503eb 100644 --- a/src/SPC/builder/windows/WindowsBuilder.php +++ b/src/SPC/builder/windows/WindowsBuilder.php @@ -34,6 +34,7 @@ class WindowsBuilder extends BuilderBase $this->options = $options; GlobalEnvManager::init(); + GlobalEnvManager::afterInit(); // ---------- set necessary options ---------- // set sdk (require visual studio 16 or 17) diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index 832ced97..9c07b074 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -99,6 +99,7 @@ abstract class BaseCommand extends Command // init GlobalEnv if (!$this instanceof BuildCommand) { GlobalEnvManager::init(); + f_putenv('SPC_SKIP_TOOLCHAIN_CHECK=yes'); } if ($this->shouldExecute()) { try { diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index 63a5ef72..e914c70d 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -13,6 +13,7 @@ use SPC\store\SourcePatcher; use SPC\util\DependencyUtil; use SPC\util\GlobalEnvManager; use SPC\util\LicenseDumper; +use SPC\util\SPCTarget; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -63,8 +64,8 @@ class BuildPHPCommand extends BuildCommand // check dynamic extension build env // linux must build with glibc - if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc' && getenv('SPC_LIBC_LINKAGE') === '-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::isStatic()) { + $this->output->writeln('Linux does not support dynamic extension loading with fully static builds, please build with a shared C runtime target!'); return static::FAILURE; } $static_and_shared = array_intersect($static_extensions, $shared_extensions); @@ -133,6 +134,8 @@ class BuildPHPCommand extends BuildCommand // print info $indent_texts = [ 'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')', + 'Build Target' => getenv('SPC_TARGET'), + 'Build Toolchain' => getenv('SPC_TOOLCHAIN'), 'Build SAPI' => $builder->getBuildTypeName($rule), 'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions), 'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions), diff --git a/src/SPC/command/CraftCommand.php b/src/SPC/command/CraftCommand.php index 7f6f506b..84080205 100644 --- a/src/SPC/command/CraftCommand.php +++ b/src/SPC/command/CraftCommand.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Process\Process; #[AsCommand('craft', 'Build static-php from craft.yml')] -class CraftCommand extends BaseCommand +class CraftCommand extends BuildCommand { public function configure(): void { diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index d5054aa9..1e895bdb 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace SPC\command; -use SPC\builder\linux\SystemUtil; use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; @@ -14,6 +13,7 @@ use SPC\store\Config; use SPC\store\Downloader; use SPC\store\LockFile; use SPC\util\DependencyUtil; +use SPC\util\SPCTarget; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -223,8 +223,8 @@ class DownloadCommand extends BaseCommand '{name}' => $source, '{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}' => 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/SPCConfigCommand.php b/src/SPC/command/SPCConfigCommand.php index d3c7a0b2..a1886ed1 100644 --- a/src/SPC/command/SPCConfigCommand.php +++ b/src/SPC/command/SPCConfigCommand.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; #[AsCommand('spc-config', 'Build dependencies')] -class SPCConfigCommand extends BuildCommand +class SPCConfigCommand extends BaseCommand { protected bool $no_motd = true; diff --git a/src/SPC/command/dev/EnvCommand.php b/src/SPC/command/dev/EnvCommand.php new file mode 100644 index 00000000..d5a0cec3 --- /dev/null +++ b/src/SPC/command/dev/EnvCommand.php @@ -0,0 +1,37 @@ +addArgument('env', InputArgument::REQUIRED, 'The environment variable to show, if not set, all will be shown'); + } + + public function initialize(InputInterface $input, OutputInterface $output): void + { + $this->no_motd = true; + parent::initialize($input, $output); + } + + public function handle(): int + { + $env = $this->getArgument('env'); + if (($val = getenv($env)) === false) { + $this->output->writeln("Environment variable '{$env}' is not set."); + return static::FAILURE; + } + $this->output->writeln("{$val}"); + return static::SUCCESS; + } +} diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php index d0d9797e..e51cdb17 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -6,7 +6,6 @@ namespace SPC\command\dev; use SPC\builder\BuilderProvider; use SPC\builder\LibraryBase; -use SPC\builder\linux\SystemUtil; use SPC\command\BuildCommand; use SPC\exception\ExceptionHandler; use SPC\exception\FileSystemException; @@ -16,6 +15,7 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\LockFile; use SPC\util\DependencyUtil; +use SPC\util\SPCTarget; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; @@ -76,8 +76,8 @@ class PackLibCommand extends BuildCommand '{name}' => $lib->getName(), '{arch}' => arch2gnu(php_uname('m')), '{os}' => strtolower(PHP_OS_FAMILY), - '{libc}' => getenv('SPC_LIBC') ?: 'default', - '{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', + '{libc}' => 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/doctor/CheckListHandler.php b/src/SPC/doctor/CheckListHandler.php index 23005dfd..cac17ce3 100644 --- a/src/SPC/doctor/CheckListHandler.php +++ b/src/SPC/doctor/CheckListHandler.php @@ -72,6 +72,14 @@ final class CheckListHandler { foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) { $ref = new \ReflectionClass($class); + $optional = $ref->getAttributes(OptionalCheck::class)[0] ?? null; + if ($optional !== null) { + /** @var OptionalCheck $instance */ + $instance = $optional->newInstance(); + if (is_callable($instance->check) && !call_user_func($instance->check)) { + continue; // skip this class if optional check is false + } + } foreach ($ref->getMethods() as $method) { foreach ($method->getAttributes() as $a) { if (is_a($a->getName(), AsCheckItem::class, true)) { diff --git a/src/SPC/doctor/OptionalCheck.php b/src/SPC/doctor/OptionalCheck.php new file mode 100644 index 00000000..4dae938b --- /dev/null +++ b/src/SPC/doctor/OptionalCheck.php @@ -0,0 +1,11 @@ + 'ld.gold', 'base-devel' => 'automake', 'gettext-devel' => 'gettextize', + 'gettext-dev' => 'gettextize', ]; /** @noinspection PhpUnused */ diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index 41fcb25d..b02d2073 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -4,13 +4,13 @@ declare(strict_types=1); namespace SPC\store; -use SPC\builder\linux\SystemUtil; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\pkg\CustomPackage; use SPC\store\source\CustomSourceBase; +use SPC\util\SPCTarget; /** * Source Downloader. @@ -591,7 +591,12 @@ class Downloader public static function getPreBuiltLockName(string $source): string { - return "{$source}-" . PHP_OS_FAMILY . '-' . getenv('GNU_ARCH') . '-' . (getenv('SPC_LIBC') ?: 'default') . '-' . (SystemUtil::getLibcVersionIfExists() ?? 'default'); + $os_family = PHP_OS_FAMILY; + $gnu_arch = getenv('GNU_ARCH') ?: 'unknown'; + $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 284d8896..08ebdaef 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -11,13 +11,13 @@ use SPC\builder\unix\UnixBuilderBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\SPCTarget; class SourcePatcher { public static function init(): void { // FileSystem::addSourceExtractHook('swow', [__CLASS__, 'patchSwow']); - FileSystem::addSourceExtractHook('micro', [__CLASS__, 'patchMicro']); FileSystem::addSourceExtractHook('openssl', [__CLASS__, 'patchOpenssl11Darwin']); FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']); @@ -105,7 +105,7 @@ class SourcePatcher * @throws RuntimeException * @throws FileSystemException */ - public static function patchMicro(string $name = '', string $target = '', ?array $items = null): bool + public static function patchMicro(?array $items = null): bool { if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) { return false; @@ -152,11 +152,7 @@ class SourcePatcher foreach ($patches as $patch) { logger()->info("Patching micro with {$patch}"); - $patchesStr = str_replace('/', DIRECTORY_SEPARATOR, $patch); - f_passthru( - 'cd ' . SOURCE_PATH . '/php-src && ' . - (PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1 ' - ); + self::patchFile(SOURCE_PATH . "/php-src/{$patch}", SOURCE_PATH . '/php-src'); } return true; @@ -165,24 +161,32 @@ class SourcePatcher /** * Use existing patch file for patching * - * @param string $patch_name Patch file name in src/globals/patch/ + * @param string $patch_name Patch file name in src/globals/patch/ or absolute path * @param string $cwd Working directory for patch command * @param bool $reverse Reverse patches (default: False) * @throws RuntimeException */ public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool { - if (!file_exists(ROOT_DIR . "/src/globals/patch/{$patch_name}")) { + if (FileSystem::isRelativePath($patch_name)) { + $patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}"; + } else { + $patch_file = $patch_name; + } + if (!file_exists($patch_file)) { return false; } - $patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}"; - $patch_str = str_replace('/', DIRECTORY_SEPARATOR, $patch_file); + $patch_str = FileSystem::convertPath($patch_file); + if (!file_exists($patch_str)) { + throw new RuntimeException("Patch file [{$patch_str}] does not exist"); + } // Copy patch from phar - if (\Phar::running() !== '') { - file_put_contents(SOURCE_PATH . '/' . $patch_name, file_get_contents($patch_file)); - $patch_str = str_replace('/', DIRECTORY_SEPARATOR, SOURCE_PATH . '/' . $patch_name); + if (str_starts_with($patch_str, 'phar://')) { + $filename = pathinfo($patch_file, PATHINFO_BASENAME); + file_put_contents(SOURCE_PATH . "/{$filename}", file_get_contents($patch_file)); + $patch_str = FileSystem::convertPath(SOURCE_PATH . "/{$filename}"); } // detect @@ -458,7 +462,7 @@ class SourcePatcher public static function patchFfiCentos7FixO3strncmp(): bool { - if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::getLibcVersionIfExists() > '2.17') { + if (!($ver = SPCTarget::getLibcVersion()) || version_compare($ver, '2.17', '>')) { return false; } if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) { diff --git a/src/SPC/store/pkg/GoXcaddy.php b/src/SPC/store/pkg/GoXcaddy.php index fa229cef..1359192d 100644 --- a/src/SPC/store/pkg/GoXcaddy.php +++ b/src/SPC/store/pkg/GoXcaddy.php @@ -7,7 +7,6 @@ namespace SPC\store\pkg; use SPC\store\Downloader; use SPC\store\FileSystem; use SPC\store\LockFile; -use SPC\util\GlobalEnvManager; class GoXcaddy extends CustomPackage { @@ -65,7 +64,6 @@ class GoXcaddy extends CustomPackage FileSystem::extractPackage($name, $source_type, $filename, $extract); - GlobalEnvManager::init(); // install xcaddy shell() ->appendEnv([ diff --git a/src/SPC/toolchain/ClangNativeToolchain.php b/src/SPC/toolchain/ClangNativeToolchain.php new file mode 100644 index 00000000..c5186521 --- /dev/null +++ b/src/SPC/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/toolchain/GccNativeToolchain.php b/src/SPC/toolchain/GccNativeToolchain.php new file mode 100644 index 00000000..bd8cc868 --- /dev/null +++ b/src/SPC/toolchain/GccNativeToolchain.php @@ -0,0 +1,33 @@ + 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 . '.'), + }; + } +} diff --git a/src/SPC/toolchain/MSVCToolchain.php b/src/SPC/toolchain/MSVCToolchain.php new file mode 100644 index 00000000..567cff38 --- /dev/null +++ b/src/SPC/toolchain/MSVCToolchain.php @@ -0,0 +1,12 @@ + MuslToolchain::class, // use musl toolchain by default, after zig pr merged, change this to ZigToolchain::class + 'Windows' => MSVCToolchain::class, + 'Darwin' => ClangNativeToolchain::class, + 'BSD' => ClangNativeToolchain::class, + ]; + + /** + * @throws WrongUsageException + */ + public static function initToolchain(): void + { + $libc = getenv('SPC_LIBC'); + if ($libc !== false) { + // uncomment this when zig pr is merged + // logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.'); + $toolchain = match ($libc) { + 'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class, + 'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_TARGET must be musl-static or musl for musl dist.'), + default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc), + }; + } else { + $toolchain = self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY]; + } + $toolchainClass = $toolchain; + /* @var ToolchainInterface $toolchainClass */ + (new $toolchainClass())->initEnv(); + GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}"); + } + + public static function afterInitToolchain(): void + { + if (!getenv('SPC_TOOLCHAIN')) { + throw new WrongUsageException('SPC_TOOLCHAIN not set'); + } + $toolchain = getenv('SPC_TOOLCHAIN'); + /* @var ToolchainInterface $toolchain */ + (new $toolchain())->afterInit(); + } +} diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php new file mode 100644 index 00000000..3922fecb --- /dev/null +++ b/src/SPC/toolchain/ZigToolchain.php @@ -0,0 +1,12 @@ + $value) { - if ($key === 'PATH') { - self::addPathIfNotExists($value); - } - } - } } public static function putenv(string $val): void @@ -133,6 +101,19 @@ class GlobalEnvManager } } + /** + * Initialize the toolchain after the environment variables are set. + * The toolchain or environment availability check is done here. + * + * @throws WrongUsageException + */ + public static function afterInit(): void + { + if (!filter_var(getenv('SPC_SKIP_TOOLCHAIN_CHECK'), FILTER_VALIDATE_BOOL)) { + ToolchainManager::afterInitToolchain(); + } + } + /** * @throws WrongUsageException */ diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index c5177bc6..64a54653 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -56,7 +56,7 @@ class SPCConfigUtil ob_get_clean(); $ldflags = $this->getLdflagsString(); $libs = $this->getLibsString($libraries, $with_dependencies); - if (PHP_OS_FAMILY === 'Darwin') { + 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 new file mode 100644 index 00000000..071f2620 --- /dev/null +++ b/src/SPC/util/SPCTarget.php @@ -0,0 +1,87 @@ + 'Linux', + str_contains($target, 'macos') => 'Darwin', + str_contains($target, 'windows') => 'Windows', + default => throw new WrongUsageException('Cannot parse target.'), + }; + } +} diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index a2f25979..c47f358d 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -21,15 +21,15 @@ $test_php_version = [ // test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available) $test_os = [ - // 'macos-13', - // 'macos-14', - // 'macos-15', - // 'ubuntu-latest', - // 'ubuntu-22.04', - // 'ubuntu-22.04-arm', - 'ubuntu-24.04', - '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 ]; $zig = true; @@ -84,7 +84,7 @@ $with_libs = match (PHP_OS_FAMILY) { // You can use `common`, `bulk`, `minimal` or `none`. // note: combination is only available for *nix platform. Windows must use `none` combination $base_combination = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'minimal', + 'Linux', 'Darwin' => 'common', 'Windows' => 'none', };