mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-19 13:24:51 +08:00
Refactor all command class exception handling
This commit is contained in:
parent
f68f060be2
commit
333b776e77
@ -4,18 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
use PharIo\FileSystem\File;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\BuildFailureException;
|
||||
use SPC\exception\InterruptException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\LockFile;
|
||||
use SPC\store\SourceManager;
|
||||
use SPC\store\SourcePatcher;
|
||||
use SPC\util\CustomExt;
|
||||
use SPC\util\AttributeMapper;
|
||||
|
||||
abstract class BuilderBase
|
||||
{
|
||||
@ -64,12 +61,11 @@ abstract class BuilderBase
|
||||
match ($status) {
|
||||
LIB_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] setup success, took ' . round(microtime(true) - $starttime, 2) . ' s'),
|
||||
LIB_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'),
|
||||
LIB_STATUS_BUILD_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'),
|
||||
LIB_STATUS_INSTALL_FAILED => logger()->error('lib [' . $lib::NAME . '] install failed'),
|
||||
default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'),
|
||||
};
|
||||
if (in_array($status, [LIB_STATUS_BUILD_FAILED, LIB_STATUS_INSTALL_FAILED])) {
|
||||
throw new RuntimeException('Library [' . $lib::NAME . '] setup failed.');
|
||||
throw new BuildFailureException('Library [' . $lib::NAME . '] setup failed.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -254,9 +250,8 @@ abstract class BuilderBase
|
||||
}
|
||||
$ext->buildShared();
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
} finally {
|
||||
FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line);
|
||||
throw $e;
|
||||
}
|
||||
FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line);
|
||||
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', '# test "[$]$1" = "no" && $1=yes', 'test "[$]$1" = "no" && $1=yes');
|
||||
@ -311,7 +306,7 @@ abstract class BuilderBase
|
||||
return intval($match[1]);
|
||||
}
|
||||
|
||||
throw new RuntimeException('PHP version file format is malformed, please remove it and download again');
|
||||
throw new WrongUsageException('PHP version file format is malformed, please remove "./source/php-src" dir and download/extract again');
|
||||
}
|
||||
|
||||
public function getPHPVersion(bool $exception_on_failure = true): string
|
||||
@ -329,7 +324,7 @@ abstract class BuilderBase
|
||||
if (!$exception_on_failure) {
|
||||
return 'unknown';
|
||||
}
|
||||
throw new RuntimeException('PHP version file format is malformed, please remove it and download again');
|
||||
throw new WrongUsageException('PHP version file format is malformed, please remove it and download again');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,7 +471,7 @@ abstract class BuilderBase
|
||||
foreach ($patches as $patch) {
|
||||
try {
|
||||
if (!file_exists($patch)) {
|
||||
throw new RuntimeException("Additional patch script file {$patch} not found!");
|
||||
throw new WrongUsageException("Additional patch script file {$patch} not found!");
|
||||
}
|
||||
logger()->debug('Running additional patch script: ' . $patch);
|
||||
require $patch;
|
||||
@ -489,11 +484,6 @@ abstract class BuilderBase
|
||||
exit($e->getCode());
|
||||
} catch (\Throwable $e) {
|
||||
logger()->critical('Patch script ' . $patch . ' failed to run.');
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->critical('Please check with --debug option to see more details.');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@ use Laravel\Prompts\Prompt;
|
||||
use Psr\Log\LogLevel;
|
||||
use SPC\ConsoleApplication;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\exception\SPCException;
|
||||
use SPC\util\AttributeMapper;
|
||||
use SPC\util\GlobalEnvManager;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
@ -32,6 +32,7 @@ abstract class BaseCommand extends Command
|
||||
parent::__construct($name);
|
||||
$this->addOption('debug', null, null, 'Enable debug mode');
|
||||
$this->addOption('no-motd', null, null, 'Disable motd');
|
||||
$this->addOption('preserve-log', null, null, 'Preserve log files, do not delete them on initialized');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output): void
|
||||
@ -93,30 +94,20 @@ abstract class BaseCommand extends Command
|
||||
GlobalEnvManager::init();
|
||||
f_putenv('SPC_SKIP_TOOLCHAIN_CHECK=yes');
|
||||
}
|
||||
if ($this->shouldExecute()) {
|
||||
try {
|
||||
// show raw argv list for logger()->debug
|
||||
logger()->debug('argv: ' . implode(' ', $_SERVER['argv']));
|
||||
return $this->handle();
|
||||
} catch (ValidationException|WrongUsageException $e) {
|
||||
$msg = explode("\n", $e->getMessage());
|
||||
foreach ($msg as $v) {
|
||||
logger()->error($v);
|
||||
}
|
||||
return static::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
$msg = explode("\n", $e->getMessage());
|
||||
foreach ($msg as $v) {
|
||||
logger()->error($v);
|
||||
}
|
||||
}
|
||||
return static::FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
// show raw argv list for logger()->debug
|
||||
logger()->debug('argv: ' . implode(' ', $_SERVER['argv']));
|
||||
return $this->handle();
|
||||
} /* @noinspection PhpRedundantCatchClauseInspection */ catch (SPCException $e) {
|
||||
// Handle SPCException and log it
|
||||
ExceptionHandler::handleSPCException($e);
|
||||
return static::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
// Handle any other exceptions
|
||||
ExceptionHandler::handleDefaultException($e);
|
||||
return static::FAILURE;
|
||||
}
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
protected function getOption(string $name): mixed
|
||||
@ -129,11 +120,6 @@ abstract class BaseCommand extends Command
|
||||
return $this->input->getArgument($name);
|
||||
}
|
||||
|
||||
protected function shouldExecute(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function logWithResult(bool $result, string $success_msg, string $fail_msg): int
|
||||
{
|
||||
if ($result) {
|
||||
|
||||
@ -50,32 +50,22 @@ class BuildLibsCommand extends BuildCommand
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 构建对象
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
// 只编译 library 的情况下,标记
|
||||
$builder->setLibsOnly();
|
||||
// 编译和检查库完整
|
||||
$libraries = DependencyUtil::getLibs($libraries);
|
||||
$display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package']));
|
||||
// 构建对象
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
// 只编译 library 的情况下,标记
|
||||
$builder->setLibsOnly();
|
||||
// 编译和检查库完整
|
||||
$libraries = DependencyUtil::getLibs($libraries);
|
||||
$display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package']));
|
||||
|
||||
logger()->info('Building libraries: ' . implode(',', $display_libs));
|
||||
sleep(2);
|
||||
$builder->proveLibs($libraries);
|
||||
$builder->validateLibsAndExts();
|
||||
$builder->setupLibs();
|
||||
logger()->info('Building libraries: ' . implode(',', $display_libs));
|
||||
sleep(2);
|
||||
$builder->proveLibs($libraries);
|
||||
$builder->validateLibsAndExts();
|
||||
$builder->setupLibs();
|
||||
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||
logger()->critical('Please check with --debug option to see more details.');
|
||||
}
|
||||
return static::FAILURE;
|
||||
}
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,8 +5,7 @@ declare(strict_types=1);
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\BuilderProvider;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\exception\SPCException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\SourcePatcher;
|
||||
@ -119,167 +118,154 @@ class BuildPHPCommand extends BuildCommand
|
||||
logger()->warning('Some cases micro.sfx cannot be packed via UPX due to dynamic size bug, be aware!');
|
||||
}
|
||||
}
|
||||
try {
|
||||
// create builder
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
$include_suggest_ext = $this->getOption('with-suggested-exts');
|
||||
$include_suggest_lib = $this->getOption('with-suggested-libs');
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs(array_merge($static_extensions, $shared_extensions), $libraries, $include_suggest_ext, $include_suggest_lib);
|
||||
$display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package']));
|
||||
// create builder
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
$include_suggest_ext = $this->getOption('with-suggested-exts');
|
||||
$include_suggest_lib = $this->getOption('with-suggested-libs');
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs(array_merge($static_extensions, $shared_extensions), $libraries, $include_suggest_ext, $include_suggest_lib);
|
||||
$display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package']));
|
||||
|
||||
// separate static and shared extensions from $extensions
|
||||
// filter rule: including shared extensions if they are in $static_extensions or $shared_extensions
|
||||
$static_extensions = array_filter($extensions, fn ($ext) => !in_array($ext, $shared_extensions) || in_array($ext, $static_extensions));
|
||||
// separate static and shared extensions from $extensions
|
||||
// filter rule: including shared extensions if they are in $static_extensions or $shared_extensions
|
||||
$static_extensions = array_filter($extensions, fn ($ext) => !in_array($ext, $shared_extensions) || in_array($ext, $static_extensions));
|
||||
|
||||
// print info
|
||||
$indent_texts = [
|
||||
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
|
||||
'Build Target' => getenv('SPC_TARGET'),
|
||||
'Build Toolchain' => getenv('SPC_TOOLCHAIN'),
|
||||
'Build SAPI' => $builder->getBuildTypeName($rule),
|
||||
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
|
||||
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
||||
'Libraries (' . count($libraries) . ')' => implode(',', $display_libs),
|
||||
'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes',
|
||||
'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no',
|
||||
];
|
||||
if (!empty($shared_extensions) || ($rule & BUILD_TARGET_EMBED)) {
|
||||
$indent_texts['Build Dev'] = 'yes';
|
||||
}
|
||||
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');
|
||||
}
|
||||
if ($this->input->getOption('disable-opcache-jit')) {
|
||||
$indent_texts['Opcache JIT'] = 'disabled';
|
||||
}
|
||||
if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
|
||||
$indent_texts['UPX Pack'] = 'enabled';
|
||||
}
|
||||
// print info
|
||||
$indent_texts = [
|
||||
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
|
||||
'Build Target' => getenv('SPC_TARGET'),
|
||||
'Build Toolchain' => getenv('SPC_TOOLCHAIN'),
|
||||
'Build SAPI' => $builder->getBuildTypeName($rule),
|
||||
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
|
||||
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
||||
'Libraries (' . count($libraries) . ')' => implode(',', $display_libs),
|
||||
'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes',
|
||||
'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no',
|
||||
];
|
||||
if (!empty($shared_extensions) || ($rule & BUILD_TARGET_EMBED)) {
|
||||
$indent_texts['Build Dev'] = 'yes';
|
||||
}
|
||||
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');
|
||||
}
|
||||
if ($this->input->getOption('disable-opcache-jit')) {
|
||||
$indent_texts['Opcache JIT'] = 'disabled';
|
||||
}
|
||||
if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) {
|
||||
$indent_texts['UPX Pack'] = 'enabled';
|
||||
}
|
||||
|
||||
$ver = $builder->getPHPVersionFromArchive() ?: $builder->getPHPVersion(false);
|
||||
$indent_texts['PHP Version'] = $ver;
|
||||
$ver = $builder->getPHPVersionFromArchive() ?: $builder->getPHPVersion(false);
|
||||
$indent_texts['PHP Version'] = $ver;
|
||||
|
||||
if (!empty($not_included)) {
|
||||
$indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included);
|
||||
}
|
||||
$this->printFormatInfo($this->getDefinedEnvs(), true);
|
||||
$this->printFormatInfo($indent_texts);
|
||||
if (!empty($not_included)) {
|
||||
$indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included);
|
||||
}
|
||||
$this->printFormatInfo($this->getDefinedEnvs(), true);
|
||||
$this->printFormatInfo($indent_texts);
|
||||
// bind extra info to SPCException
|
||||
SPCException::bindBuildPHPExtraInfo($indent_texts);
|
||||
|
||||
logger()->notice('Build will start after 2s ...');
|
||||
sleep(2);
|
||||
logger()->notice('Build will start after 2s ...');
|
||||
sleep(2);
|
||||
|
||||
// compile libraries
|
||||
$builder->proveLibs($libraries);
|
||||
// check extensions
|
||||
$builder->proveExts($static_extensions, $shared_extensions);
|
||||
// validate libs and extensions
|
||||
$builder->validateLibsAndExts();
|
||||
// compile libraries
|
||||
$builder->proveLibs($libraries);
|
||||
// check extensions
|
||||
$builder->proveExts($static_extensions, $shared_extensions);
|
||||
// validate libs and extensions
|
||||
$builder->validateLibsAndExts();
|
||||
|
||||
// check some things before building all the things
|
||||
$builder->checkBeforeBuildPHP($rule);
|
||||
// check some things before building all the things
|
||||
$builder->checkBeforeBuildPHP($rule);
|
||||
|
||||
// clean builds and sources
|
||||
if ($this->input->getOption('with-clean')) {
|
||||
logger()->info('Cleaning source and previous build dir...');
|
||||
FileSystem::removeDir(SOURCE_PATH);
|
||||
FileSystem::removeDir(BUILD_ROOT_PATH);
|
||||
}
|
||||
// clean builds and sources
|
||||
if ($this->input->getOption('with-clean')) {
|
||||
logger()->info('Cleaning source and previous build dir...');
|
||||
FileSystem::removeDir(SOURCE_PATH);
|
||||
FileSystem::removeDir(BUILD_ROOT_PATH);
|
||||
}
|
||||
|
||||
// build or install libraries
|
||||
$builder->setupLibs();
|
||||
// build or install libraries
|
||||
$builder->setupLibs();
|
||||
|
||||
// Process -I option
|
||||
$custom_ini = [];
|
||||
foreach ($this->input->getOption('with-hardcoded-ini') as $value) {
|
||||
[$source_name, $ini_value] = explode('=', $value, 2);
|
||||
$custom_ini[$source_name] = $ini_value;
|
||||
logger()->info('Adding hardcoded INI [' . $source_name . ' = ' . $ini_value . ']');
|
||||
}
|
||||
if (!empty($custom_ini)) {
|
||||
SourcePatcher::patchHardcodedINI($custom_ini);
|
||||
}
|
||||
// Process -I option
|
||||
$custom_ini = [];
|
||||
foreach ($this->input->getOption('with-hardcoded-ini') as $value) {
|
||||
[$source_name, $ini_value] = explode('=', $value, 2);
|
||||
$custom_ini[$source_name] = $ini_value;
|
||||
logger()->info('Adding hardcoded INI [' . $source_name . ' = ' . $ini_value . ']');
|
||||
}
|
||||
if (!empty($custom_ini)) {
|
||||
SourcePatcher::patchHardcodedINI($custom_ini);
|
||||
}
|
||||
|
||||
// add static-php-cli.version to main.c, in order to debug php failure more easily
|
||||
SourcePatcher::patchSPCVersionToPHP($this->getApplication()->getVersion());
|
||||
// add static-php-cli.version to main.c, in order to debug php failure more easily
|
||||
SourcePatcher::patchSPCVersionToPHP($this->getApplication()->getVersion());
|
||||
|
||||
// clean old modules that may conflict with the new php build
|
||||
FileSystem::removeDir(BUILD_MODULES_PATH);
|
||||
// start to build
|
||||
$builder->buildPHP($rule);
|
||||
// clean old modules that may conflict with the new php build
|
||||
FileSystem::removeDir(BUILD_MODULES_PATH);
|
||||
// start to build
|
||||
$builder->buildPHP($rule);
|
||||
|
||||
// build dynamic extensions if needed
|
||||
if (!empty($shared_extensions)) {
|
||||
logger()->info('Building shared extensions ...');
|
||||
$builder->buildSharedExts();
|
||||
}
|
||||
// build dynamic extensions if needed
|
||||
if (!empty($shared_extensions)) {
|
||||
logger()->info('Building shared extensions ...');
|
||||
$builder->buildSharedExts();
|
||||
}
|
||||
|
||||
$builder->testPHP($rule);
|
||||
$builder->testPHP($rule);
|
||||
|
||||
// compile stopwatch :P
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('');
|
||||
logger()->info(' Build complete, used ' . $time . ' s !');
|
||||
logger()->info('');
|
||||
// compile stopwatch :P
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
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;
|
||||
$cwd = getcwd();
|
||||
$fixed = '';
|
||||
if (!empty(getenv('SPC_FIX_DEPLOY_ROOT'))) {
|
||||
str_replace($cwd, '', $build_root_path);
|
||||
$build_root_path = getenv('SPC_FIX_DEPLOY_ROOT') . '/' . basename($build_root_path);
|
||||
$fixed = ' (host system)';
|
||||
}
|
||||
if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
|
||||
$win_suffix = PHP_OS_FAMILY === 'Windows' ? '.exe' : '';
|
||||
$path = FileSystem::convertPath("{$build_root_path}/bin/php{$win_suffix}");
|
||||
logger()->info("Static php binary path{$fixed}: {$path}");
|
||||
}
|
||||
if (($rule & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
|
||||
$path = FileSystem::convertPath("{$build_root_path}/bin/micro.sfx");
|
||||
logger()->info("phpmicro binary path{$fixed}: {$path}");
|
||||
}
|
||||
if (($rule & BUILD_TARGET_FPM) === BUILD_TARGET_FPM && PHP_OS_FAMILY !== 'Windows') {
|
||||
$path = FileSystem::convertPath("{$build_root_path}/bin/php-fpm");
|
||||
logger()->info("Static php-fpm binary path{$fixed}: {$path}");
|
||||
}
|
||||
if (!empty($shared_extensions)) {
|
||||
foreach ($shared_extensions as $ext) {
|
||||
$path = FileSystem::convertPath("{$build_root_path}/modules/{$ext}.so");
|
||||
if (file_exists(BUILD_MODULES_PATH . "/{$ext}.so")) {
|
||||
logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}");
|
||||
} else {
|
||||
logger()->warning("Shared extension [{$ext}] not found, please check!");
|
||||
}
|
||||
// ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ----------
|
||||
$build_root_path = BUILD_ROOT_PATH;
|
||||
$cwd = getcwd();
|
||||
$fixed = '';
|
||||
if (!empty(getenv('SPC_FIX_DEPLOY_ROOT'))) {
|
||||
str_replace($cwd, '', $build_root_path);
|
||||
$build_root_path = getenv('SPC_FIX_DEPLOY_ROOT') . '/' . basename($build_root_path);
|
||||
$fixed = ' (host system)';
|
||||
}
|
||||
if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
|
||||
$win_suffix = PHP_OS_FAMILY === 'Windows' ? '.exe' : '';
|
||||
$path = FileSystem::convertPath("{$build_root_path}/bin/php{$win_suffix}");
|
||||
logger()->info("Static php binary path{$fixed}: {$path}");
|
||||
}
|
||||
if (($rule & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
|
||||
$path = FileSystem::convertPath("{$build_root_path}/bin/micro.sfx");
|
||||
logger()->info("phpmicro binary path{$fixed}: {$path}");
|
||||
}
|
||||
if (($rule & BUILD_TARGET_FPM) === BUILD_TARGET_FPM && PHP_OS_FAMILY !== 'Windows') {
|
||||
$path = FileSystem::convertPath("{$build_root_path}/bin/php-fpm");
|
||||
logger()->info("Static php-fpm binary path{$fixed}: {$path}");
|
||||
}
|
||||
if (!empty($shared_extensions)) {
|
||||
foreach ($shared_extensions as $ext) {
|
||||
$path = FileSystem::convertPath("{$build_root_path}/modules/{$ext}.so");
|
||||
if (file_exists(BUILD_MODULES_PATH . "/{$ext}.so")) {
|
||||
logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}");
|
||||
} else {
|
||||
logger()->warning("Shared extension [{$ext}] not found, please check!");
|
||||
}
|
||||
}
|
||||
|
||||
// export metadata
|
||||
file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
// export licenses
|
||||
$dumper = new LicenseDumper();
|
||||
$dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license');
|
||||
$path = FileSystem::convertPath("{$build_root_path}/license/");
|
||||
logger()->info("License path{$fixed}: {$path}");
|
||||
return static::SUCCESS;
|
||||
} catch (WrongUsageException $e) {
|
||||
// WrongUsageException is not an exception, it's a user error, so we just print the error message
|
||||
logger()->critical($e->getMessage());
|
||||
logger()->error($e->getTraceAsString());
|
||||
return static::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||
logger()->critical('Please check with --debug option to see more details.');
|
||||
}
|
||||
return static::FAILURE;
|
||||
}
|
||||
|
||||
// export metadata
|
||||
file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
// export licenses
|
||||
$dumper = new LicenseDumper();
|
||||
$dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license');
|
||||
$path = FileSystem::convertPath("{$build_root_path}/license/");
|
||||
logger()->info("License path{$fixed}: {$path}");
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -4,9 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\LockFile;
|
||||
@ -36,57 +33,49 @@ class DeleteDownloadCommand extends BaseCommand
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
// get source list that will be downloaded
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
if (empty($sources)) {
|
||||
logger()->notice('Removing downloads/ directory ...');
|
||||
FileSystem::removeDir(DOWNLOAD_PATH);
|
||||
logger()->info('Removed downloads/ dir!');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
$chosen_sources = $sources;
|
||||
|
||||
$deleted_sources = [];
|
||||
foreach ($chosen_sources as $source) {
|
||||
$source = trim($source);
|
||||
if (LockFile::get($source) && !$this->getOption('pre-built-only')) {
|
||||
$deleted_sources[] = $source;
|
||||
}
|
||||
if (LockFile::get(Downloader::getPreBuiltLockName($source)) && !$this->getOption('source-only')) {
|
||||
$deleted_sources[] = Downloader::getPreBuiltLockName($source);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($deleted_sources as $lock_name) {
|
||||
$lock = LockFile::get($lock_name);
|
||||
// remove download file/dir if exists
|
||||
if ($lock['source_type'] === SPC_SOURCE_ARCHIVE) {
|
||||
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['filename']))) {
|
||||
logger()->info('Deleting file ' . $path);
|
||||
unlink($path);
|
||||
} else {
|
||||
logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file.");
|
||||
}
|
||||
} else {
|
||||
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['dirname']))) {
|
||||
logger()->info('Deleting dir ' . $path);
|
||||
FileSystem::removeDir($path);
|
||||
} else {
|
||||
logger()->warning("Source/Package [{$lock_name}] directory not found, skip deleting dir.");
|
||||
}
|
||||
}
|
||||
// remove locked sources
|
||||
LockFile::put($lock_name, null);
|
||||
}
|
||||
logger()->info('Delete success!');
|
||||
// get source list that will be downloaded
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
if (empty($sources)) {
|
||||
logger()->notice('Removing downloads/ directory ...');
|
||||
FileSystem::removeDir(DOWNLOAD_PATH);
|
||||
logger()->info('Removed downloads/ dir!');
|
||||
return static::SUCCESS;
|
||||
} catch (DownloaderException $e) {
|
||||
logger()->error($e->getMessage());
|
||||
return static::FAILURE;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
return static::FAILURE;
|
||||
}
|
||||
$chosen_sources = $sources;
|
||||
|
||||
$deleted_sources = [];
|
||||
foreach ($chosen_sources as $source) {
|
||||
$source = trim($source);
|
||||
if (LockFile::get($source) && !$this->getOption('pre-built-only')) {
|
||||
$deleted_sources[] = $source;
|
||||
}
|
||||
if (LockFile::get(Downloader::getPreBuiltLockName($source)) && !$this->getOption('source-only')) {
|
||||
$deleted_sources[] = Downloader::getPreBuiltLockName($source);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($deleted_sources as $lock_name) {
|
||||
$lock = LockFile::get($lock_name);
|
||||
// remove download file/dir if exists
|
||||
if ($lock['source_type'] === SPC_SOURCE_ARCHIVE) {
|
||||
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['filename']))) {
|
||||
logger()->info('Deleting file ' . $path);
|
||||
unlink($path);
|
||||
} else {
|
||||
logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file.");
|
||||
}
|
||||
} else {
|
||||
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['dirname']))) {
|
||||
logger()->info('Deleting dir ' . $path);
|
||||
FileSystem::removeDir($path);
|
||||
} else {
|
||||
logger()->warning("Source/Package [{$lock_name}] directory not found, skip deleting dir.");
|
||||
}
|
||||
}
|
||||
// remove locked sources
|
||||
LockFile::put($lock_name, null);
|
||||
}
|
||||
logger()->info('Delete success!');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,7 @@ namespace SPC\command;
|
||||
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\exception\SPCException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\LockFile;
|
||||
@ -87,177 +85,169 @@ class DownloadCommand extends BaseCommand
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
if ($this->getOption('clean')) {
|
||||
return $this->_clean();
|
||||
}
|
||||
|
||||
// --from-zip
|
||||
if ($path = $this->getOption('from-zip')) {
|
||||
return $this->downloadFromZip($path);
|
||||
}
|
||||
|
||||
// Define PHP major version
|
||||
$ver = $this->php_major_ver = $this->getOption('with-php');
|
||||
define('SPC_BUILD_PHP_VERSION', $ver);
|
||||
if ($ver !== 'git' && !preg_match('/^\d+\.\d+$/', $ver)) {
|
||||
// If not git, we need to check the version format
|
||||
if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $ver)) {
|
||||
logger()->error("bad version arg: {$ver}, x.y or x.y.z required!");
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// retry
|
||||
$retry = intval($this->getOption('retry'));
|
||||
f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry);
|
||||
|
||||
// Use shallow-clone can reduce git resource download
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// To read config
|
||||
Config::getSource('openssl');
|
||||
|
||||
// use openssl 1.1
|
||||
if ($this->getOption('with-openssl11')) {
|
||||
logger()->debug('Using openssl 1.1');
|
||||
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
||||
}
|
||||
|
||||
$chosen_sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
|
||||
$sss = $this->getOption('ignore-cache-sources');
|
||||
if ($sss === false) {
|
||||
// false is no-any-ignores, that is, default.
|
||||
$force_all = false;
|
||||
$force_list = [];
|
||||
} elseif ($sss === null) {
|
||||
// null means all sources will be ignored, equals to --force-all (but we don't want to add too many options)
|
||||
$force_all = true;
|
||||
$force_list = [];
|
||||
} else {
|
||||
// ignore some sources
|
||||
$force_all = false;
|
||||
$force_list = array_map('trim', array_filter(explode(',', $this->getOption('ignore-cache-sources'))));
|
||||
}
|
||||
|
||||
if ($this->getOption('all')) {
|
||||
logger()->notice('Downloading with --all option will take more times to download, we recommend you to download with --for-extensions option !');
|
||||
}
|
||||
|
||||
// Process -U options
|
||||
$custom_urls = [];
|
||||
foreach ($this->input->getOption('custom-url') as $value) {
|
||||
[$source_name, $url] = explode(':', $value, 2);
|
||||
$custom_urls[$source_name] = $url;
|
||||
}
|
||||
// Process -G options
|
||||
$custom_gits = [];
|
||||
foreach ($this->input->getOption('custom-git') as $value) {
|
||||
[$source_name, $branch, $url] = explode(':', $value, 3);
|
||||
$custom_gits[$source_name] = [$branch, $url];
|
||||
}
|
||||
|
||||
// If passing --prefer-pre-built option, we need to load pre-built library list from pre-built.json targeted releases
|
||||
if ($this->getOption('prefer-pre-built')) {
|
||||
$repo = Config::getPreBuilt('repo');
|
||||
$pre_built_libs = Downloader::getLatestGithubRelease($repo, [
|
||||
'repo' => $repo,
|
||||
'prefer-stable' => Config::getPreBuilt('prefer-stable'),
|
||||
], false);
|
||||
} else {
|
||||
$pre_built_libs = [];
|
||||
}
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$cnt = count($chosen_sources);
|
||||
$ni = 0;
|
||||
foreach ($chosen_sources as $source) {
|
||||
++$ni;
|
||||
if (isset($custom_urls[$source])) {
|
||||
$config = Config::getSource($source);
|
||||
$new_config = [
|
||||
'type' => 'url',
|
||||
'url' => $custom_urls[$source],
|
||||
];
|
||||
if (isset($config['path'])) {
|
||||
$new_config['path'] = $config['path'];
|
||||
}
|
||||
if (isset($config['filename'])) {
|
||||
$new_config['filename'] = $config['filename'];
|
||||
}
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom url: {$new_config['url']}");
|
||||
Downloader::downloadSource($source, $new_config, true);
|
||||
} elseif (isset($custom_gits[$source])) {
|
||||
$config = Config::getSource($source);
|
||||
$new_config = [
|
||||
'type' => 'git',
|
||||
'rev' => $custom_gits[$source][0],
|
||||
'url' => $custom_gits[$source][1],
|
||||
];
|
||||
if (isset($config['path'])) {
|
||||
$new_config['path'] = $config['path'];
|
||||
}
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom git: {$new_config['url']}");
|
||||
Downloader::downloadSource($source, $new_config, true);
|
||||
} else {
|
||||
$config = Config::getSource($source);
|
||||
// Prefer pre-built, we need to search pre-built library
|
||||
if ($this->getOption('prefer-pre-built') && ($config['provide-pre-built'] ?? false) === true) {
|
||||
// We need to replace pattern
|
||||
$replace = [
|
||||
'{name}' => $source,
|
||||
'{arch}' => arch2gnu(php_uname('m')),
|
||||
'{os}' => strtolower(PHP_OS_FAMILY),
|
||||
'{libc}' => SPCTarget::getLibc() ?? 'default',
|
||||
'{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
|
||||
];
|
||||
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
|
||||
// find filename in asset list
|
||||
if (($url = $this->findPreBuilt($pre_built_libs, $find)) !== null) {
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading pre-built content {$source}");
|
||||
Downloader::downloadSource($source, ['type' => 'url', 'url' => $url], $force_all || in_array($source, $force_list), SPC_DOWNLOAD_PRE_BUILT);
|
||||
continue;
|
||||
}
|
||||
logger()->warning("Pre-built content not found for {$source}, fallback to source download");
|
||||
}
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading source {$source}");
|
||||
try {
|
||||
Downloader::downloadSource($source, $config, $force_all || in_array($source, $force_list));
|
||||
} /* @noinspection PhpRedundantCatchClauseInspection */ catch (DownloaderException|RuntimeException $e) {
|
||||
// if `--no-alt` option is set, we will not download alternative sources
|
||||
if ($this->getOption('no-alt')) {
|
||||
throw $e;
|
||||
}
|
||||
// if download failed, we will try to download alternative sources
|
||||
logger()->warning("Download failed: {$e->getMessage()}");
|
||||
$alt_sources = Config::getSource($source)['alt'] ?? null;
|
||||
if ($alt_sources === null) {
|
||||
logger()->warning("No alternative sources found for {$source}, using default alternative source");
|
||||
$alt_config = array_merge($config, Downloader::getDefaultAlternativeSource($source));
|
||||
} elseif ($alt_sources === false) {
|
||||
throw new DownloaderException("No alternative sources found for {$source}, skipping alternative download");
|
||||
} else {
|
||||
logger()->notice("Trying to download alternative sources for {$source}");
|
||||
$alt_config = array_merge($config, $alt_sources);
|
||||
}
|
||||
Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list));
|
||||
}
|
||||
}
|
||||
}
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Download complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
} catch (DownloaderException $e) {
|
||||
logger()->error($e->getMessage());
|
||||
return static::FAILURE;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
return static::FAILURE;
|
||||
if ($this->getOption('clean')) {
|
||||
return $this->_clean();
|
||||
}
|
||||
|
||||
// --from-zip
|
||||
if ($path = $this->getOption('from-zip')) {
|
||||
return $this->downloadFromZip($path);
|
||||
}
|
||||
|
||||
// Define PHP major version
|
||||
$ver = $this->php_major_ver = $this->getOption('with-php');
|
||||
define('SPC_BUILD_PHP_VERSION', $ver);
|
||||
if ($ver !== 'git' && !preg_match('/^\d+\.\d+$/', $ver)) {
|
||||
// If not git, we need to check the version format
|
||||
if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $ver)) {
|
||||
logger()->error("bad version arg: {$ver}, x.y or x.y.z required!");
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// retry
|
||||
$retry = intval($this->getOption('retry'));
|
||||
f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry);
|
||||
|
||||
// Use shallow-clone can reduce git resource download
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// To read config
|
||||
Config::getSource('openssl');
|
||||
|
||||
// use openssl 1.1
|
||||
if ($this->getOption('with-openssl11')) {
|
||||
logger()->debug('Using openssl 1.1');
|
||||
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
||||
}
|
||||
|
||||
$chosen_sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
|
||||
$sss = $this->getOption('ignore-cache-sources');
|
||||
if ($sss === false) {
|
||||
// false is no-any-ignores, that is, default.
|
||||
$force_all = false;
|
||||
$force_list = [];
|
||||
} elseif ($sss === null) {
|
||||
// null means all sources will be ignored, equals to --force-all (but we don't want to add too many options)
|
||||
$force_all = true;
|
||||
$force_list = [];
|
||||
} else {
|
||||
// ignore some sources
|
||||
$force_all = false;
|
||||
$force_list = array_map('trim', array_filter(explode(',', $this->getOption('ignore-cache-sources'))));
|
||||
}
|
||||
|
||||
if ($this->getOption('all')) {
|
||||
logger()->notice('Downloading with --all option will take more times to download, we recommend you to download with --for-extensions option !');
|
||||
}
|
||||
|
||||
// Process -U options
|
||||
$custom_urls = [];
|
||||
foreach ($this->input->getOption('custom-url') as $value) {
|
||||
[$source_name, $url] = explode(':', $value, 2);
|
||||
$custom_urls[$source_name] = $url;
|
||||
}
|
||||
// Process -G options
|
||||
$custom_gits = [];
|
||||
foreach ($this->input->getOption('custom-git') as $value) {
|
||||
[$source_name, $branch, $url] = explode(':', $value, 3);
|
||||
$custom_gits[$source_name] = [$branch, $url];
|
||||
}
|
||||
|
||||
// If passing --prefer-pre-built option, we need to load pre-built library list from pre-built.json targeted releases
|
||||
if ($this->getOption('prefer-pre-built')) {
|
||||
$repo = Config::getPreBuilt('repo');
|
||||
$pre_built_libs = Downloader::getLatestGithubRelease($repo, [
|
||||
'repo' => $repo,
|
||||
'prefer-stable' => Config::getPreBuilt('prefer-stable'),
|
||||
], false);
|
||||
} else {
|
||||
$pre_built_libs = [];
|
||||
}
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$cnt = count($chosen_sources);
|
||||
$ni = 0;
|
||||
foreach ($chosen_sources as $source) {
|
||||
++$ni;
|
||||
if (isset($custom_urls[$source])) {
|
||||
$config = Config::getSource($source);
|
||||
$new_config = [
|
||||
'type' => 'url',
|
||||
'url' => $custom_urls[$source],
|
||||
];
|
||||
if (isset($config['path'])) {
|
||||
$new_config['path'] = $config['path'];
|
||||
}
|
||||
if (isset($config['filename'])) {
|
||||
$new_config['filename'] = $config['filename'];
|
||||
}
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom url: {$new_config['url']}");
|
||||
Downloader::downloadSource($source, $new_config, true);
|
||||
} elseif (isset($custom_gits[$source])) {
|
||||
$config = Config::getSource($source);
|
||||
$new_config = [
|
||||
'type' => 'git',
|
||||
'rev' => $custom_gits[$source][0],
|
||||
'url' => $custom_gits[$source][1],
|
||||
];
|
||||
if (isset($config['path'])) {
|
||||
$new_config['path'] = $config['path'];
|
||||
}
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom git: {$new_config['url']}");
|
||||
Downloader::downloadSource($source, $new_config, true);
|
||||
} else {
|
||||
$config = Config::getSource($source);
|
||||
// Prefer pre-built, we need to search pre-built library
|
||||
if ($this->getOption('prefer-pre-built') && ($config['provide-pre-built'] ?? false) === true) {
|
||||
// We need to replace pattern
|
||||
$replace = [
|
||||
'{name}' => $source,
|
||||
'{arch}' => arch2gnu(php_uname('m')),
|
||||
'{os}' => strtolower(PHP_OS_FAMILY),
|
||||
'{libc}' => SPCTarget::getLibc() ?? 'default',
|
||||
'{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
|
||||
];
|
||||
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
|
||||
// find filename in asset list
|
||||
if (($url = $this->findPreBuilt($pre_built_libs, $find)) !== null) {
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading pre-built content {$source}");
|
||||
Downloader::downloadSource($source, ['type' => 'url', 'url' => $url], $force_all || in_array($source, $force_list), SPC_DOWNLOAD_PRE_BUILT);
|
||||
continue;
|
||||
}
|
||||
logger()->warning("Pre-built content not found for {$source}, fallback to source download");
|
||||
}
|
||||
logger()->info("[{$ni}/{$cnt}] Downloading source {$source}");
|
||||
try {
|
||||
Downloader::downloadSource($source, $config, $force_all || in_array($source, $force_list));
|
||||
} catch (SPCException $e) {
|
||||
// if `--no-alt` option is set, we will not download alternative sources
|
||||
if ($this->getOption('no-alt')) {
|
||||
throw $e;
|
||||
}
|
||||
// if download failed, we will try to download alternative sources
|
||||
logger()->warning("Download failed: {$e->getMessage()}");
|
||||
$alt_sources = Config::getSource($source)['alt'] ?? null;
|
||||
if ($alt_sources === null) {
|
||||
logger()->warning("No alternative sources found for {$source}, using default alternative source");
|
||||
$alt_config = array_merge($config, Downloader::getDefaultAlternativeSource($source));
|
||||
} elseif ($alt_sources === false) {
|
||||
throw new DownloaderException("No alternative sources found for {$source}, skipping alternative download");
|
||||
} else {
|
||||
logger()->notice("Trying to download alternative sources for {$source}");
|
||||
$alt_config = array_merge($config, $alt_sources);
|
||||
}
|
||||
Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list));
|
||||
}
|
||||
}
|
||||
}
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Download complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
private function downloadFromZip(string $path): int
|
||||
@ -276,28 +266,24 @@ class DownloadCommand extends BaseCommand
|
||||
}
|
||||
// unzip command check
|
||||
if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) {
|
||||
logger()->critical('Missing unzip command, you need to install it first !');
|
||||
logger()->critical('You can use "bin/spc doctor" command to check and install required tools');
|
||||
$this->output->writeln('Missing unzip command, you need to install it first !');
|
||||
$this->output->writeln('You can use "bin/spc doctor" command to check and install required tools');
|
||||
return static::FAILURE;
|
||||
}
|
||||
// create downloads
|
||||
try {
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
$abs_path = realpath($path);
|
||||
f_passthru('mkdir ' . DOWNLOAD_PATH . ' && cd ' . DOWNLOAD_PATH . ' && unzip ' . escapeshellarg($abs_path));
|
||||
} else {
|
||||
// Windows TODO
|
||||
throw new WrongUsageException('Windows currently does not support --from-zip !');
|
||||
}
|
||||
|
||||
if (!file_exists(LockFile::LOCK_FILE)) {
|
||||
throw new RuntimeException('.lock.json not exist in "downloads/"');
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
logger()->critical('Extract failed: ' . $e->getMessage());
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
// Windows TODO
|
||||
$this->output->writeln('<error>Windows currently does not support --from-zip !</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
logger()->info('Extract success');
|
||||
$abs_path = realpath($path);
|
||||
f_passthru('mkdir ' . DOWNLOAD_PATH . ' && cd ' . DOWNLOAD_PATH . ' && unzip ' . escapeshellarg($abs_path));
|
||||
|
||||
if (!file_exists(LockFile::LOCK_FILE)) {
|
||||
$this->output->writeln('<error>.lock.json not exist in "downloads/", please run "bin/spc download" first !</error>');
|
||||
return static::FAILURE;
|
||||
}
|
||||
$this->output->writeln('<info>Extract success</info>');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -5,9 +5,6 @@ declare(strict_types=1);
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\PackageManager;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
@ -30,66 +27,58 @@ class InstallPkgCommand extends BaseCommand
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
// Use shallow-clone can reduce git resource download
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// Process -U options
|
||||
$custom_urls = [];
|
||||
foreach ($this->input->getOption('custom-url') as $value) {
|
||||
[$pkg_name, $url] = explode(':', $value, 2);
|
||||
$custom_urls[$pkg_name] = $url;
|
||||
}
|
||||
|
||||
$chosen_pkgs = array_map('trim', array_filter(explode(',', $this->getArgument('packages'))));
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$ni = 0;
|
||||
$cnt = count($chosen_pkgs);
|
||||
|
||||
foreach ($chosen_pkgs as $pkg) {
|
||||
++$ni;
|
||||
if (isset($custom_urls[$pkg])) {
|
||||
$config = Config::getPkg($pkg);
|
||||
$new_config = [
|
||||
'type' => 'url',
|
||||
'url' => $custom_urls[$pkg],
|
||||
];
|
||||
if (isset($config['extract'])) {
|
||||
$new_config['extract'] = $config['extract'];
|
||||
}
|
||||
if (isset($config['filename'])) {
|
||||
$new_config['filename'] = $config['filename'];
|
||||
}
|
||||
logger()->info("Installing source {$pkg} from custom url [{$ni}/{$cnt}]");
|
||||
PackageManager::installPackage(
|
||||
$pkg,
|
||||
$new_config,
|
||||
allow_alt: false,
|
||||
extract: !$this->getOption('skip-extract')
|
||||
);
|
||||
} else {
|
||||
logger()->info("Fetching package {$pkg} [{$ni}/{$cnt}]");
|
||||
PackageManager::installPackage(
|
||||
$pkg,
|
||||
Config::getPkg($pkg),
|
||||
allow_alt: !$this->getOption('no-alt'),
|
||||
extract: !$this->getOption('skip-extract')
|
||||
);
|
||||
}
|
||||
}
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Install packages complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
} catch (DownloaderException $e) {
|
||||
logger()->error($e->getMessage());
|
||||
return static::FAILURE;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
return static::FAILURE;
|
||||
// Use shallow-clone can reduce git resource download
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// Process -U options
|
||||
$custom_urls = [];
|
||||
foreach ($this->input->getOption('custom-url') as $value) {
|
||||
[$pkg_name, $url] = explode(':', $value, 2);
|
||||
$custom_urls[$pkg_name] = $url;
|
||||
}
|
||||
|
||||
$chosen_pkgs = array_map('trim', array_filter(explode(',', $this->getArgument('packages'))));
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$ni = 0;
|
||||
$cnt = count($chosen_pkgs);
|
||||
|
||||
foreach ($chosen_pkgs as $pkg) {
|
||||
++$ni;
|
||||
if (isset($custom_urls[$pkg])) {
|
||||
$config = Config::getPkg($pkg);
|
||||
$new_config = [
|
||||
'type' => 'url',
|
||||
'url' => $custom_urls[$pkg],
|
||||
];
|
||||
if (isset($config['extract'])) {
|
||||
$new_config['extract'] = $config['extract'];
|
||||
}
|
||||
if (isset($config['filename'])) {
|
||||
$new_config['filename'] = $config['filename'];
|
||||
}
|
||||
logger()->info("Installing source {$pkg} from custom url [{$ni}/{$cnt}]");
|
||||
PackageManager::installPackage(
|
||||
$pkg,
|
||||
$new_config,
|
||||
allow_alt: false,
|
||||
extract: !$this->getOption('skip-extract')
|
||||
);
|
||||
} else {
|
||||
logger()->info("Fetching package {$pkg} [{$ni}/{$cnt}]");
|
||||
PackageManager::installPackage(
|
||||
$pkg,
|
||||
Config::getPkg($pkg),
|
||||
allow_alt: !$this->getOption('no-alt'),
|
||||
extract: !$this->getOption('skip-extract')
|
||||
);
|
||||
}
|
||||
}
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Install packages complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,7 @@ 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\exception\ValidationException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\LockFile;
|
||||
@ -30,120 +27,110 @@ class PackLibCommand extends BuildCommand
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
$lib_name = $this->getArgument('library');
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
$builder->setLibsOnly();
|
||||
$libraries = DependencyUtil::getLibs([$lib_name]);
|
||||
logger()->info('Building libraries: ' . implode(',', $libraries));
|
||||
sleep(2);
|
||||
$lib_name = $this->getArgument('library');
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
$builder->setLibsOnly();
|
||||
$libraries = DependencyUtil::getLibs([$lib_name]);
|
||||
logger()->info('Building libraries: ' . implode(',', $libraries));
|
||||
sleep(2);
|
||||
|
||||
FileSystem::createDir(WORKING_DIR . '/dist');
|
||||
FileSystem::createDir(WORKING_DIR . '/dist');
|
||||
|
||||
$builder->proveLibs($libraries);
|
||||
$builder->validateLibsAndExts();
|
||||
$builder->proveLibs($libraries);
|
||||
$builder->validateLibsAndExts();
|
||||
|
||||
// before pack, check if the dependency tree contains lib-suggests
|
||||
foreach ($libraries as $lib) {
|
||||
if (Config::getLib($lib, 'lib-suggests', []) !== []) {
|
||||
logger()->critical("The library {$lib} has lib-suggests, packing [{$lib_name}] is not safe, abort !");
|
||||
// before pack, check if the dependency tree contains lib-suggests
|
||||
foreach ($libraries as $lib) {
|
||||
if (Config::getLib($lib, 'lib-suggests', []) !== []) {
|
||||
logger()->critical("The library {$lib} has lib-suggests, packing [{$lib_name}] is not safe, abort !");
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
$origin_files = [];
|
||||
// get pack placehoder defines
|
||||
$placehoder = get_pack_replace();
|
||||
|
||||
foreach ($builder->getLibs() as $lib) {
|
||||
if ($lib->getName() !== $lib_name) {
|
||||
// other dependencies: install or build, both ok
|
||||
$lib->setup();
|
||||
} else {
|
||||
// Get lock info
|
||||
$source = Config::getLib($lib->getName(), 'source');
|
||||
if (($lock = LockFile::get($source)) === null || ($lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT)) {
|
||||
logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built.");
|
||||
return static::FAILURE;
|
||||
}
|
||||
}
|
||||
// Before build: load buildroot/ directory
|
||||
$before_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true);
|
||||
// build
|
||||
$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);
|
||||
|
||||
$origin_files = [];
|
||||
// get pack placehoder defines
|
||||
$placehoder = get_pack_replace();
|
||||
|
||||
foreach ($builder->getLibs() as $lib) {
|
||||
if ($lib->getName() !== $lib_name) {
|
||||
// other dependencies: install or build, both ok
|
||||
$lib->setup();
|
||||
} else {
|
||||
// Get lock info
|
||||
$source = Config::getLib($lib->getName(), 'source');
|
||||
if (($lock = LockFile::get($source)) === null || ($lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT)) {
|
||||
logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built.");
|
||||
return static::FAILURE;
|
||||
// patch pkg-config and la files with absolute path
|
||||
foreach ($increase_files as $file) {
|
||||
if (str_ends_with($file, '.pc') || str_ends_with($file, '.la')) {
|
||||
$content = FileSystem::readFile(BUILD_ROOT_PATH . '/' . $file);
|
||||
$origin_files[$file] = $content;
|
||||
// replace relative paths with absolute paths
|
||||
$content = str_replace(
|
||||
array_keys($placehoder),
|
||||
array_values($placehoder),
|
||||
$content
|
||||
);
|
||||
FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content);
|
||||
}
|
||||
// Before build: load buildroot/ directory
|
||||
$before_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true);
|
||||
// build
|
||||
$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);
|
||||
|
||||
// patch pkg-config and la files with absolute path
|
||||
foreach ($increase_files as $file) {
|
||||
if (str_ends_with($file, '.pc') || str_ends_with($file, '.la')) {
|
||||
$content = FileSystem::readFile(BUILD_ROOT_PATH . '/' . $file);
|
||||
$origin_files[$file] = $content;
|
||||
// replace relative paths with absolute paths
|
||||
$content = str_replace(
|
||||
array_keys($placehoder),
|
||||
array_values($placehoder),
|
||||
$content
|
||||
);
|
||||
FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content);
|
||||
}
|
||||
}
|
||||
|
||||
// add .spc-extract-placeholder.json in BUILD_ROOT_PATH
|
||||
$placeholder_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json';
|
||||
file_put_contents($placeholder_file, json_encode(array_keys($origin_files), JSON_PRETTY_PRINT));
|
||||
$increase_files[] = '.spc-extract-placeholder.json';
|
||||
|
||||
// every file mapped with BUILD_ROOT_PATH
|
||||
// get BUILD_ROOT_PATH last dir part
|
||||
$buildroot_part = basename(BUILD_ROOT_PATH);
|
||||
$increase_files = array_map(fn ($file) => $buildroot_part . '/' . $file, $increase_files);
|
||||
// write list to packlib_files.txt
|
||||
FileSystem::writeFile(WORKING_DIR . '/packlib_files.txt', implode("\n", $increase_files));
|
||||
// pack
|
||||
$filename = Config::getPreBuilt('match-pattern');
|
||||
$replace = [
|
||||
'{name}' => $lib->getName(),
|
||||
'{arch}' => arch2gnu(php_uname('m')),
|
||||
'{os}' => strtolower(PHP_OS_FAMILY),
|
||||
'{libc}' => SPCTarget::getLibc() ?? 'default',
|
||||
'{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
|
||||
];
|
||||
// detect suffix, for proper tar option
|
||||
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));
|
||||
$filename = str_replace(array_keys($replace), array_values($replace), $filename);
|
||||
$filename = WORKING_DIR . '/dist/' . $filename;
|
||||
f_passthru("tar {$tar_option} {$filename} -T " . WORKING_DIR . '/packlib_files.txt');
|
||||
logger()->info('Pack library ' . $lib->getName() . ' to ' . $filename . ' complete.');
|
||||
|
||||
// remove temp files
|
||||
unlink($placeholder_file);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($origin_files as $file => $content) {
|
||||
// restore original files
|
||||
if (file_exists(BUILD_ROOT_PATH . '/' . $file)) {
|
||||
FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content);
|
||||
}
|
||||
}
|
||||
// add .spc-extract-placeholder.json in BUILD_ROOT_PATH
|
||||
$placeholder_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json';
|
||||
file_put_contents($placeholder_file, json_encode(array_keys($origin_files), JSON_PRETTY_PRINT));
|
||||
$increase_files[] = '.spc-extract-placeholder.json';
|
||||
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||
logger()->critical('Please check with --debug option to see more details.');
|
||||
// every file mapped with BUILD_ROOT_PATH
|
||||
// get BUILD_ROOT_PATH last dir part
|
||||
$buildroot_part = basename(BUILD_ROOT_PATH);
|
||||
$increase_files = array_map(fn ($file) => $buildroot_part . '/' . $file, $increase_files);
|
||||
// write list to packlib_files.txt
|
||||
FileSystem::writeFile(WORKING_DIR . '/packlib_files.txt', implode("\n", $increase_files));
|
||||
// pack
|
||||
$filename = Config::getPreBuilt('match-pattern');
|
||||
$replace = [
|
||||
'{name}' => $lib->getName(),
|
||||
'{arch}' => arch2gnu(php_uname('m')),
|
||||
'{os}' => strtolower(PHP_OS_FAMILY),
|
||||
'{libc}' => SPCTarget::getLibc() ?? 'default',
|
||||
'{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
|
||||
];
|
||||
// detect suffix, for proper tar option
|
||||
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));
|
||||
$filename = str_replace(array_keys($replace), array_values($replace), $filename);
|
||||
$filename = WORKING_DIR . '/dist/' . $filename;
|
||||
f_passthru("tar {$tar_option} {$filename} -T " . WORKING_DIR . '/packlib_files.txt');
|
||||
logger()->info('Pack library ' . $lib->getName() . ' to ' . $filename . ' complete.');
|
||||
|
||||
// remove temp files
|
||||
unlink($placeholder_file);
|
||||
}
|
||||
return static::FAILURE;
|
||||
}
|
||||
|
||||
foreach ($origin_files as $file => $content) {
|
||||
// restore original files
|
||||
if (file_exists(BUILD_ROOT_PATH . '/' . $file)) {
|
||||
FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content);
|
||||
}
|
||||
}
|
||||
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||
return static::SUCCESS;
|
||||
}
|
||||
|
||||
private function sanityCheckLib(LibraryBase $lib): void
|
||||
@ -152,7 +139,10 @@ class PackLibCommand extends BuildCommand
|
||||
// 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);
|
||||
throw new ValidationException(
|
||||
'Static library ' . $static_lib . ' not found in ' . BUILD_LIB_PATH,
|
||||
validation_module: "Static library {$static_lib} existence check"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,24 +4,167 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
use ZM\Logger\ConsoleColor;
|
||||
|
||||
class ExceptionHandler
|
||||
{
|
||||
protected mixed $whoops = null;
|
||||
public const array KNOWN_EXCEPTIONS = [
|
||||
BuildFailureException::class,
|
||||
DownloaderException::class,
|
||||
EnvironmentException::class,
|
||||
ExecutionException::class,
|
||||
FileSystemException::class,
|
||||
InterruptException::class,
|
||||
PatchException::class,
|
||||
SPCInternalException::class,
|
||||
ValidationException::class,
|
||||
WrongUsageException::class,
|
||||
];
|
||||
|
||||
private static ?ExceptionHandler $obj = null;
|
||||
public const array MINOR_LOG_EXCEPTIONS = [
|
||||
InterruptException::class,
|
||||
WrongUsageException::class,
|
||||
];
|
||||
|
||||
public static function getInstance(): ExceptionHandler
|
||||
public static function handleSPCException(SPCException $e): void
|
||||
{
|
||||
if (self::$obj === null) {
|
||||
self::$obj = new self();
|
||||
// XXX error: yyy
|
||||
$head_msg = match ($class = get_class($e)) {
|
||||
BuildFailureException::class => "Build failed: {$e->getMessage()}",
|
||||
DownloaderException::class => "Download failed: {$e->getMessage()}",
|
||||
EnvironmentException::class => "Environment check failed: {$e->getMessage()}",
|
||||
ExecutionException::class => "Command execution failed: {$e->getMessage()}",
|
||||
FileSystemException::class => "File system error: {$e->getMessage()}",
|
||||
InterruptException::class => "⚠ Build interrupted by user: {$e->getMessage()}",
|
||||
PatchException::class => "Patch apply failed: {$e->getMessage()}",
|
||||
SPCInternalException::class => "SPC internal error: {$e->getMessage()}",
|
||||
ValidationException::class => "Validation failed: {$e->getMessage()}",
|
||||
WrongUsageException::class => $e->getMessage(),
|
||||
default => "Unknown SPC exception {$class}: {$e->getMessage()}",
|
||||
};
|
||||
self::logError($head_msg);
|
||||
|
||||
// ----------------------------------------
|
||||
$minor_logs = in_array($class, self::MINOR_LOG_EXCEPTIONS, true);
|
||||
|
||||
if ($minor_logs) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::logError("----------------------------------------\n");
|
||||
|
||||
// get the SPCException module
|
||||
if ($php_info = $e->getBuildPHPInfo()) {
|
||||
self::logError('✗ Failed module: ' . ConsoleColor::yellow("PHP builder {$php_info['builder_class']} for {$php_info['os']}"));
|
||||
} elseif ($lib_info = $e->getLibraryInfo()) {
|
||||
self::logError('✗ Failed module: ' . ConsoleColor::yellow("library {$lib_info['library_name']} builder for {$lib_info['os']}"));
|
||||
} elseif ($ext_info = $e->getExtensionInfo()) {
|
||||
self::logError('✗ Failed module: ' . ConsoleColor::yellow("shared extension {$ext_info['extension_name']} builder"));
|
||||
} elseif (!in_array($class, self::KNOWN_EXCEPTIONS)) {
|
||||
self::logError('✗ Failed From: ' . ConsoleColor::yellow('Unknown SPC module ' . $class));
|
||||
}
|
||||
self::logError('');
|
||||
|
||||
// get command execution info
|
||||
if ($e instanceof ExecutionException) {
|
||||
self::logError('✗ Failed command: ' . ConsoleColor::yellow($e->getExecutionCommand()));
|
||||
if ($cd = $e->getCd()) {
|
||||
self::logError('✗ Command executed in: ' . ConsoleColor::yellow($cd));
|
||||
}
|
||||
if ($env = $e->getEnv()) {
|
||||
self::logError('✗ Command inline env variables:');
|
||||
foreach ($env as $k => $v) {
|
||||
self::logError(ConsoleColor::yellow("{$k}={$v}"), 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validation error
|
||||
if ($e instanceof ValidationException) {
|
||||
self::logError('✗ Failed validation module: ' . ConsoleColor::yellow($e->getValidationModuleString()));
|
||||
}
|
||||
|
||||
// environment error
|
||||
if ($e instanceof EnvironmentException) {
|
||||
self::logError('✗ Failed environment check: ' . ConsoleColor::yellow($e->getMessage()));
|
||||
if (($solution = $e->getSolution()) !== null) {
|
||||
self::logError('✗ Solution: ' . ConsoleColor::yellow($solution));
|
||||
}
|
||||
}
|
||||
|
||||
// get patch info
|
||||
if ($e instanceof PatchException) {
|
||||
self::logError("✗ Failed patch module: {$e->getPatchModule()}");
|
||||
}
|
||||
|
||||
// get internal trace
|
||||
if ($e instanceof SPCInternalException) {
|
||||
self::logError('✗ Internal trace:');
|
||||
self::logError(ConsoleColor::gray("{$e->getTraceAsString()}\n"), 4);
|
||||
}
|
||||
|
||||
// get the full build info if possible
|
||||
if (($info = $e->getBuildPHPExtraInfo()) && defined('DEBUG_MODE')) {
|
||||
self::logError('✗ Build PHP extra info:');
|
||||
$maxlen = 0;
|
||||
foreach ($info as $k => $v) {
|
||||
$maxlen = max(strlen($k), $maxlen);
|
||||
}
|
||||
foreach ($info as $k => $v) {
|
||||
if (is_string($v)) {
|
||||
self::logError($k . ': ' . str_pad('', $maxlen - strlen($k)) . ConsoleColor::yellow($v), 4);
|
||||
} elseif (is_array($v) && !is_assoc_array($v)) {
|
||||
$first = array_shift($v);
|
||||
self::logError($k . ': ' . str_pad('', $maxlen - strlen($k)) . ConsoleColor::yellow($first), 4);
|
||||
foreach ($v as $vs) {
|
||||
self::logError(str_pad('', $maxlen + 2) . ConsoleColor::yellow($vs), 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::logError("\n----------------------------------------\n");
|
||||
|
||||
// put getenv info to log
|
||||
$env_log = fopen(SPC_ENV_LOG, 'a');
|
||||
$env_info = getenv();
|
||||
if ($env_info) {
|
||||
foreach ($env_info as $k => $v) {
|
||||
fwrite($env_log, $k . ' = ' . $v . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
self::logError('⚠ The ' . ConsoleColor::cyan('console output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none(SPC_OUTPUT_LOG));
|
||||
if (file_exists(SPC_SHELL_LOG)) {
|
||||
self::logError('⚠ The ' . ConsoleColor::cyan('shell output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none(SPC_SHELL_LOG));
|
||||
}
|
||||
if ($e->getExtraLogFiles() !== []) {
|
||||
foreach ($e->getExtraLogFiles() as $key => $file) {
|
||||
self::logError("⚠ Log file [{$key}] is saved in: " . ConsoleColor::none(SPC_LOGS_DIR . "/{$file}"));
|
||||
}
|
||||
}
|
||||
if (!defined('DEBUG_MODE')) {
|
||||
self::logError('⚠ If you want to see more details in console, use `--debug` option.');
|
||||
}
|
||||
return self::$obj;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $e): void
|
||||
public static function handleDefaultException(\Throwable $e): void
|
||||
{
|
||||
logger()->error('Uncaught ' . get_class($e) . ': ' . $e->getMessage() . ' at ' . $e->getFile() . '(' . $e->getLine() . ')');
|
||||
logger()->error($e->getTraceAsString());
|
||||
logger()->critical('You can report this exception to static-php-cli GitHub repo.');
|
||||
$class = get_class($e);
|
||||
self::logError("Unhandled exception {$class}: {$e->getMessage()}\n\t{$e->getMessage()}\n");
|
||||
self::logError('Stack trace:');
|
||||
self::logError(ConsoleColor::gray($e->getTraceAsString()), 4);
|
||||
self::logError('Please report this exception to: https://github.com/crazywhalecc/static-php-cli/issues');
|
||||
}
|
||||
|
||||
private static function logError($message, int $indent_space = 0): void
|
||||
{
|
||||
$spc_log = fopen(SPC_OUTPUT_LOG, 'a');
|
||||
$msg = explode("\n", (string) $message);
|
||||
foreach ($msg as $v) {
|
||||
$line = str_pad($v, strlen($v) + $indent_space, ' ', STR_PAD_LEFT);
|
||||
fwrite($spc_log, strip_ansi_colors($line) . PHP_EOL);
|
||||
echo ConsoleColor::red($line) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ use SPC\builder\BuilderBase;
|
||||
use SPC\builder\BuilderProvider;
|
||||
use SPC\builder\Extension;
|
||||
use SPC\builder\LibraryBase;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\store\LockFile;
|
||||
@ -88,7 +87,7 @@ class BuilderTest extends TestCase
|
||||
if ($cnt !== 0) {
|
||||
$this->assertEquals(intval($match[1]), $this->builder->getPHPVersionID());
|
||||
} else {
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->builder->getPHPVersionID();
|
||||
}
|
||||
} else {
|
||||
@ -105,7 +104,7 @@ class BuilderTest extends TestCase
|
||||
if ($cnt !== 0) {
|
||||
$this->assertEquals($match[1], $this->builder->getPHPVersion());
|
||||
} else {
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->builder->getPHPVersion();
|
||||
}
|
||||
} else {
|
||||
@ -246,7 +245,7 @@ class BuilderTest extends TestCase
|
||||
public function testEmitPatchPointNotExists()
|
||||
{
|
||||
$this->expectOutputRegex('/failed to run/');
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->builder->setOption('with-added-patch', ['/tmp/patch-point.not_exsssists.php']);
|
||||
$this->builder->emitPatchPoint('not-exists');
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user