mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-17 20:34:51 +08:00
Merge branch 'main' into ext/gettext
# Conflicts: # config/ext.json # src/globals/test-extensions.php
This commit is contained in:
commit
e5d2d5e689
@ -313,13 +313,6 @@
|
||||
"type": "external",
|
||||
"source": "protobuf"
|
||||
},
|
||||
"pspell": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"aspell"
|
||||
]
|
||||
},
|
||||
"rar": {
|
||||
"type": "external",
|
||||
"source": "rar",
|
||||
@ -371,13 +364,6 @@
|
||||
"apcu"
|
||||
]
|
||||
},
|
||||
"snmp": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"net-snmp"
|
||||
]
|
||||
},
|
||||
"soap": {
|
||||
"type": "builtin",
|
||||
"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');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
@ -107,7 +107,7 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
// if no libs specified, compile all supported libs
|
||||
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
||||
$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
|
||||
|
||||
@ -192,7 +192,7 @@ class WindowsBuilder extends BuilderBase
|
||||
// if no libs specified, compile all supported libs
|
||||
if ($sorted_libraries === [] && $this->isLibsOnly()) {
|
||||
$libraries = array_keys($support_lib_list);
|
||||
$sorted_libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
$sorted_libraries = DependencyUtil::getLibs($libraries);
|
||||
}
|
||||
|
||||
// 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->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
|
||||
$this->addOption('build-micro', null, null, 'build micro');
|
||||
$this->addOption('build-cli', null, null, 'build cli');
|
||||
$this->addOption('build-fpm', null, null, 'build fpm');
|
||||
$this->addOption('build-embed', null, null, 'build embed');
|
||||
$this->addOption('build-all', null, null, 'build cli, micro, fpm, embed');
|
||||
$this->addOption('build-micro', null, null, 'Build micro SAPI');
|
||||
$this->addOption('build-cli', null, null, 'Build cli SAPI');
|
||||
$this->addOption('build-fpm', null, null, 'Build fpm SAPI');
|
||||
$this->addOption('build-embed', null, null, 'Build embed SAPI');
|
||||
$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('enable-zts', null, null, 'enable ZTS support');
|
||||
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
||||
@ -61,8 +61,9 @@ class BuildCliCommand extends BuildCommand
|
||||
try {
|
||||
// create builder
|
||||
$builder = BuilderProvider::makeBuilderByInput($this->input);
|
||||
// calculate dependencies
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
|
||||
$include_suggest_ext = $this->getOption('with-suggested-exts');
|
||||
$include_suggest_lib = $this->getOption('with-suggested-libs');
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
|
||||
|
||||
// print info
|
||||
$indent_texts = [
|
||||
@ -76,12 +77,23 @@ class BuildCliCommand extends BuildCommand
|
||||
if (!empty($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)) {
|
||||
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);
|
||||
|
||||
if ($this->input->getOption('with-clean')) {
|
||||
|
||||
@ -60,7 +60,7 @@ class BuildLibsCommand extends BuildCommand
|
||||
// 只编译 library 的情况下,标记
|
||||
$builder->setLibsOnly();
|
||||
// 编译和检查库完整
|
||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
$libraries = DependencyUtil::getLibs($libraries);
|
||||
$builder->buildLibs($libraries);
|
||||
|
||||
$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
|
||||
{
|
||||
[$extensions, $libraries] = $include_suggests ? DependencyUtil::getAllExtLibsByDeps($extensions) : DependencyUtil::getExtLibsByDeps($extensions);
|
||||
[$extensions, $libraries] = $include_suggests ? DependencyUtil::getExtsAndLibs($extensions, [], true, true) : DependencyUtil::getExtsAndLibs($extensions);
|
||||
$sources = [];
|
||||
foreach ($extensions as $extension) {
|
||||
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('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('for-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', 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');
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ class DumpLicenseCommand extends BaseCommand
|
||||
// 从参数中获取要编译的 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->addLibs($libraries);
|
||||
if (!$this->getOption('without-php')) {
|
||||
@ -52,22 +52,22 @@ class DumpLicenseCommand extends BaseCommand
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return static::SUCCESS;
|
||||
}
|
||||
if ($this->getOption('by-libs') !== null) {
|
||||
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs'))));
|
||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
if ($this->getOption('for-libs') !== null) {
|
||||
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('for-libs'))));
|
||||
$libraries = DependencyUtil::getLibs($libraries);
|
||||
$dumper->addLibs($libraries);
|
||||
$dumper->dump($this->getOption('dump-dir'));
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
return static::SUCCESS;
|
||||
}
|
||||
if ($this->getOption('by-sources') !== null) {
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources'))));
|
||||
if ($this->getOption('for-sources') !== null) {
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getOption('for-sources'))));
|
||||
$dumper->addSources($sources);
|
||||
$dumper->dump($this->getOption('dump-dir'));
|
||||
$this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir'));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,6 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use function Laravel\Prompts\table;
|
||||
|
||||
#[AsCommand('dev:extensions', 'Helper command that lists available extension details', ['list-ext'])]
|
||||
class AllExtCommand extends BaseCommand
|
||||
{
|
||||
@ -61,7 +59,7 @@ class AllExtCommand extends BaseCommand
|
||||
}
|
||||
|
||||
try {
|
||||
[, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps([$extension]);
|
||||
[, $libraries, $not_included] = DependencyUtil::getExtsAndLibs([$extension]);
|
||||
} catch (WrongUsageException) {
|
||||
$libraries = $not_included = [];
|
||||
}
|
||||
@ -88,7 +86,8 @@ class AllExtCommand extends BaseCommand
|
||||
if ($data === []) {
|
||||
$style->warning('Unknown extension selected: ' . implode(',', $extensions));
|
||||
} else {
|
||||
table($columns, $data);
|
||||
$func = PHP_OS_FAMILY === 'Windows' ? [$style, 'table'] : '\Laravel\Prompts\table';
|
||||
call_user_func($func, $columns, $data);
|
||||
}
|
||||
|
||||
return static::SUCCESS;
|
||||
|
||||
@ -227,8 +227,12 @@ class FileSystem
|
||||
{
|
||||
$dir = self::convertPath($dir);
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!file_exists($dir)) {
|
||||
logger()->debug('Scan dir failed, no such file or directory.');
|
||||
return false;
|
||||
}
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning('Scan dir failed, no such directory.');
|
||||
logger()->warning('Scan dir failed, not directory.');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('scanning directory ' . $dir);
|
||||
@ -317,8 +321,12 @@ class FileSystem
|
||||
$dir = FileSystem::convertPath($dir);
|
||||
logger()->debug('Removing path recursively: "' . $dir . '"');
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!file_exists($dir)) {
|
||||
logger()->debug('Scan dir failed, no such file or directory.');
|
||||
return false;
|
||||
}
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning('Scan dir failed, no such directory.');
|
||||
logger()->warning('Scan dir failed, not directory.');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('scanning directory ' . $dir);
|
||||
|
||||
@ -5,90 +5,164 @@ declare(strict_types=1);
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
|
||||
/**
|
||||
* 依赖处理工具类,包含处理扩展、库的依赖列表顺序等
|
||||
* Dependency processing tool class, including processing extensions, library dependency list order, etc.
|
||||
*/
|
||||
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 RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array
|
||||
public static function platExtToLibs(): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$not_included_exts = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited[$ext])) {
|
||||
self::visitExtDeps($ext, $visited, $sorted);
|
||||
}
|
||||
$exts = Config::getExts();
|
||||
$libs = Config::getLibs();
|
||||
$dep_list = [];
|
||||
foreach ($exts as $ext_name => $ext) {
|
||||
// convert ext-depends value to ext@xxx
|
||||
$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 = [];
|
||||
$visited_suggests = [];
|
||||
$final = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited_suggests[$ext])) {
|
||||
self::visitExtAllDeps($ext, $visited_suggests, $sorted_suggests);
|
||||
}
|
||||
foreach ($libs as $lib_name => $lib) {
|
||||
$dep_list[$lib_name] = [
|
||||
'depends' => Config::getLib($lib_name, 'lib-depends', []),
|
||||
'suggests' => Config::getLib($lib_name, 'lib-suggests', []),
|
||||
];
|
||||
}
|
||||
foreach ($sorted_suggests as $suggest) {
|
||||
if (in_array($suggest, $sorted)) {
|
||||
$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];
|
||||
// here is an array that only contains dependency map
|
||||
return $dep_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 lib 库的依赖关系进行一个排序,同时返回多出来的依赖列表
|
||||
*
|
||||
* @param array $libs 要排序的 libs 列表
|
||||
* @return array 排序后的列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getLibsByDeps(array $libs): array
|
||||
public static function getLibs(array $libs, bool $include_suggested_libs = false): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$dep_list = self::platExtToLibs();
|
||||
|
||||
// 遍历所有
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited[$lib])) {
|
||||
self::visitLibDeps($lib, $visited, $sorted);
|
||||
if ($include_suggested_libs) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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 = [];
|
||||
$visited_suggests = [];
|
||||
$final = [];
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited_suggests[$lib])) {
|
||||
self::visitLibAllDeps($lib, $visited_suggests, $sorted_suggests);
|
||||
foreach ($deps as $ext_name) {
|
||||
if (!isset($visited_suggests[$ext_name])) {
|
||||
self::visitPlatAllDeps($ext_name, $dep_list, $visited_suggests, $sorted_suggests);
|
||||
}
|
||||
}
|
||||
foreach ($sorted_suggests as $suggest) {
|
||||
@ -99,49 +173,7 @@ class DependencyUtil
|
||||
return $final;
|
||||
}
|
||||
|
||||
public static function getAllExtLibsByDeps(array $exts): array
|
||||
{
|
||||
$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
|
||||
private static function visitPlatAllDeps(string $lib_name, array $dep_list, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$lib_name])) {
|
||||
@ -149,37 +181,13 @@ class DependencyUtil
|
||||
}
|
||||
$visited[$lib_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (array_merge(Config::getLib($lib_name, 'lib-depends', []), Config::getLib($lib_name, 'lib-suggests', [])) as $dep) {
|
||||
self::visitLibDeps($dep, $visited, $sorted);
|
||||
foreach (array_merge($dep_list[$lib_name]['depends'], $dep_list[$lib_name]['suggests']) as $dep) {
|
||||
self::visitPlatAllDeps($dep, $dep_list, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $lib_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
private static function visitPlatDeps(string $lib_name, array $dep_list, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$lib_name])) {
|
||||
@ -187,26 +195,9 @@ class DependencyUtil
|
||||
}
|
||||
$visited[$lib_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (Config::getLib($lib_name, 'lib-depends', []) as $dep) {
|
||||
self::visitLibDeps($dep, $visited, $sorted);
|
||||
foreach ($dep_list[$lib_name]['depends'] as $dep) {
|
||||
self::visitPlatDeps($dep, $dep_list, $visited, $sorted);
|
||||
}
|
||||
$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`).
|
||||
$extensions = match (PHP_OS_FAMILY) {
|
||||
'Linux', 'Darwin' => 'gettext',
|
||||
'Windows' => 'mbstring,openssl',
|
||||
'Linux', 'Darwin' => 'event,gettext',
|
||||
'Windows' => 'mbstring',
|
||||
};
|
||||
|
||||
// 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