diff --git a/.github/workflows/build-arm.yml b/.github/workflows/build-arm.yml
new file mode 100644
index 00000000..cddd2707
--- /dev/null
+++ b/.github/workflows/build-arm.yml
@@ -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
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ea91d27f..616699f6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -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 }}
diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker
new file mode 100755
index 00000000..bab8e3a1
--- /dev/null
+++ b/bin/spc-alpine-docker
@@ -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 $@
diff --git a/composer.json b/composer.json
index 24f950ec..c7b9f9f9 100644
--- a/composer.json
+++ b/composer.json
@@ -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",
diff --git a/config/source.json b/config/source.json
index 58e6adef..481bfc85 100644
--- a/config/source.json
+++ b/config/source.json
@@ -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": {
diff --git a/ext-support.md b/ext-support.md
index 1f783ae6..bd66ac5e 100644
--- a/ext-support.md
+++ b/ext-support.md
@@ -6,58 +6,58 @@
> - no with issue link: not supported yet due to issue
> - partial with issue link: supported but not perfect due to issue
-| | Linux | macOS | Windows |
-|------------|----------------------------------------------------------------|---------------------------------------------------------------------|---------|
-| bcmath | yes | yes | |
-| bz2 | yes | yes | |
-| calendar | yes | yes | |
-| ctype | yes | yes | |
-| curl | yes | yes | |
-| dba | yes | yes | |
-| dom | yes | yes | |
-| enchant | | | |
-| event | | | |
-| exif | yes | yes | |
-| filter | yes | yes | |
-| fileinfo | yes | yes | |
-| ftp | yes | yes | |
-| gd | yes | yes | |
-| gettext | | | |
-| gmp | yes | yes | |
-| iconv | yes | yes | |
-| inotify | yes | yes | |
-| mbstring | yes | yes | |
-| mbregex | yes | yes | |
-| mcrypt | | [faulty](https://github.com/crazywhalecc/static-php-cli/issues/32) | |
-| mongodb | yes | yes | |
-| mysqli | yes | yes | |
-| mysqlnd | yes | yes | |
-| openssl | yes | yes | |
-| pcntl | yes | yes | |
-| pdo | yes | yes | |
-| pdo_mysql | yes | yes | |
-| pdo_sqlite | yes | yes | |
-| pdo_pgsql | | | |
-| phar | yes | yes | |
-| posix | yes | yes | |
-| protobuf | yes | yes | |
-| readline | | | |
-| redis | yes | yes | |
-| session | yes | yes | |
-| shmop | yes | yes | |
-| simplexml | yes | yes | |
-| 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) | |
-| tokenizer | yes | yes | |
-| xml | yes | yes | |
-| xmlreader | yes, untested | yes, untested | |
-| xmlwriter | yes, untested | yes, untested | |
-| zip | yes, untested | yes | |
-| zlib | yes | yes | |
-| zstd | yes | yes | |
+| | Linux | macOS | Windows |
+|------------|---------------------------------------------------------------------|--------------------------------------------------------------------|---------|
+| bcmath | yes | yes | |
+| bz2 | yes | yes | |
+| calendar | yes | yes | |
+| ctype | yes | yes | |
+| curl | yes | yes | |
+| dba | yes | yes | |
+| dom | yes | yes | |
+| enchant | | | |
+| event | | | |
+| exif | yes | yes | |
+| filter | yes | yes | |
+| fileinfo | yes | yes | |
+| ftp | yes | yes | |
+| gd | yes | yes | |
+| gettext | | | |
+| gmp | yes | yes | |
+| iconv | yes | yes | |
+| inotify | yes | yes | |
+| mbstring | yes | yes | |
+| mbregex | yes | yes | |
+| mcrypt | | [faulty](https://github.com/crazywhalecc/static-php-cli/issues/32) | |
+| mongodb | yes | yes | |
+| mysqli | yes | yes | |
+| mysqlnd | yes | yes | |
+| openssl | yes | yes | |
+| pcntl | yes | yes | |
+| pdo | yes | yes | |
+| pdo_mysql | yes | yes | |
+| pdo_sqlite | yes | yes | |
+| pdo_pgsql | | | |
+| phar | yes | yes | |
+| posix | yes | yes | |
+| protobuf | yes | yes | |
+| readline | | | |
+| redis | yes | yes | |
+| session | yes | yes | |
+| shmop | yes | yes | |
+| simplexml | yes | yes | |
+| soap | yes | yes | |
+| sockets | yes | yes | |
+| sqlite3 | yes | yes | |
+| 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 | |
+| xmlwriter | yes, untested | yes, untested | |
+| zip | yes, untested | yes | |
+| zlib | yes | yes | |
+| zstd | yes | yes | |
## Additional Requirements
diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php
index f67337e9..09240cb0 100644
--- a/src/SPC/builder/BuilderBase.php
+++ b/src/SPC/builder/BuilderBase.php
@@ -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']);
+ }
+ }
+ }
}
diff --git a/src/SPC/builder/extension/swoole.php b/src/SPC/builder/extension/swoole.php
index f848d77d..492ff12c 100644
--- a/src/SPC/builder/extension/swoole.php
+++ b/src/SPC/builder/extension/swoole.php
@@ -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;
diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php
index 12e458c7..71b66bce 100644
--- a/src/SPC/builder/linux/LinuxBuilder.php
+++ b/src/SPC/builder/linux/LinuxBuilder.php
@@ -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);
// 清理
diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php
index 686e930e..b0a43789 100644
--- a/src/SPC/builder/linux/SystemUtil.php
+++ b/src/SPC/builder/linux/SystemUtil.php
@@ -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
{
diff --git a/src/SPC/builder/linux/library/libpng.php b/src/SPC/builder/linux/library/libpng.php
index 8cc5d248..5c28d1e2 100644
--- a/src/SPC/builder/linux/library/libpng.php
+++ b/src/SPC/builder/linux/library/libpng.php
@@ -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')
diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php
index 05eee63f..2d01cbd6 100644
--- a/src/SPC/builder/macos/MacOSBuilder.php
+++ b/src/SPC/builder/macos/MacOSBuilder.php
@@ -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) {
diff --git a/src/SPC/builder/macos/library/curl.php b/src/SPC/builder/macos/library/curl.php
index 9c63fdc5..5432aa87 100644
--- a/src/SPC/builder/macos/library/curl.php
+++ b/src/SPC/builder/macos/library/curl.php
@@ -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();
}
}
diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php
index 8c32edb2..11ea6215 100644
--- a/src/SPC/command/BaseCommand.php
+++ b/src/SPC/command/BaseCommand.php
@@ -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;
diff --git a/src/SPC/command/BuildCliCommand.php b/src/SPC/command/BuildCliCommand.php
index 77219d15..0c0cfa49 100644
--- a/src/SPC/command/BuildCliCommand.php
+++ b/src/SPC/command/BuildCliCommand.php
@@ -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());
diff --git a/src/SPC/command/DoctorCommand.php b/src/SPC/command/DoctorCommand.php
index f8a5ee64..bf914f32 100644
--- a/src/SPC/command/DoctorCommand.php
+++ b/src/SPC/command/DoctorCommand.php
@@ -18,6 +18,7 @@ class DoctorCommand extends BaseCommand
$this->output->writeln('Doctor check complete !');
} catch (\Throwable $e) {
$this->output->writeln('' . $e->getMessage() . '');
+ pcntl_signal(SIGINT, SIG_IGN);
return 1;
}
return 0;
diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php
new file mode 100644
index 00000000..640030f9
--- /dev/null
+++ b/src/SPC/command/DownloadCommand.php
@@ -0,0 +1,114 @@
+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="(?openssl-(?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;
+ }
+}
diff --git a/src/SPC/command/FetchSourceCommand.php b/src/SPC/command/FetchSourceCommand.php
deleted file mode 100644
index a0f4adad..00000000
--- a/src/SPC/command/FetchSourceCommand.php
+++ /dev/null
@@ -1,234 +0,0 @@
-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="(?openssl-(?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', '');
- }
-}
diff --git a/src/SPC/command/SortConfigCommand.php b/src/SPC/command/SortConfigCommand.php
index 8ca09801..3de55770 100644
--- a/src/SPC/command/SortConfigCommand.php
+++ b/src/SPC/command/SortConfigCommand.php
@@ -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':
diff --git a/src/SPC/doctor/CheckListHandler.php b/src/SPC/doctor/CheckListHandler.php
index 5747dd29..b2f53e45 100644
--- a/src/SPC/doctor/CheckListHandler.php
+++ b/src/SPC/doctor/CheckListHandler.php
@@ -116,8 +116,12 @@ class CheckListHandler
private function emitFix(CheckResult $result)
{
+ pcntl_signal(SIGINT, function () {
+ $this->output->writeln('You cancelled fix');
+ });
$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('Fix done');
} else {
diff --git a/src/SPC/doctor/CheckResult.php b/src/SPC/doctor/CheckResult.php
index d7628ee8..fef036e4 100644
--- a/src/SPC/doctor/CheckResult.php
+++ b/src/SPC/doctor/CheckResult.php
@@ -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;
+ }
}
diff --git a/src/SPC/doctor/item/LinuxMuslCheck.php b/src/SPC/doctor/item/LinuxMuslCheck.php
new file mode 100644
index 00000000..c9cb956b
--- /dev/null
+++ b/src/SPC/doctor/item/LinuxMuslCheck.php
@@ -0,0 +1,52 @@
+ 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;
+ }
+ }
+}
diff --git a/src/SPC/store/Config.php b/src/SPC/store/Config.php
index 9aa21d56..2f6e992b 100644
--- a/src/SPC/store/Config.php
+++ b/src/SPC/store/Config.php
@@ -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;
+ }
}
diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php
index e4b02bc0..39a22494 100644
--- a/src/SPC/store/Downloader.php
+++ b/src/SPC/store/Downloader.php
@@ -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 {
- logger()->debug("cloning {$name} source");
- $check = !defined('DEBUG_MODE') ? ' -q' : '';
- f_passthru(
- 'git clone' . $check .
- ' --config core.autocrlf=false ' .
- "--branch \"{$branch}\" " . (defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '') . " --recursive \"{$url}\" \"{$download_path}\""
- );
+ 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");
- return;
+
+ // 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;
diff --git a/src/SPC/store/FileSystem.php b/src/SPC/store/FileSystem.php
index 786e4e4f..bc0d024f 100644
--- a/src/SPC/store/FileSystem.php
+++ b/src/SPC/store/FileSystem.php
@@ -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');
+ }
+ }
+ }
}
diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php
new file mode 100644
index 00000000..e47ecd77
--- /dev/null
+++ b/src/SPC/store/SourcePatcher.php
@@ -0,0 +1,212 @@
+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 ',
+ '#include ' . PHP_EOL . '#include '
+ );
+ 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', '');
+ }
+ }
+}
diff --git a/src/SPC/store/source/PhpSource.php b/src/SPC/store/source/PhpSource.php
index f05d8742..44998e4e 100644
--- a/src/SPC/store/source/PhpSource.php
+++ b/src/SPC/store/source/PhpSource.php
@@ -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));
}
/**
diff --git a/src/SPC/store/source/PostgreSQLSource.php b/src/SPC/store/source/PostgreSQLSource.php
index 2e9173c6..cd8a095d 100644
--- a/src/SPC/store/source/PostgreSQLSource.php
+++ b/src/SPC/store/source/PostgreSQLSource.php
@@ -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
diff --git a/src/SPC/util/Patcher.php b/src/SPC/util/Patcher.php
index 3d7a7625..3f7f7660 100644
--- a/src/SPC/util/Patcher.php
+++ b/src/SPC/util/Patcher.php
@@ -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 ',
- '#include ' . PHP_EOL . '#include '
- );
- }
-
- 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
diff --git a/src/globals/defines.php b/src/globals/defines.php
index 7297a04f..fb66869e 100644
--- a/src/globals/defines.php
+++ b/src/globals/defines.php
@@ -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';