Created
August 27, 2023 06:05
-
-
Save crazywhalecc/1f91dfa9bb1137cc78a9b2344b243f2c to your computer and use it in GitHub Desktop.
GoCommand.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace SPC\command; | |
use SPC\doctor\CheckListHandler; | |
use SPC\exception\DownloaderException; | |
use SPC\exception\FileSystemException; | |
use SPC\exception\RuntimeException; | |
use SPC\exception\WrongUsageException; | |
use SPC\store\Config; | |
use SPC\store\Downloader; | |
use Symfony\Component\Console\Attribute\AsCommand; | |
use function Laravel\Prompts\confirm; | |
use function Laravel\Prompts\multiselect; | |
use function Laravel\Prompts\select; | |
#[AsCommand('go', 'Interactive build')] | |
class GoCommand extends BaseCommand | |
{ | |
/** @var array[] The main steps */ | |
private const MAIN_STEPS = [ | |
'doctor' => [ | |
'display_name' => 'Doctor', | |
'help' => 'Check your system environment', | |
'func' => 'doctor', | |
], | |
'download' => [ | |
'display_name' => 'Download', | |
'help' => 'Download required packages to compile', | |
'func' => 'download', | |
], | |
'build:libs' => [ | |
'display_name' => 'Build Libraries', | |
'help' => 'Build your selected libraries only', | |
'func' => 'buildLibs', | |
], | |
'quit' => [ | |
'display_name' => 'Quit', | |
'help' => 'Quit', | |
'func' => 'bye', | |
], | |
]; | |
/** | |
* Go command entry | |
*/ | |
public function handle(): int | |
{ | |
// Calculate longest display name | |
$maxlen = max(...array_map(fn ($x) => strlen($x['display_name']), array_values(self::MAIN_STEPS))); | |
$select = array_map(function ($x) use ($maxlen) { | |
$x['display_name'] = str_pad($x['display_name'], $maxlen); | |
return $x['display_name'] . ' - ' . $x['help']; | |
}, self::MAIN_STEPS); | |
try { | |
$way = null; | |
while ($way !== 'quit') { | |
// Ask ways to do | |
$way = select('Please choose your compile steps', $select); | |
if (method_exists($this, self::MAIN_STEPS[$way]['func'] ?? '')) { | |
$this->{self::MAIN_STEPS[$way]['func']}(); | |
} | |
} | |
} catch (\Throwable $e) { | |
$this->output->writeln('<error>' . $e->getMessage() . '</error>'); | |
pcntl_signal(SIGINT, SIG_IGN); | |
return static::FAILURE; | |
} | |
return static::SUCCESS; | |
} | |
/** | |
* [STEP] doctor | |
* | |
* @throws \ReflectionException | |
* @throws RuntimeException | |
* @throws FileSystemException | |
*/ | |
protected function doctor(): void | |
{ | |
$checker = new CheckListHandler($this->input, $this->output); | |
$checker->runCheck(FIX_POLICY_PROMPT); | |
$this->output->writeln('<info>Doctor check complete !</info>'); | |
} | |
/** | |
* [STEP] download | |
* | |
* @throws FileSystemException | |
* @throws RuntimeException | |
* @throws WrongUsageException | |
* @throws DownloaderException | |
*/ | |
protected function download(): void | |
{ | |
// Ask which PHP to download | |
$php_ver = select('Which version of php you want to download?', [ | |
'8.2', | |
'8.1', | |
'8.0', | |
'7.4', | |
]); | |
// Choose which to download | |
$download_sources = select('Do you want to download all dependencies?', [ | |
'all' => 'Yes, All (About 50+ sources)', | |
'by-source' => 'No, I want to download sources by source name', | |
'by-ext' => 'No, I want to download sources by required extensions', | |
]); | |
switch ($download_sources) { | |
case 'all': | |
default: | |
$sources = []; | |
break; | |
case 'by-source': | |
// Ask sources to download | |
$sources = multiselect('Which resources are you going to download?', array_keys(Config::getSources()), default: [ | |
'php-src', | |
'pkg-config', | |
'micro', | |
], scroll: 10); | |
break; | |
case 'by-ext': | |
$exts = multiselect('Which extensions are you going to download?', array_keys(Config::getExts()), scroll: 10, validate: fn ($exts) => $this->validateExt($exts)); | |
$confirmed = confirm('Do you want to include suggested libraries and extensions ?'); | |
$sources = ['php-src', 'pkg-config', 'micro']; | |
foreach ($exts as $ext) { | |
$sources = array_merge($sources, $this->getExtSources($ext, suggest: $confirmed)); | |
} | |
break; | |
} | |
$this->output->writeln('<info>You will download: ' . implode(', ', $sources) . '</info>'); | |
$cnt = count($sources); | |
$ni = 0; | |
foreach ($sources as $source) { | |
++$ni; | |
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]"); | |
Downloader::downloadSource($source, Config::getSource($source), options: ['php-ver' => $php_ver]); | |
} | |
$this->output->writeln('<info>Download complete !</info>'); | |
} | |
/** | |
* [STEP] quit | |
*/ | |
private function bye(): void | |
{ | |
$this->output->writeln('<comment>Bye</comment>'); | |
} | |
/** | |
* [VALIDATE] Validate extensions list | |
* | |
* @param array $exts Extensions | |
*/ | |
private function validateExt(array $exts): null|string | |
{ | |
if (in_array('swow', $exts) && in_array('swoole', $exts)) { | |
return 'Cannot compile swow and swoole at the same time!'; | |
} | |
if (empty($exts)) { | |
return 'Please select at least one extension!'; | |
} | |
return null; | |
} | |
/** | |
* @throws WrongUsageException | |
* @throws FileSystemException | |
*/ | |
private function getExtSources(string $ext, int $depth = 0, bool $suggest = true): array | |
{ | |
if ($depth > 30) { | |
throw new \RuntimeException('Too many dependencies or circular dependencies!'); | |
} | |
$sources = []; | |
if (Config::getExt($ext, 'type') === 'external') { | |
$sources[] = Config::getExt($ext, 'source'); | |
} | |
foreach (Config::getExt($ext, 'ext-depends', []) as $ext_depend) { | |
$sources = array_merge($sources, $this->getExtSources($ext_depend, $depth + 1, $suggest)); | |
} | |
if ($suggest) { | |
foreach (Config::getExt($ext, 'ext-suggests', []) as $ext_suggest) { | |
$sources = array_merge($sources, $this->getExtSources($ext_suggest, $depth + 1, $suggest)); | |
} | |
} | |
foreach (Config::getExt($ext, 'lib-depends', []) as $lib_depend) { | |
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest)); | |
} | |
if ($suggest) { | |
foreach (Config::getExt($ext, 'lib-suggests', []) as $lib_depend) { | |
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest)); | |
} | |
} | |
return array_unique($sources); | |
} | |
/** | |
* @throws FileSystemException | |
* @throws WrongUsageException | |
*/ | |
private function getLibSources(string $lib, int $depth = 0, bool $suggest = true): array | |
{ | |
if ($depth > 30) { | |
throw new \RuntimeException('Too many dependencies or circular dependencies!'); | |
} | |
$sources = [Config::getLib($lib, 'source')]; | |
foreach (Config::getLib($lib, 'lib-depends', []) as $lib_depend) { | |
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest)); | |
} | |
if ($suggest) { | |
foreach (Config::getLib($lib, 'lib-suggests', []) as $lib_depend) { | |
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest)); | |
} | |
} | |
return array_unique($sources); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment