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-03-18 17:32:21 +08:00
|
|
|
|
use SPC\util\Patcher;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
public function __construct(?string $cc = null, ?string $cxx = null, ?string $arch = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果是 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);
|
|
|
|
|
|
// 根据 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_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 .= $lib->makeAutoconfEnv($prefix) . ' ';
|
|
|
|
|
|
} 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @throws RuntimeException
|
|
|
|
|
|
* @throws FileSystemException
|
|
|
|
|
|
*/
|
2023-03-26 22:27:51 +08:00
|
|
|
|
public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $bloat = false): void
|
2023-03-18 17:32:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
$extra_libs = $this->getFrameworks(true) . ' ' . ($this->getExt('swoole') ? '-lc++ ' : '');
|
|
|
|
|
|
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
|
|
|
|
|
|
Patcher::patchPHPBeforeConfigure($this);
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
Patcher::patchPHPConfigure($this);
|
|
|
|
|
|
|
|
|
|
|
|
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-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 ' .
|
|
|
|
|
|
"--host={$this->gnu_arch}-apple-darwin " .
|
|
|
|
|
|
"CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " .
|
|
|
|
|
|
'--disable-all ' .
|
|
|
|
|
|
'--disable-cgi ' .
|
|
|
|
|
|
'--disable-phpdbg ' .
|
|
|
|
|
|
'--enable-cli ' .
|
|
|
|
|
|
'--enable-micro ' .
|
|
|
|
|
|
($this->zts ? '--enable-zts' : '') . ' ' .
|
|
|
|
|
|
$this->makeExtensionArgs() . ' ' .
|
|
|
|
|
|
$this->configure_env
|
|
|
|
|
|
);
|
2023-03-18 17:32:21 +08:00
|
|
|
|
|
2023-03-26 22:27:51 +08:00
|
|
|
|
$this->cleanMake();
|
2023-03-18 17:32:21 +08:00
|
|
|
|
|
|
|
|
|
|
switch ($build_micro_rule) {
|
|
|
|
|
|
case BUILD_MICRO_NONE:
|
|
|
|
|
|
logger()->info('building cli');
|
|
|
|
|
|
$this->buildCli($extra_libs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case BUILD_MICRO_ONLY:
|
|
|
|
|
|
logger()->info('building micro');
|
|
|
|
|
|
$this->buildMicro($extra_libs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case BUILD_MICRO_BOTH:
|
|
|
|
|
|
logger()->info('building cli and micro');
|
|
|
|
|
|
$this->buildCli($extra_libs);
|
|
|
|
|
|
$this->buildMicro($extra_libs);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (php_uname('m') === $this->arch) {
|
|
|
|
|
|
$this->sanityCheck($build_micro_rule);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($this->phar_patched) {
|
|
|
|
|
|
f_passthru('cd ' . SOURCE_PATH . '/php-src && patch -p1 -R < sapi/micro/patches/phar.patch');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构建 phpmicro
|
|
|
|
|
|
*
|
|
|
|
|
|
* @throws RuntimeException
|
|
|
|
|
|
*/
|
|
|
|
|
|
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;
|
|
|
|
|
|
try {
|
2023-03-29 21:39:36 +08:00
|
|
|
|
// TODO: 未来改进一下 patch,让除了这种 patch 类型的文件以外可以恢复原文件
|
2023-04-03 20:47:24 +08:00
|
|
|
|
shell()->cd(SOURCE_PATH . '/php-src')->exec('patch -p1 < sapi/micro/patches/phar.patch');
|
2023-03-18 17:32:21 +08:00
|
|
|
|
} catch (RuntimeException $e) {
|
|
|
|
|
|
logger()->error('failed to patch phat due to patch exit with code ' . $e->getCode());
|
|
|
|
|
|
$this->phar_patched = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-03 20:47:24 +08:00
|
|
|
|
shell()->cd(SOURCE_PATH . '/php-src')
|
|
|
|
|
|
->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" STRIP=\"dsymutil -f \" micro");
|
|
|
|
|
|
$this->deployBinary(BUILD_TYPE_MICRO);
|
2023-03-18 17:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构建 cli
|
|
|
|
|
|
*
|
|
|
|
|
|
* @throws RuntimeException
|
2023-04-03 20:47:24 +08:00
|
|
|
|
* @throws FileSystemException
|
2023-03-18 17:32:21 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public function buildCli(string $extra_libs): void
|
|
|
|
|
|
{
|
2023-04-03 20:47:24 +08:00
|
|
|
|
shell()->cd(SOURCE_PATH . '/php-src')
|
|
|
|
|
|
// 生成调试信息、优化编译后的尺寸、禁用标识符(如变量、函数名)缩短
|
|
|
|
|
|
->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli")
|
|
|
|
|
|
->exec('dsymutil -f sapi/cli/php')
|
|
|
|
|
|
->exec('strip sapi/cli/php');
|
|
|
|
|
|
$this->deployBinary(BUILD_TYPE_CLI);
|
2023-03-18 20:08:54 +08:00
|
|
|
|
}
|
2023-03-18 17:32:21 +08:00
|
|
|
|
}
|