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

258 lines
8.7 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\BuilderBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\builder\traits\UnixBuilderTrait;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
2023-03-29 21:39:36 +08:00
use SPC\exception\WrongUsageException;
2023-04-30 12:42:19 +08:00
use SPC\store\SourcePatcher;
2023-03-18 17:32:21 +08:00
/**
* macOS 系统环境下的构建器
* 源于 Config但因为感觉叫 Config 不太合适,就换成了 Builder
*/
class MacOSBuilder extends BuilderBase
{
/** 编译的 Unix 工具集 */
use UnixBuilderTrait;
/** @var bool 标记是否 patch 了 phar */
private bool $phar_patched = false;
/**
2023-03-29 21:39:36 +08:00
* @param null|string $cc C编译器名称如果不传入则默认使用clang
* @param null|string $cxx C++编译器名称如果不传入则默认使用clang++
* @param null|string $arch 当前架构,如果不传入则默认使用当前系统架构
2023-03-18 17:32:21 +08:00
* @throws RuntimeException
2023-03-29 21:39:36 +08:00
* @throws WrongUsageException
2023-03-18 17:32:21 +08:00
*/
2023-05-17 22:19:28 +08:00
public function __construct(?string $cc = null, ?string $cxx = null, ?string $arch = null, bool $zts = false)
2023-03-18 17:32:21 +08:00
{
// 如果是 Debug 模式,才使用 set -x 显示每条执行的命令
$this->set_x = defined('DEBUG_MODE') ? 'set -x' : 'true';
// 初始化一些默认参数
$this->cc = $cc ?? 'clang';
$this->cxx = $cxx ?? 'clang++';
$this->arch = $arch ?? php_uname('m');
$this->gnu_arch = arch2gnu($this->arch);
2023-05-17 22:19:28 +08:00
$this->zts = $zts;
2023-03-18 17:32:21 +08:00
// 根据 CPU 线程数设置编译进程数
$this->concurrency = SystemUtil::getCpuCount();
// 设置 cflags
$this->arch_c_flags = SystemUtil::getArchCFlags($this->arch);
$this->arch_cxx_flags = SystemUtil::getArchCFlags($this->arch);
// 设置 cmake
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->arch, $this->arch_c_flags);
// 设置 configure 依赖的环境变量
$this->configure_env =
'PKG_CONFIG="' . BUILD_ROOT_PATH . '/bin/pkg-config" ' .
2023-03-18 17:32:21 +08:00
'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' .
"CC='{$this->cc}' " .
"CXX='{$this->cxx}' " .
"CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration'";
// 创立 pkg-config 和放头文件的目录
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
f_mkdir(BUILD_INCLUDE_PATH, recursive: true);
}
/**
* 生成库构建采用的 autoconf 参数列表
*
* @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;
$prefix = $arr[1] ?? 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);
}
/**
* 返回 macOS 系统依赖的框架列表
*
* @param bool $asString 是否以字符串形式返回(默认为 False
*/
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;
}
/**
2023-07-24 23:49:52 +08:00
* @param int $build_target build target
* @param bool $bloat just raw add all lib files
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
*/
2023-04-23 20:31:58 +08:00
public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false): void
2023-03-18 17:32:21 +08:00
{
2023-07-26 00:01:12 +08:00
$extra_libs = $this->getFrameworks(true) . ' ' . ($this->hasCppExtension() ? '-lc++ ' : '');
2023-03-18 17:32:21 +08:00
if (!$bloat) {
$extra_libs .= implode(' ', $this->getAllStaticLibFiles());
} else {
logger()->info('bloat linking');
$extra_libs .= implode(
' ',
array_map(
fn ($x) => "-Wl,-force_load,{$x}",
array_filter($this->getAllStaticLibFiles())
)
);
}
// patch before configure
2023-04-30 12:42:19 +08:00
SourcePatcher::patchPHPBuildconf($this);
2023-03-18 17:32:21 +08:00
2023-04-15 18:45:34 +08:00
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
2023-03-18 17:32:21 +08:00
2023-04-30 12:42:19 +08:00
SourcePatcher::patchPHPConfigure($this);
2023-03-18 17:32:21 +08:00
if ($this->getLib('libxml2') || $this->getExt('iconv')) {
2023-03-18 17:39:08 +08:00
$extra_libs .= ' -liconv';
2023-03-18 17:32:21 +08:00
}
2023-05-04 11:29:55 +08:00
if ($this->getPHPVersionID() < 80000) {
$json_74 = '--enable-json ';
} else {
$json_74 = '';
}
2023-03-18 17:32:21 +08:00
2023-04-15 18:45:34 +08:00
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
'./configure ' .
'--prefix= ' .
'--with-valgrind=no ' . // 不检测内存泄漏
'--enable-shared=no ' .
'--enable-static=yes ' .
"CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " .
'--disable-all ' .
'--disable-cgi ' .
'--disable-phpdbg ' .
'--enable-cli ' .
2023-04-23 20:31:58 +08:00
'--enable-fpm ' .
2023-05-04 11:29:55 +08:00
$json_74 .
2023-04-15 18:45:34 +08:00
'--enable-micro ' .
($this->zts ? '--enable-zts' : '') . ' ' .
$this->makeExtensionArgs() . ' ' .
$this->configure_env
);
2023-03-18 17:32:21 +08:00
2023-04-30 12:42:19 +08:00
SourcePatcher::patchPHPAfterConfigure($this);
$this->cleanMake();
2023-03-18 17:32:21 +08:00
2023-04-23 20:31:58 +08:00
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('building cli');
$this->buildCli($extra_libs);
}
if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
logger()->info('building fpm');
$this->buildFpm($extra_libs);
}
if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
logger()->info('building micro');
$this->buildMicro($extra_libs);
2023-03-18 17:32:21 +08:00
}
if (php_uname('m') === $this->arch) {
2023-04-23 20:31:58 +08:00
$this->sanityCheck($build_target);
2023-03-18 17:32:21 +08:00
}
if ($this->phar_patched) {
2023-05-01 12:50:01 +08:00
SourcePatcher::patchMicro(['phar'], true);
2023-03-18 17:32:21 +08:00
}
}
/**
2023-04-23 20:31:58 +08:00
* 构建 cli
2023-03-18 17:32:21 +08:00
*
* @throws RuntimeException
2023-04-23 20:31:58 +08:00
* @throws FileSystemException
*/
public function buildCli(string $extra_libs): void
{
2023-05-10 21:59:33 +08:00
$shell = shell()->cd(SOURCE_PATH . '/php-src');
$shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli");
if ($this->strip) {
$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);
}
/**
* 构建 phpmicro
*
* @throws FileSystemException|RuntimeException
2023-03-18 17:32:21 +08:00
*/
public function buildMicro(string $extra_libs): void
{
2023-03-18 20:08:54 +08:00
if ($this->getPHPVersionID() < 80000) {
throw new RuntimeException('phpmicro only support PHP >= 8.0!');
}
2023-03-18 17:32:21 +08:00
if ($this->getExt('phar')) {
$this->phar_patched = true;
2023-05-01 12:50:01 +08:00
SourcePatcher::patchMicro(['phar']);
2023-03-18 17:32:21 +08:00
}
2023-04-03 20:47:24 +08:00
shell()->cd(SOURCE_PATH . '/php-src')
2023-05-10 21:59:33 +08:00
->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" " . ($this->strip ? 'STRIP="dsymutil -f " ' : '') . 'micro');
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_MICRO);
2023-03-18 17:32:21 +08:00
}
/**
2023-04-23 20:31:58 +08:00
* 构建 fpm
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
*/
2023-04-23 20:31:58 +08:00
public function buildFpm(string $extra_libs): void
2023-03-18 17:32:21 +08:00
{
2023-05-10 21:59:33 +08:00
$shell = shell()->cd(SOURCE_PATH . '/php-src');
$shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" fpm");
if ($this->strip) {
$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
}
2023-03-18 17:32:21 +08:00
}