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** 服务器。
+**特别赞助商**:
+
+
+
+
+
## 开源协议
本项目采用 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**:
+
+
+
+
+
## 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',
};