mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 21:04:52 +08:00
refactor command as more easily
This commit is contained in:
parent
1540af266b
commit
4c0d35c723
@ -16,7 +16,7 @@ use Symfony\Component\Console\Command\ListCommand;
|
|||||||
*/
|
*/
|
||||||
class ConsoleApplication extends Application
|
class ConsoleApplication extends Application
|
||||||
{
|
{
|
||||||
public const VERSION = '2.0-beta1';
|
public const VERSION = '2.0-beta2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
|
|||||||
@ -13,6 +13,18 @@ use ZM\Logger\ConsoleLogger;
|
|||||||
|
|
||||||
abstract class BaseCommand extends Command
|
abstract class BaseCommand extends Command
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 输入
|
||||||
|
*/
|
||||||
|
protected InputInterface $input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出
|
||||||
|
*
|
||||||
|
* 一般来说同样会是 ConsoleOutputInterface
|
||||||
|
*/
|
||||||
|
protected OutputInterface $output;
|
||||||
|
|
||||||
public function __construct(string $name = null)
|
public function __construct(string $name = null)
|
||||||
{
|
{
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
@ -56,4 +68,44 @@ abstract class BaseCommand extends Command
|
|||||||
";
|
";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract public function handle(): int;
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$this->input = $input;
|
||||||
|
$this->output = $output;
|
||||||
|
if ($this->shouldExecute()) {
|
||||||
|
try {
|
||||||
|
return $this->handle();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$msg = explode("\n", $e->getMessage());
|
||||||
|
foreach ($msg as $v) {
|
||||||
|
logger()->error($v);
|
||||||
|
}
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getOption(string $name): mixed
|
||||||
|
{
|
||||||
|
return $this->input->getOption($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getArgument(string $name): mixed
|
||||||
|
{
|
||||||
|
return $this->input->getArgument($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否应该执行
|
||||||
|
*
|
||||||
|
* @return bool 返回 true 以继续执行,返回 false 以中断执行
|
||||||
|
*/
|
||||||
|
protected function shouldExecute(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,38 +9,34 @@ use SPC\exception\ExceptionHandler;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\util\DependencyUtil;
|
use SPC\util\DependencyUtil;
|
||||||
use SPC\util\LicenseDumper;
|
use SPC\util\LicenseDumper;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
#[AsCommand('build', 'build CLI binary')]
|
||||||
class BuildCliCommand extends BuildCommand
|
class BuildCliCommand extends BuildCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'build';
|
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('Build CLI binary');
|
|
||||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
$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('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
|
||||||
$this->addOption('build-micro', null, null, 'build micro only');
|
$this->addOption('build-micro', null, null, 'build micro only');
|
||||||
$this->addOption('build-all', null, null, 'build both cli and micro');
|
$this->addOption('build-all', null, null, 'build both cli and micro');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
// 从参数中获取要编译的 libraries,并转换为数组
|
// 从参数中获取要编译的 libraries,并转换为数组
|
||||||
$libraries = array_map('trim', array_filter(explode(',', $input->getOption('with-libs'))));
|
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs'))));
|
||||||
// 从参数中获取要编译的 extensions,并转换为数组
|
// 从参数中获取要编译的 extensions,并转换为数组
|
||||||
$extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions'))));
|
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));
|
||||||
|
|
||||||
define('BUILD_ALL_STATIC', true);
|
define('BUILD_ALL_STATIC', true);
|
||||||
|
|
||||||
if ($input->getOption('build-all')) {
|
if ($this->getOption('build-all')) {
|
||||||
$rule = BUILD_MICRO_BOTH;
|
$rule = BUILD_MICRO_BOTH;
|
||||||
logger()->info('Builder will build php-cli and phpmicro SAPI');
|
logger()->info('Builder will build php-cli and phpmicro SAPI');
|
||||||
} elseif ($input->getOption('build-micro')) {
|
} elseif ($this->getOption('build-micro')) {
|
||||||
$rule = BUILD_MICRO_ONLY;
|
$rule = BUILD_MICRO_ONLY;
|
||||||
logger()->info('Builder will build phpmicro SAPI');
|
logger()->info('Builder will build phpmicro SAPI');
|
||||||
} else {
|
} else {
|
||||||
@ -49,7 +45,7 @@ class BuildCliCommand extends BuildCommand
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// 构建对象
|
// 构建对象
|
||||||
$builder = BuilderProvider::makeBuilderByInput($input);
|
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||||
// 根据提供的扩展列表获取依赖库列表并编译
|
// 根据提供的扩展列表获取依赖库列表并编译
|
||||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
|
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
|
||||||
|
|
||||||
@ -64,7 +60,7 @@ class BuildCliCommand extends BuildCommand
|
|||||||
// 执行扩展检测
|
// 执行扩展检测
|
||||||
$builder->proveExts($extensions);
|
$builder->proveExts($extensions);
|
||||||
// 构建
|
// 构建
|
||||||
$builder->buildPHP($rule, $input->getOption('bloat'));
|
$builder->buildPHP($rule, $this->getOption('bloat'));
|
||||||
// 统计时间
|
// 统计时间
|
||||||
$time = round(microtime(true) - START_TIME, 3);
|
$time = round(microtime(true) - START_TIME, 3);
|
||||||
logger()->info('Build complete, used ' . $time . ' s !');
|
logger()->info('Build complete, used ' . $time . ' s !');
|
||||||
@ -86,7 +82,7 @@ class BuildCliCommand extends BuildCommand
|
|||||||
logger()->critical($e->getMessage());
|
logger()->critical($e->getMessage());
|
||||||
return 1;
|
return 1;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
if ($input->getOption('debug')) {
|
if ($this->getOption('debug')) {
|
||||||
ExceptionHandler::getInstance()->handle($e);
|
ExceptionHandler::getInstance()->handle($e);
|
||||||
} else {
|
} else {
|
||||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||||
|
|||||||
@ -6,20 +6,17 @@ namespace SPC\command;
|
|||||||
|
|
||||||
use SPC\builder\BuilderProvider;
|
use SPC\builder\BuilderProvider;
|
||||||
use SPC\exception\ExceptionHandler;
|
use SPC\exception\ExceptionHandler;
|
||||||
use SPC\exception\FileSystemException;
|
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
#[AsCommand('build:libs', 'Build dependencies')]
|
||||||
class BuildLibsCommand extends BuildCommand
|
class BuildLibsCommand extends BuildCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'build:libs';
|
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('Build dependencies');
|
|
||||||
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
||||||
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
||||||
$this->addOption('all', 'A', null, 'Build all libs that static-php-cli needed');
|
$this->addOption('all', 'A', null, 'Build all libs that static-php-cli needed');
|
||||||
@ -36,15 +33,14 @@ class BuildLibsCommand extends BuildCommand
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @throws FileSystemException
|
|
||||||
*/
|
*/
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
// 从参数中获取要编译的 libraries,并转换为数组
|
// 从参数中获取要编译的 libraries,并转换为数组
|
||||||
$libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries'))));
|
$libraries = array_map('trim', array_filter(explode(',', $this->getArgument('libraries'))));
|
||||||
|
|
||||||
// 删除旧资源
|
// 删除旧资源
|
||||||
if ($input->getOption('clean')) {
|
if ($this->getOption('clean')) {
|
||||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||||
logger()->warning(BUILD_ROOT_PATH);
|
logger()->warning(BUILD_ROOT_PATH);
|
||||||
logger()->warning('I will remove these dir after you press [Enter] !');
|
logger()->warning('I will remove these dir after you press [Enter] !');
|
||||||
@ -59,7 +55,7 @@ class BuildLibsCommand extends BuildCommand
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 构建对象
|
// 构建对象
|
||||||
$builder = BuilderProvider::makeBuilderByInput($input);
|
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||||
// 只编译 library 的情况下,标记
|
// 只编译 library 的情况下,标记
|
||||||
$builder->setLibsOnly();
|
$builder->setLibsOnly();
|
||||||
// 编译和检查库完整
|
// 编译和检查库完整
|
||||||
@ -69,7 +65,7 @@ class BuildLibsCommand extends BuildCommand
|
|||||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||||
return 0;
|
return 0;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
if ($input->getOption('debug')) {
|
if ($this->getOption('debug')) {
|
||||||
ExceptionHandler::getInstance()->handle($e);
|
ExceptionHandler::getInstance()->handle($e);
|
||||||
} else {
|
} else {
|
||||||
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage());
|
||||||
|
|||||||
@ -7,32 +7,28 @@ namespace SPC\command;
|
|||||||
use CliHelper\Tools\ArgFixer;
|
use CliHelper\Tools\ArgFixer;
|
||||||
use CliHelper\Tools\DataProvider;
|
use CliHelper\Tools\DataProvider;
|
||||||
use CliHelper\Tools\SeekableArrayIterator;
|
use CliHelper\Tools\SeekableArrayIterator;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Helper\ProgressBar;
|
use Symfony\Component\Console\Helper\ProgressBar;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
#[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')]
|
||||||
class DeployCommand extends BaseCommand
|
class DeployCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'deploy';
|
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('Deploy static-php-cli self to an .phar application');
|
|
||||||
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
|
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
|
||||||
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
|
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
|
||||||
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
|
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
// 第一阶段流程:如果没有写path,将会提示输入要打包的path
|
// 第一阶段流程:如果没有写path,将会提示输入要打包的path
|
||||||
$prompt = new ArgFixer($input, $output);
|
$prompt = new ArgFixer($this->input, $this->output);
|
||||||
// 首先得确认是不是关闭了readonly模式
|
// 首先得确认是不是关闭了readonly模式
|
||||||
if (ini_get('phar.readonly') == 1) {
|
if (ini_get('phar.readonly') == 1) {
|
||||||
if ($input->getOption('auto-phar-fix')) {
|
if ($this->getOption('auto-phar-fix')) {
|
||||||
$ask = true;
|
$ask = true;
|
||||||
} else {
|
} else {
|
||||||
$ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
|
$ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
|
||||||
@ -41,12 +37,12 @@ class DeployCommand extends BaseCommand
|
|||||||
global $argv;
|
global $argv;
|
||||||
$args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv']);
|
$args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv']);
|
||||||
if (function_exists('pcntl_exec')) {
|
if (function_exists('pcntl_exec')) {
|
||||||
$output->writeln('<info>Changing to phar.readonly=0 mode ...</info>');
|
$this->output->writeln('<info>Changing to phar.readonly=0 mode ...</info>');
|
||||||
if (pcntl_exec(PHP_BINARY, $args) === false) {
|
if (pcntl_exec(PHP_BINARY, $args) === false) {
|
||||||
throw new \PharException('切换到读写模式失败,请检查环境。');
|
throw new \PharException('切换到读写模式失败,请检查环境。');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('<info>Now running command in child process.</info>');
|
$this->output->writeln('<info>Now running command in child process.</info>');
|
||||||
passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode);
|
passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode);
|
||||||
exit($retcode);
|
exit($retcode);
|
||||||
}
|
}
|
||||||
@ -61,9 +57,9 @@ class DeployCommand extends BaseCommand
|
|||||||
$phar_path = '/tmp/' . $phar_path;
|
$phar_path = '/tmp/' . $phar_path;
|
||||||
}
|
}
|
||||||
if (file_exists($phar_path)) {
|
if (file_exists($phar_path)) {
|
||||||
$ask = $input->getOption('overwrite') ? true : $prompt->requireBool('<comment>The file "' . $phar_path . '" already exists, do you want to overwrite it?</comment>' . PHP_EOL . 'If you want to, just Enter');
|
$ask = $this->getOption('overwrite') ? true : $prompt->requireBool('<comment>The file "' . $phar_path . '" already exists, do you want to overwrite it?</comment>' . PHP_EOL . 'If you want to, just Enter');
|
||||||
if (!$ask) {
|
if (!$ask) {
|
||||||
$output->writeln('<comment>User canceled.</comment>');
|
$this->output->writeln('<comment>User canceled.</comment>');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@unlink($phar_path);
|
@unlink($phar_path);
|
||||||
@ -83,9 +79,9 @@ class DeployCommand extends BaseCommand
|
|||||||
$map[$v] = $path . '/' . $v;
|
$map[$v] = $path . '/' . $v;
|
||||||
}
|
}
|
||||||
|
|
||||||
$output->writeln('<info>Start packing files...</info>');
|
$this->output->writeln('<info>Start packing files...</info>');
|
||||||
try {
|
try {
|
||||||
foreach ($this->progress($output)->iterate($map) as $file => $origin_file) {
|
foreach ($this->progress()->iterate($map) as $file => $origin_file) {
|
||||||
$phar->addFromString($file, php_strip_whitespace($origin_file));
|
$phar->addFromString($file, php_strip_whitespace($origin_file));
|
||||||
}
|
}
|
||||||
// $phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output)));
|
// $phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output)));
|
||||||
@ -100,30 +96,30 @@ class DeployCommand extends BaseCommand
|
|||||||
$stub = '.phar-entry.php';
|
$stub = '.phar-entry.php';
|
||||||
$phar->setStub($phar->createDefaultStub($stub));
|
$phar->setStub($phar->createDefaultStub($stub));
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$output->writeln($e);
|
$this->output->writeln($e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
$phar->addFromString('.prod', 'true');
|
$phar->addFromString('.prod', 'true');
|
||||||
$phar->stopBuffering();
|
$phar->stopBuffering();
|
||||||
$output->writeln(PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".');
|
$this->output->writeln(PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".');
|
||||||
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx')) {
|
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx')) {
|
||||||
$output->writeln('Detected you have already compiled micro binary, I will make executable now for you!');
|
$this->output->writeln('Detected you have already compiled micro binary, I will make executable now for you!');
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc',
|
pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc',
|
||||||
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
|
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
|
||||||
file_get_contents($phar_path)
|
file_get_contents($phar_path)
|
||||||
);
|
);
|
||||||
chmod(pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc', 0755);
|
chmod(pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc', 0755);
|
||||||
$output->writeln('<info>Binary Executable: ' . pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc</info>');
|
$this->output->writeln('<info>Binary Executable: ' . pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc</info>');
|
||||||
}
|
}
|
||||||
chmod($phar_path, 0755);
|
chmod($phar_path, 0755);
|
||||||
$output->writeln('<info>Phar Executable: ' . $phar_path . '</info>');
|
$this->output->writeln('<info>Phar Executable: ' . $phar_path . '</info>');
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function progress(OutputInterface $output, int $max = 0): ProgressBar
|
private function progress(int $max = 0): ProgressBar
|
||||||
{
|
{
|
||||||
$progress = new ProgressBar($output, $max);
|
$progress = new ProgressBar($this->output, $max);
|
||||||
$progress->setBarCharacter('<fg=green>⚬</>');
|
$progress->setBarCharacter('<fg=green>⚬</>');
|
||||||
$progress->setEmptyBarCharacter('<fg=red>⚬</>');
|
$progress->setEmptyBarCharacter('<fg=red>⚬</>');
|
||||||
$progress->setProgressCharacter('<fg=green>➤</>');
|
$progress->setProgressCharacter('<fg=green>➤</>');
|
||||||
|
|||||||
17
src/SPC/command/DoctorCommand.php
Normal file
17
src/SPC/command/DoctorCommand.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
|
||||||
|
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
|
||||||
|
class DoctorCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
logger()->error('Not implemented');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,20 +9,17 @@ use SPC\exception\RuntimeException;
|
|||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\util\DependencyUtil;
|
use SPC\util\DependencyUtil;
|
||||||
use SPC\util\LicenseDumper;
|
use SPC\util\LicenseDumper;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改 config 后对其 kv 进行排序的操作
|
* 修改 config 后对其 kv 进行排序的操作
|
||||||
*/
|
*/
|
||||||
|
#[AsCommand('dump-license', 'Dump licenses for required libraries')]
|
||||||
class DumpLicenseCommand extends BaseCommand
|
class DumpLicenseCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'dump-license';
|
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('Dump licenses for required libraries');
|
|
||||||
$this->addOption('by-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null);
|
$this->addOption('by-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null);
|
||||||
$this->addOption('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src');
|
$this->addOption('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src');
|
||||||
$this->addOption('by-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null);
|
$this->addOption('by-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null);
|
||||||
@ -35,42 +32,42 @@ class DumpLicenseCommand extends BaseCommand
|
|||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$dumper = new LicenseDumper();
|
$dumper = new LicenseDumper();
|
||||||
if ($input->getOption('by-extensions') !== null) {
|
if ($this->getOption('by-extensions') !== null) {
|
||||||
// 从参数中获取要编译的 extensions,并转换为数组
|
// 从参数中获取要编译的 extensions,并转换为数组
|
||||||
$extensions = array_map('trim', array_filter(explode(',', $input->getOption('by-extensions'))));
|
$extensions = array_map('trim', array_filter(explode(',', $this->getOption('by-extensions'))));
|
||||||
// 根据提供的扩展列表获取依赖库列表并编译
|
// 根据提供的扩展列表获取依赖库列表并编译
|
||||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions);
|
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions);
|
||||||
$dumper->addExts($extensions);
|
$dumper->addExts($extensions);
|
||||||
$dumper->addLibs($libraries);
|
$dumper->addLibs($libraries);
|
||||||
if (!$input->getOption('without-php')) {
|
if (!$this->getOption('without-php')) {
|
||||||
$dumper->addSources(['php-src']);
|
$dumper->addSources(['php-src']);
|
||||||
}
|
}
|
||||||
$dumper->dump($input->getOption('dump-dir'));
|
$dumper->dump($this->getOption('dump-dir'));
|
||||||
$output->writeln('Dump license with extensions: ' . implode(', ', $extensions));
|
$this->output->writeln('Dump license with extensions: ' . implode(', ', $extensions));
|
||||||
$output->writeln('Dump license with libraries: ' . implode(', ', $libraries));
|
$this->output->writeln('Dump license with libraries: ' . implode(', ', $libraries));
|
||||||
$output->writeln('Dump license with' . ($input->getOption('without-php') ? 'out' : '') . ' php-src');
|
$this->output->writeln('Dump license with' . ($this->getOption('without-php') ? 'out' : '') . ' php-src');
|
||||||
$output->writeln('Dump target dir: ' . $input->getOption('dump-dir'));
|
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ($input->getOption('by-libs') !== null) {
|
if ($this->getOption('by-libs') !== null) {
|
||||||
$libraries = array_map('trim', array_filter(explode(',', $input->getOption('by-libs'))));
|
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs'))));
|
||||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||||
$dumper->addLibs($libraries);
|
$dumper->addLibs($libraries);
|
||||||
$dumper->dump($input->getOption('dump-dir'));
|
$dumper->dump($this->getOption('dump-dir'));
|
||||||
$output->writeln('Dump target dir: ' . $input->getOption('dump-dir'));
|
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ($input->getOption('by-sources') !== null) {
|
if ($this->getOption('by-sources') !== null) {
|
||||||
$sources = array_map('trim', array_filter(explode(',', $input->getOption('by-sources'))));
|
$sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources'))));
|
||||||
$dumper->addSources($sources);
|
$dumper->addSources($sources);
|
||||||
$dumper->dump($input->getOption('dump-dir'));
|
$dumper->dump($this->getOption('dump-dir'));
|
||||||
$output->writeln('Dump target dir: ' . $input->getOption('dump-dir'));
|
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump');
|
$this->output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,23 +13,20 @@ use SPC\store\Config;
|
|||||||
use SPC\store\Downloader;
|
use SPC\store\Downloader;
|
||||||
use SPC\util\Patcher;
|
use SPC\util\Patcher;
|
||||||
use SPC\util\Util;
|
use SPC\util\Util;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
/** @noinspection PhpUnused */
|
||||||
|
#[AsCommand('fetch', 'Fetch required sources')]
|
||||||
class FetchSourceCommand extends BaseCommand
|
class FetchSourceCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'fetch';
|
|
||||||
|
|
||||||
protected string $php_major_ver;
|
protected string $php_major_ver;
|
||||||
|
|
||||||
protected InputInterface $input;
|
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('Fetch required sources');
|
|
||||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
||||||
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
||||||
$this->addOption('hash', null, null, 'Hash only');
|
$this->addOption('hash', null, null, 'Hash only');
|
||||||
@ -50,12 +47,11 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
parent::initialize($input, $output);
|
parent::initialize($input, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$this->input = $input;
|
|
||||||
try {
|
try {
|
||||||
// 匹配版本
|
// 匹配版本
|
||||||
$ver = $this->php_major_ver = $input->getOption('with-php') ?? '8.1';
|
$ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1';
|
||||||
define('SPC_BUILD_PHP_VERSION', $ver);
|
define('SPC_BUILD_PHP_VERSION', $ver);
|
||||||
preg_match('/^\d+\.\d+$/', $ver, $matches);
|
preg_match('/^\d+\.\d+$/', $ver, $matches);
|
||||||
if (!$matches) {
|
if (!$matches) {
|
||||||
@ -64,7 +60,7 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 删除旧资源
|
// 删除旧资源
|
||||||
if ($input->getOption('clean')) {
|
if ($this->getOption('clean')) {
|
||||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||||
logger()->warning(SOURCE_PATH);
|
logger()->warning(SOURCE_PATH);
|
||||||
logger()->warning(DOWNLOAD_PATH);
|
logger()->warning(DOWNLOAD_PATH);
|
||||||
@ -85,7 +81,7 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用浅克隆可以减少调用 git 命令下载资源时的存储空间占用
|
// 使用浅克隆可以减少调用 git 命令下载资源时的存储空间占用
|
||||||
if ($input->getOption('shallow-clone')) {
|
if ($this->getOption('shallow-clone')) {
|
||||||
define('GIT_SHALLOW_CLONE', true);
|
define('GIT_SHALLOW_CLONE', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +89,7 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
Config::getSource('openssl');
|
Config::getSource('openssl');
|
||||||
|
|
||||||
// 是否启用openssl11
|
// 是否启用openssl11
|
||||||
if ($input->getOption('with-openssl11')) {
|
if ($this->getOption('with-openssl11')) {
|
||||||
logger()->debug('Using openssl 1.1');
|
logger()->debug('Using openssl 1.1');
|
||||||
// 手动修改配置
|
// 手动修改配置
|
||||||
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
||||||
@ -103,7 +99,7 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
$chosen_sources = ['micro'];
|
$chosen_sources = ['micro'];
|
||||||
|
|
||||||
// 从参数中获取要编译的 libraries,并转换为数组
|
// 从参数中获取要编译的 libraries,并转换为数组
|
||||||
$libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries'))));
|
$libraries = array_map('trim', array_filter(explode(',', $this->getArgument('libraries'))));
|
||||||
if ($libraries) {
|
if ($libraries) {
|
||||||
foreach ($libraries as $lib) {
|
foreach ($libraries as $lib) {
|
||||||
// 从 lib 的 config 中找到对应 source 资源名称,组成一个 lib 的 source 列表
|
// 从 lib 的 config 中找到对应 source 资源名称,组成一个 lib 的 source 列表
|
||||||
@ -115,7 +111,7 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从参数中获取要编译的 extensions,并转换为数组
|
// 从参数中获取要编译的 extensions,并转换为数组
|
||||||
$extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions'))));
|
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));
|
||||||
if ($extensions) {
|
if ($extensions) {
|
||||||
foreach ($extensions as $lib) {
|
foreach ($extensions as $lib) {
|
||||||
if (Config::getExt($lib, 'type') !== 'builtin') {
|
if (Config::getExt($lib, 'type') !== 'builtin') {
|
||||||
@ -133,9 +129,9 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
$chosen_sources = array_unique($chosen_sources);
|
$chosen_sources = array_unique($chosen_sources);
|
||||||
|
|
||||||
// 是否只hash,不下载资源
|
// 是否只hash,不下载资源
|
||||||
if ($input->getOption('hash')) {
|
if ($this->getOption('hash')) {
|
||||||
$hash = $this->doHash($chosen_sources);
|
$hash = $this->doHash($chosen_sources);
|
||||||
$output->writeln($hash);
|
$this->output->writeln($hash);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +163,7 @@ class FetchSourceCommand extends BaseCommand
|
|||||||
return 0;
|
return 0;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// 不开 debug 模式就不要再显示复杂的调试栈信息了
|
// 不开 debug 模式就不要再显示复杂的调试栈信息了
|
||||||
if ($input->getOption('debug')) {
|
if ($this->getOption('debug')) {
|
||||||
ExceptionHandler::getInstance()->handle($e);
|
ExceptionHandler::getInstance()->handle($e);
|
||||||
} else {
|
} else {
|
||||||
logger()->emergency($e->getMessage() . ', previous message: ' . $e->getPrevious()?->getMessage());
|
logger()->emergency($e->getMessage() . ', previous message: ' . $e->getPrevious()?->getMessage());
|
||||||
|
|||||||
@ -5,22 +5,19 @@ declare(strict_types=1);
|
|||||||
namespace SPC\command;
|
namespace SPC\command;
|
||||||
|
|
||||||
use SPC\builder\traits\NoMotdTrait;
|
use SPC\builder\traits\NoMotdTrait;
|
||||||
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
|
|
||||||
|
#[AsCommand('list-ext', 'List supported extensions')]
|
||||||
class ListExtCommand extends BaseCommand
|
class ListExtCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
use NoMotdTrait;
|
use NoMotdTrait;
|
||||||
|
|
||||||
protected static $defaultName = 'list-ext';
|
/**
|
||||||
|
* @throws FileSystemException
|
||||||
public function configure()
|
*/
|
||||||
{
|
public function handle(): int
|
||||||
$this->setDescription('List supported extensions');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output)
|
|
||||||
{
|
{
|
||||||
foreach (Config::getExts() as $ext => $meta) {
|
foreach (Config::getExts() as $ext => $meta) {
|
||||||
echo $ext . PHP_EOL;
|
echo $ext . PHP_EOL;
|
||||||
|
|||||||
@ -8,20 +8,17 @@ use SPC\exception\FileSystemException;
|
|||||||
use SPC\exception\ValidationException;
|
use SPC\exception\ValidationException;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use SPC\util\ConfigValidator;
|
use SPC\util\ConfigValidator;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改 config 后对其 kv 进行排序的操作
|
* 修改 config 后对其 kv 进行排序的操作
|
||||||
*/
|
*/
|
||||||
|
#[AsCommand('sort-config', 'After config edited, sort it by alphabet')]
|
||||||
class SortConfigCommand extends BaseCommand
|
class SortConfigCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'sort-config';
|
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('After config edited, sort it by alphabet');
|
|
||||||
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
|
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,9 +26,9 @@ class SortConfigCommand extends BaseCommand
|
|||||||
* @throws ValidationException
|
* @throws ValidationException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
switch ($name = $input->getArgument('config-name')) {
|
switch ($name = $this->getArgument('config-name')) {
|
||||||
case 'lib':
|
case 'lib':
|
||||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
|
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
|
||||||
ConfigValidator::validateLibs($file);
|
ConfigValidator::validateLibs($file);
|
||||||
@ -51,10 +48,10 @@ class SortConfigCommand extends BaseCommand
|
|||||||
file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$output->writeln("<error>invalid config name: {$name}</error>");
|
$this->output->writeln("<error>invalid config name: {$name}</error>");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
$output->writeln('<info>sort success</info>');
|
$this->output->writeln('<info>sort success</info>');
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user