Merge pull request #826 from crazywhalecc/grpc

fix grpc support (use cmake, openssl)
This commit is contained in:
Jerry Ma 2025-07-23 22:03:55 +08:00 committed by GitHub
commit f543b55f19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 178 additions and 90 deletions

View File

@ -10,7 +10,7 @@ on:
env:
PHP_VERSION: 8.4
MICRO_VERSION: 8.4.4
MICRO_VERSION: 8.4.10
jobs:
build-release-artifacts:
@ -42,12 +42,13 @@ jobs:
run: echo "SPC_BUILD_DEBUG=--debug" >> $GITHUB_ENV
- name: "Install PHP for official runners"
uses: "shivammathur/setup-php@v2"
uses: shivammathur/setup-php@v2
with:
coverage: none
tools: composer:v2
php-version: "${{ env.PHP_VERSION }}"
ini-values: memory_limit=-1
extensions: curl, openssl, mbstring
- name: "Get Composer Cache Directory"
id: composer-cache

View File

@ -121,6 +121,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: 8.4
extensions: curl, openssl, mbstring
- name: Define
id: gendef
@ -198,3 +199,7 @@ jobs:
- name: "Run Build Tests (build - embed for non-windows)"
if: ${{ !startsWith(matrix.os, 'windows-') }}
run: php src/globals/test-extensions.php build_embed_cmd ${{ matrix.os }} ${{ matrix.php }}
# - name: Setup tmate session
# if: ${{ failure() }}
# uses: mxschmitt/action-tmate@v3

View File

@ -32,12 +32,13 @@ jobs:
cp -r config/* docs/.vitepress/config/
- name: "Install PHP for official runners"
uses: "shivammathur/setup-php@v2"
uses: shivammathur/setup-php@v2
with:
coverage: none
tools: composer:v2
php-version: 8.4
ini-values: memory_limit=-1
extensions: curl, openssl, mbstring
- name: "Get Composer Cache Directory"
id: composer-cache

View File

@ -34,7 +34,7 @@
},
"bz2": {
"type": "builtin",
"arg-type-unix": "with-prefix",
"arg-type-unix": "with-path",
"arg-type-windows": "with",
"lib-depends": [
"bzip2"
@ -185,7 +185,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"gettext"
]
@ -211,7 +211,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"gmp"
]
@ -233,7 +233,7 @@
},
"type": "external",
"source": "grpc",
"arg-type-unix": "custom",
"arg-type-unix": "enable-path",
"cpp-extension": true,
"lib-depends": [
"grpc"
@ -244,7 +244,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"arg-type-windows": "with",
"lib-depends-unix": [
"libiconv"
@ -320,7 +320,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"ldap"
],
@ -529,7 +529,7 @@
},
"notes": true,
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"libargon2"
]
@ -571,7 +571,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"arg-type-windows": "custom",
"ext-depends": [
"pdo",
@ -674,7 +674,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"readline"
],
@ -785,7 +785,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"arg-type-windows": "with",
"lib-depends": [
"sqlite"
@ -811,7 +811,7 @@
},
"type": "external",
"source": "ext-ssh2",
"arg-type": "with-prefix",
"arg-type": "with-path",
"arg-type-windows": "with",
"lib-depends": [
"libssh2"
@ -937,7 +937,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"tidy"
]
@ -953,7 +953,7 @@
},
"type": "external",
"source": "ext-uuid",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"libuuid"
]
@ -965,7 +965,7 @@
},
"type": "external",
"source": "ext-uv",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"libuv"
],
@ -1067,7 +1067,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"lib-depends": [
"libxslt"
],
@ -1104,7 +1104,7 @@
},
"type": "external",
"source": "yaml",
"arg-type-unix": "with-prefix",
"arg-type-unix": "with-path",
"arg-type-windows": "with",
"lib-depends": [
"libyaml"
@ -1115,7 +1115,7 @@
"BSD": "wip"
},
"type": "builtin",
"arg-type": "with-prefix",
"arg-type": "with-path",
"arg-type-windows": "enable",
"lib-depends-unix": [
"libzip"

View File

@ -186,14 +186,15 @@
},
"grpc": {
"source": "grpc",
"static-libs-unix": [
"libgrpc.a",
"libcares.a"
"pkg-configs": [
"grpc"
],
"lib-depends": [
"zlib",
"openssl"
"openssl",
"libcares"
],
"provide-pre-built": true,
"frameworks": [
"CoreFoundation"
]

View File

@ -83,13 +83,15 @@ class Extension
*/
public function getEnableArg(bool $shared = false): string
{
$escapedPath = str_replace("'", '', escapeshellarg(BUILD_ROOT_PATH)) !== BUILD_ROOT_PATH || str_contains(BUILD_ROOT_PATH, ' ') ? escapeshellarg(BUILD_ROOT_PATH) : BUILD_ROOT_PATH;
$_name = str_replace('_', '-', $this->name);
return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) {
'enable' => '--enable-' . $_name . ($shared ? '=shared' : '') . ' ',
'enable-path' => '--enable-' . $_name . '=' . ($shared ? 'shared,' : '') . $escapedPath . ' ',
'with' => '--with-' . $_name . ($shared ? '=shared' : '') . ' ',
'with-prefix' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . '"' . BUILD_ROOT_PATH . '" ',
'with-path' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . $escapedPath . ' ',
'none', 'custom' => '',
default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-prefix] ."),
default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-path] ."),
};
}

View File

@ -5,36 +5,47 @@ declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\builder\windows\WindowsBuilder;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
#[CustomExt('grpc')]
class grpc extends Extension
{
public function patchBeforeBuildconf(): bool
{
// soft link to the grpc source code
if ($this->builder instanceof WindowsBuilder) {
// not support windows yet
throw new \RuntimeException('grpc extension does not support windows yet');
}
if (!is_link(SOURCE_PATH . '/php-src/ext/grpc')) {
if (is_dir($this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc')) {
shell()->exec('ln -s ' . $this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
} elseif (is_dir(BUILD_ROOT_PATH . '/grpc_php_ext_src')) {
shell()->exec('ln -s ' . BUILD_ROOT_PATH . '/grpc_php_ext_src ' . SOURCE_PATH . '/php-src/ext/grpc');
} else {
throw new \RuntimeException('Cannot find grpc source code');
}
$macos = $this->builder instanceof MacOSBuilder ? "\n" . ' LDFLAGS="$LDFLAGS -framework CoreFoundation"' : '';
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/GRPC_LIBDIR=.*$/m', 'GRPC_LIBDIR=' . BUILD_LIB_PATH . $macos);
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/SEARCH_PATH=.*$/m', 'SEARCH_PATH="' . BUILD_ROOT_PATH . '"');
return true;
if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) {
return false;
}
return false;
// soft link to the grpc source code
if (is_dir($this->source_dir . '/src/php/ext/grpc')) {
shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
} else {
throw new \RuntimeException('Cannot find grpc source code');
}
if (SPCTarget::getTargetOS() === 'Darwin') {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/ext/grpc/config.m4',
'/GRPC_LIBDIR=.*$/m',
'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"'
);
}
return true;
}
public function patchBeforeConfigure(): bool
{
$util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]);
$config = $util->config(['grpc']);
$libs = $config['libs'];
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs);
return true;
}
public function patchBeforeMake(): bool
@ -43,9 +54,4 @@ class grpc extends Extension
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true;
}
public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH;
}
}

View File

@ -128,11 +128,22 @@ abstract class UnixBuilderBase extends BuilderBase
}
// if someone changed to --enable-embed=shared, we need to add LD_LIBRARY_PATH
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$ext_path = 'LD_LIBRARY_PATH=' . BUILD_ROOT_PATH . '/lib:$LD_LIBRARY_PATH ';
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '/lib/libphp.a');
if (PHP_OS_FAMILY === 'Darwin') {
$ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH ';
} else {
$ext_path = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$LD_LIBRARY_PATH ';
}
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '/libphp.a');
} else {
$ext_path = '';
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '/lib/libphp.so');
$suffix = PHP_OS_FAMILY === 'Darwin' ? 'dylib' : 'so';
foreach (glob(BUILD_LIB_PATH . "/libphp*.{$suffix}") as $file) {
unlink($file);
}
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
}
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
@ -147,8 +158,9 @@ abstract class UnixBuilderBase extends BuilderBase
if (!file_exists($frankenphp)) {
throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp);
}
$prefix = PHP_OS_FAMILY === 'Darwin' ? 'DYLD_' : 'LD_';
[$ret, $output] = shell()
->setEnv(['LD_LIBRARY_PATH' => BUILD_LIB_PATH])
->setEnv(["{$prefix}LIBRARY_PATH" => BUILD_LIB_PATH])
->execWithResult("{$frankenphp} version");
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']');

View File

@ -4,22 +4,55 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\builder\linux\SystemUtil;
use SPC\store\FileSystem;
use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget;
trait grpc
{
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileStr(
$this->source_dir . '/third_party/re2/util/pcre.h',
["#define UTIL_PCRE_H_\n#include <stdint.h>", '#define UTIL_PCRE_H_'],
['#define UTIL_PCRE_H_', "#define UTIL_PCRE_H_\n#include <stdint.h>"],
);
return true;
}
protected function build(): void
{
shell()->cd($this->source_dir)
->exec('EXTRA_DEFINES=GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK EMBED_OPENSSL=false CXXFLAGS="-L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '" make static -j' . $this->builder->concurrency);
copy($this->source_dir . '/libs/opt/libgrpc.a', BUILD_LIB_PATH . '/libgrpc.a');
copy($this->source_dir . '/libs/opt/libboringssl.a', BUILD_LIB_PATH . '/libboringssl.a');
if (!file_exists(BUILD_LIB_PATH . '/libcares.a')) {
copy($this->source_dir . '/libs/opt/libcares.a', BUILD_LIB_PATH . '/libcares.a');
$cmake = UnixCMakeExecutor::create($this)
->setBuildDir("{$this->source_dir}/avoid_BUILD_file_conflict")
->addConfigureArgs(
'-DgRPC_INSTALL_BINDIR=' . BUILD_BIN_PATH,
'-DgRPC_INSTALL_LIBDIR=' . BUILD_LIB_PATH,
'-DgRPC_INSTALL_SHAREDIR=' . BUILD_ROOT_PATH . '/share/grpc',
'-DCMAKE_C_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK -L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '"',
'-DCMAKE_CXX_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK -L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '"',
'-DgRPC_BUILD_CODEGEN=OFF',
'-DgRPC_DOWNLOAD_ARCHIVES=OFF',
'-DgRPC_BUILD_TESTS=OFF',
// providers
'-DgRPC_ZLIB_PROVIDER=package',
'-DgRPC_CARES_PROVIDER=package',
'-DgRPC_SSL_PROVIDER=package',
);
if (PHP_OS_FAMILY === 'Linux' && SPCTarget::isStatic() && !SystemUtil::isMuslDist()) {
$cmake->addConfigureArgs(
'-DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"',
'-DCMAKE_SHARED_LINKER_FLAGS="-static-libgcc -static-libstdc++"',
'-DCMAKE_CXX_STANDARD_LIBRARIES="-static-libgcc -static-libstdc++"',
);
}
FileSystem::copyDir($this->source_dir . '/include/grpc', BUILD_INCLUDE_PATH . '/grpc');
FileSystem::copyDir($this->source_dir . '/include/grpc++', BUILD_INCLUDE_PATH . '/grpc++');
FileSystem::copyDir($this->source_dir . '/include/grpcpp', BUILD_INCLUDE_PATH . '/grpcpp');
FileSystem::copyDir($this->source_dir . '/src/php/ext/grpc', BUILD_ROOT_PATH . '/grpc_php_ext_src');
$cmake->build();
$re2Content = file_get_contents($this->source_dir . '/third_party/re2/re2.pc');
$re2Content = 'prefix=' . BUILD_ROOT_PATH . "\nexec_prefix=\${prefix}\n" . $re2Content;
file_put_contents(BUILD_LIB_PATH . '/pkgconfig/re2.pc', $re2Content);
$this->patchPkgconfPrefix(['grpc++.pc', 'grpc.pc', 'grpc++_unsecure.pc', 'grpc_unsecure.pc', 're2.pc']);
}
}

View File

@ -143,6 +143,10 @@ class SPCConfigUtil
}
$pc_cflags = implode(' ', $pc);
if ($pc_cflags !== '' && ($pc_cflags = PkgConfigUtil::getCflags($pc_cflags)) !== '') {
$arr = explode(' ', $pc_cflags);
$arr = array_unique($arr);
$arr = array_filter($arr, fn ($x) => !str_starts_with($x, 'SHELL:-Xarch_'));
$pc_cflags = implode(' ', $arr);
$includes[] = $pc_cflags;
}
}
@ -161,19 +165,6 @@ class SPCConfigUtil
$frameworks = [];
foreach ($libraries as $library) {
// convert all static-libs to short names
$libs = Config::getLib($library, 'static-libs', []);
foreach ($libs as $lib) {
// check file existence
if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) {
throw new WrongUsageException("Library file '{$lib}' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first.");
}
$lib_names[] = $this->getShortLibName($lib);
}
// add frameworks for macOS
if (SPCTarget::getTargetOS() === 'Darwin') {
$frameworks = array_merge($frameworks, Config::getLib($library, 'frameworks', []));
}
// add pkg-configs libs
$pkg_configs = Config::getLib($library, 'pkg-configs', []);
foreach ($pkg_configs as $pkg_config) {
@ -187,10 +178,24 @@ class SPCConfigUtil
$pc_libs = array_reverse(PkgConfigUtil::getLibsArray($pkg_configs));
$lib_names = [...$lib_names, ...$pc_libs];
}
// convert all static-libs to short names
$libs = Config::getLib($library, 'static-libs', []);
foreach ($libs as $lib) {
// check file existence
if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) {
throw new WrongUsageException("Library file '{$lib}' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first.");
}
$lib_names[] = $this->getShortLibName($lib);
}
// add frameworks for macOS
if (SPCTarget::getTargetOS() === 'Darwin') {
$frameworks = array_merge($frameworks, Config::getLib($library, 'frameworks', []));
}
}
// post-process
$lib_names = array_reverse(array_unique(array_filter($lib_names, fn ($x) => $x !== '')));
$lib_names = array_filter($lib_names, fn ($x) => $x !== '');
$lib_names = array_reverse(array_unique($lib_names));
$frameworks = array_unique($frameworks);
// process frameworks to short_name

View File

@ -12,7 +12,7 @@ use SPC\util\UnixShell;
class UnixAutoconfExecutor extends Executor
{
protected ?UnixShell $shell = null;
protected UnixShell $shell;
protected array $configure_args = [];

View File

@ -5,25 +5,37 @@ declare(strict_types=1);
namespace SPC\util\executor;
use Closure;
use SPC\builder\freebsd\library\BSDLibraryBase;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\UnixShell;
/**
* Unix-like OS cmake command executor.
*/
class UnixCMakeExecutor extends Executor
{
protected ?string $build_dir = null;
protected UnixShell $shell;
protected array $configure_args = [];
protected ?string $build_dir = null;
protected ?array $custom_default_args = null;
protected int $steps = 3;
protected bool $reset = true;
public function __construct(protected BSDLibraryBase|LinuxLibraryBase|MacOSLibraryBase $library)
{
parent::__construct($library);
$this->initShell();
}
public function build(string $build_pos = '..'): void
{
// set cmake dir
@ -33,17 +45,16 @@ class UnixCMakeExecutor extends Executor
FileSystem::resetDir($this->build_dir);
}
// prepare shell
$shell = shell()->cd($this->build_dir)->initializeEnv($this->library);
$this->shell = $this->shell->cd($this->build_dir);
// config
$this->steps >= 1 && $shell->exec("cmake {$this->getConfigureArgs()} {$this->getDefaultCMakeArgs()} {$build_pos}");
$this->steps >= 1 && $this->shell->exec("cmake {$this->getConfigureArgs()} {$this->getDefaultCMakeArgs()} {$build_pos}");
// make
$this->steps >= 2 && $shell->exec("cmake --build . -j {$this->library->getBuilder()->concurrency}");
$this->steps >= 2 && $this->shell->exec("cmake --build . -j {$this->library->getBuilder()->concurrency}");
// install
$this->steps >= 3 && $shell->exec('make install');
$this->steps >= 3 && $this->shell->exec('make install');
}
/**
@ -77,6 +88,12 @@ class UnixCMakeExecutor extends Executor
return $this;
}
public function appendEnv(array $env): static
{
$this->shell->appendEnv($env);
return $this;
}
/**
* To build steps.
*
@ -209,4 +226,9 @@ CMAKE;
FileSystem::writeFile(SOURCE_PATH . '/toolchain.cmake', $toolchain);
return $created = realpath(SOURCE_PATH . '/toolchain.cmake');
}
private function initShell(): void
{
$this->shell = shell()->initializeEnv($this->library);
}
}

View File

@ -21,14 +21,14 @@ $test_php_version = [
// test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available)
$test_os = [
'macos-13', // bin/spc for x86_64
// 'macos-13', // bin/spc for x86_64
// 'macos-14', // bin/spc for arm64
'macos-15', // bin/spc for arm64
'ubuntu-latest', // bin/spc-alpine-docker for x86_64
'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
'ubuntu-24.04', // bin/spc for x86_64
// 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
// 'ubuntu-24.04-arm', // bin/spc for arm64
// 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
// 'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
'ubuntu-24.04-arm', // bin/spc for arm64
// 'windows-latest', // .\bin\spc.ps1
];
@ -48,8 +48,8 @@ $prefer_pre_built = false;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'pgsql,pdo_pgsql',
'Windows' => 'intl',
'Linux', 'Darwin' => 'grpc',
'Windows' => 'curl',
};
// If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`).
@ -60,7 +60,7 @@ $shared_extensions = match (PHP_OS_FAMILY) {
};
// If you want to test lib-suggests for all extensions and libraries, set it to true.
$with_suggested_libs = true;
$with_suggested_libs = false;
// If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true.
$with_libs = match (PHP_OS_FAMILY) {