diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 5ed6d59d..ee4a17fb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -105,7 +105,7 @@ jobs:
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: "Run PHPUnit Tests"
- run: SPC_LIBC=glibc vendor/bin/phpunit tests/ --no-coverage
+ run: SPC_TARGET=glibc vendor/bin/phpunit tests/ --no-coverage
define-matrix:
name: "Define Matrix"
diff --git a/bin/build-static-frankenphp b/bin/build-static-frankenphp
index 10a693a7..bc5cdb33 100755
--- a/bin/build-static-frankenphp
+++ b/bin/build-static-frankenphp
@@ -92,7 +92,7 @@ ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative
ENV PATH="/app/bin:/cmake/bin:/usr/local/go/bin:$PATH"
-ENV SPC_LIBC=glibc
+ENV SPC_TARGET=glibc
ADD ./config/env.ini /app/config/env.ini
RUN bin/spc doctor --auto-fix --debug
@@ -146,7 +146,7 @@ echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env
echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env
echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env
echo 'SPC_DEFAULT_C_FLAGS=-fPIE -fPIC' >> /tmp/spc-gnu-docker.env
-echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env
+echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env
diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker
index 6cc36bfa..523905c5 100755
--- a/bin/spc-gnu-docker
+++ b/bin/spc-gnu-docker
@@ -108,7 +108,7 @@ ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev
ENV PATH="/app/bin:/cmake/bin:$PATH"
-ENV SPC_LIBC=glibc
+ENV SPC_TARGET=glibc
ADD ./config/env.ini /app/config/env.ini
RUN CC=gcc bin/spc doctor --auto-fix --debug
@@ -159,7 +159,7 @@ echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env
echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env
echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env
echo 'SPC_DEFAULT_C_FLAGS=-fPIC' >> /tmp/spc-gnu-docker.env
-echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env
+echo 'SPC_TARGET=glibc' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env
diff --git a/config/env.ini b/config/env.ini
index d80bc3e7..a8e7a8a6 100644
--- a/config/env.ini
+++ b/config/env.ini
@@ -55,6 +55,8 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd
; EXTENSION_DIR=
[windows]
+; build target: win7-static
+SPC_TARGET=msvc-static
; php-sdk-binary-tools path
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path
@@ -63,8 +65,17 @@ UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
[linux]
-; include PATH for musl libc.
-SPC_LIBC=musl
+; Linux can use different build toolchain, but the toolchain can not be changed in this file:
+; - musl (default): used for general linux distros, can build `musl-static` target only.
+; - zig (WIP): used for general linux distros, can build `musl` and `glibc` targets.
+; - musl-native: used for alpine linux, can build `musl-static` and `musl`(WIP) target.
+; - gnu-native (assume): used for general linux distros, can build `glibc` target only and have portability issues.
+
+; build target:
+; - musl-static (default): pure static linking, using musl-libc, can run on any linux distro.
+; - musl: static linking with dynamic linking to musl-libc, can run on musl-based linux distro.
+; - glibc: static linking with dynamic linking to glibc, can run on glibc-based linux distro.
+SPC_TARGET=musl-static
; compiler environments
CC=${SPC_LINUX_DEFAULT_CC}
CXX=${SPC_LINUX_DEFAULT_CXX}
@@ -109,6 +120,9 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie"
[macos]
+; build target: macho or macho (possibly we could support macho-universal in the future)
+; Currently we do not support universal and cross-compilation for macOS.
+SPC_TARGET=macho
; compiler environments
CC=clang
CXX=clang++
diff --git a/docs/en/guide/extension-notes.md b/docs/en/guide/extension-notes.md
index aa11c377..5a022b59 100644
--- a/docs/en/guide/extension-notes.md
+++ b/docs/en/guide/extension-notes.md
@@ -82,7 +82,7 @@ and this extension cannot be compiled into php by static linking, so it cannot b
## xdebug
-1. Xdebug is only buildable as a shared extension. On Linux, you need to use static-php-cli with SPC_LIBC=glibc.
+1. Xdebug is only buildable as a shared extension. You need to use a build target other than `musl-static` for SPC_TARGET.
2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug".
The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
diff --git a/docs/zh/guide/extension-notes.md b/docs/zh/guide/extension-notes.md
index 10e62eb0..ad787dd9 100644
--- a/docs/zh/guide/extension-notes.md
+++ b/docs/zh/guide/extension-notes.md
@@ -76,7 +76,7 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli
## xdebug
-1. Xdebug 只能作为共享扩展进行构建。在 Linux 上,您需要使用 static-php-cli 并设置 SPC_LIBC=glibc。
+1. Xdebug 只能作为共享扩展进行构建。您需要使用除了 `musl-static` 外的其他 `SPC_TARGET` 构建目标。
2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`。
diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php
index b69dcf08..a24eada9 100644
--- a/src/SPC/ConsoleApplication.php
+++ b/src/SPC/ConsoleApplication.php
@@ -9,6 +9,7 @@ use SPC\command\BuildPHPCommand;
use SPC\command\CraftCommand;
use SPC\command\DeleteDownloadCommand;
use SPC\command\dev\AllExtCommand;
+use SPC\command\dev\EnvCommand;
use SPC\command\dev\ExtVerCommand;
use SPC\command\dev\GenerateExtDepDocsCommand;
use SPC\command\dev\GenerateExtDocCommand;
@@ -70,6 +71,7 @@ final class ConsoleApplication extends Application
new GenerateExtDepDocsCommand(),
new GenerateLibDepDocsCommand(),
new PackLibCommand(),
+ new EnvCommand(),
]
);
}
diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php
index 7951ea69..bcdbffab 100644
--- a/src/SPC/builder/extension/imagick.php
+++ b/src/SPC/builder/extension/imagick.php
@@ -6,6 +6,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
+use SPC\util\SPCTarget;
#[CustomExt('imagick')]
class imagick extends Extension
@@ -15,7 +16,7 @@ class imagick extends Extension
if (PHP_OS_FAMILY !== 'Linux') {
return false;
}
- if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) {
+ if (SPCTarget::isTarget(SPCTarget::GLIBC)) {
return false;
}
// imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp
@@ -26,7 +27,7 @@ class imagick extends Extension
public function getUnixConfigureArg(bool $shared = false): string
{
- $disable_omp = !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) ? '' : ' ac_cv_func_omp_pause_resource_all=no';
+ $disable_omp = SPCTarget::isTarget(SPCTarget::GLIBC) ? ' ac_cv_func_omp_pause_resource_all=no' : '';
return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp;
}
@@ -34,7 +35,7 @@ class imagick extends Extension
{
// on centos 7, it will use the symbol _ZTINSt6thread6_StateE, which is not defined in system libstdc++.so.6
[$static, $shared] = parent::getStaticAndSharedLibs();
- if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) {
+ if (SPCTarget::isTarget(SPCTarget::GLIBC)) {
$static .= ' -lstdc++';
$shared = str_replace('-lstdc++', '', $shared);
}
diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php
index 68548615..94d8a41e 100644
--- a/src/SPC/builder/linux/LinuxBuilder.php
+++ b/src/SPC/builder/linux/LinuxBuilder.php
@@ -25,22 +25,8 @@ class LinuxBuilder extends UnixBuilderBase
{
$this->options = $options;
- // check musl-cross make installed if we use musl-cross-make
- $arch = arch2gnu(php_uname('m'));
-
GlobalEnvManager::init();
-
- if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) {
- $this->setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
- $this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
- $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
- $configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure;
- GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$configure}");
-
- if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {
- throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
- }
- }
+ GlobalEnvManager::afterInit();
// concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY'));
diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php
index a85f3dec..1e405768 100644
--- a/src/SPC/builder/linux/SystemUtil.php
+++ b/src/SPC/builder/linux/SystemUtil.php
@@ -6,6 +6,7 @@ namespace SPC\builder\linux;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
+use SPC\util\SPCTarget;
class SystemUtil
{
@@ -193,7 +194,7 @@ class SystemUtil
if (self::$libc_version !== null) {
return self::$libc_version;
}
- if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') {
+ if (SPCTarget::isTarget(SPCTarget::GLIBC)) {
$result = shell()->execWithResult('ldd --version', false);
if ($result[0] !== 0) {
return null;
@@ -208,7 +209,7 @@ class SystemUtil
}
return null;
}
- if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') {
+ if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
if (self::isMuslDist()) {
$result = shell()->execWithResult('ldd 2>&1', false);
} else {
diff --git a/src/SPC/builder/linux/library/icu.php b/src/SPC/builder/linux/library/icu.php
index aa2825b7..6fb7e1fc 100644
--- a/src/SPC/builder/linux/library/icu.php
+++ b/src/SPC/builder/linux/library/icu.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\linux\library;
use SPC\store\FileSystem;
+use SPC\util\SPCTarget;
class icu extends LinuxLibraryBase
{
@@ -16,7 +17,7 @@ class icu extends LinuxLibraryBase
{
$cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"';
$cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"';
- $ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : '';
+ $ldflags = SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? 'LDFLAGS="-static"' : '';
shell()->cd($this->source_dir . '/source')->initializeEnv($this)
->exec(
"{$cppflags} {$cxxflags} {$ldflags} " .
diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php
index 0d56b0eb..886eec68 100644
--- a/src/SPC/builder/unix/UnixBuilderBase.php
+++ b/src/SPC/builder/unix/UnixBuilderBase.php
@@ -17,6 +17,7 @@ use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil;
+use SPC\util\SPCTarget;
abstract class UnixBuilderBase extends BuilderBase
{
@@ -200,7 +201,7 @@ abstract class UnixBuilderBase extends BuilderBase
$util = new SPCConfigUtil($this);
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
- if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') {
+ if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
$lens .= ' -static';
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
@@ -334,7 +335,7 @@ abstract class UnixBuilderBase extends BuilderBase
$debugFlags = $this->getOption('no-strip') ? "'-w -s' " : '';
$extLdFlags = "-extldflags '-pie'";
$muslTags = '';
- if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') {
+ if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
$muslTags = 'static_build,';
}
diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php
index ead786a2..88ae9f7d 100644
--- a/src/SPC/builder/unix/library/imagemagick.php
+++ b/src/SPC/builder/unix/library/imagemagick.php
@@ -4,12 +4,11 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
-use SPC\builder\linux\library\LinuxLibraryBase;
-use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
+use SPC\util\SPCTarget;
trait imagemagick
{
@@ -33,16 +32,16 @@ trait imagemagick
->optionalLib('bzip2', ...ac_with_args('bzlib'))
->addConfigureArgs(
// TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so
- getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10') ? '--disable-openmp' : '--enable-openmp',
+ SPCTarget::isTarget(SPCTarget::GLIBC) ? '--disable-openmp' : '--enable-openmp',
'--without-jxl',
'--without-x',
);
- // special: linux musl needs `-static`
- $ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl';
+ // special: linux musl-static needs `-static`
+ $ldflags = !SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? ('-static -ldl') : '-ldl';
// special: macOS needs -iconv
- $libs = $this instanceof MacOSLibraryBase ? '-liconv' : '';
+ $libs = SPCTarget::isTarget(SPCTarget::MACHO) ? '-liconv' : '';
$ac->appendEnv([
'LDFLAGS' => $ldflags,
diff --git a/src/SPC/builder/unix/library/ldap.php b/src/SPC/builder/unix/library/ldap.php
index 62f7bcd7..e87310cb 100644
--- a/src/SPC/builder/unix/library/ldap.php
+++ b/src/SPC/builder/unix/library/ldap.php
@@ -6,12 +6,13 @@ namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
+use SPC\util\SPCTarget;
trait ldap
{
public function patchBeforeBuild(): bool
{
- $extra = getenv('SPC_LIBC') === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : '';
+ $extra = SPCTarget::isTarget(SPCTarget::GLIBC) ? '-ldl -lpthread -lm -lresolv -lutil' : '';
FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra);
return true;
}
diff --git a/src/SPC/builder/unix/library/libxslt.php b/src/SPC/builder/unix/library/libxslt.php
index c820f8f6..aabc277d 100644
--- a/src/SPC/builder/unix/library/libxslt.php
+++ b/src/SPC/builder/unix/library/libxslt.php
@@ -33,7 +33,13 @@ trait libxslt
'--without-debugger',
"--with-libxml-prefix={$this->getBuildRootPath()}",
);
- $ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make();
+ if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) {
+ $ac->appendEnv([
+ 'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'),
+ 'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'),
+ ]);
+ }
+ $ac->configure()->make();
$this->patchPkgconfPrefix(['libexslt.pc']);
$this->patchLaDependencyPrefix();
diff --git a/src/SPC/builder/unix/library/mimalloc.php b/src/SPC/builder/unix/library/mimalloc.php
index 78868856..0808516c 100644
--- a/src/SPC/builder/unix/library/mimalloc.php
+++ b/src/SPC/builder/unix/library/mimalloc.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\util\executor\UnixCMakeExecutor;
+use SPC\util\SPCTarget;
trait mimalloc
{
@@ -15,7 +16,7 @@ trait mimalloc
'-DMI_BUILD_SHARED=OFF',
'-DMI_INSTALL_TOPLEVEL=ON'
);
- if (getenv('SPC_LIBC') === 'musl') {
+ if (SPCTarget::isTarget(SPCTarget::MUSL) || SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
$cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON');
}
$cmake->build();
diff --git a/src/SPC/builder/unix/library/pkgconfig.php b/src/SPC/builder/unix/library/pkgconfig.php
index 05727f96..3522c52a 100644
--- a/src/SPC/builder/unix/library/pkgconfig.php
+++ b/src/SPC/builder/unix/library/pkgconfig.php
@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
-use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\util\executor\UnixAutoconfExecutor;
+use SPC\util\SPCTarget;
trait pkgconfig
{
@@ -14,7 +14,7 @@ trait pkgconfig
UnixAutoconfExecutor::create($this)
->appendEnv([
'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '',
- 'LDFLAGS' => !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--static',
+ 'LDFLAGS' => SPCTarget::isStaticTarget() ? '--static' : '',
])
->configure(
'--with-internal-glib',
diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php
index 1de3e4c2..efcfc089 100644
--- a/src/SPC/builder/unix/library/postgresql.php
+++ b/src/SPC/builder/unix/library/postgresql.php
@@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
+use SPC\util\SPCTarget;
trait postgresql
{
@@ -50,7 +51,7 @@ trait postgresql
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
if (!empty($output[1][0])) {
$ldflags = $output[1][0];
- $envs .= !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" ";
+ $envs .= SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" ";
}
$output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1;
diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php
index 21a848a5..f9938957 100644
--- a/src/SPC/command/BuildPHPCommand.php
+++ b/src/SPC/command/BuildPHPCommand.php
@@ -13,6 +13,7 @@ use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager;
use SPC\util\LicenseDumper;
+use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
@@ -63,7 +64,7 @@ class BuildPHPCommand extends BuildCommand
// check dynamic extension build env
// linux must build with glibc
- if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') {
+ if (!empty($shared_extensions) && SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
$this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!');
return static::FAILURE;
}
@@ -133,6 +134,8 @@ class BuildPHPCommand extends BuildCommand
// print info
$indent_texts = [
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
+ 'Build Target' => getenv('SPC_TARGET'),
+ 'Build Toolchain' => getenv('SPC_TOOLCHAIN'),
'Build SAPI' => $builder->getBuildTypeName($rule),
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php
index d5054aa9..35529f5e 100644
--- a/src/SPC/command/DownloadCommand.php
+++ b/src/SPC/command/DownloadCommand.php
@@ -14,6 +14,7 @@ use SPC\store\Config;
use SPC\store\Downloader;
use SPC\store\LockFile;
use SPC\util\DependencyUtil;
+use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -223,7 +224,11 @@ class DownloadCommand extends BaseCommand
'{name}' => $source,
'{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY),
- '{libc}' => getenv('SPC_LIBC') ?: 'default',
+ '{libc}' => match (getenv('SPC_TARGET')) {
+ SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl',
+ SPCTarget::GLIBC => 'glibc',
+ default => 'default',
+ },
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
];
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
diff --git a/src/SPC/command/dev/EnvCommand.php b/src/SPC/command/dev/EnvCommand.php
new file mode 100644
index 00000000..d5a0cec3
--- /dev/null
+++ b/src/SPC/command/dev/EnvCommand.php
@@ -0,0 +1,37 @@
+addArgument('env', InputArgument::REQUIRED, 'The environment variable to show, if not set, all will be shown');
+ }
+
+ public function initialize(InputInterface $input, OutputInterface $output): void
+ {
+ $this->no_motd = true;
+ parent::initialize($input, $output);
+ }
+
+ public function handle(): int
+ {
+ $env = $this->getArgument('env');
+ if (($val = getenv($env)) === false) {
+ $this->output->writeln("Environment variable '{$env}' is not set.");
+ return static::FAILURE;
+ }
+ $this->output->writeln("{$val}");
+ return static::SUCCESS;
+ }
+}
diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php
index d0d9797e..29a22353 100644
--- a/src/SPC/command/dev/PackLibCommand.php
+++ b/src/SPC/command/dev/PackLibCommand.php
@@ -16,6 +16,7 @@ use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\store\LockFile;
use SPC\util\DependencyUtil;
+use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
@@ -76,8 +77,12 @@ class PackLibCommand extends BuildCommand
'{name}' => $lib->getName(),
'{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY),
- '{libc}' => getenv('SPC_LIBC') ?: 'default',
- '{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
+ '{libc}' => match (getenv('SPC_TARGET')) {
+ SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl',
+ SPCTarget::GLIBC => 'glibc',
+ default => 'default',
+ },
+ '{libcver}' => SystemUtil::getLibcVersionIfExists() ?? 'default',
];
// detect suffix, for proper tar option
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));
diff --git a/src/SPC/doctor/CheckListHandler.php b/src/SPC/doctor/CheckListHandler.php
index 23005dfd..cac17ce3 100644
--- a/src/SPC/doctor/CheckListHandler.php
+++ b/src/SPC/doctor/CheckListHandler.php
@@ -72,6 +72,14 @@ final class CheckListHandler
{
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) {
$ref = new \ReflectionClass($class);
+ $optional = $ref->getAttributes(OptionalCheck::class)[0] ?? null;
+ if ($optional !== null) {
+ /** @var OptionalCheck $instance */
+ $instance = $optional->newInstance();
+ if (is_callable($instance->check) && !call_user_func($instance->check)) {
+ continue; // skip this class if optional check is false
+ }
+ }
foreach ($ref->getMethods() as $method) {
foreach ($method->getAttributes() as $a) {
if (is_a($a->getName(), AsCheckItem::class, true)) {
diff --git a/src/SPC/doctor/OptionalCheck.php b/src/SPC/doctor/OptionalCheck.php
new file mode 100644
index 00000000..4dae938b
--- /dev/null
+++ b/src/SPC/doctor/OptionalCheck.php
@@ -0,0 +1,11 @@
+ 'musl',
+ SPCTarget::GLIBC => 'glibc',
+ default => 'default',
+ };
+ $libc_version = SystemUtil::getLibcVersionIfExists() ?? 'default';
+
+ return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}";
}
/**
diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php
index fa5c9cf5..a526dd5a 100644
--- a/src/SPC/util/GlobalEnvManager.php
+++ b/src/SPC/util/GlobalEnvManager.php
@@ -40,24 +40,6 @@ class GlobalEnvManager
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
}
- // Define env vars for linux
- if (PHP_OS_FAMILY === 'Linux') {
- $arch = getenv('GNU_ARCH');
- if (SystemUtil::isMuslDist() || getenv('SPC_LIBC') === 'glibc') {
- self::putenv('SPC_LINUX_DEFAULT_CC=gcc');
- self::putenv('SPC_LINUX_DEFAULT_CXX=g++');
- self::putenv('SPC_LINUX_DEFAULT_AR=ar');
- self::putenv('SPC_LINUX_DEFAULT_LD=ld.gold');
- } else {
- self::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc");
- self::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++");
- self::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar");
- self::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld");
- self::addPathIfNotExists('/usr/local/musl/bin');
- self::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin");
- }
- }
-
$ini = self::readIniFile();
$default_put_list = [];
@@ -80,6 +62,29 @@ class GlobalEnvManager
self::putenv("{$k}={$v}");
}
}
+
+ // deprecated: convert SPC_LIBC to SPC_TARGET
+ if (getenv('SPC_LIBC') !== false) {
+ logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.');
+ $target = match (getenv('SPC_LIBC')) {
+ 'musl' => SPCTarget::MUSL_STATIC,
+ default => SPCTarget::GLIBC,
+ };
+ self::putenv("SPC_TARGET={$target}");
+ self::putenv('SPC_LIBC');
+ }
+
+ // auto-select toolchain based on target and OS temporarily
+ // TODO: use 'zig' instead of 'gcc-native' when ZigToolchain is implemented
+ $toolchain = match (getenv('SPC_TARGET')) {
+ SPCTarget::MUSL_STATIC, SPCTarget::MUSL => SystemUtil::isMuslDist() ? 'gcc-native' : 'musl',
+ SPCTarget::MACHO => 'clang-native',
+ SPCTarget::MSVC_STATIC => 'msvc',
+ default => 'gcc-native',
+ };
+
+ SPCTarget::initTargetForToolchain($toolchain);
+
// apply second time
$ini2 = self::readIniFile();
@@ -108,13 +113,18 @@ class GlobalEnvManager
self::$env_cache[] = $val;
}
- private static function addPathIfNotExists(string $path): void
+ public static function addPathIfNotExists(string $path): void
{
if (is_unix() && !str_contains(getenv('PATH'), $path)) {
self::putenv("PATH={$path}:" . getenv('PATH'));
}
}
+ public static function afterInit(): void
+ {
+ SPCTarget::afterInitTargetForToolchain();
+ }
+
/**
* @throws WrongUsageException
*/
diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php
index 8e69e89c..bf441efe 100644
--- a/src/SPC/util/SPCConfigUtil.php
+++ b/src/SPC/util/SPCConfigUtil.php
@@ -55,7 +55,7 @@ class SPCConfigUtil
ob_get_clean();
$ldflags = $this->getLdflagsString();
$libs = $this->getLibsString($libraries, $with_dependencies);
- if (PHP_OS_FAMILY === 'Darwin') {
+ if (SPCTarget::isTarget(SPCTarget::MACHO)) {
$libs .= " {$this->getFrameworksString($extensions)}";
}
$cflags = $this->getIncludesString();
@@ -146,7 +146,7 @@ class SPCConfigUtil
}
}
// patch: imagick (imagemagick wrapper) for linux needs libgomp
- if (in_array('imagemagick', $libraries) && PHP_OS_FAMILY === 'Linux' && !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10'))) {
+ if (in_array('imagemagick', $libraries) && !SPCTarget::isTarget(SPCTarget::GLIBC)) {
$short_name[] = '-lgomp';
}
return implode(' ', $short_name);
diff --git a/src/SPC/util/SPCTarget.php b/src/SPC/util/SPCTarget.php
new file mode 100644
index 00000000..2d81fc4e
--- /dev/null
+++ b/src/SPC/util/SPCTarget.php
@@ -0,0 +1,83 @@
+ MuslToolchain::class,
+ 'gcc-native' => GccNativeToolchain::class,
+ 'clang-native' => ClangNativeToolchain::class,
+ 'msvc' => MSVCToolchain::class,
+ 'zig' => ZigToolchain::class,
+ ];
+
+ public static function isTarget(string $target): bool
+ {
+ $env = getenv('SPC_TARGET');
+ if ($env === false) {
+ return false;
+ }
+ $env = strtolower($env);
+ return $env === $target;
+ }
+
+ public static function isStaticTarget(): bool
+ {
+ $env = getenv('SPC_TARGET');
+ if ($env === false) {
+ return false;
+ }
+ $env = strtolower($env);
+ return str_ends_with($env, '-static') || $env === self::MUSL_STATIC;
+ }
+
+ public static function initTargetForToolchain(string $toolchain): void
+ {
+ $target = getenv('SPC_TARGET');
+ $toolchain = strtolower($toolchain);
+ if (isset(self::TOOLCHAIN_LIST[$toolchain])) {
+ $toolchainClass = self::TOOLCHAIN_LIST[$toolchain];
+ /* @var ToolchainInterface $toolchainClass */
+ (new $toolchainClass())->initEnv($target);
+ }
+ GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}");
+ }
+
+ public static function afterInitTargetForToolchain()
+ {
+ if (!getenv('SPC_TOOLCHAIN')) {
+ throw new WrongUsageException('SPC_TOOLCHAIN not set');
+ }
+ $toolchain = getenv('SPC_TOOLCHAIN');
+ if (!isset(self::TOOLCHAIN_LIST[$toolchain])) {
+ throw new WrongUsageException("Unknown toolchain: {$toolchain}");
+ }
+ $toolchainClass = self::TOOLCHAIN_LIST[$toolchain];
+ (new $toolchainClass())->afterInit(getenv('SPC_TARGET'));
+ }
+}
diff --git a/src/SPC/util/toolchain/ClangNativeToolchain.php b/src/SPC/util/toolchain/ClangNativeToolchain.php
new file mode 100644
index 00000000..863636ec
--- /dev/null
+++ b/src/SPC/util/toolchain/ClangNativeToolchain.php
@@ -0,0 +1,33 @@
+ LinuxSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or manually set CC/CXX to a valid path.'),
+ 'Darwin' => MacOSSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or set CC/CXX to a valid path.'),
+ 'BSD' => FreeBSDSystemUtil::findCommand('clang++') ?? throw new WrongUsageException('Clang++ not found, please install it or set CC/CXX to a valid path.'),
+ default => throw new WrongUsageException('Clang is not supported on ' . PHP_OS_FAMILY . '.'),
+ };
+ }
+}
diff --git a/src/SPC/util/toolchain/GccNativeToolchain.php b/src/SPC/util/toolchain/GccNativeToolchain.php
new file mode 100644
index 00000000..c845d11d
--- /dev/null
+++ b/src/SPC/util/toolchain/GccNativeToolchain.php
@@ -0,0 +1,32 @@
+ LinuxSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'),
+ 'Darwin' => MacOSSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'),
+ 'BSD' => FreeBSDSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'),
+ default => throw new \RuntimeException('GCC is not supported on ' . PHP_OS_FAMILY . '.'),
+ };
+ }
+}
diff --git a/src/SPC/util/toolchain/MSVCToolchain.php b/src/SPC/util/toolchain/MSVCToolchain.php
new file mode 100644
index 00000000..2ec8111c
--- /dev/null
+++ b/src/SPC/util/toolchain/MSVCToolchain.php
@@ -0,0 +1,12 @@
+