refactor: replace SPC_LIBC with SPC_TARGET and update related logic

This commit is contained in:
crazywhalecc 2025-06-28 16:36:05 +08:00
parent b04ffadf13
commit 12aadf18cc
No known key found for this signature in database
GPG Key ID: 1F4BDD59391F2680
35 changed files with 420 additions and 85 deletions

View File

@ -105,7 +105,7 @@ jobs:
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: "Run PHPUnit Tests" - 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: define-matrix:
name: "Define Matrix" name: "Define Matrix"

View File

@ -92,7 +92,7 @@ ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative
ENV PATH="/app/bin:/cmake/bin:/usr/local/go/bin:$PATH" 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 ADD ./config/env.ini /app/config/env.ini
RUN bin/spc doctor --auto-fix --debug 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 '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 '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_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_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 echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env

View File

@ -108,7 +108,7 @@ ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev RUN /app/bin/php /app/bin/composer install --no-dev
ENV PATH="/app/bin:/cmake/bin:$PATH" ENV PATH="/app/bin:/cmake/bin:$PATH"
ENV SPC_LIBC=glibc ENV SPC_TARGET=glibc
ADD ./config/env.ini /app/config/env.ini ADD ./config/env.ini /app/config/env.ini
RUN CC=gcc bin/spc doctor --auto-fix --debug 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 '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 '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_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_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 echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env

View File

@ -55,6 +55,8 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd
; EXTENSION_DIR= ; EXTENSION_DIR=
[windows] [windows]
; build target: win7-static
SPC_TARGET=msvc-static
; php-sdk-binary-tools path ; php-sdk-binary-tools path
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path ; 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 SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
[linux] [linux]
; include PATH for musl libc. ; Linux can use different build toolchain, but the toolchain can not be changed in this file:
SPC_LIBC=musl ; - 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 ; compiler environments
CC=${SPC_LINUX_DEFAULT_CC} CC=${SPC_LINUX_DEFAULT_CC}
CXX=${SPC_LINUX_DEFAULT_CXX} 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" SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie"
[macos] [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 ; compiler environments
CC=clang CC=clang
CXX=clang++ CXX=clang++

View File

@ -82,7 +82,7 @@ and this extension cannot be compiled into php by static linking, so it cannot b
## xdebug ## 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". 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`. 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`.

View File

@ -76,7 +76,7 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli
## xdebug ## xdebug
1. Xdebug 只能作为共享扩展进行构建。在 Linux 上,您需要使用 static-php-cli 并设置 SPC_LIBC=glibc 1. Xdebug 只能作为共享扩展进行构建。您需要使用除了 `musl-static` 外的其他 `SPC_TARGET` 构建目标
2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。 2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php` 编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`

View File

@ -9,6 +9,7 @@ use SPC\command\BuildPHPCommand;
use SPC\command\CraftCommand; use SPC\command\CraftCommand;
use SPC\command\DeleteDownloadCommand; use SPC\command\DeleteDownloadCommand;
use SPC\command\dev\AllExtCommand; use SPC\command\dev\AllExtCommand;
use SPC\command\dev\EnvCommand;
use SPC\command\dev\ExtVerCommand; use SPC\command\dev\ExtVerCommand;
use SPC\command\dev\GenerateExtDepDocsCommand; use SPC\command\dev\GenerateExtDepDocsCommand;
use SPC\command\dev\GenerateExtDocCommand; use SPC\command\dev\GenerateExtDocCommand;
@ -70,6 +71,7 @@ final class ConsoleApplication extends Application
new GenerateExtDepDocsCommand(), new GenerateExtDepDocsCommand(),
new GenerateLibDepDocsCommand(), new GenerateLibDepDocsCommand(),
new PackLibCommand(), new PackLibCommand(),
new EnvCommand(),
] ]
); );
} }

View File

@ -6,6 +6,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\SPCTarget;
#[CustomExt('imagick')] #[CustomExt('imagick')]
class imagick extends Extension class imagick extends Extension
@ -15,7 +16,7 @@ class imagick extends Extension
if (PHP_OS_FAMILY !== 'Linux') { if (PHP_OS_FAMILY !== 'Linux') {
return false; return false;
} }
if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) { if (SPCTarget::isTarget(SPCTarget::GLIBC)) {
return false; return false;
} }
// imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp // 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 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; 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 // on centos 7, it will use the symbol _ZTINSt6thread6_StateE, which is not defined in system libstdc++.so.6
[$static, $shared] = parent::getStaticAndSharedLibs(); [$static, $shared] = parent::getStaticAndSharedLibs();
if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) { if (SPCTarget::isTarget(SPCTarget::GLIBC)) {
$static .= ' -lstdc++'; $static .= ' -lstdc++';
$shared = str_replace('-lstdc++', '', $shared); $shared = str_replace('-lstdc++', '', $shared);
} }

View File

@ -25,22 +25,8 @@ class LinuxBuilder extends UnixBuilderBase
{ {
$this->options = $options; $this->options = $options;
// check musl-cross make installed if we use musl-cross-make
$arch = arch2gnu(php_uname('m'));
GlobalEnvManager::init(); GlobalEnvManager::init();
GlobalEnvManager::afterInit();
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)');
}
}
// concurrency // concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY')); $this->concurrency = intval(getenv('SPC_CONCURRENCY'));

View File

@ -6,6 +6,7 @@ namespace SPC\builder\linux;
use SPC\builder\traits\UnixSystemUtilTrait; use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\util\SPCTarget;
class SystemUtil class SystemUtil
{ {
@ -193,7 +194,7 @@ class SystemUtil
if (self::$libc_version !== null) { if (self::$libc_version !== null) {
return self::$libc_version; return self::$libc_version;
} }
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') { if (SPCTarget::isTarget(SPCTarget::GLIBC)) {
$result = shell()->execWithResult('ldd --version', false); $result = shell()->execWithResult('ldd --version', false);
if ($result[0] !== 0) { if ($result[0] !== 0) {
return null; return null;
@ -208,7 +209,7 @@ class SystemUtil
} }
return null; return null;
} }
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
if (self::isMuslDist()) { if (self::isMuslDist()) {
$result = shell()->execWithResult('ldd 2>&1', false); $result = shell()->execWithResult('ldd 2>&1', false);
} else { } else {

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; namespace SPC\builder\linux\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCTarget;
class icu extends LinuxLibraryBase 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"'; $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"'; $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) shell()->cd($this->source_dir . '/source')->initializeEnv($this)
->exec( ->exec(
"{$cppflags} {$cxxflags} {$ldflags} " . "{$cppflags} {$cxxflags} {$ldflags} " .

View File

@ -17,6 +17,7 @@ use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
abstract class UnixBuilderBase extends BuilderBase abstract class UnixBuilderBase extends BuilderBase
{ {
@ -200,7 +201,7 @@ abstract class UnixBuilderBase extends BuilderBase
$util = new SPCConfigUtil($this); $util = new SPCConfigUtil($this);
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $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 = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { if (SPCTarget::isTarget(SPCTarget::MUSL_STATIC)) {
$lens .= ' -static'; $lens .= ' -static';
} }
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); [$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' " : ''; $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : '';
$extLdFlags = "-extldflags '-pie'"; $extLdFlags = "-extldflags '-pie'";
$muslTags = ''; $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'"; $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
$muslTags = 'static_build,'; $muslTags = 'static_build,';
} }

View File

@ -4,12 +4,11 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait imagemagick trait imagemagick
{ {
@ -33,16 +32,16 @@ trait imagemagick
->optionalLib('bzip2', ...ac_with_args('bzlib')) ->optionalLib('bzip2', ...ac_with_args('bzlib'))
->addConfigureArgs( ->addConfigureArgs(
// TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so // 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-jxl',
'--without-x', '--without-x',
); );
// special: linux musl needs `-static` // special: linux musl-static needs `-static`
$ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl'; $ldflags = !SPCTarget::isTarget(SPCTarget::MUSL_STATIC) ? ('-static -ldl') : '-ldl';
// special: macOS needs -iconv // special: macOS needs -iconv
$libs = $this instanceof MacOSLibraryBase ? '-liconv' : ''; $libs = SPCTarget::isTarget(SPCTarget::MACHO) ? '-liconv' : '';
$ac->appendEnv([ $ac->appendEnv([
'LDFLAGS' => $ldflags, 'LDFLAGS' => $ldflags,

View File

@ -6,12 +6,13 @@ namespace SPC\builder\unix\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait ldap trait ldap
{ {
public function patchBeforeBuild(): bool 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); FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra);
return true; return true;
} }

View File

@ -33,7 +33,13 @@ trait libxslt
'--without-debugger', '--without-debugger',
"--with-libxml-prefix={$this->getBuildRootPath()}", "--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->patchPkgconfPrefix(['libexslt.pc']);
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\util\executor\UnixCMakeExecutor; use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget;
trait mimalloc trait mimalloc
{ {
@ -15,7 +16,7 @@ trait mimalloc
'-DMI_BUILD_SHARED=OFF', '-DMI_BUILD_SHARED=OFF',
'-DMI_INSTALL_TOPLEVEL=ON' '-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->addConfigureArgs('-DMI_LIBC_MUSL=ON');
} }
$cmake->build(); $cmake->build();

View File

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait pkgconfig trait pkgconfig
{ {
@ -14,7 +14,7 @@ trait pkgconfig
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->appendEnv([ ->appendEnv([
'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '', '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( ->configure(
'--with-internal-glib', '--with-internal-glib',

View File

@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCTarget;
trait postgresql trait postgresql
{ {
@ -50,7 +51,7 @@ trait postgresql
$error_exec_cnt += $output[0] === 0 ? 0 : 1; $error_exec_cnt += $output[0] === 0 ? 0 : 1;
if (!empty($output[1][0])) { if (!empty($output[1][0])) {
$ldflags = $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}"); $output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1; $error_exec_cnt += $output[0] === 0 ? 0 : 1;

View File

@ -13,6 +13,7 @@ use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\LicenseDumper; use SPC\util\LicenseDumper;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -63,7 +64,7 @@ class BuildPHPCommand extends BuildCommand
// check dynamic extension build env // check dynamic extension build env
// linux must build with glibc // 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!'); $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!');
return static::FAILURE; return static::FAILURE;
} }
@ -133,6 +134,8 @@ class BuildPHPCommand extends BuildCommand
// print info // print info
$indent_texts = [ $indent_texts = [
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')', 'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
'Build Target' => getenv('SPC_TARGET'),
'Build Toolchain' => getenv('SPC_TOOLCHAIN'),
'Build SAPI' => $builder->getBuildTypeName($rule), 'Build SAPI' => $builder->getBuildTypeName($rule),
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions), 'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions), 'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),

View File

@ -14,6 +14,7 @@ use SPC\store\Config;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -223,7 +224,11 @@ class DownloadCommand extends BaseCommand
'{name}' => $source, '{name}' => $source,
'{arch}' => arch2gnu(php_uname('m')), '{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY), '{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', '{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
]; ];
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern')); $find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace SPC\command\dev;
use SPC\command\BaseCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('dev:env', 'Returns the internally defined environment variables')]
class EnvCommand extends BaseCommand
{
public function configure()
{
$this->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("<error>Environment variable '{$env}' is not set.</error>");
return static::FAILURE;
}
$this->output->writeln("<info>{$val}</info>");
return static::SUCCESS;
}
}

View File

@ -16,6 +16,7 @@ use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@ -76,8 +77,12 @@ class PackLibCommand extends BuildCommand
'{name}' => $lib->getName(), '{name}' => $lib->getName(),
'{arch}' => arch2gnu(php_uname('m')), '{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY), '{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default', '{libc}' => match (getenv('SPC_TARGET')) {
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl',
SPCTarget::GLIBC => 'glibc',
default => 'default',
},
'{libcver}' => SystemUtil::getLibcVersionIfExists() ?? 'default',
]; ];
// detect suffix, for proper tar option // detect suffix, for proper tar option
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern')); $tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));

View File

@ -72,6 +72,14 @@ final class CheckListHandler
{ {
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) { foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) {
$ref = new \ReflectionClass($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 ($ref->getMethods() as $method) {
foreach ($method->getAttributes() as $a) { foreach ($method->getAttributes() as $a) {
if (is_a($a->getName(), AsCheckItem::class, true)) { if (is_a($a->getName(), AsCheckItem::class, true)) {

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace SPC\doctor;
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class OptionalCheck
{
public function __construct(public array $check) {}
}

View File

@ -4,10 +4,10 @@ declare(strict_types=1);
namespace SPC\doctor\item; namespace SPC\doctor\item;
use SPC\builder\linux\SystemUtil;
use SPC\doctor\AsCheckItem; use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\doctor\OptionalCheck;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
@ -17,19 +17,18 @@ use SPC\store\FileSystem;
use SPC\store\PackageManager; use SPC\store\PackageManager;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
#[OptionalCheck([self::class, 'optionalCheck'])]
class LinuxMuslCheck class LinuxMuslCheck
{ {
public static function optionalCheck(): bool
{
return getenv('SPC_TOOLCHAIN') === 'musl';
}
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */
#[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)] #[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)]
public function checkMusl(): CheckResult public function checkMusl(): CheckResult
{ {
if (SystemUtil::isMuslDist()) {
return CheckResult::ok('musl-based distro, skipped');
}
if (getenv('SPC_LIBC') === 'glibc') {
return CheckResult::ok('Building with glibc, skipped');
}
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) { if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) {
return CheckResult::ok(); return CheckResult::ok();
@ -40,13 +39,6 @@ class LinuxMuslCheck
#[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)] #[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)]
public function checkMuslCrossMake(): CheckResult public function checkMuslCrossMake(): CheckResult
{ {
if (SystemUtil::isMuslDist()) {
return CheckResult::ok('musl-based distro, skipped');
}
if (getenv('SPC_LIBC') === 'glibc') {
return CheckResult::ok('Building with glibc, skipped');
}
$arch = arch2gnu(php_uname('m')); $arch = arch2gnu(php_uname('m'));
$cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a";
$cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc"; $cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc";

View File

@ -11,6 +11,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage; use SPC\store\pkg\CustomPackage;
use SPC\store\source\CustomSourceBase; use SPC\store\source\CustomSourceBase;
use SPC\util\SPCTarget;
/** /**
* Source Downloader. * Source Downloader.
@ -591,7 +592,16 @@ class Downloader
public static function getPreBuiltLockName(string $source): string 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 = match (getenv('SPC_TARGET')) {
SPCTarget::MUSL_STATIC, SPCTarget::MUSL => 'musl',
SPCTarget::GLIBC => 'glibc',
default => 'default',
};
$libc_version = SystemUtil::getLibcVersionIfExists() ?? 'default';
return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}";
} }
/** /**

View File

@ -40,24 +40,6 @@ class GlobalEnvManager
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig'); 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(); $ini = self::readIniFile();
$default_put_list = []; $default_put_list = [];
@ -80,6 +62,29 @@ class GlobalEnvManager
self::putenv("{$k}={$v}"); 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 // apply second time
$ini2 = self::readIniFile(); $ini2 = self::readIniFile();
@ -108,13 +113,18 @@ class GlobalEnvManager
self::$env_cache[] = $val; 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)) { if (is_unix() && !str_contains(getenv('PATH'), $path)) {
self::putenv("PATH={$path}:" . getenv('PATH')); self::putenv("PATH={$path}:" . getenv('PATH'));
} }
} }
public static function afterInit(): void
{
SPCTarget::afterInitTargetForToolchain();
}
/** /**
* @throws WrongUsageException * @throws WrongUsageException
*/ */

View File

@ -55,7 +55,7 @@ class SPCConfigUtil
ob_get_clean(); ob_get_clean();
$ldflags = $this->getLdflagsString(); $ldflags = $this->getLdflagsString();
$libs = $this->getLibsString($libraries, $with_dependencies); $libs = $this->getLibsString($libraries, $with_dependencies);
if (PHP_OS_FAMILY === 'Darwin') { if (SPCTarget::isTarget(SPCTarget::MACHO)) {
$libs .= " {$this->getFrameworksString($extensions)}"; $libs .= " {$this->getFrameworksString($extensions)}";
} }
$cflags = $this->getIncludesString(); $cflags = $this->getIncludesString();
@ -146,7 +146,7 @@ class SPCConfigUtil
} }
} }
// patch: imagick (imagemagick wrapper) for linux needs libgomp // 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'; $short_name[] = '-lgomp';
} }
return implode(' ', $short_name); return implode(' ', $short_name);

View File

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace SPC\util;
use SPC\exception\WrongUsageException;
use SPC\util\toolchain\ClangNativeToolchain;
use SPC\util\toolchain\GccNativeToolchain;
use SPC\util\toolchain\MSVCToolchain;
use SPC\util\toolchain\MuslToolchain;
use SPC\util\toolchain\ToolchainInterface;
use SPC\util\toolchain\ZigToolchain;
/**
* SPC build target constants and toolchain initialization.
* format: {target_name}[-{libc_subtype}]
*/
class SPCTarget
{
public const MUSL = 'musl';
public const MUSL_STATIC = 'musl-static';
public const GLIBC = 'glibc';
public const MACHO = 'macho';
public const MSVC_STATIC = 'msvc-static';
public const TOOLCHAIN_LIST = [
'musl' => MuslToolchain::class,
'gcc-native' => GccNativeToolchain::class,
'clang-native' => ClangNativeToolchain::class,
'msvc' => MSVCToolchain::class,
'zig' => ZigToolchain::class,
];
public 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'));
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace SPC\util\toolchain;
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
class ClangNativeToolchain implements ToolchainInterface
{
public function initEnv(string $target): void
{
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=clang');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=clang++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
}
public function afterInit(string $target): void
{
// check clang exists
match (PHP_OS_FAMILY) {
'Linux' => 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 . '.'),
};
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace SPC\util\toolchain;
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\util\GlobalEnvManager;
class GccNativeToolchain implements ToolchainInterface
{
public function initEnv(string $target): void
{
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld.gold');
}
public function afterInit(string $target): void
{
// check gcc exists
match (PHP_OS_FAMILY) {
'Linux' => LinuxSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'),
'Darwin' => MacOSSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'),
'BSD' => FreeBSDSystemUtil::findCommand('g++') ?? throw new \RuntimeException('g++ not found, please install it or set CC/CXX to a valid path.'),
default => throw new \RuntimeException('GCC is not supported on ' . PHP_OS_FAMILY . '.'),
};
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\util\toolchain;
class MSVCToolchain implements ToolchainInterface
{
public function initEnv(string $target): void {}
public function afterInit(string $target): void {}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace SPC\util\toolchain;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCTarget;
class MuslToolchain implements ToolchainInterface
{
public function initEnv(string $target): void
{
// Check if the target is musl-static (the musl(-shared) target is not supported yet)
if (!in_array($target, [SPCTarget::MUSL_STATIC/* , SPCTarget::MUSL */], true)) {
throw new WrongUsageException('MuslToolchain can only be used with the "musl-static" target.');
}
$arch = getenv('GNU_ARCH');
// Set environment variables for musl toolchain
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld");
GlobalEnvManager::addPathIfNotExists('/usr/local/musl/bin');
GlobalEnvManager::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
}
public function afterInit(string $target): void
{
$arch = getenv('GNU_ARCH');
// append LD_LIBRARY_PATH to $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$ld_library_path = getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH');
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$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)');
}
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace SPC\util\toolchain;
interface ToolchainInterface
{
/**
* Initialize the environment for the given target.
*/
public function initEnv(string $target): void;
/**
* Perform actions after the environment has been initialized for the given target.
*/
public function afterInit(string $target): void;
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace SPC\util\toolchain;
class ZigToolchain implements ToolchainInterface
{
public function initEnv(string $target): void
{
// TODO: Implement zig
}
public function afterInit(string $target): void
{
// TODO: Implement afterInit() method.
}
}