mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 21:04:52 +08:00
Merge pull request #329 from crazywhalecc/suggest-cmd
add with-suggested-libs and with-suggested-exts
This commit is contained in:
commit
be2394b39b
@ -93,13 +93,6 @@
|
|||||||
"freetype"
|
"freetype"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gettext": {
|
|
||||||
"type": "builtin",
|
|
||||||
"arg-type": "with",
|
|
||||||
"lib-depends": [
|
|
||||||
"gettext"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"glfw": {
|
"glfw": {
|
||||||
"type": "external",
|
"type": "external",
|
||||||
"arg-type": "custom",
|
"arg-type": "custom",
|
||||||
@ -313,13 +306,6 @@
|
|||||||
"type": "external",
|
"type": "external",
|
||||||
"source": "protobuf"
|
"source": "protobuf"
|
||||||
},
|
},
|
||||||
"pspell": {
|
|
||||||
"type": "builtin",
|
|
||||||
"arg-type": "with",
|
|
||||||
"lib-depends": [
|
|
||||||
"aspell"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"rar": {
|
"rar": {
|
||||||
"type": "external",
|
"type": "external",
|
||||||
"source": "rar",
|
"source": "rar",
|
||||||
@ -371,13 +357,6 @@
|
|||||||
"apcu"
|
"apcu"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"snmp": {
|
|
||||||
"type": "builtin",
|
|
||||||
"arg-type": "with",
|
|
||||||
"lib-depends": [
|
|
||||||
"net-snmp"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"soap": {
|
"soap": {
|
||||||
"type": "builtin",
|
"type": "builtin",
|
||||||
"arg-type": "custom",
|
"arg-type": "custom",
|
||||||
|
|||||||
@ -231,6 +231,30 @@ abstract class BuilderBase
|
|||||||
throw new RuntimeException('PHP version file format is malformed, please remove it and download again');
|
throw new RuntimeException('PHP version file format is malformed, please remove it and download again');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get PHP version from archive file name.
|
||||||
|
*
|
||||||
|
* @param null|string $file php-*.*.*.tar.gz filename, read from lockfile if empty
|
||||||
|
*/
|
||||||
|
public function getPHPVersionFromArchive(?string $file = null): false|string
|
||||||
|
{
|
||||||
|
if ($file === null) {
|
||||||
|
$lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false;
|
||||||
|
if ($lock === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$lock = json_decode($lock, true);
|
||||||
|
$file = $lock['php-src']['filename'] ?? null;
|
||||||
|
if ($file === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (preg_match('/php-(\d+\.\d+\.\d+)/', $file, $match)) {
|
||||||
|
return $match[1];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get build type name string to display.
|
* Get build type name string to display.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -107,7 +107,7 @@ abstract class UnixBuilderBase extends BuilderBase
|
|||||||
// if no libs specified, compile all supported libs
|
// if no libs specified, compile all supported libs
|
||||||
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
||||||
$libraries = array_keys($support_lib_list);
|
$libraries = array_keys($support_lib_list);
|
||||||
$sorted_libraries = DependencyUtil::getLibsByDeps($libraries);
|
$sorted_libraries = DependencyUtil::getLibs($libraries);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pkg-config must be compiled first, whether it is specified or not
|
// pkg-config must be compiled first, whether it is specified or not
|
||||||
|
|||||||
@ -192,7 +192,7 @@ class WindowsBuilder extends BuilderBase
|
|||||||
// if no libs specified, compile all supported libs
|
// if no libs specified, compile all supported libs
|
||||||
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
||||||
$libraries = array_keys($support_lib_list);
|
$libraries = array_keys($support_lib_list);
|
||||||
$sorted_libraries = DependencyUtil::getLibsByDeps($libraries);
|
$sorted_libraries = DependencyUtil::getLibs($libraries);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add lib object for builder
|
// add lib object for builder
|
||||||
|
|||||||
@ -23,11 +23,11 @@ class BuildCliCommand extends BuildCommand
|
|||||||
{
|
{
|
||||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
||||||
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
|
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
|
||||||
$this->addOption('build-micro', null, null, 'build micro');
|
$this->addOption('build-micro', null, null, 'Build micro SAPI');
|
||||||
$this->addOption('build-cli', null, null, 'build cli');
|
$this->addOption('build-cli', null, null, 'Build cli SAPI');
|
||||||
$this->addOption('build-fpm', null, null, 'build fpm');
|
$this->addOption('build-fpm', null, null, 'Build fpm SAPI');
|
||||||
$this->addOption('build-embed', null, null, 'build embed');
|
$this->addOption('build-embed', null, null, 'Build embed SAPI');
|
||||||
$this->addOption('build-all', null, null, 'build cli, micro, fpm, embed');
|
$this->addOption('build-all', null, null, 'Build all SAPI');
|
||||||
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
|
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
|
||||||
$this->addOption('enable-zts', null, null, 'enable ZTS support');
|
$this->addOption('enable-zts', null, null, 'enable ZTS support');
|
||||||
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
||||||
@ -61,8 +61,9 @@ class BuildCliCommand extends BuildCommand
|
|||||||
try {
|
try {
|
||||||
// create builder
|
// create builder
|
||||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||||
// calculate dependencies
|
$include_suggest_ext = $this->getOption('with-suggested-exts');
|
||||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
|
$include_suggest_lib = $this->getOption('with-suggested-libs');
|
||||||
|
[$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
|
||||||
|
|
||||||
// print info
|
// print info
|
||||||
$indent_texts = [
|
$indent_texts = [
|
||||||
@ -76,12 +77,23 @@ class BuildCliCommand extends BuildCommand
|
|||||||
if (!empty($this->input->getOption('with-hardcoded-ini'))) {
|
if (!empty($this->input->getOption('with-hardcoded-ini'))) {
|
||||||
$indent_texts['Hardcoded INI'] = $this->input->getOption('with-hardcoded-ini');
|
$indent_texts['Hardcoded INI'] = $this->input->getOption('with-hardcoded-ini');
|
||||||
}
|
}
|
||||||
$this->printFormatInfo($indent_texts);
|
if ($this->input->getOption('disable-opcache-jit')) {
|
||||||
|
$indent_texts['Opcache JIT'] = 'disabled';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$ver = $builder->getPHPVersion();
|
||||||
|
$indent_texts['PHP Version'] = $ver;
|
||||||
|
} catch (\Throwable) {
|
||||||
|
if (($ver = $builder->getPHPVersionFromArchive()) !== false) {
|
||||||
|
$indent_texts['PHP Version'] = $ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($not_included)) {
|
if (!empty($not_included)) {
|
||||||
logger()->warning('Some extensions will be enabled due to dependencies: ' . implode(',', $not_included));
|
$indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included);
|
||||||
}
|
}
|
||||||
logger()->info('Build will start after 2s ...');
|
$this->printFormatInfo($indent_texts);
|
||||||
|
logger()->notice('Build will start after 2s ...');
|
||||||
sleep(2);
|
sleep(2);
|
||||||
|
|
||||||
if ($this->input->getOption('with-clean')) {
|
if ($this->input->getOption('with-clean')) {
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class BuildLibsCommand extends BuildCommand
|
|||||||
// 只编译 library 的情况下,标记
|
// 只编译 library 的情况下,标记
|
||||||
$builder->setLibsOnly();
|
$builder->setLibsOnly();
|
||||||
// 编译和检查库完整
|
// 编译和检查库完整
|
||||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
$libraries = DependencyUtil::getLibs($libraries);
|
||||||
$builder->buildLibs($libraries);
|
$builder->buildLibs($libraries);
|
||||||
|
|
||||||
$time = round(microtime(true) - START_TIME, 3);
|
$time = round(microtime(true) - START_TIME, 3);
|
||||||
|
|||||||
@ -214,7 +214,7 @@ class DownloadCommand extends BaseCommand
|
|||||||
*/
|
*/
|
||||||
private function calculateSourcesByExt(array $extensions, bool $include_suggests = true): array
|
private function calculateSourcesByExt(array $extensions, bool $include_suggests = true): array
|
||||||
{
|
{
|
||||||
[$extensions, $libraries] = $include_suggests ? DependencyUtil::getAllExtLibsByDeps($extensions) : DependencyUtil::getExtLibsByDeps($extensions);
|
[$extensions, $libraries] = $include_suggests ? DependencyUtil::getExtsAndLibs($extensions, [], true, true) : DependencyUtil::getExtsAndLibs($extensions);
|
||||||
$sources = [];
|
$sources = [];
|
||||||
foreach ($extensions as $extension) {
|
foreach ($extensions as $extension) {
|
||||||
if (Config::getExt($extension, 'type') === 'external') {
|
if (Config::getExt($extension, 'type') === 'external') {
|
||||||
|
|||||||
@ -22,8 +22,8 @@ class DumpLicenseCommand extends BaseCommand
|
|||||||
{
|
{
|
||||||
$this->addOption('for-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null);
|
$this->addOption('for-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('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src');
|
||||||
$this->addOption('by-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null);
|
$this->addOption('for-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('for-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');
|
$this->addOption('dump-dir', null, InputOption::VALUE_REQUIRED, 'Change dump directory', BUILD_ROOT_PATH . '/license');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ class DumpLicenseCommand extends BaseCommand
|
|||||||
// 从参数中获取要编译的 extensions,并转换为数组
|
// 从参数中获取要编译的 extensions,并转换为数组
|
||||||
$extensions = array_map('trim', array_filter(explode(',', $this->getOption('for-extensions'))));
|
$extensions = array_map('trim', array_filter(explode(',', $this->getOption('for-extensions'))));
|
||||||
// 根据提供的扩展列表获取依赖库列表并编译
|
// 根据提供的扩展列表获取依赖库列表并编译
|
||||||
[$extensions, $libraries] = DependencyUtil::getExtLibsByDeps($extensions);
|
[$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions);
|
||||||
$dumper->addExts($extensions);
|
$dumper->addExts($extensions);
|
||||||
$dumper->addLibs($libraries);
|
$dumper->addLibs($libraries);
|
||||||
if (!$this->getOption('without-php')) {
|
if (!$this->getOption('without-php')) {
|
||||||
@ -52,22 +52,22 @@ class DumpLicenseCommand extends BaseCommand
|
|||||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
}
|
}
|
||||||
if ($this->getOption('by-libs') !== null) {
|
if ($this->getOption('for-libs') !== null) {
|
||||||
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs'))));
|
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('for-libs'))));
|
||||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
$libraries = DependencyUtil::getLibs($libraries);
|
||||||
$dumper->addLibs($libraries);
|
$dumper->addLibs($libraries);
|
||||||
$dumper->dump($this->getOption('dump-dir'));
|
$dumper->dump($this->getOption('dump-dir'));
|
||||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
}
|
}
|
||||||
if ($this->getOption('by-sources') !== null) {
|
if ($this->getOption('for-sources') !== null) {
|
||||||
$sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources'))));
|
$sources = array_map('trim', array_filter(explode(',', $this->getOption('for-sources'))));
|
||||||
$dumper->addSources($sources);
|
$dumper->addSources($sources);
|
||||||
$dumper->dump($this->getOption('dump-dir'));
|
$dumper->dump($this->getOption('dump-dir'));
|
||||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
}
|
}
|
||||||
$this->output->writeln('You must use one of "--for-extensions=", "--by-libs=", "--by-sources=" to dump');
|
$this->output->writeln('You must use one of "--for-extensions=", "--for-libs=", "--for-sources=" to dump');
|
||||||
return static::FAILURE;
|
return static::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,6 @@ use Symfony\Component\Console\Input\InputArgument;
|
|||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
use function Laravel\Prompts\table;
|
|
||||||
|
|
||||||
#[AsCommand('dev:extensions', 'Helper command that lists available extension details', ['list-ext'])]
|
#[AsCommand('dev:extensions', 'Helper command that lists available extension details', ['list-ext'])]
|
||||||
class AllExtCommand extends BaseCommand
|
class AllExtCommand extends BaseCommand
|
||||||
{
|
{
|
||||||
@ -61,7 +59,7 @@ class AllExtCommand extends BaseCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
[, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps([$extension]);
|
[, $libraries, $not_included] = DependencyUtil::getExtsAndLibs([$extension]);
|
||||||
} catch (WrongUsageException) {
|
} catch (WrongUsageException) {
|
||||||
$libraries = $not_included = [];
|
$libraries = $not_included = [];
|
||||||
}
|
}
|
||||||
@ -88,7 +86,8 @@ class AllExtCommand extends BaseCommand
|
|||||||
if ($data === []) {
|
if ($data === []) {
|
||||||
$style->warning('Unknown extension selected: ' . implode(',', $extensions));
|
$style->warning('Unknown extension selected: ' . implode(',', $extensions));
|
||||||
} else {
|
} else {
|
||||||
table($columns, $data);
|
$func = PHP_OS_FAMILY === 'Windows' ? [$style, 'table'] : '\Laravel\Prompts\table';
|
||||||
|
call_user_func($func, $columns, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
|
|||||||
@ -227,8 +227,12 @@ class FileSystem
|
|||||||
{
|
{
|
||||||
$dir = self::convertPath($dir);
|
$dir = self::convertPath($dir);
|
||||||
// 不是目录不扫,直接 false 处理
|
// 不是目录不扫,直接 false 处理
|
||||||
|
if (!file_exists($dir)) {
|
||||||
|
logger()->debug('Scan dir failed, no such file or directory.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
logger()->warning('Scan dir failed, no such directory.');
|
logger()->warning('Scan dir failed, not directory.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
logger()->debug('scanning directory ' . $dir);
|
logger()->debug('scanning directory ' . $dir);
|
||||||
@ -317,8 +321,12 @@ class FileSystem
|
|||||||
$dir = FileSystem::convertPath($dir);
|
$dir = FileSystem::convertPath($dir);
|
||||||
logger()->debug('Removing path recursively: "' . $dir . '"');
|
logger()->debug('Removing path recursively: "' . $dir . '"');
|
||||||
// 不是目录不扫,直接 false 处理
|
// 不是目录不扫,直接 false 处理
|
||||||
|
if (!file_exists($dir)) {
|
||||||
|
logger()->debug('Scan dir failed, no such file or directory.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
logger()->warning('Scan dir failed, no such directory.');
|
logger()->warning('Scan dir failed, not directory.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
logger()->debug('scanning directory ' . $dir);
|
logger()->debug('scanning directory ' . $dir);
|
||||||
|
|||||||
@ -5,90 +5,164 @@ declare(strict_types=1);
|
|||||||
namespace SPC\util;
|
namespace SPC\util;
|
||||||
|
|
||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
|
||||||
use SPC\exception\WrongUsageException;
|
use SPC\exception\WrongUsageException;
|
||||||
use SPC\store\Config;
|
use SPC\store\Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 依赖处理工具类,包含处理扩展、库的依赖列表顺序等
|
* Dependency processing tool class, including processing extensions, library dependency list order, etc.
|
||||||
*/
|
*/
|
||||||
class DependencyUtil
|
class DependencyUtil
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Obtain the dependent lib list according to the required ext list, and sort according to the dependency
|
* Convert platform extensions to library dependencies and suggestions.
|
||||||
*
|
*
|
||||||
* @param array $exts extensions list
|
|
||||||
* @param array $additional_libs List of additional libraries to add to activate the extra library features triggered by lib-suggests
|
|
||||||
* @return array Returns an array containing three arrays, [extensions, libraries, not included extensions]
|
|
||||||
* @throws WrongUsageException
|
* @throws WrongUsageException
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array
|
public static function platExtToLibs(): array
|
||||||
{
|
{
|
||||||
$sorted = [];
|
$exts = Config::getExts();
|
||||||
$visited = [];
|
$libs = Config::getLibs();
|
||||||
$not_included_exts = [];
|
$dep_list = [];
|
||||||
foreach ($exts as $ext) {
|
foreach ($exts as $ext_name => $ext) {
|
||||||
if (!isset($visited[$ext])) {
|
// convert ext-depends value to ext@xxx
|
||||||
self::visitExtDeps($ext, $visited, $sorted);
|
$ext_depends = Config::getExt($ext_name, 'ext-depends', []);
|
||||||
}
|
$ext_depends = array_map(fn ($x) => "ext@{$x}", $ext_depends);
|
||||||
|
// convert ext-suggests value to ext@xxx
|
||||||
|
$ext_suggests = Config::getExt($ext_name, 'ext-suggests', []);
|
||||||
|
$ext_suggests = array_map(fn ($x) => "ext@{$x}", $ext_suggests);
|
||||||
|
// merge ext-depends with lib-depends
|
||||||
|
$lib_depends = Config::getExt($ext_name, 'lib-depends', []);
|
||||||
|
$depends = array_merge($ext_depends, $lib_depends);
|
||||||
|
// merge ext-suggests with lib-suggests
|
||||||
|
$lib_suggests = Config::getExt($ext_name, 'lib-suggests', []);
|
||||||
|
$suggests = array_merge($ext_suggests, $lib_suggests);
|
||||||
|
$dep_list["ext@{$ext_name}"] = [
|
||||||
|
'depends' => $depends,
|
||||||
|
'suggests' => $suggests,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
$sorted_suggests = [];
|
foreach ($libs as $lib_name => $lib) {
|
||||||
$visited_suggests = [];
|
$dep_list[$lib_name] = [
|
||||||
$final = [];
|
'depends' => Config::getLib($lib_name, 'lib-depends', []),
|
||||||
foreach ($exts as $ext) {
|
'suggests' => Config::getLib($lib_name, 'lib-suggests', []),
|
||||||
if (!isset($visited_suggests[$ext])) {
|
];
|
||||||
self::visitExtAllDeps($ext, $visited_suggests, $sorted_suggests);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
foreach ($sorted_suggests as $suggest) {
|
// here is an array that only contains dependency map
|
||||||
if (in_array($suggest, $sorted)) {
|
return $dep_list;
|
||||||
$final[] = $suggest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$libs = $additional_libs;
|
|
||||||
|
|
||||||
foreach ($final as $ext) {
|
|
||||||
if (!in_array($ext, $exts)) {
|
|
||||||
$not_included_exts[] = $ext;
|
|
||||||
}
|
|
||||||
foreach (Config::getExt($ext, 'lib-depends', []) as $lib) {
|
|
||||||
if (!in_array($lib, $libs)) {
|
|
||||||
$libs[] = $lib;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [$final, self::getLibsByDeps($libs), $not_included_exts];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 lib 库的依赖关系进行一个排序,同时返回多出来的依赖列表
|
|
||||||
*
|
|
||||||
* @param array $libs 要排序的 libs 列表
|
|
||||||
* @return array 排序后的列表
|
|
||||||
* @throws FileSystemException
|
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws WrongUsageException
|
* @throws WrongUsageException
|
||||||
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
public static function getLibsByDeps(array $libs): array
|
public static function getLibs(array $libs, bool $include_suggested_libs = false): array
|
||||||
{
|
{
|
||||||
$sorted = [];
|
$dep_list = self::platExtToLibs();
|
||||||
$visited = [];
|
|
||||||
|
|
||||||
// 遍历所有
|
if ($include_suggested_libs) {
|
||||||
foreach ($libs as $lib) {
|
foreach ($dep_list as $name => $obj) {
|
||||||
if (!isset($visited[$lib])) {
|
foreach ($obj['suggests'] as $id => $suggest) {
|
||||||
self::visitLibDeps($lib, $visited, $sorted);
|
if (!str_starts_with($suggest, 'ext@')) {
|
||||||
|
$dep_list[$name]['depends'][] = $suggest;
|
||||||
|
array_splice($dep_list[$name]['suggests'], $id, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$final = self::doVisitPlat($libs, $dep_list);
|
||||||
|
|
||||||
|
$libs_final = [];
|
||||||
|
foreach ($final as $item) {
|
||||||
|
if (!str_starts_with($item, 'ext@')) {
|
||||||
|
$libs_final[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $libs_final;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws FileSystemException|WrongUsageException
|
||||||
|
*/
|
||||||
|
public static function getExtsAndLibs(array $exts, array $additional_libs = [], bool $include_suggested_exts = false, bool $include_suggested_libs = false): array
|
||||||
|
{
|
||||||
|
$dep_list = self::platExtToLibs();
|
||||||
|
|
||||||
|
// include suggested extensions
|
||||||
|
if ($include_suggested_exts) {
|
||||||
|
// check every deps suggests contains ext@
|
||||||
|
foreach ($dep_list as $name => $obj) {
|
||||||
|
foreach ($obj['suggests'] as $id => $suggest) {
|
||||||
|
if (str_starts_with($suggest, 'ext@')) {
|
||||||
|
$dep_list[$name]['depends'][] = $suggest;
|
||||||
|
array_splice($dep_list[$name]['suggests'], $id, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// include suggested libraries
|
||||||
|
if ($include_suggested_libs) {
|
||||||
|
// check every deps suggests
|
||||||
|
foreach ($dep_list as $name => $obj) {
|
||||||
|
foreach ($obj['suggests'] as $id => $suggest) {
|
||||||
|
if (!str_starts_with($suggest, 'ext@')) {
|
||||||
|
$dep_list[$name]['depends'][] = $suggest;
|
||||||
|
array_splice($dep_list[$name]['suggests'], $id, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert ext_name to ext@ext_name
|
||||||
|
$origin_exts = $exts;
|
||||||
|
$exts = array_map(fn ($x) => "ext@{$x}", $exts);
|
||||||
|
$exts = array_merge($exts, $additional_libs);
|
||||||
|
|
||||||
|
$final = self::doVisitPlat($exts, $dep_list);
|
||||||
|
|
||||||
|
// revert array
|
||||||
|
$exts_final = [];
|
||||||
|
$libs_final = [];
|
||||||
|
$not_included_final = [];
|
||||||
|
foreach ($final as $item) {
|
||||||
|
if (str_starts_with($item, 'ext@')) {
|
||||||
|
$tmp = substr($item, 4);
|
||||||
|
if (!in_array($tmp, $origin_exts)) {
|
||||||
|
$not_included_final[] = $tmp;
|
||||||
|
}
|
||||||
|
$exts_final[] = $tmp;
|
||||||
|
} else {
|
||||||
|
$libs_final[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [$exts_final, $libs_final, $not_included_final];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws WrongUsageException
|
||||||
|
*/
|
||||||
|
private static function doVisitPlat(array $deps, array $dep_list): array
|
||||||
|
{
|
||||||
|
// default: get extension exts and libs sorted by dep_list
|
||||||
|
$sorted = [];
|
||||||
|
$visited = [];
|
||||||
|
foreach ($deps as $ext_name) {
|
||||||
|
if (!isset($dep_list[$ext_name])) {
|
||||||
|
$ext_name = str_starts_with($ext_name, 'ext@') ? ('Extension [' . substr($ext_name, 4) . ']') : ('Library [' . $ext_name . ']');
|
||||||
|
throw new WrongUsageException("{$ext_name} not exist !");
|
||||||
|
}
|
||||||
|
if (!isset($visited[$ext_name])) {
|
||||||
|
self::visitPlatDeps($ext_name, $dep_list, $visited, $sorted);
|
||||||
|
}
|
||||||
|
}
|
||||||
$sorted_suggests = [];
|
$sorted_suggests = [];
|
||||||
$visited_suggests = [];
|
$visited_suggests = [];
|
||||||
$final = [];
|
$final = [];
|
||||||
foreach ($libs as $lib) {
|
foreach ($deps as $ext_name) {
|
||||||
if (!isset($visited_suggests[$lib])) {
|
if (!isset($visited_suggests[$ext_name])) {
|
||||||
self::visitLibAllDeps($lib, $visited_suggests, $sorted_suggests);
|
self::visitPlatAllDeps($ext_name, $dep_list, $visited_suggests, $sorted_suggests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($sorted_suggests as $suggest) {
|
foreach ($sorted_suggests as $suggest) {
|
||||||
@ -99,49 +173,7 @@ class DependencyUtil
|
|||||||
return $final;
|
return $final;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAllExtLibsByDeps(array $exts): array
|
private static function visitPlatAllDeps(string $lib_name, array $dep_list, array &$visited, array &$sorted): void
|
||||||
{
|
|
||||||
$sorted = [];
|
|
||||||
$visited = [];
|
|
||||||
$not_included_exts = [];
|
|
||||||
foreach ($exts as $ext) {
|
|
||||||
if (!isset($visited[$ext])) {
|
|
||||||
self::visitExtAllDeps($ext, $visited, $sorted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$libs = [];
|
|
||||||
foreach ($sorted as $ext) {
|
|
||||||
if (!in_array($ext, $exts)) {
|
|
||||||
$not_included_exts[] = $ext;
|
|
||||||
}
|
|
||||||
foreach (array_merge(Config::getExt($ext, 'lib-depends', []), Config::getExt($ext, 'lib-suggests', [])) as $dep) {
|
|
||||||
if (!in_array($dep, $libs)) {
|
|
||||||
$libs[] = $dep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [$sorted, self::getAllLibsByDeps($libs), $not_included_exts];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getAllLibsByDeps(array $libs): array
|
|
||||||
{
|
|
||||||
$sorted = [];
|
|
||||||
$visited = [];
|
|
||||||
|
|
||||||
foreach ($libs as $lib) {
|
|
||||||
if (!isset($visited[$lib])) {
|
|
||||||
self::visitLibAllDeps($lib, $visited, $sorted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sorted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws FileSystemException
|
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws WrongUsageException
|
|
||||||
*/
|
|
||||||
private static function visitLibAllDeps(string $lib_name, array &$visited, array &$sorted): void
|
|
||||||
{
|
{
|
||||||
// 如果已经识别到了,那就不管
|
// 如果已经识别到了,那就不管
|
||||||
if (isset($visited[$lib_name])) {
|
if (isset($visited[$lib_name])) {
|
||||||
@ -149,37 +181,13 @@ class DependencyUtil
|
|||||||
}
|
}
|
||||||
$visited[$lib_name] = true;
|
$visited[$lib_name] = true;
|
||||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||||
foreach (array_merge(Config::getLib($lib_name, 'lib-depends', []), Config::getLib($lib_name, 'lib-suggests', [])) as $dep) {
|
foreach (array_merge($dep_list[$lib_name]['depends'], $dep_list[$lib_name]['suggests']) as $dep) {
|
||||||
self::visitLibDeps($dep, $visited, $sorted);
|
self::visitPlatAllDeps($dep, $dep_list, $visited, $sorted);
|
||||||
}
|
}
|
||||||
$sorted[] = $lib_name;
|
$sorted[] = $lib_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static function visitPlatDeps(string $lib_name, array $dep_list, array &$visited, array &$sorted): void
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws FileSystemException
|
|
||||||
* @throws WrongUsageException
|
|
||||||
*/
|
|
||||||
private static function visitExtAllDeps(string $ext_name, array &$visited, array &$sorted): void
|
|
||||||
{
|
|
||||||
// 如果已经识别到了,那就不管
|
|
||||||
if (isset($visited[$ext_name])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$visited[$ext_name] = true;
|
|
||||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
|
||||||
foreach (array_merge(Config::getExt($ext_name, 'ext-depends', []), Config::getExt($ext_name, 'ext-suggests', [])) as $dep) {
|
|
||||||
self::visitExtDeps($dep, $visited, $sorted);
|
|
||||||
}
|
|
||||||
$sorted[] = $ext_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws FileSystemException
|
|
||||||
* @throws WrongUsageException
|
|
||||||
*/
|
|
||||||
private static function visitLibDeps(string $lib_name, array &$visited, array &$sorted): void
|
|
||||||
{
|
{
|
||||||
// 如果已经识别到了,那就不管
|
// 如果已经识别到了,那就不管
|
||||||
if (isset($visited[$lib_name])) {
|
if (isset($visited[$lib_name])) {
|
||||||
@ -187,26 +195,9 @@ class DependencyUtil
|
|||||||
}
|
}
|
||||||
$visited[$lib_name] = true;
|
$visited[$lib_name] = true;
|
||||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||||
foreach (Config::getLib($lib_name, 'lib-depends', []) as $dep) {
|
foreach ($dep_list[$lib_name]['depends'] as $dep) {
|
||||||
self::visitLibDeps($dep, $visited, $sorted);
|
self::visitPlatDeps($dep, $dep_list, $visited, $sorted);
|
||||||
}
|
}
|
||||||
$sorted[] = $lib_name;
|
$sorted[] = $lib_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws RuntimeException
|
|
||||||
* @throws FileSystemException
|
|
||||||
* @throws WrongUsageException
|
|
||||||
*/
|
|
||||||
private static function visitExtDeps(string $ext_name, array &$visited, array &$sorted): void
|
|
||||||
{
|
|
||||||
if (isset($visited[$ext_name])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$visited[$ext_name] = true;
|
|
||||||
foreach (Config::getExt($ext_name, 'ext-depends', []) as $dep) {
|
|
||||||
self::visitExtDeps($dep, $visited, $sorted);
|
|
||||||
}
|
|
||||||
$sorted[] = $ext_name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
|
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
|
||||||
$extensions = match (PHP_OS_FAMILY) {
|
$extensions = match (PHP_OS_FAMILY) {
|
||||||
'Linux', 'Darwin' => 'event',
|
'Linux', 'Darwin' => '',
|
||||||
'Windows' => 'mbstring,openssl',
|
'Windows' => 'mbstring',
|
||||||
};
|
};
|
||||||
|
|
||||||
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
|
// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
|
||||||
|
|||||||
90
tests/SPC/util/DependencyUtilTest.php
Normal file
90
tests/SPC/util/DependencyUtilTest.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\Tests\util;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use SPC\exception\WrongUsageException;
|
||||||
|
use SPC\store\Config;
|
||||||
|
use SPC\util\DependencyUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class DependencyUtilTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetExtLibsByDeps(): void
|
||||||
|
{
|
||||||
|
// example
|
||||||
|
Config::$source = [
|
||||||
|
'test1' => [
|
||||||
|
'type' => 'url',
|
||||||
|
'url' => 'https://pecl.php.net/get/APCu',
|
||||||
|
'filename' => 'apcu.tgz',
|
||||||
|
'license' => [
|
||||||
|
'type' => 'file',
|
||||||
|
'path' => 'LICENSE',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
Config::$lib = [
|
||||||
|
'libaaa' => [
|
||||||
|
'source' => 'test1',
|
||||||
|
'static-libs' => ['libaaa.a'],
|
||||||
|
'lib-depends' => ['libbbb', 'libccc'],
|
||||||
|
'lib-suggests' => ['libeee'],
|
||||||
|
],
|
||||||
|
'libbbb' => [
|
||||||
|
'source' => 'test1',
|
||||||
|
'static-libs' => ['libbbb.a'],
|
||||||
|
'lib-suggests' => ['libccc'],
|
||||||
|
],
|
||||||
|
'libccc' => [
|
||||||
|
'source' => 'test1',
|
||||||
|
'static-libs' => ['libccc.a'],
|
||||||
|
],
|
||||||
|
'libeee' => [
|
||||||
|
'source' => 'test1',
|
||||||
|
'static-libs' => ['libeee.a'],
|
||||||
|
'lib-suggests' => ['libfff'],
|
||||||
|
],
|
||||||
|
'libfff' => [
|
||||||
|
'source' => 'test1',
|
||||||
|
'static-libs' => ['libfff.a'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
Config::$ext = [
|
||||||
|
'ext-a' => [
|
||||||
|
'type' => 'builtin',
|
||||||
|
'lib-depends' => ['libaaa'],
|
||||||
|
'ext-suggests' => ['ext-b'],
|
||||||
|
],
|
||||||
|
'ext-b' => [
|
||||||
|
'type' => 'builtin',
|
||||||
|
'lib-depends' => ['libeee'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
// test getExtLibsByDeps (notmal test with ext-depends and lib-depends)
|
||||||
|
|
||||||
|
[$exts, $libs, $not_included] = DependencyUtil::getExtsAndLibs(['ext-a'], include_suggested_exts: true);
|
||||||
|
$this->assertContains('libbbb', $libs);
|
||||||
|
$this->assertContains('libccc', $libs);
|
||||||
|
$this->assertContains('ext-b', $exts);
|
||||||
|
$this->assertContains('ext-b', $not_included);
|
||||||
|
// test dep order
|
||||||
|
$this->assertIsInt($b = array_search('libbbb', $libs));
|
||||||
|
$this->assertIsInt($c = array_search('libccc', $libs));
|
||||||
|
$this->assertIsInt($a = array_search('libaaa', $libs));
|
||||||
|
// libbbb, libaaa
|
||||||
|
$this->assertTrue($b < $a);
|
||||||
|
$this->assertTrue($c < $a);
|
||||||
|
$this->assertTrue($c < $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNotExistExtException(): void
|
||||||
|
{
|
||||||
|
$this->expectException(WrongUsageException::class);
|
||||||
|
DependencyUtil::getExtsAndLibs(['sdsd']);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user