mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-19 21:34:53 +08:00
Remove skeleton command
This commit is contained in:
parent
2c22bf25ea
commit
a0cab24e56
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use StaticPHP\Runtime\Executor\UnixCMakeExecutor;
|
||||
use StaticPHP\Skeleton\ArtifactGenerator;
|
||||
use StaticPHP\Skeleton\ExecutorGenerator;
|
||||
use StaticPHP\Skeleton\PackageGenerator;
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
$package_generator = new PackageGenerator('foo', 'library')
|
||||
->addDependency('bar')
|
||||
->addStaticLib('libfoo.a', 'unix')
|
||||
->addStaticLib('libfoo.a', 'unix')
|
||||
->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->generateConfigArray();
|
||||
$artifact_config = $artifact_generator->generateConfigArray();
|
||||
|
||||
echo '===== pkg.json =====' . PHP_EOL;
|
||||
echo json_encode($pkg_config, 64 | 128 | 256) . PHP_EOL;
|
||||
echo '===== artifact.json =====' . 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');
|
||||
@ -1,402 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,6 @@ use StaticPHP\Command\BuildTargetCommand;
|
||||
use StaticPHP\Command\Dev\EnvCommand;
|
||||
use StaticPHP\Command\Dev\IsInstalledCommand;
|
||||
use StaticPHP\Command\Dev\ShellCommand;
|
||||
use StaticPHP\Command\Dev\SkeletonCommand;
|
||||
use StaticPHP\Command\Dev\SortConfigCommand;
|
||||
use StaticPHP\Command\DoctorCommand;
|
||||
use StaticPHP\Command\DownloadCommand;
|
||||
@ -61,7 +60,6 @@ class ConsoleApplication extends Application
|
||||
new ShellCommand(),
|
||||
new IsInstalledCommand(),
|
||||
new EnvCommand(),
|
||||
new SkeletonCommand(),
|
||||
new SortConfigCommand(),
|
||||
]);
|
||||
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace StaticPHP\Skeleton;
|
||||
|
||||
use StaticPHP\Exception\FileSystemException;
|
||||
use StaticPHP\Exception\ValidationException;
|
||||
use StaticPHP\Util\FileSystem;
|
||||
|
||||
class ArtifactGenerator
|
||||
{
|
||||
protected ?array $source = null;
|
||||
|
||||
protected ?array $binary = null;
|
||||
|
||||
protected ?string $config_file = null;
|
||||
|
||||
protected bool $generate_class = false;
|
||||
|
||||
protected bool $generate_custom_source_func = false;
|
||||
|
||||
protected bool $generate_custom_binary_func_for_unix = false;
|
||||
|
||||
protected bool $generate_custom_binary_func_for_windows = false;
|
||||
|
||||
public function __construct(protected string $name) {}
|
||||
|
||||
/**
|
||||
* Get the artifact name.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setSource(array $source): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->source = $source;
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function setCustomSource(): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->source = ['type' => 'custom'];
|
||||
$clone->generate_class = true;
|
||||
$clone->generate_custom_source_func = true;
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function getSource(): ?array
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
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 = [];
|
||||
|
||||
if ($this->source) {
|
||||
$config['source'] = $this->source;
|
||||
}
|
||||
if ($this->binary) {
|
||||
$config['binary'] = $this->binary;
|
||||
}
|
||||
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,412 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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\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.
|
||||
*/
|
||||
class PackageGenerator
|
||||
{
|
||||
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $depends An array of dependencies required by the package, categorized by operating system. */
|
||||
protected array $depends = [];
|
||||
|
||||
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $suggests An array of suggested packages for the package, categorized by operating system. */
|
||||
protected array $suggests = [];
|
||||
|
||||
/** @var array<string> $frameworks An array of macOS frameworks for the package */
|
||||
protected array $frameworks = [];
|
||||
|
||||
/** @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 = [];
|
||||
|
||||
/** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $headers An array of header files required by the package, categorized by operating system. */
|
||||
protected array $headers = [];
|
||||
|
||||
/** @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 ?string $config_file = null;
|
||||
|
||||
/** @var null|ArtifactGenerator $artifact Artifact */
|
||||
protected ?ArtifactGenerator $artifact = null;
|
||||
|
||||
/** @var array $licenses Licenses */
|
||||
protected array $licenses = [];
|
||||
|
||||
/** @var array<'Darwin'|'Linux'|'Windows', null|string> $build_for_enables Enable build function generating */
|
||||
protected array $build_for_enables = [
|
||||
'Darwin' => null,
|
||||
'Linux' => null,
|
||||
'Windows' => null,
|
||||
];
|
||||
|
||||
/** @var array<string, ExecutorGenerator> */
|
||||
protected array $func_executor_binding = [];
|
||||
|
||||
/**
|
||||
* @param string $package_name Package name
|
||||
* @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) {}
|
||||
|
||||
/**
|
||||
* Add package dependency.
|
||||
*
|
||||
* @param string $package Package name
|
||||
* @param string $os_category Operating system ('' for all OSes, 'unix', 'windows', 'macos')
|
||||
*/
|
||||
public function addDependency(string $package, string $os_category = ''): static
|
||||
{
|
||||
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||
}
|
||||
$clone = clone $this;
|
||||
if (!isset($clone->depends[$os_category])) {
|
||||
$clone->depends[$os_category] = [];
|
||||
}
|
||||
if (!in_array($package, $clone->depends[$os_category], true)) {
|
||||
$clone->depends[$os_category][] = $package;
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add package suggestion.
|
||||
*
|
||||
* @param string $package Package name
|
||||
* @param string $os_category Operating system ('' for all OSes)
|
||||
*/
|
||||
public function addSuggestion(string $package, string $os_category = ''): static
|
||||
{
|
||||
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||
}
|
||||
$clone = clone $this;
|
||||
if (!isset($clone->suggests[$os_category])) {
|
||||
$clone->suggests[$os_category] = [];
|
||||
}
|
||||
if (!in_array($package, $clone->suggests[$os_category], true)) {
|
||||
$clone->suggests[$os_category][] = $package;
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function addStaticLib(string $lib_a, string $os_category = ''): static
|
||||
{
|
||||
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||
}
|
||||
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}");
|
||||
}
|
||||
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}");
|
||||
}
|
||||
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}");
|
||||
}
|
||||
if (isset($this->static_libs[$os_category]) && in_array($lib_a, $this->static_libs[$os_category], true)) {
|
||||
// already exists
|
||||
return $this;
|
||||
}
|
||||
$clone = clone $this;
|
||||
if (!isset($clone->static_libs[$os_category])) {
|
||||
$clone->static_libs[$os_category] = [];
|
||||
}
|
||||
if (!in_array($lib_a, $clone->static_libs[$os_category], true)) {
|
||||
$clone->static_libs[$os_category][] = $lib_a;
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function addHeader(string $header_file, string $os_category = ''): static
|
||||
{
|
||||
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||
}
|
||||
$clone = clone $this;
|
||||
if (!isset($clone->headers[$os_category])) {
|
||||
$clone->headers[$os_category] = [];
|
||||
}
|
||||
if (!in_array($header_file, $clone->headers[$os_category], true)) {
|
||||
$clone->headers[$os_category][] = $header_file;
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function addStaticBin(string $bin_file, string $os_category = ''): static
|
||||
{
|
||||
if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) {
|
||||
throw new ValidationException("Invalid OS suffix: {$os_category}");
|
||||
}
|
||||
$clone = clone $this;
|
||||
if (!isset($clone->static_bins[$os_category])) {
|
||||
$clone->static_bins[$os_category] = [];
|
||||
}
|
||||
if (!in_array($bin_file, $clone->static_bins[$os_category], true)) {
|
||||
$clone->static_bins[$os_category][] = $bin_file;
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add package artifact.
|
||||
*
|
||||
* @param ArtifactGenerator $artifactGenerator Artifact generator
|
||||
*/
|
||||
public function addArtifact(ArtifactGenerator $artifactGenerator): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->artifact = $artifactGenerator;
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add license from string.
|
||||
*
|
||||
* @param string $text License content
|
||||
*/
|
||||
public function addLicenseFromString(string $text): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->licenses[] = [
|
||||
'type' => 'text',
|
||||
'text' => $text,
|
||||
];
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add license from file.
|
||||
*
|
||||
* @param string $file_path License file path
|
||||
*/
|
||||
public function addLicenseFromFile(string $file_path): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->licenses[] = [
|
||||
'type' => 'file',
|
||||
'path' => $file_path,
|
||||
];
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable build for specific OS.
|
||||
*
|
||||
* @param 'Darwin'|'Linux'|'Windows'|array<'Darwin'|'Linux'|'Windows'> $build_for Build for OS
|
||||
*/
|
||||
public function enableBuild(array|string $build_for, ?string $build_function_name = null): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
if (is_array($build_for)) {
|
||||
foreach ($build_for as $bf) {
|
||||
$clone = $clone->enableBuild($bf, $build_function_name ?? 'build');
|
||||
}
|
||||
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}";
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind function executor.
|
||||
*
|
||||
* @param string $func_name Function name
|
||||
* @param ExecutorGenerator $executor Executor generator
|
||||
*/
|
||||
public function addFunctionExecutorBinding(string $func_name, ExecutorGenerator $executor): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->func_executor_binding[$func_name] = $executor;
|
||||
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
|
||||
*/
|
||||
public function generateConfigArray(): array
|
||||
{
|
||||
$config = ['type' => $this->type];
|
||||
|
||||
// Add dependencies
|
||||
foreach ($this->depends as $suffix => $depends) {
|
||||
$k = $suffix !== '' ? "depends@{$suffix}" : 'depends';
|
||||
$config[$k] = $depends;
|
||||
}
|
||||
|
||||
// add suggests
|
||||
foreach ($this->suggests as $suffix => $suggests) {
|
||||
$k = $suffix !== '' ? "suggests@{$suffix}" : 'suggests';
|
||||
$config[$k] = $suggests;
|
||||
}
|
||||
|
||||
// Add frameworks
|
||||
if (!empty($this->frameworks)) {
|
||||
$config['frameworks'] = $this->frameworks;
|
||||
}
|
||||
|
||||
// Add static libs
|
||||
foreach ($this->static_libs as $suffix => $libs) {
|
||||
$k = $suffix !== '' ? "static-libs@{$suffix}" : 'static-libs';
|
||||
$config[$k] = $libs;
|
||||
}
|
||||
|
||||
// Add headers
|
||||
foreach ($this->headers as $suffix => $headers) {
|
||||
$k = $suffix !== '' ? "headers@{$suffix}" : 'headers';
|
||||
$config[$k] = $headers;
|
||||
}
|
||||
|
||||
// Add static bins
|
||||
foreach ($this->static_bins as $suffix => $bins) {
|
||||
$k = $suffix !== '' ? "static-bins@{$suffix}" : 'static-bins';
|
||||
$config[$k] = $bins;
|
||||
}
|
||||
|
||||
// Add artifact
|
||||
if ($this->artifact !== null) {
|
||||
$config['artifact'] = $this->artifact->getName();
|
||||
}
|
||||
|
||||
// Add licenses
|
||||
if (!empty($this->licenses)) {
|
||||
if (count($this->licenses) === 1) {
|
||||
$config['license'] = $this->licenses[0];
|
||||
} else {
|
||||
$config['license'] = $this->licenses;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user