Merge branch 'main' into ext/gettext

# Conflicts:
#	config/ext.json
#	src/globals/test-extensions.php
This commit is contained in:
crazywhalecc 2024-02-16 19:42:18 +08:00
commit e5d2d5e689
No known key found for this signature in database
GPG Key ID: 1F4BDD59391F2680
13 changed files with 300 additions and 190 deletions

View File

@ -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",

View File

@ -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.
*

View File

@ -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

View File

@ -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

View File

@ -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')) {

View File

@ -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);

View File

@ -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') {

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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`).

View 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']);
}
}