mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-08 09:25:35 +08:00
v3 base
This commit is contained in:
57
src/StaticPHP/Toolchain/ClangNativeToolchain.php
Normal file
57
src/StaticPHP/Toolchain/ClangNativeToolchain.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\Exception\EnvironmentException;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Toolchain\Interface\UnixToolchainInterface;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
use StaticPHP\Util\System\MacOSUtil;
|
||||
|
||||
/**
|
||||
* Toolchain implementation for system clang compiler.
|
||||
*/
|
||||
class ClangNativeToolchain implements UnixToolchainInterface
|
||||
{
|
||||
public function initEnv(): 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(): void
|
||||
{
|
||||
foreach (['CC', 'CXX', 'AR', 'LD'] as $env) {
|
||||
$command = getenv($env);
|
||||
if (!$command || is_file($command)) {
|
||||
continue;
|
||||
}
|
||||
match (PHP_OS_FAMILY) {
|
||||
'Linux' => LinuxUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
|
||||
'Darwin' => MacOSUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
|
||||
default => throw new EnvironmentException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
{
|
||||
$compiler = getenv('CC') ?: 'clang';
|
||||
$version = shell(false)->execWithResult("{$compiler} --version", false);
|
||||
$head = pathinfo($compiler, PATHINFO_BASENAME);
|
||||
if ($version[0] === 0 && preg_match('/clang version (\d+\.\d+\.\d+)/', $version[1][0], $match)) {
|
||||
return "{$head} {$match[1]}";
|
||||
}
|
||||
return $head;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist();
|
||||
}
|
||||
}
|
||||
54
src/StaticPHP/Toolchain/GccNativeToolchain.php
Normal file
54
src/StaticPHP/Toolchain/GccNativeToolchain.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\Exception\EnvironmentException;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Toolchain\Interface\UnixToolchainInterface;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
use StaticPHP\Util\System\MacOSUtil;
|
||||
|
||||
class GccNativeToolchain implements UnixToolchainInterface
|
||||
{
|
||||
public function initEnv(): 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');
|
||||
}
|
||||
|
||||
public function afterInit(): void
|
||||
{
|
||||
foreach (['CC', 'CXX', 'AR', 'LD'] as $env) {
|
||||
$command = getenv($env);
|
||||
if (!$command || is_file($command)) {
|
||||
continue;
|
||||
}
|
||||
match (PHP_OS_FAMILY) {
|
||||
'Linux' => LinuxUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
|
||||
'Darwin' => MacOSUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
|
||||
default => throw new EnvironmentException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
{
|
||||
$compiler = getenv('CC') ?: 'gcc';
|
||||
$version = shell(false)->execWithResult("{$compiler} --version", false);
|
||||
$head = pathinfo($compiler, PATHINFO_BASENAME);
|
||||
if ($version[0] === 0 && preg_match('/gcc.*?(\d+\.\d+\.\d+)/', $version[1][0], $match)) {
|
||||
return "{$head} {$match[1]}";
|
||||
}
|
||||
return $head;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist();
|
||||
}
|
||||
}
|
||||
43
src/StaticPHP/Toolchain/Interface/ToolchainInterface.php
Normal file
43
src/StaticPHP/Toolchain/Interface/ToolchainInterface.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain\Interface;
|
||||
|
||||
/**
|
||||
* Interface for toolchain implementations
|
||||
*
|
||||
* This interface defines the contract for toolchain classes that handle
|
||||
* environment initialization and setup for different build targets.
|
||||
*/
|
||||
interface ToolchainInterface
|
||||
{
|
||||
/**
|
||||
* Initialize the environment for the given target.
|
||||
*
|
||||
* This method should set up any necessary environment variables,
|
||||
* paths, or configurations required for the build process.
|
||||
*/
|
||||
public function initEnv(): void;
|
||||
|
||||
/**
|
||||
* Perform actions after the environment has been initialized for the given target.
|
||||
*
|
||||
* This method is called after initEnv() and can be used for any
|
||||
* post-initialization setup or validation.
|
||||
*/
|
||||
public function afterInit(): void;
|
||||
|
||||
/**
|
||||
* Returns the compiler name and version for toolchains.
|
||||
*
|
||||
* If the toolchain does not support compiler information,
|
||||
* this method can return null.
|
||||
*/
|
||||
public function getCompilerInfo(): ?string;
|
||||
|
||||
/**
|
||||
* Returns whether the toolchain with target is built for full static linking.
|
||||
*/
|
||||
public function isStatic(): bool;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain\Interface;
|
||||
|
||||
interface UnixToolchainInterface extends ToolchainInterface {}
|
||||
24
src/StaticPHP/Toolchain/MSVCToolchain.php
Normal file
24
src/StaticPHP/Toolchain/MSVCToolchain.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
|
||||
class MSVCToolchain implements ToolchainInterface
|
||||
{
|
||||
public function initEnv(): void {}
|
||||
|
||||
public function afterInit(): void {}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
56
src/StaticPHP/Toolchain/MuslToolchain.php
Normal file
56
src/StaticPHP/Toolchain/MuslToolchain.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\Exception\EnvironmentException;
|
||||
use StaticPHP\Toolchain\Interface\UnixToolchainInterface;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
|
||||
class MuslToolchain implements UnixToolchainInterface
|
||||
{
|
||||
public function initEnv(): void
|
||||
{
|
||||
$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_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
|
||||
GlobalEnvManager::putenv("SPC_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
|
||||
}
|
||||
|
||||
public function afterInit(): 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_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 EnvironmentException('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)');
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
{
|
||||
$compiler = getenv('CC') ?: getenv('SPC_LINUX_DEFAULT_CC');
|
||||
$version = shell(false)->execWithResult("{$compiler} --version", false);
|
||||
$head = pathinfo($compiler, PATHINFO_BASENAME);
|
||||
if ($version[0] === 0 && preg_match('/linux-musl-cc.*(\d+.\d+.\d+)/', $version[1][0], $match)) {
|
||||
return "{$head} {$match[1]}";
|
||||
}
|
||||
return $head;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
92
src/StaticPHP/Toolchain/ToolchainManager.php
Normal file
92
src/StaticPHP/Toolchain/ToolchainManager.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
use StaticPHP\Util\PkgConfigUtil;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
|
||||
/**
|
||||
* Manages the selection and initialization of the appropriate toolchain based on environment variables and system characteristics.
|
||||
*/
|
||||
class ToolchainManager
|
||||
{
|
||||
/**
|
||||
* Get the toolchain class based on environment variables and OS.
|
||||
* For specific toolchain selection, see the method implementation.
|
||||
*/
|
||||
public static function getToolchainClass(): string
|
||||
{
|
||||
if ($tc = getenv('SPC_TOOLCHAIN')) {
|
||||
return $tc;
|
||||
}
|
||||
$libc = getenv('SPC_LIBC');
|
||||
if ($libc && !getenv('SPC_TARGET')) {
|
||||
// trigger_error('Setting SPC_LIBC is deprecated, please use SPC_TARGET instead.', E_USER_DEPRECATED);
|
||||
return match ($libc) {
|
||||
'musl' => LinuxUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class,
|
||||
'glibc' => !LinuxUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_LIBC must be musl for musl dist.'),
|
||||
default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc),
|
||||
};
|
||||
}
|
||||
|
||||
return match (PHP_OS_FAMILY) {
|
||||
'Linux' => ZigToolchain::class,
|
||||
'Windows' => MSVCToolchain::class,
|
||||
'Darwin' => ClangNativeToolchain::class,
|
||||
default => throw new WrongUsageException('Unsupported OS family: ' . PHP_OS_FAMILY),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the toolchain and set it in the container.
|
||||
*/
|
||||
public static function initToolchain(): void
|
||||
{
|
||||
$toolchainClass = self::getToolchainClass();
|
||||
$toolchain = new $toolchainClass();
|
||||
ApplicationContext::set(ToolchainInterface::class, $toolchain);
|
||||
/* @var ToolchainInterface $toolchainClass */
|
||||
$toolchain->initEnv();
|
||||
GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchainClass}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform post-initialization checks and setups for the toolchain.
|
||||
*/
|
||||
public static function afterInitToolchain(): void
|
||||
{
|
||||
if (!getenv('SPC_TOOLCHAIN')) {
|
||||
throw new WrongUsageException('SPC_TOOLCHAIN was not properly set. Please contact the developers.');
|
||||
}
|
||||
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
|
||||
if (SystemTarget::getLibc() === 'musl' && !ApplicationContext::get(ToolchainInterface::class)->isStatic() && !file_exists($musl_wrapper_lib)) {
|
||||
throw new WrongUsageException('You are linking against musl libc dynamically, but musl libc is not installed. Please use `bin/spc doctor` to install it.');
|
||||
}
|
||||
if (SystemTarget::getLibc() === 'glibc' && LinuxUtil::isMuslDist()) {
|
||||
throw new WrongUsageException('You are linking against glibc dynamically, which is only supported on glibc distros.');
|
||||
}
|
||||
|
||||
// init pkg-config for unix
|
||||
if (SystemTarget::isUnix()) {
|
||||
if (($found = PkgConfigUtil::findPkgConfig()) !== null) {
|
||||
GlobalEnvManager::putenv("PKG_CONFIG={$found}");
|
||||
} elseif (!ApplicationContext::has('elephant')) { // skip pkg-config check in elephant mode :P (elephant mode is only for building pkg-config itself)
|
||||
throw new WrongUsageException('Cannot find pkg-config executable. Please run `doctor` to fix this.');
|
||||
}
|
||||
}
|
||||
|
||||
/* @var ToolchainInterface $toolchain */
|
||||
$instance = ApplicationContext::get(ToolchainInterface::class);
|
||||
$instance->afterInit();
|
||||
if (getenv('PHP_BUILD_COMPILER') === false && ($compiler_info = $instance->getCompilerInfo())) {
|
||||
GlobalEnvManager::putenv("PHP_BUILD_COMPILER={$compiler_info}");
|
||||
}
|
||||
}
|
||||
}
|
||||
108
src/StaticPHP/Toolchain/ZigToolchain.php
Normal file
108
src/StaticPHP/Toolchain/ZigToolchain.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Toolchain;
|
||||
|
||||
use StaticPHP\Toolchain\Interface\UnixToolchainInterface;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
|
||||
class ZigToolchain implements UnixToolchainInterface
|
||||
{
|
||||
public function initEnv(): void
|
||||
{
|
||||
// Set environment variables for zig toolchain
|
||||
GlobalEnvManager::putenv('SPC_DEFAULT_CC=zig-cc');
|
||||
GlobalEnvManager::putenv('SPC_DEFAULT_CXX=zig-c++');
|
||||
GlobalEnvManager::putenv('SPC_DEFAULT_AR=zig-ar');
|
||||
GlobalEnvManager::putenv('SPC_DEFAULT_LD=zig-ld.lld');
|
||||
|
||||
// Generate additional objects needed for zig toolchain
|
||||
$paths = ['/usr/lib/gcc', '/usr/local/lib/gcc'];
|
||||
$objects = ['crtbeginS.o', 'crtendS.o'];
|
||||
$found = [];
|
||||
|
||||
foreach ($objects as $obj) {
|
||||
$located = null;
|
||||
foreach ($paths as $base) {
|
||||
$output = shell_exec("find {$base} -name {$obj} 2>/dev/null | grep -v '/32/' | head -n 1");
|
||||
$line = trim((string) $output);
|
||||
if ($line !== '') {
|
||||
$located = $line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($located) {
|
||||
$found[] = $located;
|
||||
}
|
||||
}
|
||||
GlobalEnvManager::putenv('SPC_EXTRA_RUNTIME_OBJECTS=' . implode(' ', $found));
|
||||
}
|
||||
|
||||
public function afterInit(): void
|
||||
{
|
||||
GlobalEnvManager::addPathIfNotExists($this->getPath());
|
||||
f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough
|
||||
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: '';
|
||||
$cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: '';
|
||||
$extraCflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') ?: '';
|
||||
$cflags = trim($cflags . ' -Wno-date-time');
|
||||
$cxxflags = trim($cxxflags . ' -Wno-date-time');
|
||||
$extraCflags = trim($extraCflags . ' -Wno-date-time');
|
||||
GlobalEnvManager::putenv("SPC_DEFAULT_C_FLAGS={$cflags}");
|
||||
GlobalEnvManager::putenv("SPC_DEFAULT_CXX_FLAGS={$cxxflags}");
|
||||
GlobalEnvManager::putenv("SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS={$extraCflags}");
|
||||
GlobalEnvManager::putenv('RANLIB=zig-ranlib');
|
||||
GlobalEnvManager::putenv('OBJCOPY=zig-objcopy');
|
||||
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
|
||||
if (!str_contains($extra_libs, '-lunwind')) {
|
||||
// Add unwind library if not already present
|
||||
$extra_libs = trim($extra_libs . ' -lunwind');
|
||||
GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}");
|
||||
}
|
||||
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: '';
|
||||
$has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4');
|
||||
if (!$has_avx512) {
|
||||
GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no');
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompilerInfo(): ?string
|
||||
{
|
||||
$version = shell(false)->execWithResult('zig version', false)[1][0] ?? '';
|
||||
return trim("zig {$version}");
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
// if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released
|
||||
if ($target = getenv('SPC_TARGET')) {
|
||||
if (str_contains($target, '-macos') || str_contains($target, '-native') && PHP_OS_FAMILY === 'Darwin') {
|
||||
return false;
|
||||
}
|
||||
if (str_contains($target, '-gnu')) {
|
||||
return false;
|
||||
}
|
||||
if (str_contains($target, '-dynamic')) {
|
||||
return false;
|
||||
}
|
||||
if (str_contains($target, '-musl')) {
|
||||
return true;
|
||||
}
|
||||
if (PHP_OS_FAMILY === 'Linux') {
|
||||
return LinuxUtil::isMuslDist();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (getenv('SPC_LIBC') === 'musl') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getPath(): string
|
||||
{
|
||||
return PKG_ROOT_PATH . '/zig';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user