static-php-cli/src/SPC/builder/macos/MacOSBuilder.php

321 lines
12 KiB
PHP
Raw Normal View History

2023-03-18 17:32:21 +08:00
<?php
declare(strict_types=1);
namespace SPC\builder\macos;
use SPC\builder\macos\library\MacOSLibraryBase;
2024-01-10 21:08:25 +08:00
use SPC\builder\unix\UnixBuilderBase;
2023-03-18 17:32:21 +08:00
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
2023-03-29 21:39:36 +08:00
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
2023-04-30 12:42:19 +08:00
use SPC\store\SourcePatcher;
2024-04-07 15:52:24 +08:00
use SPC\util\GlobalEnvManager;
2023-03-18 17:32:21 +08:00
2024-01-10 21:08:25 +08:00
class MacOSBuilder extends UnixBuilderBase
2023-03-18 17:32:21 +08:00
{
/** @var bool Micro patch phar flag */
2023-03-18 17:32:21 +08:00
private bool $phar_patched = false;
/**
* @throws RuntimeException
2023-03-29 21:39:36 +08:00
* @throws WrongUsageException
* @throws FileSystemException
2023-03-18 17:32:21 +08:00
*/
public function __construct(array $options = [])
2023-03-18 17:32:21 +08:00
{
$this->options = $options;
2024-04-07 15:52:24 +08:00
// apply global environment variables
GlobalEnvManager::init($this);
// ---------- set necessary compile vars ----------
// concurrency
2024-04-07 15:52:24 +08:00
$this->concurrency = intval(getenv('SPC_CONCURRENCY'));
// cflags
2024-04-07 15:52:24 +08:00
$this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS');
$this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS');
// cmake toolchain
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', getenv('SPC_ARCH'), $this->arch_c_flags);
// create pkgconfig and include dir (some libs cannot create them automatically)
2023-03-18 17:32:21 +08:00
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
f_mkdir(BUILD_INCLUDE_PATH, recursive: true);
}
/**
* [deprecated] 生成库构建采用的 autoconf 参数列表
2023-03-18 17:32:21 +08:00
*
* @param string $name 要构建的 lib 库名,传入仅供输出日志
* @param array $lib_specs 依赖的 lib 库的 autoconf 文件
*/
public function makeAutoconfArgs(string $name, array $lib_specs): string
{
$ret = '';
foreach ($lib_specs as $libName => $arr) {
$lib = $this->getLib($libName);
$arr = $arr ?? [];
$disableArgs = $arr[0] ?? null;
if ($lib instanceof MacOSLibraryBase) {
logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support");
$ret .= '--with-' . $libName . '=yes ';
2023-03-18 17:32:21 +08:00
} else {
logger()->info("{$name} \033[31;1mwithout\033[0;1m {$libName} support");
$ret .= ($disableArgs ?? "--with-{$libName}=no") . ' ';
}
}
return rtrim($ret);
}
/**
* Get dynamically linked macOS frameworks
2023-03-18 17:32:21 +08:00
*
* @param bool $asString If true, return as string
* @throws FileSystemException
* @throws WrongUsageException
2023-03-18 17:32:21 +08:00
*/
public function getFrameworks(bool $asString = false): array|string
{
$libs = [];
// reorder libs
foreach ($this->libs as $lib) {
foreach ($lib->getDependencies() as $dep) {
$libs[] = $dep;
}
$libs[] = $lib;
}
$frameworks = [];
/** @var MacOSLibraryBase $lib */
foreach ($libs as $lib) {
array_push($frameworks, ...$lib->getFrameworks());
}
if ($asString) {
return implode(' ', array_map(fn ($x) => "-framework {$x}", $frameworks));
}
return $frameworks;
}
/**
* Just start to build statically linked php binary
*
2023-07-24 23:49:52 +08:00
* @param int $build_target build target
2023-03-18 17:32:21 +08:00
* @throws FileSystemException
2023-07-24 23:49:52 +08:00
* @throws RuntimeException
* @throws WrongUsageException
2023-03-18 17:32:21 +08:00
*/
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
2023-03-18 17:32:21 +08:00
{
2024-04-07 15:52:24 +08:00
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// ---------- Update extra-libs ----------
// add macOS frameworks
$extra_libs .= (empty($extra_libs) ? '' : ' ') . $this->getFrameworks(true);
// add libc++, some extensions or libraries need it (C++ cannot be linked statically)
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : '');
2024-04-07 15:52:24 +08:00
// bloat means force-load all static libraries, even if they are not used
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
2023-03-18 17:32:21 +08:00
} else {
logger()->info('bloat linking');
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles())));
2023-03-18 17:32:21 +08:00
}
2024-04-07 15:52:24 +08:00
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
2023-03-18 17:32:21 +08:00
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-php-buildconf');
SourcePatcher::patchBeforeBuildconf($this);
2023-03-18 17:32:21 +08:00
2024-04-07 15:52:24 +08:00
shell()->cd(SOURCE_PATH . '/php-src')->exec(getenv('SPC_CMD_PREFIX_PHP_BUILDCONF'));
2023-03-18 17:32:21 +08:00
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-php-configure');
SourcePatcher::patchBeforeConfigure($this);
2023-03-18 17:32:21 +08:00
$json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : '';
2023-03-18 17:32:21 +08:00
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
// prepare build php envs
2025-03-20 07:41:13 +01:00
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
$envs_build_php = SystemUtil::makeEnvVarString([
2024-04-07 15:52:24 +08:00
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'),
2025-03-20 07:41:13 +01:00
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
]);
if ($this->getLib('postgresql')) {
shell()
->cd(SOURCE_PATH . '/php-src')
->exec(
'sed -i.backup "s/ac_cv_func_explicit_bzero\" = xyes/ac_cv_func_explicit_bzero\" = x_fake_yes/" ./configure'
);
}
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
2023-04-15 18:45:34 +08:00
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
2024-04-07 15:52:24 +08:00
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
2023-09-02 21:25:33 +02:00
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
2023-09-02 21:25:33 +02:00
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$config_file_path .
$config_file_scan_dir .
$json_74 .
$zts .
$this->makeStaticExtensionArgs() . ' ' .
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
$envs_build_php
2023-04-15 18:45:34 +08:00
);
2023-03-18 17:32:21 +08:00
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-php-make');
SourcePatcher::patchBeforeMake($this);
2023-04-30 12:42:19 +08:00
$this->cleanMake();
2023-03-18 17:32:21 +08:00
if ($enableCli) {
2023-04-23 20:31:58 +08:00
logger()->info('building cli');
$this->buildCli();
2023-04-23 20:31:58 +08:00
}
if ($enableFpm) {
2023-04-23 20:31:58 +08:00
logger()->info('building fpm');
$this->buildFpm();
2023-04-23 20:31:58 +08:00
}
if ($enableMicro) {
2023-04-23 20:31:58 +08:00
logger()->info('building micro');
$this->buildMicro();
2023-03-18 17:32:21 +08:00
}
if ($enableEmbed) {
logger()->info('building embed');
if ($enableMicro) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
}
$this->buildEmbed();
}
2023-03-18 17:32:21 +08:00
$this->emitPatchPoint('before-sanity-check');
$this->sanityCheck($build_target);
2023-03-18 17:32:21 +08:00
}
2025-05-21 18:35:48 +07:00
public function testPHP(int $build_target = BUILD_TARGET_NONE)
{
$this->emitPatchPoint('before-sanity-check');
$this->sanityCheck($build_target);
}
2023-03-18 17:32:21 +08:00
/**
* Build cli sapi
2023-03-18 17:32:21 +08:00
*
* @throws RuntimeException
2023-04-23 20:31:58 +08:00
* @throws FileSystemException
*/
protected function buildCli(): void
2023-04-23 20:31:58 +08:00
{
2024-04-07 15:52:24 +08:00
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
2023-05-10 21:59:33 +08:00
$shell = shell()->cd(SOURCE_PATH . '/php-src');
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
$shell->exec("$SPC_CMD_PREFIX_PHP_MAKE {$vars} cli");
if (!$this->getOption('no-strip', false)) {
2023-05-10 21:59:33 +08:00
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php');
}
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_CLI);
}
/**
* Build phpmicro sapi
2023-04-23 20:31:58 +08:00
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
2023-03-18 17:32:21 +08:00
*/
protected function buildMicro(): void
2023-03-18 17:32:21 +08:00
{
2023-03-18 20:08:54 +08:00
if ($this->getPHPVersionID() < 80000) {
throw new WrongUsageException('phpmicro only support PHP >= 8.0!');
2023-03-18 20:08:54 +08:00
}
2023-03-18 17:32:21 +08:00
if ($this->getExt('phar')) {
$this->phar_patched = true;
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
2023-03-18 17:32:21 +08:00
}
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
2024-04-07 15:52:24 +08:00
$vars = $this->getMakeExtraVars();
// patch fake cli for micro
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
2024-05-30 22:01:40 +08:00
if ($this->getOption('no-strip', false)) {
$vars['STRIP'] = 'dsymutil -f ';
}
$vars = SystemUtil::makeEnvVarString($vars);
2024-04-07 15:52:24 +08:00
shell()->cd(SOURCE_PATH . '/php-src')->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} micro");
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_MICRO);
if ($this->phar_patched) {
SourcePatcher::unpatchMicroPhar();
}
2023-03-18 17:32:21 +08:00
}
/**
* Build fpm sapi
2023-03-18 17:32:21 +08:00
*
* @throws RuntimeException
2023-04-03 20:47:24 +08:00
* @throws FileSystemException
2023-03-18 17:32:21 +08:00
*/
protected function buildFpm(): void
2023-03-18 17:32:21 +08:00
{
2024-04-07 15:52:24 +08:00
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
2023-05-10 21:59:33 +08:00
$shell = shell()->cd(SOURCE_PATH . '/php-src');
2024-04-07 15:52:24 +08:00
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm");
if (!$this->getOption('no-strip', false)) {
2023-05-10 21:59:33 +08:00
$shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip sapi/fpm/php-fpm');
}
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_FPM);
2023-03-18 20:08:54 +08:00
}
/**
* Build embed sapi
*
* @throws RuntimeException
*/
protected function buildEmbed(): void
{
2024-04-07 15:52:24 +08:00
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
2024-04-07 15:52:24 +08:00
shell()->cd(SOURCE_PATH . '/php-src')
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install")
2023-08-30 18:14:59 +02:00
// Workaround for https://github.com/php/php-src/issues/12082
2023-08-30 17:47:29 +02:00
->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o')
->exec('mkdir ' . BUILD_ROOT_PATH . '/lib/php-o')
->cd(BUILD_ROOT_PATH . '/lib/php-o')
->exec('ar x ' . BUILD_ROOT_PATH . '/lib/libphp.a')
->exec('rm ' . BUILD_ROOT_PATH . '/lib/libphp.a')
->exec('ar rcs ' . BUILD_ROOT_PATH . '/lib/libphp.a *.o')
->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o');
$this->patchPhpScripts();
}
2024-04-07 15:52:24 +08:00
private function getMakeExtraVars(): array
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
{
return [
2024-04-07 15:52:24 +08:00
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LIBS' => getenv('SPC_EXTRA_LIBS') . ' ' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'),
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
];
}
2023-03-18 17:32:21 +08:00
}