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';