mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 04:44:53 +08:00
Add lib skeleton command and sort config, spc_mode suuport, etc...
This commit is contained in:
parent
1707c679e8
commit
dd5762fbd3
@ -1,6 +1,7 @@
|
|||||||
parameters:
|
parameters:
|
||||||
reportUnmatchedIgnoredErrors: false
|
reportUnmatchedIgnoredErrors: false
|
||||||
level: 4
|
level: 4
|
||||||
|
phpVersion: 80400
|
||||||
paths:
|
paths:
|
||||||
- ./src/
|
- ./src/
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use StaticPHP\Skeleton\ArtifactGenerator;
|
declare(strict_types=1);
|
||||||
use StaticPHP\Skeleton\PackageGenerator;
|
|
||||||
|
|
||||||
|
use StaticPHP\Runtime\Executor\UnixCMakeExecutor;
|
||||||
|
use StaticPHP\Skeleton\ArtifactGenerator;
|
||||||
|
use StaticPHP\Skeleton\ExecutorGenerator;
|
||||||
|
use StaticPHP\Skeleton\PackageGenerator;
|
||||||
|
|
||||||
require_once 'vendor/autoload.php';
|
require_once 'vendor/autoload.php';
|
||||||
|
|
||||||
@ -10,12 +13,16 @@ $package_generator = new PackageGenerator('foo', 'library')
|
|||||||
->addDependency('bar')
|
->addDependency('bar')
|
||||||
->addStaticLib('libfoo.a', 'unix')
|
->addStaticLib('libfoo.a', 'unix')
|
||||||
->addStaticLib('libfoo.a', 'unix')
|
->addStaticLib('libfoo.a', 'unix')
|
||||||
->addArtifact($artifact_generator = new ArtifactGenerator('foo')->setSource(['type' => 'url', 'url' => 'https://example.com/foo.tar.gz']));
|
->addArtifact($artifact_generator = new ArtifactGenerator('foo')->setSource(['type' => 'url', 'url' => 'https://example.com/foo.tar.gz']))
|
||||||
|
->enableBuild(['Darwin', 'Linux'], 'build')
|
||||||
|
->addFunctionExecutorBinding('build', new ExecutorGenerator(UnixCMakeExecutor::class));
|
||||||
|
|
||||||
$pkg_config = $package_generator->generateConfig();
|
$pkg_config = $package_generator->generateConfigArray();
|
||||||
$artifact_config = $artifact_generator->generateConfig();
|
$artifact_config = $artifact_generator->generateConfigArray();
|
||||||
|
|
||||||
echo "===== pkg.json =====" . PHP_EOL;
|
echo '===== pkg.json =====' . PHP_EOL;
|
||||||
echo json_encode($pkg_config, 64|128|256) . PHP_EOL;
|
echo json_encode($pkg_config, 64 | 128 | 256) . PHP_EOL;
|
||||||
echo "===== artifact.json =====" . PHP_EOL;
|
echo '===== artifact.json =====' . PHP_EOL;
|
||||||
echo json_encode($artifact_config, 64|128|256) . PHP_EOL;
|
echo json_encode($artifact_config, 64 | 128 | 256) . PHP_EOL;
|
||||||
|
echo '===== php code for package =====' . PHP_EOL;
|
||||||
|
echo $package_generator->generatePackageClassFile('Package\Library');
|
||||||
|
|||||||
@ -8,7 +8,6 @@ use StaticPHP\Artifact\ArtifactDownloader;
|
|||||||
use StaticPHP\Artifact\Downloader\DownloadResult;
|
use StaticPHP\Artifact\Downloader\DownloadResult;
|
||||||
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
|
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
|
||||||
use StaticPHP\Attribute\Artifact\CustomBinary;
|
use StaticPHP\Attribute\Artifact\CustomBinary;
|
||||||
use StaticPHP\Attribute\Artifact\CustomSource;
|
|
||||||
use StaticPHP\Exception\DownloaderException;
|
use StaticPHP\Exception\DownloaderException;
|
||||||
use StaticPHP\Runtime\SystemTarget;
|
use StaticPHP\Runtime\SystemTarget;
|
||||||
|
|
||||||
|
|||||||
@ -329,8 +329,7 @@ class ArtifactDownloader
|
|||||||
}
|
}
|
||||||
if ($interactive) {
|
if ($interactive) {
|
||||||
$skip_msg = !empty($skipped) ? ' (Skipped ' . count($skipped) . ' artifacts for being already downloaded)' : '';
|
$skip_msg = !empty($skipped) ? ' (Skipped ' . count($skipped) . ' artifacts for being already downloaded)' : '';
|
||||||
InteractiveTerm::success("Downloaded all {$count} artifacts.{$skip_msg}", true);
|
InteractiveTerm::success("Downloaded all {$count} artifacts.{$skip_msg}\n", true);
|
||||||
echo PHP_EOL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SPCException $e) {
|
} catch (SPCException $e) {
|
||||||
|
|||||||
@ -23,7 +23,6 @@ abstract class BaseCommand extends Command
|
|||||||
\___ \| __/ _` | __| |/ __| |_) | |_| | |_) |
|
\___ \| __/ _` | __| |/ __| |_) | |_| | |_) |
|
||||||
___) | || (_| | |_| | (__| __/| _ | __/
|
___) | || (_| | |_| | (__| __/| _ | __/
|
||||||
|____/ \__\__,_|\__|_|\___|_| |_| |_|_| {version}
|
|____/ \__\__,_|\__|_|\___|_| |_| |_|_| {version}
|
||||||
|
|
||||||
';
|
';
|
||||||
|
|
||||||
protected bool $no_motd = false;
|
protected bool $no_motd = false;
|
||||||
@ -71,7 +70,7 @@ abstract class BaseCommand extends Command
|
|||||||
$version = $this->getVersionWithCommit();
|
$version = $this->getVersionWithCommit();
|
||||||
if (!$this->no_motd) {
|
if (!$this->no_motd) {
|
||||||
$str = str_replace('{version}', '' . ConsoleColor::none("v{$version}"), '' . ConsoleColor::magenta(self::$motd));
|
$str = str_replace('{version}', '' . ConsoleColor::none("v{$version}"), '' . ConsoleColor::magenta(self::$motd));
|
||||||
echo $this->input->getOption('no-ansi') ? strip_ansi_colors($str) : $str;
|
$this->output->writeln($this->input->getOption('no-ansi') ? strip_ansi_colors($str) : $str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
402
src/StaticPHP/Command/Dev/SkeletonCommand.php
Normal file
402
src/StaticPHP/Command/Dev/SkeletonCommand.php
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace StaticPHP\Command\Dev;
|
||||||
|
|
||||||
|
use StaticPHP\Command\BaseCommand;
|
||||||
|
use StaticPHP\Exception\WrongUsageException;
|
||||||
|
use StaticPHP\Registry\Registry;
|
||||||
|
use StaticPHP\Runtime\SystemTarget;
|
||||||
|
use StaticPHP\Skeleton\ArtifactGenerator;
|
||||||
|
use StaticPHP\Skeleton\PackageGenerator;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
use function Laravel\Prompts\select;
|
||||||
|
use function Laravel\Prompts\text;
|
||||||
|
|
||||||
|
#[AsCommand('dev:skel', 'Generate a skeleton for a StaticPHP package')]
|
||||||
|
class SkeletonCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
// Only available for non-Windows systems for now
|
||||||
|
// Only available when spc loading mode is SPC_MODE_VENDOR, SPC_MODE_SOURCE
|
||||||
|
if (spc_mode(SPC_MODE_PHAR)) {
|
||||||
|
$this->output->writeln('<error>The dev:skel command is not available in phar mode.</error>');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (SystemTarget::getTargetOS() === 'Windows') {
|
||||||
|
$this->output->writeln('<error>The dev:skel command is not available on Windows systems.</error>');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->runMainMenu();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validatePackageName(string $name): ?string
|
||||||
|
{
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $name)) {
|
||||||
|
return 'Library name can only contain letters, numbers, underscores, and hyphens.';
|
||||||
|
}
|
||||||
|
// must start with a letter
|
||||||
|
if (!preg_match('/^[a-zA-Z]/', $name)) {
|
||||||
|
return 'Library name must start with a letter.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runMainMenu(): void
|
||||||
|
{
|
||||||
|
$main = select('Please select the skeleton option', [
|
||||||
|
'library' => 'Create a new library package',
|
||||||
|
'target' => 'Create a new target package',
|
||||||
|
'php-extension' => 'Create a new PHP extension',
|
||||||
|
'q' => 'Exit',
|
||||||
|
]);
|
||||||
|
$generator = match ($main) {
|
||||||
|
'library' => $this->runCreateLib(),
|
||||||
|
'target' => $this->runCreateTarget(),
|
||||||
|
'php-extension' => $this->runCreateExt(),
|
||||||
|
'q' => exit(0),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
$write = $generator->writeAll();
|
||||||
|
$this->output->writeln("<info>Package config in: {$write['package_config']}</info>");
|
||||||
|
$this->output->writeln("<info>Artifact config in: {$write['artifact_config']}</info>");
|
||||||
|
$this->output->writeln('<comment>Package class:</comment>');
|
||||||
|
$this->output->writeln($write['package_class_content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runCreateLib(): PackageGenerator
|
||||||
|
{
|
||||||
|
// init empty
|
||||||
|
$static_libs = '';
|
||||||
|
$headers = '';
|
||||||
|
$static_bins = '';
|
||||||
|
$pkg_configs = '';
|
||||||
|
|
||||||
|
// ask name
|
||||||
|
$package_name = text('Please enter your library name', placeholder: 'e.g. pcre2', validate: [$this, 'validatePackageName']);
|
||||||
|
|
||||||
|
// ask OS
|
||||||
|
$os = select("[{$package_name}] On which OS family do you want to build this library?", [
|
||||||
|
'unix' => 'Both Linux and Darwin (unix-like OS)',
|
||||||
|
'linux' => 'Linux only',
|
||||||
|
'macos' => 'Darwin(macOS) only',
|
||||||
|
'windows' => 'Windows only',
|
||||||
|
'all' => 'All supported OS (' . implode(', ', SUPPORTED_OS_FAMILY) . ')',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$produce = select("[{$package_name}] What does this library produce?", [
|
||||||
|
'static_libs' => 'Static Libraries (.a/.lib)',
|
||||||
|
'headers' => 'Header Files (.h)',
|
||||||
|
'static_bins' => 'Static Binaries (executables)',
|
||||||
|
'pkg_configs' => 'Pkg-Config files (.pc)',
|
||||||
|
'all' => 'All of the above',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($produce === 'all' || $produce === 'static_libs') {
|
||||||
|
$static_libs = text(
|
||||||
|
'Please enter the names of the static libraries produced',
|
||||||
|
placeholder: 'e.g. libpcre2.a, libbar.a',
|
||||||
|
default: str_starts_with($package_name, 'lib') ? "{$package_name}.a" : "lib{$package_name}.a",
|
||||||
|
validate: function ($value) {
|
||||||
|
$names = array_map('trim', explode(',', $value));
|
||||||
|
if (array_any($names, fn ($name) => !preg_match('/^[a-zA-Z0-9_.-]+$/', $name))) {
|
||||||
|
return 'Library names can only contain letters, numbers, underscores, hyphens, and dots.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hint: 'Separate multiple names with commas'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($produce === 'all' || $produce === 'headers') {
|
||||||
|
$headers = text(
|
||||||
|
'Please enter the names of the header files produced',
|
||||||
|
placeholder: 'e.g. foo.h, bar.h',
|
||||||
|
default: str_starts_with($package_name, 'lib') ? str_replace('lib', '', $package_name) . '.h' : $package_name . '.h',
|
||||||
|
validate: function ($value) {
|
||||||
|
$names = array_map('trim', explode(',', $value));
|
||||||
|
if (array_any($names, fn ($name) => !preg_match('/^[a-zA-Z0-9_.-]+$/', $name))) {
|
||||||
|
return 'Header file names can only contain letters, numbers, underscores, hyphens, and dots.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hint: 'Separate multiple names with commas, directories are allowed (e.g. openssl directory)'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($produce === 'all' || $produce === 'static_bins') {
|
||||||
|
$static_bins = text(
|
||||||
|
'Please enter the names of the static binaries produced',
|
||||||
|
placeholder: 'e.g. foo, bar',
|
||||||
|
default: $package_name,
|
||||||
|
validate: function ($value) {
|
||||||
|
$names = array_map('trim', explode(',', $value));
|
||||||
|
if (array_any($names, fn ($name) => !preg_match('/^[a-zA-Z0-9_.-]+$/', $name))) {
|
||||||
|
return 'Binary names can only contain letters, numbers, underscores, hyphens, and dots.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hint: 'Separate multiple names with commas'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($produce === 'all' || $produce === 'pkg_configs') {
|
||||||
|
$pkg_configs = text(
|
||||||
|
'Please enter the names of the pkg-config files produced',
|
||||||
|
placeholder: 'e.g. foo.pc, bar.pc',
|
||||||
|
default: str_starts_with($package_name, 'lib') ? str_replace('lib', '', $package_name) . '.pc' : $package_name . '.pc',
|
||||||
|
validate: function ($value) {
|
||||||
|
if (!str_ends_with($value, '.pc')) {
|
||||||
|
return 'Pkg-config file names must end with .pc extension.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hint: 'Separate multiple names with commas'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($headers === '' && $static_bins === '' && $static_libs === '' && $pkg_configs === '') {
|
||||||
|
$this->output->writeln('<error>You must specify at least one of static libraries, header files, or static binaries produced.</error>');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask source
|
||||||
|
$artifact_generator = $this->runCreateArtifact($package_name, true, false, null);
|
||||||
|
$package_generator = new PackageGenerator($package_name, 'library');
|
||||||
|
// set artifact
|
||||||
|
$package_generator = $package_generator->addArtifact($artifact_generator);
|
||||||
|
// set os
|
||||||
|
$package_generator = match ($os) {
|
||||||
|
'unix' => $package_generator->enableBuild(['Darwin', 'Linux'], 'build'),
|
||||||
|
'linux' => $package_generator->enableBuild(['Linux'], 'build'),
|
||||||
|
'macos' => $package_generator->enableBuild(['Darwin'], 'build'),
|
||||||
|
'windows' => $package_generator->enableBuild(['Windows'], 'build'),
|
||||||
|
'all' => $package_generator->enableBuild(SUPPORTED_OS_FAMILY, 'build'),
|
||||||
|
default => $package_generator,
|
||||||
|
};
|
||||||
|
// set produce
|
||||||
|
if ($static_libs !== '') {
|
||||||
|
$lib_names = array_map('trim', explode(',', $static_libs));
|
||||||
|
foreach ($lib_names as $lib_name) {
|
||||||
|
$package_generator = $package_generator->addStaticLib($lib_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($headers !== '') {
|
||||||
|
$header_names = array_map('trim', explode(',', $headers));
|
||||||
|
foreach ($header_names as $header_name) {
|
||||||
|
$package_generator = $package_generator->addHeaderFile($header_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($static_bins !== '') {
|
||||||
|
$bin_names = array_map('trim', explode(',', $static_bins));
|
||||||
|
foreach ($bin_names as $bin_name) {
|
||||||
|
$package_generator = $package_generator->addStaticBin($bin_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($pkg_configs !== '') {
|
||||||
|
$pc_names = array_map('trim', explode(',', $pkg_configs));
|
||||||
|
foreach ($pc_names as $pc_name) {
|
||||||
|
$package_generator = $package_generator->addPkgConfigFile($pc_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ask for package config writing selection, same as artifact
|
||||||
|
$package_configs = Registry::getLoadedPackageConfigs();
|
||||||
|
$package_config_file = select("[{$package_name}] Please select the package config file to write the package config to", $package_configs);
|
||||||
|
return $package_generator->setConfigFile($package_config_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runCreateArtifact(
|
||||||
|
string $package_name,
|
||||||
|
?bool $create_source,
|
||||||
|
?bool $create_binary,
|
||||||
|
string|true|null $default_extract_dir = true
|
||||||
|
): ArtifactGenerator {
|
||||||
|
$artifact = new ArtifactGenerator($package_name);
|
||||||
|
|
||||||
|
if ($create_source === null) {
|
||||||
|
$create_source = confirm("[{$package_name}] Do you want to create a source artifact?");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$create_source) {
|
||||||
|
goto binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
$source_type = select("[{$package_name}] Where is the source code located?", SPC_DOWNLOAD_TYPE_DISPLAY_NAME);
|
||||||
|
|
||||||
|
$source_config = $this->askDownloadTypeConfig($package_name, $source_type, $default_extract_dir, 'source');
|
||||||
|
$artifact = $artifact->setSource($source_config);
|
||||||
|
|
||||||
|
binary:
|
||||||
|
if ($create_binary === null) {
|
||||||
|
$create_binary = confirm("[{$package_name}] Do you want to create a binary artifact?");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$create_binary) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
$binary_fix = [
|
||||||
|
'macos-x86_64' => null,
|
||||||
|
'macos-aarch64' => null,
|
||||||
|
'linux-x86_64' => null,
|
||||||
|
'linux-aarch64' => null,
|
||||||
|
'windows-x86_64' => null,
|
||||||
|
];
|
||||||
|
while (($os = select("[{$package_name}] Please configure the binary downloading options for OS", [
|
||||||
|
'macos-x86_64' => 'macos-x86_64' . ($binary_fix['macos-x86_64'] ? ' (done)' : ''),
|
||||||
|
'macos-aarch64' => 'macos-aarch64' . ($binary_fix['macos-aarch64'] ? ' (done)' : ''),
|
||||||
|
'linux-x86_64' => 'linux-x86_64' . ($binary_fix['linux-x86_64'] ? ' (done)' : ''),
|
||||||
|
'linux-aarch64' => 'linux-aarch64' . ($binary_fix['linux-aarch64'] ? ' (done)' : ''),
|
||||||
|
'windows-x86_64' => 'windows-x86_64' . ($binary_fix['windows-x86_64'] ? ' (done)' : ''),
|
||||||
|
'copy' => 'Duplicate from another OS',
|
||||||
|
'finish' => 'Submit',
|
||||||
|
])) !== 'finish') {
|
||||||
|
$source_type = select("[{$package_name}] Where is the binary for {$os} located?", SPC_DOWNLOAD_TYPE_DISPLAY_NAME);
|
||||||
|
$source_config = $this->askDownloadTypeConfig($package_name, $source_type, $default_extract_dir, 'binary');
|
||||||
|
// set to artifact
|
||||||
|
$artifact = $artifact->setBinary($os, $source_config);
|
||||||
|
$binary_fix[$os] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
|
// generate config files, select existing package config file to write
|
||||||
|
$artifact_configs = Registry::getLoadedArtifactConfigs();
|
||||||
|
$artifact_config_file = select("[{$package_name}] Please select the artifact config file to write the artifact config to", $artifact_configs);
|
||||||
|
return $artifact->setConfigFile($artifact_config_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runCreateTarget(): PackageGenerator
|
||||||
|
{
|
||||||
|
throw new WrongUsageException('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runCreateExt(): PackageGenerator
|
||||||
|
{
|
||||||
|
throw new WrongUsageException('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function askDownloadTypeConfig(string $package_name, int|string $source_type, bool|string|null $default_extract_dir, string $config_type): array
|
||||||
|
{
|
||||||
|
$source_config = ['type' => $source_type];
|
||||||
|
switch ($source_type) {
|
||||||
|
case 'bitbuckettag':
|
||||||
|
$source_config['repo'] = text("[{$package_name}] Please enter the BitBucket repository (e.g. user/repo)");
|
||||||
|
break;
|
||||||
|
case 'filelist':
|
||||||
|
$source_config['url'] = text(
|
||||||
|
"[{$package_name}] Please enter the file index website URL",
|
||||||
|
placeholder: 'e.g. https://ftp.gnu.org/pub/gnu/gettext/',
|
||||||
|
hint: 'Make sure the target url is a directory listing page like ftp.gnu.org.'
|
||||||
|
);
|
||||||
|
$source_config['regex'] = text(
|
||||||
|
"[{$package_name}] Please enter the regex pattern to match the archive file",
|
||||||
|
placeholder: 'e.g. /gettext-(\d+\.\d+(\.\d+)?)\.tar\.gz/',
|
||||||
|
default: "/href=\"(?<file>{$package_name}-(?<version>[^\"]+)\\.tar\\.gz)\"/",
|
||||||
|
hint: 'Make sure the regex contains a capturing group for the version number.'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'git':
|
||||||
|
$source_config['url'] = text(
|
||||||
|
"[{$package_name}] Please enter the Git repository URL",
|
||||||
|
validate: function ($value) {
|
||||||
|
if (!filter_var($value, FILTER_VALIDATE_URL) && !preg_match('/^(git|ssh|http|https|git@[-\w.]+):(\/\/)?(.*?)(\.git)(\/?|#[-\d\w._]+?)$/', $value)) {
|
||||||
|
return 'Please enter a valid Git repository URL.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hint: 'e.g. https://github.com/user/repo.git'
|
||||||
|
);
|
||||||
|
$source_config['rev'] = text(
|
||||||
|
"[{$package_name}] Please enter the Git revision (branch, tag, or commit hash)",
|
||||||
|
default: 'main',
|
||||||
|
hint: 'e.g. main, master, v1.0.0, or a commit hash'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'ghrel':
|
||||||
|
$source_config['repo'] = text("[{$package_name}] Please enter the GitHub repository (e.g. user/repo)");
|
||||||
|
$source_config['match'] = text(
|
||||||
|
"[{$package_name}] Please enter the regex pattern to match the source archive file",
|
||||||
|
placeholder: 'e.g. /foo-(\d+\.\d+(\.\d+)?)\.tar\.gz/',
|
||||||
|
default: "{$package_name}-.+\\.tar\\.gz",
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'ghtar':
|
||||||
|
case 'ghtagtar':
|
||||||
|
$source_config['repo'] = text("[{$package_name}] Please enter the GitHub repository (e.g. user/repo)");
|
||||||
|
$source_config['prefer-stable'] = confirm("[{$package_name}] Do you want to prefer stable releases?");
|
||||||
|
if ($source_type === 'ghtagtar' && confirm('Do you want to match tags with a specific pattern?', default: false)) {
|
||||||
|
$source_config['match'] = text(
|
||||||
|
"[{$package_name}] Please enter the regex pattern to match tags",
|
||||||
|
placeholder: 'e.g. v(\d+\.\d+(\.\d+)?)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'local':
|
||||||
|
$source_config['dirname'] = text(
|
||||||
|
"[{$package_name}] Please enter the local directory path",
|
||||||
|
validate: function ($value) {
|
||||||
|
if (trim($value) === '') {
|
||||||
|
return 'Local source directory cannot be empty.';
|
||||||
|
}
|
||||||
|
if (!is_dir($value)) {
|
||||||
|
return 'The specified local source directory does not exist.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'pie':
|
||||||
|
$source_config['repo'] = text(
|
||||||
|
"[{$package_name}] Please enter the PIE repository name",
|
||||||
|
placeholder: 'e.g. user/repo',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'url':
|
||||||
|
$source_config['url'] = text(
|
||||||
|
"[{$package_name}] Please enter the file download URL",
|
||||||
|
validate: function ($value) {
|
||||||
|
if (!filter_var($value, FILTER_VALIDATE_URL)) {
|
||||||
|
return 'Please enter a valid URL.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'custom':
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ask extract dir if is true
|
||||||
|
if ($default_extract_dir === true) {
|
||||||
|
if (confirm('Do you want to specify a custom extract directory?')) {
|
||||||
|
$extract_hint = match ($config_type) {
|
||||||
|
'source' => 'the source will be from the `source/` dir by default',
|
||||||
|
'binary' => 'the binary will be from the `pkgroot/{arch}-{os}/` dir by default',
|
||||||
|
default => '',
|
||||||
|
};
|
||||||
|
$default_extract_dir = text(
|
||||||
|
"[{$package_name}] Please enter the source extract directory",
|
||||||
|
validate: function ($value) {
|
||||||
|
if (trim($value) === '') {
|
||||||
|
return 'Extract directory cannot be empty.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
hint: 'You can use relative path, ' . $extract_hint . '.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$default_extract_dir = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($default_extract_dir !== null) {
|
||||||
|
$source_config['extract'] = $default_extract_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return config
|
||||||
|
return $source_config;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/StaticPHP/Command/Dev/SortConfigCommand.php
Normal file
49
src/StaticPHP/Command/Dev/SortConfigCommand.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace StaticPHP\Command\Dev;
|
||||||
|
|
||||||
|
use StaticPHP\Command\BaseCommand;
|
||||||
|
use StaticPHP\Registry\Registry;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
|
||||||
|
#[AsCommand('dev:sort-config', 'Sort artifact configuration files alphabetically')]
|
||||||
|
class SortConfigCommand extends BaseCommand
|
||||||
|
{
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
// get loaded configs
|
||||||
|
$loded_configs = Registry::getLoadedArtifactConfigs();
|
||||||
|
foreach ($loded_configs as $file) {
|
||||||
|
$this->sortConfigFile($file);
|
||||||
|
}
|
||||||
|
$loaded_pkg_configs = Registry::getLoadedPackageConfigs();
|
||||||
|
foreach ($loaded_pkg_configs as $file) {
|
||||||
|
$this->sortConfigFile($file);
|
||||||
|
}
|
||||||
|
return static::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sortConfigFile(mixed $file): void
|
||||||
|
{
|
||||||
|
$content = file_get_contents($file);
|
||||||
|
if ($content === false) {
|
||||||
|
$this->output->writeln("Failed to read artifact config file: {$file}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = json_decode($content, true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
$this->output->writeln("Invalid JSON format in artifact config file: {$file}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ksort($data);
|
||||||
|
foreach ($data as $artifact_name => &$config) {
|
||||||
|
ksort($config);
|
||||||
|
}
|
||||||
|
unset($config);
|
||||||
|
$new_content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
|
||||||
|
file_put_contents($file, $new_content);
|
||||||
|
$this->output->writeln("Sorted artifact config file: {$file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,26 +11,30 @@ class ArtifactConfig
|
|||||||
{
|
{
|
||||||
private static array $artifact_configs = [];
|
private static array $artifact_configs = [];
|
||||||
|
|
||||||
public static function loadFromDir(string $dir, string $registry_name): void
|
public static function loadFromDir(string $dir, string $registry_name): array
|
||||||
{
|
{
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
throw new WrongUsageException("Directory {$dir} does not exist, cannot load artifact config.");
|
throw new WrongUsageException("Directory {$dir} does not exist, cannot load artifact config.");
|
||||||
}
|
}
|
||||||
|
$loaded = [];
|
||||||
$files = glob("{$dir}/artifact.*.json");
|
$files = glob("{$dir}/artifact.*.json");
|
||||||
if (is_array($files)) {
|
if (is_array($files)) {
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
self::loadFromFile($file, $registry_name);
|
self::loadFromFile($file, $registry_name);
|
||||||
|
$loaded[] = $file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file_exists("{$dir}/artifact.json")) {
|
if (file_exists("{$dir}/artifact.json")) {
|
||||||
self::loadFromFile("{$dir}/artifact.json", $registry_name);
|
self::loadFromFile("{$dir}/artifact.json", $registry_name);
|
||||||
|
$loaded[] = "{$dir}/artifact.json";
|
||||||
}
|
}
|
||||||
|
return $loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load artifact configurations from a specified JSON file.
|
* Load artifact configurations from a specified JSON file.
|
||||||
*/
|
*/
|
||||||
public static function loadFromFile(string $file, string $registry_name): void
|
public static function loadFromFile(string $file, string $registry_name): string
|
||||||
{
|
{
|
||||||
$content = file_get_contents($file);
|
$content = file_get_contents($file);
|
||||||
if ($content === false) {
|
if ($content === false) {
|
||||||
@ -45,6 +49,7 @@ class ArtifactConfig
|
|||||||
self::$artifact_configs[$artifact_name] = $config;
|
self::$artifact_configs[$artifact_name] = $config;
|
||||||
Registry::_bindArtifactConfigFile($artifact_name, $registry_name, $file);
|
Registry::_bindArtifactConfigFile($artifact_name, $registry_name, $file);
|
||||||
}
|
}
|
||||||
|
return $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -16,20 +16,24 @@ class PackageConfig
|
|||||||
* Load package configurations from a specified directory.
|
* Load package configurations from a specified directory.
|
||||||
* It will look for files matching the pattern 'pkg.*.json' and 'pkg.json'.
|
* It will look for files matching the pattern 'pkg.*.json' and 'pkg.json'.
|
||||||
*/
|
*/
|
||||||
public static function loadFromDir(string $dir, string $registry_name): void
|
public static function loadFromDir(string $dir, string $registry_name): array
|
||||||
{
|
{
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
throw new WrongUsageException("Directory {$dir} does not exist, cannot load pkg.json config.");
|
throw new WrongUsageException("Directory {$dir} does not exist, cannot load pkg.json config.");
|
||||||
}
|
}
|
||||||
|
$loaded = [];
|
||||||
$files = glob("{$dir}/pkg.*.json");
|
$files = glob("{$dir}/pkg.*.json");
|
||||||
if (is_array($files)) {
|
if (is_array($files)) {
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
self::loadFromFile($file, $registry_name);
|
self::loadFromFile($file, $registry_name);
|
||||||
|
$loaded[] = $file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file_exists("{$dir}/pkg.json")) {
|
if (file_exists("{$dir}/pkg.json")) {
|
||||||
self::loadFromFile("{$dir}/pkg.json", $registry_name);
|
self::loadFromFile("{$dir}/pkg.json", $registry_name);
|
||||||
|
$loaded[] = "{$dir}/pkg.json";
|
||||||
}
|
}
|
||||||
|
return $loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +41,7 @@ class PackageConfig
|
|||||||
*
|
*
|
||||||
* @param string $file the path to the json package configuration file
|
* @param string $file the path to the json package configuration file
|
||||||
*/
|
*/
|
||||||
public static function loadFromFile(string $file, string $registry_name): void
|
public static function loadFromFile(string $file, string $registry_name): string
|
||||||
{
|
{
|
||||||
$content = file_get_contents($file);
|
$content = file_get_contents($file);
|
||||||
if ($content === false) {
|
if ($content === false) {
|
||||||
@ -52,6 +56,7 @@ class PackageConfig
|
|||||||
self::$package_configs[$pkg_name] = $config;
|
self::$package_configs[$pkg_name] = $config;
|
||||||
Registry::_bindPackageConfigFile($pkg_name, $registry_name, $file);
|
Registry::_bindPackageConfigFile($pkg_name, $registry_name, $file);
|
||||||
}
|
}
|
||||||
|
return $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use StaticPHP\Command\Dev\EnvCommand;
|
|||||||
use StaticPHP\Command\Dev\IsInstalledCommand;
|
use StaticPHP\Command\Dev\IsInstalledCommand;
|
||||||
use StaticPHP\Command\Dev\ShellCommand;
|
use StaticPHP\Command\Dev\ShellCommand;
|
||||||
use StaticPHP\Command\Dev\SkeletonCommand;
|
use StaticPHP\Command\Dev\SkeletonCommand;
|
||||||
|
use StaticPHP\Command\Dev\SortConfigCommand;
|
||||||
use StaticPHP\Command\DoctorCommand;
|
use StaticPHP\Command\DoctorCommand;
|
||||||
use StaticPHP\Command\DownloadCommand;
|
use StaticPHP\Command\DownloadCommand;
|
||||||
use StaticPHP\Command\ExtractCommand;
|
use StaticPHP\Command\ExtractCommand;
|
||||||
@ -61,6 +62,7 @@ class ConsoleApplication extends Application
|
|||||||
new IsInstalledCommand(),
|
new IsInstalledCommand(),
|
||||||
new EnvCommand(),
|
new EnvCommand(),
|
||||||
new SkeletonCommand(),
|
new SkeletonCommand(),
|
||||||
|
new SortConfigCommand(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// add additional commands from registries
|
// add additional commands from registries
|
||||||
|
|||||||
@ -133,9 +133,8 @@ class PackageInstaller
|
|||||||
// show install or build options in terminal with beautiful output
|
// show install or build options in terminal with beautiful output
|
||||||
$this->printInstallerInfo();
|
$this->printInstallerInfo();
|
||||||
|
|
||||||
InteractiveTerm::notice('Build process will start after 2s ...');
|
InteractiveTerm::notice('Build process will start after 2s ...' . PHP_EOL);
|
||||||
sleep(2);
|
sleep(2);
|
||||||
echo PHP_EOL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Early validation: check if packages can be built or installed before downloading
|
// Early validation: check if packages can be built or installed before downloading
|
||||||
|
|||||||
@ -13,13 +13,39 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
|
|
||||||
class Registry
|
class Registry
|
||||||
{
|
{
|
||||||
/** @var array<string, Registry> List of loaded registries */
|
/** @var string[] List of loaded registries */
|
||||||
private static array $loaded_registries = [];
|
private static array $loaded_registries = [];
|
||||||
|
|
||||||
|
/** @var array<string, array> Loaded registry configs */
|
||||||
|
private static array $registry_configs = [];
|
||||||
|
|
||||||
|
private static array $loaded_package_configs = [];
|
||||||
|
|
||||||
|
private static array $loaded_artifact_configs = [];
|
||||||
|
|
||||||
/** @var array<string, array{registry: string, config: string}> Maps of package and artifact names to their registry config file paths (for reverse lookup) */
|
/** @var array<string, array{registry: string, config: string}> Maps of package and artifact names to their registry config file paths (for reverse lookup) */
|
||||||
private static array $package_reversed_registry_files = [];
|
private static array $package_reversed_registry_files = [];
|
||||||
|
|
||||||
private static array $artifact_reversed_registry_files = [];
|
private static array $artifact_reversed_registry_files = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current registry configuration.
|
||||||
|
* "Current" depends on SPC load mode
|
||||||
|
*/
|
||||||
|
public static function getRegistryConfig(?string $registry_name = null): array
|
||||||
|
{
|
||||||
|
if ($registry_name === null && spc_mode(SPC_MODE_SOURCE)) {
|
||||||
|
return self::$registry_configs['internal'];
|
||||||
|
}
|
||||||
|
if ($registry_name !== null && isset(self::$registry_configs[$registry_name])) {
|
||||||
|
return self::$registry_configs[$registry_name];
|
||||||
|
}
|
||||||
|
if ($registry_name === null) {
|
||||||
|
throw new RegistryException('No registry name specified.');
|
||||||
|
}
|
||||||
|
throw new RegistryException("Registry '{$registry_name}' is not loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a registry from file path.
|
* Load a registry from file path.
|
||||||
* This method handles external registries that may not be in composer autoload.
|
* This method handles external registries that may not be in composer autoload.
|
||||||
@ -52,12 +78,14 @@ class Registry
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self::$loaded_registries[] = $registry_name;
|
self::$loaded_registries[] = $registry_name;
|
||||||
|
self::$registry_configs[$registry_name] = $data;
|
||||||
|
self::$registry_configs[$registry_name]['_file'] = $registry_file;
|
||||||
|
|
||||||
logger()->debug("Loading registry '{$registry_name}' from file: {$registry_file}");
|
logger()->debug("Loading registry '{$registry_name}' from file: {$registry_file}");
|
||||||
|
|
||||||
// Load composer autoload if specified (for external registries with their own dependencies)
|
// Load composer autoload if specified (for external registries with their own dependencies)
|
||||||
if (isset($data['autoload']) && is_string($data['autoload'])) {
|
if (isset($data['autoload']) && is_string($data['autoload'])) {
|
||||||
$autoload_path = self::fullpath($data['autoload'], dirname($registry_file));
|
$autoload_path = FileSystem::fullpath($data['autoload'], dirname($registry_file));
|
||||||
if (file_exists($autoload_path)) {
|
if (file_exists($autoload_path)) {
|
||||||
logger()->debug("Loading external autoload from: {$autoload_path}");
|
logger()->debug("Loading external autoload from: {$autoload_path}");
|
||||||
require_once $autoload_path;
|
require_once $autoload_path;
|
||||||
@ -69,7 +97,7 @@ class Registry
|
|||||||
// load doctor items from PSR-4 directories
|
// load doctor items from PSR-4 directories
|
||||||
if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) {
|
if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) {
|
||||||
foreach ($data['doctor']['psr-4'] as $namespace => $path) {
|
foreach ($data['doctor']['psr-4'] as $namespace => $path) {
|
||||||
$path = self::fullpath($path, dirname($registry_file));
|
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||||
DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,11 +115,11 @@ class Registry
|
|||||||
// load package configs
|
// load package configs
|
||||||
if (isset($data['package']['config']) && is_array($data['package']['config'])) {
|
if (isset($data['package']['config']) && is_array($data['package']['config'])) {
|
||||||
foreach ($data['package']['config'] as $path) {
|
foreach ($data['package']['config'] as $path) {
|
||||||
$path = self::fullpath($path, dirname($registry_file));
|
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||||
if (is_file($path)) {
|
if (is_file($path)) {
|
||||||
PackageConfig::loadFromFile($path, $registry_name);
|
self::$loaded_package_configs[] = PackageConfig::loadFromFile($path, $registry_name);
|
||||||
} elseif (is_dir($path)) {
|
} elseif (is_dir($path)) {
|
||||||
PackageConfig::loadFromDir($path, $registry_name);
|
self::$loaded_package_configs = array_merge(self::$loaded_package_configs, PackageConfig::loadFromDir($path, $registry_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,11 +127,11 @@ class Registry
|
|||||||
// load artifact configs
|
// load artifact configs
|
||||||
if (isset($data['artifact']['config']) && is_array($data['artifact']['config'])) {
|
if (isset($data['artifact']['config']) && is_array($data['artifact']['config'])) {
|
||||||
foreach ($data['artifact']['config'] as $path) {
|
foreach ($data['artifact']['config'] as $path) {
|
||||||
$path = self::fullpath($path, dirname($registry_file));
|
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||||
if (is_file($path)) {
|
if (is_file($path)) {
|
||||||
ArtifactConfig::loadFromFile($path, $registry_name);
|
self::$loaded_artifact_configs[] = ArtifactConfig::loadFromFile($path, $registry_name);
|
||||||
} elseif (is_dir($path)) {
|
} elseif (is_dir($path)) {
|
||||||
ArtifactConfig::loadFromDir($path, $registry_name);
|
self::$loaded_package_configs = array_merge(self::$loaded_package_configs, ArtifactConfig::loadFromDir($path, $registry_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +139,7 @@ class Registry
|
|||||||
// load packages from PSR-4 directories
|
// load packages from PSR-4 directories
|
||||||
if (isset($data['package']['psr-4']) && is_assoc_array($data['package']['psr-4'])) {
|
if (isset($data['package']['psr-4']) && is_assoc_array($data['package']['psr-4'])) {
|
||||||
foreach ($data['package']['psr-4'] as $namespace => $path) {
|
foreach ($data['package']['psr-4'] as $namespace => $path) {
|
||||||
$path = self::fullpath($path, dirname($registry_file));
|
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||||
PackageLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
PackageLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +157,7 @@ class Registry
|
|||||||
// load artifacts from PSR-4 directories
|
// load artifacts from PSR-4 directories
|
||||||
if (isset($data['artifact']['psr-4']) && is_assoc_array($data['artifact']['psr-4'])) {
|
if (isset($data['artifact']['psr-4']) && is_assoc_array($data['artifact']['psr-4'])) {
|
||||||
foreach ($data['artifact']['psr-4'] as $namespace => $path) {
|
foreach ($data['artifact']['psr-4'] as $namespace => $path) {
|
||||||
$path = self::fullpath($path, dirname($registry_file));
|
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||||
ArtifactLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
ArtifactLoader::loadFromPsr4Dir($path, $namespace, $auto_require);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +175,7 @@ class Registry
|
|||||||
// load additional commands from PSR-4 directories
|
// load additional commands from PSR-4 directories
|
||||||
if (isset($data['command']['psr-4']) && is_assoc_array($data['command']['psr-4'])) {
|
if (isset($data['command']['psr-4']) && is_assoc_array($data['command']['psr-4'])) {
|
||||||
foreach ($data['command']['psr-4'] as $namespace => $path) {
|
foreach ($data['command']['psr-4'] as $namespace => $path) {
|
||||||
$path = self::fullpath($path, dirname($registry_file));
|
$path = FileSystem::fullpath($path, dirname($registry_file));
|
||||||
$classes = FileSystem::getClassesPsr4($path, $namespace, auto_require: $auto_require);
|
$classes = FileSystem::getClassesPsr4($path, $namespace, auto_require: $auto_require);
|
||||||
$instances = array_map(fn ($x) => new $x(), $classes);
|
$instances = array_map(fn ($x) => new $x(), $classes);
|
||||||
ConsoleApplication::_addAdditionalCommands($instances);
|
ConsoleApplication::_addAdditionalCommands($instances);
|
||||||
@ -262,6 +290,16 @@ class Registry
|
|||||||
return self::$artifact_reversed_registry_files[$artifact_name] ?? null;
|
return self::$artifact_reversed_registry_files[$artifact_name] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getLoadedPackageConfigs(): array
|
||||||
|
{
|
||||||
|
return self::$loaded_package_configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLoadedArtifactConfigs(): array
|
||||||
|
{
|
||||||
|
return self::$loaded_artifact_configs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a class entry from the classes array.
|
* Parse a class entry from the classes array.
|
||||||
* Supports two formats:
|
* Supports two formats:
|
||||||
@ -298,7 +336,7 @@ class Registry
|
|||||||
|
|
||||||
// If file path is provided, require it
|
// If file path is provided, require it
|
||||||
if ($file_path !== null) {
|
if ($file_path !== null) {
|
||||||
$full_path = self::fullpath($file_path, $base_path);
|
$full_path = FileSystem::fullpath($file_path, $base_path);
|
||||||
require_once $full_path;
|
require_once $full_path;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -311,21 +349,4 @@ class Registry
|
|||||||
" 3. Provide file path in classes map: \"{$class}\": \"path/to/file.php\""
|
" 3. Provide file path in classes map: \"{$class}\": \"path/to/file.php\""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return full path, resolving relative paths against a base path.
|
|
||||||
*
|
|
||||||
* @param string $path Input path (relative or absolute)
|
|
||||||
* @param string $relative_path_base Base path for relative paths
|
|
||||||
*/
|
|
||||||
private static function fullpath(string $path, string $relative_path_base): string
|
|
||||||
{
|
|
||||||
if (FileSystem::isRelativePath($path)) {
|
|
||||||
$path = $relative_path_base . DIRECTORY_SEPARATOR . $path;
|
|
||||||
}
|
|
||||||
if (!file_exists($path)) {
|
|
||||||
throw new RegistryException("Path does not exist: {$path}");
|
|
||||||
}
|
|
||||||
return FileSystem::convertPath($path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace StaticPHP\Skeleton;
|
namespace StaticPHP\Skeleton;
|
||||||
|
|
||||||
|
use StaticPHP\Exception\FileSystemException;
|
||||||
|
use StaticPHP\Exception\ValidationException;
|
||||||
|
use StaticPHP\Util\FileSystem;
|
||||||
|
|
||||||
class ArtifactGenerator
|
class ArtifactGenerator
|
||||||
{
|
{
|
||||||
protected ?array $source = null;
|
protected ?array $source = null;
|
||||||
|
|
||||||
|
protected ?array $binary = null;
|
||||||
|
|
||||||
|
protected ?string $config_file = null;
|
||||||
|
|
||||||
protected bool $generate_class = false;
|
protected bool $generate_class = false;
|
||||||
|
|
||||||
protected bool $generate_custom_source_func = false;
|
protected bool $generate_custom_source_func = false;
|
||||||
@ -45,13 +55,61 @@ class ArtifactGenerator
|
|||||||
return $this->source;
|
return $this->source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateConfig(): array
|
public function setBinary(string $os, array $config): static
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
if ($clone->binary === null) {
|
||||||
|
$clone->binary = [$os => $config];
|
||||||
|
} else {
|
||||||
|
$clone->binary[$os] = $config;
|
||||||
|
}
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateConfigArray(): array
|
||||||
{
|
{
|
||||||
$config = [];
|
$config = [];
|
||||||
|
|
||||||
if ($this->source) {
|
if ($this->source) {
|
||||||
$config['source'] = $this->source;
|
$config['source'] = $this->source;
|
||||||
}
|
}
|
||||||
|
if ($this->binary) {
|
||||||
|
$config['binary'] = $this->binary;
|
||||||
|
}
|
||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setConfigFile(string $file): static
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->config_file = $file;
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the artifact configuration to the config file.
|
||||||
|
*/
|
||||||
|
public function writeConfigFile(): string
|
||||||
|
{
|
||||||
|
if ($this->config_file === null) {
|
||||||
|
throw new ValidationException('Config file path is not set.');
|
||||||
|
}
|
||||||
|
$config_array = $this->generateConfigArray();
|
||||||
|
$config_file_json = json_decode(FileSystem::readFile($this->config_file), true);
|
||||||
|
if (!is_array($config_file_json)) {
|
||||||
|
throw new ValidationException('Existing config file is not a valid JSON array.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config_file_json[$this->name] = $config_array;
|
||||||
|
// sort keys
|
||||||
|
ksort($config_file_json);
|
||||||
|
$json_content = json_encode($config_file_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
|
if ($json_content === false) {
|
||||||
|
throw new ValidationException('Failed to encode config array to JSON.');
|
||||||
|
}
|
||||||
|
if (file_put_contents($this->config_file, $json_content) === false) {
|
||||||
|
throw new FileSystemException("Failed to write config file: {$this->config_file}");
|
||||||
|
}
|
||||||
|
return $this->config_file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace StaticPHP\Skeleton;
|
namespace StaticPHP\Skeleton;
|
||||||
|
|
||||||
use StaticPHP\Exception\ValidationException;
|
use StaticPHP\Exception\ValidationException;
|
||||||
use StaticPHP\Runtime\Executor\Executor;
|
use StaticPHP\Runtime\Executor\Executor;
|
||||||
|
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
|
||||||
|
use StaticPHP\Runtime\Executor\UnixCMakeExecutor;
|
||||||
|
use StaticPHP\Runtime\Executor\WindowsCMakeExecutor;
|
||||||
|
|
||||||
class ExecutorGenerator
|
class ExecutorGenerator
|
||||||
{
|
{
|
||||||
@ -13,4 +18,19 @@ class ExecutorGenerator
|
|||||||
throw new ValidationException('Executor class must extend ' . Executor::class);
|
throw new ValidationException('Executor class must extend ' . Executor::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the code to create an instance of the executor.
|
||||||
|
*
|
||||||
|
* @return array{0: string, 1: string} an array containing the class name and the code string
|
||||||
|
*/
|
||||||
|
public function generateCode(): array
|
||||||
|
{
|
||||||
|
return match ($this->class) {
|
||||||
|
UnixCMakeExecutor::class => [UnixCMakeExecutor::class, 'UnixCMakeExecutor::create($package)->build();'],
|
||||||
|
UnixAutoconfExecutor::class => [UnixAutoconfExecutor::class, 'UnixAutoconfExecutor::create($package)->build();'],
|
||||||
|
WindowsCMakeExecutor::class => [WindowsCMakeExecutor::class, 'WindowsCMakeExecutor::create($package)->build();'],
|
||||||
|
default => throw new ValidationException("Unsupported executor class: {$this->class}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,49 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace StaticPHP\Skeleton;
|
namespace StaticPHP\Skeleton;
|
||||||
|
|
||||||
|
use Nette\PhpGenerator\PhpFile;
|
||||||
|
use Nette\PhpGenerator\Printer;
|
||||||
|
use StaticPHP\Attribute\Package\BuildFor;
|
||||||
|
use StaticPHP\Attribute\Package\Extension;
|
||||||
|
use StaticPHP\Attribute\Package\Library;
|
||||||
|
use StaticPHP\Attribute\Package\Target;
|
||||||
|
use StaticPHP\Exception\FileSystemException;
|
||||||
use StaticPHP\Exception\ValidationException;
|
use StaticPHP\Exception\ValidationException;
|
||||||
|
use StaticPHP\Package\LibraryPackage;
|
||||||
|
use StaticPHP\Package\PackageInstaller;
|
||||||
|
use StaticPHP\Package\PhpExtensionPackage;
|
||||||
|
use StaticPHP\Package\TargetPackage;
|
||||||
|
use StaticPHP\Util\FileSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A skeleton class for generating package files and configs.
|
* A skeleton class for generating package files and configs.
|
||||||
*/
|
*/
|
||||||
class PackageGenerator
|
class PackageGenerator
|
||||||
{
|
{
|
||||||
/** @var array<''|'unix'|'windows'|'macos'|'linux', string[]> $depends An array of dependencies required by the package, categorized by operating system. */
|
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $depends An array of dependencies required by the package, categorized by operating system. */
|
||||||
protected array $depends = [];
|
protected array $depends = [];
|
||||||
|
|
||||||
/** @var array<''|'unix'|'windows'|'macos'|'linux', string[]> $suggests An array of suggested packages for the package, categorized by operating system. */
|
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $suggests An array of suggested packages for the package, categorized by operating system. */
|
||||||
protected array $suggests = [];
|
protected array $suggests = [];
|
||||||
|
|
||||||
/** @var array<string> $frameworks An array of macOS frameworks for the package */
|
/** @var array<string> $frameworks An array of macOS frameworks for the package */
|
||||||
protected array $frameworks = [];
|
protected array $frameworks = [];
|
||||||
|
|
||||||
/** @var array<''|'unix'|'windows'|'macos'|'linux', string[]> $static_libs An array of static libraries required by the package, categorized by operating system. */
|
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $static_libs An array of static libraries required by the package, categorized by operating system. */
|
||||||
protected array $static_libs = [];
|
protected array $static_libs = [];
|
||||||
|
|
||||||
/** @var array<''|'unix'|'windows'|'macos'|'linux', string[]> $headers An array of header files required by the package, categorized by operating system. */
|
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $headers An array of header files required by the package, categorized by operating system. */
|
||||||
protected array $headers = [];
|
protected array $headers = [];
|
||||||
|
|
||||||
/** @var array<''|'unix'|'windows'|'macos'|'linux', string[]> $static_bins An array of static binaries required by the package, categorized by operating system. */
|
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $static_bins An array of static binaries required by the package, categorized by operating system. */
|
||||||
protected array $static_bins = [];
|
protected array $static_bins = [];
|
||||||
|
|
||||||
/** @var ArtifactGenerator|null $artifact Artifact */
|
protected ?string $config_file = null;
|
||||||
|
|
||||||
|
/** @var null|ArtifactGenerator $artifact Artifact */
|
||||||
protected ?ArtifactGenerator $artifact = null;
|
protected ?ArtifactGenerator $artifact = null;
|
||||||
|
|
||||||
/** @var array $licenses Licenses */
|
/** @var array $licenses Licenses */
|
||||||
@ -44,28 +60,28 @@ class PackageGenerator
|
|||||||
protected array $func_executor_binding = [];
|
protected array $func_executor_binding = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $package_name Package name
|
* @param string $package_name Package name
|
||||||
* @param 'library'|'target'|'virtual-target'|'php-extension' $type Package type ('library', 'target', 'virtual-target', etc.)
|
* @param 'library'|'php-extension'|'target'|'virtual-target' $type Package type ('library', 'target', 'virtual-target', etc.)
|
||||||
*/
|
*/
|
||||||
public function __construct(protected string $package_name, protected string $type) {}
|
public function __construct(protected string $package_name, protected string $type) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add package dependency.
|
* Add package dependency.
|
||||||
*
|
*
|
||||||
* @param string $package Package name
|
* @param string $package Package name
|
||||||
* @param string $os Operating system ('' for all OSes, '@unix', '@windows', '@macos')
|
* @param string $os_category Operating system ('' for all OSes, 'unix', 'windows', 'macos')
|
||||||
*/
|
*/
|
||||||
public function addDependency(string $package, string $os = ''): static
|
public function addDependency(string $package, string $os_category = ''): static
|
||||||
{
|
{
|
||||||
if (!in_array($os, ['', 'unix', 'windows', 'macos', 'linux'], true)) {
|
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||||
throw new ValidationException("Invalid OS suffix: {$os}");
|
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||||
}
|
}
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
if (!isset($clone->depends[$os])) {
|
if (!isset($clone->depends[$os_category])) {
|
||||||
$clone->depends[$os] = [];
|
$clone->depends[$os_category] = [];
|
||||||
}
|
}
|
||||||
if (!in_array($package, $clone->depends[$os], true)) {
|
if (!in_array($package, $clone->depends[$os_category], true)) {
|
||||||
$clone->depends[$os][] = $package;
|
$clone->depends[$os_category][] = $package;
|
||||||
}
|
}
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
@ -73,78 +89,78 @@ class PackageGenerator
|
|||||||
/**
|
/**
|
||||||
* Add package suggestion.
|
* Add package suggestion.
|
||||||
*
|
*
|
||||||
* @param string $package Package name
|
* @param string $package Package name
|
||||||
* @param string $os Operating system ('' for all OSes, '@unix', '@windows', '@macos')
|
* @param string $os_category Operating system ('' for all OSes)
|
||||||
*/
|
*/
|
||||||
public function addSuggestion(string $package, string $os = ''): static
|
public function addSuggestion(string $package, string $os_category = ''): static
|
||||||
{
|
{
|
||||||
if (!in_array($os, ['', 'unix', 'windows', 'macos', 'linux'], true)) {
|
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||||
throw new ValidationException("Invalid OS suffix: {$os}");
|
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||||
}
|
}
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
if (!isset($clone->suggests[$os])) {
|
if (!isset($clone->suggests[$os_category])) {
|
||||||
$clone->suggests[$os] = [];
|
$clone->suggests[$os_category] = [];
|
||||||
}
|
}
|
||||||
if (!in_array($package, $clone->suggests[$os], true)) {
|
if (!in_array($package, $clone->suggests[$os_category], true)) {
|
||||||
$clone->suggests[$os][] = $package;
|
$clone->suggests[$os_category][] = $package;
|
||||||
}
|
}
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addStaticLib(string $lib_a, string $os = ''): static
|
public function addStaticLib(string $lib_a, string $os_category = ''): static
|
||||||
{
|
{
|
||||||
if (!in_array($os, ['', 'unix', 'windows', 'macos', 'linux'], true)) {
|
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||||
throw new ValidationException("Invalid OS suffix: {$os}");
|
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||||
}
|
}
|
||||||
if (!str_ends_with($lib_a, '.lib') && !str_ends_with($lib_a, '.a')) {
|
if (!str_ends_with($lib_a, '.lib') && !str_ends_with($lib_a, '.a')) {
|
||||||
throw new ValidationException("Static library must end with .lib or .a, got: {$lib_a}");
|
throw new ValidationException("Static library must end with .lib or .a, got: {$lib_a}");
|
||||||
}
|
}
|
||||||
if (str_ends_with($lib_a, '.lib') && in_array($os, ['unix', 'linux', 'macos'], true)) {
|
if (str_ends_with($lib_a, '.lib') && in_array($os_category, ['unix', 'linux', 'macos'], true)) {
|
||||||
throw new ValidationException("Static library with .lib extension cannot be added for non-Windows OS: {$lib_a}");
|
throw new ValidationException("Static library with .lib extension cannot be added for non-Windows OS: {$lib_a}");
|
||||||
}
|
}
|
||||||
if (str_ends_with($lib_a, '.a') && $os === 'windows') {
|
if (str_ends_with($lib_a, '.a') && $os_category === 'windows') {
|
||||||
throw new ValidationException("Static library with .a extension cannot be added for Windows OS: {$lib_a}");
|
throw new ValidationException("Static library with .a extension cannot be added for Windows OS: {$lib_a}");
|
||||||
}
|
}
|
||||||
if (isset($this->static_libs[$os]) && in_array($lib_a, $this->static_libs[$os], true)) {
|
if (isset($this->static_libs[$os_category]) && in_array($lib_a, $this->static_libs[$os_category], true)) {
|
||||||
// already exists
|
// already exists
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
if (!isset($clone->static_libs[$os])) {
|
if (!isset($clone->static_libs[$os_category])) {
|
||||||
$clone->static_libs[$os] = [];
|
$clone->static_libs[$os_category] = [];
|
||||||
}
|
}
|
||||||
if (!in_array($lib_a, $clone->static_libs[$os], true)) {
|
if (!in_array($lib_a, $clone->static_libs[$os_category], true)) {
|
||||||
$clone->static_libs[$os][] = $lib_a;
|
$clone->static_libs[$os_category][] = $lib_a;
|
||||||
}
|
}
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addHeader(string $header_file, string $os = ''): static
|
public function addHeader(string $header_file, string $os_category = ''): static
|
||||||
{
|
{
|
||||||
if (!in_array($os, ['', 'unix', 'windows', 'macos', 'linux'], true)) {
|
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||||
throw new ValidationException("Invalid OS suffix: {$os}");
|
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||||
}
|
}
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
if (!isset($clone->headers[$os])) {
|
if (!isset($clone->headers[$os_category])) {
|
||||||
$clone->headers[$os] = [];
|
$clone->headers[$os_category] = [];
|
||||||
}
|
}
|
||||||
if (!in_array($header_file, $clone->headers[$os], true)) {
|
if (!in_array($header_file, $clone->headers[$os_category], true)) {
|
||||||
$clone->headers[$os][] = $header_file;
|
$clone->headers[$os_category][] = $header_file;
|
||||||
}
|
}
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addStaticBin(string $bin_file, string $os = ''): static
|
public function addStaticBin(string $bin_file, string $os_category = ''): static
|
||||||
{
|
{
|
||||||
if (!in_array($os, ['', 'unix', 'windows', 'macos', 'linux'], true)) {
|
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||||
throw new ValidationException("Invalid OS suffix: {$os}");
|
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||||
}
|
}
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
if (!isset($clone->static_bins[$os])) {
|
if (!isset($clone->static_bins[$os_category])) {
|
||||||
$clone->static_bins[$os] = [];
|
$clone->static_bins[$os_category] = [];
|
||||||
}
|
}
|
||||||
if (!in_array($bin_file, $clone->static_bins[$os], true)) {
|
if (!in_array($bin_file, $clone->static_bins[$os_category], true)) {
|
||||||
$clone->static_bins[$os][] = $bin_file;
|
$clone->static_bins[$os_category][] = $bin_file;
|
||||||
}
|
}
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
@ -194,9 +210,9 @@ class PackageGenerator
|
|||||||
/**
|
/**
|
||||||
* Enable build for specific OS.
|
* Enable build for specific OS.
|
||||||
*
|
*
|
||||||
* @param 'Windows'|'Linux'|'Darwin'|array<'Windows'|'Linux'|'Darwin'> $build_for Build for OS
|
* @param 'Darwin'|'Linux'|'Windows'|array<'Darwin'|'Linux'|'Windows'> $build_for Build for OS
|
||||||
*/
|
*/
|
||||||
public function enableBuild(string|array $build_for, ?string $build_function_name = null): static
|
public function enableBuild(array|string $build_for, ?string $build_function_name = null): static
|
||||||
{
|
{
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
if (is_array($build_for)) {
|
if (is_array($build_for)) {
|
||||||
@ -205,6 +221,9 @@ class PackageGenerator
|
|||||||
}
|
}
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
if (!in_array($build_for, SUPPORTED_OS_FAMILY, true)) {
|
||||||
|
throw new ValidationException("Unsupported build_for value: {$build_for}");
|
||||||
|
}
|
||||||
$clone->build_for_enables[$build_for] = $build_function_name ?? "buildFor{$build_for}";
|
$clone->build_for_enables[$build_for] = $build_function_name ?? "buildFor{$build_for}";
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
@ -212,8 +231,8 @@ class PackageGenerator
|
|||||||
/**
|
/**
|
||||||
* Bind function executor.
|
* Bind function executor.
|
||||||
*
|
*
|
||||||
* @param string $func_name Function name
|
* @param string $func_name Function name
|
||||||
* @param ExecutorGenerator $executor Executor generator
|
* @param ExecutorGenerator $executor Executor generator
|
||||||
*/
|
*/
|
||||||
public function addFunctionExecutorBinding(string $func_name, ExecutorGenerator $executor): static
|
public function addFunctionExecutorBinding(string $func_name, ExecutorGenerator $executor): static
|
||||||
{
|
{
|
||||||
@ -222,10 +241,73 @@ class PackageGenerator
|
|||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generatePackageClassFile(string $namespace, bool $uppercase = false): string
|
||||||
|
{
|
||||||
|
$printer = new class extends Printer {
|
||||||
|
public string $indentation = ' ';
|
||||||
|
};
|
||||||
|
$file = new PhpFile();
|
||||||
|
$namespace = $file->setStrictTypes()->addNamespace($namespace);
|
||||||
|
|
||||||
|
$uses = [];
|
||||||
|
|
||||||
|
// class name and package attribute
|
||||||
|
$class_name = str_replace('-', '_', $uppercase ? ucwords($this->package_name, '-') : $this->package_name);
|
||||||
|
$class_attribute = match ($this->type) {
|
||||||
|
'library' => Library::class,
|
||||||
|
'php-extension' => Extension::class,
|
||||||
|
'target', 'virtual-target' => Target::class,
|
||||||
|
};
|
||||||
|
$package_class = match ($this->type) {
|
||||||
|
'library' => LibraryPackage::class,
|
||||||
|
'php-extension' => PhpExtensionPackage::class,
|
||||||
|
'target', 'virtual-target' => TargetPackage::class,
|
||||||
|
};
|
||||||
|
$uses[] = $class_attribute;
|
||||||
|
$uses[] = $package_class;
|
||||||
|
$uses[] = BuildFor::class;
|
||||||
|
$uses[] = PackageInstaller::class;
|
||||||
|
|
||||||
|
foreach ($uses as $use) {
|
||||||
|
$namespace->addUse($use);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add class attribute
|
||||||
|
$class = $namespace->addClass($class_name);
|
||||||
|
$class->addAttribute($class_attribute, [$this->package_name]);
|
||||||
|
|
||||||
|
// add build functions if enabled
|
||||||
|
$funcs = [];
|
||||||
|
foreach ($this->build_for_enables as $os_family => $func_name) {
|
||||||
|
if ($func_name !== null) {
|
||||||
|
$funcs[$func_name][] = $os_family;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($funcs as $name => $oss) {
|
||||||
|
$method = $class->addMethod(name: $name ?: 'build')
|
||||||
|
->setPublic()
|
||||||
|
->setReturnType('void');
|
||||||
|
// check if function executor is bound
|
||||||
|
if (isset($this->func_executor_binding[$name])) {
|
||||||
|
$executor = $this->func_executor_binding[$name];
|
||||||
|
[$executor_use, $code] = $executor->generateCode();
|
||||||
|
$namespace->addUse($executor_use);
|
||||||
|
$method->setBody($code);
|
||||||
|
}
|
||||||
|
$method->addParameter('package')->setType($package_class);
|
||||||
|
$method->addParameter('installer')->setType(PackageInstaller::class);
|
||||||
|
foreach ($oss as $os) {
|
||||||
|
$method->addAttribute(BuildFor::class, [$os]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $printer->printFile($file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate package config
|
* Generate package config
|
||||||
*/
|
*/
|
||||||
public function generateConfig(): array
|
public function generateConfigArray(): array
|
||||||
{
|
{
|
||||||
$config = ['type' => $this->type];
|
$config = ['type' => $this->type];
|
||||||
|
|
||||||
@ -280,4 +362,51 @@ class PackageGenerator
|
|||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setConfigFile(string $config_file): static
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->config_file = $config_file;
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeConfigFile(): string
|
||||||
|
{
|
||||||
|
if ($this->config_file === null) {
|
||||||
|
throw new ValidationException('Config file path is not set.');
|
||||||
|
}
|
||||||
|
$config_array = $this->generateConfigArray();
|
||||||
|
$config_file_json = json_decode(FileSystem::readFile($this->config_file), true);
|
||||||
|
if (!is_array($config_file_json)) {
|
||||||
|
throw new ValidationException('Existing config file is not a valid JSON array.');
|
||||||
|
}
|
||||||
|
$config_file_json[$this->package_name] = $config_array;
|
||||||
|
ksort($config_file_json);
|
||||||
|
$json_content = json_encode($config_file_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
|
if ($json_content === false) {
|
||||||
|
throw new ValidationException('Failed to encode package config to JSON.');
|
||||||
|
}
|
||||||
|
if (file_put_contents($this->config_file, $json_content) === false) {
|
||||||
|
throw new FileSystemException("Failed to write config file: {$this->config_file}");
|
||||||
|
}
|
||||||
|
return $this->config_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeAll(): array
|
||||||
|
{
|
||||||
|
// write config
|
||||||
|
$package_config_file = $this->writeConfigFile();
|
||||||
|
$artifact_config_file = $this->artifact->writeConfigFile();
|
||||||
|
|
||||||
|
// write class file
|
||||||
|
$package_class_file_content = $this->generatePackageClassFile('StaticPHP\Packages');
|
||||||
|
$package_class_file_path = str_replace('-', '_', $this->package_name) . '.php';
|
||||||
|
// file_put_contents($package_class_file_path, $package_class_file_content); // Uncomment this line to actually write the file
|
||||||
|
return [
|
||||||
|
'package_config' => $package_config_file,
|
||||||
|
'artifact_config' => $artifact_config_file,
|
||||||
|
'package_class_file' => $package_class_file_path,
|
||||||
|
'package_class_content' => $package_class_file_content,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -472,6 +472,23 @@ class FileSystem
|
|||||||
return file_put_contents($file, implode('', $lines));
|
return file_put_contents($file, implode('', $lines));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return full path, resolving relative paths against a base path.
|
||||||
|
*
|
||||||
|
* @param string $path Input path (relative or absolute)
|
||||||
|
* @param string $relative_path_base Base path for relative paths
|
||||||
|
*/
|
||||||
|
public static function fullpath(string $path, string $relative_path_base): string
|
||||||
|
{
|
||||||
|
if (FileSystem::isRelativePath($path)) {
|
||||||
|
$path = $relative_path_base . DIRECTORY_SEPARATOR . $path;
|
||||||
|
}
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
throw new FileSystemException("Path does not exist: {$path}");
|
||||||
|
}
|
||||||
|
return FileSystem::convertPath($path);
|
||||||
|
}
|
||||||
|
|
||||||
private static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): false|int
|
private static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): false|int
|
||||||
{
|
{
|
||||||
logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename);
|
logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename);
|
||||||
|
|||||||
@ -52,6 +52,7 @@ class InteractiveTerm
|
|||||||
default => logger()->info(strip_ansi_colors($message)),
|
default => logger()->info(strip_ansi_colors($message)),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
$output = $level === 'error' && $output instanceof ConsoleOutput ? $output->getErrorOutput() : $output;
|
||||||
$output->writeln(($no_ansi ? 'strip_ansi_colors' : 'strval')($message));
|
$output->writeln(($no_ansi ? 'strip_ansi_colors' : 'strval')($message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,13 +96,32 @@ const SPC_STATUS_ALREADY_BUILT = 1;
|
|||||||
|
|
||||||
const SPC_DOWNLOAD_TYPE_DISPLAY_NAME = [
|
const SPC_DOWNLOAD_TYPE_DISPLAY_NAME = [
|
||||||
'bitbuckettag' => 'BitBucket',
|
'bitbuckettag' => 'BitBucket',
|
||||||
'filelist' => 'website',
|
'filelist' => 'File index website',
|
||||||
'git' => 'git',
|
'git' => 'git',
|
||||||
'ghrel' => 'GitHub release',
|
'ghrel' => 'GitHub release',
|
||||||
'ghtar', 'ghtagtar' => 'GitHub tarball',
|
'ghtar' => 'GitHub release tarball',
|
||||||
|
'ghtagtar' => 'GitHub tag tarball',
|
||||||
'local' => 'local dir',
|
'local' => 'local dir',
|
||||||
'pie' => 'PHP Installer for Extensions',
|
'pie' => 'PHP Installer for Extensions (PIE)',
|
||||||
'url' => 'url',
|
'url' => 'url',
|
||||||
'php-release' => 'php.net',
|
'php-release' => 'php.net',
|
||||||
'custom' => 'custom downloader',
|
'custom' => 'custom downloader',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const SUPPORTED_OS_CATEGORY = [
|
||||||
|
'unix',
|
||||||
|
'windows',
|
||||||
|
'linux',
|
||||||
|
'macos',
|
||||||
|
];
|
||||||
|
|
||||||
|
const SUPPORTED_OS_FAMILY = [
|
||||||
|
'Linux',
|
||||||
|
'Darwin',
|
||||||
|
'Windows',
|
||||||
|
];
|
||||||
|
|
||||||
|
const SPC_MODE_SOURCE = 1;
|
||||||
|
const SPC_MODE_VENDOR = 2;
|
||||||
|
const SPC_MODE_PHAR = 4;
|
||||||
|
const SPC_MODE_VENDOR_PHAR = SPC_MODE_VENDOR | SPC_MODE_PHAR;
|
||||||
|
|||||||
@ -10,6 +10,31 @@ use StaticPHP\Runtime\Shell\UnixShell;
|
|||||||
use StaticPHP\Runtime\Shell\WindowsCmd;
|
use StaticPHP\Runtime\Shell\WindowsCmd;
|
||||||
use ZM\Logger\ConsoleLogger;
|
use ZM\Logger\ConsoleLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current SPC loading mode. If passed a mode to check, will return whether current mode matches the given mode.
|
||||||
|
*/
|
||||||
|
function spc_mode(?int $check_mode = null): bool|int
|
||||||
|
{
|
||||||
|
$mode = SPC_MODE_SOURCE;
|
||||||
|
// if current file is in phar, then it's phar mode
|
||||||
|
if (str_starts_with(__FILE__, 'phar://') && Phar::running()) {
|
||||||
|
// judge whether it's vendor mode (inside vendor/) or source mode (inside src/)
|
||||||
|
if (basename(dirname(__FILE__, 3)) === 'static-php-cli' && basename(dirname(__FILE__, 5)) === 'vendor') {
|
||||||
|
$mode = SPC_MODE_VENDOR_PHAR;
|
||||||
|
} else {
|
||||||
|
$mode = SPC_MODE_PHAR;
|
||||||
|
}
|
||||||
|
} elseif (basename(dirname(__FILE__, 3)) === 'static-php-cli' && basename(dirname(__FILE__, 5)) === 'vendor') {
|
||||||
|
$mode = SPC_MODE_VENDOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($check_mode === null) {
|
||||||
|
return $mode;
|
||||||
|
}
|
||||||
|
// use bitwise AND to check mode
|
||||||
|
return ($mode & $check_mode) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Judge if an array is an associative array
|
* Judge if an array is an associative array
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user