Merge branch 'main' into ext/rdkafka

# Conflicts:
#	src/globals/test-extensions.php
This commit is contained in:
crazywhalecc
2024-12-13 15:12:05 +08:00
110 changed files with 2868 additions and 859 deletions

View File

@@ -22,6 +22,7 @@ use SPC\command\DumpLicenseCommand;
use SPC\command\ExtractCommand;
use SPC\command\InstallPkgCommand;
use SPC\command\MicroCombineCommand;
use SPC\command\SPCConfigCommand;
use SPC\command\SwitchPhpVersionCommand;
use Symfony\Component\Console\Application;
@@ -30,12 +31,15 @@ use Symfony\Component\Console\Application;
*/
final class ConsoleApplication extends Application
{
public const VERSION = '2.3.4';
public const VERSION = '2.4.2';
public function __construct()
{
parent::__construct('static-php-cli', self::VERSION);
// Define internal env vars and constants
require_once ROOT_DIR . '/src/globals/internal-env.php';
$this->addCommands(
[
// Common commands
@@ -49,6 +53,7 @@ final class ConsoleApplication extends Application
new ExtractCommand(),
new MicroCombineCommand(),
new SwitchPhpVersionCommand(),
new SPCConfigCommand(),
// Dev commands
new AllExtCommand(),

View File

@@ -6,6 +6,7 @@ namespace SPC\builder;
use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException;
use SPC\exception\InterruptException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
@@ -24,6 +25,12 @@ abstract class BuilderBase
/** @var array<string, Extension> extensions */
protected array $exts = [];
/** @var array<int, string> extension names */
protected array $ext_list = [];
/** @var array<int, string> library names */
protected array $lib_list = [];
/** @var bool compile libs only (just mark it) */
protected bool $libs_only = false;
@@ -160,7 +167,7 @@ abstract class BuilderBase
* @throws FileSystemException
* @throws RuntimeException
* @throws \ReflectionException
* @throws WrongUsageException
* @throws \Throwable|WrongUsageException
* @internal
*/
public function proveExts(array $extensions, bool $skip_check_deps = false): void
@@ -190,6 +197,7 @@ abstract class BuilderBase
foreach ($this->exts as $ext) {
$ext->checkDependency();
}
$this->ext_list = $extensions;
}
/**
@@ -276,7 +284,7 @@ abstract class BuilderBase
return false;
}
}
if (preg_match('/php-(\d+\.\d+\.\d+)/', $file, $match)) {
if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) {
return $match[1];
}
return false;
@@ -407,6 +415,13 @@ abstract class BuilderBase
}
logger()->debug('Running additional patch script: ' . $patch);
require $patch;
} catch (InterruptException $e) {
if ($e->getCode() === 0) {
logger()->notice('Patch script ' . $patch . ' interrupted' . ($e->getMessage() ? (': ' . $e->getMessage()) : '.'));
} else {
logger()->error('Patch script ' . $patch . ' interrupted with error code [' . $e->getCode() . ']' . ($e->getMessage() ? (': ' . $e->getMessage()) : '.'));
}
exit($e->getCode());
} catch (\Throwable $e) {
logger()->critical('Patch script ' . $patch . ' failed to run.');
if ($this->getOption('debug')) {
@@ -414,34 +429,11 @@ abstract class BuilderBase
} else {
logger()->critical('Please check with --debug option to see more details.');
}
throw $e;
}
}
}
/**
* Check if all libs are downloaded.
* If not, throw exception.
*
* @throws RuntimeException
*/
protected function checkLibsSource(): void
{
$not_downloaded = [];
foreach ($this->libs as $lib) {
if (!file_exists($lib->getSourceDir())) {
$not_downloaded[] = $lib::NAME;
}
}
if ($not_downloaded !== []) {
throw new RuntimeException(
'"' . implode(', ', $not_downloaded) .
'" totally ' . count($not_downloaded) .
' source' . (count($not_downloaded) === 1 ? '' : 's') .
' not downloaded, maybe you need to "fetch" ' . (count($not_downloaded) === 1 ? 'it' : 'them') . ' first?'
);
}
}
/**
* Generate micro extension test php code.
*/

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\builder\windows\WindowsBuilder;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager;
#[CustomExt('grpc')]
class grpc extends Extension
{
public function patchBeforeBuildconf(): bool
{
// soft link to the grpc source code
if ($this->builder instanceof WindowsBuilder) {
// not support windows yet
throw new \RuntimeException('grpc extension does not support windows yet');
}
if (!is_link(SOURCE_PATH . '/php-src/ext/grpc')) {
shell()->exec('ln -s ' . $this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
$macos = $this->builder instanceof MacOSBuilder ? "\n" . ' LDFLAGS="$LDFLAGS -framework CoreFoundation"' : '';
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/GRPC_LIBDIR=.*$/m', 'GRPC_LIBDIR=' . BUILD_LIB_PATH . $macos);
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/SEARCH_PATH=.*$/m', 'SEARCH_PATH="' . BUILD_ROOT_PATH . '"');
return true;
}
return false;
}
public function patchBeforeMake(): bool
{
// add -Wno-strict-prototypes
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true;
}
public function getUnixConfigureArg(): string
{
return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH;
}
}

View File

@@ -6,6 +6,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('parallel')]
@@ -17,4 +18,10 @@ class parallel extends Extension
throw new WrongUsageException('ext-parallel must be built with ZTS builds. Use "--enable-zts" option!');
}
}
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/parallel/config.m4', '/PHP_VERSION=.*/m', '');
return true;
}
}

View File

@@ -6,6 +6,8 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
@@ -14,6 +16,8 @@ class pgsql extends Extension
{
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function patchBeforeConfigure(): bool
{
@@ -24,4 +28,16 @@ class pgsql extends Extension
);
return true;
}
/**
* @throws WrongUsageException
* @throws RuntimeException
*/
public function getUnixConfigureArg(): string
{
if ($this->builder->getPHPVersionID() >= 80400) {
return '--with-pgsql PGSQL_CFLAGS=-I' . BUILD_INCLUDE_PATH . ' PGSQL_LIBS="-L' . BUILD_LIB_PATH . ' -lpq -lpgport -lpgcommon"';
}
return '--with-pgsql=' . BUILD_ROOT_PATH;
}
}

View File

@@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
#[CustomExt('sodium')]
class sodium extends Extension
{
public function patchBeforeBuildconf(): bool
{
// bypass error: unknown warning option '-Wno-logical-op' for macOS
return $this->removeLineContainingString();
}
private function removeLineContainingString(): bool
{
$path = SOURCE_PATH . '/php-src/ext/sodium/config.m4';
$search = '-Wno-logical-op';
if (!file_exists($path)) {
return false;
}
$content = file_get_contents($path);
$lines = preg_split('/\r\n|\n/', $content);
$filteredLines = array_filter($lines, function ($line) use ($search) {
return strpos($line, $search) === false;
});
$newContent = implode("\n", $filteredLines);
file_put_contents($path, $newContent);
return true;
}
}

View File

@@ -12,6 +12,9 @@ class zlib extends Extension
{
public function getUnixConfigureArg(): string
{
if ($this->builder->getPHPVersionID() >= 80400) {
return '--with-zlib';
}
return '--with-zlib --with-zlib-dir="' . BUILD_ROOT_PATH . '"';
}
}

View File

@@ -89,6 +89,11 @@ class BSDBuilder extends UnixBuilderBase
$zts_enable = $this->getPHPVersionID() < 80000 ? '--enable-maintainer-zts --disable-zend-signals' : '--enable-zts --disable-zend-signals';
$zts = $this->getOption('enable-zts', false) ? $zts_enable : '';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -109,6 +114,8 @@ class BSDBuilder extends UnixBuilderBase
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static ' : '--disable-embed ') .
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$json_74 .
$zts .
$this->makeExtensionArgs()

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -35,7 +35,7 @@ class LinuxBuilder extends UnixBuilderBase
GlobalEnvManager::init($this);
if (str_ends_with(getenv('CC'), 'linux-musl-gcc') && !file_exists("/usr/local/musl/bin/{$arch}-linux-musl-gcc")) {
if (str_ends_with(getenv('CC'), 'linux-musl-gcc') && !file_exists("/usr/local/musl/bin/{$arch}-linux-musl-gcc") && (getenv('SPC_NO_MUSL_PATH') !== 'yes')) {
throw new WrongUsageException('musl-cross-make not installed, please install it first. (You can use `doctor` command to install it)');
}
@@ -134,6 +134,11 @@ class LinuxBuilder extends UnixBuilderBase
}
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enable_cli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enable_fpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -163,6 +168,8 @@ class LinuxBuilder extends UnixBuilderBase
($enable_fpm ? '--enable-fpm ' : '--disable-fpm ') .
($enable_embed ? '--enable-embed=static ' : '--disable-embed ') .
($enable_micro ? '--enable-micro=all-static ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$disable_jit .
$json_74 .
$zts .

View File

@@ -6,7 +6,6 @@ namespace SPC\builder\linux;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class SystemUtil
{
@@ -83,52 +82,6 @@ class SystemUtil
};
}
/**
* @throws RuntimeException
* @throws WrongUsageException
* @throws WrongUsageException
*/
public static function getArchCFlags(string $cc, string $arch): string
{
if (php_uname('m') === $arch) {
return '';
}
return match (static::getCCType($cc)) {
'clang' => match ($arch) {
'x86_64' => '--target=x86_64-unknown-linux',
'arm64', 'aarch64' => '--target=arm64-unknown-linux',
default => throw new WrongUsageException('unsupported arch: ' . $arch),
},
'gcc' => '',
default => throw new WrongUsageException('cc compiler ' . $cc . ' is not supported'),
};
}
/**
* @throws RuntimeException
*/
public static function getTuneCFlags(string $arch): array
{
return match ($arch) {
'x86_64', 'arm64', 'aarch64' => [],
default => throw new RuntimeException('unsupported arch: ' . $arch),
};
}
public static function checkCCFlags(array $flags, string $cc): array
{
return array_filter($flags, fn ($flag) => static::checkCCFlag($flag, $cc));
}
public static function checkCCFlag(string $flag, string $cc): string
{
[$ret] = shell()->execWithResult("echo | {$cc} -E -x c - {$flag} 2>/dev/null");
if ($ret != 0) {
return '';
}
return $flag;
}
/**
* @throws RuntimeException
* @noinspection PhpUnused

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class grpc extends LinuxLibraryBase
{
use \SPC\builder\unix\library\grpc;
public const NAME = 'grpc';
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -137,6 +137,11 @@ class MacOSBuilder extends UnixBuilderBase
$json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : '';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -164,6 +169,8 @@ class MacOSBuilder extends UnixBuilderBase
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static ' : '--disable-embed ') .
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$json_74 .
$zts .
$this->makeExtensionArgs() . ' ' .

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class grpc extends MacOSLibraryBase
{
use \SPC\builder\unix\library\grpc;
public const NAME = 'grpc';
}

View File

@@ -56,7 +56,7 @@ class imap extends MacOSLibraryBase
->exec('chmod +x src/osdep/unix/drivers')
->exec('chmod +x src/osdep/unix/mkauths')
->exec(
"yes | make osx {$ssl_options} EXTRACFLAGS='-Wimplicit-function-declaration -Wno-incompatible-function-pointer-types {$out}'"
"echo y | make osx {$ssl_options} EXTRACFLAGS='-Wno-implicit-function-declaration -Wno-incompatible-function-pointer-types {$out}'"
);
try {
shell()

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*

View File

@@ -12,6 +12,7 @@ use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil;
abstract class UnixBuilderBase extends BuilderBase
{
@@ -128,6 +129,7 @@ abstract class UnixBuilderBase extends BuilderBase
foreach ($this->libs as $lib) {
$lib->calcDependency();
}
$this->lib_list = $sorted_libraries;
}
/**
@@ -170,6 +172,32 @@ abstract class UnixBuilderBase extends BuilderBase
}
}
}
// sanity check for embed
if (($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
logger()->info('running embed sanity check');
$sample_file_path = SOURCE_PATH . '/embed-test';
if (!is_dir($sample_file_path)) {
@mkdir($sample_file_path);
}
// copy embed test files
copy(ROOT_DIR . '/src/globals/common-tests/embed.c', $sample_file_path . '/embed.c');
copy(ROOT_DIR . '/src/globals/common-tests/embed.php', $sample_file_path . '/embed.php');
$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') {
$lens .= ' -static';
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
}
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult('./embed');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output));
}
}
}
/**

View File

@@ -19,6 +19,7 @@ trait gettext
'--disable-java ' .
'--disable-c+ ' .
$extra .
'--with-included-gettext ' .
'--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' .
'--prefix=' . BUILD_ROOT_PATH
)

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
trait grpc
{
protected function build(): void
{
shell()->cd($this->source_dir)
->exec('EXTRA_DEFINES=GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK EMBED_OPENSSL=false CXXFLAGS="-L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '" make static -j' . $this->builder->concurrency);
copy($this->source_dir . '/libs/opt/libgrpc.a', BUILD_LIB_PATH . '/libgrpc.a');
copy($this->source_dir . '/libs/opt/libboringssl.a', BUILD_LIB_PATH . '/libboringssl.a');
if (!file_exists(BUILD_LIB_PATH . '/libcares.a')) {
copy($this->source_dir . '/libs/opt/libcares.a', BUILD_LIB_PATH . '/libcares.a');
}
FileSystem::copyDir($this->source_dir . '/include/grpc', BUILD_INCLUDE_PATH . '/grpc');
FileSystem::copyDir($this->source_dir . '/include/grpc++', BUILD_INCLUDE_PATH . '/grpc++');
FileSystem::copyDir($this->source_dir . '/include/grpcpp', BUILD_INCLUDE_PATH . '/grpcpp');
}
}

View File

@@ -21,7 +21,7 @@ class SystemUtil
if (!$paths) {
$paths = explode(PATH_SEPARATOR, getenv('Path'));
if ($include_sdk_bin) {
$paths[] = PHP_SDK_PATH . '\bin';
$paths[] = getenv('PHP_SDK_PATH') . '\bin';
}
}
foreach ($paths as $path) {

View File

@@ -38,7 +38,7 @@ class WindowsBuilder extends BuilderBase
// ---------- set necessary options ----------
// set sdk (require visual studio 16 or 17)
$vs = SystemUtil::findVisualStudio()['version'];
$this->sdk_prefix = PHP_SDK_PATH . "\\phpsdk-{$vs}-x64.bat -t";
$this->sdk_prefix = getenv('PHP_SDK_PATH') . "\\phpsdk-{$vs}-x64.bat -t";
// set zts
$this->zts = $this->getOption('enable-zts', false);
@@ -103,6 +103,9 @@ class WindowsBuilder extends BuilderBase
$micro_w32 = $this->getOption('enable-micro-win32') ? ' --enable-micro-win32=yes' : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
cmd()->cd(SOURCE_PATH . '\php-src')
->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" .
@@ -114,6 +117,7 @@ class WindowsBuilder extends BuilderBase
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') .
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
$config_file_scan_dir .
"{$this->makeExtensionArgs()} " .
$zts .
'"'
@@ -244,6 +248,7 @@ class WindowsBuilder extends BuilderBase
foreach ($this->libs as $lib) {
$lib->calcDependency();
}
$this->lib_list = $sorted_libraries;
}
/**

View File

@@ -32,7 +32,7 @@ class xz extends WindowsLibraryBase
);
// copy liblzma.lib to liblzma_a.lib
copy(BUILD_LIB_PATH . '/liblzma.lib', BUILD_LIB_PATH . '/liblzma_a.lib');
copy(BUILD_LIB_PATH . '/lzma.lib', BUILD_LIB_PATH . '/liblzma_a.lib');
// patch lzma.h
FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '/lzma.h', 'defined(LZMA_API_STATIC)', 'defined(_WIN32)');
}

View File

@@ -10,6 +10,7 @@ use Psr\Log\LogLevel;
use SPC\ConsoleApplication;
use SPC\exception\ExceptionHandler;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
@@ -94,6 +95,11 @@ abstract class BaseCommand extends Command
$question = new ConfirmationQuestion($prompt->label . $case, $prompt->default);
return $helper->ask($input, $output, $question);
});
// init GlobalEnv
if (!$this instanceof BuildCommand) {
GlobalEnvManager::init();
}
if ($this->shouldExecute()) {
try {
// show raw argv list for logger()->debug

View File

@@ -22,6 +22,8 @@ class BuildCliCommand extends BuildCommand
{
public function configure(): void
{
$isWindows = PHP_OS_FAMILY === 'Windows';
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
$this->addOption('build-micro', null, null, 'Build micro SAPI');
@@ -32,6 +34,8 @@ class BuildCliCommand extends BuildCommand
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
$this->addOption('enable-zts', null, null, 'enable ZTS support');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
$this->addOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', $isWindows ? null : '/usr/local/etc/php');
$this->addOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', $isWindows ? null : '/usr/local/etc/php/conf.d');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
$this->addOption('with-micro-fake-cli', null, null, 'Let phpmicro\'s PHP_SAPI use "cli" instead of "micro"');
$this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs');
@@ -114,6 +118,9 @@ class BuildCliCommand extends BuildCommand
'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes',
'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no',
];
if (!empty($this->input->getOption('with-config-file-path'))) {
$indent_texts['Config File Path'] = $this->input->getOption('with-config-file-path');
}
if (!empty($this->input->getOption('with-hardcoded-ini'))) {
$indent_texts['Hardcoded INI'] = $this->input->getOption('with-hardcoded-ini');
}
@@ -174,7 +181,9 @@ class BuildCliCommand extends BuildCommand
// compile stopwatch :P
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Build complete, used ' . $time . ' s !');
logger()->info('');
logger()->info(' Build complete, used ' . $time . ' s !');
logger()->info('');
// ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ----------
$build_root_path = BUILD_ROOT_PATH;

View File

@@ -23,6 +23,8 @@ class DoctorCommand extends BaseCommand
{
try {
$checker = new CheckListHandler();
// skipped items
$skip_items = array_filter(explode(',', getenv('SPC_SKIP_DOCTOR_CHECK_ITEMS') ?: ''));
$fix_policy = $this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT;
foreach ($checker->runChecks() as $check) {
@@ -32,13 +34,12 @@ class DoctorCommand extends BaseCommand
$this->output->write('Checking <comment>' . $check->item_name . '</comment> ... ');
$result = call_user_func($check->callback);
if ($result === null) {
// check if this item is skipped
if (in_array($check->item_name, $skip_items) || ($result = call_user_func($check->callback)) === null) {
$this->output->writeln('skipped');
} elseif ($result instanceof CheckResult) {
if ($result->isOK()) {
$this->output->writeln($result->getMessage() ?? 'ok');
continue;
}

View File

@@ -30,7 +30,7 @@ class DownloadCommand extends BaseCommand
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
$this->addOption('shallow-clone', null, null, 'Clone shallow');
$this->addOption('with-openssl11', null, null, 'Use openssl 1.1');
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format (default 8.2)', '8.2');
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format (default 8.3)', '8.3');
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\exception\RuntimeException;
use SPC\util\SPCConfigUtil;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand('spc-config', 'Build dependencies')]
class SPCConfigCommand extends BuildCommand
{
protected bool $no_motd = true;
public function configure(): void
{
$this->addArgument('extensions', InputArgument::OPTIONAL, 'The extensions will be compiled, comma separated');
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
$this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs');
$this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts');
$this->addOption('includes', null, null, 'Add additional include path');
$this->addOption('libs', null, null, 'Add additional libs path');
}
/**
* @throws RuntimeException
*/
public function handle(): int
{
// transform string to array
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs'))));
// transform string to array
$extensions = $this->getArgument('extensions') ? $this->parseExtensionList($this->getArgument('extensions')) : [];
$include_suggest_ext = $this->getOption('with-suggested-exts');
$include_suggest_lib = $this->getOption('with-suggested-libs');
$util = new SPCConfigUtil(null, $this->input);
$config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
if ($this->getOption('includes')) {
$this->output->writeln($config['cflags']);
} elseif ($this->getOption('libs')) {
$this->output->writeln("{$config['ldflags']} {$config['libs']}");
} else {
$this->output->writeln("{$config['cflags']} {$config['ldflags']} {$config['libs']}");
}
return 0;
}
}

View File

@@ -19,9 +19,9 @@ class SwitchPhpVersionCommand extends BaseCommand
$this->addArgument(
'php-major-version',
InputArgument::REQUIRED,
'PHP major version (supported: 7.4, 8.0, 8.1, 8.2, 8.3)',
'PHP major version (supported: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4)',
null,
fn () => ['7.4', '8.0', '8.1', '8.2', '8.3']
fn () => ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
);
$this->no_motd = true;
@@ -31,7 +31,7 @@ class SwitchPhpVersionCommand extends BaseCommand
public function handle(): int
{
$php_ver = $this->input->getArgument('php-major-version');
if (!in_array($php_ver, ['7.4', '8.0', '8.1', '8.2', '8.3'])) {
if (!in_array($php_ver, ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'])) {
// match x.y.z
preg_match('/^\d+\.\d+\.\d+$/', $php_ver, $matches);
if (!$matches) {

View File

@@ -5,8 +5,12 @@ declare(strict_types=1);
namespace SPC\command\dev;
use SPC\builder\BuilderProvider;
use SPC\builder\LibraryBase;
use SPC\command\BuildCommand;
use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\util\DependencyUtil;
@@ -53,6 +57,8 @@ class PackLibCommand extends BuildCommand
$lib->tryBuild(true);
// do something like patching pkg-conf files.
$lib->beforePack();
// sanity check for libs (check if the libraries are built correctly)
$this->sanityCheckLib($lib);
// After build: load buildroot/ directory, and calculate increase files
$after_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true);
$increase_files = array_diff($after_buildroot, $before_buildroot);
@@ -82,4 +88,20 @@ class PackLibCommand extends BuildCommand
return static::FAILURE;
}
}
/**
* @throws WrongUsageException
* @throws RuntimeException
* @throws FileSystemException
*/
private function sanityCheckLib(LibraryBase $lib): void
{
logger()->info('Sanity check for library ' . $lib->getName());
// config
foreach ($lib->getStaticLibs() as $static_lib) {
if (!file_exists(FileSystem::convertPath(BUILD_LIB_PATH . '/' . $static_lib))) {
throw new RuntimeException('Static library ' . $static_lib . ' not found in ' . BUILD_LIB_PATH);
}
}
}
}

View File

@@ -73,9 +73,9 @@ class LinuxMuslCheck
FileSystem::extractSource($musl_version_name, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz");
logger()->info('Installing musl wrapper');
shell()->cd(SOURCE_PATH . "/{$musl_version_name}")
->exec('./configure --disable-gcc-wrapper')
->exec('make -j')
->exec("{$prefix}make install");
->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper')
->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j')
->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
// TODO: add path using putenv instead of editing /etc/profile
return true;
} catch (RuntimeException) {

View File

@@ -37,10 +37,10 @@ class WindowsToolCheckList
#[AsCheckItem('if php-sdk-binary-tools are downloaded', limit_os: 'Windows', level: 997)]
public function checkSDK(): ?CheckResult
{
if (!file_exists(PHP_SDK_PATH . DIRECTORY_SEPARATOR . 'phpsdk-starter.bat')) {
if (!file_exists(getenv('PHP_SDK_PATH') . DIRECTORY_SEPARATOR . 'phpsdk-starter.bat')) {
return CheckResult::fail('php-sdk-binary-tools not downloaded', 'install-php-sdk');
}
return CheckResult::ok(PHP_SDK_PATH);
return CheckResult::ok(getenv('PHP_SDK_PATH'));
}
#[AsCheckItem('if git associated command exists', limit_os: 'Windows', level: 996)]
@@ -81,8 +81,8 @@ class WindowsToolCheckList
public function installPhpSdk(): bool
{
try {
FileSystem::removeDir(PHP_SDK_PATH);
cmd(true)->exec('git.exe clone --depth 1 https://github.com/php/php-sdk-binary-tools.git ' . PHP_SDK_PATH);
FileSystem::removeDir(getenv('PHP_SDK_PATH'));
cmd(true)->exec('git.exe clone --depth 1 https://github.com/php/php-sdk-binary-tools.git ' . getenv('PHP_SDK_PATH'));
} catch (RuntimeException) {
return false;
}

View File

@@ -0,0 +1,7 @@
<?php
declare(strict_types=1);
namespace SPC\exception;
class InterruptException extends \Exception {}

View File

@@ -20,11 +20,17 @@ class CurlHook
}
if (getenv('GITHUB_USER')) {
$auth = base64_encode(getenv('GITHUB_USER') . ':' . getenv('GITHUB_TOKEN'));
$headers[] = "Authorization: Basic {$auth}";
$he = "Authorization: Basic {$auth}";
if (!in_array($he, $headers)) {
$headers[] = $he;
}
logger()->info("using basic github token for {$method} {$url}");
} else {
$auth = getenv('GITHUB_TOKEN');
$headers[] = "Authorization: Bearer {$auth}";
$he = "Authorization: Bearer {$auth}";
if (!in_array($he, $headers)) {
$headers[] = $he;
}
logger()->info("using bearer github token for {$method} {$url}");
}
}

View File

@@ -28,7 +28,7 @@ class Downloader
logger()->debug("finding {$name} source from bitbucket tag");
$data = json_decode(self::curlExec(
url: "https://api.bitbucket.org/2.0/repositories/{$source['repo']}/refs/tags",
retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0)
retry: self::getRetryTime()
), true);
$ver = $data['values'][0]['name'];
if (!$ver) {
@@ -38,7 +38,7 @@ class Downloader
$headers = self::curlExec(
url: $url,
method: 'HEAD',
retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0)
retry: self::getRetryTime()
);
preg_match('/^content-disposition:\s+attachment;\s*filename=("?)(?<filename>.+\.tar\.gz)\1/im', $headers, $matches);
if ($matches) {
@@ -66,7 +66,7 @@ class Downloader
$data = json_decode(self::curlExec(
url: "https://api.github.com/repos/{$source['repo']}/{$type}",
hooks: [[CurlHook::class, 'setupGithubToken']],
retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0)
retry: self::getRetryTime()
), true);
if (($source['prefer-stable'] ?? false) === false) {
@@ -85,7 +85,7 @@ class Downloader
url: $url,
method: 'HEAD',
hooks: [[CurlHook::class, 'setupGithubToken']],
retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0)
retry: self::getRetryTime()
);
preg_match('/^content-disposition:\s+attachment;\s*filename=("?)(?<filename>.+\.tar\.gz)\1/im', $headers, $matches);
if ($matches) {
@@ -108,11 +108,11 @@ class Downloader
*/
public static function getLatestGithubRelease(string $name, array $source, bool $match_result = true): array
{
logger()->debug("finding {$name} from github releases assests");
logger()->debug("finding {$name} from github releases assets");
$data = json_decode(self::curlExec(
url: "https://api.github.com/repos/{$source['repo']}/releases",
hooks: [[CurlHook::class, 'setupGithubToken']],
retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0)
retry: self::getRetryTime()
), true);
$url = null;
foreach ($data as $release) {
@@ -149,7 +149,7 @@ class Downloader
public static function getFromFileList(string $name, array $source): array
{
logger()->debug("finding {$name} source from file list");
$page = self::curlExec($source['url'], retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0));
$page = self::curlExec($source['url'], retry: self::getRetryTime());
preg_match_all($source['regex'], $page, $matches);
if (!$matches) {
throw new DownloaderException("Failed to get {$name} version");
@@ -182,6 +182,7 @@ class Downloader
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null, int $lock_as = SPC_LOCK_SOURCE): void
{
@@ -193,7 +194,7 @@ class Downloader
}
};
self::registerCancelEvent($cancel_func);
self::curlDown(url: $url, path: FileSystem::convertPath(DOWNLOAD_PATH . "/{$filename}"), retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0));
self::curlDown(url: $url, path: FileSystem::convertPath(DOWNLOAD_PATH . "/{$filename}"), retry: self::getRetryTime());
self::unregisterCancelEvent();
logger()->debug("Locking {$filename}");
self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $lock_as]);
@@ -220,6 +221,7 @@ class Downloader
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null, int $retry = 0, int $lock_as = SPC_LOCK_SOURCE): void
{
@@ -345,7 +347,7 @@ class Downloader
$pkg['url'],
$pkg['rev'],
$pkg['extract'] ?? null,
intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0),
self::getRetryTime(),
SPC_LOCK_PRE_BUILT
);
break;
@@ -381,6 +383,7 @@ class Downloader
* @param int $lock_as Lock source type (default: SPC_LOCK_SOURCE)
* @throws DownloaderException
* @throws FileSystemException
* @throws WrongUsageException
*/
public static function downloadSource(string $name, ?array $source = null, bool $force = false, int $lock_as = SPC_LOCK_SOURCE): void
{
@@ -448,7 +451,7 @@ class Downloader
$source['url'],
$source['rev'],
$source['path'] ?? null,
intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0),
self::getRetryTime(),
$lock_as
);
break;
@@ -539,6 +542,7 @@ class Downloader
* Use curl to download sources from url
*
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function curlDown(string $url, string $path, string $method = 'GET', array $headers = [], array $hooks = [], int $retry = 0): void
{
@@ -563,7 +567,7 @@ class Downloader
}
if ($retry > 0) {
logger()->notice('Retrying curl download ...');
self::curlDown($url, $path, $method, $used_headers, retry: intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0));
self::curlDown($url, $path, $method, $used_headers, retry: $retry - 1);
return;
}
throw $e;
@@ -597,4 +601,9 @@ class Downloader
pcntl_signal(2, SIG_IGN);
}
}
private static function getRetryTime(): int
{
return intval(getenv('SPC_RETRY_TIME') ? getenv('SPC_RETRY_TIME') : 0);
}
}

View File

@@ -367,6 +367,9 @@ class FileSystem
}
}
}
if (is_link($dir)) {
return unlink($dir);
}
return rmdir($dir);
}
@@ -428,7 +431,7 @@ class FileSystem
{
$replacement = [
'{pkg_root_path}' => PKG_ROOT_PATH,
'{php_sdk_path}' => defined('PHP_SDK_PATH') ? PHP_SDK_PATH : WORKING_DIR . '/php-sdk-binary-tools',
'{php_sdk_path}' => getenv('PHP_SDK_PATH') ? getenv('PHP_SDK_PATH') : WORKING_DIR . '/php-sdk-binary-tools',
'{working_dir}' => WORKING_DIR,
'{download_path}' => DOWNLOAD_PATH,
'{source_path}' => SOURCE_PATH,
@@ -480,7 +483,7 @@ class FileSystem
};
} elseif (PHP_OS_FAMILY === 'Windows') {
// use php-sdk-binary-tools/bin/7za.exe
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
$_7z = self::convertPath(getenv('PHP_SDK_PATH') . '/bin/7za.exe');
// Windows notes: I hate windows tar.......
// When extracting .tar.gz like libxml2, it shows a symlink error and returns code[1].

View File

@@ -25,6 +25,8 @@ class SourcePatcher
FileSystem::addSourceExtractHook('pdo_sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('yaml', [SourcePatcher::class, 'patchYamlWin32']);
FileSystem::addSourceExtractHook('libyaml', [SourcePatcher::class, 'patchLibYaml']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchImapLicense']);
FileSystem::addSourceExtractHook('ext-imagick', [SourcePatcher::class, 'patchImagickWith84']);
}
/**
@@ -370,6 +372,27 @@ class SourcePatcher
return true;
}
/**
* Patch imap license file for PHP < 8.4
*/
public static function patchImapLicense(): bool
{
if (!file_exists(SOURCE_PATH . '/php-src/ext/imap/LICENSE') && is_dir(SOURCE_PATH . '/php-src/ext/imap')) {
file_put_contents(SOURCE_PATH . '/php-src/ext/imap/LICENSE', file_get_contents(ROOT_DIR . '/src/globals/extra/Apache_LICENSE'));
return true;
}
return false;
}
/**
* Patch imagick for PHP 8.4
*/
public static function patchImagickWith84(): bool
{
SourcePatcher::patchFile('imagick_php84.patch', SOURCE_PATH . '/php-src/ext/imagick');
return true;
}
/**
* Patch cli SAPI Makefile for Windows.
*
@@ -416,7 +439,7 @@ class SourcePatcher
return true;
}
if ($ver_id < 80200) {
self::patchFile('spc_fix_libxml2_12_php81.patch', SOURCE_PATH . '/php-src');
// self::patchFile('spc_fix_libxml2_12_php81.patch', SOURCE_PATH . '/php-src');
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}

View File

@@ -19,7 +19,7 @@ class PhpSource extends CustomSourceBase
*/
public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_LOCK_SOURCE): void
{
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.1';
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.3';
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major), $force);
}

View File

@@ -5,11 +5,9 @@ declare(strict_types=1);
namespace SPC\util;
use SPC\builder\BuilderBase;
use SPC\builder\freebsd\SystemUtil as BSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\builder\windows\SystemUtil as WindowsSystemUtil;
use SPC\builder\linux\SystemUtil;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
/**
* Environment variable manager
@@ -26,171 +24,87 @@ class GlobalEnvManager
/**
* Initialize the environment variables
*
* @param BuilderBase $builder Builder
* @param null|BuilderBase $builder Builder
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function init(BuilderBase $builder): void
public static function init(?BuilderBase $builder = null): void
{
// Init global env, build related path
self::putenv('BUILD_ROOT_PATH=' . BUILD_ROOT_PATH);
self::putenv('BUILD_INCLUDE_PATH=' . BUILD_INCLUDE_PATH);
self::putenv('BUILD_LIB_PATH=' . BUILD_LIB_PATH);
self::putenv('BUILD_BIN_PATH=' . BUILD_BIN_PATH);
self::putenv('PKG_ROOT_PATH=' . PKG_ROOT_PATH);
self::putenv('SOURCE_PATH=' . SOURCE_PATH);
self::putenv('DOWNLOAD_PATH=' . DOWNLOAD_PATH);
// Check pre-defined env vars exists
if (getenv('BUILD_ROOT_PATH') === false) {
throw new RuntimeException('You must include src/globals/internal-env.php before using GlobalEnvManager');
}
// Init SPC env
self::initIfNotExists('SPC_CONCURRENCY', match (PHP_OS_FAMILY) {
'Windows' => (string) WindowsSystemUtil::getCpuCount(),
'Darwin' => (string) MacOSSystemUtil::getCpuCount(),
'Linux' => (string) LinuxSystemUtil::getCpuCount(),
'BSD' => (string) BSDSystemUtil::getCpuCount(),
default => '1',
});
// Define env vars for unix
if (is_unix()) {
self::putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
if ($builder instanceof BuilderBase) {
self::putenv('SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS=' . ($builder->getOption('no-strip') ? '-g -O0' : '-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'));
}
}
// Init system-specific env
match (PHP_OS_FAMILY) {
'Windows' => self::initWindowsEnv(),
'Darwin' => self::initDarwinEnv($builder),
'Linux' => self::initLinuxEnv($builder),
'BSD' => 'TODO',
default => logger()->warning('Unknown OS: ' . PHP_OS_FAMILY),
};
}
private static function initWindowsEnv(): void
{
// Windows need php-sdk binary tools
self::initIfNotExists('PHP_SDK_PATH', WORKING_DIR . DIRECTORY_SEPARATOR . 'php-sdk-binary-tools');
self::initIfNotExists('UPX_EXEC', PKG_ROOT_PATH . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'upx.exe');
self::initIfNotExists('SPC_MICRO_PATCHES', 'static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static');
}
private static function initLinuxEnv(BuilderBase $builder): void
{
// Init C Compiler and C++ Compiler (alpine)
if (LinuxSystemUtil::isMuslDist()) {
self::initIfNotExists('CC', 'gcc');
self::initIfNotExists('CXX', 'g++');
self::initIfNotExists('AR', 'ar');
self::initIfNotExists('LD', 'ld.gold');
} else {
// Define env vars for linux
if (PHP_OS_FAMILY === 'Linux') {
$arch = arch2gnu(php_uname('m'));
self::initIfNotExists('CC', "{$arch}-linux-musl-gcc");
self::initIfNotExists('CXX', "{$arch}-linux-musl-g++");
self::initIfNotExists('AR', "{$arch}-linux-musl-ar");
self::initIfNotExists('LD', 'ld.gold');
if (SystemUtil::isMuslDist()) {
self::putenv('SPC_LINUX_DEFAULT_CC=gcc');
self::putenv('SPC_LINUX_DEFAULT_CXX=g++');
self::putenv('SPC_LINUX_DEFAULT_AR=ar');
} 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_PHP_DEFAULT_LD_LIBRARY_PATH_CMD=LD_LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib");
if (getenv('SPC_NO_MUSL_PATH') !== 'yes') {
self::putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . getenv('PATH'));
}
}
// Init arch-specific cflags
self::initIfNotExists('SPC_DEFAULT_C_FLAGS', '');
self::initIfNotExists('SPC_DEFAULT_CXX_FLAGS', '');
self::initIfNotExists('SPC_EXTRA_LIBS', '');
// SPC_MICRO_PATCHES for linux
self::initIfNotExists('SPC_MICRO_PATCHES', 'static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream');
// Init linux-only env
self::initIfNotExists('UPX_EXEC', PKG_ROOT_PATH . '/bin/upx');
self::initIfNotExists('GNU_ARCH', arch2gnu(php_uname('m')));
// optimization flags with different strip option
$php_extra_cflags_optimize = $builder->getOption('no-strip') ? '-g -O0' : '-g -Os';
// optimization flags with different c compiler
$clang_use_lld = str_ends_with(getenv('CC'), 'clang') && LinuxSystemUtil::findCommand('lld') ? '-Xcompiler -fuse-ld=lld ' : '';
$init_spc_cmd_maps = [
// Init default build command prefix
'SPC_CMD_PREFIX_PHP_BUILDCONF' => './buildconf --force',
'SPC_CMD_PREFIX_PHP_CONFIGURE' => $builder->getOption('ld_library_path') . ' ./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg',
'SPC_CMD_PREFIX_PHP_MAKE' => 'make -j' . getenv('SPC_CONCURRENCY'),
// Init default build vars for build command
'SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS' => getenv('SPC_DEFAULT_C_FLAGS'),
'SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS' => '-L' . BUILD_LIB_PATH,
'SPC_CMD_VAR_PHP_CONFIGURE_LIBS' => '-ldl -lpthread -lm',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS' => $php_extra_cflags_optimize . ' -fno-ident -fPIE',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS' => '',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM' => $clang_use_lld . '-all-static',
// Init env.ini file, read order:
// WORKING_DIR/config/env.ini
// ROOT_DIR/config/env.ini
$ini_files = [
WORKING_DIR . '/config/env.ini',
ROOT_DIR . '/config/env.ini',
];
foreach ($init_spc_cmd_maps as $name => $value) {
self::initIfNotExists($name, $value);
$ini = null;
foreach ($ini_files as $ini_file) {
if (file_exists($ini_file)) {
$ini = parse_ini_file($ini_file, true);
break;
}
}
self::initUnixEnv($builder);
}
private static function initDarwinEnv(BuilderBase $builder): void
{
// Init C Compiler and C++ Compiler
self::initIfNotExists('CC', 'clang');
self::initIfNotExists('CXX', 'clang++');
// Init arch-specific cflags
self::initIfNotExists('SPC_DEFAULT_C_FLAGS', match (php_uname('m')) {
'arm64', 'aarch64' => '--target=arm64-apple-darwin',
default => '--target=x86_64-apple-darwin',
});
// Init arch-specific cxxflags
self::initIfNotExists('SPC_DEFAULT_CXX_FLAGS', match (php_uname('m')) {
'arm64', 'aarch64' => '--target=arm64-apple-darwin',
default => '--target=x86_64-apple-darwin',
});
// Init extra libs (will be appended before `before-php-buildconf` event point)
self::initIfNotExists('SPC_EXTRA_LIBS', '');
// SPC_MICRO_PATCHES for macOS
self::initIfNotExists('SPC_MICRO_PATCHES', 'static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv');
$init_spc_cmd_maps = [
// Init default build command prefix
'SPC_CMD_PREFIX_PHP_BUILDCONF' => './buildconf --force',
'SPC_CMD_PREFIX_PHP_CONFIGURE' => './configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg',
'SPC_CMD_PREFIX_PHP_MAKE' => 'make -j' . getenv('SPC_CONCURRENCY'),
// Init default build vars for build command
'SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS' => getenv('SPC_DEFAULT_C_FLAGS') . ' -Werror=unknown-warning-option',
'SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS' => '-L' . BUILD_LIB_PATH,
'SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS' => $builder->getOption('no-strip') ? '-g -O0' : '-g -Os',
'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS' => '-lresolv',
];
foreach ($init_spc_cmd_maps as $name => $value) {
self::initIfNotExists($name, $value);
if ($ini === null) {
throw new WrongUsageException('env.ini not found');
}
self::initUnixEnv($builder);
}
private static function initUnixEnv(BuilderBase $builder): void
{
self::putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
}
/**
* Initialize the environment variable if it does not exist
*
* @param string $name Environment variable name
* @param string $value Environment variable value
*/
private static function initIfNotExists(string $name, string $value): void
{
if (($val = getenv($name)) === false) {
self::putenv($name . '=' . $value);
} else {
logger()->debug("env [{$name}] existing: {$val}");
if ($ini === false || !isset($ini['global'])) {
throw new WrongUsageException('Failed to parse ' . $ini_file);
}
self::applyConfig($ini['global']);
match (PHP_OS_FAMILY) {
'Windows' => self::applyConfig($ini['windows']),
'Darwin' => self::applyConfig($ini['macos']),
'Linux' => self::applyConfig($ini['linux']),
'BSD' => self::applyConfig($ini['freebsd']),
default => null,
};
}
private static function putenv(string $val): void
public static function putenv(string $val): void
{
f_putenv($val);
self::$env_cache[] = $val;
}
private static function applyConfig(array $ini): void
{
foreach ($ini as $k => $v) {
if (getenv($k) === false) {
self::putenv($k . '=' . $v);
}
}
}
}

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
namespace SPC\util;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
use SPC\builder\macos\MacOSBuilder;
use SPC\store\Config;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
class SPCConfigUtil
{
public function __construct(private ?BuilderBase $builder = null, ?InputInterface $input = null)
{
if ($builder === null) {
$this->builder = BuilderProvider::makeBuilderByInput($input ?? new ArgvInput());
}
}
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
{
[$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
ob_start();
$this->builder->proveLibs($libraries);
$this->builder->proveExts($extensions);
ob_get_clean();
$ldflags = $this->getLdflagsString();
$libs = $this->getLibsString($libraries);
$cflags = $this->getIncludesString();
// embed
$libs = '-lphp -lc ' . $libs;
$extra_env = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS');
if (is_string($extra_env)) {
$libs .= ' ' . $extra_env;
}
// c++
if ($this->builder->hasCpp()) {
$libs .= $this->builder instanceof MacOSBuilder ? ' -lc++' : ' -lstdc++';
}
return [
'cflags' => $cflags,
'ldflags' => $ldflags,
'libs' => $libs,
];
}
private function getIncludesString(): string
{
$base = BUILD_INCLUDE_PATH;
$php_embed_includes = [
"-I{$base}",
"-I{$base}/php",
"-I{$base}/php/main",
"-I{$base}/php/TSRM",
"-I{$base}/php/Zend",
"-I{$base}/php/ext",
];
return implode(' ', $php_embed_includes);
}
private function getLdflagsString(): string
{
return '-L' . BUILD_LIB_PATH;
}
private function getLibsString(array $libraries): string
{
$short_name = [];
foreach (array_reverse($libraries) as $library) {
$libs = Config::getLib($library, 'static-libs', []);
foreach ($libs as $lib) {
$short_name[] = $this->getShortLibName($lib);
}
if (PHP_OS_FAMILY !== 'Darwin') {
continue;
}
foreach (Config::getLib($library, 'frameworks', []) as $fw) {
$ks = '-framework ' . $fw;
if (!in_array($ks, $short_name)) {
$short_name[] = $ks;
}
}
}
// patch: imagick (imagemagick wrapper) for linux needs -lgomp
if (in_array('imagemagick', $libraries) && PHP_OS_FAMILY === 'Linux') {
$short_name[] = '-lgomp';
}
return implode(' ', $short_name);
}
private function getShortLibName(string $lib): string
{
if (!str_starts_with($lib, 'lib') || !str_ends_with($lib, '.a')) {
return BUILD_LIB_PATH . '/' . $lib;
}
// get short name
return '-l' . substr($lib, 3, -2);
}
}

View File

@@ -62,6 +62,9 @@ class UnixShell
logger()->debug(ConsoleColor::blue('[EXEC] ') . ConsoleColor::gray($cmd));
}
logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']);
if ($this->cd !== null) {
$cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd;
}
exec($cmd, $out, $code);
return [$code, $out];
}

View File

@@ -0,0 +1,17 @@
#include <sapi/embed/php_embed.h>
int main(int argc,char **argv){
PHP_EMBED_START_BLOCK(argc,argv)
zend_file_handle file_handle;
zend_stream_init_filename(&file_handle,"embed.php");
if(php_execute_script(&file_handle) == FAILURE){
php_printf("Failed to execute PHP script.\n");
}
PHP_EMBED_END_BLOCK()
return 0;
}

View File

@@ -0,0 +1,4 @@
<?php
declare(strict_types=1);
echo 'hello' . PHP_EOL;

View File

@@ -2,32 +2,16 @@
declare(strict_types=1);
use SPC\store\FileSystem;
use ZM\Logger\ConsoleLogger;
define('WORKING_DIR', getcwd());
define('ROOT_DIR', dirname(__DIR__, 2));
putenv('WORKING_DIR=' . WORKING_DIR);
putenv('ROOT_DIR=' . ROOT_DIR);
// CLI start time
define('START_TIME', microtime(true));
define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot')));
define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source')));
define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads')));
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin')));
define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib')));
define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('INSTALL_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include')));
define('SEPARATED_PATH', [
'/' . pathinfo(BUILD_LIB_PATH)['basename'], // lib
'/' . pathinfo(BUILD_INCLUDE_PATH)['basename'], // include
BUILD_ROOT_PATH,
]);
if (PHP_OS_FAMILY === 'Windows') {
define('PHP_SDK_PATH', is_string($a = getenv('PHP_SDK_PATH')) ? $a : (WORKING_DIR . DIRECTORY_SEPARATOR . 'php-sdk-binary-tools'));
}
// for windows, prevent calling Invoke-WebRequest and wsl command
const SPC_CURL_EXEC = PHP_OS_FAMILY === 'Windows' ? 'curl.exe' : 'curl';
const SPC_GIT_EXEC = PHP_OS_FAMILY === 'Windows' ? 'git.exe' : 'git';

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
assert(function_exists('gettext'));
assert(function_exists('bindtextdomain'));
assert(function_exists('textdomain'));
assert(function_exists('bind_textdomain_codeset'));
if (!is_dir('locale/en_US/LC_MESSAGES/')) {
mkdir('locale/en_US/LC_MESSAGES/', 0755, true);
@@ -19,7 +18,6 @@ setlocale(LC_ALL, 'en_US');
$domain = 'test';
bindtextdomain($domain, 'locale/');
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
assert(gettext(json_decode('"\u793a\u4f8b"', true)) === 'Example');

View File

@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
assert(class_exists('Grpc\ChannelCredentials'));

View File

@@ -6,7 +6,6 @@ assert(function_exists('swoole_cpu_num'));
assert(function_exists('swoole_string'));
assert(class_exists('Swoole\Coroutine'));
assert(class_exists('Swoole\Coroutine\Http2\Client'));
assert(class_exists('Swoole\Coroutine\Redis'));
assert(class_exists('Swoole\Coroutine\WaitGroup'));
assert(class_exists('Swoole\Http2\Request'));
assert(constant('SWOOLE_VERSION'));

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
use Psr\Log\LoggerInterface;
use SPC\builder\BuilderBase;
use SPC\builder\BuilderProvider;
use SPC\exception\InterruptException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\util\UnixShell;
@@ -31,6 +32,11 @@ function logger(): LoggerInterface
return $ob_logger;
}
function is_unix(): bool
{
return in_array(PHP_OS_FAMILY, ['Linux', 'Darwin', 'BSD']);
}
/**
* Transfer architecture name to gnu triplet
*
@@ -120,6 +126,11 @@ function patch_point(): string
return BuilderProvider::getBuilder()->getPatchPoint();
}
function patch_point_interrupt(int $retcode, string $msg = ''): InterruptException
{
return new InterruptException(message: $msg, code: $retcode);
}
// ------- function f_* part -------
// f_ means standard function wrapper

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
use SPC\builder\freebsd\SystemUtil as BSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\builder\windows\SystemUtil as WindowsSystemUtil;
use SPC\store\FileSystem;
use SPC\util\GlobalEnvManager;
define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot')));
define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include')));
define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib')));
define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin')));
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source')));
define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads')));
define('CPU_COUNT', match (PHP_OS_FAMILY) {
'Windows' => (string) WindowsSystemUtil::getCpuCount(),
'Darwin' => (string) MacOSSystemUtil::getCpuCount(),
'Linux' => (string) LinuxSystemUtil::getCpuCount(),
'BSD' => (string) BSDSystemUtil::getCpuCount(),
default => 1,
});
define('GNU_ARCH', arch2gnu(php_uname('m')));
define('MAC_ARCH', match ($_im8a = arch2gnu(php_uname('m'))) {
'aarch64' => 'arm64',
default => $_im8a
});
// deprecated variables
define('SEPARATED_PATH', [
'/' . pathinfo(BUILD_LIB_PATH)['basename'], // lib
'/' . pathinfo(BUILD_INCLUDE_PATH)['basename'], // include
BUILD_ROOT_PATH,
]);
// add these to env vars with same name
GlobalEnvManager::putenv('BUILD_ROOT_PATH=' . BUILD_ROOT_PATH);
GlobalEnvManager::putenv('BUILD_INCLUDE_PATH=' . BUILD_INCLUDE_PATH);
GlobalEnvManager::putenv('BUILD_LIB_PATH=' . BUILD_LIB_PATH);
GlobalEnvManager::putenv('BUILD_BIN_PATH=' . BUILD_BIN_PATH);
GlobalEnvManager::putenv('PKG_ROOT_PATH=' . PKG_ROOT_PATH);
GlobalEnvManager::putenv('SOURCE_PATH=' . SOURCE_PATH);
GlobalEnvManager::putenv('DOWNLOAD_PATH=' . DOWNLOAD_PATH);
GlobalEnvManager::putenv('CPU_COUNT=' . CPU_COUNT);
GlobalEnvManager::putenv('GNU_ARCH=' . GNU_ARCH);
GlobalEnvManager::putenv('MAC_ARCH=' . MAC_ARCH);

View File

@@ -0,0 +1,40 @@
From 65e27f2bc02e7e8f1bf64e26e359e42a1331fca1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= <mvorisek@mvorisek.cz>
Date: Wed, 25 Sep 2024 10:56:28 +0200
Subject: [PATCH] Fix removed "php_strtolower" for PHP 8.4
---
imagick.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/imagick.c b/imagick.c
index 1b765389..ebab7ae7 100644
--- a/imagick.c
+++ b/imagick.c
@@ -610,7 +610,7 @@ static zval *php_imagick_read_property(zend_object *object, zend_string *member,
if (format) {
retval = rv;
ZVAL_STRING(retval, format);
- php_strtolower(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
+ zend_str_tolower(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
IMAGICK_FREE_MAGICK_MEMORY(format);
} else {
retval = rv;
@@ -683,7 +683,7 @@ static zval *php_imagick_read_property(zval *object, zval *member, int type, voi
if (format) {
retval = rv;
ZVAL_STRING(retval, format);
- php_strtolower(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
+ zend_str_tolower(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
IMAGICK_FREE_MAGICK_MEMORY(format);
} else {
retval = rv;
@@ -766,7 +766,7 @@ static zval *php_imagick_read_property(zval *object, zval *member, int type, con
if (format) {
ZVAL_STRING(retval, format, 1);
- php_strtolower(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
+ zend_str_tolower(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
IMAGICK_FREE_MAGICK_MEMORY(format);
} else {
ZVAL_STRING(retval, "", 1);

View File

@@ -11,16 +11,35 @@ declare(strict_types=1);
// --------------------------------- edit area ---------------------------------
$zts = false;
// test php version
$test_php_version = [
'8.1',
'8.2',
'8.3',
'8.4',
];
// test os (macos-13, macos-14, ubuntu-latest, windows-latest are available)
$test_os = [
'macos-14',
'ubuntu-latest',
];
// whether enable thread safe
$zts = true;
$no_strip = false;
$upx = true;
// compress with upx
$upx = false;
// prefer downloading pre-built packages to speed up the build process
$prefer_pre_built = true;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'rdkafka',
'Windows' => 'rdkafka',
'Linux', 'Darwin' => 'apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,parallel,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd',
'Windows' => 'amqp,apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,ds,exif,ffi,fileinfo,filter,ftp,gd,iconv,igbinary,libxml,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pdo,pdo_mysql,pdo_sqlite,pdo_sqlsrv,phar,rar,redis,session,shmop,simdjson,simplexml,soap,sockets,sqlite3,sqlsrv,ssh2,swow,sysvshm,tokenizer,xml,xmlreader,xmlwriter,yac,yaml,zip,zlib',
};
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
@@ -58,7 +77,7 @@ function _getCombination(string $type = 'common'): string
}
if (!isset($argv[1])) {
exit("Please use 'extensions', 'cmd' or 'libs' as output type");
exit("Please use 'extensions', 'cmd', 'os', 'php' or 'libs' as output type");
}
$trim_value = "\r\n \t,";
@@ -72,7 +91,43 @@ if (PHP_OS_FAMILY === 'Windows') {
$final_extensions_cmd = $final_extensions;
}
function quote2(string $param): string
{
global $argv;
if (str_starts_with($argv[2], 'windows-')) {
return '"' . $param . '"';
}
return $param;
}
// generate download command
if ($argv[1] === 'download_cmd') {
$down_cmd = 'download ';
$down_cmd .= '--for-extensions=' . quote2($final_extensions) . ' ';
$down_cmd .= '--for-libs=' . quote2($final_libs) . ' ';
$down_cmd .= '--with-php=' . quote2($argv[3]) . ' ';
$down_cmd .= '--ignore-cache-sources=php-src ';
$down_cmd .= '--debug ';
$down_cmd .= '--retry=5 ';
$down_cmd .= '--shallow-clone ';
$down_cmd .= $prefer_pre_built ? '--prefer-pre-built ' : '';
}
// generate build command
if ($argv[1] === 'build_cmd' || $argv[1] === 'build_embed_cmd') {
$build_cmd = 'build ';
$build_cmd .= quote2($final_extensions) . ' ';
$build_cmd .= $zts ? '--enable-zts ' : '';
$build_cmd .= $no_strip ? '--no-strip ' : '';
$build_cmd .= $upx ? '--with-upx-pack ' : '';
$build_cmd .= $final_libs === '' ? '' : ('--with-libs=' . quote2($final_libs) . ' ');
$build_cmd .= str_starts_with($argv[2], 'windows-') ? '' : '--build-fpm ';
$build_cmd .= '--debug ';
}
echo match ($argv[1]) {
'os' => json_encode($test_os),
'php' => json_encode($test_php_version),
'extensions' => $final_extensions,
'libs' => $final_libs,
'libs_cmd' => ($final_libs === '' ? '' : (' --with-libs=' . $final_libs)),
@@ -80,5 +135,32 @@ echo match ($argv[1]) {
'zts' => $zts ? '--enable-zts' : '',
'no_strip' => $no_strip ? '--no-strip' : '',
'upx' => $upx ? '--with-upx-pack' : '',
'prefer_pre_built' => $prefer_pre_built ? '--prefer-pre-built' : '',
'download_cmd' => $down_cmd,
'build_cmd' => $build_cmd,
'build_embed_cmd' => $build_cmd,
default => '',
};
if ($argv[1] === 'download_cmd') {
if (str_starts_with($argv[2], 'windows-')) {
passthru('powershell.exe -file .\bin\spc.ps1 ' . $down_cmd, $retcode);
} else {
passthru('./bin/spc ' . $down_cmd, $retcode);
}
} elseif ($argv[1] === 'build_cmd') {
if (str_starts_with($argv[2], 'windows-')) {
passthru('powershell.exe -file .\bin\spc.ps1 ' . $build_cmd . ' --build-cli --build-micro', $retcode);
} else {
passthru('./bin/spc ' . $build_cmd . ' --build-cli --build-micro', $retcode);
}
} elseif ($argv[1] === 'build_embed_cmd') {
if (str_starts_with($argv[2], 'windows-')) {
// windows does not accept embed SAPI
passthru('powershell.exe -file .\bin\spc.ps1 ' . $build_cmd . ' --build-cli', $retcode);
} else {
passthru('./bin/spc ' . $build_cmd . ' --build-embed', $retcode);
}
}
exit($retcode);