mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-07 16:55:38 +08:00
initial commit for macOS support
This commit is contained in:
106
src/SPC/util/DependencyUtil.php
Normal file
106
src/SPC/util/DependencyUtil.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
|
||||
/**
|
||||
* 依赖处理工具类,包含处理扩展、库的依赖列表顺序等
|
||||
*/
|
||||
class DependencyUtil
|
||||
{
|
||||
/**
|
||||
* 根据需要的 ext 列表获取依赖的 lib 列表,同时根据依赖关系排序
|
||||
*
|
||||
* @param array $exts 要获取 libs 依赖的列表
|
||||
* @param array $additional_libs 额外要添加的库列表,用于激活 lib-suggests 触发的额外库特性
|
||||
* @return array 返回一个包含三个数组的数组,第一个是排序后的 ext 列表,第二个是排序后的 lib 列表,第三个是没有传入但是依赖了的 ext 列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array
|
||||
{
|
||||
// 先对扩展列表进行一个依赖筛选
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$not_included_exts = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited[$ext])) {
|
||||
self::visitExtDeps($ext, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
$libs = $additional_libs;
|
||||
// 遍历每一个 ext 的 libs
|
||||
foreach ($sorted 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 [$sorted, self::getLibsByDeps($libs), $not_included_exts];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 lib 库的依赖关系进行一个排序,同时返回多出来的依赖列表
|
||||
*
|
||||
* @param array $libs 要排序的 libs 列表
|
||||
* @return array 排序后的列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getLibsByDeps(array $libs): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
|
||||
// 遍历所有
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited[$lib])) {
|
||||
self::visitLibDeps($lib, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
private static function visitLibDeps(string $lib_name, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$lib_name])) {
|
||||
return;
|
||||
}
|
||||
$visited[$lib_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (Config::getLib($lib_name, 'lib-depends', []) as $dep) {
|
||||
self::visitLibDeps($dep, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $lib_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
271
src/SPC/util/Patcher.php
Normal file
271
src/SPC/util/Patcher.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class Patcher
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchPHPDepFiles(): void
|
||||
{
|
||||
$ver_file = SOURCE_PATH . '/php-src/main/php_version.h';
|
||||
if (!file_exists($ver_file)) {
|
||||
throw new FileSystemException('Patch failed, cannot find php source files');
|
||||
}
|
||||
$version_h = FileSystem::readFile(SOURCE_PATH . '/php-src/main/php_version.h');
|
||||
preg_match('/#\s*define\s+PHP_MAJOR_VERSION\s+(\d+)\s+#\s*define\s+PHP_MINOR_VERSION\s+(\d+)\s+/m', $version_h, $match);
|
||||
// $ver = "{$match[1]}.{$match[2]}";
|
||||
|
||||
logger()->info('Patching php');
|
||||
|
||||
$major_ver = $match[1] . $match[2];
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
// f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD');
|
||||
|
||||
$patch_list = [
|
||||
'static_opcache',
|
||||
'static_extensions_win32',
|
||||
'cli_checks',
|
||||
'disable_huge_page',
|
||||
'vcruntime140',
|
||||
'win32',
|
||||
'zend_stream',
|
||||
];
|
||||
$patch_list = array_merge($patch_list, match (PHP_OS_FAMILY) {
|
||||
'Windows' => [
|
||||
'cli_static',
|
||||
],
|
||||
'Darwin' => [
|
||||
'macos_iconv',
|
||||
],
|
||||
default => [],
|
||||
});
|
||||
$patches = [];
|
||||
$serial = ['80', '81', '82'];
|
||||
foreach ($patch_list as $patchName) {
|
||||
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
|
||||
$patches[] = "sapi/micro/patches/{$patchName}.patch";
|
||||
continue;
|
||||
}
|
||||
for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) {
|
||||
$tryMajMin = $serial[$i];
|
||||
if (!file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}_{$tryMajMin}.patch")) {
|
||||
continue;
|
||||
}
|
||||
$patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch";
|
||||
continue 2;
|
||||
}
|
||||
throw new RuntimeException("failed finding patch {$patchName}");
|
||||
}
|
||||
|
||||
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, implode(' ', $patches));
|
||||
|
||||
f_passthru(
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchOpenssl3(): void
|
||||
{
|
||||
logger()->info('Patching PHP with openssl 3.0');
|
||||
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
|
||||
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchSwow(): void
|
||||
{
|
||||
logger()->info('Patching swow');
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
|
||||
} else {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchPHPBeforeConfigure(BuilderBase $builder): void
|
||||
{
|
||||
if ($builder->getExt('curl')) {
|
||||
logger()->info('patching before-configure for curl checks');
|
||||
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
|
||||
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
|
||||
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])';
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
|
||||
}
|
||||
|
||||
// if ($builder->getExt('pdo_sqlite')) {
|
||||
// FileSystem::replaceFile()
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchPHPConfigure(BuilderBase $builder): void
|
||||
{
|
||||
$frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : '';
|
||||
$curl = $builder->getExt('curl');
|
||||
if ($curl) {
|
||||
logger()->info('patching configure for curl checks');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lcurl/',
|
||||
$curl->getLibFilesString() . $frameworks
|
||||
);
|
||||
}
|
||||
$bzip2 = $builder->getExt('bz2');
|
||||
if ($bzip2) {
|
||||
logger()->info('patching configure for bzip2 checks');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lbz2/',
|
||||
$bzip2->getLibFilesString() . $frameworks
|
||||
);
|
||||
}
|
||||
$pdo_sqlite = $builder->getExt('pdo_sqlite');
|
||||
if ($pdo_sqlite) {
|
||||
logger()->info('patching configure for pdo_sqlite linking');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/sqlite3_column_table_name=yes/',
|
||||
'sqlite3_column_table_name=no'
|
||||
);
|
||||
}
|
||||
logger()->info('patching configure for disable capstone');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_capstone="yes"/',
|
||||
'have_capstone="no"'
|
||||
);
|
||||
if (property_exists($builder, 'arch') && php_uname('m') !== $builder->arch) {
|
||||
// cross-compiling
|
||||
switch ($builder->arch) {
|
||||
case 'aarch64':
|
||||
case 'arm64':
|
||||
// almost all bsd/linux supports this
|
||||
logger()->info('patching configure for shm mmap checks (cross-compiling)');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_shm_mmap_anon=no/',
|
||||
'have_shm_mmap_anon=yes'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_shm_mmap_posix=no/',
|
||||
'have_shm_mmap_posix=yes'
|
||||
);
|
||||
break;
|
||||
case 'x86_64':
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('unsupported arch: ' . $builder->arch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchUnixLibpng(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lz',
|
||||
BUILD_LIB_PATH . '/libz.a'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchDarwinOpenssl11(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/openssl/test/v3ext.c',
|
||||
REPLACE_FILE_STR,
|
||||
'#include <stdio.h>',
|
||||
'#include <stdio.h>' . PHP_EOL . '#include <string.h>'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchLinuxPkgConfig(string $path): void
|
||||
{
|
||||
logger()->info("fixing pc {$path}");
|
||||
|
||||
$workspace = BUILD_ROOT_PATH;
|
||||
if ($workspace === '/') {
|
||||
$workspace = '';
|
||||
}
|
||||
|
||||
$content = file_get_contents($path);
|
||||
$content = preg_replace('/^prefix=.+$/m', "prefix={$workspace}", $content);
|
||||
$content = preg_replace('/^libdir=.+$/m', 'libdir=${prefix}/lib', $content);
|
||||
$content = preg_replace('/^includedir=.+$/m', 'includedir=${prefix}/include', $content);
|
||||
file_put_contents($path, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchLinuxConfigHeader(string $libc): void
|
||||
{
|
||||
switch ($libc) {
|
||||
case 'musl_wrapper':
|
||||
// bad checks
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', '');
|
||||
// no break
|
||||
case 'musl':
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_FUNC_ATTRIBUTE_IFUNC 1$/m', '');
|
||||
break;
|
||||
case 'glibc':
|
||||
// avoid lcrypt dependency
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_R 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_H 1$/m', '');
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('not implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user