initial commit for macOS support

This commit is contained in:
crazywhalecc 2023-03-18 17:32:21 +08:00
parent 64054f16c5
commit 4eee09c390
No known key found for this signature in database
GPG Key ID: 1F4BDD59391F2680
50 changed files with 4385 additions and 6 deletions

16
bin/spc Executable file
View File

@ -0,0 +1,16 @@
#!php
<?php
require_once __DIR__ . '/../vendor/autoload.php';
// 防止 Micro 打包状态下不支持中文的显示(虽然这个项目目前好像没输出过中文?)
if (PHP_OS_FAMILY === 'Windows' && Phar::running()) {
exec('CHCP 65001');
}
// 跑,反正一条命令跑就对了
try {
(new \SPC\ConsoleApplication())->run();
} catch (Exception $e) {
\SPC\exception\ExceptionHandler::getInstance()->handle($e);
}

387
config/ext.json Normal file
View File

@ -0,0 +1,387 @@
{
"bcmath": {
"type": "builtin"
},
"bz2": {
"type": "builtin",
"arg-type": "custom",
"lib-depends": [
"bzip2"
]
},
"calendar": {
"type": "builtin"
},
"ctype": {
"type": "builtin"
},
"curl": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"curl"
]
},
"dba": {
"type": "builtin",
"arg-type-windows": "with"
},
"dom": {
"type": "builtin",
"arg-type-windows": "with",
"lib-depends": [
"libxml2"
]
},
"enchant": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"enchant2"
]
},
"exif": {
"type": "builtin"
},
"ffi": {
"arg-type": "with",
"type": "builtin",
"lib-depends": [
"libffi"
]
},
"fileinfo": {
"type": "builtin"
},
"filter": {
"type": "builtin"
},
"ftp": {
"type": "builtin",
"lib-suggests": [
"openssl"
]
},
"gd": {
"type": "builtin",
"arg-type-windows": "with",
"lib-depends": [
"zlib",
"libpng"
],
"lib-suggests": [
"gd",
"libavif",
"libwebp",
"libjpeg",
"xpm",
"libfreetype"
],
"lib-depends-windows": [
"libiconv",
"libfreetype",
"libjpeg",
"zlib",
"libpng",
"xpm"
],
"lib-suggests-windows": [
"gd",
"libavif",
"libwebp"
]
},
"gettext": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"gettext"
]
},
"gmp": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"gmp"
]
},
"iconv": {
"type": "builtin",
"arg-type": "with",
"lib-depends-windows": [
"libiconv"
]
},
"imap": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"imap"
],
"lib-suggests": [
"kerberos"
]
},
"intl": {
"type": "builtin",
"lib-depends": [
"icu"
]
},
"ldap": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"ldap"
]
},
"mbregex": {
"type": "builtin",
"lib-depends": [
"onig"
]
},
"mbstring": {
"type": "builtin",
"lib-depends": [
"onig"
]
},
"mysqli": {
"type": "builtin",
"arg-type": "with",
"ext-depends": [
"mysqlnd"
]
},
"mysqlnd": {
"type": "builtin",
"arg-type-windows": "with"
},
"opcache": {
"type": "builtin"
},
"openssl": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"openssl"
]
},
"pcntl": {
"type": "builtin",
"unix-only": true
},
"pdo": {
"type": "builtin"
},
"pdo_mysql": {
"type": "builtin",
"arg-type": "with",
"ext-depends": [
"pdo",
"mysqlnd"
]
},
"pdo_pgsql": {
"type": "builtin",
"arg-type": "with",
"ext-depends": [
"pdo"
],
"lib-depends": [
"pq"
]
},
"pdo_sqlite": {
"type": "builtin",
"arg-type": "with",
"ext-depends": [
"pdo",
"sqlite3"
],
"lib-depends": [
"sqlite"
]
},
"phar": {
"type": "builtin",
"lib-suggests": [
"zlib"
]
},
"posix": {
"type": "builtin",
"unix-only": true
},
"pspell": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"aspell"
]
},
"readline": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"readline"
],
"lib-suggests": [
"libedit",
"ncurses"
]
},
"redis": {
"type": "external",
"source": "redis"
},
"session": {
"type": "builtin"
},
"shmop": {
"type": "builtin"
},
"simplexml": {
"type": "builtin",
"arg-type-windows": "with",
"lib-depends": [
"libxml2"
]
},
"snmp": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"net-snmp"
]
},
"soap": {
"type": "builtin",
"lib-depends": [
"libxml2"
]
},
"sockets": {
"type": "builtin"
},
"sodium": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"sodium"
]
},
"sqlite3": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"sqlite"
]
},
"swoole": {
"type": "external",
"source": "swoole",
"lib-depends": [
"openssl",
"curl"
],
"ext-depends": [
"openssl"
],
"ext-suggests": [
"curl"
],
"unix-only": true
},
"swow": {
"type": "external",
"source": "swow",
"lib-suggests": [
"openssl",
"curl"
],
"ext-suggests": [
"curl"
]
},
"sysvmsg": {
"type": "builtin",
"unix-only": true
},
"sysvsem": {
"type": "builtin",
"unix-only": true
},
"sysvshm": {
"type": "builtin",
"unix-only": true
},
"tidy": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"tidy"
]
},
"tokenizer": {
"type": "builtin"
},
"xml": {
"type": "builtin",
"arg-type-windows": "with",
"lib-depends": [
"libxml2"
]
},
"xmlreader": {
"type": "builtin",
"lib-depends": [
"libxml2"
]
},
"xmlwriter": {
"type": "builtin",
"lib-depends": [
"libxml2"
]
},
"xsl": {
"type": "builtin",
"arg-type": "with",
"lib-depends": [
"libxslt"
]
},
"yaml": {
"type": "external",
"source": "yaml",
"arg-type": "with",
"lib-depends": [
"libyaml"
]
},
"zip": {
"type": "builtin",
"arg-type": "with",
"arg-type-windows": "enable",
"lib-depends": [
"libzip"
]
},
"zlib": {
"type": "builtin",
"arg-type": "with",
"arg-type-windows": "enable",
"lib-depends": [
"zlib"
]
},
"zstd": {
"type": "external",
"source": "ext-zstd",
"lib-depends": [
"zstd"
]
}
}

330
config/lib.json Normal file
View File

@ -0,0 +1,330 @@
{
"brotli": {
"source": "brotli",
"static-libs-unix": [
"libbrotlidec-static.a",
"libbrotlienc-static.a",
"libbrotlicommon-static.a"
],
"static-libs-windows": [
"brotlicommon-static.lib",
"brotlienc-static.lib",
"brotlidec-static.lib"
],
"headers": [
"brotli"
]
},
"bzip2": {
"source": "bzip2",
"static-libs-unix": [
"libbz2.a"
],
"static-libs-windows": [
[
"libbz2.lib",
"libbz2_a.lib"
]
],
"headers": [
"bzlib.h"
]
},
"curl": {
"source": "curl",
"static-libs-unix": [
"libcurl.a"
],
"static-libs-windows": [
"libcurl.lib"
],
"headers": [
"curl"
],
"lib-depends-unix": [
"zlib"
],
"lib-suggests": [
"libssh2",
"brotli",
"nghttp2",
"zstd",
"openssl",
"idn2",
"psl"
],
"lib-suggests-windows": [
"zlib",
"libssh2",
"brotli",
"nghttp2",
"zstd",
"openssl",
"idn2",
"psl"
],
"frameworks": [
"CoreFoundation",
"SystemConfiguration"
]
},
"libffi": {
"source": "libffi",
"static-libs-unix": [
"libffi.a"
],
"static-libs-windows": [
"libffi.lib"
],
"headers-unix": [
"ffi.h",
"ffitarget.h"
],
"headers-windows": [
"ffi.h",
"fficonfig.h",
"ffitarget.h"
]
},
"libpng": {
"source": "libpng",
"static-libs-unix": [
"libpng.a"
],
"static-libs-windows": [
"libpng16_static.lib"
],
"headers-unix": [
"png.h",
"pngconf.h",
"pnglibconf.h"
],
"headers-windows": [
"png.h",
"pngconf.h"
],
"lib-depends": [
"zlib"
]
},
"libssh2": {
"source": "libssh2",
"static-libs-unix": [
"libssh2.a"
],
"static-libs-windows": [
"libssh2.lib"
],
"headers": [
"libssh2.h",
"libssh2_publickey.h",
"libssh2_sftp.h"
],
"lib-depends": [
"openssl"
],
"lib-suggests": [
"zlib"
]
},
"libxml2": {
"source": "libxml2",
"static-libs-unix": [
"libxml2.a"
],
"static-libs-windows": [
[
"libxml2s.lib",
"libxml2_a.lib"
]
],
"headers": [
"libxml2"
],
"lib-suggests": [
"icu",
"xz",
"zlib"
],
"lib-suggests-windows": [
"icu",
"xz",
"zlib",
"pthreads4w"
]
},
"libyaml": {
"source": "libyaml",
"static-libs-unix": [
"libyaml.a"
],
"static-libs-windows": [
"yaml.lib"
],
"headers": [
"yaml.h"
]
},
"libzip": {
"source": "libzip",
"static-libs-unix": [
"libzip.a"
],
"static-libs-windows": [
[
"zip.lib",
"libzip_a.lib"
]
],
"headers": [
"zip.h",
"zipconf.h"
],
"lib-depends": [
"zlib"
],
"lib-suggests": [
"bzip2",
"xz",
"zstd",
"openssl"
]
},
"nghttp2": {
"source": "nghttp2",
"static-libs-unix": [
"libnghttp2.a"
],
"static-libs-windows": [
"nghttp2.lib"
],
"headers": [
"nghttp2"
],
"lib-depends": [
"zlib",
"openssl"
],
"lib-suggests": [
"libxml2",
"libev",
"libcares",
"libngtcp2",
"libnghttp3",
"libbpf",
"libevent-openssl",
"jansson",
"jemalloc",
"systemd",
"cunit"
]
},
"onig": {
"source": "onig",
"static-libs-unix": [
"libonig.a"
],
"static-libs-windows": [
[
"onig.lib",
"onig_a.lib"
]
],
"headers": [
"oniggnu.h",
"oniguruma.h"
]
},
"openssl": {
"source": "openssl",
"static-libs-unix": [
"libssl.a",
"libcrypto.a"
],
"static-libs-windows": [
"libssl.lib",
"libcrypto.lib"
],
"headers": [
"openssl"
],
"lib-suggests": [
"zlib"
]
},
"pthreads4w": {
"source": "pthreads4w",
"static-libs-windows": [
"libpthreadVC3.lib"
],
"headers-windows": [
"_ptw32.h",
"pthread.h",
"sched.h",
"semaphore.h"
]
},
"sqlite": {
"source": "sqlite",
"static-libs-unix": [
"libsqlite3.a"
],
"headers-unix": [
"sqlite3.h",
"sqlite3ext.h"
]
},
"xz": {
"source": "xz",
"static-libs-unix": [
"liblzma.a"
],
"static-libs-windows": [
[
"liblzma.lib",
"liblzma_a.lib"
]
],
"headers-unix": [
"lzma"
],
"headers-windows": [
"lzma",
"lzma.h"
]
},
"zlib": {
"source": "zlib",
"static-libs-unix": [
"libz.a"
],
"static-libs-windows": [
"zlib_a.lib"
],
"headers": [
"zlib.h",
"zconf.h"
]
},
"zstd": {
"source": "zstd",
"static-libs-unix": [
"libzstd.a"
],
"static-libs-windows": [
[
"zstd.lib",
"zstd_static.lib"
]
],
"headers-unix": [
"zdict.h",
"zstd.h",
"zstd_errors.h"
],
"headers-windows": [
"zstd.h",
"zstd_errors.h"
]
}
}

220
config/source.json Normal file

File diff suppressed because one or more lines are too long

59
ext-support.md Normal file
View File

@ -0,0 +1,59 @@
# Extension List
> - yes: supported and tested
> - untested: supported but not tested
> - empty: not supported yet
> - issue link: not supported yet due to issue
| | Linux | macOS | Windows |
|------------|-------|----------|---------|
| bcmath | | yes | |
| bz2 | | untested | |
| calendar | | yes | |
| ctype | | | |
| curl | | yes | |
| date | | yes | |
| dom | | | |
| event | | | |
| exif | | | |
| filter | | | |
| fileinfo | | | |
| ftp | | | |
| gd | | untested | |
| gmp | | untested | |
| hash | | yes | |
| iconv | | | |
| inotify | | | |
| json | | yes | |
| libxml | | | |
| mbstring | | | |
| mcrypt | | | |
| mongodb | | | |
| mysqli | | | |
| mysqlnd | | | |
| openssl | | yes | |
| pcntl | | untested | |
| pcre | | yes | |
| pdo | | yes | |
| pdo_mysql | | | |
| pdo_sqlite | | yes | |
| pdo_pgsql | | | |
| phar | | | |
| posix | | | |
| protobuf | | | |
| readline | | | |
| redis | | | |
| Reflection | | yes | |
| shmop | | | |
| simplexml | | | |
| soap | | | |
| sockets | | | |
| sqlite3 | | untested | |
| swow | | | |
| swoole | | yes | |
| tokenizer | | | |
| xml | | | |
| xmlreader | | | |
| xmlwriter | | | |
| zip | | | |
| zlib | | | |

View File

@ -0,0 +1,227 @@
<?php
declare(strict_types=1);
namespace SPC\builder;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\util\DependencyUtil;
abstract class BuilderBase
{
/** @var bool 是否启用 ZTS 线程安全 */
public bool $zts = false;
/** @var string 编译目标架构 */
public string $arch;
/** @var string GNU 格式的编译目标架构 */
public string $gnu_arch;
/** @var int 编译进程数 */
public int $concurrency = 1;
/** @var array<string, LibraryBase> 要编译的 libs 列表 */
protected array $libs = [];
/** @var array<string, Extension> 要编译的扩展列表 */
protected array $exts = [];
/** @var bool 本次编译是否只编译 libs不编译 PHP */
protected bool $libs_only = false;
/**
* 构建指定列表的 libs
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function buildLibs(array $libraries): void
{
// 通过扫描目录查找 lib
$support_lib_list = [];
$classes = FileSystem::getClassesPsr4(
ROOT_DIR . '/src/SPC/builder/' . osfamily2dir() . '/library',
'SPC\\builder\\' . osfamily2dir() . '\\library'
);
foreach ($classes as $class) {
if (defined($class . '::NAME') && $class::NAME !== 'unknown' && Config::getLib($class::NAME) !== null) {
$support_lib_list[$class::NAME] = $class;
}
}
// 如果传入了空则默认检查和安置所有支持的liblibraries为要build的support_lib_list为支持的列表
if ($libraries === [] && $this->isLibsOnly()) {
$libraries = array_keys($support_lib_list);
}
// 排序 libs根据依赖计算一个新的列表出来
$libraries = DependencyUtil::getLibsByDeps($libraries);
// 这里筛选 libraries比如纯静态模式排除掉ffi
if (defined('BUILD_ALL_STATIC') && BUILD_ALL_STATIC) {
$k = array_search('libffi', $libraries, true);
$k !== false && array_splice($libraries, $k, 1);
}
// 过滤不支持的库后添加
foreach ($libraries as $library) {
if (!isset($support_lib_list[$library])) {
throw new RuntimeException('library [' . $library . '] is in the lib.json list but not supported to compile, but in the future I will support it!');
}
$lib = new ($support_lib_list[$library])($this);
$this->addLib($lib);
}
// 统计还没 fetch 到本地的库
$this->checkLibsSource();
// 计算依赖,经过这里的遍历,如果没有抛出异常,说明依赖符合要求,可以继续下面的
foreach ($this->libs as $lib) {
$lib->calcDependency();
}
foreach ($this->libs as $lib) {
match ($lib->tryBuild()) {
BUILD_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] build success'),
BUILD_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'),
BUILD_STATUS_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'),
default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'),
};
}
}
/**
* 添加要编译的 Lib
*
* @param LibraryBase $library Lib 库对象
*/
public function addLib(LibraryBase $library): void
{
$this->libs[$library::NAME] = $library;
}
/**
* 获取要编译的 Lib 库对象
*
* @param string $name 库名称
*/
public function getLib(string $name): ?LibraryBase
{
return $this->libs[$name] ?? null;
}
/**
* 添加要编译的扩展
*
* @param Extension $extension 扩展对象
*/
public function addExt(Extension $extension): void
{
$this->exts[$extension->getName()] = $extension;
}
/**
* 获取要编译的扩展对象
*
* @param string $name 扩展名称
*/
public function getExt(string $name): ?Extension
{
return $this->exts[$name] ?? null;
}
/**
* 设置本次 Builder 是否为仅编译库的模式
*/
public function setLibsOnly(bool $status = true): void
{
$this->libs_only = $status;
}
/**
* 检验 ext 扩展列表是否合理,并声明 Extension 对象,检查扩展的依赖
*
* @throws FileSystemException
* @throws RuntimeException
*/
public function proveExts(array $extensions): void
{
if (defined('BUILD_ALL_STATIC') && BUILD_ALL_STATIC) {
$k = array_search('ffi', $extensions, true);
$k !== false && array_splice($extensions, $k, 1);
}
foreach ($extensions as $extension) {
$ext = new Extension($extension, $this);
$this->addExt($ext);
}
foreach ($this->exts as $ext) {
// 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理
$ext->checkDependency();
}
}
/**
* 开始构建 PHP
* 构建 micro 的规则:
* - BUILD_MICRO_NONE(默认):只编译 cli
* - BUILD_MICRO_ONLY只编译 micro
* - BUILD_MICRO_BOTH同时编译 micro cli
*
* @param int $build_micro_rule 规则
* @param bool $with_clean 是否为新构建?
* @param bool $bloat 保留
*/
abstract public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false);
/**
* 生成依赖的扩展编译启用参数
* 例如 --enable-mbstring
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function makeExtensionArgs(): string
{
$ret = [];
foreach ($this->exts as $ext) {
$ret[] = $ext->getConfigureArg();
}
logger()->info('Using configure: ' . implode(' ', $ret));
return implode(' ', $ret);
}
/**
* 返回是否只编译 libs 的模式
*/
public function isLibsOnly(): bool
{
return $this->libs_only;
}
/**
* 检查是否存在 lib 库对应的源码,如果不存在,则抛出异常
*
* @throws RuntimeException
*/
protected function checkLibsSource(): void
{
$not_downloaded = [];
foreach ($this->libs as $lib) {
if (!file_exists($lib->getSourceDir())) {
$not_downloaded[] = $lib::NAME;
}
}
if ($not_downloaded !== []) {
throw new RuntimeException(
'"' . implode(', ', $not_downloaded) .
'" totally ' . count($not_downloaded) .
' source' . (count($not_downloaded) === 1 ? '' : 's') .
' not downloaded, maybe you need to "fetch" ' . (count($not_downloaded) === 1 ? 'it' : 'them') . ' first?'
);
}
}
}

View File

@ -0,0 +1,299 @@
<?php
declare(strict_types=1);
namespace SPC\builder;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\Config;
class Extension
{
protected array $dependencies = [];
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public function __construct(protected string $name, protected BuilderBase $builder)
{
$ext_type = Config::getExt($this->name, 'type');
$unix_only = Config::getExt($this->name, 'unix-only', false);
$windows_only = Config::getExt($this->name, 'windows-only', false);
if (PHP_OS_FAMILY !== 'Windows' && $windows_only) {
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Linux and macOS platform");
}
if (PHP_OS_FAMILY === 'Windows' && $unix_only) {
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Windows platform");
}
}
/**
* 获取开启该扩展的 PHP 编译添加的参数
*
* @throws FileSystemException|RuntimeException
*/
public function getConfigureArg(): string
{
$arg = $this->getEnableArg();
switch (PHP_OS_FAMILY) {
case 'Windows':
$arg .= $this->getWindowsConfigureArg();
break;
case 'Darwin':
case 'Linux':
$arg .= $this->getUnixConfigureArg();
break;
}
return $arg;
}
/**
* 根据 ext arg-type 获取对应开启的参数,一般都是 --enable-xxx --with-xxx
*
* @throws FileSystemException
* @throws RuntimeException
*/
public function getEnableArg(): string
{
$_name = str_replace('_', '-', $this->name);
return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) {
'enable' => '--enable-' . $_name,
'with' => '--with-' . $_name,
'none', 'custom' => '',
default => throw new RuntimeException("argType does not accept {$arg_type}, use [enable/with] ."),
};
}
/**
* 导出当前扩展依赖的所有 lib 库生成的 .a 静态编译库文件,以字符串形式导出,用空格分割
*/
public function getLibFilesString(): string
{
$ret = array_map(
fn ($x) => $x->getStaticLibFiles(),
$this->getLibraryDependencies(recursive: true)
);
return implode(' ', $ret);
}
/**
* 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function checkDependency(): static
{
foreach (Config::getExt($this->name, 'lib-depends', []) as $name) {
$this->addLibraryDependency($name);
}
foreach (Config::getExt($this->name, 'lib-suggests', []) as $name) {
$this->addLibraryDependency($name, true);
}
foreach (Config::getExt($this->name, 'ext-depends', []) as $name) {
$this->addExtensionDependency($name);
}
foreach (Config::getExt($this->name, 'ext-suggests', []) as $name) {
$this->addExtensionDependency($name, true);
}
return $this;
}
public function getExtensionDependency(): array
{
return array_filter($this->dependencies, fn ($x) => $x instanceof Extension);
}
public function getName(): string
{
return $this->name;
}
/**
* @throws RuntimeException
*/
protected function addLibraryDependency(string $name, bool $optional = false): void
{
$depLib = $this->builder->getLib($name);
if (!$depLib) {
if (!$optional) {
throw new RuntimeException("extension {$this->name} requires library {$name}");
}
logger()->info("enabling {$this->name} without library {$name}");
} else {
$this->dependencies[] = $depLib;
}
}
/**
* @throws RuntimeException
*/
protected function addExtensionDependency(string $name, bool $optional = false): void
{
$depExt = $this->builder->getExt($name);
if (!$depExt) {
if (!$optional) {
throw new RuntimeException("{$this->name} requires extension {$name}");
}
logger()->info("enabling {$this->name} without extension {$name}");
} else {
$this->dependencies[] = $depExt;
}
}
private function getWindowsConfigureArg(): string
{
$arg = '';
switch ($this->name) {
case 'redis':
// $arg = '--enable-redis';
// if ($this->builder->getLib('zstd')) {
// $arg .= ' --enable-redis-zstd --with-libzstd ';
// }
break;
case 'xml':
case 'soap':
case 'xmlreader':
case 'xmlwriter':
case 'dom':
$arg .= ' --with-libxml ';
break;
case 'swow':
if ($this->builder->getLib('openssl')) {
$arg .= ' --enable-swow-ssl';
}
if ($this->builder->getLib('curl')) {
$arg .= ' --enable-swow-curl';
}
break;
}
return $arg;
}
private function getUnixConfigureArg(): string
{
$arg = '';
switch ($this->name) {
/*case 'event':
$arg = ' --with-event-core --with-event-libevent-dir="' . BUILD_ROOT_PATH . '"';
if ($this->builder->getLib('openssl')) {
$arg .= ' --with-event-openssl --with-openssl-dir="' . BUILD_ROOT_PATH . '"';
}
break;*/
case 'sqlite3':
$arg = ' --with-sqlite3="' . BUILD_ROOT_PATH . '" ' .
'SQLITE_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'SQLITE_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'redis':
$arg = ' --enable-redis --disable-redis-session';
if ($this->builder->getLib('zstd')) {
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '" ';
}
break;
case 'yaml':
$arg .= ' --with-yaml="' . BUILD_ROOT_PATH . '" ';
break;
case 'zstd':
$arg .= ' --with-libzstd';
break;
case 'bz2':
$arg = ' --with-bz2="' . BUILD_ROOT_PATH . '" ';
break;
case 'openssl':
$arg .= ' ' .
'OPENSSL_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'OPENSSL_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'curl':
$arg .= ' ' .
'CURL_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'CURL_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'gd':
$arg .= ' ' .
'PNG_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'PNG_LIBS="' . $this->getLibFilesString() . '" ';
break;
// TODO: other libraries
case 'phar':
case 'zlib':
$arg .= ' ' .
'ZLIB_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'ZLIB_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'xml': // xml may use expat
if ($this->getLibraryDependencies()['expat'] ?? null) {
$arg .= ' --with-expat="' . BUILD_ROOT_PATH . '" ' .
'EXPAT_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'EXPAT_LIBS="' . $this->getLibFilesString() . '" ';
break;
}
// no break
case 'soap':
case 'xmlreader':
case 'xmlwriter':
case 'dom':
$arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '" ' .
'LIBXML_CFLAGS=-I"' . realpath('include/libxml2') . '" ' .
'LIBXML_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'ffi':
$arg .= ' ' .
'FFI_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'FFI_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'zip':
$arg .= ' ' .
'LIBZIP_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'LIBZIP_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'mbregex':
$arg .= ' ' .
'ONIG_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
'ONIG_LIBS="' . $this->getLibFilesString() . '" ';
break;
case 'swow':
$arg .= $this->builder->getLib('openssl') ? ' --enable-swow-ssl' : ' --disable-swow-ssl';
$arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl';
break;
case 'swoole':
if ($this->builder->getLib('openssl')) {
$arg .= ' --enable-openssl';
} else {
$arg .= ' --disable-openssl --without-openssl';
}
}
return $arg;
}
private function getLibraryDependencies(bool $recursive = false): array
{
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
if (!$recursive) {
return $ret;
}
$deps = [];
$added = 1;
while ($added !== 0) {
$added = 0;
foreach ($ret as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!in_array($depdepName, array_keys($deps), true)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!in_array($depName, array_keys($deps), true)) {
$deps[$depName] = $dep;
}
}
}
return $deps;
}
}

View File

@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
namespace SPC\builder;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\Config;
/**
* Lib 库的基类操作对象
*/
abstract class LibraryBase
{
/** @var string lib 依赖名称,必须重写 */
public const NAME = 'unknown';
/** @var string lib 依赖的根目录 */
protected string $source_dir;
/** @var array 依赖列表 */
protected array $dependencies = [];
/**
* @throws RuntimeException
*/
public function __construct(?string $source_dir = null)
{
if (static::NAME === 'unknown') {
throw new RuntimeException('no unknown!!!!!');
}
$this->source_dir = $source_dir ?? (SOURCE_PATH . '/' . static::NAME);
}
/**
* 获取 lib 库的根目录
*/
public function getSourceDir(): string
{
return $this->source_dir;
}
/**
* 获取当前 lib 库的所有依赖列表
*
* @param bool $recursive 是否递归获取(默认为 False
* @return array<string, LibraryBase> 依赖的 Map
*/
public function getDependencies(bool $recursive = false): array
{
// 非递归情况下直接返回通过 addLibraryDependency 方法添加的依赖
if (!$recursive) {
return $this->dependencies;
}
// 下面为递归获取依赖列表,根据依赖顺序
$deps = [];
$added = 1;
while ($added !== 0) {
$added = 0;
foreach ($this->dependencies as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!in_array($depdepName, array_keys($deps), true)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!in_array($depName, array_keys($deps), true)) {
$deps[$depName] = $dep;
}
}
}
return $deps;
}
/**
* 计算依赖列表,不符合依赖将抛出异常
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function calcDependency(): void
{
// 先从配置文件添加依赖,这里根据不同的操作系统分别选择不同的元信息
/*
选择规则:
如果是 Windows 系统,则依次尝试有无 lib-depends-windows、lib-depends-win、lib-depends。
如果是 macOS 系统,则依次尝试 lib-depends-darwin、lib-depends-unix、lib-depends。
如果是 Linux 系统,则依次尝试 lib-depends-linux、lib-depends-unix、lib-depends。
*/
foreach (Config::getLib(static::NAME, 'lib-depends', []) as $dep_name) {
$this->addLibraryDependency($dep_name);
}
foreach (Config::getLib(static::NAME, 'lib-suggests', []) as $dep_name) {
$this->addLibraryDependency($dep_name, true);
}
}
/**
* 获取当前库编译出来获取到的静态库文件列表
*
* @return string[] 获取编译出来后的需要的静态库文件列表
* @throws FileSystemException
* @throws RuntimeException
*/
public function getStaticLibs(): array
{
return Config::getLib(static::NAME, 'static-libs', []);
}
/**
* 获取当前 lib 编译出来的 C Header 文件列表
*
* @return string[] 获取编译出来后需要的 C Header 文件列表
* @throws FileSystemException
* @throws RuntimeException
*/
public function getHeaders(): array
{
return Config::getLib(static::NAME, 'headers', []);
}
/**
* 证明该库是否已编译好且就绪,如果没有就绪,内部会调用 build 来进行构建该库
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function tryBuild(bool $force_build = false): int
{
// 传入 true表明直接编译
if ($force_build) {
$this->build();
return BUILD_STATUS_OK;
}
// 看看这些库是不是存在,如果不存在,则调用编译并返回结果状态
foreach ($this->getStaticLibs() as $name) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
$this->tryBuild(true);
return BUILD_STATUS_OK;
}
}
// 头文件同理
foreach ($this->getHeaders() as $name) {
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
$this->tryBuild(true);
return BUILD_STATUS_OK;
}
}
// 到这里说明所有的文件都存在,就跳过编译
return BUILD_STATUS_ALREADY;
}
/**
* 获取构建当前 lib Builder 对象
*/
abstract public function getBuilder(): BuilderBase;
/**
* 构建该库需要调用的命令和操作
*
* @throws RuntimeException
*/
abstract protected function build();
/**
* 添加 lib 库的依赖库
*
* @param string $name 依赖名称
* @param bool $optional 是否是可选依赖(默认为 False
* @throws RuntimeException
*/
protected function addLibraryDependency(string $name, bool $optional = false): void
{
// Log::i("add $name as dep of {$this->name}");
$dep_lib = $this->getBuilder()->getLib($name);
if (!$dep_lib) {
if (!$optional) {
throw new RuntimeException(static::NAME . " requires library {$name}");
}
logger()->debug('enabling ' . static::NAME . " without {$name}");
} else {
$this->dependencies[$name] = $dep_lib;
}
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace SPC\builder;
interface LibraryInterface
{
public function getName(): string;
}

View File

@ -0,0 +1,265 @@
<?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;
use SPC\util\Patcher;
/**
* macOS 系统环境下的构建器
* 源于 Config但因为感觉叫 Config 不太合适,就换成了 Builder
*/
class MacOSBuilder extends BuilderBase
{
/** 编译的 Unix 工具集 */
use UnixBuilderTrait;
/** @var string[] MacOS 环境下编译依赖的命令 */
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'pkg-config', 'git', 'autoconf', 'automake', 'tar', 'unzip', 'xz', 'gzip', 'bzip2', 'cmake'];
/** @var bool 标记是否 patch 了 phar */
private bool $phar_patched = false;
/**
* @throws RuntimeException
*/
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'";
// 保存丢失的命令
$missing = [];
foreach (self::REQUIRED_COMMANDS as $cmd) {
if (SystemUtil::findCommand($cmd) === null) {
$missing[] = $cmd;
}
}
if (!empty($missing)) {
throw new RuntimeException('missing system commands: ' . implode(', ', $missing));
}
// 创立 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
*/
public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false): void
{
$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())
)
);
}
$extra_libs .= ' /Users/jerry/project/git-project/static-php-cli/buildroot/lib/libsqlite3.a';
// patch before configure
Patcher::patchPHPBeforeConfigure($this);
f_passthru(
$this->set_x . ' && ' .
'cd ' . SOURCE_PATH . '/php-src && ' .
'./buildconf --force'
);
Patcher::patchPHPConfigure($this);
if ($this->getLib('libxml2') || $this->getExt('iconv')) {
$extra_libs .= ' /Users/jerry/project/git-project/static-php-cli/buildroot/lib/libcrypto.a -liconv';
}
f_passthru(
$this->set_x . ' && ' .
'cd ' . SOURCE_PATH . '/php-src && ' .
'./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
);
if ($with_clean) {
logger()->info('cleaning up');
f_passthru(
$this->set_x . ' && ' .
'cd ' . SOURCE_PATH . '/php-src && ' .
'make clean'
);
}
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
{
if ($this->getExt('phar')) {
$this->phar_patched = true;
try {
f_passthru('cd ' . SOURCE_PATH . '/php-src && patch -p1 < sapi/micro/patches/phar.patch');
} catch (RuntimeException $e) {
logger()->error('failed to patch phat due to patch exit with code ' . $e->getCode());
$this->phar_patched = false;
}
}
f_passthru(
$this->set_x . ' && ' .
'cd ' . SOURCE_PATH . '/php-src && ' .
"make -j{$this->concurrency} " .
'EXTRA_CFLAGS="-g -Os -fno-ident" ' .
"EXTRA_LIBS=\"{$extra_libs} -lresolv\" " .
'STRIP="dsymutil -f " ' .
// TODO: comment things
'micro'
);
}
/**
* 构建 cli
*
* @throws RuntimeException
*/
public function buildCli(string $extra_libs): void
{
f_passthru(
$this->set_x . ' && ' .
'cd ' . SOURCE_PATH . '/php-src && ' .
"make -j{$this->concurrency} " .
'EXTRA_CFLAGS="-g -Os -fno-ident" ' . // 生成调试信息、优化编译后的尺寸、禁用标识符(如变量、函数名)缩短
"EXTRA_LIBS=\"{$extra_libs} -lresolv -lsqlite3\" " .
// TODO: comment things
'cli &&' .
'dsymutil -f sapi/cli/php &&' .
'strip sapi/cli/php'
);
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
class SystemUtil
{
/** macOS 兼容 unix 的系统工具 */
use UnixSystemUtilTrait;
/**
* 获取系统 CPU 逻辑内核数
*
* @throws RuntimeException
*/
public static function getCpuCount(): int
{
f_exec('sysctl -n hw.ncpu', $output, $ret);
if ($ret !== 0) {
throw new RuntimeException('Failed to get cpu count');
}
return (int) $output[0];
}
/**
* 获取不同架构对应的 cflags 参数
*
* @throws RuntimeException
*/
public static function getArchCFlags(string $arch): string
{
return match ($arch) {
'x86_64' => '--target=x86_64-apple-darwin',
'arm64','aarch64' => '--target=arm64-apple-darwin',
default => throw new RuntimeException('unsupported arch: ' . $arch),
};
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\builder\BuilderBase;
use SPC\builder\LibraryBase;
use SPC\builder\macos\MacOSBuilder;
use SPC\builder\traits\UnixLibraryTrait;
use SPC\store\Config;
abstract class MacOSLibraryBase extends LibraryBase
{
use UnixLibraryTrait;
protected array $static_libs;
protected array $headers;
/**
* 依赖的名字及是否可选例如curl => true,代表依赖 curl 但可选
*/
protected array $dep_names;
public function __construct(protected MacOSBuilder $builder)
{
parent::__construct();
}
public function getBuilder(): BuilderBase
{
return $this->builder;
}
/**
* 获取当前 lib 库依赖的 macOS framework
*/
public function getFrameworks(): array
{
return Config::getLib(static::NAME, 'frameworks', []);
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class brotli extends MacOSLibraryBase
{
public const NAME = 'brotli';
protected function build()
{
[$lib, $include, $destdir] = SEPARATED_PATH;
f_passthru(
"{$this->builder->set_x} && " .
"cd {$this->source_dir} && " .
'rm -rf build && ' .
'mkdir -p build && ' .
'cd build && ' .
"{$this->builder->configure_env} " . ' cmake ' .
// '--debug-find ' .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=/ ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'.. && ' .
"cmake --build . -j {$this->builder->concurrency} && " .
'make install DESTDIR="' . $destdir . '"'
);
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class bzip2 extends MacOSLibraryBase
{
public const NAME = 'bzip2';
protected function build()
{
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean" . ' && ' .
"make -j{$this->builder->concurrency} {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' libbz2.a" . ' && ' .
// make install may fail when cross-compiling, so we copy files.
'cp libbz2.a ' . BUILD_LIB_PATH . ' && ' .
'cp bzlib.h ' . BUILD_INCLUDE_PATH
);
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\RuntimeException;
class curl extends MacOSLibraryBase
{
public const NAME = 'curl';
protected function build()
{
$extra = '';
// lib:openssl
$openssl = $this->getBuilder()->getLib('openssl');
if ($openssl instanceof MacOSLibraryBase) {
$extra .= '-DCURL_USE_OPENSSL=ON ';
} else {
$extra .= '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF ';
}
// lib:zlib
$zlib = $this->getBuilder()->getLib('zlib');
if ($zlib instanceof MacOSLibraryBase) {
$extra .= '-DZLIB_LIBRARY="' . $zlib->getStaticLibFiles(style: 'cmake') . '" ' .
'-DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
}
// lib:libssh2
$libssh2 = $this->builder->getLib('libssh2');
if ($libssh2 instanceof MacOSLibraryBase) {
$extra .= '-DLIBSSH2_LIBRARY="' . $libssh2->getStaticLibFiles(style: 'cmake') . '" ' .
'-DLIBSSH2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DCURL_USE_LIBSSH2=OFF ';
}
// lib:brotli
$brotli = $this->builder->getLib('brotli');
if ($brotli) {
$extra .= '-DCURL_BROTLI=ON ' .
'-DBROTLIDEC_LIBRARY="' . realpath(BUILD_LIB_PATH . '/libbrotlidec-static.a') . ';' . realpath(BUILD_LIB_PATH . '/libbrotlicommon-static.a') . '" ' .
'-DBROTLICOMMON_LIBRARY="' . realpath(BUILD_LIB_PATH . '/libbrotlicommon-static.a') . '" ' .
'-DBROTLI_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DCURL_BROTLI=OFF ';
}
// lib:nghttp2
$nghttp2 = $this->builder->getLib('nghttp2');
if ($nghttp2 instanceof MacOSLibraryBase) {
$extra .= '-DUSE_NGHTTP2=ON ' .
'-DNGHTTP2_LIBRARY="' . $nghttp2->getStaticLibFiles(style: 'cmake') . '" ' .
'-DNGHTTP2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DUSE_NGHTTP2=OFF ';
}
// lib:ldap
$ldap = $this->builder->getLib('ldap');
if ($ldap instanceof MacOSLibraryBase) {
// $extra .= '-DCURL_DISABLE_LDAP=OFF ';
// TODO: LDAP support
throw new RuntimeException('LDAP support is not implemented yet');
}
$extra .= '-DCURL_DISABLE_LDAP=ON ';
// lib:zstd
$zstd = $this->builder->getLib('zstd');
if ($zstd instanceof MacOSLibraryBase) {
$extra .= '-DCURL_ZSTD=ON ' .
'-DZstd_LIBRARY="' . $zstd->getStaticLibFiles(style: 'cmake') . '" ' .
'-DZstd_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DCURL_ZSTD=OFF ';
}
// lib:idn2
$idn2 = $this->builder->getLib('idn2');
$extra .= $idn2 instanceof MacOSLibraryBase ? '-DUSE_LIBIDN2=ON ' : '-DUSE_LIBIDN2=OFF ';
// lib:psl
$libpsl = $this->builder->getLib('psl');
$extra .= $libpsl instanceof MacOSLibraryBase ? '-DCURL_USE_LIBPSL=ON ' : '-DCURL_USE_LIBPSL=OFF ';
[$lib, $include, $destdir] = SEPARATED_PATH;
// compile
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
'rm -rf build && ' .
'mkdir -p build && ' .
'cd build && ' .
"{$this->builder->configure_env} " . ' cmake ' .
// '--debug-find ' .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
$extra .
'-DCMAKE_INSTALL_PREFIX= ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'.. && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR="' . $destdir . '"'
);
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class libffi extends MacOSLibraryBase
{
public const NAME = 'libffi';
protected function build()
{
[$lib, $include, $destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} ./configure " .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->arch}-apple-darwin " .
"--target={$this->builder->arch}-apple-darwin " .
'--prefix= ' . // use prefix=/
"--libdir={$lib} && " .
'make clean && ' .
"make -j{$this->builder->concurrency} && " .
"make install DESTDIR={$destdir}"
);
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\util\Patcher;
class libpng extends MacOSLibraryBase
{
public const NAME = 'libpng';
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build()
{
// 不同架构的专属优化
$optimizations = match ($this->builder->arch) {
'x86_64' => '--enable-intel-sse ',
'arm64' => '--enable-arm-neon ',
default => '',
};
// patch configure
Patcher::patchUnixLibpng();
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} " .
'./configure ' .
"--host={$this->builder->gnu_arch}-apple-darwin " .
'--disable-shared ' .
'--enable-static ' .
'--enable-hardware-optimizations ' .
$optimizations .
'--prefix= && ' . // use prefix=/
'make clean && ' .
"make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I. -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la && " .
'make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH . ' && ' .
'cd ' . BUILD_LIB_PATH . ' && ' .
'ln -sf libpng16.a libpng.a'
);
}
}

View File

@ -0,0 +1,56 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class libssh2 extends MacOSLibraryBase
{
public const NAME = 'libssh2';
protected function build()
{
// lib:zlib
$enable_zlib = $this->builder->getLib('zlib') !== null ? 'ON' : 'OFF';
[$lib, $include, $destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
'rm -rf build && ' .
'mkdir -p build && ' .
'cd build && ' .
"{$this->builder->configure_env} " . ' cmake ' .
// '--debug-find ' .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_EXAMPLES=OFF ' .
'-DBUILD_TESTING=OFF ' .
"-DENABLE_ZLIB_COMPRESSION={$enable_zlib} " .
'-DCMAKE_INSTALL_PREFIX=/ ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'.. && ' .
"cmake --build . -j {$this->builder->concurrency} --target libssh2 && " .
'make install DESTDIR="' . $destdir . '"'
);
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\RuntimeException;
class libxml2 extends MacOSLibraryBase
{
public const NAME = 'libxml2';
/**
* @throws RuntimeException
*/
protected function build()
{
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';
[$lib, $include, $destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
'rm -rf build && ' .
'mkdir -p build && ' .
'cd build && ' .
"{$this->builder->configure_env} " . ' cmake ' .
// '--debug-find ' .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DLIBXML2_WITH_ICONV=ON ' .
"-DLIBXML2_WITH_ZLIB={$enable_zlib} " .
"-DLIBXML2_WITH_ICU={$enable_icu} " .
"-DLIBXML2_WITH_LZMA={$enable_xz} " .
'-DLIBXML2_WITH_PYTHON=OFF ' .
'-DLIBXML2_WITH_PROGRAMS=OFF ' .
'-DLIBXML2_WITH_TESTS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=/ ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'.. && ' .
"cmake --build . -j {$this->builder->concurrency} && " .
'make install DESTDIR="' . $destdir . '"'
);
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\RuntimeException;
class libyaml extends MacOSLibraryBase
{
public const NAME = 'libyaml';
/**
* @throws RuntimeException
*/
public function build()
{
// prepare cmake/config.h.in
if (!is_file(SOURCE_PATH . '/libyaml/cmake/config.h.in')) {
f_mkdir(SOURCE_PATH . '/libyaml/cmake');
file_put_contents(
SOURCE_PATH . '/libyaml/cmake/config.h.in',
<<<'EOF'
#define YAML_VERSION_MAJOR @YAML_VERSION_MAJOR@
#define YAML_VERSION_MINOR @YAML_VERSION_MINOR@
#define YAML_VERSION_PATCH @YAML_VERSION_PATCH@
#define YAML_VERSION_STRING "@YAML_VERSION_STRING@"
EOF
);
}
// prepare yamlConfig.cmake.in
if (!is_file(SOURCE_PATH . '/libyaml/yamlConfig.cmake.in')) {
file_put_contents(
SOURCE_PATH . '/libyaml/yamlConfig.cmake.in',
<<<'EOF'
# Config file for the yaml library.
#
# It defines the following variables:
# yaml_LIBRARIES - libraries to link against
@PACKAGE_INIT@
set_and_check(yaml_TARGETS "@PACKAGE_CONFIG_DIR_CONFIG@/yamlTargets.cmake")
if(NOT yaml_TARGETS_IMPORTED)
set(yaml_TARGETS_IMPORTED 1)
include(${yaml_TARGETS})
endif()
set(yaml_LIBRARIES yaml)
EOF
);
}
[$lib, $include, $destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
'rm -rf build && ' .
'mkdir -p build && ' .
'cd build && ' .
"{$this->builder->configure_env} " . ' cmake ' .
// '--debug-find ' .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_TESTING=OFF ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=/ ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'.. && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,101 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class libzip extends MacOSLibraryBase
{
public const NAME = 'libzip';
protected function build()
{
$extra = '';
// lib:zlib
$zlib = $this->builder->getLib('zlib');
if ($zlib instanceof MacOSLibraryBase) {
$extra .= '-DZLIB_LIBRARY="' . $zlib->getStaticLibFiles(style: 'cmake') . '" ' .
'-DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
}
// lib:bzip2
$libbzip2 = $this->builder->getLib('bzip2');
if ($libbzip2 instanceof MacOSLibraryBase) {
$extra .= '-DENABLE_BZIP2=ON ' .
'-DBZIP2_LIBRARIES="' . $libbzip2->getStaticLibFiles(style: 'cmake') . '" ' .
'-DBZIP2_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
} else {
$extra .= '-DENABLE_BZIP2=OFF ';
}
// lib:xz
$xz = $this->builder->getLib('xz');
if ($xz instanceof MacOSLibraryBase) {
$extra .= '-DENABLE_LZMA=ON ' .
'-DLIBLZMA_LIBRARY="' . $xz->getStaticLibFiles(style: 'cmake') . '" ' .
'-DLIBLZMA_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
} else {
$extra .= '-DENABLE_LZMA=OFF ';
}
// lib:zstd
$libzstd = $this->builder->getLib('zstd');
if ($libzstd instanceof MacOSLibraryBase) {
$extra .= '-DENABLE_ZSTD=ON ' .
'-DZstd_LIBRARY="' . $libzstd->getStaticLibFiles(style: 'cmake') . '" ' .
'-DZstd_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DENABLE_ZSTD=OFF ';
}
// lib:openssl
$libopenssl = $this->builder->getLib('openssl');
if ($libopenssl instanceof MacOSLibraryBase) {
$extra .= '-DENABLE_OPENSSL=ON ' .
'-DOpenSSL_LIBRARY="' . $libopenssl->getStaticLibFiles(style: 'cmake') . '" ' .
'-DOpenSSL_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
} else {
$extra .= '-DENABLE_OPENSSL=OFF ';
}
[$lib, $include, $destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
'rm -rf build && ' .
'mkdir -p build && ' .
'cd build && ' .
"{$this->builder->configure_env} " . ' cmake ' .
// '--debug-find ' .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DENABLE_GNUTLS=OFF ' .
'-DENABLE_MBEDTLS=OFF ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_DOC=OFF ' .
'-DBUILD_EXAMPLES=OFF ' .
'-DBUILD_REGRESS=OFF ' .
'-DBUILD_TOOLS=OFF ' .
$extra .
'-DCMAKE_INSTALL_PREFIX=/ ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'.. && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class nghttp2 extends MacOSLibraryBase
{
public const NAME = 'nghttp2';
protected function build()
{
$args = $this->builder->makeAutoconfArgs(static::NAME, [
'zlib' => null,
'openssl' => null,
'libxml2' => null,
'libev' => null,
'libcares' => null,
'libngtcp2' => null,
'libnghttp3' => null,
'libbpf' => null,
'libevent-openssl' => null,
'jansson' => null,
'jemalloc' => null,
'systemd' => null,
'cunit' => null,
]);
[,,$destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} " . ' ./configure ' .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->gnu_arch}-apple-darwin " .
'--enable-lib-only ' .
'--with-boost=no ' .
$args . ' ' .
'--prefix= && ' . // use prefix=/
'make clean && ' .
"make -j{$this->builder->concurrency} && " .
"make install DESTDIR={$destdir}"
);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class onig extends MacOSLibraryBase
{
public const NAME = 'onig';
protected function build()
{
[,,$destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} " . ' ./configure ' .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->arch}-apple-darwin " .
'--prefix= && ' . // use prefix=/
'make clean && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class openssl extends MacOSLibraryBase
{
public const NAME = 'openssl';
protected function build()
{
[$lib,,$destdir] = SEPARATED_PATH;
// lib:zlib
$extra = '';
$ex_lib = '';
$zlib = $this->builder->getLib('zlib');
if ($zlib instanceof MacOSLibraryBase) {
$extra = 'zlib';
$ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib);
}
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} ./Configure no-shared {$extra} " .
'--prefix=/ ' . // use prefix=/
"--libdir={$lib} " .
" darwin64-{$this->builder->arch}-cc && " .
'make clean && ' .
"make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\" && " .
'make install_sw DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class sqlite extends MacOSLibraryBase
{
public const NAME = 'sqlite';
protected function build()
{
[,,$destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} ./configure " .
'--enable-static --disable-shared ' .
'--prefix= && ' . // use prefix=/
'make clean && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class xz extends MacOSLibraryBase
{
public const NAME = 'xz';
protected function build()
{
[,,$destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
'autoreconf -i --force && ' .
"{$this->builder->configure_env} ./configure " .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->gnu_arch}-apple-darwin " .
'--disable-xz ' .
'--disable-xzdec ' .
'--disable-lzmadec ' .
'--disable-lzmainfo ' .
'--disable-scripts ' .
'--disable-doc ' .
'--with-libiconv ' .
'--prefix= && ' . // use prefix=/
'make clean && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class zlib extends MacOSLibraryBase
{
public const NAME = 'zlib';
protected function build()
{
[,,$destdir] = SEPARATED_PATH;
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"{$this->builder->configure_env} ./configure " .
'--static ' .
'--prefix= && ' . // use prefix=/
'make clean && ' .
"make -j{$this->builder->concurrency} && " .
'make install DESTDIR=' . $destdir
);
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* static-php-cli is licensed under Mulan PSL v2. You can use this
* software according to the terms and conditions of the
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
*/
declare(strict_types=1);
namespace SPC\builder\macos\library;
class zstd extends MacOSLibraryBase
{
public const NAME = 'zstd';
protected function build()
{
f_passthru(
$this->builder->set_x . ' && ' .
"cd {$this->source_dir} && " .
"make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean" . ' && ' .
"make -j{$this->builder->concurrency} " .
"{$this->builder->configure_env} " .
"PREFIX='" . BUILD_ROOT_PATH . "' " .
'-C lib libzstd.a CPPFLAGS_STATLIB=-DZSTD_MULTITHREAD && ' .
'cp lib/libzstd.a ' . BUILD_LIB_PATH . ' && ' .
'cp lib/zdict.h lib/zstd_errors.h lib/zstd.h ' . BUILD_INCLUDE_PATH
);
}
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace SPC\builder\traits;
trait LibraryTrait
{
}

View File

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace SPC\builder\traits;
use SPC\exception\RuntimeException;
trait UnixBuilderTrait
{
/** @var string 设置的命令前缀,设置为 set -x 可以在终端打印命令 */
public string $set_x = 'set -x';
/** @var string C 编译器命令 */
public string $cc;
/** @var string C++ 编译器命令 */
public string $cxx;
/** @var string cflags 参数 */
public string $arch_c_flags;
/** @var string C++ flags 参数 */
public string $arch_cxx_flags;
/** @var string cmake toolchain file */
public string $cmake_toolchain_file;
/** @var string configure 环境依赖的变量 */
public string $configure_env;
public function getAllStaticLibFiles(): array
{
$libs = [];
// reorder libs
foreach ($this->libs as $lib) {
foreach ($lib->getDependencies() as $dep) {
$libs[] = $dep;
}
$libs[] = $lib;
}
$libFiles = [];
$libNames = [];
// merge libs
foreach ($libs as $lib) {
if (!in_array($lib::NAME, $libNames, true)) {
$libNames[] = $lib::NAME;
array_unshift($libFiles, ...$lib->getStaticLibs());
}
}
return array_map(fn ($x) => realpath(BUILD_LIB_PATH . "/{$x}"), $libFiles);
}
/**
* @throws RuntimeException
*/
public function sanityCheck(int $build_micro_rule): void
{
logger()->info('running sanity check');
if ($build_micro_rule !== BUILD_MICRO_ONLY) {
f_exec(
$this->set_x . ' && ' .
SOURCE_PATH . '/php-src/sapi/cli/php -r "echo \"hello\";"',
$output,
$ret
);
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new RuntimeException('cli failed sanity check');
}
foreach ($this->exts as $ext) {
logger()->debug('checking ext: ' . $ext->getName());
if (file_exists(ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php')) {
f_exec(
$this->set_x . ' && ' . SOURCE_PATH . '/php-src/sapi/cli/php ' . ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php',
$output,
$ret
);
if ($ret !== 0) {
throw new RuntimeException('extension ' . $ext->getName() . ' failed sanity check');
}
}
}
}
if ($build_micro_rule !== BUILD_MICRO_NONE) {
if (file_exists(SOURCE_PATH . '/hello.exe')) {
@unlink(SOURCE_PATH . '/hello.exe');
}
file_put_contents(
SOURCE_PATH . '/hello.exe',
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
'<?php echo "hello";'
);
chmod(SOURCE_PATH . '/hello.exe', 0755);
f_exec(SOURCE_PATH . '/hello.exe', $output2, $ret);
if ($ret !== 0 || trim($out = implode('', $output2)) !== 'hello') {
throw new RuntimeException('micro failed sanity check, ret[' . $ret . '], out[' . ($out ?? 'NULL') . ']');
}
}
}
}

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace SPC\builder\traits;
use SPC\builder\LibraryBase;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
trait UnixLibraryTrait
{
use LibraryTrait;
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string
{
$libs = [$this];
if ($recursive) {
array_unshift($libs, ...array_values($this->getDependencies(recursive: true)));
}
$sep = match ($style) {
'autoconf' => ' ',
'cmake' => ';',
default => throw new RuntimeException('style only support autoconf and cmake'),
};
$ret = [];
/** @var LibraryBase $lib */
foreach ($libs as $lib) {
$libFiles = [];
foreach ($lib->getStaticLibs() as $name) {
$name = str_replace(' ', '\ ', realpath(BUILD_LIB_PATH . "/{$name}"));
$name = str_replace('"', '\"', $name);
$libFiles[] = $name;
}
array_unshift($ret, implode($sep, $libFiles));
}
return implode($sep, $ret);
}
public function makeAutoconfEnv(string $prefix = null): string
{
if ($prefix === null) {
$prefix = str_replace('-', '_', strtoupper(static::NAME));
}
return $prefix . '_CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ' .
$prefix . '_LIBS="' . $this->getStaticLibFiles() . '"';
}
}

View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace SPC\builder\traits;
/**
* Unix 系统的工具函数 Trait,适用于 Linux、macOS
*/
trait UnixSystemUtilTrait
{
/**
* 生成 toolchain.cmake用于 cmake 构建
*
* @param string $os 操作系统代号
* @param string $target_arch 目标架构
* @param string $cflags CFLAGS 参数
* @param null|string $cc CC 参数(默认空)
* @param null|string $cxx CXX 参数(默认空)
*/
public static function makeCmakeToolchainFile(
string $os,
string $target_arch,
string $cflags,
?string $cc = null,
?string $cxx = null
): string {
logger()->debug("making cmake tool chain file for {$os} {$target_arch} with CFLAGS='{$cflags}'");
$root = BUILD_ROOT_PATH;
$ccLine = '';
if ($cc) {
$ccLine = 'SET(CMAKE_C_COMPILER ' . self::findCommand($cc) . ')';
}
$cxxLine = '';
if ($cxx) {
$cxxLine = 'SET(CMAKE_CXX_COMPILER ' . self::findCommand($cxx) . ')';
}
$toolchain = <<<CMAKE
SET(CMAKE_SYSTEM_NAME {$os})
SET(CMAKE_SYSTEM_PROCESSOR {$target_arch})
{$ccLine}
{$cxxLine}
SET(CMAKE_C_FLAGS "{$cflags}")
SET(CMAKE_CXX_FLAGS "{$cflags}")
SET(CMAKE_FIND_ROOT_PATH "{$root}")
CMAKE;
file_put_contents(SOURCE_PATH . '/toolchain.cmake', $toolchain);
return realpath(SOURCE_PATH . '/toolchain.cmake');
}
/**
* @param string $name 命令名称
* @param array $paths 寻找的目标路径(如果不传入,则使用环境变量 PATH
* @return null|string 找到了返回命令路径,找不到返回 null
*/
public static function findCommand(string $name, array $paths = []): ?string
{
if (!$paths) {
$paths = explode(PATH_SEPARATOR, getenv('PATH'));
}
foreach ($paths as $path) {
if (file_exists($path . DIRECTORY_SEPARATOR . $name)) {
return $path . DIRECTORY_SEPARATOR . $name;
}
}
return null;
}
}

View File

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\builder\BuilderProvider;
use SPC\exception\ExceptionHandler;
use SPC\util\DependencyUtil;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/** @noinspection PhpUnused */
class BuildCliCommand extends BuildCommand
{
protected static $defaultName = 'build';
public function configure()
{
$this->setDescription('Build CLI binary');
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
$this->addOption('build-micro', null, null, 'build micro only');
$this->addOption('build-all', null, null, 'build both cli and micro');
}
public function execute(InputInterface $input, OutputInterface $output): int
{
// 从参数中获取要编译的 libraries并转换为数组
$libraries = array_map('trim', array_filter(explode(',', $input->getOption('with-libs'))));
// 从参数中获取要编译的 extensions并转换为数组
$extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions'))));
define('BUILD_ALL_STATIC', true);
if ($input->getOption('build-all')) {
$rule = BUILD_MICRO_BOTH;
logger()->info('Builder will build php-cli and phpmicro SAPI');
} elseif ($input->getOption('build-micro')) {
$rule = BUILD_MICRO_ONLY;
logger()->info('Builder will build phpmicro SAPI');
} else {
$rule = BUILD_MICRO_NONE;
logger()->info('Builder will build php-cli SAPI');
}
try {
// 构建对象
$builder = BuilderProvider::makeBuilderByInput($input);
// 根据提供的扩展列表获取依赖库列表并编译
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
logger()->info('Enabled extensions: ' . implode(', ', $extensions));
logger()->info('Required libraries: ' . implode(', ', $libraries));
if (!empty($not_included)) {
logger()->warning('some extensions will be enabled due to dependencies: ' . implode(',', $not_included));
}
sleep(2);
// 编译和检查库是否完整
$builder->buildLibs($libraries);
// 执行扩展检测
$builder->proveExts($extensions);
// 构建
$builder->buildPHP($rule, $input->getOption('with-clean'), $input->getOption('bloat'));
// 统计时间
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Build complete, used ' . $time . ' s !');
if ($rule !== BUILD_MICRO_ONLY) {
logger()->info('Static php binary path: ' . SOURCE_PATH . '/php-src/sapi/cli/php');
}
if ($rule !== BUILD_MICRO_NONE) {
logger()->info('phpmicro binary path: ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
}
return 0;
} catch (\Throwable $e) {
if ($input->getOption('debug')) {
ExceptionHandler::getInstance()->handle($e);
} else {
logger()->emergency('Build failed, please check terminal output, or build with --debug option to see more details.');
logger()->emergency($e->getMessage());
}
return 1;
}
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use Symfony\Component\Console\Input\InputOption;
abstract class BuildCommand extends BaseCommand
{
public function __construct(string $name = null)
{
parent::__construct($name);
// 根据运行的操作系统分配允许不同的命令行参数Windows 需要额外的 VS 和 SDK等*nix 需要提供架构
switch (PHP_OS_FAMILY) {
case 'Windows':
$this->addOption('with-sdk-binary-dir', null, InputOption::VALUE_REQUIRED, 'path to binary sdk');
$this->addOption('vs-ver', null, InputOption::VALUE_REQUIRED, 'vs version, e.g. "17" for Visual Studio 2022');
$this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture, "x64" or "arm64"', 'x64');
break;
case 'Linux':
$this->addOption('no-system-static', null, null, 'do not use system static libraries');
// no break
case 'Darwin':
$this->addOption('cc', null, InputOption::VALUE_REQUIRED, 'C compiler');
$this->addOption('cxx', null, InputOption::VALUE_REQUIRED, 'C++ compiler');
$this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture', php_uname('m'));
break;
}
// 是否在编译 make 前清除旧的文件
$this->addOption('with-clean', null, null, 'fresh build, `make clean` before `make`');
// 是否采用强制链接,让链接器强制加载静态库文件
$this->addOption('bloat', null, null, 'add all libraries into binary');
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\builder\BuilderProvider;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/** @noinspection PhpUnused */
class BuildLibsCommand extends BuildCommand
{
protected static $defaultName = 'build:libs';
public function configure()
{
$this->setDescription('Build dependencies');
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('all', 'A', null, 'Build all libs that static-php-cli needed');
}
public function initialize(InputInterface $input, OutputInterface $output)
{
// --all 等于 ""
if ($input->getOption('all')) {
$input->setArgument('libraries', '');
}
}
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
// 从参数中获取要编译的 libraries并转换为数组
$libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries'))));
// 删除旧资源
if ($input->getOption('clean')) {
logger()->warning('You are doing some operations that not recoverable: removing directories below');
logger()->warning(BUILD_ROOT_PATH);
logger()->warning('I will remove these dir after you press [Enter] !');
echo 'Confirm operation? [Yes] ';
fgets(STDIN);
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH);
} else {
f_passthru('rm -rf ' . BUILD_ROOT_PATH);
}
}
// 构建对象
$builder = BuilderProvider::makeBuilderByInput($input);
// 只编译 library 的情况下,标记
$builder->setLibsOnly();
// 编译和检查库完整
$builder->buildLibs($libraries);
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Build libs complete, used ' . $time . ' s !');
return 0;
}
}

View File

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use CliHelper\Tools\ArgFixer;
use CliHelper\Tools\DataProvider;
use CliHelper\Tools\SeekableArrayIterator;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/** @noinspection PhpUnused */
class DeployCommand extends BaseCommand
{
protected static $defaultName = 'deploy-self';
public function configure()
{
$this->setDescription('Deploy static-php-cli self to an .phar application');
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
$this->addOption('disable-gzip', 'z', InputOption::VALUE_NONE, 'disable gzip archive mode');
}
public function execute(InputInterface $input, OutputInterface $output): int
{
// 第一阶段流程如果没有写path将会提示输入要打包的path
$prompt = new ArgFixer($input, $output);
// 首先得确认是不是关闭了readonly模式
if (ini_get('phar.readonly') == 1) {
if ($input->getOption('auto-phar-fix')) {
$ask = true;
} else {
$ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
}
$output->writeln('<info>Now running command in child process.</info>');
if ($ask) {
global $argv;
passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode);
exit($retcode);
}
}
// 获取路径
$path = WORKING_DIR;
// 如果是目录,则将目录下的所有文件打包
$phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
if (DataProvider::isRelativePath($phar_path)) {
$phar_path = '/tmp/' . $phar_path;
}
if (file_exists($phar_path)) {
$ask = $input->getOption('overwrite') ? true : $prompt->requireBool('<comment>The file "' . $phar_path . '" already exists, do you want to overwrite it?</comment>' . PHP_EOL . 'If you want to, just Enter');
if (!$ask) {
$output->writeln('<comment>User canceled.</comment>');
return 1;
}
@unlink($phar_path);
}
$phar = new \Phar($phar_path);
$phar->startBuffering();
$all = DataProvider::scanDirFiles($path, true, true);
$all = array_filter($all, function ($x) {
$dirs = preg_match('/(^(bin|config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x);
return !($dirs !== 1);
});
sort($all);
$map = [];
foreach ($all as $v) {
$map[$v] = $path . '/' . $v;
}
$output->writeln('<info>Start packing files...</info>');
try {
$phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output)));
$phar->addFromString(
'.phar-entry.php',
str_replace(
'/../vendor/autoload.php',
'/vendor/autoload.php',
file_get_contents(ROOT_DIR . '/bin/static-php-cli')
)
);
$stub = '.phar-entry.php';
$phar->setStub($phar->createDefaultStub($stub));
} catch (\Throwable $e) {
$output->writeln($e);
return 1;
}
$phar->addFromString('.prod', 'true');
if (!$input->getOption('disable-gzip')) {
$phar->compressFiles(\Phar::GZ);
}
$phar->stopBuffering();
$output->writeln(PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".');
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx')) {
$output->writeln('Detected you have already compiled micro binary, I will make executable now for you!');
file_put_contents(
$phar_path . '.exe',
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
file_get_contents($phar_path)
);
chmod($phar_path . '.exe', 0755);
$output->writeln('<info>Static: ' . $phar_path . '.exe</info>');
}
chmod($phar_path, 0755);
$output->writeln('<info>Phar: ' . $phar_path . '</info>');
return 0;
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* 修改 config 后对其 kv 进行排序的操作
*/
class DumpLicenseCommand extends BaseCommand
{
protected static $defaultName = 'dump-license';
public function configure()
{
$this->setDescription('Dump licenses for required libraries');
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
}
public function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('<info>not implemented</info>');
return 1;
}
}

View File

@ -0,0 +1,238 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\exception\DownloaderException;
use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException;
use SPC\exception\InvalidArgumentException;
use SPC\exception\RuntimeException;
use SPC\store\Config;
use SPC\store\Downloader;
use SPC\util\Patcher;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/** @noinspection PhpUnused */
class FetchSourceCommand extends BaseCommand
{
protected static $defaultName = 'fetch';
protected string $php_major_ver;
protected InputInterface $input;
public function configure()
{
$this->setDescription('Fetch required sources');
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
$this->addOption('hash', null, null, 'Hash only');
$this->addOption('shallow-clone', null, null, 'Clone shallow');
$this->addOption('with-openssl11', null, null, 'Use openssl 1.1');
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1');
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
}
public function initialize(InputInterface $input, OutputInterface $output)
{
// --all 等于 "" "",也就是所有东西都要下载
if ($input->getOption('all')) {
$input->setArgument('extensions', '');
$input->setArgument('libraries', '');
}
parent::initialize($input, $output);
}
public function execute(InputInterface $input, OutputInterface $output): int
{
$this->input = $input;
try {
// 匹配版本
$ver = $this->php_major_ver = $input->getOption('with-php') ?? '8.1';
preg_match('/^\d+\.\d+$/', $ver, $matches);
if (!$matches) {
logger()->error("bad version arg: {$ver}, x.y required!");
return 1;
}
// 删除旧资源
if ($input->getOption('clean')) {
logger()->warning('You are doing some operations that not recoverable: removing directories below');
logger()->warning(SOURCE_PATH);
logger()->warning(DOWNLOAD_PATH);
logger()->warning('I will remove these dir after you press [Enter] !');
echo 'Confirm operation? [Yes] ';
$r = strtolower(trim(fgets(STDIN)));
if ($r !== 'yes' && $r !== '') {
logger()->notice('Operation canceled.');
return 1;
}
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . SOURCE_PATH);
f_passthru('rmdir /s /q ' . DOWNLOAD_PATH);
} else {
f_passthru('rm -rf ' . SOURCE_PATH);
f_passthru('rm -rf ' . DOWNLOAD_PATH);
}
}
// 使用浅克隆可以减少调用 git 命令下载资源时的存储空间占用
if ($input->getOption('shallow-clone')) {
define('GIT_SHALLOW_CLONE', true);
}
// 读取源配置随便读一个source用于缓存 source 配置
Config::getSource('openssl');
// 是否启用openssl11
if ($input->getOption('with-openssl11')) {
logger()->debug('Using openssl 1.1');
// 手动修改配置
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
}
// 默认预选 phpmicro
$chosen_sources = ['micro'];
// 从参数中获取要编译的 libraries并转换为数组
$libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries'))));
if ($libraries) {
foreach ($libraries as $lib) {
// 从 lib 的 config 中找到对应 source 资源名称,组成一个 lib 的 source 列表
$src_name = Config::getLib($lib, 'source');
$chosen_sources[] = $src_name;
}
} else { // 如果传入了空串,那么代表 fetch 所有包
$chosen_sources = [...$chosen_sources, ...array_map(fn ($x) => $x['source'], array_values(Config::getLibs()))];
}
// 从参数中获取要编译的 extensions并转换为数组
$extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions'))));
if ($extensions) {
foreach ($extensions as $lib) {
if (Config::getExt($lib, 'type') !== 'builtin') {
$src_name = Config::getExt($lib, 'source');
$chosen_sources[] = $src_name;
}
}
} else {
foreach (Config::getExts() as $ext) {
if ($ext['type'] !== 'builtin') {
$chosen_sources[] = $ext['source'];
}
}
}
$chosen_sources = array_unique($chosen_sources);
// 是否只hash不下载资源
if ($input->getOption('hash')) {
$hash = $this->doHash($chosen_sources);
$output->writeln($hash);
return 0;
}
// 创建目录
f_mkdir(SOURCE_PATH);
f_mkdir(DOWNLOAD_PATH);
// 下载 PHP
Downloader::fetchSource('php-src', Downloader::getLatestPHPInfo($ver));
// 下载别的依赖资源
$cnt = count($chosen_sources);
$ni = 0;
foreach ($chosen_sources as $name) {
++$ni;
logger()->info("Fetching source {$name} [{$ni}/{$cnt}]");
Downloader::fetchSource($name, Config::getSource($name));
}
// patch 每份资源只需一次如果已经下载好的资源已经patch了就标记一下不patch了
if (!file_exists(SOURCE_PATH . '/.patched')) {
$this->doPatch();
} else {
logger()->notice('sources already patched');
}
// 打印拉取资源用时
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Fetch complete, used ' . $time . ' s !');
return 0;
} catch (\Throwable $e) {
// 不开 debug 模式就不要再显示复杂的调试栈信息了
if ($input->getOption('debug')) {
ExceptionHandler::getInstance()->handle($e);
} else {
logger()->emergency($e->getMessage() . ', previous message: ' . $e->getPrevious()->getMessage());
}
return 1;
}
}
/**
* 计算资源名称列表的 Hash
*
* @param array $chosen_sources 要计算 hash 的资源名称列表
* @throws InvalidArgumentException
* @throws DownloaderException
* @throws FileSystemException
*/
private function doHash(array $chosen_sources): string
{
$files = [];
foreach ($chosen_sources as $name) {
$source = Config::getSource($name);
$filename = match ($source['type']) {
'ghtar' => Downloader::getLatestGithubTarball($name, $source)[1],
'ghtagtar' => Downloader::getLatestGithubTarball($name, $source, 'tags')[1],
'ghrel' => Downloader::getLatestGithubRelease($name, $source)[1],
'filelist' => Downloader::getFromFileList($name, $source)[1],
'url' => $source['filename'] ?? basename($source['url']),
'git' => null,
default => throw new InvalidArgumentException('unknown source type: ' . $source['type']),
};
if ($filename !== null) {
logger()->info("found {$name} source: {$filename}");
$files[] = $filename;
}
}
return hash('sha256', implode('|', $files));
}
/**
* 在拉回资源后,需要对一些文件做一些补丁 patch
*
* @throws FileSystemException
* @throws RuntimeException
*/
private function doPatch(): void
{
// patch 一些 PHP 的资源,以便编译
Patcher::patchPHPDepFiles();
// openssl 3 需要 patch 额外的东西
if (!$this->input->getOption('with-openssl11') && $this->php_major_ver === '8.0') {
Patcher::patchOpenssl3();
}
// openssl1.1.1q 在 MacOS 上直接编译会报错patch 一下
// @see: https://github.com/openssl/openssl/issues/18720
if ($this->input->getOption('with-openssl11') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c') && PHP_OS_FAMILY === 'Darwin') {
Patcher::patchDarwinOpenssl11();
}
// swow 需要软链接内部的文件夹才能正常编译
if (!file_exists(SOURCE_PATH . '/php-src/ext/swow')) {
Patcher::patchSwow();
}
// 标记 patch 完成,避免重复 patch
file_put_contents(SOURCE_PATH . '/.patched', '');
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\builder\traits\NoMotdTrait;
use SPC\store\Config;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ListExtCommand extends BaseCommand
{
use NoMotdTrait;
protected static $defaultName = 'list-ext';
public function configure()
{
$this->setDescription('List supported extensions');
}
public function execute(InputInterface $input, OutputInterface $output)
{
foreach (Config::getExts() as $ext => $meta) {
echo $ext . PHP_EOL;
}
return 0;
}
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\exception\FileSystemException;
use SPC\exception\ValidationException;
use SPC\store\FileSystem;
use SPC\util\ConfigValidator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* 修改 config 后对其 kv 进行排序的操作
*/
class SortConfigCommand extends BaseCommand
{
protected static $defaultName = 'sort-config';
public function configure()
{
$this->setDescription('After config edited, sort it by alphabet');
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
}
/**
* @throws ValidationException
* @throws FileSystemException
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
switch ($name = $input->getArgument('config-name')) {
case 'lib':
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
ConfigValidator::validateLibs($file);
ksort($file);
file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
break;
case 'source':
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true);
ConfigValidator::validateSource($file);
ksort($file);
file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
break;
case 'ext':
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true);
ConfigValidator::validateExts($file);
ksort($file);
file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
break;
default:
$output->writeln("<error>invalid config name: {$name}</error>");
return 1;
}
$output->writeln('<info>sort success</info>');
return 0;
}
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace SPC\exception;
class ValidationException extends \Exception
{
}

View File

@ -10,8 +10,6 @@ use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
class FileSystem
{
private static $source_cache;
/**
* @throws FileSystemException
*/
@ -291,11 +289,15 @@ class FileSystem
}
/**
* @param null|mixed $rule
* @param mixed $return_path_value
* 获取该路径下的所有类名,根据 psr-4 方式
*
* @param string $dir 目录
* @param string $base_namespace 基类命名空间
* @param null|mixed $rule 规则回调
* @param bool|string $return_path_value 是否返回路径对应的数组,默认只返回类名列表
* @throws FileSystemException
*/
public static function getClassesPsr4(string $dir, string $base_namespace, $rule = null, $return_path_value = false): array
public static function getClassesPsr4(string $dir, string $base_namespace, mixed $rule = null, bool|string $return_path_value = false): array
{
$classes = [];
// 扫描目录使用递归模式相对路径模式因为下面此路径要用作转换成namespace

View 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
View 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');
}
}
}

View File

@ -35,9 +35,14 @@ const REPLACE_FILE_STR = 1;
const REPLACE_FILE_PREG = 2;
const REPLACE_FILE_USER = 3;
// 编译输出类型
const BUILD_MICRO_NONE = 0;
const BUILD_MICRO_ONLY = 1;
const BUILD_MICRO_BOTH = 2;
// 编译状态
const BUILD_STATUS_OK = 0;
const BUILD_STATUS_ALREADY = 1;
const BUILD_STATUS_FAILED = 2;
ConsoleLogger::$date_format = 'H:i:s';

View File

@ -0,0 +1,8 @@
<?php
/** @noinspection PhpComposerExtensionStubsInspection */
declare(strict_types=1);
bcscale(3);
exit(bcdiv('105', '6.55957') === '16.007' ? 0 : 1);

View File

@ -0,0 +1,7 @@
<?php
/** @noinspection PhpComposerExtensionStubsInspection */
declare(strict_types=1);
exit(function_exists('cal_info') && is_array(cal_info(0)) ? 0 : 1);

View File

@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
exit(function_exists('curl_init') ? 0 : 1);

View File

@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
exit(class_exists('\\DOMDocument') ? 0 : 1);

View File

@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
exit(class_exists('\\Redis') ? 0 : 1);