From 960addc8f22928fa5680e463b5855c860e528ff7 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 15 Apr 2023 18:46:46 +0800 Subject: [PATCH] add license dumper --- .github/workflows/build.yml | 12 +++ config/source.json | 6 +- src/SPC/command/BuildCliCommand.php | 8 ++ src/SPC/command/DumpLicenseCommand.php | 53 ++++++++++++- src/SPC/util/LicenseDumper.php | 100 +++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 src/SPC/util/LicenseDumper.php diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 904b1870..b53fd207 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,3 +98,15 @@ jobs: with: name: micro-${{ inputs.version }}-${{ env.SPC_BUILD_OS }} path: buildroot/bin/micro.sfx + + # Upload extensions metadata + - uses: actions/upload-artifact@v3 + with: + name: license-files.zip + path: buildroot/license/ + - uses: actions/upload-artifact@v3 + with: + name: build-meta + path: | + buildroot/build-extensions.json + buildroot/build-libraries.json diff --git a/config/source.json b/config/source.json index 413ac23c..cfe329ba 100644 --- a/config/source.json +++ b/config/source.json @@ -74,8 +74,8 @@ "url": "https://ftp.gnu.org/gnu/libiconv/", "regex": "/href=\"(?libiconv-(?[^\"]+)\\.tar\\.gz)\"/", "license": { - "type": "text", - "text": "TODO" + "type": "file", + "path": "COPYING" } }, "libmcrypt": { @@ -253,7 +253,7 @@ "url": "https://www.sqlite.org/2023/sqlite-autoconf-3410100.tar.gz", "license": { "type": "text", - "path": "The author disclaims copyright to this source code. In place of\na legal notice, here is a blessing:\n\n * May you do good and not evil.\n * May you find forgiveness for yourself and forgive others.\n * May you share freely, never taking more than you give." + "text": "The author disclaims copyright to this source code. In place of\na legal notice, here is a blessing:\n\n * May you do good and not evil.\n * May you find forgiveness for yourself and forgive others.\n * May you share freely, never taking more than you give." } }, "swoole": { diff --git a/src/SPC/command/BuildCliCommand.php b/src/SPC/command/BuildCliCommand.php index f8dda3f4..3bc182d0 100644 --- a/src/SPC/command/BuildCliCommand.php +++ b/src/SPC/command/BuildCliCommand.php @@ -8,6 +8,7 @@ use SPC\builder\BuilderProvider; use SPC\exception\ExceptionHandler; use SPC\exception\WrongUsageException; use SPC\util\DependencyUtil; +use SPC\util\LicenseDumper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -73,6 +74,13 @@ class BuildCliCommand extends BuildCommand if ($rule !== BUILD_MICRO_NONE) { logger()->info('phpmicro binary path: ' . BUILD_ROOT_PATH . '/bin/micro.sfx'); } + // 导出相关元数据 + file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + // 导出 LICENSE + $dumper = new LicenseDumper(); + $dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license'); + logger()->info('License path: ' . BUILD_ROOT_PATH . '/license/'); return 0; } catch (WrongUsageException $e) { logger()->critical($e->getMessage()); diff --git a/src/SPC/command/DumpLicenseCommand.php b/src/SPC/command/DumpLicenseCommand.php index 01911941..93df9a2c 100644 --- a/src/SPC/command/DumpLicenseCommand.php +++ b/src/SPC/command/DumpLicenseCommand.php @@ -4,8 +4,13 @@ declare(strict_types=1); namespace SPC\command; -use Symfony\Component\Console\Input\InputArgument; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; +use SPC\util\DependencyUtil; +use SPC\util\LicenseDumper; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -18,12 +23,54 @@ class DumpLicenseCommand extends BaseCommand public function configure() { $this->setDescription('Dump licenses for required libraries'); - $this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".'); + $this->addOption('by-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null); + $this->addOption('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src'); + $this->addOption('by-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null); + $this->addOption('by-sources', null, InputOption::VALUE_REQUIRED, 'Dump by original sources (source.json)', null); + $this->addOption('dump-dir', null, InputOption::VALUE_REQUIRED, 'Change dump directory', BUILD_ROOT_PATH . '/license'); } + /** + * @throws WrongUsageException + * @throws FileSystemException + * @throws RuntimeException + */ public function execute(InputInterface $input, OutputInterface $output): int { - $output->writeln('not implemented'); + $dumper = new LicenseDumper(); + if ($input->getOption('by-extensions') !== null) { + // 从参数中获取要编译的 extensions,并转换为数组 + $extensions = array_map('trim', array_filter(explode(',', $input->getOption('by-extensions')))); + // 根据提供的扩展列表获取依赖库列表并编译 + [$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions); + $dumper->addExts($extensions); + $dumper->addLibs($libraries); + if (!$input->getOption('without-php')) { + $dumper->addSources(['php-src']); + } + $dumper->dump($input->getOption('dump-dir')); + $output->writeln('Dump license with extensions: ' . implode(', ', $extensions)); + $output->writeln('Dump license with libraries: ' . implode(', ', $libraries)); + $output->writeln('Dump license with' . ($input->getOption('without-php') ? 'out' : '') . ' php-src'); + $output->writeln('Dump target dir: ' . $input->getOption('dump-dir')); + return 0; + } + if ($input->getOption('by-libs') !== null) { + $libraries = array_map('trim', array_filter(explode(',', $input->getOption('by-libs')))); + $libraries = DependencyUtil::getLibsByDeps($libraries); + $dumper->addLibs($libraries); + $dumper->dump($input->getOption('dump-dir')); + $output->writeln('Dump target dir: ' . $input->getOption('dump-dir')); + return 0; + } + if ($input->getOption('by-sources') !== null) { + $sources = array_map('trim', array_filter(explode(',', $input->getOption('by-sources')))); + $dumper->addSources($sources); + $dumper->dump($input->getOption('dump-dir')); + $output->writeln('Dump target dir: ' . $input->getOption('dump-dir')); + return 0; + } + $output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump'); return 1; } } diff --git a/src/SPC/util/LicenseDumper.php b/src/SPC/util/LicenseDumper.php new file mode 100644 index 00000000..32bbdc93 --- /dev/null +++ b/src/SPC/util/LicenseDumper.php @@ -0,0 +1,100 @@ +exts = array_merge($exts, $this->exts); + return $this; + } + + public function addLibs(array $libs): LicenseDumper + { + $this->libs = array_merge($libs, $this->libs); + return $this; + } + + public function addSources(array $sources): LicenseDumper + { + $this->sources = array_merge($sources, $this->sources); + return $this; + } + + /** + * @throws WrongUsageException + * @throws FileSystemException + * @throws RuntimeException + */ + public function dump(string $target_dir): bool + { + // mkdir first + if (is_dir($target_dir) && !FileSystem::removeDir($target_dir)) { + logger()->warning('Target dump directory is noe empty, be aware!'); + } + FileSystem::createDir($target_dir); + foreach ($this->exts as $ext) { + if (Config::getExt($ext, 'type') !== 'external') { + continue; + } + $source_name = Config::getExt($ext, 'source'); + $content = $this->getSourceLicense($source_name); + file_put_contents($target_dir . '/ext_' . $ext . '.txt', $content); + } + + foreach ($this->libs as $lib) { + $source_name = Config::getLib($lib, 'source'); + $content = $this->getSourceLicense($source_name); + file_put_contents($target_dir . '/lib_' . $lib . '.txt', $content); + } + + foreach ($this->sources as $source) { + file_put_contents($target_dir . '/src_' . $source . '.txt', $this->getSourceLicense($source)); + } + return true; + } + + /** + * @throws FileSystemException + * @throws RuntimeException + */ + private function getSourceLicense(string $source_name): ?string + { + $src = Config::getSource($source_name)['license'] ?? null; + if ($src === null) { + throw new RuntimeException('source [' . $source_name . '] license meta is not exist'); + } + + return match ($src['type']) { + 'text' => $src['text'], + 'file' => $this->loadSourceFile($source_name, $src['path'], Config::getSource($source_name)['path'] ?? null), + default => throw new RuntimeException('source [' . $source_name . '] license type is not allowed'), + }; + } + + /** + * @throws RuntimeException + */ + private function loadSourceFile(string $source_name, string $in_path, ?string $custom_base_path = null): string + { + if (!file_exists(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path)) { + throw new RuntimeException('source [' . $source_name . '] license file [' . $in_path . '] is not exist'); + } + return file_get_contents(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path); + } +}