mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 04:44:53 +08:00
Move all interactive input to construct
This commit is contained in:
parent
1f768ffc64
commit
77e129881a
@ -443,7 +443,27 @@ trait unix
|
||||
$package->runStage([$this, 'makeForUnix']);
|
||||
|
||||
$package->runStage([$this, 'unixBuildSharedExt']);
|
||||
$package->runStage([$this, 'smokeTestForUnix']);
|
||||
}
|
||||
|
||||
#[Stage('postInstall')]
|
||||
public function postInstall(TargetPackage $package, PackageInstaller $installer): void
|
||||
{
|
||||
if ($package->getName() === 'frankenphp') {
|
||||
$package->runStage([$this, 'smokeTestFrankenphpForUnix']);
|
||||
return;
|
||||
}
|
||||
if ($package->getName() !== 'php') {
|
||||
return;
|
||||
}
|
||||
if (SystemTarget::isUnix()) {
|
||||
if ($installer->interactive) {
|
||||
InteractiveTerm::indicateProgress('Running PHP smoke tests');
|
||||
}
|
||||
$package->runStage([$this, 'smokeTestForUnix']);
|
||||
if ($installer->interactive) {
|
||||
InteractiveTerm::finish('PHP smoke tests passed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -106,7 +106,7 @@ class ArtifactDownloader
|
||||
* no-shallow-clone?: bool
|
||||
* } $options Downloader options
|
||||
*/
|
||||
public function __construct(protected array $options = [])
|
||||
public function __construct(protected array $options = [], public readonly bool $interactive = true)
|
||||
{
|
||||
// Allow setting concurrency via options
|
||||
$this->parallel = max(1, (int) ($options['parallel'] ?? 1));
|
||||
@ -273,12 +273,10 @@ class ArtifactDownloader
|
||||
|
||||
/**
|
||||
* Download all artifacts, with optional parallel processing.
|
||||
*
|
||||
* @param bool $interactive Enable interactive mode with Ctrl+C handling
|
||||
*/
|
||||
public function download(bool $interactive = true): void
|
||||
public function download(): void
|
||||
{
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
Shell::passthruCallback(function () {
|
||||
InteractiveTerm::advance();
|
||||
});
|
||||
@ -311,7 +309,7 @@ class ArtifactDownloader
|
||||
$count = count($this->artifacts);
|
||||
$artifacts_str = implode(',', array_map(fn ($x) => '' . ConsoleColor::yellow($x->getName()), $this->artifacts));
|
||||
// mute the first line if not interactive
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::notice("Downloading {$count} artifacts: {$artifacts_str} ...");
|
||||
}
|
||||
try {
|
||||
@ -329,19 +327,19 @@ class ArtifactDownloader
|
||||
$skipped = [];
|
||||
foreach ($this->artifacts as $artifact) {
|
||||
++$current;
|
||||
if ($this->downloadWithType($artifact, $current, $count, interactive: $interactive) === SPC_DOWNLOAD_STATUS_SKIPPED) {
|
||||
if ($this->downloadWithType($artifact, $current, $count) === SPC_DOWNLOAD_STATUS_SKIPPED) {
|
||||
$skipped[] = $artifact->getName();
|
||||
continue;
|
||||
}
|
||||
$this->_before_files = FileSystem::scanDirFiles(DOWNLOAD_PATH, false, true, true) ?: [];
|
||||
}
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
$skip_msg = !empty($skipped) ? ' (Skipped ' . count($skipped) . ' artifacts for being already downloaded)' : '';
|
||||
InteractiveTerm::success("Downloaded all {$count} artifacts.{$skip_msg}\n", true);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
Shell::passthruCallback(null);
|
||||
keyboard_interrupt_unregister();
|
||||
}
|
||||
@ -537,7 +535,7 @@ class ArtifactDownloader
|
||||
return $dl->checkUpdate($artifact_name, $platform_config, null, $this);
|
||||
}
|
||||
|
||||
private function downloadWithType(Artifact $artifact, int $current, int $total, bool $parallel = false, bool $interactive = true): int
|
||||
private function downloadWithType(Artifact $artifact, int $current, int $total, bool $parallel = false): int
|
||||
{
|
||||
$queue = $this->generateQueue($artifact);
|
||||
// already downloaded
|
||||
@ -558,7 +556,7 @@ class ArtifactDownloader
|
||||
};
|
||||
$try_h = $try ? 'Try downloading' : 'Downloading';
|
||||
logger()->info("{$try_h} artifact '{$artifact->getName()}' {$item['display']} ...");
|
||||
if ($parallel === false && $interactive) {
|
||||
if ($parallel === false && $this->interactive) {
|
||||
InteractiveTerm::indicateProgress("[{$current}/{$total}] Downloading artifact " . ConsoleColor::green($artifact->getName()) . " {$item['display']} from {$type_display_name} ...");
|
||||
}
|
||||
// is valid download type
|
||||
@ -597,13 +595,13 @@ class ArtifactDownloader
|
||||
}
|
||||
// process lock
|
||||
ApplicationContext::get(ArtifactCache::class)->lock($artifact, $item['lock'], $lock, SystemTarget::getCurrentPlatformString());
|
||||
if ($parallel === false && $interactive) {
|
||||
if ($parallel === false && $this->interactive) {
|
||||
$ver = $lock->hasVersion() ? (' (' . ConsoleColor::yellow($lock->version) . ')') : '';
|
||||
InteractiveTerm::finish('Downloaded ' . ($verified ? 'and verified ' : '') . 'artifact ' . ConsoleColor::green($artifact->getName()) . $ver . " {$item['display']} .");
|
||||
}
|
||||
return SPC_DOWNLOAD_STATUS_SUCCESS;
|
||||
} catch (DownloaderException|ExecutionException $e) {
|
||||
if ($parallel === false && $interactive) {
|
||||
if ($parallel === false && $this->interactive) {
|
||||
InteractiveTerm::finish("Download artifact {$artifact->getName()} {$item['display']} failed !", false);
|
||||
InteractiveTerm::error("Failed message: {$e->getMessage()}", true);
|
||||
}
|
||||
|
||||
@ -34,9 +34,9 @@ class InstallPackageCommand extends BaseCommand
|
||||
public function handle(): int
|
||||
{
|
||||
ApplicationContext::set('elephant', true);
|
||||
$installer = new PackageInstaller([...$this->input->getOptions(), 'dl-prefer-binary' => true]);
|
||||
$installer = new PackageInstaller([...$this->input->getOptions(), 'dl-prefer-binary' => true], false);
|
||||
$installer->addInstallPackage($this->input->getArgument('package'));
|
||||
$installer->run(true, true);
|
||||
$installer->run(true);
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ class ConfigValidator
|
||||
public const array PACKAGE_FIELD_TYPES = [
|
||||
// package fields
|
||||
'type' => ConfigType::STRING,
|
||||
'description' => ConfigType::STRING,
|
||||
'depends' => ConfigType::LIST_ARRAY, // @
|
||||
'suggests' => ConfigType::LIST_ARRAY, // @
|
||||
'artifact' => [self::class, 'validateArtifactField'], // STRING or OBJECT
|
||||
@ -43,6 +44,7 @@ class ConfigValidator
|
||||
|
||||
public const array PACKAGE_FIELDS = [
|
||||
'type' => true,
|
||||
'description' => false,
|
||||
'depends' => false, // @
|
||||
'suggests' => false, // @
|
||||
'artifact' => false,
|
||||
|
||||
@ -18,7 +18,7 @@ use function Laravel\Prompts\confirm;
|
||||
|
||||
readonly class Doctor
|
||||
{
|
||||
public function __construct(private ?OutputInterface $output = null, private int $auto_fix = FIX_POLICY_PROMPT)
|
||||
public function __construct(private ?OutputInterface $output = null, private int $auto_fix = FIX_POLICY_PROMPT, public readonly bool $interactive = true)
|
||||
{
|
||||
// debug shows all loaded doctor items
|
||||
$items = DoctorLoader::getDoctorItems();
|
||||
@ -53,13 +53,13 @@ readonly class Doctor
|
||||
* Check all valid check items.
|
||||
* @return bool true if all checks passed, false otherwise
|
||||
*/
|
||||
public function checkAll(bool $interactive = true): bool
|
||||
public function checkAll(): bool
|
||||
{
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::notice('Starting doctor checks ...');
|
||||
}
|
||||
foreach ($this->getValidCheckList() as $check) {
|
||||
if (!$this->checkItem($check, $interactive)) {
|
||||
if (!$this->checkItem($check)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -72,7 +72,7 @@ readonly class Doctor
|
||||
* @param CheckItem|string $check The check item to be checked
|
||||
* @return bool True if the check passed or was fixed, false otherwise
|
||||
*/
|
||||
public function checkItem(CheckItem|string $check, bool $interactive = true): bool
|
||||
public function checkItem(CheckItem|string $check): bool
|
||||
{
|
||||
if (is_string($check)) {
|
||||
$found = null;
|
||||
@ -88,7 +88,7 @@ readonly class Doctor
|
||||
}
|
||||
$check = $found;
|
||||
}
|
||||
$prepend = $interactive ? ' - ' : '';
|
||||
$prepend = $this->interactive ? ' - ' : '';
|
||||
$this->output?->write("{$prepend}Checking <comment>{$check->item_name}</comment> ... ");
|
||||
|
||||
// call check
|
||||
|
||||
@ -59,8 +59,8 @@ class LinuxMuslCheck
|
||||
#[FixItem('fix-musl-wrapper')]
|
||||
public function fixMusl(): bool
|
||||
{
|
||||
$downloader = new ArtifactDownloader();
|
||||
$downloader->add('musl-wrapper')->download(false);
|
||||
$downloader = new ArtifactDownloader(interactive: false);
|
||||
$downloader->add('musl-wrapper')->download();
|
||||
$extractor = new ArtifactExtractor(ApplicationContext::get(ArtifactCache::class));
|
||||
$extractor->extract('musl-wrapper');
|
||||
|
||||
@ -96,9 +96,9 @@ class LinuxMuslCheck
|
||||
Shell::passthruCallback(function () {
|
||||
InteractiveTerm::advance();
|
||||
});
|
||||
$downloader = new ArtifactDownloader();
|
||||
$downloader = new ArtifactDownloader(interactive: false);
|
||||
$extractor = new ArtifactExtractor(ApplicationContext::get(ArtifactCache::class));
|
||||
$downloader->add('musl-toolchain')->download(false);
|
||||
$downloader->add('musl-toolchain')->download();
|
||||
$extractor->extract('musl-toolchain');
|
||||
$pkg_root = PKG_ROOT_PATH . '/musl-toolchain';
|
||||
f_passthru("{$prefix}cp -rf {$pkg_root}/* /usr/local/musl");
|
||||
|
||||
@ -45,9 +45,9 @@ class PkgConfigCheck
|
||||
public function fix(): bool
|
||||
{
|
||||
ApplicationContext::set('elephant', true);
|
||||
$installer = new PackageInstaller(['dl-binary-only' => true]);
|
||||
$installer = new PackageInstaller(['dl-binary-only' => true], interactive: false);
|
||||
$installer->addInstallPackage('pkg-config');
|
||||
$installer->run(false, true);
|
||||
$installer->run(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,9 +30,9 @@ class Re2cVersionCheck
|
||||
#[FixItem('build-re2c')]
|
||||
public function buildRe2c(): bool
|
||||
{
|
||||
$installer = new PackageInstaller();
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('re2c');
|
||||
$installer->run(false);
|
||||
$installer->run(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ class WindowsToolCheck
|
||||
{
|
||||
$installer = new PackageInstaller();
|
||||
$installer->addInstallPackage('strawberry-perl');
|
||||
$installer->run(false);
|
||||
$installer->run(true);
|
||||
GlobalEnvManager::addPathIfNotExists(PKG_ROOT_PATH . '\strawberry-perl');
|
||||
return true;
|
||||
}
|
||||
@ -116,27 +116,27 @@ class WindowsToolCheck
|
||||
public function installSDK(): bool
|
||||
{
|
||||
FileSystem::removeDir(getenv('PHP_SDK_PATH'));
|
||||
$installer = new PackageInstaller();
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('php-sdk-binary-tools');
|
||||
$installer->run(false);
|
||||
$installer->run(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
#[FixItem('install-nasm')]
|
||||
public function installNasm(): bool
|
||||
{
|
||||
$installer = new PackageInstaller();
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('nasm');
|
||||
$installer->run(false);
|
||||
$installer->run(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
#[FixItem('install-vswhere')]
|
||||
public function installVSWhere(): bool
|
||||
{
|
||||
$installer = new PackageInstaller();
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('vswhere');
|
||||
$installer->run(false);
|
||||
$installer->run(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,9 +34,9 @@ class ZigCheck
|
||||
#[FixItem('install-zig')]
|
||||
public function installZig(): bool
|
||||
{
|
||||
$installer = new PackageInstaller();
|
||||
$installer = new PackageInstaller(interactive: false);
|
||||
$installer->addInstallPackage('zig');
|
||||
$installer->run(false);
|
||||
$installer->run(true);
|
||||
return $installer->isPackageInstalled('zig');
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ class PackageInstaller
|
||||
/** @var null|BuildRootTracker buildroot file tracker for debugging purpose */
|
||||
protected ?BuildRootTracker $tracker = null;
|
||||
|
||||
public function __construct(protected array $options = [])
|
||||
public function __construct(protected array $options = [], public readonly bool $interactive = true)
|
||||
{
|
||||
ApplicationContext::set(PackageInstaller::class, $this);
|
||||
$builder = new PackageBuilder($options);
|
||||
@ -143,7 +143,7 @@ class PackageInstaller
|
||||
/**
|
||||
* Run the package installation process.
|
||||
*/
|
||||
public function run(bool $interactive = true, bool $disable_delay_msg = false): void
|
||||
public function run(bool $disable_delay_msg = false): void
|
||||
{
|
||||
// apply build toolchain envs
|
||||
GlobalEnvManager::afterInit();
|
||||
@ -153,7 +153,7 @@ class PackageInstaller
|
||||
$this->resolvePackages();
|
||||
}
|
||||
|
||||
if ($interactive && !$disable_delay_msg) {
|
||||
if ($this->interactive && !$disable_delay_msg) {
|
||||
// show install or build options in terminal with beautiful output
|
||||
$this->printInstallerInfo();
|
||||
|
||||
@ -167,14 +167,17 @@ class PackageInstaller
|
||||
// check download
|
||||
if ($this->download) {
|
||||
$downloaderOptions = DownloaderOptions::extractFromConsoleOptions($this->options, 'dl');
|
||||
$downloader = new ArtifactDownloader([...$downloaderOptions, 'source-only' => implode(',', array_map(fn ($x) => $x->getName(), $this->build_packages))]);
|
||||
$downloader->addArtifacts($this->getArtifacts())->download($interactive);
|
||||
$downloader = new ArtifactDownloader(
|
||||
[...$downloaderOptions, 'source-only' => implode(',', array_map(fn ($x) => $x->getName(), $this->build_packages))],
|
||||
$this->interactive
|
||||
);
|
||||
$downloader->addArtifacts($this->getArtifacts())->download();
|
||||
} else {
|
||||
logger()->notice('Skipping download (--no-download option enabled)');
|
||||
}
|
||||
|
||||
// extract sources
|
||||
$this->extractSourceArtifacts(interactive: $interactive);
|
||||
$this->extractSourceArtifacts();
|
||||
|
||||
// validate packages
|
||||
foreach ($this->packages as $package) {
|
||||
@ -183,7 +186,7 @@ class PackageInstaller
|
||||
}
|
||||
|
||||
// build/install packages
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::notice('Building/Installing packages ...');
|
||||
keyboard_interrupt_register(function () {
|
||||
InteractiveTerm::finish('Build/Install process interrupted by user!', false);
|
||||
@ -198,7 +201,7 @@ class PackageInstaller
|
||||
$has_source = $package->hasSource();
|
||||
if (!$is_to_build && $should_use_binary) {
|
||||
// install binary
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::indicateProgress('Installing package: ' . ConsoleColor::yellow($package->getName()));
|
||||
}
|
||||
try {
|
||||
@ -210,17 +213,17 @@ class PackageInstaller
|
||||
} catch (\Throwable $e) {
|
||||
// Stop tracking on error
|
||||
$this->tracker?->stopTracking();
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::finish('Installing binary package failed: ' . ConsoleColor::red($package->getName()), false);
|
||||
echo PHP_EOL;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::finish('Installed binary package: ' . ConsoleColor::green($package->getName()) . ($status === SPC_STATUS_ALREADY_INSTALLED ? ' (already installed, skipped)' : ''));
|
||||
}
|
||||
} elseif ($is_to_build && $has_build_stage || $has_source && $has_build_stage) {
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::indicateProgress('Building package: ' . ConsoleColor::yellow($package->getName()));
|
||||
}
|
||||
try {
|
||||
@ -243,22 +246,20 @@ class PackageInstaller
|
||||
} catch (\Throwable $e) {
|
||||
// Stop tracking on error
|
||||
$this->tracker?->stopTracking();
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::finish('Building package failed: ' . ConsoleColor::red($package->getName()), false);
|
||||
echo PHP_EOL;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::finish('Built package: ' . ConsoleColor::green($package->getName()) . ($status === SPC_STATUS_ALREADY_BUILT ? ' (already built, skipped)' : ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->dumpLicenseFiles($this->packages);
|
||||
if ($interactive) {
|
||||
InteractiveTerm::success('Exported package licenses', true);
|
||||
}
|
||||
// perform after-install actions and emit post-install events
|
||||
$this->emitPostInstallEvents();
|
||||
}
|
||||
|
||||
public function isBuildPackage(Package|string $package): bool
|
||||
@ -311,6 +312,17 @@ class PackageInstaller
|
||||
return false;
|
||||
}
|
||||
|
||||
public function emitPostInstallEvents(): void
|
||||
{
|
||||
foreach ($this->packages as $package) {
|
||||
if ($package->hasStage('postInstall')) {
|
||||
$package->runStage('postInstall');
|
||||
}
|
||||
}
|
||||
|
||||
$this->dumpLicenseFiles($this->packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the download status of all artifacts for the resolved packages.
|
||||
*
|
||||
@ -368,7 +380,7 @@ class PackageInstaller
|
||||
/**
|
||||
* Extract all artifacts for resolved packages.
|
||||
*/
|
||||
public function extractSourceArtifacts(bool $interactive = true): void
|
||||
public function extractSourceArtifacts(): void
|
||||
{
|
||||
FileSystem::createDir(SOURCE_PATH);
|
||||
$packages = array_values($this->packages);
|
||||
@ -403,7 +415,7 @@ class PackageInstaller
|
||||
}
|
||||
|
||||
// Extract each artifact
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::notice('Extracting source for ' . count($artifacts) . ' artifacts: ' . implode(',', array_map(fn ($x) => ConsoleColor::yellow($x->getName()), $artifacts)) . ' ...');
|
||||
InteractiveTerm::indicateProgress('Extracting artifacts');
|
||||
}
|
||||
@ -411,7 +423,7 @@ class PackageInstaller
|
||||
try {
|
||||
V2CompatLayer::beforeExtsExtractHook();
|
||||
foreach ($artifacts as $artifact) {
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::setMessage('Extracting source: ' . ConsoleColor::green($artifact->getName()));
|
||||
}
|
||||
if (($pkg = array_search($artifact->getName(), $pkg_artifact_map, true)) !== false) {
|
||||
@ -423,12 +435,12 @@ class PackageInstaller
|
||||
}
|
||||
}
|
||||
V2CompatLayer::afterExtsExtractHook();
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::finish('Extracted all sources successfully.');
|
||||
echo PHP_EOL;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
if ($interactive) {
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::finish('Artifact extraction failed!', false);
|
||||
echo PHP_EOL;
|
||||
}
|
||||
@ -525,6 +537,9 @@ class PackageInstaller
|
||||
}
|
||||
}
|
||||
$dumper->dump(BUILD_ROOT_PATH . '/license');
|
||||
if ($this->interactive) {
|
||||
InteractiveTerm::success('Exported package licenses', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user