Do some code quality check and fix #126

This commit is contained in:
crazywhalecc 2023-08-20 19:51:45 +08:00 committed by Jerry Ma
parent 9c57ed6439
commit c8fa767576
104 changed files with 1040 additions and 785 deletions

View File

@ -22,7 +22,7 @@ esac
# set project dir
__DIR__=$(cd "$(dirname "$0")" && pwd)
__PROJECT__=$(cd ${__DIR__}/../ && pwd)
__PROJECT__=$(cd "${__DIR__}"/../ && pwd)
# set download dir
__PHP_RUNTIME_URL__="https://dl.zhamao.xin/static-php-cli/php-8.2.6-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
@ -52,17 +52,17 @@ china)
esac
test -d ${__PROJECT__}/downloads || mkdir ${__PROJECT__}/downloads
test -d "${__PROJECT__}"/downloads || mkdir "${__PROJECT__}"/downloads
# download static php binary
test -f ${__PROJECT__}/downloads/runtime.tar.gz || { echo "Downloading $__PHP_RUNTIME_URL__ ..." && curl -#fSL -o ${__PROJECT__}/downloads/runtime.tar.gz "$__PHP_RUNTIME_URL__" ; }
test -f ${__DIR__}/php || { tar -xf ${__PROJECT__}/downloads/runtime.tar.gz -C ${__DIR__}/ ; }
chmod +x ${__DIR__}/php
test -f "${__PROJECT__}"/downloads/runtime.tar.gz || { echo "Downloading $__PHP_RUNTIME_URL__ ..." && curl -#fSL -o "${__PROJECT__}"/downloads/runtime.tar.gz "$__PHP_RUNTIME_URL__" ; }
test -f "${__DIR__}"/php || { tar -xf "${__PROJECT__}"/downloads/runtime.tar.gz -C "${__DIR__}"/ ; }
chmod +x "${__DIR__}"/php
# download composer
test -f ${__DIR__}/composer || curl -#fSL -o ${__DIR__}/composer "$__COMPOSER_URL__"
chmod +x ${__DIR__}/composer
test -f "${__DIR__}"/composer || curl -#fSL -o "${__DIR__}"/composer "$__COMPOSER_URL__"
chmod +x "${__DIR__}"/composer
# sanity check for php and composer
${__DIR__}/php -v >/dev/null || { echo "Failed to run php" && exit 1; }
${__DIR__}/php ${__DIR__}/composer --version >/dev/null || { echo "Failed to run composer" && exit 1; }
"${__DIR__}"/php -v >/dev/null || { echo "Failed to run php" && exit 1; }
"${__DIR__}"/php "${__DIR__}"/composer --version >/dev/null || { echo "Failed to run composer" && exit 1; }
echo "Setup runtime OK!"
echo "runtime bin path needs to add manually by command below:"

View File

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

View File

@ -2,18 +2,18 @@
# This file is using docker to run commands
self_dir=$(cd "$(dirname "$0")";pwd)
# Detect docker can run
if ! which docker >/dev/null; then
echo "Docker is not installed, please install docker first !"
exit 1
fi
DOCKER_EXECUTABLE="docker"
# shellcheck disable=SC2046
if [ $(id -u) -ne 0 ]; then
if ! docker info > /dev/null 2>&1; then
if [ "$SPC_USE_SUDO" != "yes" ]; then
echo "Docker command requires sudo"
# shellcheck disable=SC2039
echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again'
exit 1
fi
@ -33,6 +33,7 @@ x86_64)
;;
aarch64)
ALPINE_FROM=multiarch/alpine:aarch64-edge
# shellcheck disable=SC2039
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m"
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
;;
@ -65,10 +66,10 @@ ADD ./bin /app/bin
RUN composer update --no-dev
EOF
)
echo "$ALPINE_DOCKERFILE" > $(pwd)/Dockerfile
echo "$ALPINE_DOCKERFILE" > "$(pwd)"/Dockerfile
$DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH .
rm $(pwd)/Dockerfile
rm "$(pwd)"/Dockerfile
fi
# Check if in ci (local terminal can execute with -it)
@ -79,4 +80,5 @@ else
fi
# Run docker
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT=$(pwd) -v $(pwd)/config:/app/config -v $(pwd)/src:/app/src -v $(pwd)/buildroot:/app/buildroot -v $(pwd)/source:/app/source -v $(pwd)/downloads:/app/downloads cwcc-spc-$SPC_USE_ARCH bin/spc $@
# shellcheck disable=SC2068
$DOCKER_EXECUTABLE run --rm "$INTERACT" -e SPC_FIX_DEPLOY_ROOT="$(pwd)" -v "$(pwd)"/config:/app/config -v "$(pwd)"/src:/app/src -v "$(pwd)"/buildroot:/app/buildroot -v "$(pwd)"/source:/app/source -v "$(pwd)"/downloads:/app/downloads cwcc-spc-$SPC_USE_ARCH bin/spc $@

View File

@ -53,19 +53,6 @@
"sockets"
]
},
"memcached": {
"type": "external",
"source": "memcached",
"arg-type": "custom",
"cpp-extension": true,
"lib-depends": [
"libmemcached"
],
"ext-depends": [
"session",
"zlib"
]
},
"exif": {
"type": "builtin"
},
@ -113,6 +100,14 @@
"gettext"
]
},
"glfw": {
"type": "external",
"arg-type": "custom",
"source": "ext-glfw",
"lib-depends": [
"glfw"
]
},
"gmp": {
"type": "builtin",
"arg-type": "with-prefix",
@ -135,14 +130,6 @@
"imagemagick"
]
},
"glfw": {
"type": "external",
"arg-type": "custom",
"source": "ext-glfw",
"lib-depends": [
"glfw"
]
},
"imap": {
"type": "builtin",
"arg-type": "with",
@ -196,6 +183,19 @@
"session"
]
},
"memcached": {
"type": "external",
"source": "memcached",
"arg-type": "custom",
"cpp-extension": true,
"lib-depends": [
"libmemcached"
],
"ext-depends": [
"session",
"zlib"
]
},
"mongodb": {
"type": "external",
"source": "mongodb",
@ -485,4 +485,4 @@
"zstd"
]
}
}
}

View File

@ -15,18 +15,6 @@
"brotli"
]
},
"glfw": {
"source": "ext-glfw",
"static-libs-unix": [
"libglfw3.a"
],
"frameworks": [
"CoreVideo",
"OpenGL",
"Cocoa",
"IOKit"
]
},
"bzip2": {
"source": "bzip2",
"static-libs-unix": [
@ -96,6 +84,18 @@
"brotli"
]
},
"glfw": {
"source": "ext-glfw",
"static-libs-unix": [
"libglfw3.a"
],
"frameworks": [
"CoreVideo",
"OpenGL",
"Cocoa",
"IOKit"
]
},
"gmp": {
"source": "gmp",
"static-libs-unix": [
@ -200,6 +200,13 @@
"libmcrypt.a"
]
},
"libmemcached": {
"source": "libmemcached",
"static-libs-unix": [
"libmemcached.a",
"libmemcachedutil.a"
]
},
"libpng": {
"source": "libpng",
"static-libs-unix": [
@ -474,13 +481,6 @@
"zconf.h"
]
},
"libmemcached": {
"source": "libmemcached",
"static-libs-unix": [
"libmemcached.a",
"libmemcachedutil.a"
]
},
"zstd": {
"source": "zstd",
"static-libs-unix": [

View File

@ -6,15 +6,6 @@
"path": "LICENSE"
}
},
"ext-glfw": {
"type": "git",
"url": "https://github.com/mario-deluna/php-glfw",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"apcu": {
"type": "url",
"url": "https://pecl.php.net/get/APCu",
@ -25,25 +16,6 @@
"path": "LICENSE"
}
},
"memcached": {
"type": "url",
"url": "https://pecl.php.net/get/memcached",
"path": "php-src/ext/memcached",
"filename": "memcached.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libmemcached": {
"type": "git",
"url": "https://github.com/crazywhalecc/libmemcached-macos.git",
"rev": "master",
"license": {
"type": "file",
"path": "COPYING"
}
},
"brotli": {
"type": "ghtar",
"repo": "google/brotli",
@ -79,6 +51,15 @@
"path": "LICENSE"
}
},
"ext-glfw": {
"type": "git",
"url": "https://github.com/mario-deluna/php-glfw",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-imagick": {
"type": "url",
"url": "https://pecl.php.net/get/imagick",
@ -213,6 +194,15 @@
"path": "COPYING"
}
},
"libmemcached": {
"type": "git",
"url": "https://github.com/crazywhalecc/libmemcached-macos.git",
"rev": "master",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libpng": {
"type": "git",
"url": "https://git.code.sf.net/p/libpng/code",
@ -290,6 +280,16 @@
"path": "COPYING"
}
},
"memcached": {
"type": "url",
"url": "https://pecl.php.net/get/memcached",
"path": "php-src/ext/memcached",
"filename": "memcached.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"micro": {
"type": "git",
"path": "php-src/sapi/micro",
@ -476,4 +476,4 @@
"path": "LICENSE"
}
}
}
}

View File

@ -10,11 +10,11 @@ use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
/**
* spc 应用究级入口
* static-php-cli console app entry
*/
class ConsoleApplication extends Application
{
public const VERSION = '2.0-rc5';
public const VERSION = '2.0.0';
/**
* @throws \ReflectionException
@ -26,10 +26,10 @@ class ConsoleApplication extends Application
global $argv;
// 生产环境不显示详细的调试错误,只使用 symfony console 自带的错误显示
// Detailed debugging errors are not displayed in the production environment. Only the error display provided by Symfony console is used.
$this->setCatchExceptions(file_exists(ROOT_DIR . '/.prod') || !in_array('--debug', $argv));
// 通过扫描目录 src/static-php-cli/command/ 添加子命令
// Add subcommands by scanning the directory src/static-php-cli/command/
$commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command');
$phar = class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar');
$commands = array_filter($commands, function ($y) use ($phar) {
@ -46,9 +46,6 @@ class ConsoleApplication extends Application
$this->addCommands(array_map(function ($x) { return new $x(); }, $commands));
}
/**
* 重载以去除一些不必要的默认命令
*/
protected function getDefaultCommands(): array
{
return [new HelpCommand(), new ListCommand()];

View File

@ -15,43 +15,32 @@ 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 编译进程数 */
/** @var int Concurrency */
public int $concurrency = 1;
/** @var array<string, LibraryBase> 要编译的 libs 列表 */
/** @var array<string, LibraryBase> libraries */
protected array $libs = [];
/** @var array<string, Extension> 要编译的扩展列表 */
/** @var array<string, Extension> extensions */
protected array $exts = [];
/** @var array<int, string> 要编译的扩展列表(仅名字列表,用于最后生成编译的扩展列表给 micro */
protected array $plain_extensions = [];
/** @var bool 本次编译是否只编译 libs不编译 PHP */
/** @var bool compile libs only (just mark it) */
protected bool $libs_only = false;
/** @var bool 是否 strip 最终的二进制 */
protected bool $strip = true;
/** @var array<string, mixed> compile options */
protected array $options = [];
/**
* 构建指定列表的 libs
* Build libraries
*
* @param array<string> $libraries Libraries to build
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function buildLibs(array $libraries): void
{
// 通过扫描目录查找 lib
// search all supported libs
$support_lib_list = [];
$classes = FileSystem::getClassesPsr4(
ROOT_DIR . '/src/SPC/builder/' . osfamily2dir() . '/library',
@ -63,19 +52,22 @@ abstract class BuilderBase
}
}
// 如果传入了空则默认检查和安置所有支持的liblibraries为要build的support_lib_list为支持的列表
// if no libs specified, compile all supported libs
if ($libraries === [] && $this->isLibsOnly()) {
$libraries = array_keys($support_lib_list);
}
// pkg-config must be compiled first, whether it is specified or not
if (!in_array('pkg-config', $libraries)) {
array_unshift($libraries, 'pkg-config');
}
// 排序 libs根据依赖计算一个新的列表出来
// append dependencies
$libraries = DependencyUtil::getLibsByDeps($libraries);
// 过滤不支持的库后添加
// add lib object for builder
foreach ($libraries as $library) {
// if some libs are not supported (but in config "lib.json", throw exception)
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!');
}
@ -83,14 +75,15 @@ abstract class BuilderBase
$this->addLib($lib);
}
// 计算依赖,经过这里的遍历,如果没有抛出异常,说明依赖符合要求,可以继续下面的
// calculate and check dependencies
foreach ($this->libs as $lib) {
$lib->calcDependency();
}
// extract sources
SourceExtractor::initSource(libs: $libraries);
// 构建库
// build all libs
foreach ($this->libs as $lib) {
match ($lib->tryBuild()) {
BUILD_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] build success'),
@ -102,9 +95,9 @@ abstract class BuilderBase
}
/**
* 添加要编译的 Lib
* Add library to build.
*
* @param LibraryBase $library Lib 库对象
* @param LibraryBase $library Library object
*/
public function addLib(LibraryBase $library): void
{
@ -112,9 +105,7 @@ abstract class BuilderBase
}
/**
* 获取要编译的 Lib 库对象
*
* @param string $name 库名称
* Get library object by name.
*/
public function getLib(string $name): ?LibraryBase
{
@ -122,9 +113,7 @@ abstract class BuilderBase
}
/**
* 添加要编译的扩展
*
* @param Extension $extension 扩展对象
* Add extension to build.
*/
public function addExt(Extension $extension): void
{
@ -132,9 +121,7 @@ abstract class BuilderBase
}
/**
* 获取要编译的扩展对象
*
* @param string $name 扩展名称
* Get extension object by name.
*/
public function getExt(string $name): ?Extension
{
@ -142,7 +129,7 @@ abstract class BuilderBase
}
/**
* 获取所有要编译的扩展对象
* Get all extension objects.
*
* @return Extension[]
*/
@ -152,10 +139,9 @@ abstract class BuilderBase
}
/**
* 检查 C++ 扩展是否存在
* Check if there is a cpp extension.
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function hasCppExtension(): bool
@ -173,7 +159,7 @@ abstract class BuilderBase
}
/**
* 设置本次 Builder 是否为仅编译库的模式
* Set libs only mode.
*/
public function setLibsOnly(bool $status = true): void
{
@ -181,7 +167,7 @@ abstract class BuilderBase
}
/**
* 检验 ext 扩展列表是否合理,并声明 Extension 对象,检查扩展的依赖
* Verify the list of "ext" extensions for validity and declare an Extension object to check the dependencies of the extensions.
*
* @throws FileSystemException
* @throws RuntimeException
@ -203,26 +189,21 @@ abstract class BuilderBase
}
foreach ($this->exts as $ext) {
// 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理
$ext->checkDependency();
}
$this->plain_extensions = $extensions;
}
/**
* 开始构建 PHP
* Start to build PHP
*
* @param int $build_target 规则
* @param bool $bloat 保留
* @param int $build_target Build target, see BUILD_TARGET_*
*/
abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false);
abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE);
/**
* 生成依赖的扩展编译启用参数
* 例如 --enable-mbstring
* Generate extension enable arguments for configure.
* e.g. --enable-mbstring
*
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
@ -237,7 +218,7 @@ abstract class BuilderBase
}
/**
* 返回是否只编译 libs 的模式
* Get libs only mode.
*/
public function isLibsOnly(): bool
{
@ -245,7 +226,7 @@ abstract class BuilderBase
}
/**
* 获取当前即将编译的 PHP 的版本 ID五位数那个
* Get PHP Version ID from php-src/main/php_version.h
*/
public function getPHPVersionID(): int
{
@ -254,6 +235,11 @@ abstract class BuilderBase
return intval($match[1]);
}
/**
* Get build type name string to display.
*
* @param int $type Build target type
*/
public function getBuildTypeName(int $type): string
{
$ls = [];
@ -269,13 +255,46 @@ abstract class BuilderBase
return implode(', ', $ls);
}
public function setStrip(bool $strip): void
/**
* Get builder options (maybe changed by user)
*
* @param string $key Option key
* @param mixed $default If not exists, return this value
*/
public function getOption(string $key, mixed $default = null): mixed
{
$this->strip = $strip;
return $this->options[$key] ?? $default;
}
/**
* 检查是否存在 lib 库对应的源码,如果不存在,则抛出异常
* Get all builder options
*/
public function getOptions(): array
{
return $this->options;
}
/**
* Set builder options if not exists.
*/
public function setOptionIfNotExist(string $key, mixed $value): void
{
if (!isset($this->options[$key])) {
$this->options[$key] = $value;
}
}
/**
* Set builder options.
*/
public function setOption(string $key, mixed $value): void
{
$this->options[$key] = $value;
}
/**
* Check if all libs are downloaded.
* If not, throw exception.
*
* @throws RuntimeException
*/

View File

@ -6,7 +6,7 @@ namespace SPC\builder;
use SPC\builder\linux\LinuxBuilder;
use SPC\builder\macos\MacOSBuilder;
use SPC\builder\windows\WindowsBuilder;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use Symfony\Component\Console\Input\InputInterface;
@ -17,7 +17,9 @@ use Symfony\Component\Console\Input\InputInterface;
class BuilderProvider
{
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function makeBuilderByInput(InputInterface $input): BuilderBase
{
@ -27,18 +29,8 @@ class BuilderProvider
// vs_ver: $input->getOption('vs-ver'),
// arch: $input->getOption('arch'),
// ),
'Darwin' => new MacOSBuilder(
cc: $input->getOption('cc'),
cxx: $input->getOption('cxx'),
arch: $input->getOption('arch'),
zts: $input->hasOption('enable-zts') ? $input->getOption('enable-zts') : false,
),
'Linux' => new LinuxBuilder(
cc: $input->getOption('cc'),
cxx: $input->getOption('cxx'),
arch: $input->getOption('arch'),
zts: $input->hasOption('enable-zts') ? $input->getOption('enable-zts') : false,
),
'Darwin' => new MacOSBuilder($input->getOptions()),
'Linux' => new LinuxBuilder($input->getOptions()),
default => throw new WrongUsageException('Current OS "' . PHP_OS_FAMILY . '" is not supported yet'),
};
}

View File

@ -34,7 +34,7 @@ class Extension
/**
* 获取开启该扩展的 PHP 编译添加的参数
*
* @throws FileSystemException|RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function getConfigureArg(): string
@ -56,7 +56,6 @@ class Extension
* 根据 ext arg-type 获取对应开启的参数,一般都是 --enable-xxx --with-xxx
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function getEnableArg(): string

View File

@ -7,20 +7,16 @@ namespace SPC\builder;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
/**
* Lib 库的基类操作对象
*/
abstract class LibraryBase
{
/** @var string lib 依赖名称,必须重写 */
/** @var string */
public const NAME = 'unknown';
/** @var string lib 依赖的根目录 */
protected string $source_dir;
/** @var array 依赖列表 */
protected array $dependencies = [];
/**
@ -35,7 +31,7 @@ abstract class LibraryBase
}
/**
* 获取 lib 库的根目录
* Get current lib source root dir.
*/
public function getSourceDir(): string
{
@ -43,10 +39,9 @@ abstract class LibraryBase
}
/**
* 获取当前 lib 库的所有依赖列表
* Get current lib dependencies.
*
* @param bool $recursive 是否递归获取(默认为 False
* @return array<string, LibraryBase> 依赖的 Map
* @return array<string, LibraryBase>
*/
public function getDependencies(bool $recursive = false): array
{
@ -55,7 +50,6 @@ abstract class LibraryBase
return $this->dependencies;
}
// 下面为递归获取依赖列表,根据依赖顺序
$deps = [];
$added = 1;
@ -78,20 +72,21 @@ abstract class LibraryBase
}
/**
* 计算依赖列表,不符合依赖将抛出异常
* Calculate dependencies for current library.
*
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function calcDependency(): void
{
// 先从配置文件添加依赖,这里根据不同的操作系统分别选择不同的元信息
// Add dependencies from the configuration file. Here, choose different metadata based on the operating system.
/*
选择规则:
如果是 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。
*/
Rules:
If it is a Windows system, try the following dependencies in order: lib-depends-windows, lib-depends-win, lib-depends.
If it is a macOS system, try the following dependencies in order: lib-depends-darwin, lib-depends-unix, lib-depends.
If it is a Linux system, try the following dependencies in order: lib-depends-linux, lib-depends-unix, lib-depends.
*/
foreach (Config::getLib(static::NAME, 'lib-depends', []) as $dep_name) {
$this->addLibraryDependency($dep_name);
}
@ -101,11 +96,10 @@ abstract class LibraryBase
}
/**
* 获取当前库编译出来获取到的静态库文件列表
* Get config static libs.
*
* @return string[] 获取编译出来后的需要的静态库文件列表
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function getStaticLibs(): array
{
@ -113,11 +107,10 @@ abstract class LibraryBase
}
/**
* 获取当前 lib 编译出来的 C Header 文件列表
* Get config headers.
*
* @return string[] 获取编译出来后需要的 C Header 文件列表
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function getHeaders(): array
{
@ -125,14 +118,19 @@ abstract class LibraryBase
}
/**
* 证明该库是否已编译好且就绪,如果没有就绪,内部会调用 build 来进行构建该库
* Try to build this library, before build, we check first.
*
* BUILD_STATUS_OK if build success
* BUILD_STATUS_ALREADY if already built
* BUILD_STATUS_FAILED if build failed
*
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function tryBuild(bool $force_build = false): int
{
// 传入 true表明直接编译
// force means just build
if ($force_build) {
logger()->info('Building required library [' . static::NAME . ']');
$this->patchBeforeBuild();
@ -140,31 +138,31 @@ abstract class LibraryBase
return BUILD_STATUS_OK;
}
// 看看这些库是不是存在,如果不存在,则调用编译并返回结果状态
// check if these libraries exist, if not, invoke compilation and return the result status
foreach ($this->getStaticLibs() as $name) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
$this->tryBuild(true);
return BUILD_STATUS_OK;
}
}
// 头文件同理
// header files the same
foreach ($this->getHeaders() as $name) {
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
$this->tryBuild(true);
return BUILD_STATUS_OK;
}
}
// pkg-config 做特殊处理,如果是 pkg-config 就检查有没有 pkg-config 二进制
// pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists
if ($this instanceof MacOSLibraryBase && static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) {
$this->tryBuild(true);
return BUILD_STATUS_OK;
}
// 到这里说明所有的文件都存在,就跳过编译
// if all the files exist at this point, skip the compilation process
return BUILD_STATUS_ALREADY;
}
/**
* Patch before build, overwrite this and return true to patch libs
* Patch before build, overwrite this and return true to patch libs.
*/
public function patchBeforeBuild(): bool
{
@ -172,35 +170,32 @@ abstract class LibraryBase
}
/**
* 获取构建当前 lib Builder 对象
* Get current builder object.
*/
abstract public function getBuilder(): BuilderBase;
/**
* 构建该库需要调用的命令和操作
* Build this library.
*
* @throws RuntimeException
*/
abstract protected function build();
/**
* 添加 lib 库的依赖库
* Add lib dependency
*
* @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 {
if ($dep_lib) {
$this->dependencies[$name] = $dep_lib;
return;
}
if (!$optional) {
throw new RuntimeException(static::NAME . " requires library {$name}");
}
logger()->debug('enabling ' . static::NAME . " without {$name}");
}
}

View File

@ -7,6 +7,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
@ -15,11 +16,12 @@ class bz2 extends Extension
{
/**
* @throws FileSystemException
* @throws WrongUsageException
*/
public function patchBeforeConfigure(): bool
{
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/-lbz2/', $this->getLibFilesString() . $frameworks);
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lbz2/', $this->getLibFilesString() . $frameworks);
return true;
}
}

View File

@ -7,12 +7,16 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('curl')]
class curl extends Extension
{
/**
* @throws FileSystemException
*/
public function patchBeforeBuildconf(): bool
{
logger()->info('patching before-configure for curl checks');
@ -42,11 +46,12 @@ class curl extends Extension
/**
* @throws FileSystemException
* @throws WrongUsageException
*/
public function patchBeforeConfigure(): bool
{
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/-lcurl/', $this->getLibFilesString() . $frameworks);
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lcurl/', $this->getLibFilesString() . $frameworks);
return true;
}
}

View File

@ -31,12 +31,7 @@ class event extends Extension
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-levent_openssl/',
$this->getLibFilesString()
);
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-levent_openssl/', $this->getLibFilesString());
return true;
}
}

View File

@ -5,12 +5,16 @@ declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('glfw')]
class glfw extends Extension
{
/**
* @throws RuntimeException
*/
public function patchBeforeBuildconf(): bool
{
FileSystem::copyDir(SOURCE_PATH . '/ext-glfw', SOURCE_PATH . '/php-src/ext/glfw');

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
#[CustomExt('iconv')]
class iconv extends Extension
{
public function patchBeforeConfigure(): bool
{
// macOS need to link iconv dynamically, we add it to extra-libs
$extra_libs = $this->builder->getOption('extra-libs', '');
if (!str_contains($extra_libs, '-liconv')) {
$extra_libs .= ' -liconv';
}
$this->builder->setOption('extra-libs', $extra_libs);
return true;
}
}

View File

@ -10,6 +10,17 @@ use SPC\util\CustomExt;
#[CustomExt('imagick')]
class imagick extends Extension
{
public function patchBeforeBuildconf(): bool
{
// linux need to link library manually, we add it to extra-libs
$extra_libs = $this->builder->getOption('extra-libs', '');
if (!str_contains($extra_libs, 'libMagickCore')) {
$extra_libs .= ' /usr/lib/libMagick++-7.Q16HDRI.a /usr/lib/libMagickCore-7.Q16HDRI.a /usr/lib/libMagickWand-7.Q16HDRI.a';
}
$this->builder->setOption('extra-libs', $extra_libs);
return true;
}
public function getUnixConfigureArg(): string
{
return '--with-imagick=' . BUILD_ROOT_PATH;

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
@ -16,17 +17,18 @@ class memcache extends Extension
return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH;
}
/**
* @throws FileSystemException
*/
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
REPLACE_FILE_STR,
'if test -d $abs_srcdir/src ; then',
'if test -d $abs_srcdir/main ; then'
);
FileSystem::replaceFile(
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
REPLACE_FILE_STR,
'export CPPFLAGS="$CPPFLAGS $INCLUDES"',
'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"'
);

View File

@ -17,9 +17,8 @@ class pdo_sqlite extends Extension
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/sqlite3_column_table_name=yes/',
'sqlite3_column_table_name=no'
);

View File

@ -17,9 +17,8 @@ class pgsql extends Extension
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-lpq/',
$this->getLibFilesString()
);

View File

@ -17,9 +17,8 @@ class readline extends Extension
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-lncurses/',
$this->getLibFilesString()
);

View File

@ -17,9 +17,8 @@ class ssh2 extends Extension
*/
public function patchBeforeConfigure(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/configure',
REPLACE_FILE_PREG,
'/-lssh2/',
$this->getLibFilesString()
);

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\RuntimeException;
use SPC\util\CustomExt;
use SPC\util\Util;
@ -19,6 +20,9 @@ class swow extends Extension
return $arg;
}
/**
* @throws RuntimeException
*/
public function patchBeforeBuildconf(): bool
{
if (Util::getPHPVersionID() >= 80000 && !is_link(SOURCE_PATH . '/php-src/ext/swow')) {

View File

@ -12,94 +12,90 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\SourcePatcher;
/**
* Linux 系统环境下的构建器
*/
class LinuxBuilder extends BuilderBase
{
/** 编译的 Unix 工具集 */
/** Unix compatible builder methods */
use UnixBuilderTrait;
/** @var string[] Linux 环境下编译依赖的命令 */
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'git', 'autoconf', 'automake', 'tar', 'unzip', /* 'xz', 好像不需要 */ 'gzip', 'bzip2', 'cmake'];
/** @var string 使用的 libc */
/** @var string Using libc [musl,glibc] */
public string $libc;
/** @var array 特殊架构下的 cflags */
/** @var array Tune cflags */
public array $tune_c_flags;
/** @var string pkg-config 环境变量 */
/** @var string pkg-config env, including PKG_CONFIG_PATH, PKG_CONFIG */
public string $pkgconf_env;
/** @var string 交叉编译变量 */
public string $cross_compile_prefix = '';
public string $note_section = "Je pense, donc je suis\0";
/** @var bool Micro patch phar flag */
private bool $phar_patched = false;
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function __construct(?string $cc = null, ?string $cxx = null, ?string $arch = null, bool $zts = false)
public function __construct(array $options = [])
{
// 初始化一些默认参数
$this->cc = $cc ?? match (SystemUtil::getOSRelease()['dist']) {
$this->options = $options;
// ---------- set necessary options ----------
// set C Compiler (default: alpine: gcc, others: musl-gcc)
$this->setOptionIfNotExist('cc', match (SystemUtil::getOSRelease()['dist']) {
'alpine' => 'gcc',
default => 'musl-gcc'
};
$this->cxx = $cxx ?? 'g++';
$this->arch = $arch ?? php_uname('m');
$this->gnu_arch = arch2gnu($this->arch);
$this->zts = $zts;
$this->libc = 'musl'; // SystemUtil::selectLibc($this->cc);
});
// set C++ Compiler (default: g++)
$this->setOptionIfNotExist('cxx', 'g++');
// set arch (default: current)
$this->setOptionIfNotExist('arch', php_uname('m'));
$this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch')));
// 根据 CPU 线程数设置编译进程数
// ---------- set necessary compile environments ----------
// set libc
$this->libc = 'musl'; // SystemUtil::selectLibc($this->cc);
// concurrency
$this->concurrency = SystemUtil::getCpuCount();
// 设置 cflags
$this->arch_c_flags = SystemUtil::getArchCFlags($this->cc, $this->arch);
$this->arch_cxx_flags = SystemUtil::getArchCFlags($this->cxx, $this->arch);
$this->tune_c_flags = SystemUtil::checkCCFlags(SystemUtil::getTuneCFlags($this->arch), $this->cc);
// 设置 cmake
// cflags
$this->arch_c_flags = SystemUtil::getArchCFlags($this->getOption('cc'), $this->getOption('arch'));
$this->arch_cxx_flags = SystemUtil::getArchCFlags($this->getOption('cxx'), $this->getOption('arch'));
$this->tune_c_flags = SystemUtil::checkCCFlags(SystemUtil::getTuneCFlags($this->getOption('arch')), $this->getOption('cc'));
// cmake toolchain
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile(
os: 'Linux',
target_arch: $this->arch,
cflags: $this->arch_c_flags,
cc: $this->cc,
cxx: $this->cxx
'Linux',
$this->getOption('arch'),
$this->arch_c_flags,
$this->getOption('cc'),
$this->getOption('cxx'),
);
// 设置 pkgconfig
$this->pkgconf_env = 'PKG_CONFIG="' . BUILD_ROOT_PATH . '/bin/pkg-config" PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig"';
// 设置 configure 依赖的环境变量
$this->configure_env =
$this->pkgconf_env . ' ' .
"CC='{$this->cc}' " .
"CXX='{$this->cxx}' " .
(php_uname('m') === $this->arch ? '' : "CFLAGS='{$this->arch_c_flags}'");
// 交叉编译依赖的TODO
if (php_uname('m') !== $this->arch) {
// pkg-config
$vars = [
'PKG_CONFIG' => BUILD_ROOT_PATH . '/bin/pkg-config',
'PKG_CONFIG_PATH' => BUILD_LIB_PATH . '/pkgconfig',
];
$this->pkgconf_env = SystemUtil::makeEnvVarString($vars);
// configure environment
$this->configure_env = SystemUtil::makeEnvVarString([
...$vars,
'CC' => $this->getOption('cc'),
'CXX' => $this->getOption('cxx'),
]);
// cross-compile does not support yet
/*if (php_uname('m') !== $this->arch) {
$this->cross_compile_prefix = SystemUtil::getCrossCompilePrefix($this->cc, $this->arch);
logger()->info('using cross compile prefix: ' . $this->cross_compile_prefix);
$this->configure_env .= " CROSS_COMPILE='{$this->cross_compile_prefix}'";
}
}*/
$missing = [];
foreach (self::REQUIRED_COMMANDS as $cmd) {
if (SystemUtil::findCommand($cmd) === null) {
$missing[] = $cmd;
}
}
if (!empty($missing)) {
throw new WrongUsageException('missing system commands: ' . implode(', ', $missing));
}
// 创立 pkg-config 和放头文件的目录
// create pkgconfig and include dir (some libs cannot create them automatically)
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
f_mkdir(BUILD_INCLUDE_PATH, recursive: true);
}
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function makeAutoconfArgs(string $name, array $libSpecs): string
{
$ret = '';
@ -124,32 +120,22 @@ class LinuxBuilder extends BuilderBase
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $with_clean = false, bool $bloat = false)
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{
if (!$bloat) {
$extra_libs = implode(' ', $this->getAllStaticLibFiles());
// ---------- Update extra-libs ----------
$extra_libs = $this->getOption('extra-libs', '');
// non-bloat linking
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
} else {
logger()->info('bloat linking');
$extra_libs = implode(
' ',
array_map(
fn ($x) => "-Xcompiler {$x}",
array_filter($this->getAllStaticLibFiles())
)
);
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", array_filter($this->getAllStaticLibFiles())));
}
// add libstdc++, some extensions or libraries need it (C++ cannot be linked statically)
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCppExtension() ? '-lc++ ' : '');
$this->setOption('extra-libs', $extra_libs);
if ($this->hasCppExtension()) {
$extra_libs .= ' -lstdc++';
}
if ($this->getExt('imagick')) {
$extra_libs .= ' /usr/lib/libMagick++-7.Q16HDRI.a /usr/lib/libMagickCore-7.Q16HDRI.a /usr/lib/libMagickWand-7.Q16HDRI.a';
}
$envs = $this->pkgconf_env . ' ' .
"CC='{$this->cc}' " .
"CXX='{$this->cxx}' ";
$cflags = $this->arch_c_flags;
$use_lld = '';
@ -159,7 +145,7 @@ class LinuxBuilder extends BuilderBase
$cflags .= ' -static-libgcc -I"' . BUILD_INCLUDE_PATH . '"';
break;
case 'musl':
if (str_ends_with($this->cc, 'clang') && SystemUtil::findCommand('lld')) {
if (str_ends_with($this->getOption('cc'), 'clang') && SystemUtil::findCommand('lld')) {
$use_lld = '-Xcompiler -fuse-ld=lld';
}
break;
@ -167,7 +153,12 @@ class LinuxBuilder extends BuilderBase
throw new WrongUsageException('libc ' . $this->libc . ' is not implemented yet');
}
$envs = "{$envs} CFLAGS='{$cflags}' LIBS='-ldl -lpthread'";
$envs = $this->pkgconf_env . ' ' . SystemUtil::makeEnvVarString([
'CC' => $this->getOption('cc'),
'CXX' => $this->getOption('cxx'),
'CFLAGS' => $cflags,
'LIBS' => '-ldl -lpthread',
]);
SourcePatcher::patchBeforeBuildconf($this);
@ -175,11 +166,8 @@ class LinuxBuilder extends BuilderBase
SourcePatcher::patchBeforeConfigure($this);
if ($this->getPHPVersionID() < 80000) {
$json_74 = '--enable-json ';
} else {
$json_74 = '';
}
$json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts ' : '';
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
@ -194,24 +182,16 @@ class LinuxBuilder extends BuilderBase
'--enable-cli ' .
'--enable-fpm ' .
$json_74 .
$zts .
'--enable-micro=all-static ' .
($this->zts ? '--enable-zts' : '') . ' ' .
$this->makeExtensionArgs() . ' ' .
$envs
);
SourcePatcher::patchBeforeMake($this);
file_put_contents('/tmp/comment', $this->note_section);
// 清理
$this->cleanMake();
if ($bloat) {
logger()->info('bloat linking');
$extra_libs = "-Wl,--whole-archive {$extra_libs} -Wl,--no-whole-archive";
}
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('building cli');
$this->buildCli($extra_libs, $use_lld);
@ -225,7 +205,7 @@ class LinuxBuilder extends BuilderBase
$this->buildMicro($extra_libs, $use_lld, $cflags);
}
if (php_uname('m') === $this->arch) {
if (php_uname('m') === $this->getOption('arch')) {
$this->sanityCheck($build_target);
}
@ -235,30 +215,34 @@ class LinuxBuilder extends BuilderBase
}
/**
* Build cli sapi
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function buildCli(string $extra_libs, string $use_lld): void
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)),
'EXTRA_LIBS' => $extra_libs,
'EXTRA_LDFLAGS_PROGRAM' => "{$use_lld} -all-static",
]);
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec(
'make -j' . $this->concurrency .
' EXTRA_CFLAGS="-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)) . '" ' .
"EXTRA_LIBS=\"{$extra_libs}\" " .
"EXTRA_LDFLAGS_PROGRAM='{$use_lld} -all-static' " .
'cli'
);
->exec("make -j{$this->concurrency} {$vars} cli");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php');
}
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
->exec("{$this->cross_compile_prefix}objcopy --only-keep-debug php php.debug")
->exec('elfedit --output-osabi linux php')
->exec("{$this->cross_compile_prefix}strip --strip-all php")
->exec("{$this->cross_compile_prefix}objcopy --update-section .comment=/tmp/comment --add-gnu-debuglink=php.debug --remove-section=.note php");
$this->deployBinary(BUILD_TARGET_CLI);
}
/**
* Build phpmicro sapi
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function buildMicro(string $extra_libs, string $use_lld, string $cflags): void
{
@ -270,43 +254,45 @@ class LinuxBuilder extends BuilderBase
SourcePatcher::patchMicro(['phar']);
}
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)) . $enable_fake_cli,
'EXTRA_LIBS' => $extra_libs,
'EXTRA_LDFLAGS_PROGRAM' => "{$cflags} {$use_lld} -all-static",
]);
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec(
"make -j{$this->concurrency} " .
'EXTRA_CFLAGS=' . quote('-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags))) . ' ' .
'EXTRA_LIBS=' . quote($extra_libs) . ' ' .
'EXTRA_LDFLAGS_PROGRAM=' . quote("{$cflags} {$use_lld}" . ' -all-static', "'") . ' ' .
'micro'
);
->exec("make -j{$this->concurrency} {$vars} micro");
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec("{$this->cross_compile_prefix}strip --strip-all micro.sfx");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx');
}
$this->deployBinary(BUILD_TARGET_MICRO);
}
/**
* 构建 fpm
* Build fpm sapi
*
* @throws FileSystemException|RuntimeException
* @throws FileSystemException
* @throws RuntimeException
*/
public function buildFpm(string $extra_libs, string $use_lld): void
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)),
'EXTRA_LIBS' => $extra_libs,
'EXTRA_LDFLAGS_PROGRAM' => "{$use_lld} -all-static",
]);
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec(
'make -j' . $this->concurrency .
' EXTRA_CFLAGS="-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)) . '" ' .
"EXTRA_LIBS=\"{$extra_libs}\" " .
"EXTRA_LDFLAGS_PROGRAM='{$use_lld} -all-static' " .
'fpm'
);
->exec("make -j{$this->concurrency} {$vars} fpm");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm');
}
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
->exec("{$this->cross_compile_prefix}objcopy --only-keep-debug php-fpm php-fpm.debug")
->exec('elfedit --output-osabi linux php-fpm')
->exec("{$this->cross_compile_prefix}strip --strip-all php-fpm")
->exec("{$this->cross_compile_prefix}objcopy --update-section .comment=/tmp/comment --add-gnu-debuglink=php-fpm.debug --remove-section=.note php-fpm");
$this->deployBinary(BUILD_TARGET_FPM);
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace SPC\builder\linux;
use JetBrains\PhpStorm\ArrayShape;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
@ -13,7 +12,7 @@ class SystemUtil
{
use UnixSystemUtilTrait;
#[ArrayShape(['dist' => 'mixed|string', 'ver' => 'mixed|string'])]
/** @noinspection PhpMissingBreakStatementInspection */
public static function getOSRelease(): array
{
$ret = [
@ -81,6 +80,8 @@ class SystemUtil
/**
* @throws RuntimeException
* @throws WrongUsageException
* @throws WrongUsageException
*/
public static function getArchCFlags(string $cc, string $arch): string
{
@ -129,6 +130,7 @@ class SystemUtil
/**
* @throws RuntimeException
* @noinspection PhpUnused
*/
public static function getCrossCompilePrefix(string $cc, string $arch): string
{
@ -159,6 +161,7 @@ class SystemUtil
return null;
}
/** @noinspection PhpUnused */
public static function findStaticLibs(array $names): ?array
{
$ret = [];
@ -187,6 +190,7 @@ class SystemUtil
return null;
}
/** @noinspection PhpUnused */
public static function findHeaders(array $names): ?array
{
$ret = [];

View File

@ -8,7 +8,9 @@ use SPC\builder\BuilderBase;
use SPC\builder\LibraryBase;
use SPC\builder\linux\LinuxBuilder;
use SPC\builder\traits\UnixLibraryTrait;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
abstract class LinuxLibraryBase extends LibraryBase
{
@ -37,6 +39,8 @@ abstract class LinuxLibraryBase extends LibraryBase
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function tryBuild(bool $force_build = false): int
{
@ -71,7 +75,7 @@ abstract class LinuxLibraryBase extends LibraryBase
return BUILD_STATUS_ALREADY;
}
protected function makeFakePkgconfs()
protected function makeFakePkgconfs(): void
{
$workspace = BUILD_ROOT_PATH;
if ($workspace === '/') {

View File

@ -8,7 +8,7 @@ class icu extends LinuxLibraryBase
{
public const NAME = 'icu';
protected function build()
protected function build(): void
{
$root = BUILD_ROOT_PATH;
$cppflag = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1"';

View File

@ -23,24 +23,26 @@ namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
class libpng extends LinuxLibraryBase
{
public const NAME = 'libpng';
/**
* @throws FileSystemException
*/
public function patchBeforeBuild(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileStr(
SOURCE_PATH . '/libpng/configure',
REPLACE_FILE_STR,
'-lz',
BUILD_LIB_PATH . '/libz.a'
);
if (SystemUtil::getOSRelease()['dist'] === 'alpine') {
FileSystem::replaceFile(
FileSystem::replaceFileStr(
SOURCE_PATH . '/libpng/configure',
REPLACE_FILE_STR,
'-lm',
'/usr/lib/libm.a'
);
@ -49,12 +51,13 @@ class libpng extends LinuxLibraryBase
}
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function build()
public function build(): void
{
$optimizations = match ($this->builder->arch) {
$optimizations = match ($this->builder->getOption('arch')) {
'x86_64' => '--enable-intel-sse ',
'arm64' => '--enable-arm-neon ',
default => '',
@ -64,7 +67,7 @@ class libpng extends LinuxLibraryBase
->exec('chmod +x ./install-sh')
->exec(
"{$this->builder->configure_env} ./configure " .
"--host={$this->builder->gnu_arch}-unknown-linux " .
"--host={$this->builder->getOption('gnu-arch')}-unknown-linux " .
'--disable-shared ' .
'--enable-static ' .
'--enable-hardware-optimizations ' .

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\builder\linux\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
@ -13,14 +14,15 @@ class libxml2 extends LinuxLibraryBase
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public function build()
public function build(): void
{
$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;
[, , $destdir] = SEPARATED_PATH;
FileSystem::resetDir($this->source_dir . '/build');
shell()->cd($this->source_dir . '/build')

View File

@ -20,11 +20,20 @@ declare(strict_types=1);
namespace SPC\builder\linux\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class nghttp2 extends LinuxLibraryBase
{
public const NAME = 'nghttp2';
public function build()
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function build(): void
{
$args = $this->builder->makeAutoconfArgs(static::NAME, [
'zlib' => null,
@ -49,7 +58,7 @@ class nghttp2 extends LinuxLibraryBase
"{$this->builder->configure_env} ./configure " .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->gnu_arch}-unknown-linux " .
"--host={$this->builder->getOption('gnu-arch')}-unknown-linux " .
'--enable-lib-only ' .
'--with-boost=no ' .
$args . ' ' .

View File

@ -23,26 +23,28 @@ namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class openssl extends LinuxLibraryBase
{
public const NAME = 'openssl';
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function build()
public function build(): void
{
[$lib,$include,$destdir] = SEPARATED_PATH;
[,,$destdir] = SEPARATED_PATH;
$extra = '';
$ex_lib = '-ldl -pthread';
$env = $this->builder->pkgconf_env . " CFLAGS='{$this->builder->arch_c_flags}'";
$env .= " CC='{$this->builder->cc} -static -idirafter " . BUILD_INCLUDE_PATH .
$env .= " CC='{$this->builder->getOption('cc')} -static -idirafter " . BUILD_INCLUDE_PATH .
' -idirafter /usr/include/ ' .
' -idirafter /usr/include/' . $this->builder->arch . '-linux-gnu/ ' .
' -idirafter /usr/include/' . $this->builder->getOption('arch') . '-linux-gnu/ ' .
"' ";
// lib:zlib
$zlib = $this->builder->getLib('zlib');
@ -58,7 +60,7 @@ class openssl extends LinuxLibraryBase
$ex_lib = trim($ex_lib);
$clang_postfix = SystemUtil::getCCType($this->builder->cc) === 'clang' ? '-clang' : '';
$clang_postfix = SystemUtil::getCCType($this->builder->getOption('cc')) === 'clang' ? '-clang' : '';
shell()->cd($this->source_dir)
->exec(
@ -68,7 +70,7 @@ class openssl extends LinuxLibraryBase
'-static ' .
"{$zlib_extra}" .
'no-legacy ' .
"linux-{$this->builder->arch}{$clang_postfix}"
"linux-{$this->builder->getOption('arch')}{$clang_postfix}"
)
->exec('make clean')
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")

View File

@ -12,57 +12,56 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\SourcePatcher;
/**
* macOS 系统环境下的构建器
* 源于 Config但因为感觉叫 Config 不太合适,就换成了 Builder
*/
class MacOSBuilder extends BuilderBase
{
/** 编译的 Unix 工具集 */
/** Unix compatible builder methods */
use UnixBuilderTrait;
/** @var bool 标记是否 patch 了 phar */
/** @var bool Micro patch phar flag */
private bool $phar_patched = false;
/**
* @param null|string $cc C编译器名称如果不传入则默认使用clang
* @param null|string $cxx C++编译器名称如果不传入则默认使用clang++
* @param null|string $arch 当前架构,如果不传入则默认使用当前系统架构
* @throws RuntimeException
* @throws WrongUsageException
* @throws FileSystemException
*/
public function __construct(?string $cc = null, ?string $cxx = null, ?string $arch = null, bool $zts = false)
public function __construct(array $options = [])
{
// 如果是 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);
$this->zts = $zts;
// 根据 CPU 线程数设置编译进程数
$this->concurrency = SystemUtil::getCpuCount();
// 设置 cflags
$this->arch_c_flags = SystemUtil::getArchCFlags($this->arch);
$this->arch_cxx_flags = SystemUtil::getArchCFlags($this->arch);
// 设置 cmake
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->arch, $this->arch_c_flags);
// 设置 configure 依赖的环境变量
$this->configure_env =
'PKG_CONFIG="' . BUILD_ROOT_PATH . '/bin/pkg-config" ' .
'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' .
"CC='{$this->cc}' " .
"CXX='{$this->cxx}' " .
"CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration -Os'";
$this->options = $options;
// 创立 pkg-config 和放头文件的目录
// ---------- set necessary options ----------
// set C Compiler (default: clang)
$this->setOptionIfNotExist('cc', 'clang');
// set C++ Composer (default: clang++)
$this->setOptionIfNotExist('cxx', 'clang++');
// set arch (default: current)
$this->setOptionIfNotExist('arch', php_uname('m'));
$this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch')));
// ---------- set necessary compile environments ----------
// concurrency
$this->concurrency = SystemUtil::getCpuCount();
// cflags
$this->arch_c_flags = SystemUtil::getArchCFlags($this->getOption('arch'));
$this->arch_cxx_flags = SystemUtil::getArchCFlags($this->getOption('arch'));
// cmake toolchain
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->getOption('arch'), $this->arch_c_flags);
// configure environment
$this->configure_env = SystemUtil::makeEnvVarString([
'PKG_CONFIG' => BUILD_ROOT_PATH . '/bin/pkg-config',
'PKG_CONFIG_PATH' => BUILD_LIB_PATH . '/pkgconfig/',
'CC' => $this->getOption('cc'),
'CXX' => $this->getOption('cxx'),
'CFLAGS' => "{$this->arch_c_flags} -Wimplicit-function-declaration -Os",
]);
// create pkgconfig and include dir (some libs cannot create them automatically)
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
f_mkdir(BUILD_INCLUDE_PATH, recursive: true);
}
/**
* 生成库构建采用的 autoconf 参数列表
* [deprecated] 生成库构建采用的 autoconf 参数列表
*
* @param string $name 要构建的 lib 库名,传入仅供输出日志
* @param array $lib_specs 依赖的 lib 库的 autoconf 文件
@ -76,7 +75,6 @@ class MacOSBuilder extends BuilderBase
$arr = $arr ?? [];
$disableArgs = $arr[0] ?? null;
$prefix = $arr[1] ?? null;
if ($lib instanceof MacOSLibraryBase) {
logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support");
$ret .= '--with-' . $libName . '=yes ';
@ -89,9 +87,11 @@ class MacOSBuilder extends BuilderBase
}
/**
* 返回 macOS 系统依赖的框架列表
* Get dynamically linked macOS frameworks
*
* @param bool $asString 是否以字符串形式返回(默认为 False
* @param bool $asString If true, return as string
* @throws FileSystemException
* @throws WrongUsageException
*/
public function getFrameworks(bool $asString = false): array|string
{
@ -118,50 +118,43 @@ class MacOSBuilder extends BuilderBase
}
/**
* Just start to build statically linked php binary
*
* @param int $build_target build target
* @param bool $bloat just raw add all lib files
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false): void
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{
$extra_libs = $this->getFrameworks(true) . ' ' . ($this->hasCppExtension() ? '-lc++ ' : '');
if (!$bloat) {
$extra_libs .= implode(' ', $this->getAllStaticLibFiles());
// ---------- Update extra-libs ----------
$extra_libs = $this->getOption('extra-libs', '');
// add macOS frameworks
$extra_libs .= (empty($extra_libs) ? '' : ' ') . $this->getFrameworks(true);
// add libc++, some extensions or libraries need it (C++ cannot be linked statically)
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCppExtension() ? '-lc++ ' : '');
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($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 .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles())));
}
$this->setOption('extra-libs', $extra_libs);
// patch before buildconf
SourcePatcher::patchBeforeBuildconf($this);
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
SourcePatcher::patchBeforeConfigure($this);
if ($this->getLib('libxml2') || $this->getExt('iconv')) {
$extra_libs .= ' -liconv';
}
if ($this->getPHPVersionID() < 80000) {
$json_74 = '--enable-json ';
} else {
$json_74 = '';
}
$json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts ' : '';
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
'./configure ' .
'--prefix= ' .
'--with-valgrind=no ' . // 不检测内存泄漏
'--with-valgrind=no ' . // Not detect memory leak
'--enable-shared=no ' .
'--enable-static=yes ' .
"CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " .
@ -170,9 +163,9 @@ class MacOSBuilder extends BuilderBase
'--disable-phpdbg ' .
'--enable-cli ' .
'--enable-fpm ' .
$json_74 .
'--enable-micro ' .
($this->zts ? '--enable-zts' : '') . ' ' .
$json_74 .
$zts .
$this->makeExtensionArgs() . ' ' .
$this->configure_env
);
@ -183,18 +176,18 @@ class MacOSBuilder extends BuilderBase
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('building cli');
$this->buildCli($extra_libs);
$this->buildCli();
}
if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
logger()->info('building fpm');
$this->buildFpm($extra_libs);
$this->buildFpm();
}
if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
logger()->info('building micro');
$this->buildMicro($extra_libs);
$this->buildMicro();
}
if (php_uname('m') === $this->arch) {
if (php_uname('m') === $this->getOption('arch')) {
$this->sanityCheck($build_target);
}
@ -204,27 +197,32 @@ class MacOSBuilder extends BuilderBase
}
/**
* 构建 cli
* Build cli sapi
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function buildCli(string $extra_libs): void
public function buildCli(): void
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS need it)
]);
$shell = shell()->cd(SOURCE_PATH . '/php-src');
$shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli");
if ($this->strip) {
$shell->exec("make -j{$this->concurrency} {$vars} cli");
if (!$this->getOption('no-strip', false)) {
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php');
}
$this->deployBinary(BUILD_TARGET_CLI);
}
/**
* 构建 phpmicro
* Build phpmicro sapi
*
* @throws FileSystemException|RuntimeException
*/
public function buildMicro(string $extra_libs): void
public function buildMicro(): void
{
if ($this->getPHPVersionID() < 80000) {
throw new RuntimeException('phpmicro only support PHP >= 8.0!');
@ -234,22 +232,39 @@ class MacOSBuilder extends BuilderBase
SourcePatcher::patchMicro(['phar']);
}
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
$vars = [
// with debug information, optimize for size, remove identifiers, patch fake cli for micro
'EXTRA_CFLAGS' => '-g -Os -fno-ident' . $enable_fake_cli,
// link resolv library (macOS need it)
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv",
];
if (!$this->getOption('no-strip', false)) {
$vars['STRIP'] = 'dsymutil -f ';
}
$vars = SystemUtil::makeEnvVarString($vars);
shell()->cd(SOURCE_PATH . '/php-src')
->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" " . ($this->strip ? 'STRIP="dsymutil -f " ' : '') . 'micro');
->exec("make -j{$this->concurrency} {$vars} micro");
$this->deployBinary(BUILD_TARGET_MICRO);
}
/**
* 构建 fpm
* Build fpm sapi
*
* @throws RuntimeException
* @throws FileSystemException
*/
public function buildFpm(string $extra_libs): void
public function buildFpm(): void
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS need it)
]);
$shell = shell()->cd(SOURCE_PATH . '/php-src');
$shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" fpm");
if ($this->strip) {
$shell->exec("make -j{$this->concurrency} {$vars} fpm");
if (!$this->getOption('no-strip', false)) {
$shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip sapi/fpm/php-fpm');
}
$this->deployBinary(BUILD_TARGET_FPM);

View File

@ -10,11 +10,11 @@ use SPC\exception\WrongUsageException;
class SystemUtil
{
/** macOS 兼容 unix 的系统工具 */
/** Unix System Util Compatible */
use UnixSystemUtilTrait;
/**
* 获取系统 CPU 逻辑内核数
* Get Logic CPU Count for macOS
*
* @throws RuntimeException
*/
@ -29,9 +29,10 @@ class SystemUtil
}
/**
* 获取不同架构对应的 cflags 参数
* Get Target Arch CFlags
*
* @param string $arch 架构名称
* @param string $arch Arch Name
* @return string return Arch CFlags string
* @throws WrongUsageException
*/
public static function getArchCFlags(string $arch): string

View File

@ -8,6 +8,8 @@ use SPC\builder\BuilderBase;
use SPC\builder\LibraryBase;
use SPC\builder\macos\MacOSBuilder;
use SPC\builder\traits\UnixLibraryTrait;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
abstract class MacOSLibraryBase extends LibraryBase
@ -27,7 +29,8 @@ abstract class MacOSLibraryBase extends LibraryBase
}
/**
* 获取当前 lib 库依赖的 macOS framework
* @throws WrongUsageException
* @throws FileSystemException
*/
public function getFrameworks(): array
{

View File

@ -34,15 +34,13 @@ class curl extends MacOSLibraryBase
*/
public function patchBeforeBuild(): bool
{
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
SOURCE_PATH . '/curl/CMakeLists.txt',
REPLACE_FILE_PREG,
'/NOT COREFOUNDATION_FRAMEWORK/m',
'FALSE'
);
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
SOURCE_PATH . '/curl/CMakeLists.txt',
REPLACE_FILE_PREG,
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
'FALSE'
);

View File

@ -4,11 +4,18 @@ declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
class glfw extends MacOSLibraryBase
{
public const NAME = 'glfw';
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
// compile
shell()->cd(SOURCE_PATH . '/ext-glfw/vendor/glfw')

View File

@ -8,7 +8,7 @@ class icu extends MacOSLibraryBase
{
public const NAME = 'icu';
protected function build()
protected function build(): void
{
$root = BUILD_ROOT_PATH;
shell()->cd($this->source_dir . '/source')

View File

@ -1,39 +1,30 @@
<?php
/**
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
*
* lwmbs 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;
class libffi extends MacOSLibraryBase
{
public const NAME = 'libffi';
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
[$lib, , $destdir] = SEPARATED_PATH;
[, , $destdir] = SEPARATED_PATH;
shell()->cd($this->source_dir)
->exec(
"{$this->builder->configure_env} ./configure " .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->arch}-apple-darwin " .
"--target={$this->builder->arch}-apple-darwin " .
"--host={$this->builder->getOption('arch')}-apple-darwin " .
"--target={$this->builder->getOption('arch')}-apple-darwin " .
'--prefix= ' // use prefix=/
)
->exec('make clean')

View File

@ -11,7 +11,7 @@ class libmemcached extends MacOSLibraryBase
{
public const NAME = 'libmemcached';
public function build()
public function build(): void
{
$rootdir = BUILD_ROOT_PATH;

View File

@ -22,6 +22,7 @@ namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class libpng extends MacOSLibraryBase
{
@ -30,10 +31,11 @@ class libpng extends MacOSLibraryBase
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build()
protected function build(): void
{
$optimizations = match ($this->builder->arch) {
$optimizations = match ($this->builder->getOption('arch')) {
'x86_64' => '--enable-intel-sse ',
'arm64' => '--enable-arm-neon ',
default => '',
@ -43,7 +45,7 @@ class libpng extends MacOSLibraryBase
->exec('chmod +x ./install-sh')
->exec(
"{$this->builder->configure_env} ./configure " .
"--host={$this->builder->gnu_arch}-apple-darwin " .
"--host={$this->builder->getOption('gnu-arch')}-apple-darwin " .
'--disable-shared ' .
'--enable-static ' .
'--enable-hardware-optimizations ' .

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
@ -13,14 +14,22 @@ class libxml2 extends MacOSLibraryBase
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build()
protected function build(): void
{
// macOS need to link iconv dynamically, we add it to extra-libs
$extra_libs = $this->builder->getOption('extra-libs', '');
if (!str_contains($extra_libs, '-liconv')) {
$extra_libs .= ' -liconv';
}
$this->builder->setOption('extra-libs', $extra_libs);
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
// $enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';
[$lib, $include, $destdir] = SEPARATED_PATH;
[, , $destdir] = SEPARATED_PATH;
FileSystem::resetDir($this->source_dir . '/build');
shell()->cd($this->source_dir . '/build')

View File

@ -20,11 +20,18 @@ declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
class nghttp2 extends MacOSLibraryBase
{
public const NAME = 'nghttp2';
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
$args = $this->builder->makeAutoconfArgs(static::NAME, [
'zlib' => null,
@ -49,7 +56,7 @@ class nghttp2 extends MacOSLibraryBase
"{$this->builder->configure_env} " . ' ./configure ' .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->gnu_arch}-apple-darwin " .
"--host={$this->builder->getOption('gnu-arch')}-apple-darwin " .
'--enable-lib-only ' .
'--with-boost=no ' .
$args . ' ' .

View File

@ -20,11 +20,20 @@ declare(strict_types=1);
namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class openssl extends MacOSLibraryBase
{
public const NAME = 'openssl';
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
[$lib,,$destdir] = SEPARATED_PATH;
@ -43,7 +52,7 @@ class openssl extends MacOSLibraryBase
'--prefix=/ ' . // use prefix=/
"--libdir={$lib} " .
'--openssldir=/System/Library/OpenSSL ' .
"darwin64-{$this->builder->arch}-cc"
"darwin64-{$this->builder->getOption('arch')}-cc"
)
->exec('make clean')
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")

View File

@ -7,31 +7,27 @@ namespace SPC\builder\traits;
use SPC\builder\linux\LinuxBuilder;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
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 参数 */
/** @var string cflags */
public string $arch_c_flags;
/** @var string C++ flags 参数 */
/** @var string C++ flags */
public string $arch_cxx_flags;
/** @var string cmake toolchain file */
public string $cmake_toolchain_file;
/** @var string configure 环境依赖的变量 */
/** @var string configure environments */
public string $configure_env;
/**
* @throws WrongUsageException
* @throws FileSystemException
*/
public function getAllStaticLibFiles(): array
{
$libs = [];
@ -125,7 +121,7 @@ trait UnixBuilderTrait
}
/**
* 清理编译好的文件
* Run php clean
*
* @throws RuntimeException
*/
@ -141,7 +137,7 @@ trait UnixBuilderTrait
public function makeCmakeArgs(): string
{
[$lib, $include] = SEPARATED_PATH;
$extra = $this instanceof LinuxBuilder ? '-DCMAKE_C_COMPILER=' . $this->cc . ' ' : '';
$extra = $this instanceof LinuxBuilder ? '-DCMAKE_C_COMPILER=' . $this->getOption('cc') . ' ' : '';
return $extra . '-DCMAKE_BUILD_TYPE=Release ' .
'-DCMAKE_INSTALL_PREFIX=/ ' .
"-DCMAKE_INSTALL_LIBDIR={$lib} " .

View File

@ -7,6 +7,7 @@ namespace SPC\builder\traits;
use SPC\builder\LibraryBase;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
trait UnixLibraryTrait
@ -16,6 +17,7 @@ trait UnixLibraryTrait
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
*/
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string
{
@ -43,6 +45,11 @@ trait UnixLibraryTrait
return implode($sep, $ret);
}
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function makeAutoconfEnv(string $prefix = null): string
{
if ($prefix === null) {
@ -82,7 +89,7 @@ trait UnixLibraryTrait
* remove libtool archive files
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function cleanLaFiles(): void
{

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\builder\traits;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem;
/**
@ -14,11 +15,12 @@ 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 参数(默认空)
* @param string $os 操作系统代号
* @param string $target_arch 目标架构
* @param string $cflags CFLAGS 参数
* @param null|string $cc CC 参数(默认空)
* @param null|string $cxx CXX 参数(默认空)
* @throws FileSystemException
*/
public static function makeCmakeToolchainFile(
string $os,
@ -76,4 +78,20 @@ CMAKE;
}
return null;
}
/**
* @param array $vars Variables, like: ["CFLAGS" => "-Ixxx"]
* @return string like: CFLAGS="-Ixxx"
*/
public static function makeEnvVarString(array $vars): string
{
$str = '';
foreach ($vars as $key => $value) {
if ($str !== '') {
$str .= ' ';
}
$str .= $key . '=' . escapeshellarg($value);
}
return $str;
}
}

View File

@ -4,10 +4,16 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait brotli
{
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
FileSystem::resetDir($this->source_dir . '/build-dir');

View File

@ -4,11 +4,17 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait curl
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
$extra = '';
// lib:openssl

View File

@ -4,11 +4,19 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
trait freetype
{
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
$suggested = $this->builder->getLib('libpng') ? '--with-png' : '--without-png';
$suggested .= ' ';
@ -27,9 +35,8 @@ trait freetype
->exec("make -j{$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['freetype2.pc']);
FileSystem::replaceFile(
FileSystem::replaceFileStr(
BUILD_ROOT_PATH . '/lib/pkgconfig/freetype2.pc',
REPLACE_FILE_STR,
' -L/lib ',
' -L' . BUILD_ROOT_PATH . '/lib '
);

View File

@ -4,9 +4,16 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
trait gmp
{
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
shell()->cd($this->source_dir)
->exec(

View File

@ -4,10 +4,16 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait imagemagick
{
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
$extra = '--without-jxl --without-xml --without-zstd --without-x --disable-openmp ';
@ -46,9 +52,8 @@ trait imagemagick
];
$this->patchPkgconfPrefix($filelist);
foreach ($filelist as $file) {
FileSystem::replaceFile(
FileSystem::replaceFileRegex(
BUILD_LIB_PATH . '/pkgconfig/' . $file,
REPLACE_FILE_PREG,
'#includearchdir=/include/ImageMagick-7#m',
'includearchdir=${prefix}/include/ImageMagick-7'
);

View File

@ -4,11 +4,19 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
trait libavif
{
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
// CMake needs a clean build directory
FileSystem::resetDir($this->source_dir . '/build');

View File

@ -4,11 +4,17 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait libevent
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
// CMake needs a clean build directory
FileSystem::resetDir($this->source_dir . '/build');
@ -29,6 +35,5 @@ trait libevent
)
->exec("cmake --build . -j {$this->builder->concurrency}")
->exec('make install');
// patch pkgconfig
}
}

View File

@ -6,7 +6,7 @@ namespace SPC\builder\unix\library;
trait libiconv
{
protected function build()
protected function build(): void
{
[,,$destdir] = SEPARATED_PATH;

View File

@ -4,11 +4,19 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
trait libjpeg
{
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
// CMake needs a clean build directory
FileSystem::resetDir($this->source_dir . '/build');

View File

@ -6,7 +6,7 @@ namespace SPC\builder\unix\library;
trait libsodium
{
protected function build()
protected function build(): void
{
$root = BUILD_ROOT_PATH;
shell()->cd($this->source_dir)

View File

@ -4,11 +4,17 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait libssh2
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
$enable_zlib = $this->builder->getLib('zlib') !== null ? 'ON' : 'OFF';

View File

@ -4,9 +4,18 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
trait libwebp
{
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
protected function build(): void
{
[,,$destdir] = SEPARATED_PATH;

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
@ -11,8 +12,9 @@ trait libyaml
{
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build()
protected function build(): void
{
// prepare cmake/config.h.in
if (!is_file(SOURCE_PATH . '/libyaml/cmake/config.h.in')) {

View File

@ -4,11 +4,17 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait libzip
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
$extra = '';
// lib:bzip2

View File

@ -6,7 +6,7 @@ namespace SPC\builder\unix\library;
trait ncurses
{
protected function build()
protected function build(): void
{
shell()->cd($this->source_dir)
->exec(

View File

@ -4,9 +4,16 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
trait onig
{
protected function build()
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
[,,$destdir] = SEPARATED_PATH;

View File

@ -6,20 +6,16 @@ namespace SPC\builder\unix\library;
trait pkgconfig
{
protected function build()
protected function build(): void
{
$macos_env = 'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' .
"CC='{$this->builder->cc}' " .
"CXX='{$this->builder->cxx}' " .
"CC='{$this->builder->getOption('cc')}' " .
"CXX='{$this->builder->getOption('cxx')}' " .
"CFLAGS='{$this->builder->arch_c_flags} -Wimplicit-function-declaration' ";
$linux_env = 'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig" ' .
"CC='{$this->builder->cc}' " .
"CXX='{$this->builder->cxx}' ";
"CC='{$this->builder->getOption('cc')}' " .
"CXX='{$this->builder->getOption('cxx')}' ";
$extra = match (PHP_OS_FAMILY) {
'Darwin' => '',
default => '--with-internal-glib ',
};
shell()->cd($this->source_dir)
->exec(
match (PHP_OS_FAMILY) {
@ -29,7 +25,7 @@ trait pkgconfig
'./configure ' .
'--disable-shared ' .
'--enable-static ' .
$extra .
'--with-internal-glib ' .
'--prefix=' . BUILD_ROOT_PATH . ' ' .
'--without-sysroot ' .
'--without-system-include-path ' .

View File

@ -15,7 +15,7 @@ trait postgresql
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build()
protected function build(): void
{
$builddir = BUILD_ROOT_PATH;
$env = $this->builder->configure_env;

View File

@ -4,9 +4,16 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
trait readline
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
shell()->cd($this->source_dir)
->exec(

View File

@ -6,7 +6,7 @@ namespace SPC\builder\unix\library;
trait sqlite
{
protected function build()
protected function build(): void
{
shell()->cd($this->source_dir)
->exec("{$this->builder->configure_env} ./configure --enable-static --disable-shared --prefix=")

View File

@ -4,16 +4,23 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
trait xz
{
public function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public function build(): void
{
shell()->cd($this->source_dir)
->exec(
"{$this->builder->configure_env} ./configure " .
'--enable-static ' .
'--disable-shared ' .
"--host={$this->builder->gnu_arch}-unknown-linux " .
"--host={$this->builder->getOption('gnu-arch')}-unknown-linux " .
'--disable-scripts ' .
'--disable-doc ' .
'--with-libiconv ' .

View File

@ -4,9 +4,16 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
trait zlib
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
[,,$destdir] = SEPARATED_PATH;

View File

@ -4,11 +4,17 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait zstd
{
protected function build()
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
FileSystem::resetDir($this->source_dir . '/build/cmake/build');
shell()->cd($this->source_dir . '/build/cmake/build')

View File

@ -17,16 +17,8 @@ abstract class BaseCommand extends Command
{
protected bool $no_motd = false;
/**
* 输入
*/
protected InputInterface $input;
/**
* 输出
*
* 一般来说同样会是 ConsoleOutputInterface
*/
protected OutputInterface $output;
public function __construct(string $name = null)
@ -36,12 +28,12 @@ abstract class BaseCommand extends Command
$this->addOption('no-motd', null, null, 'Disable motd');
}
public function initialize(InputInterface $input, OutputInterface $output)
public function initialize(InputInterface $input, OutputInterface $output): void
{
if ($input->getOption('no-motd')) {
$this->no_motd = true;
}
// 注册全局错误处理器
set_error_handler(static function ($error_no, $error_msg, $error_file, $error_line) {
$tips = [
E_WARNING => ['PHP Warning: ', 'warning'],
@ -77,6 +69,9 @@ abstract class BaseCommand extends Command
}
}
/**
* @throws WrongUsageException
*/
abstract public function handle(): int;
protected function execute(InputInterface $input, OutputInterface $output): int
@ -93,7 +88,6 @@ abstract class BaseCommand extends Command
}
return static::FAILURE;
} catch (\Throwable $e) {
// 不开 debug 模式就不要再显示复杂的调试栈信息了
if ($this->getOption('debug')) {
ExceptionHandler::getInstance()->handle($e);
} else {
@ -118,11 +112,6 @@ abstract class BaseCommand extends Command
return $this->input->getArgument($name);
}
/**
* 是否应该执行
*
* @return bool 返回 true 以继续执行,返回 false 以中断执行
*/
protected function shouldExecute(): bool
{
return true;

View File

@ -18,7 +18,7 @@ use ZM\Logger\ConsoleColor;
#[AsCommand('build', 'build CLI binary')]
class BuildCliCommand extends BuildCommand
{
public function configure()
public function configure(): void
{
$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', '');
@ -29,17 +29,16 @@ class BuildCliCommand extends BuildCommand
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
$this->addOption('enable-zts', null, null, 'enable ZTS support');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
$this->addOption('with-micro-fake-cli', null, null, 'Enable phpmicro fake cli');
}
public function handle(): int
{
// 从参数中获取要编译的 libraries并转换为数组
// transform string to array
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs'))));
// 从参数中获取要编译的 extensions并转换为数组
// transform string to array
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));
define('BUILD_ALL_STATIC', true);
$rule = BUILD_TARGET_NONE;
$rule = $rule | ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
$rule = $rule | ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
@ -54,9 +53,9 @@ class BuildCliCommand extends BuildCommand
return static::FAILURE;
}
try {
// 构建对象
// create builder
$builder = BuilderProvider::makeBuilderByInput($this->input);
// 根据提供的扩展列表获取依赖库列表并编译
// calculate dependencies
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
/* @phpstan-ignore-next-line */
logger()->info('Build target: ' . ConsoleColor::yellow($builder->getBuildTypeName($rule)));
@ -68,12 +67,11 @@ class BuildCliCommand extends BuildCommand
logger()->warning('some extensions will be enabled due to dependencies: ' . implode(',', $not_included));
}
sleep(2);
// 编译和检查库是否完整
// compile libraries
$builder->buildLibs($libraries);
// 执行扩展检测
// check extensions
$builder->proveExts($extensions);
// strip
$builder->setStrip(!$this->getOption('no-strip'));
// Process -I option
$custom_ini = [];
foreach ($this->input->getOption('with-hardcoded-ini') as $value) {
@ -84,11 +82,15 @@ class BuildCliCommand extends BuildCommand
if (!empty($custom_ini)) {
SourcePatcher::patchHardcodedINI($custom_ini);
}
// 构建
$builder->buildPHP($rule, $this->getOption('bloat'));
// 统计时间
// start to build
$builder->buildPHP($rule);
// compile stopwatch :P
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Build complete, used ' . $time . ' s !');
// ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ----------
$build_root_path = BUILD_ROOT_PATH;
$cwd = getcwd();
$fixed = '';
@ -106,15 +108,17 @@ class BuildCliCommand extends BuildCommand
if (($rule & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
logger()->info('Static php-fpm binary path' . $fixed . ': ' . $build_root_path . '/bin/php-fpm');
}
// 导出相关元数据
// export metadata
file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
// 导出 LICENSE
// export licenses
$dumper = new LicenseDumper();
$dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license');
logger()->info('License path' . $fixed . ': ' . $build_root_path . '/license/');
return static::SUCCESS;
} catch (WrongUsageException $e) {
// WrongUsageException is not an exception, it's a user error, so we just print the error message
logger()->critical($e->getMessage());
return static::FAILURE;
} catch (\Throwable $e) {

View File

@ -12,7 +12,6 @@ abstract class BuildCommand extends BaseCommand
{
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');
@ -29,9 +28,7 @@ abstract class BuildCommand extends BaseCommand
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

@ -15,14 +15,14 @@ use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('build:libs', 'Build dependencies')]
class BuildLibsCommand extends BuildCommand
{
public function configure()
public function configure(): void
{
$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)
public function initialize(InputInterface $input, OutputInterface $output): void
{
// --all 等于 ""
if ($input->getOption('all')) {

View File

@ -16,7 +16,7 @@ use function Laravel\Prompts\text;
#[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')]
class DeployCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
@ -25,6 +25,9 @@ class DeployCommand extends BaseCommand
$this->addOption('with-dev', 'd', InputOption::VALUE_NONE, 'Automatically use dev composer dependencies');
}
/**
* @throws \PharException
*/
public function handle(): int
{
$composer = require ROOT_DIR . '/vendor/composer/installed.php';
@ -149,9 +152,9 @@ class DeployCommand extends BaseCommand
return static::SUCCESS;
}
private function progress(int $max = 0): ProgressBar
private function progress(): ProgressBar
{
$progress = new ProgressBar($this->output, $max);
$progress = new ProgressBar($this->output, 0);
$progress->setBarCharacter('<fg=green>⚬</>');
$progress->setEmptyBarCharacter('<fg=red>⚬</>');
$progress->setProgressCharacter('<fg=green>➤</>');

View File

@ -10,7 +10,7 @@ use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
class DoctorCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addOption('auto-fix', null, null, 'Automatically fix failed items (if possible)');
}

View File

@ -23,7 +23,7 @@ class DownloadCommand extends BaseCommand
protected string $php_major_ver;
public function configure()
public function configure(): void
{
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
$this->addOption('shallow-clone', null, null, 'Clone shallow');
@ -35,7 +35,7 @@ class DownloadCommand extends BaseCommand
$this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive');
}
public function initialize(InputInterface $input, OutputInterface $output)
public function initialize(InputInterface $input, OutputInterface $output): void
{
// --all 等于 "" "",也就是所有东西都要下载
if ($input->getOption('all') || $input->getOption('clean') || $input->getOption('from-zip')) {

View File

@ -18,7 +18,7 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand('dump-license', 'Dump licenses for required libraries')]
class DumpLicenseCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addOption('by-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null);
$this->addOption('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src');
@ -39,7 +39,7 @@ class DumpLicenseCommand extends BaseCommand
// 从参数中获取要编译的 extensions并转换为数组
$extensions = array_map('trim', array_filter(explode(',', $this->getOption('by-extensions'))));
// 根据提供的扩展列表获取依赖库列表并编译
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions);
[$extensions, $libraries] = DependencyUtil::getExtLibsByDeps($extensions);
$dumper->addExts($extensions);
$dumper->addLibs($libraries);
if (!$this->getOption('without-php')) {

View File

@ -5,6 +5,9 @@ declare(strict_types=1);
namespace SPC\command;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\SourceExtractor;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
@ -14,13 +17,16 @@ class ExtractCommand extends BaseCommand
{
use UnixSystemUtilTrait;
protected string $php_major_ver;
public function configure()
public function configure(): void
{
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
}
/**
* @throws WrongUsageException
* @throws FileSystemException
* @throws RuntimeException
*/
public function handle(): int
{
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));

View File

@ -12,7 +12,7 @@ use Symfony\Component\Console\Input\InputOption;
#[AsCommand('micro:combine', 'Combine micro.sfx and php code together')]
class MicroCombineCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addArgument('file', InputArgument::REQUIRED, 'The php or phar file to be combined');
$this->addOption('with-micro', 'M', InputOption::VALUE_REQUIRED, 'Customize your micro.sfx file');

View File

@ -5,17 +5,21 @@ declare(strict_types=1);
namespace SPC\command\dev;
use SPC\command\BaseCommand;
use SPC\exception\FileSystemException;
use SPC\store\Config;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('dev:ext-all', 'Dev command', ['list-ext'])]
class AllExtCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addOption('line', 'l', null, 'Show with separate lines');
}
/**
* @throws FileSystemException
*/
public function handle(): int
{
$this->output->writeln(implode($this->input->getOption('line') ? PHP_EOL : ',', array_keys(Config::getExts())));

View File

@ -5,6 +5,9 @@ declare(strict_types=1);
namespace SPC\command\dev;
use SPC\command\BaseCommand;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\util\DependencyUtil;
use Symfony\Component\Console\Attribute\AsCommand;
@ -13,11 +16,16 @@ use Symfony\Component\Console\Input\InputArgument;
#[AsCommand('dev:ext-info', 'Dev command')]
class ExtInfoCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extension name you need to get info');
}
/**
* @throws WrongUsageException
* @throws FileSystemException
* @throws RuntimeException
*/
public function handle(): int
{
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));

View File

@ -12,7 +12,7 @@ use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('dev:php-ver', 'Dev command')]
class PhpVerCommand extends BaseCommand
{
public function initialize(InputInterface $input, OutputInterface $output)
public function initialize(InputInterface $input, OutputInterface $output): void
{
$this->no_motd = true;
parent::initialize($input, $output);

View File

@ -18,7 +18,7 @@ use Symfony\Component\Console\Input\InputArgument;
#[AsCommand('dev:sort-config', 'After config edited, sort it by alphabet', ['sort-config'])]
class SortConfigCommand extends BaseCommand
{
public function configure()
public function configure(): void
{
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
}

View File

@ -24,11 +24,14 @@ class CheckListHandler
* @throws FileSystemException
* @throws RuntimeException
*/
public function __construct(private InputInterface $input, private OutputInterface $output, bool $include_manual = false)
public function __construct(private readonly InputInterface $input, private readonly OutputInterface $output, bool $include_manual = false)
{
$this->loadCheckList($include_manual);
}
/**
* @throws RuntimeException
*/
public function runSingleCheck(string $item_name, int $fix_policy = FIX_POLICY_DIE): void
{
foreach ($this->check_list as $item) {
@ -127,7 +130,10 @@ class CheckListHandler
usort($this->check_list, fn ($a, $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
}
private function emitFix(CheckResult $result)
/**
* @throws RuntimeException
*/
private function emitFix(CheckResult $result): void
{
pcntl_signal(SIGINT, function () {
$this->output->writeln('<error>You cancelled fix</error>');

View File

@ -6,7 +6,7 @@ namespace SPC\doctor;
class CheckResult
{
public function __construct(private bool $ok, private ?string $message = null, private string $fix_item = '', private array $fix_params = [])
public function __construct(private readonly bool $ok, private readonly ?string $message = null, private string $fix_item = '', private array $fix_params = [])
{
}
@ -40,7 +40,7 @@ class CheckResult
return $this->ok;
}
public function setFixItem(string $fix_item = '', array $fix_params = [])
public function setFixItem(string $fix_item = '', array $fix_params = []): void
{
$this->fix_item = $fix_item;
$this->fix_params = $fix_params;

View File

@ -12,11 +12,11 @@ use SPC\exception\RuntimeException;
class LinuxMuslCheck
{
/** @noinspection PhpUnused */
#[AsCheckItem('if musl-libc is installed', limit_os: 'Linux')]
public function checkMusl(): ?CheckResult
{
$file = '/lib/ld-musl-x86_64.so.1';
$result = null;
if (file_exists($file)) {
return CheckResult::ok();
}
@ -29,6 +29,10 @@ class LinuxMuslCheck
};
}
/**
* @throws RuntimeException
* @noinspection PhpUnused
*/
#[AsFixItem('fix-musl')]
public function fixMusl(array $distro): bool
{

View File

@ -30,6 +30,7 @@ class LinuxToolCheckList
'bzip2', 'cmake', 'patch',
];
/** @noinspection PhpUnused */
#[AsCheckItem('if necessary tools are installed', limit_os: 'Linux')]
public function checkCliTools(): ?CheckResult
{
@ -54,6 +55,7 @@ class LinuxToolCheckList
return CheckResult::ok();
}
/** @noinspection PhpUnused */
#[AsCheckItem('if necessary packages are installed', limit_os: 'Linux')]
public function checkSystemOSPackages(): ?CheckResult
{
@ -67,6 +69,10 @@ class LinuxToolCheckList
return CheckResult::ok();
}
/**
* @throws RuntimeException
* @noinspection PhpUnused
*/
#[AsFixItem('install-linux-tools')]
public function fixBuildTools(array $distro, array $missing): bool
{

View File

@ -6,9 +6,9 @@ namespace SPC\exception;
class ExceptionHandler
{
protected $whoops;
protected mixed $whoops;
private static $obj;
private static ?ExceptionHandler $obj = null;
private function __construct()
{
@ -32,14 +32,6 @@ class ExceptionHandler
return self::$obj;
}
public function getWhoops()
{
return $this->whoops;
}
/**
* 处理异常
*/
public function handle(\Throwable $e): void
{
if (is_null($this->whoops)) {

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace SPC\store;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
/**
@ -83,7 +82,6 @@ class Config
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public static function getExt(string $name, ?string $key = null, mixed $default = null)
@ -126,6 +124,9 @@ class Config
return self::$ext;
}
/**
* @throws FileSystemException
*/
public static function getSources(): array
{
if (self::$source === null) {

View File

@ -13,7 +13,7 @@ class CurlHook
* @param string $url 修改的链接
* @param array $headers 修改的 headers
*/
public static function setupGithubToken(string &$method, string &$url, array &$headers): void
public static function setupGithubToken(string $method, string $url, array &$headers): void
{
if (!getenv('GITHUB_TOKEN')) {
return;

View File

@ -4,23 +4,22 @@ declare(strict_types=1);
namespace SPC\store;
use JetBrains\PhpStorm\ArrayShape;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\source\CustomSourceBase;
/**
* 资源下载器
* Source Downloader.
*/
class Downloader
{
/**
* 获取 BitBucket 仓库的最新 Tag
* Get latest version from BitBucket tag
*
* @param string $name 资源名称
* @param array $source 资源的元信息,包含字段 repo
* @return array<int, string> 返回下载 url 链接和文件名
* @param string $name source name
* @param array $source source meta info: [repo]
* @return array<int, string> [url, filename]
* @throws DownloaderException
*/
public static function getLatestBitbucketTag(string $name, array $source): array
@ -49,10 +48,13 @@ class Downloader
}
/**
* 获取 GitHub 最新的打包地址和文件名
* Get latest version from GitHub tarball
*
* @param string $name source name
* @param array $source source meta info: [repo]
* @param string $type type of tarball, default is 'releases'
* @return array<int, string> [url, filename]
*
* @param string $name 包名称
* @param array $source 源信息
* @throws DownloaderException
*/
public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array
@ -82,10 +84,11 @@ class Downloader
}
/**
* 获取 GitHub 最新的 Release 下载信息
* Get latest version from GitHub release (uploaded archive)
*
* @param string $name 资源名
* @param array $source 资源的元信息,包含字段 repo、match
* @param string $name source name
* @param array $source source meta info: [repo, match]
* @return array<int, string> [url, filename]
* @throws DownloaderException
*/
public static function getLatestGithubRelease(string $name, array $source): array
@ -111,10 +114,11 @@ class Downloader
}
/**
* 获取文件列表的资源链接和名称
* Get latest version from file list (regex based crawler)
*
* @param string $name 资源名称
* @param array $source 资源元信息,包含 url、regex
* @param string $name source name
* @param array $source source meta info: [url, regex]
* @return array<int, string> [url, filename]
* @throws DownloaderException
*/
public static function getFromFileList(string $name, array $source): array
@ -149,6 +153,8 @@ class Downloader
}
/**
* Just download file using system curl command, and lock it
*
* @throws DownloaderException
* @throws RuntimeException
* @throws FileSystemException
@ -168,6 +174,11 @@ class Downloader
self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path]);
}
/**
* Try to lock source.
*
* @throws FileSystemException
*/
public static function lockSource(string $name, array $data): void
{
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
@ -180,40 +191,11 @@ class Downloader
}
/**
* 通过链接下载资源到本地并解压
* Download git source, and lock it.
*
* @param string $name 资源名称
* @param string $url 下载链接
* @param string $filename 下载到下载目录的目标文件名称,例如 xz.tar.gz
* @param null|string $path 如果指定了此参数,则会移动该资源目录到目标目录
* @throws FileSystemException
* @throws RuntimeException
* @throws DownloaderException
*/
public static function downloadUrl(string $name, string $url, string $filename, ?string $path = null): void
{
if (!file_exists(DOWNLOAD_PATH . "/{$filename}")) {
logger()->debug("downloading {$url}");
self::curlDown(url: $url, path: DOWNLOAD_PATH . "/{$filename}");
} else {
logger()->notice("{$filename} already exists");
}
FileSystem::extractSource($name, DOWNLOAD_PATH . "/{$filename}");
if ($path) {
$path = FileSystem::convertPath(SOURCE_PATH . "/{$path}");
$src_path = FileSystem::convertPath(SOURCE_PATH . "/{$name}");
switch (PHP_OS_FAMILY) {
case 'Windows':
f_passthru('move "' . $src_path . '" "' . $path . '"');
break;
case 'Linux':
case 'Darwin':
f_passthru('mv "' . $src_path . '" "' . $path . '"');
break;
}
}
}
public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null): void
{
$download_path = DOWNLOAD_PATH . "/{$name}";
@ -257,10 +239,10 @@ class Downloader
}
/**
* 拉取资源
* Download source by name and meta.
*
* @param string $name 资源名称
* @param null|array $source 资源参数,包含 type、path、rev、url、filename、regex、license
* @param string $name source name
* @param null|array $source source meta info: [type, path, rev, url, filename, regex, license]
* @throws DownloaderException
* @throws FileSystemException
* @throws RuntimeException
@ -291,35 +273,35 @@ class Downloader
try {
switch ($source['type']) {
case 'bitbuckettag': // BitBucket Tag 拉取
case 'bitbuckettag': // BitBucket Tag
[$url, $filename] = self::getLatestBitbucketTag($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
break;
case 'ghtar': // 从 GitHub 的 TarBall 拉取
case 'ghtar': // GitHub Release (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
break;
case 'ghtagtar': // 根据 GitHub 的 Tag 拉取相应版本的 Tar
case 'ghtagtar': // GitHub Tag (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags');
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
break;
case 'ghrel': // 通过 GitHub Release 来拉取
case 'ghrel': // GitHub Release (uploaded)
[$url, $filename] = self::getLatestGithubRelease($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
break;
case 'filelist': // 通过网站提供的 filelist 使用正则提取后拉取
case 'filelist': // Basic File List (regex based crawler)
[$url, $filename] = self::getFromFileList($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
break;
case 'url': // 通过直链拉取
case 'url': // Direct download URL
$url = $source['url'];
$filename = $source['filename'] ?? basename($source['url']);
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
break;
case 'git': // 通过拉回 Git 仓库的形式拉取
case 'git': // Git repo
self::downloadGit($name, $source['url'], $source['rev'], $source['path'] ?? null);
break;
case 'custom': // 自定义,可能是通过复杂 API 形式获取的文件,需要手写 crawler
case 'custom': // Custom download method, like API-based download or other
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\\store\\source');
foreach ($classes as $class) {
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
@ -332,7 +314,8 @@ class Downloader
throw new DownloaderException('unknown source type: ' . $source['type']);
}
} catch (RuntimeException $e) {
// 因为某些时候通过命令行下载的文件在失败后不会删除,这里检测到文件存在需要手动删一下
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
logger()->warning('Deleting download file: ' . $filename);
unlink(DOWNLOAD_PATH . '/' . $filename);
@ -342,27 +325,7 @@ class Downloader
}
/**
* 获取 PHP x.y 的具体版本号,例如通过 8.1 来获取 8.1.10
*
* @throws DownloaderException
*/
#[ArrayShape(['type' => 'string', 'path' => 'string', 'rev' => 'string', 'url' => 'string'])]
public static function getLatestPHPInfo(string $major_version): array
{
// 查找最新的小版本号
$info = json_decode(self::curlExec(url: "https://www.php.net/releases/index.php?json&version={$major_version}"), true);
$version = $info['version'];
// 从官网直接下载
return [
'type' => 'url',
'url' => "https://www.php.net/distributions/php-{$version}.tar.gz",
// 'url' => "https://mirrors.zhamao.xin/php/php-{$version}.tar.gz",
];
}
/**
* 使用 curl 命令拉取元信息
* Use curl command to get http response
*
* @throws DownloaderException
*/
@ -408,7 +371,7 @@ class Downloader
}
/**
* 使用 curl 命令下载文件
* Use curl to download sources from url
*
* @throws DownloaderException
* @throws RuntimeException

View File

@ -55,23 +55,25 @@ class FileSystem
/**
* @throws FileSystemException
*/
public static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): bool|int
public static function replaceFileStr(string $filename, mixed $search = null, mixed $replace = null): bool|int
{
logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename);
$file = self::readFile($filename);
switch ($replace_type) {
case REPLACE_FILE_STR:
default:
$file = str_replace($callback_or_search, $to_replace, $file);
break;
case REPLACE_FILE_PREG:
$file = preg_replace($callback_or_search, $to_replace, $file);
break;
case REPLACE_FILE_USER:
$file = $callback_or_search($file);
break;
}
return file_put_contents($filename, $file);
return self::replaceFile($filename, REPLACE_FILE_STR, $search, $replace);
}
/**
* @throws FileSystemException
*/
public static function replaceFileRegex(string $filename, mixed $search = null, mixed $replace = null): bool|int
{
return self::replaceFile($filename, REPLACE_FILE_PREG, $search, $replace);
}
/**
* @throws FileSystemException
*/
public static function replaceFileUser(string $filename, mixed $callback = null): bool|int
{
return self::replaceFile($filename, REPLACE_FILE_USER, $callback);
}
/**
@ -117,6 +119,9 @@ class FileSystem
return null;
}
/**
* @throws RuntimeException
*/
public static function copyDir(string $from, string $to): void
{
$dst_path = FileSystem::convertPath($to);
@ -384,6 +389,9 @@ class FileSystem
return rmdir($dir);
}
/**
* @throws FileSystemException
*/
public static function createDir(string $path): void
{
if (!is_dir($path) && !f_mkdir($path, 0755, true) && !is_dir($path)) {
@ -391,7 +399,11 @@ class FileSystem
}
}
public static function writeFile(string $path, $content, ...$args): bool|string|int
/**
* @param mixed ...$args Arguments passed to file_put_contents
* @throws FileSystemException
*/
public static function writeFile(string $path, mixed $content, ...$args): bool|string|int
{
$dir = pathinfo($path, PATHINFO_DIRNAME);
if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
@ -413,7 +425,7 @@ class FileSystem
self::createDir($dir_name);
}
public static function addSourceExtractHook(string $name, callable $callback)
public static function addSourceExtractHook(string $name, callable $callback): void
{
self::$_extract_hook[$name][] = $callback;
}
@ -425,14 +437,35 @@ class FileSystem
*/
public static function isRelativePath(string $path): bool
{
// 适配 Windows 的多盘符目录形式
if (DIRECTORY_SEPARATOR === '\\') {
return !(strlen($path) > 2 && ctype_alpha($path[0]) && $path[1] === ':');
}
return strlen($path) > 0 && $path[0] !== '/';
}
private static function emitSourceExtractHook(string $name)
/**
* @throws FileSystemException
*/
private static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): bool|int
{
logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename);
$file = self::readFile($filename);
switch ($replace_type) {
case REPLACE_FILE_STR:
default:
$file = str_replace($callback_or_search, $to_replace, $file);
break;
case REPLACE_FILE_PREG:
$file = preg_replace($callback_or_search, $to_replace, $file);
break;
case REPLACE_FILE_USER:
$file = $callback_or_search($file);
break;
}
return file_put_contents($filename, $file);
}
private static function emitSourceExtractHook(string $name): void
{
foreach ((self::$_extract_hook[$name] ?? []) as $hook) {
if ($hook() === true) {

View File

@ -4,10 +4,17 @@ declare(strict_types=1);
namespace SPC\store;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class SourceExtractor
{
/**
* @throws WrongUsageException
* @throws FileSystemException
* @throws RuntimeException
*/
public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void
{
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {

View File

@ -11,7 +11,7 @@ use SPC\exception\RuntimeException;
class SourcePatcher
{
public static function init()
public static function init(): void
{
// FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']);
@ -46,9 +46,13 @@ class SourcePatcher
}
}
// patch capstone
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, '/have_capstone="yes"/', 'have_capstone="no"');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"');
}
/**
* @throws RuntimeException
* @throws FileSystemException
*/
public static function patchMicro(?array $list = null, bool $reverse = false): bool
{
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
@ -66,7 +70,7 @@ class SourcePatcher
if ($major_ver === '74') {
return false;
}
$check = !defined('DEBUG_MODE') ? ' -q' : '';
// $check = !defined('DEBUG_MODE') ? ' -q' : '';
// f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD');
$default = [
@ -113,15 +117,13 @@ class SourcePatcher
return true;
}
/**
* @throws FileSystemException
*/
public static function patchOpenssl11Darwin(): bool
{
if (PHP_OS_FAMILY === 'Darwin' && !file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c')) {
FileSystem::replaceFile(
SOURCE_PATH . '/openssl/test/v3ext.c',
REPLACE_FILE_STR,
'#include <stdio.h>',
'#include <stdio.h>' . PHP_EOL . '#include <string.h>'
);
FileSystem::replaceFileStr(SOURCE_PATH . '/openssl/test/v3ext.c', '#include <stdio.h>', '#include <stdio.h>' . PHP_EOL . '#include <string.h>');
return true;
}
return false;
@ -134,10 +136,10 @@ class SourcePatcher
{
// Try to fix debian environment build lack HAVE_STRLCAT problem
if ($builder instanceof LinuxBuilder) {
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', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', '');
}
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_OPENPTY 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
// call extension patch before make
foreach ($builder->getExts() as $ext) {
@ -158,7 +160,7 @@ class SourcePatcher
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
// Try to reverse backup file
$find_pattern = 'const char HARDCODED_INI[] =';
$find_str = 'const char HARDCODED_INI[] =';
$patch_str = '';
foreach ($ini as $key => $value) {
$patch_str .= "\"{$key}={$value}\\n\"\n";
@ -178,8 +180,8 @@ class SourcePatcher
}
// Patch it
FileSystem::replaceFile($cli_c, REPLACE_FILE_STR, $find_pattern, $patch_str);
FileSystem::replaceFile($micro_c, REPLACE_FILE_STR, $find_pattern, $patch_str);
FileSystem::replaceFileStr($cli_c, $find_str, $patch_str);
FileSystem::replaceFileStr($micro_c, $find_str, $patch_str);
return true;
}

View File

@ -6,6 +6,7 @@ namespace SPC\store\source;
use JetBrains\PhpStorm\ArrayShape;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\Downloader;
@ -16,8 +17,9 @@ class PhpSource extends CustomSourceBase
/**
* @throws DownloaderException
* @throws RuntimeException
* @throws FileSystemException
*/
public function fetch()
public function fetch(): void
{
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.1';
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major));

View File

@ -4,20 +4,31 @@ declare(strict_types=1);
namespace SPC\store\source;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\Downloader;
class PostgreSQLSource extends CustomSourceBase
{
public const NAME = 'postgresql';
public function fetch()
/**
* @throws DownloaderException
* @throws RuntimeException
* @throws FileSystemException
*/
public function fetch(): void
{
Downloader::downloadSource('postgresql', self::getLatestInfo());
}
/**
* @throws DownloaderException
*/
public function getLatestInfo(): array
{
[$url, $filename, $version] = Downloader::getFromFileList('postgresql', [
[, $filename, $version] = Downloader::getFromFileList('postgresql', [
'url' => 'https://www.postgresql.org/ftp/source/',
'regex' => '/href="(?<file>v(?<version>[^"]+)\/)"/',
]);

View File

@ -49,10 +49,9 @@ class ConfigValidator
}
/**
* @param mixed $data
* @throws ValidationException
*/
public static function validateLibs($data, array $source_data = []): void
public static function validateLibs(mixed $data, array $source_data = []): void
{
is_array($data) || throw new ValidationException('lib.json is broken');
foreach ($data as $name => $lib) {
@ -64,7 +63,10 @@ class ConfigValidator
}
}
public static function validateExts($data, array $source_data = []): void
/**
* @throws ValidationException
*/
public static function validateExts(mixed $data): void
{
is_array($data) || throw new ValidationException('ext.json is broken');
}

View File

@ -15,18 +15,17 @@ use SPC\store\Config;
class DependencyUtil
{
/**
* 根据需要的 ext 列表获取依赖的 lib 列表,同时根据依赖关系排序
* Obtain the dependent lib list according to the required ext list, and sort according to the dependency
*
* @param array $exts 要获取 libs 依赖的列表
* @param array $additional_libs 额外要添加的库列表,用于激活 lib-suggests 触发的额外库特性
* @return array 返回一个包含三个数组的数组,第一个是排序后的 ext 列表,第二个是排序后的 lib 列表,第三个是没有传入但是依赖了的 ext 列表
* @param array $exts extensions list
* @param array $additional_libs List of additional libraries to add to activate the extra library features triggered by lib-suggests
* @return array Returns an array containing three arrays, [extensions, libraries, not included extensions]
* @throws WrongUsageException
* @throws RuntimeException
* @throws FileSystemException
*/
public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array
{
// 先对扩展列表进行一个依赖筛选
$sorted = [];
$visited = [];
$not_included_exts = [];
@ -49,7 +48,7 @@ class DependencyUtil
}
}
$libs = $additional_libs;
// 遍历每一个 ext 的 libs
foreach ($final as $ext) {
if (!in_array($ext, $exts)) {
$not_included_exts[] = $ext;

View File

@ -19,17 +19,17 @@ class Patcher
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', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#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', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#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', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_CRYPT 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_CRYPT_R 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_CRYPT_H 1$/m', '');
break;
default:
throw new RuntimeException('not implemented');

View File

@ -63,6 +63,9 @@ class UnixShell
return $this;
}
/**
* @throws RuntimeException
*/
public function execWithEnv(string $cmd): UnixShell
{
return $this->exec($this->getEnvString() . ' ' . $cmd);

Some files were not shown because too many files have changed in this diff Show More