mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 04:44:53 +08:00
refactor download
This commit is contained in:
parent
117cd93e8f
commit
0bed76da11
106
.github/workflows/build-arm.yml
vendored
Normal file
106
.github/workflows/build-arm.yml
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
name: CI on arm linux
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
operating-system:
|
||||
required: true
|
||||
description: Compile target arch (Linux only)
|
||||
type: choice
|
||||
options:
|
||||
- aarch64
|
||||
- armv7l
|
||||
version:
|
||||
required: true
|
||||
description: php version to compile
|
||||
default: '8.2'
|
||||
type: choice
|
||||
options:
|
||||
- '8.2'
|
||||
- '8.1'
|
||||
- '8.0'
|
||||
- '7.4'
|
||||
build-cli:
|
||||
description: build cli binary
|
||||
default: true
|
||||
type: boolean
|
||||
build-micro:
|
||||
description: build phpmicro binary
|
||||
type: boolean
|
||||
build-fpm:
|
||||
description: build fpm binary
|
||||
type: boolean
|
||||
extensions:
|
||||
description: extensions to compile (comma separated)
|
||||
required: true
|
||||
type: string
|
||||
debug:
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build ${{ inputs.version }} on ${{ inputs.operating-system }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Cache downloaded source
|
||||
- id: cache-download
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: downloads
|
||||
key: php-${{ inputs.version }}-dependencies
|
||||
|
||||
# With or without debug
|
||||
- if: inputs.debug == true
|
||||
run: echo "SPC_BUILD_DEBUG=--debug" >> $GITHUB_ENV
|
||||
|
||||
# With target select: cli, micro or both
|
||||
- if: ${{ inputs.build-cli == true }}
|
||||
run: echo "SPC_BUILD_CLI=--build-cli" >> $GITHUB_ENV
|
||||
- if: ${{ inputs.build-micro == true }}
|
||||
run: echo "SPC_BUILD_MICRO=--build-micro" >> $GITHUB_ENV
|
||||
- if: ${{ inputs.build-fpm == true }}
|
||||
run: echo "SPC_BUILD_FPM=--build-fpm" >> $GITHUB_ENV
|
||||
|
||||
# If there's no dependencies cache, fetch sources, with or without debug
|
||||
- run: SPC_USE_ARCH=${{ inputs.operating-system }} ./bin/spc-alpine-docker download --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }}
|
||||
|
||||
# Run build command
|
||||
- run: SPC_USE_ARCH=${{ inputs.operating-system }} ./bin/spc-alpine-docker build ${{ inputs.extensions }} ${{ env.SPC_BUILD_DEBUG }} ${{ env.SPC_BUILD_CLI }} ${{ env.SPC_BUILD_MICRO }} ${{ env.SPC_BUILD_FPM }}
|
||||
|
||||
# Upload cli executable
|
||||
- if: ${{ inputs.build-cli == true }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: php-${{ inputs.version }}-linux-${{ inputs.operating-system }}
|
||||
path: buildroot/bin/php
|
||||
|
||||
# Upload micro self-extracted executable
|
||||
- if: ${{ inputs.build-micro == true }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: micro-${{ inputs.version }}-linux-${{ inputs.operating-system }}
|
||||
path: buildroot/bin/micro.sfx
|
||||
|
||||
# Upload fpm executable
|
||||
- if: ${{ inputs.build-fpm == true }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: php-fpm-${{ inputs.version }}-linux-${{ inputs.operating-system }}
|
||||
path: buildroot/bin/php-fpm
|
||||
|
||||
# Upload extensions metadata
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: license-files
|
||||
path: buildroot/license/
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-meta
|
||||
path: |
|
||||
buildroot/build-extensions.json
|
||||
buildroot/build-libraries.json
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: CI
|
||||
name: CI on x86_64
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@ -90,7 +90,7 @@ jobs:
|
||||
run: echo "SPC_BUILD_FPM=--build-fpm" >> $GITHUB_ENV
|
||||
|
||||
# If there's no dependencies cache, fetch sources, with or without debug
|
||||
- run: CACHE_API_EXEC=yes ./bin/spc fetch --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }}
|
||||
- run: CACHE_API_EXEC=yes ./bin/spc download --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }}
|
||||
|
||||
# Run build command
|
||||
- run: ./bin/spc build ${{ inputs.extensions }} ${{ env.SPC_BUILD_DEBUG }} ${{ env.SPC_BUILD_CLI }} ${{ env.SPC_BUILD_MICRO }} ${{ env.SPC_BUILD_FPM }}
|
||||
|
||||
80
bin/spc-alpine-docker
Executable file
80
bin/spc-alpine-docker
Executable file
@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 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"
|
||||
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"
|
||||
echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again'
|
||||
exit 1
|
||||
fi
|
||||
DOCKER_EXECUTABLE="sudo docker"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# to check if qemu-docker run
|
||||
if [ "$SPC_USE_ARCH" = "" ]; then
|
||||
SPC_USE_ARCH=x86_64
|
||||
fi
|
||||
case $SPC_USE_ARCH in
|
||||
x86_64)
|
||||
ALPINE_FROM=alpine:edge
|
||||
;;
|
||||
aarch64)
|
||||
ALPINE_FROM=multiarch/alpine:aarch64-edge
|
||||
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
|
||||
;;
|
||||
armv7l)
|
||||
ALPINE_FROM=multiarch/alpine:armv7-edge
|
||||
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
|
||||
;;
|
||||
*)
|
||||
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$SPC_USE_MIRROR" = "yes" ]; then
|
||||
SPC_USE_MIRROR="RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories"
|
||||
else
|
||||
SPC_USE_MIRROR="RUN echo 'Using original repo'"
|
||||
fi
|
||||
|
||||
# Detect docker env is setup
|
||||
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH; then
|
||||
echo "Docker container does not exist. Building docker image ..."
|
||||
ALPINE_DOCKERFILE=$(cat << EOF
|
||||
FROM $ALPINE_FROM
|
||||
$SPC_USE_MIRROR
|
||||
RUN apk update
|
||||
RUN apk add bash file wget cmake gcc g++ jq autoconf git libstdc++ linux-headers make m4 libgcc binutils bison flex pkgconfig automake curl
|
||||
RUN apk add build-base xz php81 php81-common php81-pcntl php81-tokenizer php81-phar php81-posix php81-xml composer
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
ADD ./src /app/src
|
||||
ADD ./composer.json /app/composer.json
|
||||
ADD ./bin /app/bin
|
||||
RUN composer update --no-dev
|
||||
EOF
|
||||
)
|
||||
echo "$ALPINE_DOCKERFILE" > $(pwd)/Dockerfile
|
||||
|
||||
$DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH .
|
||||
rm $(pwd)/Dockerfile
|
||||
fi
|
||||
|
||||
# Run docker
|
||||
$DOCKER_EXECUTABLE run --rm -it -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 $@
|
||||
@ -15,7 +15,8 @@
|
||||
"symfony/console": "^6 || ^5 || ^4",
|
||||
"zhamao/logger": "^1.0",
|
||||
"crazywhalecc/cli-helper": "^0.1.0",
|
||||
"nunomaduro/collision": "*"
|
||||
"nunomaduro/collision": "*",
|
||||
"ext-pcntl": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2 != 3.7.0",
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
{
|
||||
"php-src": {
|
||||
"type": "custom",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"brotli": {
|
||||
"type": "ghtar",
|
||||
"repo": "google/brotli",
|
||||
@ -229,13 +236,6 @@
|
||||
"path": "LICENSE.txt"
|
||||
}
|
||||
},
|
||||
"php-src": {
|
||||
"type": "custom",
|
||||
"license": {
|
||||
"type": "file",
|
||||
"path": "LICENSE"
|
||||
}
|
||||
},
|
||||
"postgresql": {
|
||||
"type": "custom",
|
||||
"license": {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
> - partial with issue link: supported but not perfect due to issue
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
|------------|----------------------------------------------------------------|---------------------------------------------------------------------|---------|
|
||||
|------------|---------------------------------------------------------------------|--------------------------------------------------------------------|---------|
|
||||
| bcmath | yes | yes | |
|
||||
| bz2 | yes | yes | |
|
||||
| calendar | yes | yes | |
|
||||
@ -49,8 +49,8 @@
|
||||
| soap | yes | yes | |
|
||||
| sockets | yes | yes | |
|
||||
| sqlite3 | yes | yes | |
|
||||
| swow | yes | [no](https://github.com/crazywhalecc/static-php-cli/issues/32) | |
|
||||
| swoole | [no](https://github.com/crazywhalecc/static-php-cli/issues/32) | [partial](https://github.com/crazywhalecc/static-php-cli/issues/32) | |
|
||||
| swow | yes | yes | |
|
||||
| swoole | [partial](https://github.com/crazywhalecc/static-php-cli/issues/32) | yes | |
|
||||
| tokenizer | yes | yes | |
|
||||
| xml | yes | yes | |
|
||||
| xmlreader | yes, untested | yes, untested | |
|
||||
|
||||
@ -6,6 +6,7 @@ namespace SPC\builder;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\CustomExt;
|
||||
@ -40,8 +41,9 @@ abstract class BuilderBase
|
||||
/**
|
||||
* 构建指定列表的 libs
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public function buildLibs(array $libraries): void
|
||||
{
|
||||
@ -80,13 +82,14 @@ abstract class BuilderBase
|
||||
$this->addLib($lib);
|
||||
}
|
||||
|
||||
// 统计还没 fetch 到本地的库
|
||||
$this->checkLibsSource();
|
||||
|
||||
// 计算依赖,经过这里的遍历,如果没有抛出异常,说明依赖符合要求,可以继续下面的
|
||||
foreach ($this->libs as $lib) {
|
||||
$lib->calcDependency();
|
||||
}
|
||||
|
||||
$this->initSource(libs: $libraries);
|
||||
|
||||
// 构建库
|
||||
foreach ($this->libs as $lib) {
|
||||
match ($lib->tryBuild()) {
|
||||
BUILD_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] build success'),
|
||||
@ -154,6 +157,11 @@ abstract class BuilderBase
|
||||
public function proveExts(array $extensions): void
|
||||
{
|
||||
CustomExt::loadCustomExt();
|
||||
$this->initSource(sources: ['php-src']);
|
||||
if ($this->getPHPVersionID() >= 80000) {
|
||||
$this->initSource(sources: ['micro']);
|
||||
}
|
||||
$this->initSource(exts: $extensions);
|
||||
foreach ($extensions as $extension) {
|
||||
$class = CustomExt::getExtClass($extension);
|
||||
$ext = new $class($extension, $this);
|
||||
@ -248,4 +256,52 @@ abstract class BuilderBase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void
|
||||
{
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?');
|
||||
}
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
||||
|
||||
$sources_extracted = [];
|
||||
// source check exist
|
||||
if (is_array($sources)) {
|
||||
foreach ($sources as $source) {
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
// lib check source exist
|
||||
if (is_array($libs)) {
|
||||
foreach ($libs as $lib) {
|
||||
// get source name for lib
|
||||
$source = Config::getLib($lib, 'source');
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
// ext check source exist
|
||||
if (is_array($exts)) {
|
||||
foreach ($exts as $ext) {
|
||||
// get source name for ext
|
||||
if (Config::getExt($ext, 'type') !== 'external') {
|
||||
continue;
|
||||
}
|
||||
$source = Config::getExt($ext, 'source');
|
||||
$sources_extracted[$source] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// start check
|
||||
foreach ($sources_extracted as $source => $item) {
|
||||
if (!isset($lock[$source])) {
|
||||
throw new WrongUsageException('Source [' . $source . '] not downloaded, you should download it first !');
|
||||
}
|
||||
|
||||
// check source dir exist
|
||||
$check = $lock[$source]['move_path'] === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $lock[$source]['move_path'];
|
||||
if (!is_dir($check)) {
|
||||
FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,11 +13,8 @@ class swoole extends Extension
|
||||
public function getUnixConfigureArg(): string
|
||||
{
|
||||
$arg = '--enable-swoole';
|
||||
if ($this->builder->getLib('openssl')) {
|
||||
$arg .= ' --enable-openssl';
|
||||
} else {
|
||||
$arg .= ' --disable-openssl --without-openssl';
|
||||
}
|
||||
$arg .= $this->builder->getLib('openssl') ? ' --enable-openssl' : ' --disable-openssl --without-openssl';
|
||||
$arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : '';
|
||||
// curl hook is buggy for static php
|
||||
$arg .= ' --disable-swoole-curl';
|
||||
return $arg;
|
||||
|
||||
@ -10,7 +10,7 @@ use SPC\builder\traits\UnixBuilderTrait;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\util\Patcher;
|
||||
use SPC\store\SourcePatcher;
|
||||
|
||||
/**
|
||||
* Linux 系统环境下的构建器
|
||||
@ -46,7 +46,10 @@ class LinuxBuilder extends BuilderBase
|
||||
public function __construct(?string $cc = null, ?string $cxx = null, ?string $arch = null)
|
||||
{
|
||||
// 初始化一些默认参数
|
||||
$this->cc = $cc ?? 'musl-gcc';
|
||||
$this->cc = $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);
|
||||
@ -135,6 +138,9 @@ class LinuxBuilder extends BuilderBase
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($this->getExt('swoole')) {
|
||||
$extra_libs .= ' -lstdc++';
|
||||
}
|
||||
|
||||
$envs = $this->pkgconf_env . ' ' .
|
||||
"CC='{$this->cc}' " .
|
||||
@ -158,11 +164,11 @@ class LinuxBuilder extends BuilderBase
|
||||
|
||||
$envs = "{$envs} CFLAGS='{$cflags}' LIBS='-ldl -lpthread'";
|
||||
|
||||
Patcher::patchPHPBeforeConfigure($this);
|
||||
SourcePatcher::patchPHPBuildconf($this);
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
|
||||
|
||||
Patcher::patchPHPConfigure($this);
|
||||
SourcePatcher::patchPHPConfigure($this);
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(
|
||||
@ -183,6 +189,8 @@ class LinuxBuilder extends BuilderBase
|
||||
$envs
|
||||
);
|
||||
|
||||
SourcePatcher::patchPHPAfterConfigure($this);
|
||||
|
||||
file_put_contents('/tmp/comment', $this->note_section);
|
||||
|
||||
// 清理
|
||||
|
||||
@ -13,44 +13,6 @@ class SystemUtil
|
||||
{
|
||||
use UnixSystemUtilTrait;
|
||||
|
||||
/**
|
||||
* 查找并选择编译器命令
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function selectCC(): string
|
||||
{
|
||||
logger()->debug('Choose cc');
|
||||
if (self::findCommand('clang')) {
|
||||
logger()->info('using clang');
|
||||
return 'clang';
|
||||
}
|
||||
if (self::findCommand('gcc')) {
|
||||
logger()->info('using gcc');
|
||||
return 'gcc';
|
||||
}
|
||||
throw new RuntimeException('no supported cc found');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找并选择编译器命令
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function selectCXX(): string
|
||||
{
|
||||
logger()->debug('Choose cxx');
|
||||
if (self::findCommand('clang++')) {
|
||||
logger()->info('using clang++');
|
||||
return 'clang++';
|
||||
}
|
||||
if (self::findCommand('g++')) {
|
||||
logger()->info('using g++');
|
||||
return 'g++';
|
||||
}
|
||||
return self::selectCC();
|
||||
}
|
||||
|
||||
#[ArrayShape(['dist' => 'mixed|string', 'ver' => 'mixed|string'])]
|
||||
public static function getOSRelease(): array
|
||||
{
|
||||
|
||||
@ -22,7 +22,7 @@ namespace SPC\builder\linux\library;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\Patcher;
|
||||
use SPC\store\SourcePatcher;
|
||||
|
||||
class libpng extends LinuxLibraryBase
|
||||
{
|
||||
@ -41,7 +41,7 @@ class libpng extends LinuxLibraryBase
|
||||
};
|
||||
|
||||
// patch configure
|
||||
Patcher::patchUnixLibpng();
|
||||
SourcePatcher::patchUnixLibpng();
|
||||
|
||||
shell()->cd($this->source_dir)
|
||||
->exec('chmod +x ./configure')
|
||||
|
||||
@ -10,7 +10,7 @@ use SPC\builder\traits\UnixBuilderTrait;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\util\Patcher;
|
||||
use SPC\store\SourcePatcher;
|
||||
|
||||
/**
|
||||
* macOS 系统环境下的构建器
|
||||
@ -136,11 +136,11 @@ class MacOSBuilder extends BuilderBase
|
||||
}
|
||||
|
||||
// patch before configure
|
||||
Patcher::patchPHPBeforeConfigure($this);
|
||||
SourcePatcher::patchPHPBuildconf($this);
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
|
||||
|
||||
Patcher::patchPHPConfigure($this);
|
||||
SourcePatcher::patchPHPConfigure($this);
|
||||
|
||||
if ($this->getLib('libxml2') || $this->getExt('iconv')) {
|
||||
$extra_libs .= ' -liconv';
|
||||
@ -166,6 +166,8 @@ class MacOSBuilder extends BuilderBase
|
||||
$this->configure_env
|
||||
);
|
||||
|
||||
SourcePatcher::patchPHPAfterConfigure($this);
|
||||
|
||||
$this->cleanMake();
|
||||
|
||||
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
|
||||
|
||||
@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\util\Patcher;
|
||||
use SPC\store\SourcePatcher;
|
||||
|
||||
class curl extends MacOSLibraryBase
|
||||
{
|
||||
@ -32,7 +32,7 @@ class curl extends MacOSLibraryBase
|
||||
|
||||
protected function build()
|
||||
{
|
||||
Patcher::patchCurlMacOS();
|
||||
SourcePatcher::patchCurlMacOS();
|
||||
$this->unixBuild();
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ namespace SPC\command;
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use SPC\ConsoleApplication;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@ -84,12 +86,23 @@ abstract class BaseCommand extends Command
|
||||
if ($this->shouldExecute()) {
|
||||
try {
|
||||
return $this->handle();
|
||||
} catch (\Throwable $e) {
|
||||
} catch (WrongUsageException $e) {
|
||||
$msg = explode("\n", $e->getMessage());
|
||||
foreach ($msg as $v) {
|
||||
logger()->error($v);
|
||||
}
|
||||
return self::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
// 不开 debug 模式就不要再显示复杂的调试栈信息了
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
$msg = explode("\n", $e->getMessage());
|
||||
foreach ($msg as $v) {
|
||||
logger()->error($v);
|
||||
}
|
||||
}
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
return self::SUCCESS;
|
||||
|
||||
@ -73,14 +73,22 @@ class BuildCliCommand extends BuildCommand
|
||||
// 统计时间
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build complete, used ' . $time . ' s !');
|
||||
$build_root_path = BUILD_ROOT_PATH;
|
||||
$cwd = getcwd();
|
||||
$fixed = '';
|
||||
if (!empty(getenv('SPC_FIX_DEPLOY_ROOT'))) {
|
||||
str_replace($cwd, '', $build_root_path);
|
||||
$build_root_path = getenv('SPC_FIX_DEPLOY_ROOT') . $build_root_path;
|
||||
$fixed = ' (host system)';
|
||||
}
|
||||
if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
|
||||
logger()->info('Static php binary path: ' . BUILD_ROOT_PATH . '/bin/php');
|
||||
logger()->info('Static php binary path' . $fixed . ': ' . $build_root_path . '/bin/php');
|
||||
}
|
||||
if (($rule & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
|
||||
logger()->info('phpmicro binary path: ' . BUILD_ROOT_PATH . '/bin/micro.sfx');
|
||||
logger()->info('phpmicro binary path' . $fixed . ': ' . $build_root_path . '/bin/micro.sfx');
|
||||
}
|
||||
if (($rule & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
|
||||
logger()->info('Static php-fpm binary path: ' . BUILD_ROOT_PATH . '/bin/php-fpm');
|
||||
logger()->info('Static php-fpm binary path' . $fixed . ': ' . $build_root_path . '/bin/php-fpm');
|
||||
}
|
||||
// 导出相关元数据
|
||||
file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
@ -88,7 +96,7 @@ class BuildCliCommand extends BuildCommand
|
||||
// 导出 LICENSE
|
||||
$dumper = new LicenseDumper();
|
||||
$dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license');
|
||||
logger()->info('License path: ' . BUILD_ROOT_PATH . '/license/');
|
||||
logger()->info('License path' . $fixed . ': ' . $build_root_path . '/license/');
|
||||
return 0;
|
||||
} catch (WrongUsageException $e) {
|
||||
logger()->critical($e->getMessage());
|
||||
|
||||
@ -18,6 +18,7 @@ class DoctorCommand extends BaseCommand
|
||||
$this->output->writeln('<info>Doctor check complete !</info>');
|
||||
} catch (\Throwable $e) {
|
||||
$this->output->writeln('<error>' . $e->getMessage() . '</error>');
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
114
src/SPC/command/DownloadCommand.php
Normal file
114
src/SPC/command/DownloadCommand.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\Downloader;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand('download', 'Download required sources', ['fetch'])]
|
||||
class DownloadCommand extends BaseCommand
|
||||
{
|
||||
protected string $php_major_ver;
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
|
||||
$this->addOption('shallow-clone', null, null, 'Clone shallow');
|
||||
$this->addOption('with-openssl11', null, null, 'Use openssl 1.1');
|
||||
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1');
|
||||
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
||||
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// --all 等于 "" "",也就是所有东西都要下载
|
||||
if ($input->getOption('all') || $input->getOption('clean')) {
|
||||
$input->setArgument('sources', '');
|
||||
}
|
||||
parent::initialize($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
// 删除旧资源
|
||||
if ($this->getOption('clean')) {
|
||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||
logger()->warning(SOURCE_PATH);
|
||||
logger()->warning(DOWNLOAD_PATH);
|
||||
logger()->warning(BUILD_ROOT_PATH);
|
||||
logger()->alert('I will remove these dir after 5 seconds !');
|
||||
sleep(5);
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . SOURCE_PATH);
|
||||
f_passthru('rmdir /s /q ' . DOWNLOAD_PATH);
|
||||
f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH);
|
||||
} else {
|
||||
f_passthru('rm -rf ' . SOURCE_PATH . '/*');
|
||||
f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*');
|
||||
f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Define PHP major version
|
||||
$ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1';
|
||||
define('SPC_BUILD_PHP_VERSION', $ver);
|
||||
preg_match('/^\d+\.\d+$/', $ver, $matches);
|
||||
if (!$matches) {
|
||||
logger()->error("bad version arg: {$ver}, x.y required!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Use shallow-clone can reduce git resource download
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// To read config
|
||||
Config::getSource('openssl');
|
||||
|
||||
// use openssl 1.1
|
||||
if ($this->getOption('with-openssl11')) {
|
||||
logger()->debug('Using openssl 1.1');
|
||||
// 手动修改配置
|
||||
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
||||
}
|
||||
|
||||
// get source list that will be downloaded
|
||||
$sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
|
||||
if (empty($sources)) {
|
||||
$sources = array_keys(Config::getSources());
|
||||
}
|
||||
$chosen_sources = $sources;
|
||||
|
||||
// Download them
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
$cnt = count($chosen_sources);
|
||||
$ni = 0;
|
||||
foreach ($chosen_sources as $source) {
|
||||
++$ni;
|
||||
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
|
||||
Downloader::downloadSource($source, Config::getSource($source));
|
||||
}
|
||||
// 打印拉取资源用时
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Download complete, used ' . $time . ' s !');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1,234 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\InvalidArgumentException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\util\Patcher;
|
||||
use SPC\util\Util;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
#[AsCommand('fetch', 'Fetch required sources')]
|
||||
class FetchSourceCommand extends BaseCommand
|
||||
{
|
||||
protected string $php_major_ver;
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
||||
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
||||
$this->addOption('hash', null, null, 'Hash only');
|
||||
$this->addOption('shallow-clone', null, null, 'Clone shallow');
|
||||
$this->addOption('with-openssl11', null, null, 'Use openssl 1.1');
|
||||
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1');
|
||||
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
||||
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// --all 等于 "" "",也就是所有东西都要下载
|
||||
if ($input->getOption('all')) {
|
||||
$input->setArgument('extensions', '');
|
||||
$input->setArgument('libraries', '');
|
||||
}
|
||||
parent::initialize($input, $output);
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
// 匹配版本
|
||||
$ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1';
|
||||
define('SPC_BUILD_PHP_VERSION', $ver);
|
||||
preg_match('/^\d+\.\d+$/', $ver, $matches);
|
||||
if (!$matches) {
|
||||
logger()->error("bad version arg: {$ver}, x.y required!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 删除旧资源
|
||||
if ($this->getOption('clean')) {
|
||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||
logger()->warning(SOURCE_PATH);
|
||||
logger()->warning(DOWNLOAD_PATH);
|
||||
logger()->warning('I will remove these dir after you press [Enter] !');
|
||||
echo 'Confirm operation? [Yes] ';
|
||||
$r = strtolower(trim(fgets(STDIN)));
|
||||
if ($r !== 'yes' && $r !== '') {
|
||||
logger()->notice('Operation canceled.');
|
||||
return 1;
|
||||
}
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . SOURCE_PATH);
|
||||
f_passthru('rmdir /s /q ' . DOWNLOAD_PATH);
|
||||
} else {
|
||||
f_passthru('rm -rf ' . SOURCE_PATH);
|
||||
f_passthru('rm -rf ' . DOWNLOAD_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用浅克隆可以减少调用 git 命令下载资源时的存储空间占用
|
||||
if ($this->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// 读取源配置,随便读一个source,用于缓存 source 配置
|
||||
Config::getSource('openssl');
|
||||
|
||||
// 是否启用openssl11
|
||||
if ($this->getOption('with-openssl11')) {
|
||||
logger()->debug('Using openssl 1.1');
|
||||
// 手动修改配置
|
||||
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
||||
}
|
||||
|
||||
// 默认预选 phpmicro
|
||||
$chosen_sources = ['micro'];
|
||||
|
||||
// 从参数中获取要编译的 libraries,并转换为数组
|
||||
$libraries = array_map('trim', array_filter(explode(',', $this->getArgument('libraries'))));
|
||||
if ($libraries) {
|
||||
foreach ($libraries as $lib) {
|
||||
// 从 lib 的 config 中找到对应 source 资源名称,组成一个 lib 的 source 列表
|
||||
$src_name = Config::getLib($lib, 'source');
|
||||
$chosen_sources[] = $src_name;
|
||||
}
|
||||
} else { // 如果传入了空串,那么代表 fetch 所有包
|
||||
$chosen_sources = [...$chosen_sources, ...array_map(fn ($x) => $x['source'], array_values(Config::getLibs()))];
|
||||
}
|
||||
|
||||
// 从参数中获取要编译的 extensions,并转换为数组
|
||||
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));
|
||||
if ($extensions) {
|
||||
foreach ($extensions as $lib) {
|
||||
if (Config::getExt($lib, 'type') !== 'builtin') {
|
||||
$src_name = Config::getExt($lib, 'source');
|
||||
$chosen_sources[] = $src_name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (Config::getExts() as $ext) {
|
||||
if ($ext['type'] !== 'builtin') {
|
||||
$chosen_sources[] = $ext['source'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$chosen_sources = array_unique($chosen_sources);
|
||||
|
||||
// 是否只hash,不下载资源
|
||||
if ($this->getOption('hash')) {
|
||||
$hash = $this->doHash($chosen_sources);
|
||||
$this->output->writeln($hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 创建目录
|
||||
f_mkdir(SOURCE_PATH);
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
|
||||
// 下载 PHP
|
||||
array_unshift($chosen_sources, 'php-src');
|
||||
// 下载别的依赖资源
|
||||
$cnt = count($chosen_sources);
|
||||
$ni = 0;
|
||||
foreach ($chosen_sources as $name) {
|
||||
++$ni;
|
||||
logger()->info("Fetching source {$name} [{$ni}/{$cnt}]");
|
||||
Downloader::fetchSource($name, Config::getSource($name));
|
||||
}
|
||||
|
||||
// patch 每份资源只需一次,如果已经下载好的资源已经patch了,就标记一下不patch了
|
||||
if (!file_exists(SOURCE_PATH . '/.patched')) {
|
||||
$this->doPatch();
|
||||
} else {
|
||||
logger()->notice('sources already patched');
|
||||
}
|
||||
|
||||
// 打印拉取资源用时
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Fetch complete, used ' . $time . ' s !');
|
||||
return 0;
|
||||
} catch (\Throwable $e) {
|
||||
// 不开 debug 模式就不要再显示复杂的调试栈信息了
|
||||
if ($this->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->emergency($e->getMessage() . ', previous message: ' . $e->getPrevious()?->getMessage());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算资源名称列表的 Hash
|
||||
*
|
||||
* @param array $chosen_sources 要计算 hash 的资源名称列表
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DownloaderException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
private function doHash(array $chosen_sources): string
|
||||
{
|
||||
$files = [];
|
||||
foreach ($chosen_sources as $name) {
|
||||
$source = Config::getSource($name);
|
||||
$filename = match ($source['type']) {
|
||||
'ghtar' => Downloader::getLatestGithubTarball($name, $source)[1],
|
||||
'ghtagtar' => Downloader::getLatestGithubTarball($name, $source, 'tags')[1],
|
||||
'ghrel' => Downloader::getLatestGithubRelease($name, $source)[1],
|
||||
'filelist' => Downloader::getFromFileList($name, $source)[1],
|
||||
'url' => $source['filename'] ?? basename($source['url']),
|
||||
'git' => null,
|
||||
default => throw new InvalidArgumentException('unknown source type: ' . $source['type']),
|
||||
};
|
||||
if ($filename !== null) {
|
||||
logger()->info("found {$name} source: {$filename}");
|
||||
$files[] = $filename;
|
||||
}
|
||||
}
|
||||
return hash('sha256', implode('|', $files));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在拉回资源后,需要对一些文件做一些补丁 patch
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function doPatch(): void
|
||||
{
|
||||
// swow 需要软链接内部的文件夹才能正常编译
|
||||
if (!file_exists(SOURCE_PATH . '/php-src/ext/swow') && Util::getPHPVersionID() >= 80000) {
|
||||
Patcher::patchSwow();
|
||||
// patch 一些 PHP 的资源,以便编译
|
||||
Patcher::patchMicroThings();
|
||||
}
|
||||
|
||||
// openssl 3 需要 patch 额外的东西
|
||||
if (!$this->input->getOption('with-openssl11') && $this->php_major_ver === '8.0') {
|
||||
Patcher::patchOpenssl3();
|
||||
}
|
||||
|
||||
// openssl1.1.1q 在 MacOS 上直接编译会报错,patch 一下
|
||||
// @see: https://github.com/openssl/openssl/issues/18720
|
||||
if ($this->input->getOption('with-openssl11') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c') && PHP_OS_FAMILY === 'Darwin') {
|
||||
Patcher::patchDarwinOpenssl11();
|
||||
}
|
||||
|
||||
// 标记 patch 完成,避免重复 patch
|
||||
file_put_contents(SOURCE_PATH . '/.patched', '');
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,7 @@ class SortConfigCommand extends BaseCommand
|
||||
case 'source':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true);
|
||||
ConfigValidator::validateSource($file);
|
||||
ksort($file);
|
||||
uksort($file, fn ($a, $b) => $a === 'php-src' ? -1 : ($b === 'php-src' ? 1 : ($a < $b ? -1 : 1)));
|
||||
file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
break;
|
||||
case 'ext':
|
||||
|
||||
@ -116,8 +116,12 @@ class CheckListHandler
|
||||
|
||||
private function emitFix(CheckResult $result)
|
||||
{
|
||||
pcntl_signal(SIGINT, function () {
|
||||
$this->output->writeln('<error>You cancelled fix</error>');
|
||||
});
|
||||
$fix = $this->fix_map[$result->getFixItem()];
|
||||
$fix_result = call_user_func($fix, ...$result->getFixParams());
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
if ($fix_result) {
|
||||
$this->output->writeln('<info>Fix done</info>');
|
||||
} else {
|
||||
|
||||
@ -39,4 +39,10 @@ class CheckResult
|
||||
{
|
||||
return empty($this->message);
|
||||
}
|
||||
|
||||
public function setFixItem(string $fix_item = '', array $fix_params = [])
|
||||
{
|
||||
$this->fix_item = $fix_item;
|
||||
$this->fix_params = $fix_params;
|
||||
}
|
||||
}
|
||||
|
||||
52
src/SPC/doctor/item/LinuxMuslCheck.php
Normal file
52
src/SPC/doctor/item/LinuxMuslCheck.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\doctor\item;
|
||||
|
||||
use SPC\builder\linux\SystemUtil;
|
||||
use SPC\doctor\AsCheckItem;
|
||||
use SPC\doctor\AsFixItem;
|
||||
use SPC\doctor\CheckResult;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
class LinuxMuslCheck
|
||||
{
|
||||
#[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();
|
||||
}
|
||||
|
||||
// non-exist, need to recognize distro
|
||||
$distro = SystemUtil::getOSRelease();
|
||||
return match ($distro['dist']) {
|
||||
'ubuntu', 'alpine', 'debian' => CheckResult::fail('musl-libc is not installed on your system', 'fix-musl', [$distro]),
|
||||
default => CheckResult::fail('musl-libc is not installed on your system'),
|
||||
};
|
||||
}
|
||||
|
||||
#[AsFixItem('fix-musl')]
|
||||
public function fixMusl(array $distro): bool
|
||||
{
|
||||
$install_cmd = match ($distro['dist']) {
|
||||
'ubuntu', 'debian' => 'apt install musl musl-tools -y',
|
||||
'alpine' => 'apk add musl musl-utils musl-dev',
|
||||
default => throw new RuntimeException('Current linux distro is not supported for auto-install musl packages'),
|
||||
};
|
||||
$prefix = '';
|
||||
if (get_current_user() !== 'root') {
|
||||
$prefix = 'sudo ';
|
||||
logger()->warning('Current user is not root, using sudo for running command');
|
||||
}
|
||||
try {
|
||||
shell(true)->exec($prefix . $install_cmd);
|
||||
return true;
|
||||
} catch (RuntimeException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,4 +125,12 @@ class Config
|
||||
}
|
||||
return self::$ext;
|
||||
}
|
||||
|
||||
public static function getSources(): array
|
||||
{
|
||||
if (self::$source === null) {
|
||||
self::$source = FileSystem::loadConfigArray('source');
|
||||
}
|
||||
return self::$source;
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +148,37 @@ class Downloader
|
||||
return [$source['url'] . end($versions), end($versions), key($versions)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null): void
|
||||
{
|
||||
logger()->debug("Downloading {$url}");
|
||||
pcntl_signal(SIGINT, function () use ($filename) {
|
||||
if (file_exists(DOWNLOAD_PATH . '/' . $filename)) {
|
||||
logger()->warning('Deleting download file: ' . $filename);
|
||||
unlink(DOWNLOAD_PATH . '/' . $filename);
|
||||
}
|
||||
});
|
||||
self::curlDown(url: $url, path: DOWNLOAD_PATH . "/{$filename}");
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
logger()->debug("Locking {$filename}");
|
||||
self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path]);
|
||||
}
|
||||
|
||||
public static function lockSource(string $name, array $data): void
|
||||
{
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
$lock = [];
|
||||
} else {
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
||||
}
|
||||
$lock[$name] = $data;
|
||||
FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过链接下载资源到本地并解压
|
||||
*
|
||||
@ -183,25 +214,32 @@ class Downloader
|
||||
}
|
||||
}
|
||||
|
||||
public static function downloadGit(string $name, string $url, string $branch, ?string $path = null): void
|
||||
public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null): void
|
||||
{
|
||||
if ($path !== null) {
|
||||
$path = SOURCE_PATH . "/{$path}";
|
||||
} else {
|
||||
$path = DOWNLOAD_PATH . "/{$name}";
|
||||
}
|
||||
$download_path = DOWNLOAD_PATH . "/{$name}";
|
||||
if (file_exists($download_path)) {
|
||||
logger()->notice("{$name} git source already fetched");
|
||||
} else {
|
||||
FileSystem::removeDir($download_path);
|
||||
}
|
||||
logger()->debug("cloning {$name} source");
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
pcntl_signal(SIGINT, function () use ($download_path) {
|
||||
if (is_dir($download_path)) {
|
||||
logger()->warning('Removing path ' . $download_path);
|
||||
FileSystem::removeDir($download_path);
|
||||
}
|
||||
});
|
||||
f_passthru(
|
||||
'git clone' . $check .
|
||||
' --config core.autocrlf=false ' .
|
||||
"--branch \"{$branch}\" " . (defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '') . " --recursive \"{$url}\" \"{$download_path}\""
|
||||
);
|
||||
}
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
|
||||
// Lock
|
||||
logger()->debug("Locking git source {$name}");
|
||||
self::lockSource($name, ['source_type' => 'dir', 'dirname' => $name, 'move_path' => $move_path]);
|
||||
|
||||
/*
|
||||
// 复制目录过去
|
||||
if ($path !== $download_path) {
|
||||
$dst_path = FileSystem::convertPath($path);
|
||||
@ -215,64 +253,78 @@ class Downloader
|
||||
f_passthru('cp -r "' . $src_path . '" "' . $dst_path . '"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取资源
|
||||
*
|
||||
* @param string $name 资源名称
|
||||
* @param array $source 资源参数,包含 type、path、rev、url、filename、regex、license
|
||||
* @param null|array $source 资源参数,包含 type、path、rev、url、filename、regex、license
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function fetchSource(string $name, array $source): void
|
||||
public static function downloadSource(string $name, ?array $source = null): void
|
||||
{
|
||||
// 如果是 git 类型,且没有设置 path,那我就需要指定一下 path
|
||||
if ($source['type'] === 'git' && !isset($source['path'])) {
|
||||
$source['path'] = $name;
|
||||
if ($source === null) {
|
||||
$source = Config::getSource($name);
|
||||
}
|
||||
// 避免重复 fetch
|
||||
if (!isset($source['path']) && is_dir(FileSystem::convertPath(DOWNLOAD_PATH . "/{$name}")) || isset($source['path']) && is_dir(FileSystem::convertPath(SOURCE_PATH . "/{$source['path']}"))) {
|
||||
logger()->notice("{$name} source already extracted");
|
||||
|
||||
// load lock file
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
$lock = [];
|
||||
} else {
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
||||
}
|
||||
// If lock file exists, skip downloading
|
||||
if (isset($lock[$name])) {
|
||||
if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) {
|
||||
logger()->notice("source [{$name}] already downloaded: " . $lock[$name]['filename']);
|
||||
return;
|
||||
}
|
||||
if ($lock[$name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname'])) {
|
||||
logger()->notice("source [{$name}] already downloaded: " . $lock[$name]['dirname']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($source['type']) {
|
||||
case 'bitbuckettag': // 从 BitBucket 的 Tag 拉取
|
||||
[$url, $filename] = self::getLatestBitbucketTag($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'ghtar': // 从 GitHub 的 TarBall 拉取
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'ghtagtar': // 根据 GitHub 的 Tag 拉取相应版本的 Tar
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags');
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'ghrel': // 通过 GitHub Release 来拉取
|
||||
[$url, $filename] = self::getLatestGithubRelease($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'filelist': // 通过网站提供的 filelist 使用正则提取后拉取
|
||||
[$url, $filename] = self::getFromFileList($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'url': // 通过直链拉取
|
||||
$url = $source['url'];
|
||||
$filename = $source['filename'] ?? basename($source['url']);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
self::downloadFile($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'git': // 通过拉回 Git 仓库的形式拉取
|
||||
self::downloadGit($name, $source['url'], $source['rev'], $source['path'] ?? null);
|
||||
break;
|
||||
case 'custom':
|
||||
case 'custom': // 自定义,可能是通过复杂 API 形式获取的文件,需要手写 crawler
|
||||
$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) {
|
||||
(new $class())->fetch();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -6,10 +6,11 @@ namespace SPC\store;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
private static array $_extract_hook = [];
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@ -118,20 +119,16 @@ class FileSystem
|
||||
|
||||
public static function copyDir(string $from, string $to): void
|
||||
{
|
||||
$iterator = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($from, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_FILEINFO), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($iterator as $item) {
|
||||
/**
|
||||
* @var \SplFileInfo $item
|
||||
*/
|
||||
$target = $to . substr($item->getPathname(), strlen($from));
|
||||
if ($item->isDir()) {
|
||||
logger()->info("mkdir {$target}");
|
||||
f_mkdir($target, recursive: true);
|
||||
} else {
|
||||
logger()->info("copying {$item} to {$target}");
|
||||
@f_mkdir(dirname($target), recursive: true);
|
||||
copy($item->getPathname(), $target);
|
||||
}
|
||||
$dst_path = FileSystem::convertPath($to);
|
||||
$src_path = FileSystem::convertPath($from);
|
||||
switch (PHP_OS_FAMILY) {
|
||||
case 'Windows':
|
||||
f_passthru('xcopy "' . $src_path . '" "' . $dst_path . '" /s/e/v/y/i');
|
||||
break;
|
||||
case 'Linux':
|
||||
case 'Darwin':
|
||||
f_passthru('cp -r "' . $src_path . '" "' . $dst_path . '"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,41 +140,56 @@ class FileSystem
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function extractSource(string $name, string $filename): void
|
||||
public static function extractSource(string $name, string $filename, ?string $move_path = null): void
|
||||
{
|
||||
// if source hook is empty, load it
|
||||
if (self::$_extract_hook === []) {
|
||||
SourcePatcher::init();
|
||||
}
|
||||
if ($move_path !== null) {
|
||||
$move_path = SOURCE_PATH . '/' . $move_path;
|
||||
}
|
||||
logger()->info("extracting {$name} source");
|
||||
try {
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
if (f_mkdir(directory: SOURCE_PATH . "/{$name}", recursive: true) !== true) {
|
||||
$target = $move_path ?? (SOURCE_PATH . "/{$name}");
|
||||
// Git source, just move
|
||||
if (is_dir($filename)) {
|
||||
self::copyDir($filename, $target);
|
||||
self::emitSourceExtractHook($name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PHP_OS_FAMILY === 'Darwin' || PHP_OS_FAMILY === 'Linux') {
|
||||
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
||||
throw new FileSystemException('create ' . $name . 'source dir failed');
|
||||
}
|
||||
switch (self::extname($filename)) {
|
||||
case 'xz':
|
||||
case 'txz':
|
||||
f_passthru("tar -xf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
f_passthru("tar -xf {$filename} -C {$target} --strip-components 1");
|
||||
// f_passthru("cat {$filename} | xz -d | tar -x -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
f_passthru("tar -xzf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'bz2':
|
||||
f_passthru("tar -xjf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'zip':
|
||||
f_passthru("unzip {$filename} -d " . SOURCE_PATH . "/{$name}");
|
||||
f_passthru("unzip {$filename} -d {$target}");
|
||||
break;
|
||||
// case 'zstd':
|
||||
// case 'zst':
|
||||
// passthru('cat ' . $filename . ' | zstd -d | tar -x -C ".SOURCE_PATH . "/' . $name . ' --strip-components 1', $ret);
|
||||
// break;
|
||||
case 'tar':
|
||||
f_passthru("tar -xf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
f_passthru("tar -xf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException('unknown archive format: ' . $filename);
|
||||
}
|
||||
} else {
|
||||
} elseif (PHP_OS_FAMILY === 'Windows') {
|
||||
// find 7z
|
||||
$_7zExe = self::findCommandPath('7z', [
|
||||
'C:\Program Files\7-Zip-Zstandard',
|
||||
@ -201,13 +213,13 @@ class FileSystem
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
case 'bz2':
|
||||
f_passthru("\"{$_7zExe}\" x -so {$filename} | tar -f - -x -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
f_passthru("\"{$_7zExe}\" x -so {$filename} | tar -f - -x -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'tar':
|
||||
f_passthru("tar -xf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
f_passthru("tar -xf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'zip':
|
||||
f_passthru("\"{$_7zExe}\" x {$filename} -o" . SOURCE_PATH . "/{$name}");
|
||||
f_passthru("\"{$_7zExe}\" x {$filename} -o{$target}");
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException("unknown archive format: {$filename}");
|
||||
@ -421,4 +433,18 @@ class FileSystem
|
||||
}
|
||||
self::createDir($dir_name);
|
||||
}
|
||||
|
||||
public static function addSourceExtractHook(string $name, callable $callback)
|
||||
{
|
||||
self::$_extract_hook[$name][] = $callback;
|
||||
}
|
||||
|
||||
private static function emitSourceExtractHook(string $name)
|
||||
{
|
||||
foreach ((self::$_extract_hook[$name] ?? []) as $hook) {
|
||||
if ($hook($name) === true) {
|
||||
logger()->info('Patched source [' . $name . '] after extracted');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
212
src/SPC/store/SourcePatcher.php
Normal file
212
src/SPC/store/SourcePatcher.php
Normal file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\linux\LinuxBuilder;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\Util;
|
||||
|
||||
class SourcePatcher
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
|
||||
FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']);
|
||||
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl3']);
|
||||
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']);
|
||||
}
|
||||
|
||||
public static function patchPHPBuildconf(BuilderBase $builder): void
|
||||
{
|
||||
if ($builder->getExt('curl')) {
|
||||
logger()->info('patching before-configure for curl checks');
|
||||
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
|
||||
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
|
||||
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])';
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
|
||||
}
|
||||
|
||||
// if ($builder->getExt('pdo_sqlite')) {
|
||||
// FileSystem::replaceFile()
|
||||
// }
|
||||
}
|
||||
|
||||
public static function patchSwow(): bool
|
||||
{
|
||||
if (Util::getPHPVersionID() >= 80000) {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
|
||||
} else {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function patchPHPConfigure(BuilderBase $builder): void
|
||||
{
|
||||
$frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : '';
|
||||
$patch = [];
|
||||
if ($curl = $builder->getExt('curl')) {
|
||||
$patch[] = ['curl check', '/-lcurl/', $curl->getLibFilesString() . $frameworks];
|
||||
}
|
||||
if ($bzip2 = $builder->getExt('bz2')) {
|
||||
$patch[] = ['bzip2 check', '/-lbz2/', $bzip2->getLibFilesString() . $frameworks];
|
||||
}
|
||||
if ($pdo_sqlite = $builder->getExt('pdo_sqlite')) {
|
||||
$patch[] = ['pdo_sqlite linking', '/sqlite3_column_table_name=yes/', 'sqlite3_column_table_name=no'];
|
||||
}
|
||||
$patch[] = ['disable capstone', '/have_capstone="yes"/', 'have_capstone="no"'];
|
||||
foreach ($patch as $item) {
|
||||
logger()->info('Patching configure: ' . $item[0]);
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, $item[1], $item[2]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchUnixLibpng(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lz',
|
||||
BUILD_LIB_PATH . '/libz.a'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchCurlMacOS(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT COREFOUNDATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchMicro(): bool
|
||||
{
|
||||
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
|
||||
return false;
|
||||
}
|
||||
$ver_file = SOURCE_PATH . '/php-src/main/php_version.h';
|
||||
if (!file_exists($ver_file)) {
|
||||
throw new FileSystemException('Patch failed, cannot find php source files');
|
||||
}
|
||||
$version_h = FileSystem::readFile(SOURCE_PATH . '/php-src/main/php_version.h');
|
||||
preg_match('/#\s*define\s+PHP_MAJOR_VERSION\s+(\d+)\s+#\s*define\s+PHP_MINOR_VERSION\s+(\d+)\s+/m', $version_h, $match);
|
||||
// $ver = "{$match[1]}.{$match[2]}";
|
||||
|
||||
$major_ver = $match[1] . $match[2];
|
||||
if ($major_ver === '74') {
|
||||
return false;
|
||||
}
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
// f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD');
|
||||
|
||||
$patch_list = [
|
||||
'static_opcache',
|
||||
'static_extensions_win32',
|
||||
'cli_checks',
|
||||
'disable_huge_page',
|
||||
'vcruntime140',
|
||||
'win32',
|
||||
'zend_stream',
|
||||
];
|
||||
$patch_list = array_merge($patch_list, match (PHP_OS_FAMILY) {
|
||||
'Windows' => [
|
||||
'cli_static',
|
||||
],
|
||||
'Darwin' => [
|
||||
'macos_iconv',
|
||||
],
|
||||
default => [],
|
||||
});
|
||||
$patches = [];
|
||||
$serial = ['80', '81', '82'];
|
||||
foreach ($patch_list as $patchName) {
|
||||
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
|
||||
$patches[] = "sapi/micro/patches/{$patchName}.patch";
|
||||
continue;
|
||||
}
|
||||
for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) {
|
||||
$tryMajMin = $serial[$i];
|
||||
if (!file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}_{$tryMajMin}.patch")) {
|
||||
continue;
|
||||
}
|
||||
$patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch";
|
||||
continue 2;
|
||||
}
|
||||
throw new RuntimeException("failed finding patch {$patchName}");
|
||||
}
|
||||
|
||||
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, implode(' ', $patches));
|
||||
|
||||
f_passthru(
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function patchOpenssl3(): bool
|
||||
{
|
||||
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::getPHPVersionID() >= 80000) {
|
||||
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
|
||||
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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>'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function patchPHPAfterConfigure(BuilderBase $param)
|
||||
{
|
||||
if ($param 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', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,6 @@ 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,13 +15,12 @@ class PhpSource extends CustomSourceBase
|
||||
|
||||
/**
|
||||
* @throws DownloaderException
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function fetch()
|
||||
{
|
||||
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.1';
|
||||
Downloader::fetchSource('php-src', self::getLatestPHPInfo($major));
|
||||
Downloader::downloadSource('php-src', self::getLatestPHPInfo($major));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -12,7 +12,7 @@ class PostgreSQLSource extends CustomSourceBase
|
||||
|
||||
public function fetch()
|
||||
{
|
||||
Downloader::fetchSource('postgresql', self::getLatestInfo());
|
||||
Downloader::downloadSource('postgresql', self::getLatestInfo());
|
||||
}
|
||||
|
||||
public function getLatestInfo(): array
|
||||
|
||||
@ -4,261 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class Patcher
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchMicroThings(): void
|
||||
{
|
||||
$ver_file = SOURCE_PATH . '/php-src/main/php_version.h';
|
||||
if (!file_exists($ver_file)) {
|
||||
throw new FileSystemException('Patch failed, cannot find php source files');
|
||||
}
|
||||
$version_h = FileSystem::readFile(SOURCE_PATH . '/php-src/main/php_version.h');
|
||||
preg_match('/#\s*define\s+PHP_MAJOR_VERSION\s+(\d+)\s+#\s*define\s+PHP_MINOR_VERSION\s+(\d+)\s+/m', $version_h, $match);
|
||||
// $ver = "{$match[1]}.{$match[2]}";
|
||||
|
||||
logger()->info('Patching php');
|
||||
|
||||
$major_ver = $match[1] . $match[2];
|
||||
if ($major_ver === '74') {
|
||||
return;
|
||||
}
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
// f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD');
|
||||
|
||||
$patch_list = [
|
||||
'static_opcache',
|
||||
'static_extensions_win32',
|
||||
'cli_checks',
|
||||
'disable_huge_page',
|
||||
'vcruntime140',
|
||||
'win32',
|
||||
'zend_stream',
|
||||
];
|
||||
$patch_list = array_merge($patch_list, match (PHP_OS_FAMILY) {
|
||||
'Windows' => [
|
||||
'cli_static',
|
||||
],
|
||||
'Darwin' => [
|
||||
'macos_iconv',
|
||||
],
|
||||
default => [],
|
||||
});
|
||||
$patches = [];
|
||||
$serial = ['80', '81', '82'];
|
||||
foreach ($patch_list as $patchName) {
|
||||
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
|
||||
$patches[] = "sapi/micro/patches/{$patchName}.patch";
|
||||
continue;
|
||||
}
|
||||
for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) {
|
||||
$tryMajMin = $serial[$i];
|
||||
if (!file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}_{$tryMajMin}.patch")) {
|
||||
continue;
|
||||
}
|
||||
$patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch";
|
||||
continue 2;
|
||||
}
|
||||
throw new RuntimeException("failed finding patch {$patchName}");
|
||||
}
|
||||
|
||||
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, implode(' ', $patches));
|
||||
|
||||
f_passthru(
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchOpenssl3(): void
|
||||
{
|
||||
logger()->info('Patching PHP with openssl 3.0');
|
||||
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
|
||||
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchSwow(): void
|
||||
{
|
||||
logger()->info('Patching swow');
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
|
||||
} else {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchPHPBeforeConfigure(BuilderBase $builder): void
|
||||
{
|
||||
if ($builder->getExt('curl')) {
|
||||
logger()->info('patching before-configure for curl checks');
|
||||
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
|
||||
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
|
||||
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])';
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
|
||||
}
|
||||
|
||||
// if ($builder->getExt('pdo_sqlite')) {
|
||||
// FileSystem::replaceFile()
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchPHPConfigure(BuilderBase $builder): void
|
||||
{
|
||||
$frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : '';
|
||||
$curl = $builder->getExt('curl');
|
||||
if ($curl) {
|
||||
logger()->info('patching configure for curl checks');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lcurl/',
|
||||
$curl->getLibFilesString() . $frameworks
|
||||
);
|
||||
}
|
||||
$bzip2 = $builder->getExt('bz2');
|
||||
if ($bzip2) {
|
||||
logger()->info('patching configure for bzip2 checks');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lbz2/',
|
||||
$bzip2->getLibFilesString() . $frameworks
|
||||
);
|
||||
}
|
||||
$pdo_sqlite = $builder->getExt('pdo_sqlite');
|
||||
if ($pdo_sqlite) {
|
||||
logger()->info('patching configure for pdo_sqlite linking');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/sqlite3_column_table_name=yes/',
|
||||
'sqlite3_column_table_name=no'
|
||||
);
|
||||
}
|
||||
logger()->info('patching configure for disable capstone');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_capstone="yes"/',
|
||||
'have_capstone="no"'
|
||||
);
|
||||
if (property_exists($builder, 'arch') && php_uname('m') !== $builder->arch) {
|
||||
// cross-compiling
|
||||
switch ($builder->arch) {
|
||||
case 'aarch64':
|
||||
case 'arm64':
|
||||
// almost all bsd/linux supports this
|
||||
logger()->info('patching configure for shm mmap checks (cross-compiling)');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_shm_mmap_anon=no/',
|
||||
'have_shm_mmap_anon=yes'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_shm_mmap_posix=no/',
|
||||
'have_shm_mmap_posix=yes'
|
||||
);
|
||||
break;
|
||||
case 'x86_64':
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('unsupported arch while patching php configure: ' . $builder->arch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchUnixLibpng(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lz',
|
||||
BUILD_LIB_PATH . '/libz.a'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchCurlMacOS(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT COREFOUNDATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/curl/CMakeLists.txt',
|
||||
REPLACE_FILE_PREG,
|
||||
'/NOT SYSTEMCONFIGURATION_FRAMEWORK/m',
|
||||
'FALSE'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchDarwinOpenssl11(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/openssl/test/v3ext.c',
|
||||
REPLACE_FILE_STR,
|
||||
'#include <stdio.h>',
|
||||
'#include <stdio.h>' . PHP_EOL . '#include <string.h>'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchLinuxPkgConfig(string $path): void
|
||||
{
|
||||
logger()->info("fixing pc {$path}");
|
||||
|
||||
$workspace = BUILD_ROOT_PATH;
|
||||
if ($workspace === '/') {
|
||||
$workspace = '';
|
||||
}
|
||||
|
||||
$content = file_get_contents($path);
|
||||
$content = preg_replace('/^prefix=.+$/m', "prefix={$workspace}", $content);
|
||||
$content = preg_replace('/^libdir=.+$/m', 'libdir=${prefix}/lib', $content);
|
||||
$content = preg_replace('/^includedir=.+$/m', 'includedir=${prefix}/include', $content);
|
||||
file_put_contents($path, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
|
||||
@ -57,4 +57,9 @@ const PKGCONF_PATCH_LIBDIR = 4;
|
||||
const PKGCONF_PATCH_INCLUDEDIR = 8;
|
||||
const PKGCONF_PATCH_ALL = 15;
|
||||
|
||||
// Custom download type
|
||||
const DOWNLOAD_TYPE_NONE = 0;
|
||||
const DOWNLOAD_TYPE_ARCHIVE = 1;
|
||||
const DOWNLOAD_TYPE_DIR = 2;
|
||||
|
||||
ConsoleLogger::$date_format = 'H:i:s';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user