diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a7166ec2..13441a81 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,7 +7,11 @@ > If your PR involves the changes mentioned below and completed the action, please tick the corresponding option. > If a modification is not involved, please skip it directly. -- [ ] If you modified `*.php`, run `composer cs-fix` at local machine. -- [ ] If it's an extension or dependency update, make sure adding related extensions in `src/global/test-extensions.php`. -- [ ] If you changed the behavior of static-php-cli, update docs in `./docs/`. -- [ ] If you updated `config/xxx.json` content, run `bin/spc dev:sort-config xxx`. +- If you modified `*.php` or `*.json`, run them locally to ensure your changes are valid: + - [ ] `PHP_CS_FIXER_IGNORE_ENV=1 composer cs-fix` + - [ ] `composer analyse` + - [ ] `composer test` + - [ ] `bin/spc dev:sort-config` +- If it's an extension or dependency update, please ensure the following: + - [ ] Add your test combination to `src/globals/test-extensions.php`. + - [ ] If adding new or fixing bugs, add commit message containing `extension test` or `test extensions` to trigger full test suite. diff --git a/.github/workflows/ext-matrix-tests.yml b/.github/workflows/ext-matrix-tests.yml index da30d008..1c04f5c9 100644 --- a/.github/workflows/ext-matrix-tests.yml +++ b/.github/workflows/ext-matrix-tests.yml @@ -1,16 +1,14 @@ -name: "Extension matrix tests" +name: "Extension Matrix Tests" on: workflow_dispatch: - pull_request: - branches: [ "main" ] - paths: - - '.github/workflows/ext-matrix-tests.yml' + push: jobs: test: name: "${{ matrix.extension }} (PHP ${{ matrix.php-version }} on ${{ matrix.operating-system }})" runs-on: ${{ matrix.operating-system }} + if: contains(github.event.head_commit.message, 'extension test') || contains(github.event.head_commit.message, 'test extensions') strategy: fail-fast: false matrix: diff --git a/.gitignore b/.gitignore index 7f5ea9b7..a95eeaac 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ docker/source/ # default source extract directory /source/** +# built by shared embed tests +/locale/ + # default source download directory /downloads/** diff --git a/README-zh.md b/README-zh.md index 0cbc4780..03fa7160 100755 --- a/README-zh.md +++ b/README-zh.md @@ -278,7 +278,7 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys 如果你知道 [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed),你应该知道如何使用它。对于有可能编译用到引入其他库的问题,你可以使用 `buildroot/bin/php-config` 来获取编译时的配置。 -另外,有关如何使用此功能的高级示例,请查看[如何使用它构建 FrankenPHP 的静态版本](https://github.com/dunglas/frankenphp/blob/main/docs/static.md)。 +另外,有关如何使用此功能的高级示例,请查看[如何使用它构建 FrankenPHP 的静态版本](https://github.com/php/frankenphp/blob/main/docs/static.md)。 ## 贡献 diff --git a/README.md b/README.md index 301d76fd..94c10d58 100755 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ If you know [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed), You may require the introduction of other libraries during compilation, you can use `buildroot/bin/php-config` to obtain the compile-time configuration. -For an advanced example of how to use this feature, take a look at [how to use it to build a static version of FrankenPHP](https://github.com/dunglas/frankenphp/blob/main/docs/static.md). +For an advanced example of how to use this feature, take a look at [how to use it to build a static version of FrankenPHP](https://github.com/php/frankenphp/blob/main/docs/static.md). ## Contribution diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 0733b232..58c24ce7 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -1,12 +1,14 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash + +set -e # This file is using docker to run commands -SPC_DOCKER_VERSION=v3 +SPC_DOCKER_VERSION=v4 # Detect docker can run if ! which docker >/dev/null; then - echo "Docker is not installed, please install docker first !" - exit 1 + echo "Docker is not installed, please install docker first !" + exit 1 fi DOCKER_EXECUTABLE="docker" # shellcheck disable=SC2046 @@ -22,27 +24,48 @@ if [ $(id -u) -ne 0 ]; then fi fi - - -# to check if qemu-docker run -if [ "$SPC_USE_ARCH" = "" ]; then - SPC_USE_ARCH=x86_64 +# Convert uname to gnu arch +CURRENT_ARCH=$(uname -m) +if [ "$CURRENT_ARCH" = "arm64" ]; then + CURRENT_ARCH=aarch64 fi +if [ -z "$SPC_USE_ARCH" ]; then + SPC_USE_ARCH=$CURRENT_ARCH +fi +# parse SPC_USE_ARCH case $SPC_USE_ARCH in -x86_64) - ALPINE_FROM=alpine:edge +x86_64|amd64) + SPC_USE_ARCH=x86_64 + if [ "$CURRENT_ARCH" != "x86_64" ]; then + PLATFORM_ARG="--platform linux/amd64" + ALPINE_FROM=multiarch/alpine:x86_64-edge + fi ;; -aarch64) - ALPINE_FROM=multiarch/alpine:aarch64-edge - # shellcheck disable=SC2039 - echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m" - $DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null +aarch64|arm64) + SPC_USE_ARCH=aarch64 + if [ "$CURRENT_ARCH" != "aarch64" ]; then + PLATFORM_ARG="--platform linux/arm64" + ALPINE_FROM=multiarch/alpine:aarch64-edge + fi ;; *) echo "Current arch is not supported to run in docker: $SPC_USE_ARCH" exit 1 ;; esac +# if ALPINE_FROM is not set, use alpine:edge +if [ -z "$ALPINE_FROM" ]; then + ALPINE_FROM=alpine:edge +fi +if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then + echo "* Using different arch needs to setup qemu-static for docker !" + ALPINE_FROM=multiarch/alpine:$SPC_USE_ARCH-edge + if [ "$(uname -s)" = "Linux" ]; then + $DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null + fi +else + ALPINE_FROM=alpine:edge +fi 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" @@ -53,7 +76,7 @@ fi # Detect docker env is setup if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then echo "Docker container does not exist. Building docker image ..." - $DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . </dev/null; then echo "Docker is not installed, please install docker first !" @@ -19,35 +24,47 @@ if [ $(id -u) -ne 0 ]; then fi fi - - -# to check if qemu-docker run -if [ "$SPC_USE_ARCH" = "" ]; then - SPC_USE_ARCH=current +# Convert uname to gnu arch +CURRENT_ARCH=$(uname -m) +if [ "$CURRENT_ARCH" = "arm64" ]; then + CURRENT_ARCH=aarch64 fi +if [ -z "$SPC_USE_ARCH" ]; then + SPC_USE_ARCH=$CURRENT_ARCH +fi +# parse SPC_USE_ARCH case $SPC_USE_ARCH in -current) - BASE_ARCH=$(uname -m) - if [ "$BASE_ARCH" = "arm64" ]; then - BASE_ARCH=aarch64 +x86_64|amd64) + SPC_USE_ARCH=x86_64 + SPC_USE_ARCH_DOCKER=amd64 + if [ "$CURRENT_ARCH" != "x86_64" ]; then + PLATFORM_ARG="--platform linux/amd64" fi ;; -aarch64) - BASE_ARCH=aarch64 - # shellcheck disable=SC2039 - echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m" - $DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null +aarch64|arm64) + SPC_USE_ARCH=aarch64 + SPC_USE_ARCH_DOCKER=arm64 + if [ "$CURRENT_ARCH" != "aarch64" ]; then + PLATFORM_ARG="--platform linux/arm64" + fi ;; *) echo "Current arch is not supported to run in docker: $SPC_USE_ARCH" exit 1 ;; esac +# detect if we need to use qemu-static +if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then + if [ "$(uname -s)" = "Linux" ]; then + echo "* Using different arch needs to setup qemu-static for docker !" + $DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static --reset -p yes > /dev/null + fi +fi # Detect docker env is setup -if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH; then +if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then echo "Docker container does not exist. Building docker image ..." - $DOCKER_EXECUTABLE build -t cwcc-spc-gnu-$SPC_USE_ARCH -f- . <> /etc/bashrc RUN source /etc/bashrc +RUN yum install -y which -RUN curl -o cmake.tgz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$BASE_ARCH.tar.gz && \ +RUN curl -fsSL -o patchelf.tgz https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-$SPC_USE_ARCH.tar.gz && \ + mkdir -p /patchelf && \ + tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \ + cp /patchelf/bin/patchelf /usr/bin/ + +RUN curl -o cmake.tgz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \ mkdir /cmake && \ tar -xzf cmake.tgz -C /cmake --strip-components 1 @@ -88,7 +111,7 @@ ENV PATH="/app/bin:/cmake/bin:$PATH" ENV SPC_LIBC=glibc ADD ./config/env.ini /app/config/env.ini -RUN bin/spc doctor --auto-fix --debug +RUN CC=gcc bin/spc doctor --auto-fix --debug RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \ tar -zxvf make.tgz && \ @@ -135,7 +158,7 @@ echo 'CC=/opt/rh/devtoolset-10/root/usr/bin/gcc' > /tmp/spc-gnu-docker.env echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env -echo 'SPC_DEFAULT_C_FLAGS=-fPIE -fPIC' >> /tmp/spc-gnu-docker.env +echo 'SPC_DEFAULT_C_FLAGS=-fPIC' >> /tmp/spc-gnu-docker.env echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env @@ -167,7 +190,7 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then echo "* ./pkgroot: $(pwd)/pkgroot" echo "*" set -ex - $DOCKER_EXECUTABLE run --rm -it --privileged $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH + $DOCKER_EXECUTABLE run $PLATFORM_ARG --privileged --rm -it $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash else - $DOCKER_EXECUTABLE run --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@ + $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@ fi diff --git a/config/env.ini b/config/env.ini index f70ad0a2..d80bc3e7 100644 --- a/config/env.ini +++ b/config/env.ini @@ -42,6 +42,9 @@ SPC_CONCURRENCY=${CPU_COUNT} SPC_SKIP_PHP_VERSION_CHECK="no" ; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed") SPC_SKIP_DOCTOR_CHECK_ITEMS="" +; extra modules that xcaddy will include in the FrankenPHP build +SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/caddy --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli" + ; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them ; only useful for builds targeting not pure-static linking ; default paths @@ -66,10 +69,10 @@ SPC_LIBC=musl CC=${SPC_LINUX_DEFAULT_CC} CXX=${SPC_LINUX_DEFAULT_CXX} AR=${SPC_LINUX_DEFAULT_AR} -LD=ld.gold +LD=${SPC_LINUX_DEFAULT_LD} ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build -SPC_DEFAULT_C_FLAGS="-fPIC" -SPC_DEFAULT_CXX_FLAGS= +SPC_DEFAULT_C_FLAGS="-fPIC -Os" +SPC_DEFAULT_CXX_FLAGS="-fPIC -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_EXTRA_LIBS= ; upx executable path @@ -81,7 +84,7 @@ SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime ; buildconf command SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force" ; configure command -SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg --with-pic" +SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-cgi --disable-phpdbg --with-pic" ; make command SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}" ; embed type for php, static (libphp.a) or shared (libphp.so) @@ -97,9 +100,11 @@ SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}" ; LIBS for configuring php SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm" ; EXTRA_CFLAGS for `make` php -SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -fno-ident -fPIE -fPIC" +SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}" ; EXTRA_LIBS for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm" +; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so +SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" ; EXTRA_LDFLAGS_PROGRAM for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" @@ -108,8 +113,8 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" CC=clang CXX=clang++ ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build -SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin" -SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin" +SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" +SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_EXTRA_LIBS= ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches @@ -131,7 +136,7 @@ SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}" ; LDFLAGS for configuring php SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}" ; EXTRA_CFLAGS for `make` php -SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" +SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie ${SPC_DEFAULT_C_FLAGS}" ; EXTRA_LIBS for `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv" ; embed type for php, static (libphp.a) or shared (libphp.dylib) diff --git a/config/ext.json b/config/ext.json index 539658d0..3a3cacdf 100644 --- a/config/ext.json +++ b/config/ext.json @@ -24,6 +24,14 @@ "bcmath": { "type": "builtin" }, + "brotli": { + "type": "external", + "source": "ext-brotli", + "arg-type": "enable", + "lib-depends": [ + "brotli" + ] + }, "bz2": { "type": "builtin", "arg-type-unix": "with-prefix", @@ -95,7 +103,10 @@ "ev": { "type": "external", "source": "ev", - "arg-type-windows": "with" + "arg-type-windows": "with", + "ext-depends": [ + "sockets" + ] }, "event": { "support": { @@ -124,10 +135,6 @@ "Linux": "partial", "BSD": "wip" }, - "target": [ - "static", - "shared" - ], "notes": true, "arg-type": "custom", "type": "builtin", @@ -334,6 +341,9 @@ "ext-depends": [ "xml" ] + "target": [ + "static" + ] }, "lz4": { "support": { @@ -350,6 +360,9 @@ "mbregex": { "type": "builtin", "arg-type": "custom", + "target": [ + "static" + ], "ext-depends": [ "mbstring" ], @@ -382,13 +395,13 @@ "ext-depends": [ "zlib", "session" - ] + ], + "build-with-php": true }, "memcached": { "support": { "Windows": "wip", - "BSD": "wip", - "Linux": "no" + "BSD": "wip" }, "type": "external", "source": "memcached", @@ -397,6 +410,10 @@ "lib-depends": [ "libmemcached" ], + "lib-depends-unix": [ + "libmemcached", + "fastlz" + ], "ext-depends": [ "session", "zlib" @@ -415,6 +432,10 @@ "openssl", "zstd", "zlib" + ], + "frameworks": [ + "CoreFoundation", + "Security" ] }, "msgpack": { @@ -424,7 +445,10 @@ "type": "external", "source": "msgpack", "arg-type-unix": "with", - "arg-type-win": "enable" + "arg-type-win": "enable", + "ext-depends": [ + "session" + ] }, "mysqli": { "type": "builtin", @@ -463,7 +487,8 @@ }, "opcache": { "type": "builtin", - "arg-type-unix": "custom" + "arg-type-unix": "custom", + "zend-extension": true }, "openssl": { "notes": true, @@ -652,6 +677,9 @@ "arg-type": "with-prefix", "lib-depends": [ "readline" + ], + "target": [ + "static" ] }, "redis": { @@ -671,10 +699,12 @@ ] }, "session": { - "type": "builtin" + "type": "builtin", + "build-with-php": true }, "shmop": { - "type": "builtin" + "type": "builtin", + "build-with-php": true }, "simdjson": { "type": "external", @@ -692,7 +722,8 @@ ], "ext-depends-windows": [ "xml" - ] + ], + "build-with-php": true }, "snappy": { "support": { @@ -785,7 +816,7 @@ "lib-depends": [ "libssh2" ], - "ext-depends-windows": [ + "ext-depends": [ "openssl", "zlib" ] @@ -795,9 +826,6 @@ "Windows": "no", "BSD": "wip" }, - "target": [ - "static" - ], "notes": true, "type": "external", "source": "swoole", @@ -915,7 +943,8 @@ ] }, "tokenizer": { - "type": "builtin" + "type": "builtin", + "build-with-php": true }, "uuid": { "support": { @@ -956,7 +985,8 @@ "Darwin": "partial", "Linux": "partial" }, - "notes": true + "notes": true, + "zend-extension": true }, "xhprof": { "support": { @@ -968,7 +998,8 @@ "source": "xhprof", "ext-depends": [ "ctype" - ] + ], + "build-with-php": true }, "xlswriter": { "support": { @@ -998,7 +1029,8 @@ ], "ext-depends-windows": [ "iconv" - ] + ], + "build-with-php": true }, "xmlreader": { "support": { @@ -1012,7 +1044,8 @@ "ext-depends-windows": [ "xml", "dom" - ] + ], + "build-with-php": true }, "xmlwriter": { "support": { @@ -1025,7 +1058,8 @@ ], "ext-depends-windows": [ "xml" - ] + ], + "build-with-php": true }, "xsl": { "support": { @@ -1042,6 +1076,14 @@ "dom" ] }, + "xz": { + "type": "external", + "source": "ext-xz", + "arg-type": "with", + "lib-depends": [ + "xz" + ] + }, "yac": { "support": { "BSD": "wip" @@ -1049,6 +1091,9 @@ "type": "external", "source": "yac", "arg-type-unix": "custom", + "lib-depends-unix": [ + "fastlz" + ], "ext-depends-unix": [ "igbinary" ] @@ -1069,9 +1114,6 @@ "support": { "BSD": "wip" }, - "target": [ - "static" - ], "type": "builtin", "arg-type": "with-prefix", "arg-type-windows": "enable", @@ -1095,6 +1137,9 @@ "arg-type-windows": "enable", "lib-depends": [ "zlib" + ], + "target": [ + "static" ] }, "zstd": { diff --git a/config/lib.json b/config/lib.json index 3048610a..1a195f66 100644 --- a/config/lib.json +++ b/config/lib.json @@ -101,6 +101,15 @@ "SystemConfiguration" ] }, + "fastlz": { + "source": "fastlz", + "static-libs-unix": [ + "libfastlz.a" + ], + "headers": [ + "fastlz/fastlz.h" + ] + }, "freetype": { "source": "freetype", "static-libs-unix": [ @@ -246,8 +255,8 @@ "ldap": { "source": "ldap", "static-libs-unix": [ - "liblber.a", - "libldap.a" + "libldap.a", + "liblber.a" ], "lib-depends": [ "openssl", @@ -401,7 +410,9 @@ "source": "libmemcached", "static-libs-unix": [ "libmemcached.a", - "libmemcachedutil.a" + "libmemcachedprotocol.a", + "libmemcachedutil.a", + "libhashkit.a" ] }, "libpng": { @@ -855,5 +866,14 @@ "zstd.h", "zstd_errors.h" ] + }, + "watcher": { + "source": "watcher", + "static-libs-unix": [ + "libwatcher-c.a" + ], + "headers": [ + "wtr/watcher-c.h" + ] } } diff --git a/config/pkg.json b/config/pkg.json index 5760c0b1..e0762cac 100644 --- a/config/pkg.json +++ b/config/pkg.json @@ -42,5 +42,17 @@ "extract-files": { "upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe" } + }, + "go-xcaddy-x86_64-linux": { + "type": "custom" + }, + "go-xcaddy-aarch64-linux": { + "type": "custom" + }, + "go-xcaddy-x86_64-macos": { + "type": "custom" + }, + "go-xcaddy-aarch64-macos": { + "type": "custom" } } diff --git a/config/source.json b/config/source.json index bc245fae..b3c9cd62 100644 --- a/config/source.json +++ b/config/source.json @@ -50,8 +50,9 @@ } }, "brotli": { - "type": "ghtar", + "type": "ghtagtar", "repo": "google/brotli", + "match": "v1\\.\\d.*", "provide-pre-built": true, "license": { "type": "file", @@ -102,6 +103,16 @@ "path": "LICENSE" } }, + "ext-brotli": { + "type": "git", + "path": "php-src/ext/brotli", + "rev": "master", + "url": "https://github.com/kjdev/php-ext-brotli", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-ds": { "type": "url", "url": "https://pecl.php.net/get/ds", @@ -241,6 +252,16 @@ "path": "LICENSE" } }, + "ext-xz": { + "type": "git", + "path": "php-src/ext/xz", + "rev": "main", + "url": "https://github.com/codemasher/php-ext-xz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-zstd": { "type": "git", "path": "php-src/ext/zstd", @@ -251,6 +272,15 @@ "path": "LICENSE" } }, + "fastlz": { + "type": "git", + "url": "https://github.com/ariya/FastLZ.git", + "rev": "master", + "license": { + "type": "file", + "path": "LICENSE.MIT" + } + }, "freetype": { "type": "git", "rev": "VER-2-13-2", @@ -515,12 +545,12 @@ } }, "libmemcached": { - "type": "git", - "url": "https://github.com/static-php/libmemcached-macos.git", - "rev": "master", + "type": "ghtagtar", + "repo": "awesomized/libmemcached", + "match": "1.\\d.\\d", "license": { "type": "file", - "path": "COPYING" + "path": "LICENSE" } }, "libpng": { @@ -809,8 +839,9 @@ } }, "postgresql": { - "type": "url", - "url": "https://ftp.postgresql.org/pub/source/v16.2/postgresql-16.2.tar.bz2", + "type": "ghtagtar", + "repo": "postgres/postgres", + "match": "REL_16_\\d+", "license": { "type": "file", "path": "COPYRIGHT" @@ -1047,5 +1078,14 @@ "type": "file", "path": "LICENSE" } + }, + "watcher": { + "type": "ghtar", + "repo": "e-dant/watcher", + "prefer-stable": true, + "license": { + "type": "file", + "path": "license" + } } } diff --git a/docs/.vitepress/components/CliGenerator.vue b/docs/.vitepress/components/CliGenerator.vue index 0d830b73..ad17783a 100644 --- a/docs/.vitepress/components/CliGenerator.vue +++ b/docs/.vitepress/components/CliGenerator.vue @@ -30,6 +30,7 @@
{{ I18N[lang].selectCommon }}
+
{{ I18N[lang].selectAll }}
{{ I18N[lang].selectNone }}
@@ -44,6 +45,7 @@

TIP

{{ I18N[lang].depTips }}

+

{{ I18N[lang].depTips2 }}

{{ I18N[lang].buildTarget }}

@@ -252,6 +254,7 @@ const I18N = { no: '否', resultShow: '结果展示', selectCommon: '选择常用扩展', + selectAll: '选择全部', selectNone: '全部取消选择', useZTS: '是否编译线程安全版', hardcodedINI: '硬编码 INI 选项', @@ -267,6 +270,7 @@ const I18N = { selectedSystem: '选择操作系统', buildLibs: '要构建的库', depTips: '选择扩展后,不可选中的项目为必需的依赖,编译的依赖库列表中可选的为现有扩展和依赖库的可选依赖。选择可选依赖后,将生成 --with-libs 参数。', + depTips2: '无法同时构建所有扩展,因为有些扩展之间相互冲突。请根据需要选择扩展。', microUnavailable: 'micro 不支持 PHP 7.4 及更早版本!', windowsSAPIUnavailable: 'Windows 目前不支持 fpm、embed 构建!', useUPX: '是否开启 UPX 压缩(减小二进制体积)', @@ -286,6 +290,7 @@ const I18N = { no: 'No', resultShow: 'Result', selectCommon: 'Select common extensions', + selectAll: 'Select all', selectNone: 'Unselect all', useZTS: 'Enable ZTS', hardcodedINI: 'Hardcoded INI options', @@ -301,6 +306,7 @@ const I18N = { selectedSystem: 'Select Build OS', buildLibs: 'Select Dependencies', depTips: 'After selecting the extensions, the unselectable items are essential dependencies. In the compiled dependencies list, optional dependencies consist of existing extensions and optional dependencies of libraries. Optional dependencies will be added in --with-libs parameter.', + depTips2: 'It is not possible to build all extensions at the same time, as some extensions conflict with each other. Please select the extensions you need.', microUnavailable: 'Micro does not support PHP 7.4 and earlier versions!', windowsSAPIUnavailable: 'Windows does not support fpm and embed build!', useUPX: 'Enable UPX compression (reduce binary size)', @@ -337,6 +343,10 @@ const selectCommon = () => { ]; }; +const selectAll = () => { + checkedExts.value = extFilter.value; +}; + const extList = computed(() => { return checkedExts.value.join(','); }); @@ -364,7 +374,7 @@ const checkedTargets = ref(['cli']); const selectedEnv = ref('spc'); // chosen php version -const selectedPhpVersion = ref('8.2'); +const selectedPhpVersion = ref('8.4'); // chosen debug const debug = ref(0); diff --git a/docs/deps-craft-yml.md b/docs/deps-craft-yml.md index 9e660be8..0ee0e11d 100644 --- a/docs/deps-craft-yml.md +++ b/docs/deps-craft-yml.md @@ -42,6 +42,9 @@ build-options: # Set micro SAPI as win32 mode, without this, micro SAPI will be compiled as a console application (only for Windows, default: false) enable-micro-win32: false +# Build options for shared extensions (list or comma-separated are both accepted) +shared-extensions: [ ] + # Download options download-options: # Use custom url for specified sources, format: "{source-name}:{url}" (e.g. "php-src:https://example.com/php-8.4.0.tar.gz") diff --git a/docs/en/faq/index.md b/docs/en/faq/index.md index 1e8e0a47..a942244c 100644 --- a/docs/en/faq/index.md +++ b/docs/en/faq/index.md @@ -38,17 +38,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" - ``` For macOS platform, almost all binaries under macOS cannot be truly purely statically linked, and almost all binaries will link macOS system libraries: `/usr/lib/libresolv.9.dylib` and `/usr/lib/libSystem.B.dylib`. -So on macOS, you can use statically compiled PHP binaries under certain compilation conditions, and dynamically linked extensions: +So on macOS, you can **directly** use SPC to build statically compiled PHP binaries with dynamically linked extensions: -1. Using the `--no-strip` parameter will not strip information such as debugging symbols from the binary file for use with external Zend extensions such as `Xdebug`. -2. If you want to compile some Zend extensions, use Homebrew, MacPorts, source code compilation, and install a normal version of PHP on your operating system. -3. Use the `phpize && ./configure && make` command to compile the extensions you want to use. -4. Copy the extension file `xxxx.so` to the outside, use the statically compiled PHP binary, for example to use the Xdebug extension: `cd buildroot/bin/ && ./php -d "zend_extension=/path/to/xdebug.so"`. - -```bash -# build statically linked php-cli but not stripped -bin/spc build ffi --build-cli --no-strip -``` +1. Build shared extension `xxx.so` using: `--build-shared=XXX` option. e.g. `bin/spc build bcmath,zlib --build-shared=xdebug --build-cli` +2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`. +3. The `xdebug.so` file could be used for php that version and thread-safe are the same. ## Can it support Oracle database extension? diff --git a/docs/en/guide/build-with-glibc.md b/docs/en/guide/build-with-glibc.md index 0d6c1736..9024f6b0 100644 --- a/docs/en/guide/build-with-glibc.md +++ b/docs/en/guide/build-with-glibc.md @@ -61,6 +61,4 @@ If you have related issues or requirements, please indicate that you are buildin If you need to build glibc-based binaries without using Docker, please refer to the `bin/spc-gnu-docker` script to manually create a similar environment. -Since glibc binaries are not the main goal of the project, -we generally do not test the compatibility of various libraries and extensions under glibc. -If any specific library builds successfully on musl-libc but fails on glibc, please submit an issue. +Please keep in mind that we only support glibc build with `bin/spc-gnu-docker`. Compilation on RHEL 9 & 10 has been tested and is stable, but if you run into issues, we may choose not to fix them. diff --git a/docs/en/guide/extension-notes.md b/docs/en/guide/extension-notes.md index dc0f1d7a..aa11c377 100644 --- a/docs/en/guide/extension-notes.md +++ b/docs/en/guide/extension-notes.md @@ -151,8 +151,7 @@ Parallel is only supported on PHP 8.0 ZTS and above. ## spx -1. The [SPX extension](https://github.com/NoiseByNorthwest/php-spx) only supports NTS mode. -2. SPX does not support Windows, and the official repository does not support static compilation. static-php-cli uses a [modified version](https://github.com/static-php/php-spx). +1. SPX does not support Windows, and the official repository does not support static compilation. static-php-cli uses a [modified version](https://github.com/static-php/php-spx). ## mimalloc diff --git a/docs/en/guide/manual-build.md b/docs/en/guide/manual-build.md index 3c40d5eb..e8e4510e 100644 --- a/docs/en/guide/manual-build.md +++ b/docs/en/guide/manual-build.md @@ -167,6 +167,7 @@ If the build is successful, you will see the `buildroot/bin` directory in the cu - fpm: The build result is `buildroot/bin/php-fpm`. - micro: The build result is `buildroot/bin/micro.sfx`. If you need to further package it with PHP code, please refer to [Packaging micro binary](./manual-build#command-micro-combine). - embed: See [Using embed](./manual-build#embed-usage). +- frankenphp: The build result is `buildroot/bin/frankenphp`. If the build fails, you can use the `--debug` parameter to view detailed error information, or use the `--with-clean` to clear the old compilation results and recompile. @@ -290,6 +291,7 @@ You need to specify a compilation target, choose from the following parameters: - `--build-fpm`: Build a fpm sapi (php-fpm, used in conjunction with other traditional fpm architecture software such as nginx) - `--build-micro`: Build a micro sapi (used to build a standalone executable binary containing PHP code) - `--build-embed`: Build an embed sapi (used to embed into other C language programs) +- `--build-frankenphp`: Build a [FrankenPHP](https://github.com/php/frankenphp) executable - `--build-all`: build all above sapi ```bash @@ -337,7 +339,7 @@ You can try to use the following commands: - `--cxx=XXX`: Specifies the execution command of the C++ language compiler (Linux defaults to `g++`, macOS defaults to `clang++`) - `--with-clean`: clean up old make files before compiling PHP - `--enable-zts`: Make compiled PHP thread-safe version (default is NTS version) -- `--no-strip`: Do not run `strip` after compiling the PHP library to trim the binary file to reduce its size (the macOS binary file without trim can use dynamically linked third-party extensions) +- `--no-strip`: Do not run `strip` after compiling the PHP library to trim the binary file to reduce its size - `--with-libs=XXX,YYY`: Compile the specified dependent library before compiling PHP, and activate some extended optional functions (such as libavif of the gd library, etc.) - `--with-config-file-path=XXX`: Set the path in which to look for `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths) - `--with-config-file-scan-dir=XXX`: Set the directory to scan for `.ini` files after reading `php.ini` (Check [here](../faq/index.html#what-is-the-path-of-php-ini) for default paths) @@ -509,6 +511,8 @@ When `bin/spc doctor` automatically repairs the Windows environment, tools such Here is an example of installing the tool: - Download and install UPX (Linux and Windows only): `bin/spc install-pkg upx` +- Download and install nasm (Windows only): `bin/spc install-pkg nasm` +- Download and install go-xcaddy: `bin/spc install-pkg go-xcaddy` ## Command - del-download diff --git a/docs/zh/faq/index.md b/docs/zh/faq/index.md index 4367b35b..4dfbade4 100644 --- a/docs/zh/faq/index.md +++ b/docs/zh/faq/index.md @@ -35,17 +35,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" - ``` 对于 macOS 平台来说,macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 的系统库:`/usr/lib/libresolv.9.dylib` 和 `/usr/lib/libSystem.B.dylib`。 -所以在 macOS 系统下,在特定的编译条件下可以使用静态编译的 php 二进制文件,可使用动态链接的扩展: +因此,在 macOS 上,您可以直接构建出使用静态编译的 PHP 二进制文件和动态链接的扩展: -1. 使用 `--no-strip` 参数,将不会对二进制文件去除调试符号等信息,以供使用 `Xdebug` 等外部 Zend 扩展。 -2. 如果要编译某些 Zend 扩展,使用 Homebrew、MacPorts、源码编译的形式,在所在的操作系统安装一个普通版本的 PHP。 -3. 使用 `phpize && ./configure && make` 命令编译想要使用的扩展。 -4. 将扩展文件 `xxxx.so` 拷贝到外部,使用静态编译的 PHP 二进制,例如使用 Xdebug 扩展:`cd buildroot/bin/ && ./php -d "zend_extension=/path/to/xdebug.so"`。 - -```bash -# 构建静态 php-cli -bin/spc build ffi --build-cli --no-strip -``` +1. 使用 `--build-shared=XXX` 选项构建共享扩展 `xxx.so`。例如:`bin/spc build bcmath,zlib --build-shared=xdebug --build-cli` +2. 您将获得 `buildroot/modules/xdebug.so` 和 `buildroot/bin/php`。 +3. `xdebug.so` 文件可用于版本和线程安全相同的 php。 ## 可以支持 Oracle 数据库扩展吗 diff --git a/docs/zh/guide/build-with-glibc.md b/docs/zh/guide/build-with-glibc.md index b025769b..7d7632d0 100644 --- a/docs/zh/guide/build-with-glibc.md +++ b/docs/zh/guide/build-with-glibc.md @@ -51,5 +51,4 @@ glibc 构建为扩展的特性,不属于默认 static-php 的支持范围。 如果你需要不使用 Docker 构建基于 glibc 的二进制,请参考 `bin/spc-gnu-docker` 脚本,手动构建一个类似的环境。 -由于 glibc 二进制不是项目的主要目标,一般情况下我们不会额外测试 glibc 下的各个库和扩展的兼容性。 -任何特定库如果在 musl-libc 上构建成功,但在 glibc 上构建失败,请提交 issue,我们将会单独解决。 +请注意,我们仅支持使用 bin/spc-gnu-docker 构建的 glibc 版本。已在 RHEL 9 和 10 上进行了编译测试并验证其稳定性,但如果您遇到问题,我们可能不会进行修复。 diff --git a/docs/zh/guide/extension-notes.md b/docs/zh/guide/extension-notes.md index c7b3a8d1..10e62eb0 100644 --- a/docs/zh/guide/extension-notes.md +++ b/docs/zh/guide/extension-notes.md @@ -141,8 +141,7 @@ parallel 扩展只支持 PHP 8.0 及以上版本,并只支持 ZTS 构建(`-- ## spx -1. [SPX 扩展](https://github.com/NoiseByNorthwest/php-spx) 只支持非线程模式。 -2. SPX 目前不支持 Windows,且官方仓库也不支持静态编译,static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。 +1. SPX 目前不支持 Windows,且官方仓库也不支持静态编译,static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。 ## mimalloc diff --git a/docs/zh/guide/manual-build.md b/docs/zh/guide/manual-build.md index ca0395c8..5301fca1 100644 --- a/docs/zh/guide/manual-build.md +++ b/docs/zh/guide/manual-build.md @@ -145,6 +145,7 @@ bin/spc craft --debug - fpm: 构建结果为 `buildroot/bin/php-fpm`。 - micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。 - embed: 参见 [embed 使用](./manual-build#embed-使用)。 +- frankenphp: 构建结果为 `buildroot/bin/frankenphp`。 如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。 @@ -250,6 +251,7 @@ bin/spc doctor --auto-fix - `--build-fpm`: 构建一个 fpm sapi(php-fpm,用于和其他传统的 fpm 架构的软件如 nginx 配合使用) - `--build-micro`: 构建一个 micro sapi(用于构建一个包含 PHP 代码的独立可执行二进制) - `--build-embed`: 构建一个 embed sapi(用于嵌入到其他 C 语言程序中) +- `--build-frankenphp`: 构建一个 [frankenphp](https://github.com/php/frankenphp) 二进制 - `--build-all`: 构建以上所有 sapi ```bash @@ -294,7 +296,7 @@ bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli - `--cxx=XXX`: 指定 C++ 语言编译器的执行命令(Linux 默认 `g++`,macOS 默认 `clang++`) - `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件 - `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本) -- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积(不裁剪的 macOS 二进制文件可使用动态链接的第三方扩展) +- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积 - `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等) - `--with-config-file-path=XXX`: 查找 `php.ini` 的路径(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径) - `--with-config-file-scan-dir=XXX`: 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径) @@ -457,6 +459,8 @@ bin/spc dev:sort-config ext 下面是安装工具的示例: - 下载安装 UPX(仅限 Linux 和 Windows): `bin/spc install-pkg upx` +- 下载安装 nasm(仅限 Windows): `bin/spc install-pkg nasm` +- 下载安装 go-xcaddy: `bin/spc install-pkg go-xcaddy` ## 命令 del-download - 删除已下载的资源 diff --git a/package.json b/package.json index 4b448715..da1bbb88 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "docs:preview": "vitepress preview docs" }, "devDependencies": { - "vitepress": "^1.0.0-rc.35", + "vitepress": "^2.0.0-alpha.5", "vue": "^3.2.47" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9b045629..dc58130d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,4 +2,7 @@ - \ No newline at end of file + + + + diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index 0ec10ced..b69dcf08 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -33,7 +33,7 @@ use Symfony\Component\Console\Application; */ final class ConsoleApplication extends Application { - public const VERSION = '2.5.2'; + public const VERSION = '2.6.1'; public function __construct() { diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 59f371ef..88e827c5 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SPC\builder; +use PharIo\FileSystem\File; use SPC\exception\ExceptionHandler; use SPC\exception\FileSystemException; use SPC\exception\InterruptException; @@ -11,6 +12,7 @@ use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\store\SourceManager; use SPC\util\CustomExt; @@ -127,7 +129,7 @@ abstract class BuilderBase if ($including_shared) { return $this->exts; } - return array_filter($this->exts, fn ($ext) => !$ext->isBuildShared()); + return array_filter($this->exts, fn ($ext) => $ext->isBuildStatic()); } /** @@ -233,15 +235,41 @@ abstract class BuilderBase */ abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE); + /** + * Test PHP + */ + abstract public function testPHP(int $build_target = BUILD_TARGET_NONE); + + /** + * @throws WrongUsageException + * @throws RuntimeException + * @throws FileSystemException + */ public function buildSharedExts(): void { - foreach ($this->getExts() as $ext) { - if (!$ext->isBuildShared()) { - continue; + $lines = file(BUILD_BIN_PATH . '/php-config'); + $extension_dir_line = null; + foreach ($lines as $key => $value) { + if (str_starts_with($value, 'extension_dir=')) { + $lines[$key] = 'extension_dir="' . BUILD_MODULES_PATH . '"' . PHP_EOL; + $extension_dir_line = $value; + break; } - logger()->info('Building extension [' . $ext->getName() . '] as shared extension (' . $ext->getName() . '.so)'); - $ext->buildShared(); } + file_put_contents(BUILD_BIN_PATH . '/php-config', implode('', $lines)); + FileSystem::createDir(BUILD_MODULES_PATH); + try { + foreach ($this->getExts() as $ext) { + if (!$ext->isBuildShared()) { + continue; + } + $ext->buildShared(); + } + } catch (RuntimeException $e) { + FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line); + throw $e; + } + FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line); } /** @@ -254,9 +282,21 @@ abstract class BuilderBase public function makeStaticExtensionArgs(): string { $ret = []; - foreach ($this->getExts(false) as $ext) { - logger()->info($ext->getName() . ' is using ' . $ext->getConfigureArg()); - $ret[] = trim($ext->getConfigureArg()); + foreach ($this->getExts() as $ext) { + $arg = $ext->getConfigureArg(); + if ($ext->isBuildShared() && !$ext->isBuildStatic()) { + if ( + (Config::getExt($ext->getName(), 'type') === 'builtin' && + !file_exists(SOURCE_PATH . '/php-src/ext/' . $ext->getName() . '/config.m4')) || + Config::getExt($ext->getName(), 'build-with-php') === true + ) { + $arg = $ext->getConfigureArg(true); + } else { + continue; + } + } + logger()->info($ext->getName() . ' is using ' . $arg); + $ret[] = trim($arg); } logger()->debug('Using configure: ' . implode(' ', $ret)); return implode(' ', $ret); @@ -311,15 +351,11 @@ abstract class BuilderBase public function getPHPVersionFromArchive(?string $file = null): false|string { if ($file === null) { - $lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false; - if ($lock === false) { - return false; - } - $lock = json_decode($lock, true); - $file = $lock['php-src']['filename'] ?? null; - if ($file === null) { + $lock = LockFile::get('php-src'); + if ($lock === null) { return false; } + $file = LockFile::getLockFullPath($lock); } if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) { return $match[1]; @@ -365,6 +401,9 @@ abstract class BuilderBase if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) { $ls[] = 'embed'; } + if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) { + $ls[] = 'frankenphp'; + } return implode(', ', $ls); } @@ -471,6 +510,29 @@ abstract class BuilderBase } } + public function checkBeforeBuildPHP(int $rule): void + { + if (($rule & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) { + if (!$this->getOption('enable-zts')) { + throw new WrongUsageException('FrankenPHP SAPI requires ZTS enabled PHP, build with `--enable-zts`!'); + } + // frankenphp doesn't support windows, BSD is currently not supported by static-php-cli + if (!in_array(PHP_OS_FAMILY, ['Linux', 'Darwin'])) { + throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!'); + } + // frankenphp needs package go-xcaddy installed + $pkg_dir = PKG_ROOT_PATH . '/go-xcaddy-' . arch2gnu(php_uname('m')) . '-' . osfamily2shortname(); + if (!file_exists("{$pkg_dir}/bin/go") || !file_exists("{$pkg_dir}/bin/xcaddy")) { + global $argv; + throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy"); + } + // frankenphp needs libxml2 lib on macos, see: https://github.com/php/frankenphp/blob/main/frankenphp.go#L17 + if (PHP_OS_FAMILY === 'Darwin' && !$this->getLib('libxml2')) { + throw new WrongUsageException('FrankenPHP SAPI for macOS requires libxml2 library, please include the `xml` extension in your build.'); + } + } + } + /** * Generate micro extension test php code. */ diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 79c0a957..3f63c84b 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -53,26 +53,26 @@ class Extension } } + public function getFrameworks(): array + { + return Config::getExt($this->getName(), 'frameworks', []); + } + /** * 获取开启该扩展的 PHP 编译添加的参数 * * @throws FileSystemException * @throws WrongUsageException */ - public function getConfigureArg(): string + public function getConfigureArg(bool $shared = false): string { - $arg = $this->getEnableArg(); - switch (PHP_OS_FAMILY) { - case 'Windows': - $arg .= $this->getWindowsConfigureArg(); - break; - case 'Darwin': - case 'Linux': - case 'BSD': - $arg .= $this->getUnixConfigureArg(); - break; - } - return $arg; + return match (PHP_OS_FAMILY) { + 'Windows' => $this->getWindowsConfigureArg($shared), + 'Darwin', + 'Linux', + 'BSD' => $this->getUnixConfigureArg($shared), + default => throw new WrongUsageException(PHP_OS_FAMILY . ' build is not supported yet'), + }; } /** @@ -81,13 +81,13 @@ class Extension * @throws FileSystemException * @throws WrongUsageException */ - public function getEnableArg(): string + public function getEnableArg(bool $shared = false): string { $_name = str_replace('_', '-', $this->name); return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) { - 'enable' => '--enable-' . $_name . ' ', - 'with' => '--with-' . $_name . ' ', - 'with-prefix' => '--with-' . $_name . '="' . BUILD_ROOT_PATH . '" ', + 'enable' => '--enable-' . $_name . ($shared ? '=shared' : '') . ' ', + 'with' => '--with-' . $_name . ($shared ? '=shared' : '') . ' ', + 'with-prefix' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . '"' . BUILD_ROOT_PATH . '" ', 'none', 'custom' => '', default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-prefix] ."), }; @@ -147,15 +147,15 @@ class Extension return $this->name; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { - return ''; + return $this->getEnableArg(); // Windows is not supported yet } public function getUnixConfigureArg(bool $shared = false): string { - return ''; + return $this->getEnableArg($shared); } /** @@ -189,18 +189,80 @@ class Extension } /** - * Run shared extension check when cli is enabled - * @throws RuntimeException + * Patch code before shared extension phpize + * If you need to patch some code, overwrite this + * return true if you patched something, false if not */ - public function runSharedExtensionCheckUnix(): void + public function patchBeforeSharedPhpize(): bool { - [$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -d "extension=' . BUILD_LIB_PATH . '/' . $this->getName() . '.so" --ri ' . $this->getName()); - if ($ret !== 0) { - throw new RuntimeException($this->getName() . '.so failed to load'); + return false; + } + + /** + * Patch code before shared extension ./configure + * If you need to patch some code, overwrite this + * return true if you patched something, false if not + */ + public function patchBeforeSharedConfigure(): bool + { + return false; + } + + /** + * Patch code before shared extension make + * If you need to patch some code, overwrite this + * return true if you patched something, false if not + */ + public function patchBeforeSharedMake(): bool + { + return false; + } + + /** + * @return string + * returns a command line string with all required shared extensions to load + * i.e.; pdo_pgsql would return: + * + * `-d "extension=pgsql" -d "extension=pdo_pgsql"` + * @throws FileSystemException + * @throws WrongUsageException + */ + public function getSharedExtensionLoadString(): string + { + $loaded = []; + $order = []; + + $resolve = function ($extension) use (&$resolve, &$loaded, &$order) { + if (isset($loaded[$extension->getName()])) { + return; + } + $loaded[$extension->getName()] = true; + + foreach ($this->dependencies as $dependency) { + $resolve($dependency); + } + + $order[] = $extension; + }; + + $resolve($this); + + $ret = ''; + foreach ($order as $ext) { + if ($ext instanceof Extension && $ext->isBuildShared()) { + if (Config::getExt($ext->getName(), 'zend-extension', false) === true) { + $ret .= " -d \"zend_extension={$ext->getName()}\""; + } else { + $ret .= " -d \"extension={$ext->getName()}\""; + } + } } - if ($this->isBuildStatic()) { - logger()->warning($this->getName() . '.so test succeeded, but has little significance since it is also compiled in statically.'); + + if ($ret !== '') { + $ret = ' -d "extension_dir=' . BUILD_MODULES_PATH . '"' . $ret; } + + return $ret; } /** @@ -211,9 +273,13 @@ class Extension // Run compile check if build target is cli // If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php // If check failed, throw RuntimeException - [$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "' . $this->getDistName() . '"', false); + $sharedExtensions = $this->getSharedExtensionLoadString(); + [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); if ($ret !== 0) { - throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); + throw new RuntimeException( + 'extension ' . $this->getName() . ' failed runtime check: php-cli returned ' . $ret . "\n" . + join("\n", $out) + ); } if (file_exists(ROOT_DIR . '/src/globals/ext-tests/' . $this->getName() . '.php')) { @@ -224,7 +290,7 @@ class Extension file_get_contents(ROOT_DIR . '/src/globals/ext-tests/' . $this->getName() . '.php') ); - [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n -r "' . trim($test) . '"'); + [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' -r "' . trim($test) . '"'); if ($ret !== 0) { if ($this->builder->getOption('debug')) { var_dump($out); @@ -275,6 +341,30 @@ class Extension */ public function buildShared(): void { + if (Config::getExt($this->getName(), 'type') === 'builtin' || Config::getExt($this->getName(), 'build-with-php') === true) { + if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) { + logger()->info('Shared extension [' . $this->getName() . '] was already built by php-src/configure (' . $this->getName() . '.so)'); + return; + } + if (Config::getExt($this->getName(), 'build-with-php') === true) { + logger()->warning('Shared extension [' . $this->getName() . '] did not build with php-src/configure (' . $this->getName() . '.so)'); + logger()->warning('Try deleting your build and source folders and running `spc build`` again.'); + return; + } + } + if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) { + logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)'); + } + logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)'); + foreach ($this->dependencies as $dependency) { + if (!$dependency instanceof Extension) { + continue; + } + if (!$dependency->isBuildStatic()) { + logger()->info('extension ' . $this->getName() . ' requires extension ' . $dependency->getName()); + $dependency->buildShared(); + } + } match (PHP_OS_FAMILY) { 'Darwin', 'Linux' => $this->buildUnixShared(), default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'), @@ -292,26 +382,58 @@ class Extension */ public function buildUnixShared(): void { - $config = (new SPCConfigUtil($this->builder))->config([$this->getName()]); + $config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true); + [$staticLibString, $sharedLibString] = $this->getStaticAndSharedLibs(); + + // macOS ld64 doesn't understand these, while Linux and BSD do + // use them to make sure that all symbols are picked up, even if a library has already been visited before + $preStatic = PHP_OS_FAMILY !== 'Darwin' ? '-Wl,-Bstatic -Wl,--start-group ' : ''; + $postStatic = PHP_OS_FAMILY !== 'Darwin' ? ' -Wl,--end-group -Wl,-Bdynamic ' : ' '; $env = [ 'CFLAGS' => $config['cflags'], + 'CXXFLAGS' => $config['cflags'], 'LDFLAGS' => $config['ldflags'], - 'LIBS' => $config['libs'], + 'LIBS' => $preStatic . $staticLibString . $postStatic . $sharedLibString, + 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, ]; + + if ($this->patchBeforeSharedPhpize()) { + logger()->info("Extension [{$this->getName()}] patched before shared phpize"); + } + // prepare configure args shell()->cd($this->source_dir) ->setEnv($env) - ->exec(BUILD_BIN_PATH . '/phpize') - ->exec('./configure ' . $this->getUnixConfigureArg(true) . ' --with-php-config=' . BUILD_BIN_PATH . '/php-config --enable-shared --disable-static') - ->exec('make clean') - ->exec('make -j' . $this->builder->concurrency); + ->exec(BUILD_BIN_PATH . '/phpize'); - // copy shared library - copy($this->source_dir . '/modules/' . $this->getDistName() . '.so', BUILD_LIB_PATH . '/' . $this->getDistName() . '.so'); - // check shared extension with php-cli - if (file_exists(BUILD_BIN_PATH . '/php')) { - $this->runSharedExtensionCheckUnix(); + if ($this->patchBeforeSharedConfigure()) { + logger()->info("Extension [{$this->getName()}] patched before shared configure"); } + + shell()->cd($this->source_dir) + ->setEnv($env) + ->exec( + './configure ' . $this->getUnixConfigureArg(true) . + ' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' . + '--enable-shared --disable-static' + ); + + // some extensions don't define their dependencies well, this patch is only needed for a few + FileSystem::replaceFileRegex( + $this->source_dir . '/Makefile', + '/^(.*_SHARED_LIBADD\s*=.*)$/m', + '$1 ' . $staticLibString + ); + + if ($this->patchBeforeSharedMake()) { + logger()->info("Extension [{$this->getName()}] patched before shared make"); + } + + shell()->cd($this->source_dir) + ->setEnv($env) + ->exec('make clean') + ->exec('make -j' . $this->builder->concurrency) + ->exec('make install'); } /** @@ -382,6 +504,37 @@ class Extension } } + /** + * Get required static and shared libraries as a pair of strings in format -l{libname} -l{libname2} + * + * @return array [staticLibString, sharedLibString] + */ + protected function getStaticAndSharedLibs(): array + { + $config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true); + $sharedLibString = ''; + $staticLibString = ''; + $staticLibs = $this->getLibFilesString(); + $staticLibs = str_replace(BUILD_LIB_PATH . '/lib', '-l', $staticLibs); + $staticLibs = str_replace('.a', '', $staticLibs); + $staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']); + foreach ($staticLibs as $lib) { + $lib = trim($lib); + if ($lib === '') { + continue; + } + $static_lib = 'lib' . $lib . '.a'; + if (file_exists(BUILD_LIB_PATH . '/' . $static_lib) && !str_contains($static_lib, 'libphp')) { + if (!str_contains($staticLibString, '-l' . $lib . ' ')) { + $staticLibString .= '-l' . $lib . ' '; + } + } elseif (!str_contains($sharedLibString, '-l' . $lib . ' ')) { + $sharedLibString .= '-l' . $lib . ' '; + } + } + return [trim($staticLibString), trim($sharedLibString)]; + } + private function getLibraryDependencies(bool $recursive = false): array { $ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase); @@ -407,6 +560,11 @@ class Extension } } + if (array_key_exists(0, $deps)) { + $zero = [0 => $deps[0]]; + unset($deps[0]); + return $zero + $deps; + } return $deps; } } diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index 0d8ab739..fc6adefc 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -10,10 +10,14 @@ use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\Downloader; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\store\SourceManager; +use SPC\util\GlobalValueTrait; abstract class LibraryBase { + use GlobalValueTrait; + /** @var string */ public const NAME = 'unknown'; @@ -31,7 +35,7 @@ abstract class LibraryBase if (static::NAME === 'unknown') { throw new RuntimeException('no unknown!!!!!'); } - $this->source_dir = $source_dir ?? (SOURCE_PATH . '/' . static::NAME); + $this->source_dir = $source_dir ?? (SOURCE_PATH . '/' . Config::getLib(static::NAME, 'source')); } /** @@ -43,12 +47,11 @@ abstract class LibraryBase */ public function setup(bool $force = false): int { - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $source = Config::getLib(static::NAME, 'source'); // if source is locked as pre-built, we just tryInstall it $pre_built_name = Downloader::getPreBuiltLockName($source); - if (isset($lock[$pre_built_name]) && ($lock[$pre_built_name]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) { - return $this->tryInstall($lock[$pre_built_name]['filename'], $force); + if (($lock = LockFile::get($pre_built_name)) && $lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT) { + return $this->tryInstall($lock, $force); } return $this->tryBuild($force); } @@ -163,14 +166,15 @@ abstract class LibraryBase * @throws WrongUsageException * @throws FileSystemException */ - public function tryInstall(string $install_file, bool $force_install = false): int + public function tryInstall(array $lock, bool $force_install = false): int { + $install_file = $lock['filename']; if ($force_install) { logger()->info('Installing required library [' . static::NAME . '] from pre-built binaries'); // Extract files try { - FileSystem::extractPackage($install_file, DOWNLOAD_PATH . '/' . $install_file, BUILD_ROOT_PATH); + FileSystem::extractPackage($install_file, $lock['source_type'], DOWNLOAD_PATH . '/' . $install_file, BUILD_ROOT_PATH); $this->install(); return LIB_STATUS_OK; } catch (FileSystemException|RuntimeException $e) { @@ -180,19 +184,19 @@ abstract class LibraryBase } foreach ($this->getStaticLibs() as $name) { if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { - $this->tryInstall($install_file, true); + $this->tryInstall($lock, true); return LIB_STATUS_OK; } } foreach ($this->getHeaders() as $name) { if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { - $this->tryInstall($install_file, true); + $this->tryInstall($lock, true); return LIB_STATUS_OK; } } // pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) { - $this->tryInstall($install_file, true); + $this->tryInstall($lock, true); return LIB_STATUS_OK; } return LIB_STATUS_ALREADY; @@ -328,21 +332,6 @@ abstract class LibraryBase return false; } - public function getIncludeDir(): string - { - return BUILD_INCLUDE_PATH; - } - - public function getBuildRootPath(): string - { - return BUILD_ROOT_PATH; - } - - public function getLibDir(): string - { - return BUILD_LIB_PATH; - } - /** * Build this library. * diff --git a/src/SPC/builder/extension/amqp.php b/src/SPC/builder/extension/amqp.php index 8fbfea24..7e0ea2de 100644 --- a/src/SPC/builder/extension/amqp.php +++ b/src/SPC/builder/extension/amqp.php @@ -25,10 +25,10 @@ class amqp extends Extension public function getUnixConfigureArg(bool $shared = false): string { - return '--with-amqp --with-librabbitmq-dir=' . BUILD_ROOT_PATH; + return '--with-amqp' . ($shared ? '=shared' : '') . ' --with-librabbitmq-dir=' . BUILD_ROOT_PATH; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg($shared = false): string { return '--with-amqp'; } diff --git a/src/SPC/builder/extension/curl.php b/src/SPC/builder/extension/curl.php index d4f8b078..0c5de6a5 100644 --- a/src/SPC/builder/extension/curl.php +++ b/src/SPC/builder/extension/curl.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\builder\linux\LinuxBuilder; use SPC\builder\macos\MacOSBuilder; use SPC\exception\FileSystemException; use SPC\exception\WrongUsageException; @@ -21,7 +22,7 @@ class curl extends Extension { 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'); + $files = FileSystem::readFile($this->source_dir . '/config.m4'); $file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [ save_old_LDFLAGS=$LDFLAGS ac_stuff="$5" @@ -40,7 +41,7 @@ class curl extends Extension $4 ])dnl ])'; - file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2); + file_put_contents($this->source_dir . '/config.m4', $file1 . "\n" . $files . "\n" . $file2); return true; } @@ -52,6 +53,72 @@ class curl extends Extension { $frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : ''; FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lcurl/', $this->getLibFilesString() . $frameworks); + $this->patchBeforeSharedConfigure(); return true; } + + public function patchBeforeSharedConfigure(): bool + { + $file = $this->source_dir . '/config.m4'; + $content = FileSystem::readFile($file); + + // Inject patch before it + $patch = ' save_LIBS="$LIBS" + LIBS="$LIBS $CURL_LIBS" +'; + // Check if already patched + if (str_contains($content, $patch)) { + return false; // Already patched + } + + // Match the line containing PHP_CHECK_LIBRARY for curl + $pattern = '/(PHP_CHECK_LIBRARY\(\[curl],\s*\[curl_easy_perform],)/'; + + // Restore LIBS after the check — append this just after the macro block + $restore = ' + LIBS="$save_LIBS"'; + + // Apply patch + $patched = preg_replace_callback($pattern, function ($matches) use ($patch) { + return $patch . $matches[1]; + }, $content, 1); + + // Inject restore after the matching PHP_CHECK_LIBRARY block + $patched = preg_replace( + '/(PHP_CHECK_LIBRARY\(\[curl],\s*\[curl_easy_perform],.*?\)\n)/s', + "$1{$restore}\n", + $patched, + 1 + ); + + if ($patched === null) { + throw new \RuntimeException('Failed to patch config.m4 due to a regex error'); + } + + FileSystem::writeFile($file, $patched); + return true; + } + + public function buildUnixShared(): void + { + if (!$this->builder instanceof LinuxBuilder) { + parent::buildUnixShared(); + return; + } + + FileSystem::replaceFileStr( + $this->source_dir . '/config.m4', + ['$ext_dir/phar.1', '$ext_dir/phar.phar.1'], + ['${ext_dir}phar.1', '${ext_dir}phar.phar.1'] + ); + try { + parent::buildUnixShared(); + } finally { + FileSystem::replaceFileStr( + $this->source_dir . '/config.m4', + ['${ext_dir}phar.1', '${ext_dir}phar.phar.1'], + ['$ext_dir/phar.1', '$ext_dir/phar.phar.1'] + ); + } + } } diff --git a/src/SPC/builder/extension/dba.php b/src/SPC/builder/extension/dba.php index bd7388f3..abda5651 100644 --- a/src/SPC/builder/extension/dba.php +++ b/src/SPC/builder/extension/dba.php @@ -12,11 +12,11 @@ class dba extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - $qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . BUILD_ROOT_PATH) : ''; - return '--enable-dba' . $qdbm; + $qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH) : ''; + return '--enable-dba' . ($shared ? '=shared' : '') . $qdbm; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { $qdbm = $this->builder->getLib('qdbm') ? ' --with-qdbm' : ''; return '--with-dba' . $qdbm; diff --git a/src/SPC/builder/extension/dom.php b/src/SPC/builder/extension/dom.php new file mode 100644 index 00000000..85437f62 --- /dev/null +++ b/src/SPC/builder/extension/dom.php @@ -0,0 +1,35 @@ +builder->getLib('freetype') ? ' --with-freetype' : ''; $arg .= $this->builder->getLib('libjpeg') ? ' --with-jpeg' : ''; $arg .= $this->builder->getLib('libwebp') ? ' --with-webp' : ''; diff --git a/src/SPC/builder/extension/glfw.php b/src/SPC/builder/extension/glfw.php index 444b5d93..3ebb5228 100644 --- a/src/SPC/builder/extension/glfw.php +++ b/src/SPC/builder/extension/glfw.php @@ -35,7 +35,7 @@ class glfw extends Extension return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { return '--enable-glfw=static'; } diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index a4cb9386..7951ea69 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -12,10 +12,13 @@ class imagick extends Extension { public function patchBeforeMake(): bool { - if (getenv('SPC_LIBC') !== 'musl') { + if (PHP_OS_FAMILY !== 'Linux') { return false; } - // imagick with calls omp_pause_all which requires -lgomp, on non-musl we build imagick without openmp + if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) { + return false; + } + // imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp $extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lgomp'); f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); return true; @@ -23,7 +26,18 @@ class imagick extends Extension public function getUnixConfigureArg(bool $shared = false): string { - $disable_omp = getenv('SPC_LIBC') === 'musl' ? '' : ' ac_cv_func_omp_pause_resource_all=no'; - return '--with-imagick=' . BUILD_ROOT_PATH . $disable_omp; + $disable_omp = !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) ? '' : ' ac_cv_func_omp_pause_resource_all=no'; + return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; + } + + protected function getStaticAndSharedLibs(): array + { + // on centos 7, it will use the symbol _ZTINSt6thread6_StateE, which is not defined in system libstdc++.so.6 + [$static, $shared] = parent::getStaticAndSharedLibs(); + if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) { + $static .= ' -lstdc++'; + $shared = str_replace('-lstdc++', '', $shared); + } + return [$static, $shared]; } } diff --git a/src/SPC/builder/extension/intl.php b/src/SPC/builder/extension/intl.php index d52a15b0..0c1e323d 100644 --- a/src/SPC/builder/extension/intl.php +++ b/src/SPC/builder/extension/intl.php @@ -20,16 +20,13 @@ class intl extends Extension 'EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", true,', 'EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", PHP_INTL_SHARED,' ); - } else { - // TODO: remove the following line when https://github.com/php/php-src/pull/14002 will be released - FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/intl/config.m4', 'PHP_CXX_COMPILE_STDCXX(11', 'PHP_CXX_COMPILE_STDCXX(17'); - // Also need to use clang++ -std=c++17 to force override the default C++ standard - if (is_string($env = getenv('CXX')) && !str_contains($env, 'std=c++17')) { - f_putenv('CXX=' . $env . ' -std=c++17'); - } else { - f_putenv('CXX=clang++ -std=c++17'); - } + return true; } - return true; + return false; + } + + public function patchBeforeSharedPhpize(): bool + { + return $this->patchBeforeBuildconf(); } } diff --git a/src/SPC/builder/extension/lz4.php b/src/SPC/builder/extension/lz4.php index 541b1ecd..5727a97d 100644 --- a/src/SPC/builder/extension/lz4.php +++ b/src/SPC/builder/extension/lz4.php @@ -15,7 +15,7 @@ class lz4 extends Extension return '--enable-lz4' . ($shared ? '=shared' : '') . ' --with-lz4-includedir=' . BUILD_ROOT_PATH; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { return '--enable-lz4'; } diff --git a/src/SPC/builder/extension/mbregex.php b/src/SPC/builder/extension/mbregex.php index 4bf28544..0e1cad28 100644 --- a/src/SPC/builder/extension/mbregex.php +++ b/src/SPC/builder/extension/mbregex.php @@ -16,7 +16,7 @@ class mbregex extends Extension return 'mbstring'; } - public function getConfigureArg(): string + public function getConfigureArg(bool $shared = false): string { return ''; } @@ -26,7 +26,8 @@ class mbregex extends Extension */ public function runCliCheckUnix(): void { - [$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "mbstring" | grep regex', false); + $sharedext = $this->builder->getExt('mbstring')->isBuildShared() ? '-d "extension_dir=' . BUILD_MODULES_PATH . '" -d "extension=mbstring"' : ''; + [$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $sharedext . ' --ri "mbstring" | grep regex', false); if ($ret !== 0) { throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !'); } diff --git a/src/SPC/builder/extension/mbstring.php b/src/SPC/builder/extension/mbstring.php index 5fcb88bd..3576877f 100644 --- a/src/SPC/builder/extension/mbstring.php +++ b/src/SPC/builder/extension/mbstring.php @@ -10,9 +10,20 @@ use SPC\util\CustomExt; #[CustomExt('mbstring')] class mbstring extends Extension { - public function getConfigureArg(): string + public function getConfigureArg(bool $shared = false): string { - $arg = '--enable-mbstring'; + $arg = '--enable-mbstring' . ($shared ? '=shared' : ''); + if ($this->builder->getExt('mbregex') === null) { + $arg .= ' --disable-mbregex'; + } else { + $arg .= ' --enable-mbregex'; + } + return $arg; + } + + public function getUnixConfigureArg(bool $shared = false): string + { + $arg = '--enable-mbstring' . ($shared ? '=shared' : ''); if ($this->builder->getExt('mbregex') === null) { $arg .= ' --disable-mbregex'; } else { diff --git a/src/SPC/builder/extension/memcache.php b/src/SPC/builder/extension/memcache.php index 4625cae2..479744fd 100644 --- a/src/SPC/builder/extension/memcache.php +++ b/src/SPC/builder/extension/memcache.php @@ -14,7 +14,7 @@ class memcache extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - return '--enable-memcache --with-zlib-dir=' . BUILD_ROOT_PATH; + return '--enable-memcache' . ($shared ? '=shared' : '') . ' --with-zlib-dir=' . BUILD_ROOT_PATH; } /** diff --git a/src/SPC/builder/extension/memcached.php b/src/SPC/builder/extension/memcached.php index 9c433954..a1b88ac6 100644 --- a/src/SPC/builder/extension/memcached.php +++ b/src/SPC/builder/extension/memcached.php @@ -12,8 +12,11 @@ class memcached extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - $rootdir = BUILD_ROOT_PATH; - $zlib_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : "--with-zlib-dir={$rootdir}"; - return "--enable-memcached {$zlib_dir} --with-libmemcached-dir={$rootdir} --disable-memcached-sasl --enable-memcached-json"; + return '--enable-memcached' . ($shared ? '=shared' : '') . ' ' . + '--with-zlib-dir=' . BUILD_ROOT_PATH . ' ' . + '--with-libmemcached-dir=' . BUILD_ROOT_PATH . ' ' . + '--disable-memcached-sasl ' . + '--enable-memcached-json ' . + '--with-system-fastlz'; } } diff --git a/src/SPC/builder/extension/odbc.php b/src/SPC/builder/extension/odbc.php index ac5c3e8f..278b5865 100644 --- a/src/SPC/builder/extension/odbc.php +++ b/src/SPC/builder/extension/odbc.php @@ -12,6 +12,6 @@ class odbc extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - return '--with-unixODBC=' . BUILD_ROOT_PATH; + return '--with-unixODBC=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH; } } diff --git a/src/SPC/builder/extension/openssl.php b/src/SPC/builder/extension/openssl.php index add1aede..fc4831f3 100644 --- a/src/SPC/builder/extension/openssl.php +++ b/src/SPC/builder/extension/openssl.php @@ -26,6 +26,6 @@ class openssl extends Extension public function getUnixConfigureArg(bool $shared = false): string { $openssl_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-openssl-dir=' . BUILD_ROOT_PATH; - return '--with-openssl=' . BUILD_ROOT_PATH . $openssl_dir; + return '--with-openssl=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $openssl_dir; } } diff --git a/src/SPC/builder/extension/pdo_odbc.php b/src/SPC/builder/extension/pdo_odbc.php index e436f381..c47144fe 100644 --- a/src/SPC/builder/extension/pdo_odbc.php +++ b/src/SPC/builder/extension/pdo_odbc.php @@ -19,10 +19,10 @@ class pdo_odbc extends Extension public function getUnixConfigureArg(bool $shared = false): string { - return '--with-pdo-odbc=unixODBC,' . BUILD_ROOT_PATH; + return '--with-pdo-odbc=' . ($shared ? 'shared,' : '') . 'unixODBC,' . BUILD_ROOT_PATH; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { return '--with-pdo-odbc'; } diff --git a/src/SPC/builder/extension/pdo_pgsql.php b/src/SPC/builder/extension/pdo_pgsql.php index e29014cb..b1ca1a81 100644 --- a/src/SPC/builder/extension/pdo_pgsql.php +++ b/src/SPC/builder/extension/pdo_pgsql.php @@ -10,7 +10,7 @@ use SPC\util\CustomExt; #[CustomExt('pdo_pgsql')] class pdo_pgsql extends Extension { - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { return '--with-pdo-pgsql=yes'; } diff --git a/src/SPC/builder/extension/pgsql.php b/src/SPC/builder/extension/pgsql.php index 1c63f163..a70c1fb7 100644 --- a/src/SPC/builder/extension/pgsql.php +++ b/src/SPC/builder/extension/pgsql.php @@ -36,16 +36,21 @@ class pgsql extends Extension public function getUnixConfigureArg(bool $shared = false): string { if ($this->builder->getPHPVersionID() >= 80400) { - return '--with-pgsql PGSQL_CFLAGS=-I' . BUILD_INCLUDE_PATH . ' PGSQL_LIBS="-L' . BUILD_LIB_PATH . ' -lpq -lpgport -lpgcommon"'; + $libfiles = $this->getLibFilesString(); + $libfiles = str_replace(BUILD_LIB_PATH . '/lib', '-l', $libfiles); + $libfiles = str_replace('.a', '', $libfiles); + return '--with-pgsql' . ($shared ? '=shared' : '') . + ' PGSQL_CFLAGS=-I' . BUILD_INCLUDE_PATH . + ' PGSQL_LIBS="-L' . BUILD_LIB_PATH . ' ' . $libfiles . '"'; } - return '--with-pgsql=' . BUILD_ROOT_PATH; + return '--with-pgsql=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH; } /** * @throws WrongUsageException * @throws RuntimeException */ - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { if ($this->builder->getPHPVersionID() >= 80400) { return '--with-pgsql'; diff --git a/src/SPC/builder/extension/phar.php b/src/SPC/builder/extension/phar.php new file mode 100644 index 00000000..7396ba83 --- /dev/null +++ b/src/SPC/builder/extension/phar.php @@ -0,0 +1,37 @@ +builder instanceof LinuxBuilder) { + parent::buildUnixShared(); + return; + } + + FileSystem::replaceFileStr( + $this->source_dir . '/config.m4', + ['$ext_dir/phar.1', '$ext_dir/phar.phar.1'], + ['${ext_dir}phar.1', '${ext_dir}phar.phar.1'] + ); + try { + parent::buildUnixShared(); + } finally { + FileSystem::replaceFileStr( + $this->source_dir . '/config.m4', + ['${ext_dir}phar.1', '${ext_dir}phar.phar.1'], + ['$ext_dir/phar.1', '$ext_dir/phar.phar.1'] + ); + } + } +} diff --git a/src/SPC/builder/extension/protobuf.php b/src/SPC/builder/extension/protobuf.php index 3393ad43..0aa6ed51 100644 --- a/src/SPC/builder/extension/protobuf.php +++ b/src/SPC/builder/extension/protobuf.php @@ -15,8 +15,9 @@ class protobuf extends Extension if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') { throw new \RuntimeException('The latest protobuf extension requires PHP 8.0 or later'); } + $grpc = $this->builder->getExt('grpc'); // protobuf conflicts with grpc - if ($this->builder->getExt('grpc') !== null) { + if ($grpc?->isBuildStatic()) { throw new \RuntimeException('protobuf conflicts with grpc, please remove grpc or protobuf extension'); } } diff --git a/src/SPC/builder/extension/rdkafka.php b/src/SPC/builder/extension/rdkafka.php index 50610325..cb1792c5 100644 --- a/src/SPC/builder/extension/rdkafka.php +++ b/src/SPC/builder/extension/rdkafka.php @@ -11,6 +11,13 @@ use SPC\util\CustomExt; #[CustomExt('rdkafka')] class rdkafka extends Extension { + public function patchBeforeBuildconf(): bool + { + FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n"); + FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"'); + return true; + } + public function patchBeforeMake(): bool { // when compiling rdkafka with inline builds, it shows some errors, I don't know why. @@ -27,10 +34,10 @@ class rdkafka extends Extension return true; } - public function getConfigureArg(): string + public function getUnixConfigureArg(bool $shared = false): string { $pkgconf_libs = shell()->execWithResult('pkg-config --libs --static rdkafka')[1]; $pkgconf_libs = trim(implode('', $pkgconf_libs)); - return '--with-rdkafka=' . BUILD_ROOT_PATH . ' LIBS="' . $pkgconf_libs . '"'; + return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . ' RDKAFKA_LIBS="' . $pkgconf_libs . '"'; } } diff --git a/src/SPC/builder/extension/readline.php b/src/SPC/builder/extension/readline.php index c66e7afa..dc4d15f7 100644 --- a/src/SPC/builder/extension/readline.php +++ b/src/SPC/builder/extension/readline.php @@ -24,4 +24,18 @@ class readline extends Extension ); return true; } + + public function getUnixConfigureArg(bool $shared = false): string + { + return '--without-libedit --with-readline=' . BUILD_ROOT_PATH; + } + + public function buildUnixShared(): void + { + if (!file_exists(BUILD_BIN_PATH . '/php') || !file_exists(BUILD_INCLUDE_PATH . '/php/sapi/cli/cli.h')) { + logger()->warning('CLI mode is not enabled, skipping readline build'); + return; + } + parent::buildUnixShared(); + } } diff --git a/src/SPC/builder/extension/redis.php b/src/SPC/builder/extension/redis.php index 0b60075c..75158ad7 100644 --- a/src/SPC/builder/extension/redis.php +++ b/src/SPC/builder/extension/redis.php @@ -24,7 +24,7 @@ class redis extends Extension return $arg; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { $arg = '--enable-redis'; $arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session'; diff --git a/src/SPC/builder/extension/spx.php b/src/SPC/builder/extension/spx.php index dc341e39..f5e736d5 100644 --- a/src/SPC/builder/extension/spx.php +++ b/src/SPC/builder/extension/spx.php @@ -5,28 +5,28 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; -use SPC\exception\WrongUsageException; +use SPC\store\FileSystem; use SPC\util\CustomExt; #[CustomExt('spx')] class spx extends Extension { - /** - * @throws WrongUsageException - */ - public function validate(): void - { - if ($this->builder->getOption('enable-zts')) { - throw new WrongUsageException('ext-spx is not thread safe, do not build it with ZTS builds'); - } - } - public function getUnixConfigureArg(bool $shared = false): string { - $arg = '--enable-spx'; - if ($this->builder->getExt('zlib') === null) { + $arg = '--enable-spx' . ($shared ? '=shared' : ''); + if ($this->builder->getLib('zlib') !== null) { $arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH; } return $arg; } + + public function patchBeforeConfigure(): bool + { + FileSystem::replaceFileStr( + $this->source_dir . '/Makefile.frag', + '@cp -r assets/web-ui/*', + '@cp -r ' . $this->source_dir . '/assets/web-ui/*', + ); + return true; + } } diff --git a/src/SPC/builder/extension/swoole_hook_mysql.php b/src/SPC/builder/extension/swoole_hook_mysql.php index e9684872..b45516ee 100644 --- a/src/SPC/builder/extension/swoole_hook_mysql.php +++ b/src/SPC/builder/extension/swoole_hook_mysql.php @@ -29,7 +29,7 @@ class swoole_hook_mysql extends Extension if ($this->builder->getExt('swoole') === null) { return; } - [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "swoole"', false); + [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $this->getSharedExtensionLoadString() . ' --ri "swoole"', false); $out = implode('', $out); if ($ret !== 0) { throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); diff --git a/src/SPC/builder/extension/swoole_hook_pgsql.php b/src/SPC/builder/extension/swoole_hook_pgsql.php index 113b8eb6..dfbf7dc8 100644 --- a/src/SPC/builder/extension/swoole_hook_pgsql.php +++ b/src/SPC/builder/extension/swoole_hook_pgsql.php @@ -37,7 +37,8 @@ class swoole_hook_pgsql extends Extension if ($this->builder->getExt('swoole') === null) { return; } - [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "swoole"', false); + $sharedExtensions = $this->getSharedExtensionLoadString(); + [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); $out = implode('', $out); if ($ret !== 0) { throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); diff --git a/src/SPC/builder/extension/swoole_hook_sqlite.php b/src/SPC/builder/extension/swoole_hook_sqlite.php index 7948dd29..29e9ef84 100644 --- a/src/SPC/builder/extension/swoole_hook_sqlite.php +++ b/src/SPC/builder/extension/swoole_hook_sqlite.php @@ -37,7 +37,8 @@ class swoole_hook_sqlite extends Extension if ($this->builder->getExt('swoole') === null) { return; } - [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "swoole"', false); + $sharedExtensions = $this->getSharedExtensionLoadString(); + [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); $out = implode('', $out); if ($ret !== 0) { throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); diff --git a/src/SPC/builder/extension/swow.php b/src/SPC/builder/extension/swow.php index 27576635..390ec27f 100644 --- a/src/SPC/builder/extension/swow.php +++ b/src/SPC/builder/extension/swow.php @@ -18,7 +18,7 @@ class swow extends Extension } } - public function getConfigureArg(): string + public function getConfigureArg(bool $shared = false): string { $arg = '--enable-swow'; $arg .= $this->builder->getLib('openssl') ? ' --enable-swow-ssl' : ' --disable-swow-ssl'; diff --git a/src/SPC/builder/extension/uv.php b/src/SPC/builder/extension/uv.php index af879882..8c54e007 100644 --- a/src/SPC/builder/extension/uv.php +++ b/src/SPC/builder/extension/uv.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\store\FileSystem; use SPC\util\CustomExt; #[CustomExt('uv')] @@ -16,4 +17,13 @@ class uv extends Extension throw new \RuntimeException('The latest uv extension requires PHP 8.0 or later'); } } + + public function patchBeforeSharedMake(): bool + { + if (PHP_OS_FAMILY !== 'Linux' || arch2gnu(php_uname('m')) !== 'aarch64') { + return false; + } + FileSystem::replaceFileRegex($this->source_dir . '/Makefile', '/^(LDFLAGS =.*)$/m', '$1 -luv -ldl -lrt -pthread'); + return true; + } } diff --git a/src/SPC/builder/extension/xdebug.php b/src/SPC/builder/extension/xdebug.php deleted file mode 100644 index ed592056..00000000 --- a/src/SPC/builder/extension/xdebug.php +++ /dev/null @@ -1,21 +0,0 @@ -execWithResult(BUILD_BIN_PATH . '/php -n -d "zend_extension=' . BUILD_LIB_PATH . '/xdebug.so" --ri xdebug'); - if ($ret !== 0) { - throw new RuntimeException('xdebug.so failed to load.'); - } - } -} diff --git a/src/SPC/builder/extension/xlswriter.php b/src/SPC/builder/extension/xlswriter.php index 2ceb8e5c..878168ad 100644 --- a/src/SPC/builder/extension/xlswriter.php +++ b/src/SPC/builder/extension/xlswriter.php @@ -20,7 +20,7 @@ class xlswriter extends Extension return $arg; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { return '--with-xlswriter'; } diff --git a/src/SPC/builder/extension/xml.php b/src/SPC/builder/extension/xml.php index fc8bfc4d..bd64d346 100644 --- a/src/SPC/builder/extension/xml.php +++ b/src/SPC/builder/extension/xml.php @@ -13,7 +13,6 @@ use SPC\util\CustomExt; #[CustomExt('soap')] #[CustomExt('xmlreader')] #[CustomExt('xmlwriter')] -#[CustomExt('dom')] #[CustomExt('simplexml')] class xml extends Extension { @@ -27,11 +26,10 @@ class xml extends Extension 'soap' => '--enable-soap', 'xmlreader' => '--enable-xmlreader', 'xmlwriter' => '--enable-xmlwriter', - 'dom' => '--enable-dom', 'simplexml' => '--enable-simplexml', default => throw new RuntimeException('Not accept non-xml extension'), }; - $arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '"'; + $arg .= ($shared ? '=shared' : '') . ' --with-libxml="' . BUILD_ROOT_PATH . '"'; return $arg; } @@ -41,14 +39,13 @@ class xml extends Extension return true; } - public function getWindowsConfigureArg(): string + public function getWindowsConfigureArg(bool $shared = false): string { $arg = match ($this->name) { 'xml' => '--with-xml', 'soap' => '--enable-soap', 'xmlreader' => '--enable-xmlreader', 'xmlwriter' => '--enable-xmlwriter', - 'dom' => '--with-dom', 'simplexml' => '--with-simplexml', default => throw new RuntimeException('Not accept non-xml extension'), }; diff --git a/src/SPC/builder/extension/yac.php b/src/SPC/builder/extension/yac.php index bb085133..335aafa1 100644 --- a/src/SPC/builder/extension/yac.php +++ b/src/SPC/builder/extension/yac.php @@ -21,6 +21,6 @@ class yac extends Extension public function getUnixConfigureArg(bool $shared = false): string { - return '--enable-yac --enable-igbinary --enable-json'; + return '--enable-yac' . ($shared ? '=shared' : '') . ' --enable-igbinary --enable-json --with-system-fastlz'; } } diff --git a/src/SPC/builder/freebsd/BSDBuilder.php b/src/SPC/builder/freebsd/BSDBuilder.php index 0727e103..65ebea57 100644 --- a/src/SPC/builder/freebsd/BSDBuilder.php +++ b/src/SPC/builder/freebsd/BSDBuilder.php @@ -96,6 +96,7 @@ class BSDBuilder extends UnixBuilderBase $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; + $enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP; shell()->cd(SOURCE_PATH . '/php-src') ->exec( @@ -143,7 +144,14 @@ class BSDBuilder extends UnixBuilderBase } $this->buildEmbed(); } + if ($enableFrankenphp) { + logger()->info('building frankenphp'); + $this->buildFrankenphp(); + } + } + public function testPHP(int $build_target = BUILD_TARGET_NONE) + { if (php_uname('m') === $this->getOption('arch')) { $this->emitPatchPoint('before-sanity-check'); $this->sanityCheck($build_target); diff --git a/src/SPC/builder/freebsd/library/curl.php b/src/SPC/builder/freebsd/library/curl.php index bb5c9265..4eeab20d 100644 --- a/src/SPC/builder/freebsd/library/curl.php +++ b/src/SPC/builder/freebsd/library/curl.php @@ -10,9 +10,9 @@ class curl extends BSDLibraryBase public const NAME = 'curl'; - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string + public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string { - $libs = parent::getStaticLibFiles($style, $recursive); + $libs = parent::getStaticLibFiles($style, $recursive, $include_self); if ($this->builder->getLib('openssl')) { $this->builder->setOption('extra-libs', $this->builder->getOption('extra-libs') . ' /usr/lib/libpthread.a /usr/lib/libdl.a'); } diff --git a/src/SPC/builder/freebsd/library/openssl.php b/src/SPC/builder/freebsd/library/openssl.php index fb029a77..1bbabf52 100644 --- a/src/SPC/builder/freebsd/library/openssl.php +++ b/src/SPC/builder/freebsd/library/openssl.php @@ -48,7 +48,7 @@ class openssl extends BSDLibraryBase $ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib); } - shell()->cd($this->source_dir) + shell()->cd($this->source_dir)->initializeEnv($this) ->exec( "./Configure no-shared {$extra} " . '--prefix=/ ' . // use prefix=/ diff --git a/src/SPC/builder/freebsd/library/watcher.php b/src/SPC/builder/freebsd/library/watcher.php new file mode 100644 index 00000000..1a08dd24 --- /dev/null +++ b/src/SPC/builder/freebsd/library/watcher.php @@ -0,0 +1,12 @@ +setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\""); $this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\""); - GlobalEnvManager::putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . getenv('PATH')); $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE'); $configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure; GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$configure}"); @@ -110,10 +109,11 @@ class LinuxBuilder extends UnixBuilderBase $config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ? ('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : ''; - $enable_cli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI; - $enable_fpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; - $enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; - $enable_embed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; + $enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI; + $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; + $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; + $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; + $enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP; $mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : ''; // prepare build php envs @@ -125,7 +125,7 @@ class LinuxBuilder extends UnixBuilderBase ]); // process micro upx patch if micro sapi enabled - if ($enable_micro) { + if ($enableMicro) { if (version_compare($this->getMicroVersion(), '0.2.0') < 0) { // for phpmicro 0.1.x $this->processMicroUPXLegacy(); @@ -137,10 +137,10 @@ class LinuxBuilder extends UnixBuilderBase shell()->cd(SOURCE_PATH . '/php-src') ->exec( getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' . - ($enable_cli ? '--enable-cli ' : '--disable-cli ') . - ($enable_fpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') . - ($enable_embed ? "--enable-embed={$embed_type} " : '--disable-embed ') . - ($enable_micro ? '--enable-micro=all-static ' : '--disable-micro ') . + ($enableCli ? '--enable-cli ' : '--disable-cli ') . + ($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') . + ($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') . + ($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') . $config_file_path . $config_file_scan_dir . $disable_jit . @@ -156,26 +156,33 @@ class LinuxBuilder extends UnixBuilderBase $this->cleanMake(); - if ($enable_cli) { + if ($enableCli) { logger()->info('building cli'); $this->buildCli(); } - if ($enable_fpm) { + if ($enableFpm) { logger()->info('building fpm'); $this->buildFpm(); } - if ($enable_micro) { + if ($enableMicro) { logger()->info('building micro'); $this->buildMicro(); } - if ($enable_embed) { + if ($enableEmbed) { logger()->info('building embed'); - if ($enable_micro) { + if ($enableMicro) { FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la'); } $this->buildEmbed(); } + if ($enableFrankenphp) { + logger()->info('building frankenphp'); + $this->buildFrankenphp(); + } + } + public function testPHP(int $build_target = BUILD_TARGET_NONE) + { $this->emitPatchPoint('before-sanity-check'); $this->sanityCheck($build_target); } @@ -189,9 +196,10 @@ class LinuxBuilder extends UnixBuilderBase protected function buildCli(): void { $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); + $SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; shell()->cd(SOURCE_PATH . '/php-src') ->exec('sed -i "s|//lib|/lib|g" Makefile') - ->exec("\$SPC_CMD_PREFIX_PHP_MAKE {$vars} cli"); + ->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli"); if ($this->getOption('with-upx-pack')) { shell()->cd(SOURCE_PATH . '/php-src/sapi/cli') @@ -227,10 +235,11 @@ class LinuxBuilder extends UnixBuilderBase // patch fake cli for micro $vars['EXTRA_CFLAGS'] .= $enable_fake_cli; $vars = SystemUtil::makeEnvVarString($vars); + $SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; shell()->cd(SOURCE_PATH . '/php-src') ->exec('sed -i "s|//lib|/lib|g" Makefile') - ->exec("\$SPC_CMD_PREFIX_PHP_MAKE {$vars} micro"); + ->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} micro"); $this->processMicroUPX(); @@ -250,9 +259,10 @@ class LinuxBuilder extends UnixBuilderBase protected function buildFpm(): void { $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); + $SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; shell()->cd(SOURCE_PATH . '/php-src') ->exec('sed -i "s|//lib|/lib|g" Makefile') - ->exec("\$SPC_CMD_PREFIX_PHP_MAKE {$vars} fpm"); + ->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} fpm"); if ($this->getOption('with-upx-pack')) { shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm') @@ -275,7 +285,38 @@ class LinuxBuilder extends UnixBuilderBase shell()->cd(SOURCE_PATH . '/php-src') ->exec('sed -i "s|//lib|/lib|g" Makefile') + ->exec('sed -i "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile') ->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install"); + + $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'); + if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) { + $release = $matches[1]; + $realLibName = 'libphp-' . $release . '.so'; + $realLib = BUILD_LIB_PATH . '/' . $realLibName; + rename(BUILD_LIB_PATH . '/libphp.so', $realLib); + $cwd = getcwd(); + chdir(BUILD_LIB_PATH); + symlink($realLibName, 'libphp.so'); + chdir(BUILD_MODULES_PATH); + foreach ($this->getExts() as $ext) { + if (!$ext->isBuildShared()) { + continue; + } + $name = $ext->getName(); + $versioned = "{$name}-{$release}.so"; + $unversioned = "{$name}.so"; + if (is_file(BUILD_MODULES_PATH . "/{$versioned}")) { + rename(BUILD_MODULES_PATH . "/{$versioned}", BUILD_MODULES_PATH . "/{$unversioned}"); + shell()->cd(BUILD_MODULES_PATH) + ->exec(sprintf( + 'patchelf --set-soname %s %s', + escapeshellarg($unversioned), + escapeshellarg($unversioned) + )); + } + } + chdir($cwd); + } $this->patchPhpScripts(); } @@ -284,6 +325,7 @@ class LinuxBuilder extends UnixBuilderBase return [ 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), 'EXTRA_LIBS' => getenv('SPC_EXTRA_LIBS') . ' ' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'), + 'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), 'EXTRA_LDFLAGS_PROGRAM' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM'), ]; } diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php index e49830d2..a85f3dec 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -11,6 +11,8 @@ class SystemUtil { use UnixSystemUtilTrait; + public static ?string $libc_version = null; + /** @noinspection PhpMissingBreakStatementInspection */ public static function getOSRelease(): array { @@ -188,6 +190,9 @@ class SystemUtil */ public static function getLibcVersionIfExists(): ?string { + if (self::$libc_version !== null) { + return self::$libc_version; + } if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') { $result = shell()->execWithResult('ldd --version', false); if ($result[0] !== 0) { @@ -198,7 +203,8 @@ class SystemUtil // match ldd version: "ldd (some useless text) 2.17" match 2.17 $pattern = '/ldd\s+\(.*?\)\s+(\d+\.\d+)/'; if (preg_match($pattern, $first_line, $matches)) { - return $matches[1]; + self::$libc_version = $matches[1]; + return self::$libc_version; } return null; } @@ -212,7 +218,8 @@ class SystemUtil // match ldd version: "Version 1.2.3" match 1.2.3 $pattern = '/Version\s+(\d+\.\d+\.\d+)/'; if (preg_match($pattern, $result[1][1] ?? '', $matches)) { - return $matches[1]; + self::$libc_version = $matches[1]; + return self::$libc_version; } } return null; diff --git a/src/SPC/builder/linux/library/curl.php b/src/SPC/builder/linux/library/curl.php index d45492c4..f382773f 100644 --- a/src/SPC/builder/linux/library/curl.php +++ b/src/SPC/builder/linux/library/curl.php @@ -10,9 +10,9 @@ class curl extends LinuxLibraryBase public const NAME = 'curl'; - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string + public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string { - $libs = parent::getStaticLibFiles($style, $recursive); + $libs = parent::getStaticLibFiles($style, $recursive, $include_self); if ($this->builder->getLib('openssl')) { $libs .= ' -ldl -lpthread'; } diff --git a/src/SPC/builder/linux/library/fastlz.php b/src/SPC/builder/linux/library/fastlz.php new file mode 100644 index 00000000..07bee9bb --- /dev/null +++ b/src/SPC/builder/linux/library/fastlz.php @@ -0,0 +1,12 @@ +cd($this->source_dir . '/source') + shell()->cd($this->source_dir . '/source')->initializeEnv($this) ->exec( "{$cppflags} {$cxxflags} {$ldflags} " . './runConfigureICU Linux ' . diff --git a/src/SPC/builder/linux/library/libffi.php b/src/SPC/builder/linux/library/libffi.php index e64581bc..ecb3e142 100644 --- a/src/SPC/builder/linux/library/libffi.php +++ b/src/SPC/builder/linux/library/libffi.php @@ -6,6 +6,7 @@ namespace SPC\builder\linux\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; class libffi extends LinuxLibraryBase { @@ -17,23 +18,14 @@ class libffi extends LinuxLibraryBase */ public function build(): void { - [$lib, , $destdir] = SEPARATED_PATH; $arch = getenv('SPC_ARCH'); - - shell()->cd($this->source_dir) - ->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - "--host={$arch}-unknown-linux " . - "--target={$arch}-unknown-linux " . - '--prefix= ' . - "--libdir={$lib}" + UnixAutoconfExecutor::create($this) + ->configure( + "--host={$arch}-unknown-linux", + "--target={$arch}-unknown-linux", + "--libdir={$this->getLibDir()}" ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + ->make(); if (is_file(BUILD_ROOT_PATH . '/lib64/libffi.a')) { copy(BUILD_ROOT_PATH . '/lib64/libffi.a', BUILD_ROOT_PATH . '/lib/libffi.a'); diff --git a/src/SPC/builder/linux/library/libmemcached.php b/src/SPC/builder/linux/library/libmemcached.php index ab393d2e..fb09b52a 100644 --- a/src/SPC/builder/linux/library/libmemcached.php +++ b/src/SPC/builder/linux/library/libmemcached.php @@ -4,17 +4,14 @@ declare(strict_types=1); namespace SPC\builder\linux\library; -use SPC\exception\RuntimeException; +use SPC\util\executor\UnixCMakeExecutor; -/** - * gmp is a template library class for unix - */ class libmemcached extends LinuxLibraryBase { public const NAME = 'libmemcached'; - public function build() + public function build(): void { - throw new RuntimeException('libmemcached is currently not supported on Linux platform'); + UnixCMakeExecutor::create($this)->build(); } } diff --git a/src/SPC/builder/linux/library/libpng.php b/src/SPC/builder/linux/library/libpng.php index 8c33ef0f..c25dd654 100644 --- a/src/SPC/builder/linux/library/libpng.php +++ b/src/SPC/builder/linux/library/libpng.php @@ -24,6 +24,7 @@ namespace SPC\builder\linux\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\executor\UnixAutoconfExecutor; class libpng extends LinuxLibraryBase { @@ -36,28 +37,22 @@ class libpng extends LinuxLibraryBase */ public function build(): void { - $optimizations = match (getenv('SPC_ARCH')) { - 'x86_64' => '--enable-intel-sse ', - 'aarch64' => '--enable-arm-neon ', - default => '', - }; - shell()->cd($this->source_dir)->initializeEnv($this) + UnixAutoconfExecutor::create($this) ->exec('chmod +x ./configure') ->exec('chmod +x ./install-sh') - ->exec( - 'LDFLAGS="-L' . BUILD_LIB_PATH . '" ' . - './configure ' . - '--disable-shared ' . - '--enable-static ' . - '--enable-hardware-optimizations ' . - '--with-zlib-prefix="' . BUILD_ROOT_PATH . '" ' . - $optimizations . - '--prefix=' + ->appendEnv(['LDFLAGS' => "-L{$this->getLibDir()}"]) + ->configure( + '--enable-hardware-optimizations', + "--with-zlib-prefix={$this->getBuildRootPath()}", + match (getenv('SPC_ARCH')) { + 'x86_64' => '--enable-intel-sse', + 'aarch64' => '--enable-arm-neon', + default => '', + } ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I{$this->source_dir} -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la") - ->exec('make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH); + ->make('libpng16.la', 'install-libLTLIBRARIES install-data-am', after_env_vars: ['DEFAULT_INCLUDES' => "-I{$this->source_dir} -I{$this->getIncludeDir()}"]); + $this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX); - $this->cleanLaFiles(); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/linux/library/openssl.php b/src/SPC/builder/linux/library/openssl.php index 11c60067..252eb47f 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -92,9 +92,9 @@ class openssl extends LinuxLibraryBase FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); } - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string + public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string { - $libFiles = parent::getStaticLibFiles($style, $recursive); + $libFiles = parent::getStaticLibFiles($style, $recursive, $include_self); if (!str_contains('-ldl -lpthread', $libFiles)) { $libFiles .= ' -ldl -lpthread'; } diff --git a/src/SPC/builder/linux/library/watcher.php b/src/SPC/builder/linux/library/watcher.php new file mode 100644 index 00000000..01bc114d --- /dev/null +++ b/src/SPC/builder/linux/library/watcher.php @@ -0,0 +1,12 @@ +getFrameworks()); } + foreach ($this->exts as $ext) { + array_push($frameworks, ...$ext->getFrameworks()); + } + if ($asString) { return implode(' ', array_map(fn ($x) => "-framework {$x}", $frameworks)); } @@ -118,6 +122,7 @@ class MacOSBuilder extends UnixBuilderBase $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; + $enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP; // prepare build php envs $mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : ''; @@ -176,7 +181,14 @@ class MacOSBuilder extends UnixBuilderBase } $this->buildEmbed(); } + if ($enableFrankenphp) { + logger()->info('building frankenphp'); + $this->buildFrankenphp(); + } + } + public function testPHP(int $build_target = BUILD_TARGET_NONE) + { $this->emitPatchPoint('before-sanity-check'); $this->sanityCheck($build_target); } @@ -192,9 +204,10 @@ class MacOSBuilder extends UnixBuilderBase $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $shell = shell()->cd(SOURCE_PATH . '/php-src'); - $shell->exec("\$SPC_CMD_PREFIX_PHP_MAKE {$vars} cli"); + $SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; + $shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli"); if (!$this->getOption('no-strip', false)) { - $shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php'); + $shell->exec('dsymutil -f sapi/cli/php')->exec('strip -S sapi/cli/php'); } $this->deployBinary(BUILD_TARGET_CLI); } @@ -221,12 +234,15 @@ class MacOSBuilder extends UnixBuilderBase // patch fake cli for micro $vars['EXTRA_CFLAGS'] .= $enable_fake_cli; - if ($this->getOption('no-strip', false)) { - $vars['STRIP'] = 'dsymutil -f '; - } $vars = SystemUtil::makeEnvVarString($vars); - shell()->cd(SOURCE_PATH . '/php-src')->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} micro"); + $shell = shell()->cd(SOURCE_PATH . '/php-src'); + // build + $shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} micro"); + // strip + if (!$this->getOption('no-strip', false)) { + $shell->exec('dsymutil -f sapi/micro/micro.sfx')->exec('strip -S sapi/micro/micro.sfx'); + } $this->deployBinary(BUILD_TARGET_MICRO); @@ -248,7 +264,7 @@ class MacOSBuilder extends UnixBuilderBase $shell = shell()->cd(SOURCE_PATH . '/php-src'); $shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm"); if (!$this->getOption('no-strip', false)) { - $shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip sapi/fpm/php-fpm'); + $shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip -S sapi/fpm/php-fpm'); } $this->deployBinary(BUILD_TARGET_FPM); } @@ -263,15 +279,12 @@ class MacOSBuilder extends UnixBuilderBase $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); shell()->cd(SOURCE_PATH . '/php-src') - ->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install") - // Workaround for https://github.com/php/php-src/issues/12082 - ->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o') - ->exec('mkdir ' . BUILD_ROOT_PATH . '/lib/php-o') - ->cd(BUILD_ROOT_PATH . '/lib/php-o') - ->exec('ar x ' . BUILD_ROOT_PATH . '/lib/libphp.a') - ->exec('rm ' . BUILD_ROOT_PATH . '/lib/libphp.a') - ->exec('ar rcs ' . BUILD_ROOT_PATH . '/lib/libphp.a *.o') - ->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o'); + ->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install"); + + if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') { + shell()->cd(SOURCE_PATH . '/php-src') + ->exec('ar -t ' . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 ar d " . BUILD_LIB_PATH . '/libphp.a'); + } $this->patchPhpScripts(); } diff --git a/src/SPC/builder/macos/library/fastlz.php b/src/SPC/builder/macos/library/fastlz.php new file mode 100644 index 00000000..db0f517a --- /dev/null +++ b/src/SPC/builder/macos/library/fastlz.php @@ -0,0 +1,12 @@ +cd($this->source_dir) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - "--host={$arch}-apple-darwin " . - "--target={$arch}-apple-darwin " . - '--prefix= ' // use prefix=/ + UnixAutoconfExecutor::create($this) + ->configure( + "--host={$arch}-apple-darwin", + "--target={$arch}-apple-darwin", ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + ->make(); $this->patchPkgconfPrefix(['libffi.pc']); } } diff --git a/src/SPC/builder/macos/library/libheif.php b/src/SPC/builder/macos/library/libheif.php index e785809d..af99740b 100644 --- a/src/SPC/builder/macos/library/libheif.php +++ b/src/SPC/builder/macos/library/libheif.php @@ -4,24 +4,9 @@ declare(strict_types=1); namespace SPC\builder\macos\library; -use SPC\store\FileSystem; - class libheif extends MacOSLibraryBase { use \SPC\builder\unix\library\libheif; public const NAME = 'libheif'; - - public function patchBeforeBuild(): bool - { - if (!str_contains(file_get_contents($this->source_dir . '/CMakeLists.txt'), 'libbrotlienc')) { - FileSystem::replaceFileStr( - $this->source_dir . '/CMakeLists.txt', - 'list(APPEND REQUIRES_PRIVATE "libbrotlidec")', - 'list(APPEND REQUIRES_PRIVATE "libbrotlidec")' . "\n" . ' list(APPEND REQUIRES_PRIVATE "libbrotlienc")' - ); - return true; - } - return false; - } } diff --git a/src/SPC/builder/macos/library/libmemcached.php b/src/SPC/builder/macos/library/libmemcached.php index 09a600a2..b212b487 100644 --- a/src/SPC/builder/macos/library/libmemcached.php +++ b/src/SPC/builder/macos/library/libmemcached.php @@ -4,28 +4,14 @@ declare(strict_types=1); namespace SPC\builder\macos\library; -/** - * gmp is a template library class for unix - */ +use SPC\util\executor\UnixCMakeExecutor; + class libmemcached extends MacOSLibraryBase { public const NAME = 'libmemcached'; public function build(): void { - $rootdir = BUILD_ROOT_PATH; - - shell()->cd($this->source_dir) - ->exec('chmod +x configure') - ->exec( - './configure ' . - '--enable-static --disable-shared ' . - '--disable-sasl ' . - "--prefix={$rootdir}" - ) - ->exec('make clean') - ->exec('sed -ie "s/-Werror//g" ' . $this->source_dir . '/Makefile') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install'); + UnixCMakeExecutor::create($this)->build(); } } diff --git a/src/SPC/builder/macos/library/libpng.php b/src/SPC/builder/macos/library/libpng.php index b61cc898..0fec806e 100644 --- a/src/SPC/builder/macos/library/libpng.php +++ b/src/SPC/builder/macos/library/libpng.php @@ -24,6 +24,7 @@ namespace SPC\builder\macos\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\executor\UnixAutoconfExecutor; class libpng extends MacOSLibraryBase { @@ -36,29 +37,25 @@ class libpng extends MacOSLibraryBase */ protected function build(): void { - $optimizations = match (php_uname('m')) { - 'x86_64' => '--enable-intel-sse ', - 'arm64' => '--enable-arm-neon ', - default => '', - }; - shell()->cd($this->source_dir) + $arch = arch2gnu(php_uname('m')); + UnixAutoconfExecutor::create($this) ->exec('chmod +x ./configure') ->exec('chmod +x ./install-sh') - ->exec( - './configure ' . - '--host=' . arch2gnu(php_uname('m')) . '-apple-darwin ' . - '--disable-shared ' . - '--enable-static ' . - '--enable-hardware-optimizations ' . - $optimizations . - '--prefix=' + ->appendEnv(['LDFLAGS' => "-L{$this->getLibDir()}"]) + ->configure( + "--host={$arch}-apple-darwin", + '--enable-hardware-optimizations', + "--with-zlib-prefix={$this->getBuildRootPath()}", + match (getenv('SPC_ARCH')) { + 'x86_64' => '--enable-intel-sse', + 'aarch64' => '--enable-arm-neon', + default => '', + } ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I. -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la") - ->exec('make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH) - ->cd(BUILD_LIB_PATH) - ->exec('ln -sf libpng16.a libpng.a'); + ->make('libpng16.la', 'install-libLTLIBRARIES install-data-am', after_env_vars: ['DEFAULT_INCLUDES' => "-I{$this->source_dir} -I{$this->getIncludeDir()}"]); + + shell()->cd(BUILD_LIB_PATH)->exec('ln -sf libpng16.a libpng.a'); $this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX); - $this->cleanLaFiles(); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/macos/library/watcher.php b/src/SPC/builder/macos/library/watcher.php new file mode 100644 index 00000000..ef88c0ed --- /dev/null +++ b/src/SPC/builder/macos/library/watcher.php @@ -0,0 +1,12 @@ +getDependencies(recursive: true))); } @@ -84,19 +84,34 @@ trait UnixLibraryTrait } } - /** - * remove libtool archive files - * - * @throws FileSystemException - * @throws WrongUsageException - */ - public function cleanLaFiles(): void + public function patchLaDependencyPrefix(?array $files = null): void { - foreach ($this->getStaticLibs() as $lib) { - $filename = pathinfo($lib, PATHINFO_FILENAME) . '.la'; - if (file_exists(BUILD_LIB_PATH . '/' . $filename)) { - unlink(BUILD_LIB_PATH . '/' . $filename); + logger()->info('Patching library [' . static::NAME . '] la files'); + $throwOnMissing = true; + if ($files === null) { + $files = $this->getStaticLibs(); + $files = array_map(fn ($name) => str_replace('.a', '.la', $name), $files); + $throwOnMissing = false; + } + foreach ($files as $name) { + $realpath = realpath(BUILD_LIB_PATH . '/' . $name); + if ($realpath === false) { + if ($throwOnMissing) { + throw new RuntimeException('Cannot find library [' . static::NAME . '] la file [' . $name . '] !'); + } + logger()->warning('Cannot find library [' . static::NAME . '] la file [' . $name . '] !'); + continue; } + logger()->debug('Patching ' . $realpath); + // replace prefix + $file = FileSystem::readFile($realpath); + $file = str_replace( + ' /lib/', + ' ' . BUILD_LIB_PATH . '/', + $file + ); + $file = preg_replace('/^libdir=.*$/m', "libdir='" . BUILD_LIB_PATH . "'", $file); + FileSystem::writeFile($realpath, $file); } } diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index e44b4476..0d56b0eb 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -12,6 +12,8 @@ use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; +use SPC\store\CurlHook; +use SPC\store\Downloader; use SPC\store\FileSystem; use SPC\util\DependencyUtil; use SPC\util\SPCConfigUtil; @@ -153,13 +155,13 @@ abstract class UnixBuilderBase extends BuilderBase // sanity check for php-cli if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { logger()->info('running cli sanity check'); - [$ret, $output] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n -r "echo \"hello\";"'); + [$ret, $output] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -r "echo \"hello\";"'); $raw_output = implode('', $output); if ($ret !== 0 || trim($raw_output) !== 'hello') { throw new RuntimeException("cli failed sanity check: ret[{$ret}]. out[{$raw_output}]"); } - foreach ($this->getExts(false) as $ext) { + foreach ($this->getExts() as $ext) { logger()->debug('testing ext: ' . $ext->getName()); $ext->runCliCheckUnix(); } @@ -218,6 +220,21 @@ abstract class UnixBuilderBase extends BuilderBase throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output)); } } + + // sanity check for frankenphp + if (($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) { + logger()->info('running frankenphp sanity check'); + $frankenphp = BUILD_BIN_PATH . '/frankenphp'; + if (!file_exists($frankenphp)) { + throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp); + } + [$ret, $output] = shell() + ->setEnv(['LD_LIBRARY_PATH' => BUILD_LIB_PATH]) + ->execWithResult("{$frankenphp} version"); + if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) { + throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']'); + } + } } /** @@ -236,8 +253,8 @@ abstract class UnixBuilderBase extends BuilderBase default => throw new RuntimeException('Deployment does not accept type ' . $type), }; logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); - FileSystem::createDir(BUILD_ROOT_PATH . '/bin'); - shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '/bin/')); + FileSystem::createDir(BUILD_BIN_PATH); + shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_BIN_PATH)); return true; } @@ -263,6 +280,7 @@ abstract class UnixBuilderBase extends BuilderBase logger()->debug('Patching phpize prefix'); FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'"); FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#'); + FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', 'test "[$]$1" = "no" && $1=yes', '# test "[$]$1" = "no" && $1=yes'); } // patch php-config if (file_exists(BUILD_BIN_PATH . '/php-config')) { @@ -276,4 +294,70 @@ abstract class UnixBuilderBase extends BuilderBase FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); } } + + /** + * @throws WrongUsageException + * @throws RuntimeException + */ + protected function buildFrankenphp(): void + { + $os = match (PHP_OS_FAMILY) { + 'Linux' => 'linux', + 'Windows' => 'win', + 'Darwin' => 'macos', + 'BSD' => 'freebsd', + default => throw new RuntimeException('Unsupported OS: ' . PHP_OS_FAMILY), + }; + $arch = arch2gnu(php_uname('m')); + + // define executables for go and xcaddy + $xcaddy_exec = PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}/bin/xcaddy"; + + $nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : ''; + $nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : ''; + $xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES'); + // make it possible to build from a different frankenphp directory! + if (!str_contains($xcaddyModules, '--with github.com/dunglas/frankenphp')) { + $xcaddyModules = '--with github.com/dunglas/frankenphp ' . $xcaddyModules; + } + if ($this->getLib('brotli') === null && str_contains($xcaddyModules, '--with github.com/dunglas/caddy-cbrotli')) { + logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.'); + $xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules); + } + $lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : ''; + $releaseInfo = json_decode(Downloader::curlExec('https://api.github.com/repos/php/frankenphp/releases/latest', retries: 3, hooks: [[CurlHook::class, 'setupGithubToken']]), true); + $frankenPhpVersion = $releaseInfo['tag_name']; + $libphpVersion = $this->getPHPVersion(); + if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { + $libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion); + } + $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; + $extLdFlags = "-extldflags '-pie'"; + $muslTags = ''; + if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { + $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; + $muslTags = 'static_build,'; + } + + $config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list, with_dependencies: true); + + $env = [ + 'PATH' => PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}/bin:" . getenv('PATH'), + 'GOROOT' => PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}", + 'GOBIN' => PKG_ROOT_PATH . "/go-xcaddy-{$arch}-{$os}/bin", + 'GOPATH' => PKG_ROOT_PATH . '/go', + 'CGO_ENABLED' => '1', + 'CGO_CFLAGS' => $config['cflags'], + 'CGO_LDFLAGS' => "{$config['ldflags']} {$config['libs']} {$lrt}", + 'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' . + '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags . + '-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' . + "{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . + "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", + 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, + ]; + shell()->cd(BUILD_BIN_PATH) + ->setEnv($env) + ->exec("{$xcaddy_exec} build --output frankenphp {$xcaddyModules}"); + } } diff --git a/src/SPC/builder/unix/library/attr.php b/src/SPC/builder/unix/library/attr.php index b6254720..439acf38 100644 --- a/src/SPC/builder/unix/library/attr.php +++ b/src/SPC/builder/unix/library/attr.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait attr { @@ -13,14 +14,11 @@ trait attr */ protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->appendEnv(['CFLAGS' => "-I{$this->getIncludeDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}"]) + UnixAutoconfExecutor::create($this) ->exec('libtoolize --force --copy') ->exec('./autogen.sh || autoreconf -if') - ->exec('./configure --prefix= --enable-static --disable-shared --with-pic --disable-nls') - ->exec("make -j {$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); - + ->configure('--disable-nls') + ->make(); $this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX); } } diff --git a/src/SPC/builder/unix/library/brotli.php b/src/SPC/builder/unix/library/brotli.php index 34a10086..6de9c15c 100644 --- a/src/SPC/builder/unix/library/brotli.php +++ b/src/SPC/builder/unix/library/brotli.php @@ -23,6 +23,8 @@ trait brotli ->build(); $this->patchPkgconfPrefix(['libbrotlicommon.pc', 'libbrotlidec.pc', 'libbrotlienc.pc']); + FileSystem::replaceFileLineContainsString(BUILD_LIB_PATH . '/pkgconfig/libbrotlidec.pc', 'Libs: -L${libdir} -lbrotlidec', 'Libs: -L${libdir} -lbrotlidec -lbrotlicommon'); + FileSystem::replaceFileLineContainsString(BUILD_LIB_PATH . '/pkgconfig/libbrotlienc.pc', 'Libs: -L${libdir} -lbrotlienc', 'Libs: -L${libdir} -lbrotlienc -lbrotlicommon'); shell()->cd(BUILD_ROOT_PATH . '/lib')->exec('ln -sf libbrotlicommon.a libbrotli.a'); foreach (FileSystem::scanDirFiles(BUILD_ROOT_PATH . '/lib/', false, true) as $filename) { if (str_starts_with($filename, 'libbrotli') && (str_contains($filename, '.so') || str_ends_with($filename, '.dylib'))) { diff --git a/src/SPC/builder/unix/library/curl.php b/src/SPC/builder/unix/library/curl.php index 0d0b8d8d..c38201e5 100644 --- a/src/SPC/builder/unix/library/curl.php +++ b/src/SPC/builder/unix/library/curl.php @@ -24,6 +24,7 @@ trait curl ->optionalLib('libssh2', fn ($lib) => "-DLIBSSH2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DLIBSSH2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DCURL_USE_LIBSSH2=OFF') ->optionalLib('nghttp2', fn ($lib) => "-DUSE_NGHTTP2=ON -DNGHTTP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP2=OFF') ->optionalLib('nghttp3', fn ($lib) => "-DUSE_NGHTTP3=ON -DNGHTTP3_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP3_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP3=OFF') + ->optionalLib('ngtcp2', fn ($lib) => "-DUSE_NGTCP2=ON -DNGNGTCP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGNGTCP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGTCP2=OFF') ->optionalLib('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true)) ->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD')) ->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2')) diff --git a/src/SPC/builder/unix/library/fastlz.php b/src/SPC/builder/unix/library/fastlz.php new file mode 100644 index 00000000..db0b7f79 --- /dev/null +++ b/src/SPC/builder/unix/library/fastlz.php @@ -0,0 +1,22 @@ +cd($this->source_dir)->initializeEnv($this) + ->exec((getenv('CC') ?: 'cc') . ' -c -O3 -fPIC fastlz.c -o fastlz.o') + ->exec((getenv('AR') ?: 'ar') . ' rcs libfastlz.a fastlz.o'); + + if (!copy($this->source_dir . '/fastlz.h', BUILD_INCLUDE_PATH . '/fastlz.h')) { + throw new \RuntimeException('Failed to copy fastlz.h'); + } + if (!copy($this->source_dir . '/libfastlz.a', BUILD_LIB_PATH . '/libfastlz.a')) { + throw new \RuntimeException('Failed to copy libfastlz.a'); + } + } +} diff --git a/src/SPC/builder/unix/library/freetype.php b/src/SPC/builder/unix/library/freetype.php index 35e62602..581a5e75 100644 --- a/src/SPC/builder/unix/library/freetype.php +++ b/src/SPC/builder/unix/library/freetype.php @@ -38,7 +38,5 @@ trait freetype ' -L/lib ', ' -L' . BUILD_ROOT_PATH . '/lib ' ); - - $this->cleanLaFiles(); } } diff --git a/src/SPC/builder/unix/library/gettext.php b/src/SPC/builder/unix/library/gettext.php index 75f50d1b..257fa305 100644 --- a/src/SPC/builder/unix/library/gettext.php +++ b/src/SPC/builder/unix/library/gettext.php @@ -4,34 +4,34 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\util\executor\UnixAutoconfExecutor; + trait gettext { protected function build(): void { - $extra = $this->builder->getLib('ncurses') ? ('--with-libncurses-prefix=' . BUILD_ROOT_PATH . ' ') : ''; - $extra .= $this->builder->getLib('libxml2') ? ('--with-libxml2-prefix=' . BUILD_ROOT_PATH . ' ') : ''; + $autoconf = UnixAutoconfExecutor::create($this) + ->optionalLib('ncurses', "--with-libncurses-prefix={$this->getBuildRootPath()}") + ->optionalLib('libxml2', "--with-libxml2-prefix={$this->getBuildRootPath()}") + ->addConfigureArgs( + '--disable-java', + '--disable-c++', + '--with-included-gettext', + "--with-iconv-prefix={$this->getBuildRootPath()}", + ); - $zts = $this->builder->getOption('enable-zts') ? '--enable-threads=isoc+posix ' : '--disable-threads '; + // zts + if ($this->builder->getOption('enable-zts')) { + $autoconf->addConfigureArgs('--enable-threads=isoc+posix') + ->appendEnv([ + 'CFLAGS' => '-lpthread -D_REENTRANT', + 'LDFLGAS' => '-lpthread', + ]); + } else { + $autoconf->addConfigureArgs('--disable-threads'); + } - $cflags = $this->builder->getOption('enable-zts') ? '-lpthread -D_REENTRANT' : ''; - $ldflags = $this->builder->getOption('enable-zts') ? '-lpthread' : ''; - - shell()->cd($this->source_dir)->initializeEnv($this) - ->appendEnv(['CFLAGS' => $cflags, 'LDFLAGS' => $ldflags]) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--disable-java ' . - '--disable-c++ ' . - $zts . - $extra . - '--with-included-gettext ' . - '--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' . - '--prefix=' . BUILD_ROOT_PATH - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install'); + $autoconf->configure()->make(with_clean: true); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/gmp.php b/src/SPC/builder/unix/library/gmp.php index 2f4ba1a4..a8e4d15c 100644 --- a/src/SPC/builder/unix/library/gmp.php +++ b/src/SPC/builder/unix/library/gmp.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait gmp { @@ -15,15 +16,7 @@ trait gmp */ protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static --disable-shared ' . - '--prefix=' - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + UnixAutoconfExecutor::create($this)->configure()->make(); $this->patchPkgconfPrefix(['gmp.pc']); } } diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index 5ace2296..ead786a2 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -9,6 +9,7 @@ use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\executor\UnixAutoconfExecutor; trait imagemagick { @@ -18,45 +19,39 @@ trait imagemagick */ protected function build(): void { - // TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC -fPIE so we can't use openmp without depending on libgomp.so - $openmp = getenv('SPC_LIBC') === 'musl' ? '--enable-openmp' : '--disable-openmp'; - $extra = "--without-jxl --without-x {$openmp} "; - $required_libs = ''; - $optional_libs = [ - 'libzip' => 'zip', - 'libjpeg' => 'jpeg', - 'libpng' => 'png', - 'libwebp' => 'webp', - 'libxml2' => 'xml', - 'libheif' => 'heic', - 'zlib' => 'zlib', - 'xz' => 'lzma', - 'zstd' => 'zstd', - 'freetype' => 'freetype', - 'bzip2' => 'bzlib', - ]; - foreach ($optional_libs as $lib => $option) { - $extra .= $this->builder->getLib($lib) ? "--with-{$option} " : "--without-{$option} "; - if ($this->builder->getLib($lib) instanceof LinuxLibraryBase) { - $required_libs .= ' ' . $this->builder->getLib($lib)->getStaticLibFiles(); - } - } + $ac = UnixAutoconfExecutor::create($this) + ->optionalLib('libzip', ...ac_with_args('zip')) + ->optionalLib('libjpeg', ...ac_with_args('jpeg')) + ->optionalLib('libpng', ...ac_with_args('png')) + ->optionalLib('libwebp', ...ac_with_args('webp')) + ->optionalLib('libxml2', ...ac_with_args('xml')) + ->optionalLib('libheif', ...ac_with_args('heic')) + ->optionalLib('zlib', ...ac_with_args('zlib')) + ->optionalLib('xz', ...ac_with_args('lzma')) + ->optionalLib('zstd', ...ac_with_args('zstd')) + ->optionalLib('freetype', ...ac_with_args('freetype')) + ->optionalLib('bzip2', ...ac_with_args('bzlib')) + ->addConfigureArgs( + // TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so + getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10') ? '--disable-openmp' : '--enable-openmp', + '--without-jxl', + '--without-x', + ); + // special: linux musl needs `-static` $ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl'; - // libxml iconv patch - $required_libs .= $this instanceof MacOSLibraryBase ? ('-liconv') : ''; - shell()->cd($this->source_dir)->initializeEnv($this) - ->appendEnv(['LDFLAGS' => $ldflags, 'LIBS' => $required_libs, 'PKG_CONFIG' => '$PKG_CONFIG --static']) - ->exec( - './configure ' . - '--enable-static --disable-shared ' . - $extra . - '--prefix=' - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + // special: macOS needs -iconv + $libs = $this instanceof MacOSLibraryBase ? '-liconv' : ''; + + $ac->appendEnv([ + 'LDFLAGS' => $ldflags, + 'LIBS' => $libs, + 'PKG_CONFIG' => '$PKG_CONFIG --static', + ]); + + $ac->configure()->make(); + $filelist = [ 'ImageMagick.pc', 'ImageMagick-7.Q16HDRI.pc', @@ -75,5 +70,6 @@ trait imagemagick 'includearchdir=${prefix}/include/ImageMagick-7' ); } + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/ldap.php b/src/SPC/builder/unix/library/ldap.php index bad3a65b..62f7bcd7 100644 --- a/src/SPC/builder/unix/library/ldap.php +++ b/src/SPC/builder/unix/library/ldap.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\store\FileSystem; +use SPC\util\executor\UnixAutoconfExecutor; trait ldap { @@ -17,33 +18,25 @@ trait ldap protected function build(): void { - $alt = ''; - // openssl support - $alt .= $this->builder->getLib('openssl') ? '--with-tls=openssl ' : ''; - // gmp support - $alt .= $this->builder->getLib('gmp') ? '--with-mp=gmp ' : ''; - // libsodium support - $alt .= $this->builder->getLib('libsodium') ? '--with-argon2=libsodium ' : '--enable-argon2=no '; - f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config'); - f_putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig'); - shell()->cd($this->source_dir)->initializeEnv($this) - ->appendEnv(['LDFLAGS' => "-L{$this->getLibDir()}"]) - ->exec( - $this->builder->makeAutoconfFlags(AUTOCONF_CPPFLAGS) . - ' ./configure ' . - '--enable-static ' . - '--disable-shared ' . - '--disable-slapd ' . - '--without-systemd ' . - '--without-cyrus-sasl ' . - $alt . - '--prefix=' + UnixAutoconfExecutor::create($this) + ->optionalLib('openssl', '--with-tls=openssl') + ->optionalLib('gmp', '--with-mp=gmp') + ->optionalLib('libsodium', '--with-argon2=libsodium', '--enable-argon2=no') + ->addConfigureArgs( + '--disable-slapd', + '--without-systemd', + '--without-cyrus-sasl', ) - ->exec('make clean') - // remove tests and doc to prevent compile failed with error: soelim not found + ->appendEnv([ + 'LDFLAGS' => "-L{$this->getLibDir()}", + 'CPPFLAGS' => "-I{$this->getIncludeDir()}", + ]) + ->configure() ->exec('sed -i -e "s/SUBDIRS= include libraries clients servers tests doc/SUBDIRS= include libraries clients servers/g" Makefile') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + ->make(); + + FileSystem::replaceFileLineContainsString(BUILD_LIB_PATH . '/pkgconfig/ldap.pc', 'Libs: -L${libdir} -lldap', 'Libs: -L${libdir} -lldap -llber'); $this->patchPkgconfPrefix(['ldap.pc', 'lber.pc']); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/libacl.php b/src/SPC/builder/unix/library/libacl.php index 8f61be1b..c25e7336 100644 --- a/src/SPC/builder/unix/library/libacl.php +++ b/src/SPC/builder/unix/library/libacl.php @@ -7,6 +7,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\executor\UnixAutoconfExecutor; trait libacl { @@ -29,14 +30,11 @@ trait libacl */ protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->appendEnv(['CFLAGS' => "-I{$this->getIncludeDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}"]) + UnixAutoconfExecutor::create($this) ->exec('libtoolize --force --copy') ->exec('./autogen.sh || autoreconf -if') - ->exec('./configure --prefix= --enable-static --disable-shared --disable-tests --disable-nls') - ->exec("make -j {$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); - + ->configure('--disable-nls', '--disable-tests') + ->make(); $this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX); } } diff --git a/src/SPC/builder/unix/library/libavif.php b/src/SPC/builder/unix/library/libavif.php index cbec640b..dea45c6e 100644 --- a/src/SPC/builder/unix/library/libavif.php +++ b/src/SPC/builder/unix/library/libavif.php @@ -23,6 +23,5 @@ trait libavif ->build(); // patch pkgconfig $this->patchPkgconfPrefix(['libavif.pc']); - $this->cleanLaFiles(); } } diff --git a/src/SPC/builder/unix/library/libcares.php b/src/SPC/builder/unix/library/libcares.php index 61683bbf..59d9852e 100644 --- a/src/SPC/builder/unix/library/libcares.php +++ b/src/SPC/builder/unix/library/libcares.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\executor\UnixAutoconfExecutor; trait libcares { @@ -24,11 +25,7 @@ trait libcares */ protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec('./configure --prefix= --enable-static --disable-shared --disable-tests --with-pic') - ->exec("make -j {$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); - + UnixAutoconfExecutor::create($this)->configure('--disable-tests')->make(); $this->patchPkgconfPrefix(['libcares.pc'], PKGCONF_PATCH_PREFIX); } } diff --git a/src/SPC/builder/unix/library/libevent.php b/src/SPC/builder/unix/library/libevent.php index da726935..55889d1d 100644 --- a/src/SPC/builder/unix/library/libevent.php +++ b/src/SPC/builder/unix/library/libevent.php @@ -40,15 +40,19 @@ trait libevent */ protected function build(): void { - UnixCMakeExecutor::create($this) + $cmake = UnixCMakeExecutor::create($this) ->addConfigureArgs( '-DEVENT__LIBRARY_TYPE=STATIC', '-DEVENT__DISABLE_BENCHMARK=ON', '-DEVENT__DISABLE_THREAD_SUPPORT=ON', '-DEVENT__DISABLE_TESTS=ON', '-DEVENT__DISABLE_SAMPLES=ON', - ) - ->build(); + '-DEVENT__DISABLE_MBEDTLS=ON ', + ); + if (version_compare(get_cmake_version(), '4.0.0', '>=')) { + $cmake->addConfigureArgs('-DCMAKE_POLICY_VERSION_MINIMUM=3.10'); + } + $cmake->build(); $this->patchPkgconfPrefix(['libevent.pc', 'libevent_core.pc', 'libevent_extra.pc', 'libevent_openssl.pc']); diff --git a/src/SPC/builder/unix/library/libheif.php b/src/SPC/builder/unix/library/libheif.php index 131a330d..680f3321 100644 --- a/src/SPC/builder/unix/library/libheif.php +++ b/src/SPC/builder/unix/library/libheif.php @@ -6,10 +6,24 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\store\FileSystem; use SPC\util\executor\UnixCMakeExecutor; trait libheif { + public function patchBeforeBuild(): bool + { + if (!str_contains(file_get_contents($this->source_dir . '/CMakeLists.txt'), 'libbrotlienc')) { + FileSystem::replaceFileStr( + $this->source_dir . '/CMakeLists.txt', + 'list(APPEND REQUIRES_PRIVATE "libbrotlidec")', + 'list(APPEND REQUIRES_PRIVATE "libbrotlidec")' . "\n" . ' list(APPEND REQUIRES_PRIVATE "libbrotlienc")' + ); + return true; + } + return false; + } + /** * @throws RuntimeException * @throws FileSystemException diff --git a/src/SPC/builder/unix/library/libiconv.php b/src/SPC/builder/unix/library/libiconv.php index 89c8e5bf..fd68e309 100644 --- a/src/SPC/builder/unix/library/libiconv.php +++ b/src/SPC/builder/unix/library/libiconv.php @@ -4,26 +4,13 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\util\executor\UnixAutoconfExecutor; + trait libiconv { protected function build(): void { - [,,$destdir] = SEPARATED_PATH; - - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--enable-extra-encodings ' . - '--prefix=' - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . $destdir); - - if (file_exists(BUILD_BIN_PATH . '/iconv')) { - unlink(BUILD_BIN_PATH . '/iconv'); - } + UnixAutoconfExecutor::create($this)->configure('--enable-extra-encodings')->make(); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/libjpeg.php b/src/SPC/builder/unix/library/libjpeg.php index 3f8aff9e..6196f8af 100644 --- a/src/SPC/builder/unix/library/libjpeg.php +++ b/src/SPC/builder/unix/library/libjpeg.php @@ -26,6 +26,5 @@ trait libjpeg ->build(); // patch pkgconfig $this->patchPkgconfPrefix(['libjpeg.pc', 'libturbojpeg.pc']); - $this->cleanLaFiles(); } } diff --git a/src/SPC/builder/unix/library/librdkafka.php b/src/SPC/builder/unix/library/librdkafka.php index 5ee4c0c8..32f389e1 100644 --- a/src/SPC/builder/unix/library/librdkafka.php +++ b/src/SPC/builder/unix/library/librdkafka.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait librdkafka { @@ -15,25 +16,33 @@ trait librdkafka */ protected function build(): void { - $builddir = BUILD_ROOT_PATH; - - $zstd_option = $this->builder->getLib('zstd') ? ("STATIC_LIB_libzstd={$builddir}/lib/libzstd.a ") : ''; - shell()->cd($this->source_dir) - ->exec( - $zstd_option . - './configure ' . - '--enable-static --disable-shared --disable-curl --disable-sasl --disable-valgrind --disable-zlib --disable-ssl ' . - ($zstd_option == '' ? '--disable-zstd ' : '') . - '--prefix=' + UnixAutoconfExecutor::create($this) + ->optionalLib( + 'zstd', + function ($lib) { + putenv("STATIC_LIB_libzstd={$lib->getLibDir()}/libzstd.a"); + return ''; + }, + '--disable-zstd' ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec("make install DESTDIR={$builddir}"); + ->removeConfigureArgs( + '--with-pic', + '--enable-pic', + ) + ->configure( + '--disable-curl', + '--disable-sasl', + '--disable-valgrind', + '--disable-zlib', + '--disable-ssl', + ) + ->make(); + $this->patchPkgconfPrefix(['rdkafka.pc', 'rdkafka-static.pc', 'rdkafka++.pc', 'rdkafka++-static.pc']); // remove dynamic libs shell() - ->exec("rm -rf {$builddir}/lib/*.so.*") - ->exec("rm -rf {$builddir}/lib/*.so") - ->exec("rm -rf {$builddir}/lib/*.dylib"); + ->exec("rm -rf {$this->getLibDir()}/*.so.*") + ->exec("rm -rf {$this->getLibDir()}/*.so") + ->exec("rm -rf {$this->getLibDir()}/*.dylib"); } } diff --git a/src/SPC/builder/unix/library/libsodium.php b/src/SPC/builder/unix/library/libsodium.php index b24d52b7..441166c9 100644 --- a/src/SPC/builder/unix/library/libsodium.php +++ b/src/SPC/builder/unix/library/libsodium.php @@ -4,16 +4,13 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\util\executor\UnixAutoconfExecutor; + trait libsodium { protected function build(): void { - shell()->cd($this->source_dir) - ->exec('./configure --enable-static --disable-shared --prefix=') - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); - + UnixAutoconfExecutor::create($this)->configure()->make(); $this->patchPkgconfPrefix(['libsodium.pc'], PKGCONF_PATCH_PREFIX); } } diff --git a/src/SPC/builder/unix/library/libtiff.php b/src/SPC/builder/unix/library/libtiff.php index 17862a79..f615019a 100644 --- a/src/SPC/builder/unix/library/libtiff.php +++ b/src/SPC/builder/unix/library/libtiff.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait libtiff { @@ -15,30 +16,26 @@ trait libtiff */ protected function build(): void { - // zlib - $extra_libs = '--enable-zlib --with-zlib-include-dir=' . BUILD_ROOT_PATH . '/include --with-zlib-lib-dir=' . BUILD_ROOT_PATH . '/lib'; - // libjpeg - $extra_libs .= ' --enable-jpeg --disable-old-jpeg --disable-jpeg12 --with-jpeg-include-dir=' . BUILD_ROOT_PATH . '/include --with-jpeg-lib-dir=' . BUILD_ROOT_PATH . '/lib'; - // We disabled lzma, zstd, webp, libdeflate by default to reduce the size of the binary - $extra_libs .= ' --disable-lzma --disable-zstd --disable-webp --disable-libdeflate'; - - $shell = shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static --disable-shared ' . - "{$extra_libs} " . - '--disable-cxx ' . - '--prefix=' - ); - - // TODO: Remove this check when https://gitlab.com/libtiff/libtiff/-/merge_requests/635 will be merged and released - if (file_exists($this->source_dir . '/html')) { - $shell->exec('make clean'); - } - - $shell - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + UnixAutoconfExecutor::create($this) + ->configure( + // zlib deps + '--enable-zlib', + "--with-zlib-include-dir={$this->getIncludeDir()}", + "--with-zlib-lib-dir={$this->getLibDir()}", + // libjpeg deps + '--enable-jpeg', + '--disable-old-jpeg', + '--disable-jpeg12', + "--with-jpeg-include-dir={$this->getIncludeDir()}", + "--with-jpeg-lib-dir={$this->getLibDir()}", + // We disabled lzma, zstd, webp, libdeflate by default to reduce the size of the binary + '--disable-lzma', + '--disable-zstd', + '--disable-webp', + '--disable-libdeflate', + '--disable-cxx', + ) + ->make(); $this->patchPkgconfPrefix(['libtiff-4.pc']); } } diff --git a/src/SPC/builder/unix/library/libwebp.php b/src/SPC/builder/unix/library/libwebp.php index e4f45eb4..32331ea2 100644 --- a/src/SPC/builder/unix/library/libwebp.php +++ b/src/SPC/builder/unix/library/libwebp.php @@ -24,8 +24,6 @@ trait libwebp // patch pkgconfig $this->patchPkgconfPrefix(['libsharpyuv.pc', 'libwebp.pc', 'libwebpdecoder.pc', 'libwebpdemux.pc', 'libwebpmux.pc'], PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR); $this->patchPkgconfPrefix(['libsharpyuv.pc'], PKGCONF_PATCH_CUSTOM, ['/^includedir=.*$/m', 'includedir=${prefix}/include/webp']); - $this->cleanLaFiles(); - // fix imagemagick binary linking issue $this->patchPkgconfPrefix(['libwebp.pc'], PKGCONF_PATCH_CUSTOM, ['/-lwebp$/m', '-lwebp -lsharpyuv']); } } diff --git a/src/SPC/builder/unix/library/libxml2.php b/src/SPC/builder/unix/library/libxml2.php index 72a418ef..85a6e8b6 100644 --- a/src/SPC/builder/unix/library/libxml2.php +++ b/src/SPC/builder/unix/library/libxml2.php @@ -17,7 +17,13 @@ trait libxml2 public function build(): void { $cmake = UnixCMakeExecutor::create($this) - ->optionalLib('zlib', "-DLIBXML2_WITH_ZLIB=ON -DZLIB_LIBRARY={$this->getLibDir()}/libz.a -DZLIB_INCLUDE_DIR={$this->getIncludeDir()}", '-DLIBXML2_WITH_ZLIB=OFF') + ->optionalLib( + 'zlib', + '-DLIBXML2_WITH_ZLIB=ON ' . + "-DZLIB_LIBRARY={$this->getLibDir()}/libz.a " . + "-DZLIB_INCLUDE_DIR={$this->getIncludeDir()}", + '-DLIBXML2_WITH_ZLIB=OFF', + ) ->optionalLib('icu', ...cmake_boolean_args('LIBXML2_WITH_ICU')) ->optionalLib('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA')) ->addConfigureArgs( @@ -35,8 +41,13 @@ trait libxml2 FileSystem::replaceFileStr( BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc', - '-licudata -licui18n -licuuc', - '-licui18n -licuuc -licudata' + '-lxml2 -liconv', + '-lxml2' + ); + FileSystem::replaceFileStr( + BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc', + '-lxml2', + '-lxml2 -liconv' ); } } diff --git a/src/SPC/builder/unix/library/libxslt.php b/src/SPC/builder/unix/library/libxslt.php index b241d715..c820f8f6 100644 --- a/src/SPC/builder/unix/library/libxslt.php +++ b/src/SPC/builder/unix/library/libxslt.php @@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\executor\UnixAutoconfExecutor; trait libxslt { @@ -18,34 +19,26 @@ trait libxslt */ protected function build(): void { - $required_libs = ''; - foreach ($this->getDependencies() as $dep) { - if ($dep instanceof LinuxLibraryBase) { - $required_libs .= ' ' . $dep->getStaticLibFiles(); - } - } - shell()->cd($this->source_dir)->initializeEnv($this) + $static_libs = $this instanceof LinuxLibraryBase ? $this->getStaticLibFiles(include_self: false) : ''; + $ac = UnixAutoconfExecutor::create($this) ->appendEnv([ 'CFLAGS' => "-I{$this->getIncludeDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}", - 'LIBS' => "{$required_libs} -lstdc++", + 'LIBS' => "{$static_libs} -lstdc++", ]) - ->exec( - "{$this->builder->getOption('library_path')} " . - "{$this->builder->getOption('ld_library_path')} " . - './configure ' . - '--enable-static --disable-shared ' . - '--without-python ' . - '--without-mem-debug ' . - '--without-crypto ' . - '--without-debug ' . - '--without-debugger ' . - '--with-libxml-prefix=' . escapeshellarg(BUILD_ROOT_PATH) . ' ' . - '--prefix=' - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . escapeshellarg(BUILD_ROOT_PATH)); + ->addConfigureArgs( + '--without-python', + '--without-crypto', + '--without-debug', + '--without-debugger', + "--with-libxml-prefix={$this->getBuildRootPath()}", + ); + $ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make(); + $this->patchPkgconfPrefix(['libexslt.pc']); + $this->patchLaDependencyPrefix(); + shell()->cd(BUILD_LIB_PATH) + ->exec("ar -t libxslt.a | grep '\\.a$' | xargs -n1 ar d libxslt.a") + ->exec("ar -t libexslt.a | grep '\\.a$' | xargs -n1 ar d libexslt.a"); } } diff --git a/src/SPC/builder/unix/library/libyaml.php b/src/SPC/builder/unix/library/libyaml.php index 47ce3fb3..12ab3915 100644 --- a/src/SPC/builder/unix/library/libyaml.php +++ b/src/SPC/builder/unix/library/libyaml.php @@ -26,6 +26,10 @@ trait libyaml protected function build(): void { - UnixCMakeExecutor::create($this)->addConfigureArgs('-DBUILD_TESTING=OFF')->build(); + $cmake = UnixCMakeExecutor::create($this)->addConfigureArgs('-DBUILD_TESTING=OFF'); + if (version_compare(get_cmake_version(), '4.0.0', '>=')) { + $cmake->addConfigureArgs('-DCMAKE_POLICY_VERSION_MINIMUM=3.5'); + } + $cmake->build(); } } diff --git a/src/SPC/builder/unix/library/libzip.php b/src/SPC/builder/unix/library/libzip.php index b8d2893f..a442129f 100644 --- a/src/SPC/builder/unix/library/libzip.php +++ b/src/SPC/builder/unix/library/libzip.php @@ -24,7 +24,6 @@ trait libzip ->addConfigureArgs( '-DENABLE_GNUTLS=OFF', '-DENABLE_MBEDTLS=OFF', - '-DBUILD_SHARED_LIBS=OFF', '-DBUILD_DOC=OFF', '-DBUILD_EXAMPLES=OFF', '-DBUILD_REGRESS=OFF', diff --git a/src/SPC/builder/unix/library/ncurses.php b/src/SPC/builder/unix/library/ncurses.php index 7d33c76a..14d2a6dc 100644 --- a/src/SPC/builder/unix/library/ncurses.php +++ b/src/SPC/builder/unix/library/ncurses.php @@ -5,37 +5,33 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\store\FileSystem; +use SPC\util\executor\UnixAutoconfExecutor; trait ncurses { protected function build(): void { $filelist = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true); - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--enable-overwrite ' . - '--with-curses-h ' . - '--enable-pc-files ' . - '--enable-echo ' . - '--disable-widec ' . - '--with-normal ' . - '--with-ticlib ' . - '--without-tests ' . - '--without-dlsym ' . - '--without-debug ' . - '-enable-symlinks ' . - '--bindir=' . BUILD_ROOT_PATH . '/bin ' . - '--includedir=' . BUILD_ROOT_PATH . '/include ' . - '--libdir=' . BUILD_ROOT_PATH . '/lib ' . - '--prefix=' . BUILD_ROOT_PATH - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install'); + UnixAutoconfExecutor::create($this) + ->configure( + '--enable-overwrite', + '--with-curses-h', + '--enable-pc-files', + '--enable-echo', + '--disable-widec', + '--with-normal', + '--with-ticlib', + '--without-tests', + '--without-dlsym', + '--without-debug', + '-enable-symlinks', + "--bindir={$this->getBinDir()}", + "--includedir={$this->getIncludeDir()}", + "--libdir={$this->getLibDir()}", + "--prefix={$this->getBuildRootPath()}", + ) + ->make(); $final = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true); // Remove the new files $new_files = array_diff($final, $filelist); diff --git a/src/SPC/builder/unix/library/nghttp2.php b/src/SPC/builder/unix/library/nghttp2.php index 03c883bf..d54a47da 100644 --- a/src/SPC/builder/unix/library/nghttp2.php +++ b/src/SPC/builder/unix/library/nghttp2.php @@ -7,6 +7,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\executor\UnixAutoconfExecutor; trait nghttp2 { @@ -17,42 +18,33 @@ trait nghttp2 */ protected function build(): void { - $args = $this->builder->makeAutoconfArgs(static::NAME, [ - 'zlib' => null, - 'openssl' => null, - 'libxml2' => null, - 'libev' => null, - 'libcares' => null, - 'libngtcp2' => null, - 'libnghttp3' => null, - 'libbpf' => null, - 'libevent-openssl' => null, - 'jansson' => null, - 'jemalloc' => null, - 'systemd' => null, - ]); - if ($brotli = $this->builder->getLib('brotli')) { - /* @phpstan-ignore-next-line */ - $args .= ' --with-libbrotlidec=yes LIBBROTLIDEC_CFLAGS="-I' . BUILD_ROOT_PATH . '/include" LIBBROTLIDEC_LIBS="' . $brotli->getStaticLibFiles() . '"'; - /* @phpstan-ignore-next-line */ - $args .= ' --with-libbrotlienc=yes LIBBROTLIENC_CFLAGS="-I' . BUILD_ROOT_PATH . '/include" LIBBROTLIENC_LIBS="' . $brotli->getStaticLibFiles() . '"'; - } - - [,,$destdir] = SEPARATED_PATH; - - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--with-pic ' . - '--enable-lib-only ' . - $args . ' ' . - '--prefix=' + UnixAutoconfExecutor::create($this) + ->optionalLib('zlib', ...ac_with_args('zlib', true)) + ->optionalLib('openssl', ...ac_with_args('openssl', true)) + ->optionalLib('libxml2', ...ac_with_args('libxml2', true)) + ->optionalLib('libev', ...ac_with_args('libev', true)) + ->optionalLib('libcares', ...ac_with_args('libcares', true)) + ->optionalLib('ngtcp2', ...ac_with_args('libngtcp2', true)) + ->optionalLib('nghttp3', ...ac_with_args('libnghttp3', true)) + // ->optionalLib('libbpf', ...ac_with_args('libbpf', true)) + // ->optionalLib('libevent-openssl', ...ac_with_args('libevent-openssl', true)) + // ->optionalLib('jansson', ...ac_with_args('jansson', true)) + // ->optionalLib('jemalloc', ...ac_with_args('jemalloc', true)) + // ->optionalLib('systemd', ...ac_with_args('systemd', true)) + ->optionalLib( + 'brotli', + fn ($lib) => implode(' ', [ + '--with-brotlidec=yes', + "LIBBROTLIDEC_CFLAGS=\"-I{$lib->getIncludeDir()}\"", + "LIBBROTLIDEC_LIBS=\"{$lib->getStaticLibFiles()}\"", + '--with-libbrotlienc=yes', + "LIBBROTLIENC_CFLAGS=\"-I{$lib->getIncludeDir()}\"", + "LIBBROTLIENC_LIBS=\"{$lib->getStaticLibFiles()}\"", + ]) ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + ->configure('--enable-lib-only') + ->make(); $this->patchPkgconfPrefix(['libnghttp2.pc']); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/nghttp3.php b/src/SPC/builder/unix/library/nghttp3.php index 95cc22bf..6d9d577c 100644 --- a/src/SPC/builder/unix/library/nghttp3.php +++ b/src/SPC/builder/unix/library/nghttp3.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait nghttp3 { @@ -15,18 +16,8 @@ trait nghttp3 */ protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--with-pic ' . - '--enable-lib-only ' . - '--prefix=' - ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + UnixAutoconfExecutor::create($this)->configure('--enable-lib-only')->make(); $this->patchPkgconfPrefix(['libnghttp3.pc']); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/ngtcp2.php b/src/SPC/builder/unix/library/ngtcp2.php index 97a0a8af..df29fba3 100644 --- a/src/SPC/builder/unix/library/ngtcp2.php +++ b/src/SPC/builder/unix/library/ngtcp2.php @@ -7,6 +7,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\util\executor\UnixAutoconfExecutor; trait ngtcp2 { @@ -17,37 +18,34 @@ trait ngtcp2 */ protected function build(): void { - $args = $this->builder->makeAutoconfArgs(static::NAME, [ - 'openssl' => null, - 'libev' => null, - 'jemalloc' => null, - 'libnghttp3' => null, - ]); - if ($brotli = $this->builder->getLib('brotli')) { - /* @phpstan-ignore-next-line */ - $args .= ' --with-libbrotlidec=yes LIBBROTLIDEC_CFLAGS="-I' . BUILD_ROOT_PATH . '/include" LIBBROTLIDEC_LIBS="' . $brotli->getStaticLibFiles() . '"'; - /* @phpstan-ignore-next-line */ - $args .= ' --with-libbrotlienc=yes LIBBROTLIENC_CFLAGS="-I' . BUILD_ROOT_PATH . '/include" LIBBROTLIENC_LIBS="' . $brotli->getStaticLibFiles() . '"'; - } - - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--with-pic ' . - '--enable-lib-only ' . - $args . ' ' . - '--prefix=' + UnixAutoconfExecutor::create($this) + ->optionalLib('openssl', fn ($lib) => implode(' ', [ + '--with-openssl=yes', + "OPENSSL_LIBS=\"{$lib->getStaticLibFiles()}\"", + "OPENSSL_CFLAGS=\"-I{$lib->getIncludeDir()}\"", + ]), '--with-openssl=no') + ->optionalLib('libev', ...ac_with_args('libev', true)) + ->optionalLib('nghttp3', ...ac_with_args('libnghttp3', true)) + ->optionalLib('jemalloc', ...ac_with_args('jemalloc', true)) + ->optionalLib( + 'brotli', + fn ($lib) => implode(' ', [ + '--with-brotlidec=yes', + "LIBBROTLIDEC_CFLAGS=\"-I{$lib->getIncludeDir()}\"", + "LIBBROTLIDEC_LIBS=\"{$lib->getStaticLibFiles()}\"", + '--with-libbrotlienc=yes', + "LIBBROTLIENC_CFLAGS=\"-I{$lib->getIncludeDir()}\"", + "LIBBROTLIENC_LIBS=\"{$lib->getStaticLibFiles()}\"", + ]) ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + ->appendEnv(['PKG_CONFIG' => '$PKG_CONFIG --static']) + ->configure('--enable-lib-only') + ->make(); $this->patchPkgconfPrefix(['libngtcp2.pc', 'libngtcp2_crypto_ossl.pc']); + $this->patchLaDependencyPrefix(); // on macOS, the static library may contain other static libraries? // ld: archive member 'libssl.a' not a mach-o file in libngtcp2_crypto_ossl.a - shell()->cd(BUILD_LIB_PATH) - ->exec("ar -t libngtcp2_crypto_ossl.a | grep '\\.a$' | xargs -n1 ar d libngtcp2_crypto_ossl.a"); + shell()->cd(BUILD_LIB_PATH)->exec("ar -t libngtcp2_crypto_ossl.a | grep '\\.a$' | xargs -n1 ar d libngtcp2_crypto_ossl.a"); } } diff --git a/src/SPC/builder/unix/library/onig.php b/src/SPC/builder/unix/library/onig.php index 0ff844db..368c852b 100644 --- a/src/SPC/builder/unix/library/onig.php +++ b/src/SPC/builder/unix/library/onig.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait onig { @@ -15,13 +16,7 @@ trait onig */ protected function build(): void { - [,,$destdir] = SEPARATED_PATH; - - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec('./configure --enable-static --disable-shared --prefix=') - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + UnixAutoconfExecutor::create($this)->configure()->make(); $this->patchPkgconfPrefix(['oniguruma.pc']); } } diff --git a/src/SPC/builder/unix/library/pkgconfig.php b/src/SPC/builder/unix/library/pkgconfig.php index 51fa586b..05727f96 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -5,32 +5,27 @@ declare(strict_types=1); namespace SPC\builder\unix\library; use SPC\builder\linux\library\LinuxLibraryBase; +use SPC\util\executor\UnixAutoconfExecutor; trait pkgconfig { protected function build(): void { - $cflags = PHP_OS_FAMILY !== 'Linux' ? "{$this->builder->arch_c_flags} -Wimplicit-function-declaration -Wno-int-conversion" : ''; - $ldflags = !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--static'; - - shell()->cd($this->source_dir)->initializeEnv($this) - ->appendEnv(['CFLAGS' => $cflags, 'LDFLAGS' => $ldflags]) - ->exec( - './configure ' . - '--disable-shared ' . - '--enable-static ' . - '--with-internal-glib ' . - '--disable-host-tool ' . - '--with-pic ' . - '--prefix=' . BUILD_ROOT_PATH . ' ' . - '--without-sysroot ' . - '--without-system-include-path ' . - '--without-system-library-path ' . - '--without-pc-path' + UnixAutoconfExecutor::create($this) + ->appendEnv([ + 'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '', + 'LDFLAGS' => !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--static', + ]) + ->configure( + '--with-internal-glib', + '--disable-host-tool', + '--without-sysroot', + '--without-system-include-path', + '--without-system-library-path', + '--without-pc-path', ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install-exec'); + ->make(with_install: 'install-exec'); + shell()->exec('strip ' . BUILD_ROOT_PATH . '/bin/pkg-config'); } } diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php index 7885eca3..1de3e4c2 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -22,7 +22,7 @@ trait postgresql $packages = 'zlib openssl readline libxml-2.0'; $optional_packages = [ 'zstd' => 'libzstd', - // 'ldap' => 'ldap', + 'ldap' => 'ldap', 'libxslt' => 'libxslt', 'icu' => 'icu-i18n', ]; @@ -39,10 +39,12 @@ trait postgresql $output = shell()->execWithResult("pkg-config --cflags-only-I --static {$packages}"); $error_exec_cnt += $output[0] === 0 ? 0 : 1; + $macos_15_bug_cflags = PHP_OS_FAMILY === 'Darwin' ? ' -Wno-unguarded-availability-new' : ''; + $cflags = ''; if (!empty($output[1][0])) { - $cppflags = $output[1][0]; - $macos_15_bug_cflags = PHP_OS_FAMILY === 'Darwin' ? ' -Wno-unguarded-availability-new' : ''; - $envs .= " CPPFLAGS=\"{$cppflags} -fPIC -fPIE -fno-ident{$macos_15_bug_cflags}\""; + $cflags = $output[1][0]; + $envs .= ' CPPFLAGS="-DPIC"'; + $cflags = "{$cflags} -fno-ident{$macos_15_bug_cflags}"; } $output = shell()->execWithResult("pkg-config --libs-only-L --static {$packages}"); $error_exec_cnt += $output[0] === 0 ? 0 : 1; @@ -79,17 +81,19 @@ trait postgresql } // configure - shell()->cd($this->source_dir . '/build') + shell()->cd($this->source_dir . '/build')->initializeEnv($this) + ->appendEnv(['CFLAGS' => $cflags]) ->exec( "{$envs} ../configure " . "--prefix={$builddir} " . - '--disable-thread-safety ' . + ($this->builder->getOption('enable-zts') ? '--enable-thread-safety ' : '--disable-thread-safety ') . '--enable-coverage=no ' . '--with-ssl=openssl ' . '--with-readline ' . '--with-libxml ' . ($this->builder->getLib('icu') ? '--with-icu ' : '--without-icu ') . - '--without-ldap ' . + ($this->builder->getLib('ldap') ? '--with-ldap ' : '--without-ldap ') . + // '--without-ldap ' . ($this->builder->getLib('libxslt') ? '--with-libxslt ' : '--without-libxslt ') . ($this->builder->getLib('zstd') ? '--with-zstd ' : '--without-zstd ') . '--without-lz4 ' . @@ -98,11 +102,7 @@ trait postgresql '--without-pam ' . '--without-bonjour ' . '--without-tcl ' - ); - // ($this->builder->getLib('ldap') ? '--with-ldap ' : '--without-ldap ') . - - // build - shell()->cd($this->source_dir . '/build') + ) ->exec($envs . ' make -C src/bin/pg_config install') ->exec($envs . ' make -C src/include install') ->exec($envs . ' make -C src/common install') @@ -114,6 +114,8 @@ trait postgresql ->exec("rm -rf {$builddir}/lib/*.so.*") ->exec("rm -rf {$builddir}/lib/*.so") ->exec("rm -rf {$builddir}/lib/*.dylib"); + + FileSystem::replaceFileStr(BUILD_LIB_PATH . '/pkgconfig/libpq.pc', '-lldap', '-lldap -llber'); } private function getVersion(): string diff --git a/src/SPC/builder/unix/library/qdbm.php b/src/SPC/builder/unix/library/qdbm.php index 21eff478..b7a43cdf 100644 --- a/src/SPC/builder/unix/library/qdbm.php +++ b/src/SPC/builder/unix/library/qdbm.php @@ -8,6 +8,7 @@ use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; +use SPC\util\executor\UnixAutoconfExecutor; trait qdbm { @@ -17,17 +18,9 @@ trait qdbm */ protected function build(): void { - shell()->cd($this->source_dir) - ->exec( - './configure ' . - '--enable-static --disable-shared ' . - '--prefix=' - ) - ->exec('make clean'); + $ac = UnixAutoconfExecutor::create($this)->configure(); FileSystem::replaceFileRegex($this->source_dir . '/Makefile', '/MYLIBS = libqdbm.a.*/m', 'MYLIBS = libqdbm.a'); - shell()->cd($this->source_dir) - ->exec("make -j{$this->builder->concurrency}" . ($this instanceof MacOSLibraryBase ? ' mac' : '')) - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + $ac->make($this instanceof MacOSLibraryBase ? 'mac' : ''); $this->patchPkgconfPrefix(['qdbm.pc']); } } diff --git a/src/SPC/builder/unix/library/readline.php b/src/SPC/builder/unix/library/readline.php index c0fe9078..8cfca0ab 100644 --- a/src/SPC/builder/unix/library/readline.php +++ b/src/SPC/builder/unix/library/readline.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait readline { @@ -15,18 +16,12 @@ trait readline */ protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec( - './configure ' . - '--enable-static=yes ' . - '--enable-shared=no ' . - '--prefix= ' . - '--with-curses ' . - '--enable-multibyte=yes' + UnixAutoconfExecutor::create($this) + ->configure( + '--with-curses', + '--enable-multibyte=yes', ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + ->make(); $this->patchPkgconfPrefix(['readline.pc']); } } diff --git a/src/SPC/builder/unix/library/sqlite.php b/src/SPC/builder/unix/library/sqlite.php index c6219171..8f9add24 100644 --- a/src/SPC/builder/unix/library/sqlite.php +++ b/src/SPC/builder/unix/library/sqlite.php @@ -4,15 +4,13 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\util\executor\UnixAutoconfExecutor; + trait sqlite { protected function build(): void { - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec('./configure --enable-static --disable-shared --prefix=') - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + UnixAutoconfExecutor::create($this)->configure()->make(); $this->patchPkgconfPrefix(['sqlite3.pc']); } } diff --git a/src/SPC/builder/unix/library/tidy.php b/src/SPC/builder/unix/library/tidy.php index d842dc6d..33405a47 100644 --- a/src/SPC/builder/unix/library/tidy.php +++ b/src/SPC/builder/unix/library/tidy.php @@ -16,13 +16,16 @@ trait tidy */ protected function build(): void { - UnixCMakeExecutor::create($this) + $cmake = UnixCMakeExecutor::create($this) ->setBuildDir("{$this->source_dir}/build-dir") ->addConfigureArgs( '-DSUPPORT_CONSOLE_APP=OFF', '-DBUILD_SHARED_LIB=OFF' - ) - ->build(); + ); + if (version_compare(get_cmake_version(), '4.0.0', '>=')) { + $cmake->addConfigureArgs('-DCMAKE_POLICY_VERSION_MINIMUM=3.5'); + } + $cmake->build(); $this->patchPkgconfPrefix(['tidy.pc']); } } diff --git a/src/SPC/builder/unix/library/unixodbc.php b/src/SPC/builder/unix/library/unixodbc.php index f89eb0a4..a795d244 100644 --- a/src/SPC/builder/unix/library/unixodbc.php +++ b/src/SPC/builder/unix/library/unixodbc.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait unixodbc { @@ -15,21 +16,16 @@ trait unixodbc */ protected function build(): void { - shell()->cd($this->source_dir) - ->exec( - './configure ' . - '--enable-static --disable-shared ' . - '--disable-debug ' . - '--disable-dependency-tracking ' . - '--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' . - '--with-included-ltdl ' . - '--enable-gui=no ' . - '--prefix=' + UnixAutoconfExecutor::create($this) + ->configure( + '--disable-debug', + '--disable-dependency-tracking', + "--with-libiconv-prefix={$this->getBuildRootPath()}", + '--with-included-ltdl', + '--enable-gui=no', ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + ->make(); $this->patchPkgconfPrefix(['odbc.pc', 'odbccr.pc', 'odbcinst.pc']); - $this->cleanLaFiles(); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/watcher.php b/src/SPC/builder/unix/library/watcher.php new file mode 100644 index 00000000..2463cc4f --- /dev/null +++ b/src/SPC/builder/unix/library/watcher.php @@ -0,0 +1,28 @@ +cd($this->source_dir . '/watcher-c') + ->initializeEnv($this) + ->exec(getenv('CC') . ' -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC') + ->exec(getenv('AR') . ' rcs libwatcher-c.a libwatcher-c.o'); + + copy($this->source_dir . '/watcher-c/libwatcher-c.a', BUILD_LIB_PATH . '/libwatcher-c.a'); + FileSystem::createDir(BUILD_INCLUDE_PATH . '/wtr'); + copy($this->source_dir . '/watcher-c/include/wtr/watcher-c.h', BUILD_INCLUDE_PATH . '/wtr/watcher-c.h'); + } +} diff --git a/src/SPC/builder/unix/library/xz.php b/src/SPC/builder/unix/library/xz.php index 1869f589..f127e59a 100644 --- a/src/SPC/builder/unix/library/xz.php +++ b/src/SPC/builder/unix/library/xz.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait xz { @@ -15,19 +16,14 @@ trait xz */ public function build(): void { - shell()->cd($this->source_dir) - ->exec( - './configure ' . - '--enable-static ' . - '--disable-shared ' . - '--disable-scripts ' . - '--disable-doc ' . - '--with-libiconv ' . - '--prefix=' + UnixAutoconfExecutor::create($this) + ->configure( + '--disable-scripts', + '--disable-doc', + '--with-libiconv', ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + ->make(); $this->patchPkgconfPrefix(['liblzma.pc']); + $this->patchLaDependencyPrefix(); } } diff --git a/src/SPC/builder/unix/library/zlib.php b/src/SPC/builder/unix/library/zlib.php index 8accaf66..717ec3fb 100644 --- a/src/SPC/builder/unix/library/zlib.php +++ b/src/SPC/builder/unix/library/zlib.php @@ -6,6 +6,7 @@ namespace SPC\builder\unix\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\util\executor\UnixAutoconfExecutor; trait zlib { @@ -15,13 +16,7 @@ trait zlib */ protected function build(): void { - [,,$destdir] = SEPARATED_PATH; - - shell()->cd($this->source_dir)->initializeEnv($this) - ->exec('./configure --static --prefix=') - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + UnixAutoconfExecutor::create($this)->exec("./configure --static --prefix={$this->getBuildRootPath()}")->make(); $this->patchPkgconfPrefix(['zlib.pc']); } } diff --git a/src/SPC/builder/windows/WindowsBuilder.php b/src/SPC/builder/windows/WindowsBuilder.php index 7a604ca8..c7643143 100644 --- a/src/SPC/builder/windows/WindowsBuilder.php +++ b/src/SPC/builder/windows/WindowsBuilder.php @@ -146,7 +146,10 @@ class WindowsBuilder extends BuilderBase // logger()->info('building embed'); $this->buildEmbed(); } + } + public function testPHP(int $build_target = BUILD_TARGET_NONE) + { $this->sanityCheck($build_target); } diff --git a/src/SPC/builder/windows/library/WindowsLibraryBase.php b/src/SPC/builder/windows/library/WindowsLibraryBase.php index e8de629e..076afa03 100644 --- a/src/SPC/builder/windows/library/WindowsLibraryBase.php +++ b/src/SPC/builder/windows/library/WindowsLibraryBase.php @@ -29,9 +29,9 @@ abstract class WindowsLibraryBase extends LibraryBase * @throws FileSystemException * @throws WrongUsageException */ - public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string + public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string { - $libs = [$this]; + $libs = $include_self ? [$this] : []; if ($recursive) { array_unshift($libs, ...array_values($this->getDependencies(recursive: true))); } diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index c04b3fa8..832ced97 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -80,7 +80,7 @@ abstract class BaseCommand extends Command $this->output = $output; global $ob_logger; - if ($input->getOption('debug')) { + if ($input->getOption('debug') || $output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL) { $ob_logger = new ConsoleLogger(LogLevel::DEBUG, decorated: !$input->getOption('no-ansi')); define('DEBUG_MODE', true); } else { diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index 49067b0b..21a848a5 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -32,8 +32,9 @@ class BuildPHPCommand extends BuildCommand $this->addOption('build-cli', null, null, 'Build cli SAPI'); $this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)'); $this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)'); + $this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)'); $this->addOption('build-all', null, null, 'Build all SAPI'); - $this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions'); + $this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug'); $this->addOption('disable-opcache-jit', null, null, 'disable opcache jit'); $this->addOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', $isWindows ? null : '/usr/local/etc/php'); $this->addOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', $isWindows ? null : '/usr/local/etc/php/conf.d'); @@ -61,11 +62,6 @@ class BuildPHPCommand extends BuildCommand $rule = $this->parseRules($shared_extensions); // check dynamic extension build env - // macOS must use --no-strip option - if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Darwin' && !$this->getOption('no-strip')) { - $this->output->writeln('MacOS does not support dynamic extension loading with stripped binary, please use --no-strip option!'); - return static::FAILURE; - } // linux must build with glibc if (!empty($shared_extensions) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') { $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with glibc!'); @@ -73,16 +69,17 @@ class BuildPHPCommand extends BuildCommand } $static_and_shared = array_intersect($static_extensions, $shared_extensions); if (!empty($static_and_shared)) { - $this->output->writeln('Building extensions [' . implode(',', $static_and_shared) . '] as both static and shared\, tests may not be accurate or fail.'); + $this->output->writeln('Building extensions [' . implode(',', $static_and_shared) . '] as both static and shared, tests may not be accurate or fail.'); } if ($rule === BUILD_TARGET_NONE) { $this->output->writeln('Please add at least one build SAPI!'); - $this->output->writeln("\t--build-cli\tBuild php-cli SAPI"); - $this->output->writeln("\t--build-micro\tBuild phpmicro SAPI"); - $this->output->writeln("\t--build-fpm\tBuild php-fpm SAPI"); - $this->output->writeln("\t--build-embed\tBuild embed SAPI/libphp"); - $this->output->writeln("\t--build-all\tBuild all SAPI: cli, micro, fpm, embed"); + $this->output->writeln("\t--build-cli\t\tBuild php-cli SAPI"); + $this->output->writeln("\t--build-micro\t\tBuild phpmicro SAPI"); + $this->output->writeln("\t--build-fpm\t\tBuild php-fpm SAPI"); + $this->output->writeln("\t--build-embed\t\tBuild embed SAPI/libphp"); + $this->output->writeln("\t--build-frankenphp\tBuild FrankenPHP SAPI/libphp"); + $this->output->writeln("\t--build-all\t\tBuild all SAPI: cli, micro, fpm, embed, frankenphp"); return static::FAILURE; } if ($rule === BUILD_TARGET_ALL) { @@ -128,7 +125,6 @@ class BuildPHPCommand extends BuildCommand $include_suggest_lib = $this->getOption('with-suggested-libs'); [$extensions, $libraries, $not_included] = DependencyUtil::getExtsAndLibs(array_merge($static_extensions, $shared_extensions), $libraries, $include_suggest_ext, $include_suggest_lib); $display_libs = array_filter($libraries, fn ($lib) => in_array(Config::getLib($lib, 'type', 'lib'), ['lib', 'package'])); - $display_extensions = array_map(fn ($ext) => in_array($ext, $shared_extensions) ? "*{$ext}" : $ext, $extensions); // separate static and shared extensions from $extensions // filter rule: including shared extensions if they are in $static_extensions or $shared_extensions @@ -138,7 +134,8 @@ class BuildPHPCommand extends BuildCommand $indent_texts = [ 'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')', 'Build SAPI' => $builder->getBuildTypeName($rule), - 'Extensions (' . count($extensions) . ')' => implode(',', $display_extensions), + 'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions), + 'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions), 'Libraries (' . count($libraries) . ')' => implode(',', $display_libs), 'Strip Binaries' => $builder->getOption('no-strip') ? 'no' : 'yes', 'Enable ZTS' => $builder->getOption('enable-zts') ? 'yes' : 'no', @@ -158,14 +155,9 @@ class BuildPHPCommand extends BuildCommand if ($this->input->getOption('with-upx-pack') && in_array(PHP_OS_FAMILY, ['Linux', 'Windows'])) { $indent_texts['UPX Pack'] = 'enabled'; } - try { - $ver = $builder->getPHPVersion(); - $indent_texts['PHP Version'] = $ver; - } catch (\Throwable) { - if (($ver = $builder->getPHPVersionFromArchive()) !== false) { - $indent_texts['PHP Version'] = $ver; - } - } + + $ver = $builder->getPHPVersionFromArchive() ?: $builder->getPHPVersion(); + $indent_texts['PHP Version'] = $ver; if (!empty($not_included)) { $indent_texts['Extra Exts (' . count($not_included) . ')'] = implode(', ', $not_included); @@ -183,6 +175,9 @@ class BuildPHPCommand extends BuildCommand // validate libs and extensions $builder->validateLibsAndExts(); + // check some things before building all the things + $builder->checkBeforeBuildPHP($rule); + // clean builds and sources if ($this->input->getOption('with-clean')) { logger()->info('Cleaning source and previous build dir...'); @@ -207,6 +202,8 @@ class BuildPHPCommand extends BuildCommand // add static-php-cli.version to main.c, in order to debug php failure more easily SourcePatcher::patchSPCVersionToPHP($this->getApplication()->getVersion()); + // clean old modules that may conflict with the new php build + FileSystem::removeDir(BUILD_MODULES_PATH); // start to build $builder->buildPHP($rule); @@ -216,6 +213,8 @@ class BuildPHPCommand extends BuildCommand $builder->buildSharedExts(); } + $builder->testPHP($rule); + // compile stopwatch :P $time = round(microtime(true) - START_TIME, 3); logger()->info(''); @@ -246,8 +245,12 @@ class BuildPHPCommand extends BuildCommand } if (!empty($shared_extensions)) { foreach ($shared_extensions as $ext) { - $path = FileSystem::convertPath("{$build_root_path}/lib/{$ext}.so"); - logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}"); + $path = FileSystem::convertPath("{$build_root_path}/modules/{$ext}.so"); + if (file_exists(BUILD_MODULES_PATH . "/{$ext}.so")) { + logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}"); + } else { + logger()->warning("Shared extension [{$ext}] not found, please check!"); + } } } @@ -284,7 +287,8 @@ class BuildPHPCommand extends BuildCommand $rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE); $rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE); $rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE); - $rule |= ($this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE); + $rule |= $this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE; + $rule |= ($this->getOption('build-frankenphp') ? (BUILD_TARGET_FRANKENPHP | BUILD_TARGET_EMBED) : BUILD_TARGET_NONE); $rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE); return $rule; } diff --git a/src/SPC/command/CraftCommand.php b/src/SPC/command/CraftCommand.php index 9fa2f385..c8003a3e 100644 --- a/src/SPC/command/CraftCommand.php +++ b/src/SPC/command/CraftCommand.php @@ -48,7 +48,8 @@ class CraftCommand extends BaseCommand } } - $extensions = implode(',', $craft['extensions']); + $static_extensions = implode(',', $craft['extensions']); + $shared_extensions = implode(',', $craft['shared-extensions'] ?? []); $libs = implode(',', $craft['libs']); // init log @@ -65,18 +66,26 @@ class CraftCommand extends BaseCommand return static::FAILURE; } } + // install go and xcaddy for frankenphp + if (in_array('frankenphp', $craft['sapi'])) { + $retcode = $this->runCommand('install-pkg', 'go-xcaddy'); + if ($retcode !== 0) { + $this->output->writeln('craft go-xcaddy failed'); + $this->log("craft go-xcaddy failed with code: {$retcode}", true); + return static::FAILURE; + } + } // craft download if ($craft['craft-options']['download']) { - $args = ["--for-extensions={$extensions}"]; + $sharedAppend = $shared_extensions ? ',' . $shared_extensions : ''; + $args = ["--for-extensions={$static_extensions}{$sharedAppend}"]; if ($craft['libs'] !== []) { $args[] = "--for-libs={$libs}"; } if (isset($craft['php-version'])) { $args[] = '--with-php=' . $craft['php-version']; - if (!array_key_exists('ignore-cache-sources', $craft['download-options']) || $craft['download-options']['ignore-cache-sources'] === false) { + if (!array_key_exists('ignore-cache-sources', $craft['download-options'])) { $craft['download-options']['ignore-cache-sources'] = 'php-src'; - } elseif ($craft['download-options']['ignore-cache-sources'] !== null) { - $craft['download-options']['ignore-cache-sources'] .= ',php-src'; } } $this->optionsToArguments($craft['download-options'], $args); @@ -90,7 +99,7 @@ class CraftCommand extends BaseCommand // craft build if ($craft['craft-options']['build']) { - $args = [$extensions, "--with-libs={$libs}", ...array_map(fn ($x) => "--build-{$x}", $craft['sapi'])]; + $args = [$static_extensions, "--with-libs={$libs}", "--build-shared={$shared_extensions}", ...array_map(fn ($x) => "--build-{$x}", $craft['sapi'])]; $this->optionsToArguments($craft['build-options'], $args); $retcode = $this->runCommand('build', ...$args); if ($retcode !== 0) { @@ -131,7 +140,8 @@ class CraftCommand extends BaseCommand } $prefix = PHP_SAPI === 'cli' ? [PHP_BINARY, $argv[0]] : [$argv[0]]; - $process = new Process([...$prefix, $cmd, '--no-motd', ...$args], timeout: null); + $env = getenv(); + $process = new Process([...$prefix, $cmd, '--no-motd', ...$args], env: $env, timeout: null); $this->log("Running: {$process->getCommandLine()}", true); if (PHP_OS_FAMILY === 'Windows') { @@ -142,7 +152,6 @@ class CraftCommand extends BaseCommand }); } elseif (extension_loaded('pcntl')) { pcntl_signal(SIGINT, function () use ($process) { - /* @noinspection PhpComposerExtensionStubsInspection */ $process->signal(SIGINT); }); } else { diff --git a/src/SPC/command/DeleteDownloadCommand.php b/src/SPC/command/DeleteDownloadCommand.php index b68b8618..1eeddf7e 100644 --- a/src/SPC/command/DeleteDownloadCommand.php +++ b/src/SPC/command/DeleteDownloadCommand.php @@ -9,6 +9,7 @@ use SPC\exception\FileSystemException; use SPC\exception\WrongUsageException; use SPC\store\Downloader; use SPC\store\FileSystem; +use SPC\store\LockFile; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -21,6 +22,8 @@ class DeleteDownloadCommand extends BaseCommand { $this->addArgument('sources', InputArgument::REQUIRED, 'The sources/packages will be deleted, comma separated'); $this->addOption('all', 'A', null, 'Delete all downloaded and locked sources/packages'); + $this->addOption('pre-built-only', 'W', null, 'Delete only pre-built sources/packages, not the original ones'); + $this->addOption('source-only', 'S', null, 'Delete only sources, not the pre-built packages'); } public function initialize(InputInterface $input, OutputInterface $output): void @@ -46,29 +49,30 @@ class DeleteDownloadCommand extends BaseCommand return static::SUCCESS; } $chosen_sources = $sources; - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $deleted_sources = []; foreach ($chosen_sources as $source) { $source = trim($source); - foreach ([$source, Downloader::getPreBuiltLockName($source)] as $name) { - if (isset($lock[$name])) { - $deleted_sources[] = $name; - } + if (LockFile::get($source) && !$this->getOption('pre-built-only')) { + $deleted_sources[] = $source; + } + if (LockFile::get(Downloader::getPreBuiltLockName($source)) && !$this->getOption('source-only')) { + $deleted_sources[] = Downloader::getPreBuiltLockName($source); } } foreach ($deleted_sources as $lock_name) { + $lock = LockFile::get($lock_name); // remove download file/dir if exists - if ($lock[$lock_name]['source_type'] === 'archive') { - if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']))) { + if ($lock['source_type'] === SPC_SOURCE_ARCHIVE) { + if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['filename']))) { logger()->info('Deleting file ' . $path); unlink($path); } else { logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file."); } } else { - if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname']))) { + if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['dirname']))) { logger()->info('Deleting dir ' . $path); FileSystem::removeDir($path); } else { @@ -76,9 +80,8 @@ class DeleteDownloadCommand extends BaseCommand } } // remove locked sources - unset($lock[$lock_name]); + LockFile::put($lock_name, null); } - FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); logger()->info('Delete success!'); return static::SUCCESS; } catch (DownloaderException $e) { diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index 57e83cd0..d5054aa9 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -12,6 +12,7 @@ use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\Downloader; +use SPC\store\LockFile; use SPC\util\DependencyUtil; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; @@ -301,7 +302,7 @@ class DownloadCommand extends BaseCommand throw new WrongUsageException('Windows currently does not support --from-zip !'); } - if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { + if (!file_exists(LockFile::LOCK_FILE)) { throw new RuntimeException('.lock.json not exist in "downloads/"'); } } catch (RuntimeException $e) { diff --git a/src/SPC/command/SwitchPhpVersionCommand.php b/src/SPC/command/SwitchPhpVersionCommand.php index ef463ef4..30ee0c79 100644 --- a/src/SPC/command/SwitchPhpVersionCommand.php +++ b/src/SPC/command/SwitchPhpVersionCommand.php @@ -7,6 +7,7 @@ namespace SPC\command; use SPC\store\Config; use SPC\store\Downloader; use SPC\store\FileSystem; +use SPC\store\LockFile; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -40,16 +41,9 @@ class SwitchPhpVersionCommand extends BaseCommand } } - // detect if downloads/.lock.json exists - $lock_file = DOWNLOAD_PATH . '/.lock.json'; - // parse php-src part of lock file - $lock_data = json_decode(file_get_contents($lock_file), true); - // get php-src downloaded file name - $php_src = $lock_data['php-src']; - $file = DOWNLOAD_PATH . '/' . ($php_src['filename'] ?? '.donot.delete.me'); - if (file_exists($file)) { + if (LockFile::isLockFileExists('php-src')) { $this->output->writeln('Removing old PHP source...'); - unlink($file); + LockFile::put('php-src', null); } // Download new PHP source diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php index 924c3171..d0d9797e 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -14,6 +14,7 @@ use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\util\DependencyUtil; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; @@ -47,9 +48,8 @@ class PackLibCommand extends BuildCommand $lib->setup(); } else { // Get lock info - $lock = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $source = Config::getLib($lib->getName(), 'source'); - if (!isset($lock[$source]) || ($lock[$source]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) { + if (($lock = LockFile::get($source)) === null || ($lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT)) { logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built."); return static::FAILURE; } diff --git a/src/SPC/doctor/item/LinuxMuslCheck.php b/src/SPC/doctor/item/LinuxMuslCheck.php index bd89c0d7..15516355 100644 --- a/src/SPC/doctor/item/LinuxMuslCheck.php +++ b/src/SPC/doctor/item/LinuxMuslCheck.php @@ -78,7 +78,7 @@ class LinuxMuslCheck ]; logger()->info('Downloading ' . $musl_source['url']); Downloader::downloadSource($musl_version_name, $musl_source); - FileSystem::extractSource($musl_version_name, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz"); + FileSystem::extractSource($musl_version_name, SPC_SOURCE_ARCHIVE, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz"); // Apply CVE-2025-26519 patch SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0001.patch', SOURCE_PATH . "/{$musl_version_name}"); diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index 976d5fd6..56235b0c 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -21,7 +21,8 @@ class LinuxToolCheckList 'tar', 'unzip', 'gzip', 'bzip2', 'cmake', 'gcc', 'g++', 'patch', 'binutils-gold', - 'libtoolize', + 'libtoolize', 'which', + 'patchelf', ]; public const TOOLS_DEBIAN = [ @@ -29,15 +30,17 @@ class LinuxToolCheckList 'git', 'autoconf', 'automake', 'autopoint', 'tar', 'unzip', 'gzip', 'bzip2', 'cmake', 'patch', - 'xz', 'libtoolize', + 'xz', 'libtoolize', 'which', + 'patchelf', ]; public const TOOLS_RHEL = [ 'perl', 'make', 'bison', 'flex', 'git', 'autoconf', 'automake', 'tar', 'unzip', 'gzip', 'gcc', - 'bzip2', 'cmake', 'patch', + 'bzip2', 'cmake', 'patch', 'which', 'xz', 'libtool', 'gettext-devel', + 'perl', 'patchelf', ]; public const TOOLS_ARCH = [ @@ -47,7 +50,7 @@ class LinuxToolCheckList private const PROVIDED_COMMAND = [ 'binutils-gold' => 'ld.gold', 'base-devel' => 'automake', - 'gettext-devel' => 'gettext', + 'gettext-devel' => 'gettextize', ]; /** @noinspection PhpUnused */ diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index c17e5b98..ba0cd124 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -9,6 +9,7 @@ use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; +use SPC\store\pkg\CustomPackage; use SPC\store\source\CustomSourceBase; /** @@ -208,31 +209,7 @@ class Downloader if ($download_as === SPC_DOWNLOAD_PRE_BUILT) { $name = self::getPreBuiltLockName($name); } - self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]); - } - - /** - * Try to lock source. - * - * @param string $name Source name - * @param array{ - * source_type: string, - * dirname: ?string, - * filename: ?string, - * move_path: ?string, - * lock_as: int - * } $data Source data - * @throws FileSystemException - */ - public static function lockSource(string $name, array $data): void - { - if (!file_exists(FileSystem::convertPath(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)); + LockFile::lockSource($name, ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]); } /** @@ -278,7 +255,7 @@ class Downloader } // Lock logger()->debug("Locking git source {$name}"); - self::lockSource($name, ['source_type' => 'dir', 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]); + LockFile::lockSource($name, ['source_type' => SPC_SOURCE_GIT, 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]); /* // 复制目录过去 @@ -371,11 +348,24 @@ class Downloader SPC_DOWNLOAD_PRE_BUILT ); break; + case 'local': + // Local directory, do nothing, just lock it + logger()->debug("Locking local source {$name}"); + LockFile::lockSource($name, [ + 'source_type' => SPC_SOURCE_LOCAL, + 'dirname' => $pkg['dirname'], + 'move_path' => $pkg['extract'] ?? null, + 'lock_as' => SPC_DOWNLOAD_PACKAGE, + ]); + break; case 'custom': // Custom download method, like API-based download or other - $classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source'); + $classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'); foreach ($classes as $class) { - if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) { - (new $class())->fetch($force); + if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) { + $cls = new $class(); + if (in_array($name, $cls->getSupportName())) { + (new $class())->fetch($name, $force, $pkg); + } break; } } @@ -477,6 +467,16 @@ class Downloader $download_as ); break; + case 'local': + // Local directory, do nothing, just lock it + logger()->debug("Locking local source {$name}"); + LockFile::lockSource($name, [ + 'source_type' => SPC_SOURCE_LOCAL, + 'dirname' => $source['dirname'], + 'move_path' => $source['extract'] ?? null, + 'lock_as' => $download_as, + ]); + break; case 'custom': // Custom download method, like API-based download or other if (isset($source['func']) && is_callable($source['func'])) { $source['name'] = $name; @@ -537,10 +537,10 @@ class Downloader } f_exec($cmd, $output, $ret); if ($ret === 2 || $ret === -1073741510) { - throw new RuntimeException('failed http fetch'); + throw new RuntimeException(sprintf('Failed to fetch "%s"', $url)); } if ($ret !== 0) { - throw new DownloaderException('failed http fetch'); + throw new DownloaderException(sprintf('Failed to fetch "%s"', $url)); } $cache[$cmd]['cache'] = implode("\n", $output); $cache[$cmd]['expire'] = time() + 3600; @@ -549,10 +549,10 @@ class Downloader } f_exec($cmd, $output, $ret); if ($ret === 2 || $ret === -1073741510) { - throw new RuntimeException('failed http fetch'); + throw new RuntimeException(sprintf('Failed to fetch "%s"', $url)); } if ($ret !== 0) { - throw new DownloaderException('failed http fetch'); + throw new DownloaderException(sprintf('Failed to fetch "%s"', $url)); } return implode("\n", $output); } @@ -629,33 +629,30 @@ class Downloader /** * @throws FileSystemException + * @throws WrongUsageException */ private static function isAlreadyDownloaded(string $name, bool $force, int $download_as = SPC_DOWNLOAD_SOURCE): bool { - 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 for source mode - if (!$force && $download_as === SPC_DOWNLOAD_SOURCE && isset($lock[$name])) { - if ( - $lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename']) || - $lock[$name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname']) - ) { - logger()->notice("Source [{$name}] already downloaded: " . ($lock[$name]['filename'] ?? $lock[$name]['dirname'])); + // If the lock file exists, skip downloading for source mode + $lock_item = LockFile::get($name); + if (!$force && $download_as === SPC_DOWNLOAD_SOURCE && $lock_item !== null) { + if (file_exists($path = LockFile::getLockFullPath($lock_item))) { + logger()->notice("Source [{$name}] already downloaded: {$path}"); return true; } } - // If lock file exists for current arch and glibc target, skip downloading - - if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && isset($lock[$lock_name = self::getPreBuiltLockName($name)])) { + $lock_name = self::getPreBuiltLockName($name); + $lock_item = LockFile::get($lock_name); + if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && $lock_item !== null) { // lock name with env - if ( - $lock[$lock_name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']) || - $lock[$lock_name]['source_type'] === 'dir' && is_dir(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname']) - ) { - logger()->notice("Pre-built content [{$name}] already downloaded: " . ($lock[$lock_name]['filename'] ?? $lock[$lock_name]['dirname'])); + if (file_exists($path = LockFile::getLockFullPath($lock_item))) { + logger()->notice("Pre-built content [{$name}] already downloaded: {$path}"); + return true; + } + } + if (!$force && $download_as === SPC_DOWNLOAD_PACKAGE && $lock_item !== null) { + if (file_exists($path = LockFile::getLockFullPath($lock_item))) { + logger()->notice("Source [{$name}] already downloaded: {$path}"); return true; } } diff --git a/src/SPC/store/FileSystem.php b/src/SPC/store/FileSystem.php index 16047427..d67ed6cf 100644 --- a/src/SPC/store/FileSystem.php +++ b/src/SPC/store/FileSystem.php @@ -142,7 +142,7 @@ class FileSystem * @throws RuntimeException * @throws FileSystemException */ - public static function extractPackage(string $name, string $filename, ?string $extract_path = null): void + public static function extractPackage(string $name, string $source_type, string $filename, ?string $extract_path = null): void { if ($extract_path !== null) { // replace @@ -151,14 +151,15 @@ class FileSystem } else { $extract_path = PKG_ROOT_PATH . '/' . $name; } - logger()->info("extracting {$name} package to {$extract_path} ..."); + logger()->info("Extracting {$name} package to {$extract_path} ..."); $target = self::convertPath($extract_path); if (!is_dir($dir = dirname($target))) { self::createDir($dir); } try { - self::extractArchive($filename, $target); + // extract wrapper command + self::extractWithType($source_type, $filename, $extract_path); } catch (RuntimeException $e) { if (PHP_OS_FAMILY === 'Windows') { f_passthru('rmdir /s /q ' . $target); @@ -177,24 +178,23 @@ class FileSystem * @throws FileSystemException * @throws RuntimeException */ - public static function extractSource(string $name, string $filename, ?string $move_path = null): void + public static function extractSource(string $name, string $source_type, 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; - } else { - $move_path = SOURCE_PATH . "/{$name}"; - } + $move_path = match ($move_path) { + null => SOURCE_PATH . '/' . $name, + default => self::isRelativePath($move_path) ? (SOURCE_PATH . '/' . $move_path) : $move_path, + }; $target = self::convertPath($move_path); - logger()->info("extracting {$name} source to {$target}" . ' ...'); + logger()->info("Extracting {$name} source to {$target}" . ' ...'); if (!is_dir($dir = dirname($target))) { self::createDir($dir); } try { - self::extractArchive($filename, $target); + self::extractWithType($source_type, $filename, $move_path); self::emitSourceExtractHook($name, $target); } catch (RuntimeException $e) { if (PHP_OS_FAMILY === 'Windows') { @@ -484,11 +484,6 @@ class FileSystem */ private static function extractArchive(string $filename, string $target): void { - // Git source, just move - if (is_dir(self::convertPath($filename))) { - self::copyDir(self::convertPath($filename), $target); - return; - } // Create base dir if (f_mkdir(directory: $target, recursive: true) !== true) { throw new FileSystemException('create ' . $target . ' dir failed'); @@ -553,4 +548,16 @@ class FileSystem } } } + + private static function extractWithType(string $source_type, string $filename, string $extract_path): void + { + logger()->debug('Extracting source [' . $source_type . ']: ' . $filename); + /* @phpstan-ignore-next-line */ + match ($source_type) { + SPC_SOURCE_ARCHIVE => self::extractArchive($filename, $extract_path), + SPC_SOURCE_GIT => self::copyDir(self::convertPath($filename), $extract_path), + // soft link to the local source + SPC_SOURCE_LOCAL => symlink(self::convertPath($filename), $extract_path), + }; + } } diff --git a/src/SPC/store/LockFile.php b/src/SPC/store/LockFile.php new file mode 100644 index 00000000..999f91b4 --- /dev/null +++ b/src/SPC/store/LockFile.php @@ -0,0 +1,227 @@ +warning("Lock entry for '{$lock_name}' has 'source_type' set to 'dir', which is deprecated. Please re-download your dependencies."); + $result['source_type'] = SPC_SOURCE_GIT; + } + + return $result; + } + + /** + * Check if a lock file exists for a given lock name. + * + * @param string $lock_name Lock name to check + */ + public static function isLockFileExists(string $lock_name): bool + { + return match (self::get($lock_name)['source_type'] ?? null) { + SPC_SOURCE_ARCHIVE => file_exists(DOWNLOAD_PATH . '/' . (self::get($lock_name)['filename'] ?? '.never-exist-file')), + SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => is_dir(DOWNLOAD_PATH . '/' . (self::get($lock_name)['dirname'] ?? '.never-exist-dir')), + default => false, + }; + } + + /** + * Put a lock entry into the lock file. + * + * @param string $lock_name Lock name to set or remove + * @param null|array $lock_content lock content to set, or null to remove the lock entry + * @throws FileSystemException + * @throws WrongUsageException + */ + public static function put(string $lock_name, ?array $lock_content): void + { + self::init(); + + if ($lock_content === null && isset(self::$lock_file_content[$lock_name])) { + self::removeLockFileIfExists(self::$lock_file_content[$lock_name]); + unset(self::$lock_file_content[$lock_name]); + } else { + self::$lock_file_content[$lock_name] = $lock_content; + } + + // Write the updated lock data back to the file + FileSystem::createDir(dirname(self::LOCK_FILE)); + file_put_contents(self::LOCK_FILE, json_encode(self::$lock_file_content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + + /** + * Get the full path of a lock file or directory based on the lock options. + * + * @param array $lock_options lock item options, must contain 'source_type', 'filename' or 'dirname' + * @return string the absolute path to the lock file or directory + * @throws WrongUsageException + */ + public static function getLockFullPath(array $lock_options): string + { + return match ($lock_options['source_type']) { + SPC_SOURCE_ARCHIVE => FileSystem::isRelativePath($lock_options['filename']) ? (DOWNLOAD_PATH . '/' . $lock_options['filename']) : $lock_options['filename'], + SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => FileSystem::isRelativePath($lock_options['dirname']) ? (DOWNLOAD_PATH . '/' . $lock_options['dirname']) : $lock_options['dirname'], + default => throw new WrongUsageException("Unknown source type: {$lock_options['source_type']}"), + }; + } + + public static function getExtractPath(string $lock_name, string $default_path): ?string + { + $lock = self::get($lock_name); + if ($lock === null) { + return null; + } + + // If move_path is set, use it; otherwise, use the default extract directory + if (isset($lock['move_path'])) { + if (FileSystem::isRelativePath($lock['move_path'])) { + // If move_path is relative, prepend the default extract directory + return match ($lock['lock_as']) { + SPC_DOWNLOAD_SOURCE, SPC_DOWNLOAD_PRE_BUILT => FileSystem::convertPath(SOURCE_PATH . '/' . $lock['move_path']), + SPC_DOWNLOAD_PACKAGE => FileSystem::convertPath(PKG_ROOT_PATH . '/' . $lock['move_path']), + default => throw new WrongUsageException("Unknown lock type: {$lock['lock_as']}"), + }; + } + return FileSystem::convertPath($lock['move_path']); + } + return FileSystem::convertPath($default_path); + } + + /** + * Get the hash of the lock source based on the lock options. + * + * @param array $lock_options Lock options + * @return string Hash of the lock source + * @throws RuntimeException + */ + public static function getLockSourceHash(array $lock_options): string + { + $result = match ($lock_options['source_type']) { + SPC_SOURCE_ARCHIVE => sha1_file(DOWNLOAD_PATH . '/' . $lock_options['filename']), + SPC_SOURCE_GIT => exec('cd ' . escapeshellarg(DOWNLOAD_PATH . '/' . $lock_options['dirname']) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD'), + SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT', + default => filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN) ? '' : throw new RuntimeException("Unknown source type: {$lock_options['source_type']}"), + }; + if ($result === false && !filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN)) { + throw new RuntimeException("Failed to get hash for source: {$lock_options['source_type']}"); + } + return $result ?: ''; + } + + /** + * @param array $lock_options Lock options + * @param string $destination Target directory + * @throws FileSystemException + * @throws RuntimeException + */ + public static function putLockSourceHash(array $lock_options, string $destination): void + { + $hash = LockFile::getLockSourceHash($lock_options); + if ($lock_options['source_type'] === SPC_SOURCE_LOCAL) { + logger()->debug("Source [{$lock_options['dirname']}] is local, no hash will be written."); + return; + } + FileSystem::writeFile("{$destination}/.spc-hash", $hash); + } + + /** + * Try to lock source with hash. + * + * @param string $name Source name + * @param array{ + * source_type: string, + * dirname: ?string, + * filename: ?string, + * move_path: ?string, + * lock_as: int + * } $data Source data + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + public static function lockSource(string $name, array $data): void + { + // calculate hash + $hash = LockFile::getLockSourceHash($data); + $data['hash'] = $hash; + self::put($name, $data); + } + + private static function init(): void + { + if (self::$lock_file_content === null) { + // Initialize the lock file content if it hasn't been loaded yet + if (!file_exists(self::LOCK_FILE)) { + logger()->debug('Lock file does not exist: ' . self::LOCK_FILE . ', initializing empty lock file.'); + self::$lock_file_content = []; + file_put_contents(self::LOCK_FILE, json_encode(self::$lock_file_content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } else { + $file_content = file_get_contents(self::LOCK_FILE); + self::$lock_file_content = json_decode($file_content, true); + if (self::$lock_file_content === null) { + throw new \RuntimeException('Failed to decode lock file: ' . self::LOCK_FILE); + } + } + } + } + + /** + * Remove the lock file or directory if it exists. + * + * @param array $lock_options lock item options, must contain 'source_type', 'filename' or 'dirname' + * @throws WrongUsageException + * @throws FileSystemException + */ + private static function removeLockFileIfExists(array $lock_options): void + { + if ($lock_options['source_type'] === SPC_SOURCE_ARCHIVE) { + $path = self::getLockFullPath($lock_options); + if (file_exists($path)) { + logger()->info('Removing file ' . $path); + unlink($path); + } else { + logger()->debug("Lock file [{$lock_options['filename']}] not found, skip removing file."); + } + } else { + $path = self::getLockFullPath($lock_options); + if (is_dir($path)) { + logger()->info('Removing directory ' . $path); + FileSystem::removeDir($path); + } else { + logger()->debug("Lock directory [{$lock_options['dirname']}] not found, skip removing directory."); + } + } + } +} diff --git a/src/SPC/store/PackageManager.php b/src/SPC/store/PackageManager.php index 6ff2faef..d5c1043d 100644 --- a/src/SPC/store/PackageManager.php +++ b/src/SPC/store/PackageManager.php @@ -6,6 +6,7 @@ namespace SPC\store; use SPC\exception\FileSystemException; use SPC\exception\WrongUsageException; +use SPC\store\pkg\CustomPackage; class PackageManager { @@ -32,11 +33,27 @@ class PackageManager // Download package Downloader::downloadPackage($pkg_name, $config, $force); + if (Config::getPkg($pkg_name)['type'] === 'custom') { + // Custom extract function + $classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'); + foreach ($classes as $class) { + if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) { + $cls = new $class(); + if (in_array($pkg_name, $cls->getSupportName())) { + (new $class())->extract($pkg_name); + break; + } + } + } + return; + } // After download, read lock file name - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true); - $filename = DOWNLOAD_PATH . '/' . ($lock[$pkg_name]['filename'] ?? $lock[$pkg_name]['dirname']); - $extract = $lock[$pkg_name]['move_path'] === null ? (PKG_ROOT_PATH . '/' . $pkg_name) : $lock[$pkg_name]['move_path']; - FileSystem::extractPackage($pkg_name, $filename, $extract); + $lock = LockFile::get($pkg_name); + $source_type = $lock['source_type']; + $filename = LockFile::getLockFullPath($lock); + $extract = LockFile::getExtractPath($pkg_name, PKG_ROOT_PATH . '/' . $pkg_name); + + FileSystem::extractPackage($pkg_name, $source_type, $filename, $extract); // if contains extract-files, we just move this file to destination, and remove extract dir if (is_array($config['extract-files'] ?? null) && is_assoc_array($config['extract-files'])) { diff --git a/src/SPC/store/SourceManager.php b/src/SPC/store/SourceManager.php index 4b06df34..02d07263 100644 --- a/src/SPC/store/SourceManager.php +++ b/src/SPC/store/SourceManager.php @@ -17,11 +17,6 @@ class SourceManager */ public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null, bool $source_only = false): 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)) { @@ -56,8 +51,8 @@ class SourceManager } // check source downloaded $pre_built_name = Downloader::getPreBuiltLockName($source); - if ($source_only || !isset($lock[$pre_built_name])) { - if (!isset($lock[$source])) { + if ($source_only || LockFile::get($pre_built_name) === null) { + if (LockFile::get($source) === null) { throw new WrongUsageException("Source [{$source}] not downloaded or not locked, you should download it first !"); } $lock_name = $source; @@ -65,14 +60,38 @@ class SourceManager $lock_name = $pre_built_name; } + $lock_content = LockFile::get($lock_name); + // check source dir exist - $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']); + $check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source); + // $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']); if (!is_dir($check)) { logger()->debug('Extracting source [' . $source . '] to ' . $check . ' ...'); - FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$lock_name]['filename'] ?? $lock[$lock_name]['dirname']), $lock[$lock_name]['move_path']); - } else { - logger()->debug('Source [' . $source . '] already extracted in ' . $check . ', skip !'); + $filename = LockFile::getLockFullPath($lock_content); + FileSystem::extractSource($source, $lock_content['source_type'], $filename, $check); + LockFile::putLockSourceHash($lock_content, $check); + continue; } + // if a lock file does not have hash, calculate with the current source (backward compatibility) + if (!isset($lock_content['hash'])) { + $hash = LockFile::getLockSourceHash($lock_content); + } else { + $hash = $lock_content['hash']; + } + + // when source already extracted, detect if the extracted source hash is the same as the lock file one + if (file_exists("{$check}/.spc-hash") && FileSystem::readFile("{$check}/.spc-hash") === $hash) { + logger()->debug('Source [' . $source . '] already extracted in ' . $check . ', skip !'); + continue; + } + + // if not, remove the source dir and extract again + logger()->notice("Source [{$source}] hash mismatch, removing old source dir and extracting again ..."); + FileSystem::removeDir($check); + $filename = LockFile::getLockFullPath($lock_content); + $move_path = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source); + FileSystem::extractSource($source, $lock_content['source_type'], $filename, $move_path); + LockFile::putLockSourceHash($lock_content, $check); } } } diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index b5de166f..69d3a649 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -22,6 +22,7 @@ class SourcePatcher FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']); FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']); FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchGDWin32']); + FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchFfiCentos7FixO3strncmp']); FileSystem::addSourceExtractHook('sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('pdo_sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('yaml', [SourcePatcher::class, 'patchYamlWin32']); @@ -43,14 +44,14 @@ class SourcePatcher */ public static function patchBeforeBuildconf(BuilderBase $builder): void { - foreach ($builder->getExts(false) as $ext) { + foreach ($builder->getExts() as $ext) { if ($ext->patchBeforeBuildconf() === true) { - logger()->info('Extension [' . $ext->getName() . '] patched before buildconf'); + logger()->info("Extension [{$ext->getName()}] patched before buildconf"); } } foreach ($builder->getLibs() as $lib) { if ($lib->patchBeforeBuildconf() === true) { - logger()->info('Library [' . $lib->getName() . '] patched before buildconf'); + logger()->info("Library [{$lib->getName()}]patched before buildconf"); } } // patch windows php 8.1 bug @@ -86,21 +87,18 @@ class SourcePatcher */ public static function patchBeforeConfigure(BuilderBase $builder): void { - foreach ($builder->getExts(false) as $ext) { + foreach ($builder->getExts() as $ext) { if ($ext->patchBeforeConfigure() === true) { - logger()->info('Extension [' . $ext->getName() . '] patched before configure'); + logger()->info("Extension [{$ext->getName()}] patched before configure"); } } foreach ($builder->getLibs() as $lib) { if ($lib->patchBeforeConfigure() === true) { - logger()->info('Library [' . $lib->getName() . '] patched before configure'); + logger()->info("Library [{$lib->getName()}] patched before configure"); } } // patch capstone FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"'); - if ($builder instanceof LinuxBuilder && getenv('SPC_LIBC') === 'glibc') { - FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Zend/zend_operators.h', '# define ZEND_USE_ASM_ARITHMETIC 1', '# define ZEND_USE_ASM_ARITHMETIC 0'); - } } /** @@ -269,14 +267,28 @@ class SourcePatcher // call extension patch before make foreach ($builder->getExts(false) as $ext) { if ($ext->patchBeforeMake() === true) { - logger()->info('Extension [' . $ext->getName() . '] patched before make'); + logger()->info("Extension [{$ext->getName()}] patched before make"); } } foreach ($builder->getLibs() as $lib) { if ($lib->patchBeforeMake() === true) { - logger()->info('Library [' . $lib->getName() . '] patched before make'); + logger()->info("Library [{$lib->getName()}] patched before make"); } } + + if (str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), '-release')) { + FileSystem::replaceFileLineContainsString( + SOURCE_PATH . '/php-src/ext/standard/info.c', + '#ifdef CONFIGURE_COMMAND', + '#ifdef NO_CONFIGURE_COMMAND', + ); + } else { + FileSystem::replaceFileLineContainsString( + SOURCE_PATH . '/php-src/ext/standard/info.c', + '#ifdef NO_CONFIGURE_COMMAND', + '#ifdef CONFIGURE_COMMAND', + ); + } } /** @@ -442,6 +454,22 @@ class SourcePatcher return false; } + public static function patchFfiCentos7FixO3strncmp(): bool + { + if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::getLibcVersionIfExists() > '2.17') { + return false; + } + if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) { + return false; + } + $file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h'); + if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) { + return false; + } + SourcePatcher::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src'); + return true; + } + public static function patchLibaomForAlpine(): bool { if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) { diff --git a/src/SPC/store/pkg/CustomPackage.php b/src/SPC/store/pkg/CustomPackage.php new file mode 100644 index 00000000..89edb17e --- /dev/null +++ b/src/SPC/store/pkg/CustomPackage.php @@ -0,0 +1,17 @@ + 'amd64', + 'aarch64' => 'arm64', + default => throw new \InvalidArgumentException('Unsupported architecture: ' . $name), + }; + $os = match (explode('-', $name)[3]) { + 'linux' => 'linux', + 'macos' => 'darwin', + default => throw new \InvalidArgumentException('Unsupported OS: ' . $name), + }; + $go_version = '1.24.4'; + $config = [ + 'type' => 'url', + 'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz", + ]; + Downloader::downloadPackage($name, $config, $force); + } + + public function extract(string $name): void + { + $pkgroot = PKG_ROOT_PATH; + $go_exec = "{$pkgroot}/{$name}/bin/go"; + $xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy"; + if (file_exists($go_exec) && file_exists($xcaddy_exec)) { + return; + } + $lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true); + $source_type = $lock[$name]['source_type']; + $filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']); + $extract = $lock[$name]['move_path'] === null ? "{$pkgroot}/{$name}" : $lock[$name]['move_path']; + + FileSystem::extractPackage($name, $source_type, $filename, $extract); + + GlobalEnvManager::init(); + // install xcaddy + shell() + ->appendEnv([ + 'PATH' => "{$pkgroot}/{$name}/bin:" . getenv('PATH'), + 'GOROOT' => "{$pkgroot}/{$name}", + 'GOBIN' => "{$pkgroot}/{$name}/bin", + 'GOPATH' => "{$pkgroot}/go", + ]) + ->exec("{$go_exec} install github.com/caddyserver/xcaddy/cmd/xcaddy@latest"); + } +} diff --git a/src/SPC/util/ConfigValidator.php b/src/SPC/util/ConfigValidator.php index d455dda5..dbc8ce23 100644 --- a/src/SPC/util/ConfigValidator.php +++ b/src/SPC/util/ConfigValidator.php @@ -120,6 +120,7 @@ class ConfigValidator * @return array{ * php-version?: string, * extensions: array, + * shared-extensions?: array, * libs?: array, * sapi: array, * debug?: bool, @@ -163,6 +164,12 @@ class ConfigValidator if (is_string($craft['extensions'])) { $craft['extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['extensions']))); } + if (!isset($craft['shared-extensions'])) { + $craft['shared-extensions'] = []; + } + if (is_string($craft['shared-extensions'] ?? [])) { + $craft['shared-extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['shared-extensions']))); + } // check libs if (isset($craft['libs']) && is_string($craft['libs'])) { $craft['libs'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['libs']))); diff --git a/src/SPC/util/GlobalEnvManager.php b/src/SPC/util/GlobalEnvManager.php index 8ffd5d05..17785205 100644 --- a/src/SPC/util/GlobalEnvManager.php +++ b/src/SPC/util/GlobalEnvManager.php @@ -45,14 +45,17 @@ class GlobalEnvManager // Define env vars for linux if (PHP_OS_FAMILY === 'Linux') { $arch = getenv('GNU_ARCH'); - if (SystemUtil::isMuslDist()) { + if (SystemUtil::isMuslDist() || getenv('SPC_LIBC') === 'glibc') { self::putenv('SPC_LINUX_DEFAULT_CC=gcc'); self::putenv('SPC_LINUX_DEFAULT_CXX=g++'); self::putenv('SPC_LINUX_DEFAULT_AR=ar'); + self::putenv('SPC_LINUX_DEFAULT_LD=ld.gold'); } else { self::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc"); self::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++"); self::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar"); + self::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld"); + GlobalEnvManager::putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . getenv('PATH')); } } diff --git a/src/SPC/util/GlobalValueTrait.php b/src/SPC/util/GlobalValueTrait.php new file mode 100644 index 00000000..d815b1f0 --- /dev/null +++ b/src/SPC/util/GlobalValueTrait.php @@ -0,0 +1,28 @@ +getLdflagsString(); - $libs = $this->getLibsString($libraries); + $libs = $this->getLibsString($libraries, $with_dependencies); + if (PHP_OS_FAMILY === 'Darwin') { + $libs .= " {$this->getFrameworksString($extensions)}"; + } $cflags = $this->getIncludesString(); // embed - $libs = '-lphp -lc ' . $libs; + $libs = trim("-lphp -lc {$libs}"); $extra_env = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'); if (is_string($extra_env)) { $libs .= ' ' . trim($extra_env, '"'); @@ -97,13 +100,40 @@ class SPCConfigUtil return '-L' . BUILD_LIB_PATH; } - private function getLibsString(array $libraries): string + private function getLibsString(array $libraries, bool $withDependencies = false): string { $short_name = []; foreach (array_reverse($libraries) as $library) { $libs = Config::getLib($library, 'static-libs', []); foreach ($libs as $lib) { - $short_name[] = $this->getShortLibName($lib); + if ($withDependencies) { + $noExt = str_replace('.a', '', $lib); + $requiredLibs = []; + $pkgconfFile = BUILD_LIB_PATH . "/pkgconfig/{$noExt}.pc"; + if (file_exists($pkgconfFile)) { + $lines = file($pkgconfFile); + foreach ($lines as $value) { + if (str_starts_with($value, 'Libs')) { + $items = explode(' ', $value); + foreach ($items as $item) { + $item = trim($item); + if (str_starts_with($item, '-l')) { + $requiredLibs[] = $item; + } + } + } + } + } else { + $requiredLibs[] = $this->getShortLibName($lib); + } + foreach ($requiredLibs as $requiredLib) { + if (!in_array($requiredLib, $short_name)) { + $short_name[] = $requiredLib; + } + } + } else { + $short_name[] = $this->getShortLibName($lib); + } } if (PHP_OS_FAMILY !== 'Darwin') { continue; @@ -116,7 +146,7 @@ class SPCConfigUtil } } // patch: imagick (imagemagick wrapper) for linux needs libgomp - if (in_array('imagemagick', $libraries) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { + if (in_array('imagemagick', $libraries) && PHP_OS_FAMILY === 'Linux' && !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10'))) { $short_name[] = '-lgomp'; } return implode(' ', $short_name); @@ -130,4 +160,18 @@ class SPCConfigUtil // get short name return '-l' . substr($lib, 3, -2); } + + private function getFrameworksString(array $extensions): string + { + $list = []; + foreach ($extensions as $extension) { + foreach (Config::getExt($extension, 'frameworks', []) as $fw) { + $ks = '-framework ' . $fw; + if (!in_array($ks, $list)) { + $list[] = $ks; + } + } + } + return implode(' ', $list); + } } diff --git a/src/SPC/util/UnixShell.php b/src/SPC/util/UnixShell.php index a53f423d..ffe18910 100644 --- a/src/SPC/util/UnixShell.php +++ b/src/SPC/util/UnixShell.php @@ -44,14 +44,7 @@ class UnixShell { /* @phpstan-ignore-next-line */ logger()->info(ConsoleColor::yellow('[EXEC] ') . ConsoleColor::green($cmd)); - logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']); - $env_str = $this->getEnvString(); - if (!empty($env_str)) { - $cmd = "{$env_str} {$cmd}"; - } - if ($this->cd !== null) { - $cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd; - } + $cmd = $this->getExecString($cmd); if (!$this->debug) { $cmd .= ' 1>/dev/null 2>&1'; } @@ -99,10 +92,7 @@ class UnixShell /* @phpstan-ignore-next-line */ logger()->debug(ConsoleColor::blue('[EXEC] ') . ConsoleColor::gray($cmd)); } - logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']); - if ($this->cd !== null) { - $cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd; - } + $cmd = $this->getExecString($cmd); exec($cmd, $out, $code); return [$code, $out]; } @@ -110,15 +100,15 @@ class UnixShell public function setEnv(array $env): UnixShell { foreach ($env as $k => $v) { - if ($v === '') { + if (trim($v) === '') { continue; } - $this->env[$k] = $v; + $this->env[$k] = trim($v); } return $this; } - private function getEnvString(): string + public function getEnvString(): string { $str = ''; foreach ($this->env as $k => $v) { @@ -126,4 +116,17 @@ class UnixShell } return trim($str); } + + private function getExecString(string $cmd): string + { + logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']); + $env_str = $this->getEnvString(); + if (!empty($env_str)) { + $cmd = "{$env_str} {$cmd}"; + } + if ($this->cd !== null) { + $cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd; + } + return $cmd; + } } diff --git a/src/SPC/util/WindowsCmd.php b/src/SPC/util/WindowsCmd.php index ca06dc25..4398dc44 100644 --- a/src/SPC/util/WindowsCmd.php +++ b/src/SPC/util/WindowsCmd.php @@ -75,24 +75,4 @@ class WindowsCmd $this->env = array_merge($this->env, $env); return $this; } - - /** - * @throws RuntimeException - */ - public function execWithEnv(string $cmd): WindowsCmd - { - if ($this->getEnvString() !== '') { - return $this->exec($this->getEnvString() . "call {$cmd}"); - } - return $this->exec($cmd); - } - - private function getEnvString(): string - { - $str = ''; - foreach ($this->env as $k => $v) { - $str .= 'set ' . $k . '=' . $v . ' && '; - } - return $str; - } } diff --git a/src/SPC/util/executor/UnixAutoconfExecutor.php b/src/SPC/util/executor/UnixAutoconfExecutor.php new file mode 100644 index 00000000..1c9f496e --- /dev/null +++ b/src/SPC/util/executor/UnixAutoconfExecutor.php @@ -0,0 +1,141 @@ +initShell(); + } + + /** + * Run ./configure + */ + public function configure(...$args): static + { + // remove all the ignored args + $args = array_merge($args, $this->getDefaultConfigureArgs(), $this->configure_args); + $args = array_diff($args, $this->ignore_args); + $configure_args = implode(' ', $args); + + $this->shell->exec("./configure {$configure_args}"); + return $this; + } + + public function getConfigureArgsString(): string + { + return implode(' ', array_merge($this->getDefaultConfigureArgs(), $this->configure_args)); + } + + /** + * Run make + * + * @param string $target Build target + * @throws RuntimeException + */ + public function make(string $target = '', false|string $with_install = 'install', bool $with_clean = true, array $after_env_vars = []): static + { + if ($with_clean) { + $this->shell->exec('make clean'); + } + $after_env_vars_str = $after_env_vars !== [] ? shell()->setEnv($after_env_vars)->getEnvString() : ''; + $this->shell->exec("make -j{$this->library->getBuilder()->concurrency} {$target} {$after_env_vars_str}"); + if ($with_install !== false) { + $this->shell->exec("make {$with_install}"); + } + return $this; + } + + public function exec(string $cmd): static + { + $this->shell->exec($cmd); + return $this; + } + + /** + * Add optional library configuration. + * This method checks if a library is available and adds the corresponding arguments to the CMake configuration. + * + * @param string $name library name to check + * @param \Closure|string $true_args arguments to use if the library is available (allow closure, returns string) + * @param string $false_args arguments to use if the library is not available + * @return $this + */ + public function optionalLib(string $name, \Closure|string $true_args, string $false_args = ''): static + { + if ($get = $this->library->getBuilder()->getLib($name)) { + logger()->info("Building library [{$this->library->getName()}] with {$name} support"); + $args = $true_args instanceof \Closure ? $true_args($get) : $true_args; + } else { + logger()->info("Building library [{$this->library->getName()}] without {$name} support"); + $args = $false_args; + } + $this->addConfigureArgs($args); + return $this; + } + + /** + * Add configure args. + */ + public function addConfigureArgs(...$args): static + { + $this->configure_args = [...$this->configure_args, ...$args]; + return $this; + } + + /** + * Remove some configure args, to bypass the configure option checking for some libs. + */ + public function removeConfigureArgs(...$args): static + { + $this->ignore_args = [...$this->ignore_args, ...$args]; + return $this; + } + + public function appendEnv(array $env): static + { + $this->shell->appendEnv($env); + return $this; + } + + /** + * Returns the default autoconf ./configure arguments + */ + private function getDefaultConfigureArgs(): array + { + return [ + '--disable-shared', + '--enable-static', + "--prefix={$this->library->getBuildRootPath()}", + '--with-pic', + '--enable-pic', + ]; + } + + /** + * Initialize UnixShell class. + */ + private function initShell(): void + { + $this->shell = shell()->cd($this->library->getSourceDir())->initializeEnv($this->library)->appendEnv([ + 'CFLAGS' => "-I{$this->library->getIncludeDir()}", + 'LDFLAGS' => "-L{$this->library->getLibDir()}", + ]); + } +} diff --git a/src/SPC/util/executor/UnixCMakeExecutor.php b/src/SPC/util/executor/UnixCMakeExecutor.php index 4b8ec2e2..b9c7ef58 100644 --- a/src/SPC/util/executor/UnixCMakeExecutor.php +++ b/src/SPC/util/executor/UnixCMakeExecutor.php @@ -58,8 +58,10 @@ class UnixCMakeExecutor extends Executor public function optionalLib(string $name, \Closure|string $true_args, string $false_args = ''): static { if ($get = $this->library->getBuilder()->getLib($name)) { + logger()->info("Building library [{$this->library->getName()}] with {$name} support"); $args = $true_args instanceof \Closure ? $true_args($get) : $true_args; } else { + logger()->info("Building library [{$this->library->getName()}] without {$name} support"); $args = $false_args; } $this->addConfigureArgs($args); @@ -137,6 +139,7 @@ class UnixCMakeExecutor extends Executor '-DCMAKE_INSTALL_BINDIR=bin', '-DCMAKE_INSTALL_LIBDIR=lib', '-DCMAKE_INSTALL_INCLUDEDIR=include', + '-DPOSITION_INDEPENDENT_CODE=ON', '-DBUILD_SHARED_LIBS=OFF', "-DCMAKE_TOOLCHAIN_FILE={$this->makeCmakeToolchainFile()}", ]); @@ -192,6 +195,7 @@ SET(CMAKE_INSTALL_PREFIX "{$root}") SET(CMAKE_INSTALL_LIBDIR "lib") set(PKG_CONFIG_EXECUTABLE "{$root}/bin/pkg-config") +list(APPEND PKG_CONFIG_EXECUTABLE "--static") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/src/globals/defines.php b/src/globals/defines.php index f3d290ba..ab37ace9 100644 --- a/src/globals/defines.php +++ b/src/globals/defines.php @@ -40,7 +40,7 @@ const SPC_EXTENSION_ALIAS = [ 'zendopcache' => 'opcache', ]; -// spc lock type +// spc download lock type const SPC_DOWNLOAD_SOURCE = 1; // lock source const SPC_DOWNLOAD_PRE_BUILT = 2; // lock pre-built const SPC_DOWNLOAD_PACKAGE = 3; // lock as package @@ -62,7 +62,8 @@ const BUILD_TARGET_CLI = 1; // build cli const BUILD_TARGET_MICRO = 2; // build micro const BUILD_TARGET_FPM = 4; // build fpm const BUILD_TARGET_EMBED = 8; // build embed -const BUILD_TARGET_ALL = 15; // build all +const BUILD_TARGET_FRANKENPHP = 16; // build frankenphp +const BUILD_TARGET_ALL = BUILD_TARGET_CLI | BUILD_TARGET_MICRO | BUILD_TARGET_FPM | BUILD_TARGET_EMBED | BUILD_TARGET_FRANKENPHP; // build all // doctor error fix policy const FIX_POLICY_DIE = 1; // die directly @@ -84,5 +85,10 @@ const AUTOCONF_CPPFLAGS = 4; const AUTOCONF_LDFLAGS = 8; const AUTOCONF_ALL = 15; +// spc download source type +const SPC_SOURCE_ARCHIVE = 'archive'; // download as archive +const SPC_SOURCE_GIT = 'git'; // download as git repository +const SPC_SOURCE_LOCAL = 'local'; // download as local directory + ConsoleLogger::$date_format = 'H:i:s'; ConsoleLogger::$format = '[%date%] [%level_short%] %body%'; diff --git a/src/globals/ext-tests/brotli.php b/src/globals/ext-tests/brotli.php new file mode 100644 index 00000000..bfe9f259 --- /dev/null +++ b/src/globals/ext-tests/brotli.php @@ -0,0 +1,15 @@ + 'win', + 'Darwin' => 'macos', + 'Linux' => 'linux', + 'BSD' => 'bsd', + default => throw new WrongUsageException('Not support os: ' . PHP_OS_FAMILY), + }; +} + function shell(?bool $debug = null): UnixShell { /* @noinspection PhpUnhandledExceptionInspection */ @@ -216,3 +227,8 @@ function cmake_boolean_args(string $arg_name, bool $negative = false): array $res = ["-D{$arg_name}=ON", "-D{$arg_name}=OFF"]; return $negative ? array_reverse($res) : $res; } + +function ac_with_args(string $arg_name, bool $use_value = false): array +{ + return $use_value ? ["--with-{$arg_name}=yes", "--with-{$arg_name}=no"] : ["--with-{$arg_name}", "--without-{$arg_name}"]; +} diff --git a/src/globals/internal-env.php b/src/globals/internal-env.php index 34b1ed99..1046848e 100644 --- a/src/globals/internal-env.php +++ b/src/globals/internal-env.php @@ -9,10 +9,17 @@ use SPC\builder\windows\SystemUtil as WindowsSystemUtil; use SPC\store\FileSystem; use SPC\util\GlobalEnvManager; +// output path for everything, other paths are defined relative to this by default define('BUILD_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot'))); +// output path for header files for development define('BUILD_INCLUDE_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include'))); +// output path for libraries and for libphp.so, if building shared embed define('BUILD_LIB_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib'))); +// output path for binaries define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_BIN_PATH')) ? $a : (BUILD_ROOT_PATH . '/bin'))); +// output path for shared extensions +define('BUILD_MODULES_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_MODULES_PATH')) ? $a : (BUILD_ROOT_PATH . '/modules'))); + define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot'))); define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source'))); define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads'))); diff --git a/src/globals/patch/ffi_centos7_fix_O3_strncmp.patch b/src/globals/patch/ffi_centos7_fix_O3_strncmp.patch new file mode 100644 index 00000000..815170be --- /dev/null +++ b/src/globals/patch/ffi_centos7_fix_O3_strncmp.patch @@ -0,0 +1,31 @@ +--- a/ext/ffi/ffi.c 2025-01-17 10:37:37 ++++ a/ext/ffi/ffi.c 2025-01-17 10:39:17 +@@ -57,6 +57,8 @@ + /* XXX need something better, perhaps with regard to SIMD, etc. */ + # define __BIGGEST_ALIGNMENT__ sizeof(size_t) + #endif ++ ++#define ZEND_STRNCMP(s,str) strncmp((s),(str),(sizeof(str)-1)) + + ZEND_DECLARE_MODULE_GLOBALS(ffi) + +@@ -5046,16 +5048,16 @@ static char *zend_ffi_parse_directives(const char *fil + *scope_name = NULL; + *lib = NULL; + while (*code_pos == '#') { +- if (strncmp(code_pos, ZEND_STRL("#define")) == 0) { ++ if (ZEND_STRNCMP(code_pos, "#define") == 0) { + p = zend_ffi_skip_ws_and_comments(code_pos + sizeof("#define") - 1, false); + + char **target = NULL; + const char *target_name = NULL; +- if (strncmp(p, ZEND_STRL("FFI_SCOPE")) == 0) { ++ if (ZEND_STRNCMP(p, "FFI_SCOPE") == 0) { + p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_SCOPE") - 1, false); + target = scope_name; + target_name = "FFI_SCOPE"; +- } else if (strncmp(p, ZEND_STRL("FFI_LIB")) == 0) { ++ } else if (ZEND_STRNCMP(p, "FFI_LIB") == 0) { + p = zend_ffi_skip_ws_and_comments(p + sizeof("FFI_LIB") - 1, false); + target = lib; + target_name = "FFI_LIB"; diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index ef58039e..70758f85 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -33,15 +33,18 @@ $test_os = [ ]; // whether enable thread safe -$zts = false; +$zts = true; $no_strip = false; // compress with upx $upx = false; +// whether to test frankenphp build, only available for macos and linux +$frankenphp = false; + // prefer downloading pre-built packages to speed up the build process -$prefer_pre_built = false; +$prefer_pre_built = true; // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). $extensions = match (PHP_OS_FAMILY) { @@ -51,11 +54,15 @@ $extensions = match (PHP_OS_FAMILY) { // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). $shared_extensions = match (PHP_OS_FAMILY) { - 'Linux' => '', - 'Windows', 'Darwin' => '', + 'Linux' => 'uv', + 'Darwin' => '', + 'Windows' => '', }; -// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`). +// If you want to test lib-suggests for all extensions and libraries, set it to true. +$with_suggested_libs = false; + +// If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true. $with_libs = match (PHP_OS_FAMILY) { 'Linux', 'Darwin' => '', 'Windows' => '', @@ -168,6 +175,7 @@ if ($argv[1] === 'build_cmd' || $argv[1] === 'build_embed_cmd') { $build_cmd = 'build '; $build_cmd .= quote2($final_extensions) . ' '; $build_cmd .= $shared_cmd; + $build_cmd .= $with_suggested_libs ? '--with-suggested-libs ' : ''; $build_cmd .= $zts ? '--enable-zts ' : ''; $build_cmd .= $no_strip ? '--no-strip ' : ''; $build_cmd .= $upx ? '--with-upx-pack ' : ''; @@ -203,7 +211,13 @@ switch ($argv[1] ?? null) { passthru($prefix . $build_cmd . ' --build-cli --build-micro', $retcode); break; case 'build_embed_cmd': - passthru($prefix . $build_cmd . (str_starts_with($argv[2], 'windows-') ? ' --build-cli' : ' --build-embed'), $retcode); + if ($frankenphp) { + passthru("{$prefix}install-pkg go-xcaddy --debug", $retcode); + if ($retcode !== 0) { + break; + } + } + passthru($prefix . $build_cmd . (str_starts_with($argv[2], 'windows-') ? ' --build-cli' : (' --build-embed' . ($frankenphp ? ' --build-frankenphp' : ''))), $retcode); break; case 'doctor_cmd': passthru($prefix . $doctor_cmd, $retcode); diff --git a/tests/SPC/builder/BuilderTest.php b/tests/SPC/builder/BuilderTest.php index 17f1e799..b80f4a0d 100644 --- a/tests/SPC/builder/BuilderTest.php +++ b/tests/SPC/builder/BuilderTest.php @@ -13,6 +13,7 @@ use SPC\builder\LibraryBase; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\util\CustomExt; use SPC\util\DependencyUtil; use Symfony\Component\Console\Input\ArgvInput; @@ -117,7 +118,7 @@ class BuilderTest extends TestCase public function testGetPHPVersionFromArchive() { - $lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false; + $lock = file_exists(LockFile::LOCK_FILE) ? file_get_contents(LockFile::LOCK_FILE) : false; if ($lock === false) { $this->assertFalse($this->builder->getPHPVersionFromArchive()); } else { @@ -161,7 +162,8 @@ class BuilderTest extends TestCase [BUILD_TARGET_FPM, 'fpm'], [BUILD_TARGET_MICRO, 'micro'], [BUILD_TARGET_EMBED, 'embed'], - [BUILD_TARGET_ALL, 'cli, micro, fpm, embed'], + [BUILD_TARGET_FRANKENPHP, 'frankenphp'], + [BUILD_TARGET_ALL, 'cli, micro, fpm, embed, frankenphp'], [BUILD_TARGET_CLI | BUILD_TARGET_EMBED, 'cli, embed'], ]; } diff --git a/tests/SPC/store/DownloaderTest.php b/tests/SPC/store/DownloaderTest.php index a9507e08..91865bfb 100644 --- a/tests/SPC/store/DownloaderTest.php +++ b/tests/SPC/store/DownloaderTest.php @@ -7,6 +7,7 @@ namespace SPC\Tests\store; use PHPUnit\Framework\TestCase; use SPC\exception\WrongUsageException; use SPC\store\Downloader; +use SPC\store\LockFile; /** * @internal @@ -57,16 +58,16 @@ class DownloaderTest extends TestCase public function testLockSource() { - Downloader::lockSource('fake-file', ['source_type' => 'archive', 'filename' => 'fake-file-name', 'move_path' => 'fake-path', 'lock_as' => 'fake-lock-as']); - $this->assertFileExists(DOWNLOAD_PATH . '/.lock.json'); - $json = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true); + LockFile::lockSource('fake-file', ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => 'fake-file-name', 'move_path' => 'fake-path', 'lock_as' => 'fake-lock-as']); + $this->assertFileExists(LockFile::LOCK_FILE); + $json = json_decode(file_get_contents(LockFile::LOCK_FILE), true); $this->assertIsArray($json); $this->assertArrayHasKey('fake-file', $json); $this->assertArrayHasKey('source_type', $json['fake-file']); $this->assertArrayHasKey('filename', $json['fake-file']); $this->assertArrayHasKey('move_path', $json['fake-file']); $this->assertArrayHasKey('lock_as', $json['fake-file']); - $this->assertEquals('archive', $json['fake-file']['source_type']); + $this->assertEquals(SPC_SOURCE_ARCHIVE, $json['fake-file']['source_type']); $this->assertEquals('fake-file-name', $json['fake-file']['filename']); $this->assertEquals('fake-path', $json['fake-file']['move_path']); $this->assertEquals('fake-lock-as', $json['fake-file']['lock_as']); diff --git a/yarn.lock b/yarn.lock index 1e18c1b2..3e357ce9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,819 +2,969 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.9.3": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz#1d56482a768c33aae0868c8533049e02e8961be7" - integrity sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw== +"@algolia/autocomplete-core@1.17.9": + version "1.17.9" + resolved "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.9.tgz" + integrity sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ== dependencies: - "@algolia/autocomplete-plugin-algolia-insights" "1.9.3" - "@algolia/autocomplete-shared" "1.9.3" + "@algolia/autocomplete-plugin-algolia-insights" "1.17.9" + "@algolia/autocomplete-shared" "1.17.9" -"@algolia/autocomplete-plugin-algolia-insights@1.9.3": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz#9b7f8641052c8ead6d66c1623d444cbe19dde587" - integrity sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg== +"@algolia/autocomplete-plugin-algolia-insights@1.17.9": + version "1.17.9" + resolved "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.9.tgz" + integrity sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ== dependencies: - "@algolia/autocomplete-shared" "1.9.3" + "@algolia/autocomplete-shared" "1.17.9" -"@algolia/autocomplete-preset-algolia@1.9.3": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz#64cca4a4304cfcad2cf730e83067e0c1b2f485da" - integrity sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA== +"@algolia/autocomplete-preset-algolia@1.17.9": + version "1.17.9" + resolved "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.9.tgz" + integrity sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ== dependencies: - "@algolia/autocomplete-shared" "1.9.3" + "@algolia/autocomplete-shared" "1.17.9" -"@algolia/autocomplete-shared@1.9.3": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz#2e22e830d36f0a9cf2c0ccd3c7f6d59435b77dfa" - integrity sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ== +"@algolia/autocomplete-shared@1.17.9": + version "1.17.9" + resolved "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.9.tgz" + integrity sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ== -"@algolia/cache-browser-local-storage@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz#97bc6d067a9fd932b9c922faa6b7fd6e546e1348" - integrity sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww== +"@algolia/client-abtesting@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.25.0.tgz" + integrity sha512-1pfQulNUYNf1Tk/svbfjfkLBS36zsuph6m+B6gDkPEivFmso/XnRgwDvjAx80WNtiHnmeNjIXdF7Gos8+OLHqQ== dependencies: - "@algolia/cache-common" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/cache-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.24.0.tgz#81a8d3a82ceb75302abb9b150a52eba9960c9744" - integrity sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g== - -"@algolia/cache-in-memory@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz#ffcf8872f3a10cb85c4f4641bdffd307933a6e44" - integrity sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w== +"@algolia/client-analytics@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.25.0.tgz" + integrity sha512-AFbG6VDJX/o2vDd9hqncj1B6B4Tulk61mY0pzTtzKClyTDlNP0xaUiEKhl6E7KO9I/x0FJF5tDCm0Hn6v5x18A== dependencies: - "@algolia/cache-common" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/client-account@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.24.0.tgz#eba7a921d828e7c8c40a32d4add21206c7fe12f1" - integrity sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA== +"@algolia/client-common@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.25.0.tgz" + integrity sha512-il1zS/+Rc6la6RaCdSZ2YbJnkQC6W1wiBO8+SH+DE6CPMWBU6iDVzH0sCKSAtMWl9WBxoN6MhNjGBnCv9Yy2bA== + +"@algolia/client-insights@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.25.0.tgz" + integrity sha512-blbjrUH1siZNfyCGeq0iLQu00w3a4fBXm0WRIM0V8alcAPo7rWjLbMJMrfBtzL9X5ic6wgxVpDADXduGtdrnkw== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/client-analytics@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.24.0.tgz#9d2576c46a9093a14e668833c505ea697a1a3e30" - integrity sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg== +"@algolia/client-personalization@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.25.0.tgz" + integrity sha512-aywoEuu1NxChBcHZ1pWaat0Plw7A8jDMwjgRJ00Mcl7wGlwuPt5dJ/LTNcg3McsEUbs2MBNmw0ignXBw9Tbgow== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/client-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.24.0.tgz#77c46eee42b9444a1d1c1583a83f7df4398a649d" - integrity sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA== +"@algolia/client-query-suggestions@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.25.0.tgz" + integrity sha512-a/W2z6XWKjKjIW1QQQV8PTTj1TXtaKx79uR3NGBdBdGvVdt24KzGAaN7sCr5oP8DW4D3cJt44wp2OY/fZcPAVA== dependencies: - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/client-personalization@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.24.0.tgz#8b47789fb1cb0f8efbea0f79295b7c5a3850f6ae" - integrity sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w== +"@algolia/client-search@>= 4.9.1 < 6", "@algolia/client-search@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.25.0.tgz" + integrity sha512-9rUYcMIBOrCtYiLX49djyzxqdK9Dya/6Z/8sebPn94BekT+KLOpaZCuc6s0Fpfq7nx5J6YY5LIVFQrtioK9u0g== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/client-search@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.24.0.tgz#75e6c02d33ef3e0f34afd9962c085b856fc4a55f" - integrity sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA== +"@algolia/ingestion@1.25.0": + version "1.25.0" + resolved "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.25.0.tgz" + integrity sha512-jJeH/Hk+k17Vkokf02lkfYE4A+EJX+UgnMhTLR/Mb+d1ya5WhE+po8p5a/Nxb6lo9OLCRl6w3Hmk1TX1e9gVbQ== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/logger-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.24.0.tgz#28d439976019ec0a46ba7a1a739ef493d4ef8123" - integrity sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA== - -"@algolia/logger-console@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.24.0.tgz#c6ff486036cd90b81d07a95aaba04461da7e1c65" - integrity sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg== +"@algolia/monitoring@1.25.0": + version "1.25.0" + resolved "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.25.0.tgz" + integrity sha512-Ls3i1AehJ0C6xaHe7kK9vPmzImOn5zBg7Kzj8tRYIcmCWVyuuFwCIsbuIIz/qzUf1FPSWmw0TZrGeTumk2fqXg== dependencies: - "@algolia/logger-common" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/recommend@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-4.24.0.tgz#8a3f78aea471ee0a4836b78fd2aad4e9abcaaf34" - integrity sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw== +"@algolia/recommend@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.25.0.tgz" + integrity sha512-79sMdHpiRLXVxSjgw7Pt4R1aNUHxFLHiaTDnN2MQjHwJ1+o3wSseb55T9VXU4kqy3m7TUme3pyRhLk5ip/S4Mw== dependencies: - "@algolia/cache-browser-local-storage" "4.24.0" - "@algolia/cache-common" "4.24.0" - "@algolia/cache-in-memory" "4.24.0" - "@algolia/client-common" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/logger-common" "4.24.0" - "@algolia/logger-console" "4.24.0" - "@algolia/requester-browser-xhr" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/requester-node-http" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -"@algolia/requester-browser-xhr@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz#313c5edab4ed73a052e75803855833b62dd19c16" - integrity sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA== +"@algolia/requester-browser-xhr@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.25.0.tgz" + integrity sha512-JLaF23p1SOPBmfEqozUAgKHQrGl3z/Z5RHbggBu6s07QqXXcazEsub5VLonCxGVqTv6a61AAPr8J1G5HgGGjEw== dependencies: - "@algolia/requester-common" "4.24.0" + "@algolia/client-common" "5.25.0" -"@algolia/requester-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.24.0.tgz#1c60c198031f48fcdb9e34c4057a3ea987b9a436" - integrity sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA== - -"@algolia/requester-node-http@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz#4461593714031d02aa7da221c49df675212f482f" - integrity sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw== +"@algolia/requester-fetch@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.25.0.tgz" + integrity sha512-rtzXwqzFi1edkOF6sXxq+HhmRKDy7tz84u0o5t1fXwz0cwx+cjpmxu/6OQKTdOJFS92JUYHsG51Iunie7xbqfQ== dependencies: - "@algolia/requester-common" "4.24.0" + "@algolia/client-common" "5.25.0" -"@algolia/transporter@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.24.0.tgz#226bb1f8af62430374c1972b2e5c8580ab275102" - integrity sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA== +"@algolia/requester-node-http@5.25.0": + version "5.25.0" + resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.25.0.tgz" + integrity sha512-ZO0UKvDyEFvyeJQX0gmZDQEvhLZ2X10K+ps6hViMo1HgE2V8em00SwNsQ+7E/52a+YiBkVWX61pJJJE44juDMQ== dependencies: - "@algolia/cache-common" "4.24.0" - "@algolia/logger-common" "4.24.0" - "@algolia/requester-common" "4.24.0" + "@algolia/client-common" "5.25.0" -"@babel/parser@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" - integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@docsearch/css@3.6.0", "@docsearch/css@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.6.0.tgz#0e9f56f704b3a34d044d15fd9962ebc1536ba4fb" - integrity sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ== +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== -"@docsearch/js@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.6.0.tgz#f9e46943449b9092d874944f7a80bcc071004cfb" - integrity sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ== +"@babel/parser@^7.27.2": + version "7.27.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz" + integrity sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw== dependencies: - "@docsearch/react" "3.6.0" + "@babel/types" "^7.27.3" + +"@babel/types@^7.27.3": + version "7.27.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz" + integrity sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@docsearch/css@^3.9.0", "@docsearch/css@3.9.0": + version "3.9.0" + resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.9.0.tgz" + integrity sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA== + +"@docsearch/js@^3.9.0": + version "3.9.0" + resolved "https://registry.npmjs.org/@docsearch/js/-/js-3.9.0.tgz" + integrity sha512-4bKHcye6EkLgRE8ze0vcdshmEqxeiJM77M0JXjef7lrYZfSlMunrDOCqyLjiZyo1+c0BhUqA2QpFartIjuHIjw== + dependencies: + "@docsearch/react" "3.9.0" preact "^10.0.0" -"@docsearch/react@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.6.0.tgz#b4f25228ecb7fc473741aefac592121e86dd2958" - integrity sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w== +"@docsearch/react@3.9.0": + version "3.9.0" + resolved "https://registry.npmjs.org/@docsearch/react/-/react-3.9.0.tgz" + integrity sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ== dependencies: - "@algolia/autocomplete-core" "1.9.3" - "@algolia/autocomplete-preset-algolia" "1.9.3" - "@docsearch/css" "3.6.0" - algoliasearch "^4.19.1" + "@algolia/autocomplete-core" "1.17.9" + "@algolia/autocomplete-preset-algolia" "1.17.9" + "@docsearch/css" "3.9.0" + algoliasearch "^5.14.2" -"@esbuild/aix-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" - integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== +"@esbuild/darwin-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz" + integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ== -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== - -"@esbuild/android-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" - integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== - -"@esbuild/android-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" - integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== - -"@esbuild/darwin-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== - -"@esbuild/darwin-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" - integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== - -"@esbuild/freebsd-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" - integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== - -"@esbuild/freebsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" - integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== - -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== - -"@esbuild/linux-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" - integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== - -"@esbuild/linux-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" - integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== - -"@esbuild/linux-loong64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" - integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== - -"@esbuild/linux-mips64el@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" - integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== - -"@esbuild/linux-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" - integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== - -"@esbuild/linux-riscv64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" - integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== - -"@esbuild/linux-s390x@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" - integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== - -"@esbuild/linux-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" - integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== - -"@esbuild/netbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" - integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== - -"@esbuild/openbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" - integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== - -"@esbuild/sunos-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" - integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== - -"@esbuild/win32-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" - integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== - -"@esbuild/win32-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" - integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== - -"@esbuild/win32-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" - integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== - -"@jridgewell/sourcemap-codec@^1.4.15": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@rollup/rollup-android-arm-eabi@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27" - integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ== - -"@rollup/rollup-android-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203" - integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA== - -"@rollup/rollup-darwin-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096" - integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w== - -"@rollup/rollup-darwin-x64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c" - integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA== - -"@rollup/rollup-linux-arm-gnueabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8" - integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA== - -"@rollup/rollup-linux-arm-musleabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549" - integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A== - -"@rollup/rollup-linux-arm64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577" - integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw== - -"@rollup/rollup-linux-arm64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c" - integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ== - -"@rollup/rollup-linux-powerpc64le-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf" - integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA== - -"@rollup/rollup-linux-riscv64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9" - integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg== - -"@rollup/rollup-linux-s390x-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec" - integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg== - -"@rollup/rollup-linux-x64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942" - integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w== - -"@rollup/rollup-linux-x64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d" - integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg== - -"@rollup/rollup-win32-arm64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf" - integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA== - -"@rollup/rollup-win32-ia32-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54" - integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg== - -"@rollup/rollup-win32-x64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" - integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== - -"@shikijs/core@1.10.0", "@shikijs/core@^1.6.2": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.10.0.tgz#ec3356ace7cb8b41f6baee0116f036fca85054cc" - integrity sha512-BZcr6FCmPfP6TXaekvujZcnkFmJHZ/Yglu97r/9VjzVndQA56/F4WjUKtJRQUnK59Wi7p/UTAOekMfCJv7jnYg== - -"@shikijs/transformers@^1.6.2": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@shikijs/transformers/-/transformers-1.10.0.tgz#327a8d63d0d0fd5237ee41c4444376723b7c5a2c" - integrity sha512-5Eu/kuJu7/CzAjFlTJkyyPoLTLSVQZ31Ps81cjIeR/3PDJ2RUuX1/R8d0qFziBKToym1LXbNiXoJQq0mg5+Cwg== +"@iconify-json/simple-icons@^1.2.32": + version "1.2.36" + resolved "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.36.tgz" + integrity sha512-ZMpVdoW/7hhbt2aHVSvudjH8eSVNNjKkAAjwAQHgiuPUiIfbvNakVin+H9uhUz4N9TbDT/nanzV/4Slb+6dDXw== dependencies: - shiki "1.10.0" + "@iconify/types" "*" -"@types/estree@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== - -"@types/linkify-it@^5": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" - integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== - -"@types/markdown-it@^14.1.1": - version "14.1.1" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.1.tgz#06bafb7a4e3f77b62b1f308acf7df76687887e0b" - integrity sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg== - dependencies: - "@types/linkify-it" "^5" - "@types/mdurl" "^2" - -"@types/mdurl@^2": +"@iconify/types@*": version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" - integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== + resolved "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz" + integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== -"@types/web-bluetooth@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597" - integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow== +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@vitejs/plugin-vue@^5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz#e3dc11e427d4b818b7e3202766ad156e3d5e2eaa" - integrity sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ== +"@rollup/rollup-darwin-arm64@4.41.1": + version "4.41.1" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz" + integrity sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w== -"@vue/compiler-core@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.31.tgz#b51a76f1b30e9b5eba0553264dff0f171aedb7c6" - integrity sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg== +"@shikijs/core@^3.2.2", "@shikijs/core@3.4.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/core/-/core-3.4.2.tgz" + integrity sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ== dependencies: - "@babel/parser" "^7.24.7" - "@vue/shared" "3.4.31" + "@shikijs/types" "3.4.2" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.5" + +"@shikijs/engine-javascript@3.4.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.4.2.tgz" + integrity sha512-1/adJbSMBOkpScCE/SB6XkjJU17ANln3Wky7lOmrnpl+zBdQ1qXUJg2GXTYVHRq+2j3hd1DesmElTXYDgtfSOQ== + dependencies: + "@shikijs/types" "3.4.2" + "@shikijs/vscode-textmate" "^10.0.2" + oniguruma-to-es "^4.3.3" + +"@shikijs/engine-oniguruma@3.4.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.2.tgz" + integrity sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q== + dependencies: + "@shikijs/types" "3.4.2" + "@shikijs/vscode-textmate" "^10.0.2" + +"@shikijs/langs@3.4.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/langs/-/langs-3.4.2.tgz" + integrity sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA== + dependencies: + "@shikijs/types" "3.4.2" + +"@shikijs/themes@3.4.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/themes/-/themes-3.4.2.tgz" + integrity sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg== + dependencies: + "@shikijs/types" "3.4.2" + +"@shikijs/transformers@^3.2.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.4.2.tgz" + integrity sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg== + dependencies: + "@shikijs/core" "3.4.2" + "@shikijs/types" "3.4.2" + +"@shikijs/types@^3.2.2", "@shikijs/types@3.4.2": + version "3.4.2" + resolved "https://registry.npmjs.org/@shikijs/types/-/types-3.4.2.tgz" + integrity sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg== + dependencies: + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + +"@shikijs/vscode-textmate@^10.0.2": + version "10.0.2" + resolved "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz" + integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== + +"@types/estree@1.0.7": + version "1.0.7" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz" + integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== + +"@types/hast@^3.0.0", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/web-bluetooth@^0.0.21": + version "0.0.21" + resolved "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz" + integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA== + +"@ungap/structured-clone@^1.0.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@vitejs/plugin-vue@^5.2.3": + version "5.2.4" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz" + integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA== + +"@vue/compiler-core@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz" + integrity sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ== + dependencies: + "@babel/parser" "^7.27.2" + "@vue/shared" "3.5.16" entities "^4.5.0" estree-walker "^2.0.2" - source-map-js "^1.2.0" + source-map-js "^1.2.1" -"@vue/compiler-dom@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz#30961ca847f5d6ad18ffa26236c219f61b195f6b" - integrity sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ== +"@vue/compiler-dom@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz" + integrity sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ== dependencies: - "@vue/compiler-core" "3.4.31" - "@vue/shared" "3.4.31" + "@vue/compiler-core" "3.5.16" + "@vue/shared" "3.5.16" -"@vue/compiler-sfc@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz#cc6bfccda17df8268cc5440842277f61623c591f" - integrity sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ== +"@vue/compiler-sfc@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz" + integrity sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw== dependencies: - "@babel/parser" "^7.24.7" - "@vue/compiler-core" "3.4.31" - "@vue/compiler-dom" "3.4.31" - "@vue/compiler-ssr" "3.4.31" - "@vue/shared" "3.4.31" + "@babel/parser" "^7.27.2" + "@vue/compiler-core" "3.5.16" + "@vue/compiler-dom" "3.5.16" + "@vue/compiler-ssr" "3.5.16" + "@vue/shared" "3.5.16" estree-walker "^2.0.2" - magic-string "^0.30.10" - postcss "^8.4.38" - source-map-js "^1.2.0" + magic-string "^0.30.17" + postcss "^8.5.3" + source-map-js "^1.2.1" -"@vue/compiler-ssr@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz#f62ffecdf15bacb883d0099780cf9a1e3654bfc4" - integrity sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA== +"@vue/compiler-ssr@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz" + integrity sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A== dependencies: - "@vue/compiler-dom" "3.4.31" - "@vue/shared" "3.4.31" + "@vue/compiler-dom" "3.5.16" + "@vue/shared" "3.5.16" -"@vue/devtools-api@^7.2.1": - version "7.3.5" - resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.3.5.tgz#afd9f3bca50cfff96aebeea3cc3853fd127267f7" - integrity sha512-BSdBBu5hOIv+gBJC9jzYMh5bC27FQwjWLSb8fVAniqlL9gvsqvK27xTgczMf+hgctlszMYQnRm3bpY/j8vhPqw== +"@vue/devtools-api@^7.7.5": + version "7.7.6" + resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.6.tgz" + integrity sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw== dependencies: - "@vue/devtools-kit" "^7.3.5" + "@vue/devtools-kit" "^7.7.6" -"@vue/devtools-kit@^7.3.5": - version "7.3.5" - resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.3.5.tgz#66669ee94da6c927fc28255477f65aae3f616316" - integrity sha512-wwfi10gJ1HMtjzcd8aIOnzBHlIRqsYDgcDyrKvkeyc0Gbcoe7UrkXRVHZUOtcxxoplHA0PwpT6wFg0uUCmi8Ww== +"@vue/devtools-kit@^7.7.6": + version "7.7.6" + resolved "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz" + integrity sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA== dependencies: - "@vue/devtools-shared" "^7.3.5" - birpc "^0.2.17" + "@vue/devtools-shared" "^7.7.6" + birpc "^2.3.0" hookable "^5.5.3" mitt "^3.0.1" perfect-debounce "^1.0.0" speakingurl "^14.0.1" - superjson "^2.2.1" + superjson "^2.2.2" -"@vue/devtools-shared@^7.3.5": - version "7.3.5" - resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.3.5.tgz#4c4020df6d71ab058518a8f3a272fc7f2682c5d8" - integrity sha512-Rqii3VazmWTi67a86rYopi61n5Ved05EybJCwyrfoO9Ok3MaS/4yRFl706ouoISMlyrASJFEzM0/AiDA6w4f9A== +"@vue/devtools-shared@^7.7.6": + version "7.7.6" + resolved "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz" + integrity sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA== dependencies: rfdc "^1.4.1" -"@vue/reactivity@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.31.tgz#eda80e90c4f9d7659efe1f5ed99c2dfdc9e93d77" - integrity sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q== +"@vue/reactivity@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz" + integrity sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA== dependencies: - "@vue/shared" "3.4.31" + "@vue/shared" "3.5.16" -"@vue/runtime-core@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.31.tgz#ad3a41ad76385c0429e3e4dbefb81918494e10cf" - integrity sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw== +"@vue/runtime-core@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz" + integrity sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ== dependencies: - "@vue/reactivity" "3.4.31" - "@vue/shared" "3.4.31" + "@vue/reactivity" "3.5.16" + "@vue/shared" "3.5.16" -"@vue/runtime-dom@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz#bae7ad844f944af33699c73581bc36125bab96ce" - integrity sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw== +"@vue/runtime-dom@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz" + integrity sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww== dependencies: - "@vue/reactivity" "3.4.31" - "@vue/runtime-core" "3.4.31" - "@vue/shared" "3.4.31" + "@vue/reactivity" "3.5.16" + "@vue/runtime-core" "3.5.16" + "@vue/shared" "3.5.16" csstype "^3.1.3" -"@vue/server-renderer@3.4.31": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.31.tgz#bbe990f793c36d62d05bdbbaf142511d53e159fd" - integrity sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA== +"@vue/server-renderer@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz" + integrity sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg== dependencies: - "@vue/compiler-ssr" "3.4.31" - "@vue/shared" "3.4.31" + "@vue/compiler-ssr" "3.5.16" + "@vue/shared" "3.5.16" -"@vue/shared@3.4.31", "@vue/shared@^3.4.27": - version "3.4.31" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.31.tgz#af9981f57def2c3f080c14bf219314fc0dc808a0" - integrity sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA== +"@vue/shared@^3.5.13", "@vue/shared@3.5.16": + version "3.5.16" + resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz" + integrity sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg== -"@vueuse/core@10.11.0", "@vueuse/core@^10.10.0": - version "10.11.0" - resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.11.0.tgz#b042585a8bf98bb29c177b33999bd0e3fcd9e65d" - integrity sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g== +"@vueuse/core@^13.1.0", "@vueuse/core@13.3.0": + version "13.3.0" + resolved "https://registry.npmjs.org/@vueuse/core/-/core-13.3.0.tgz" + integrity sha512-uYRz5oEfebHCoRhK4moXFM3NSCd5vu2XMLOq/Riz5FdqZMy2RvBtazdtL3gEcmDyqkztDe9ZP/zymObMIbiYSg== dependencies: - "@types/web-bluetooth" "^0.0.20" - "@vueuse/metadata" "10.11.0" - "@vueuse/shared" "10.11.0" - vue-demi ">=0.14.8" + "@types/web-bluetooth" "^0.0.21" + "@vueuse/metadata" "13.3.0" + "@vueuse/shared" "13.3.0" -"@vueuse/integrations@^10.10.0": - version "10.11.0" - resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-10.11.0.tgz#ce2746587172af9ab8faa713f42e619609ed0de1" - integrity sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg== +"@vueuse/integrations@^13.1.0": + version "13.3.0" + resolved "https://registry.npmjs.org/@vueuse/integrations/-/integrations-13.3.0.tgz" + integrity sha512-h5mGRYPbiTZTFP/AKELLPGnUDBly7z7Qd1pgEQlT3ItQ0NlZM0vB+8SOQycpSBOBlgg72Zgw+mi2r+4O/G8RuQ== dependencies: - "@vueuse/core" "10.11.0" - "@vueuse/shared" "10.11.0" - vue-demi ">=0.14.8" + "@vueuse/core" "13.3.0" + "@vueuse/shared" "13.3.0" -"@vueuse/metadata@10.11.0": - version "10.11.0" - resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.11.0.tgz#27be47cf115ee98e947a1bfcd0b1b5b35d785fb6" - integrity sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ== +"@vueuse/metadata@13.3.0": + version "13.3.0" + resolved "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.3.0.tgz" + integrity sha512-42IzJIOYCKIb0Yjv1JfaKpx8JlCiTmtCWrPxt7Ja6Wzoq0h79+YVXmBV03N966KEmDEESTbp5R/qO3AB5BDnGw== -"@vueuse/shared@10.11.0": - version "10.11.0" - resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.11.0.tgz#be09262b2c5857069ed3dadd1680f22c4cb6f984" - integrity sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A== +"@vueuse/shared@13.3.0": + version "13.3.0" + resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-13.3.0.tgz" + integrity sha512-L1QKsF0Eg9tiZSFXTgodYnu0Rsa2P0En2LuLrIs/jgrkyiDuJSsPZK+tx+wU0mMsYHUYEjNsuE41uqqkuR8VhA== + +algoliasearch@^5.14.2, "algoliasearch@>= 4.9.1 < 6": + version "5.25.0" + resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.25.0.tgz" + integrity sha512-n73BVorL4HIwKlfJKb4SEzAYkR3Buwfwbh+MYxg2mloFph2fFGV58E90QTzdbfzWrLn4HE5Czx/WTjI8fcHaMg== dependencies: - vue-demi ">=0.14.8" + "@algolia/client-abtesting" "5.25.0" + "@algolia/client-analytics" "5.25.0" + "@algolia/client-common" "5.25.0" + "@algolia/client-insights" "5.25.0" + "@algolia/client-personalization" "5.25.0" + "@algolia/client-query-suggestions" "5.25.0" + "@algolia/client-search" "5.25.0" + "@algolia/ingestion" "1.25.0" + "@algolia/monitoring" "1.25.0" + "@algolia/recommend" "5.25.0" + "@algolia/requester-browser-xhr" "5.25.0" + "@algolia/requester-fetch" "5.25.0" + "@algolia/requester-node-http" "5.25.0" -algoliasearch@^4.19.1: - version "4.24.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.24.0.tgz#b953b3e2309ef8f25da9de311b95b994ac918275" - integrity sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g== - dependencies: - "@algolia/cache-browser-local-storage" "4.24.0" - "@algolia/cache-common" "4.24.0" - "@algolia/cache-in-memory" "4.24.0" - "@algolia/client-account" "4.24.0" - "@algolia/client-analytics" "4.24.0" - "@algolia/client-common" "4.24.0" - "@algolia/client-personalization" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/logger-common" "4.24.0" - "@algolia/logger-console" "4.24.0" - "@algolia/recommend" "4.24.0" - "@algolia/requester-browser-xhr" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/requester-node-http" "4.24.0" - "@algolia/transporter" "4.24.0" +birpc@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz" + integrity sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g== -birpc@^0.2.17: - version "0.2.17" - resolved "https://registry.yarnpkg.com/birpc/-/birpc-0.2.17.tgz#d0bdb90d4d063061156637f03b7b0adea1779734" - integrity sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg== +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== copy-anything@^3.0.2: version "3.0.5" - resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.5.tgz#2d92dce8c498f790fa7ad16b01a1ae5a45b020a0" + resolved "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz" integrity sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w== dependencies: is-what "^4.1.8" csstype@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + entities@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -esbuild@^0.21.3: - version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" - integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== +esbuild@^0.25.0: + version "0.25.5" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz" + integrity sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ== optionalDependencies: - "@esbuild/aix-ppc64" "0.21.5" - "@esbuild/android-arm" "0.21.5" - "@esbuild/android-arm64" "0.21.5" - "@esbuild/android-x64" "0.21.5" - "@esbuild/darwin-arm64" "0.21.5" - "@esbuild/darwin-x64" "0.21.5" - "@esbuild/freebsd-arm64" "0.21.5" - "@esbuild/freebsd-x64" "0.21.5" - "@esbuild/linux-arm" "0.21.5" - "@esbuild/linux-arm64" "0.21.5" - "@esbuild/linux-ia32" "0.21.5" - "@esbuild/linux-loong64" "0.21.5" - "@esbuild/linux-mips64el" "0.21.5" - "@esbuild/linux-ppc64" "0.21.5" - "@esbuild/linux-riscv64" "0.21.5" - "@esbuild/linux-s390x" "0.21.5" - "@esbuild/linux-x64" "0.21.5" - "@esbuild/netbsd-x64" "0.21.5" - "@esbuild/openbsd-x64" "0.21.5" - "@esbuild/sunos-x64" "0.21.5" - "@esbuild/win32-arm64" "0.21.5" - "@esbuild/win32-ia32" "0.21.5" - "@esbuild/win32-x64" "0.21.5" + "@esbuild/aix-ppc64" "0.25.5" + "@esbuild/android-arm" "0.25.5" + "@esbuild/android-arm64" "0.25.5" + "@esbuild/android-x64" "0.25.5" + "@esbuild/darwin-arm64" "0.25.5" + "@esbuild/darwin-x64" "0.25.5" + "@esbuild/freebsd-arm64" "0.25.5" + "@esbuild/freebsd-x64" "0.25.5" + "@esbuild/linux-arm" "0.25.5" + "@esbuild/linux-arm64" "0.25.5" + "@esbuild/linux-ia32" "0.25.5" + "@esbuild/linux-loong64" "0.25.5" + "@esbuild/linux-mips64el" "0.25.5" + "@esbuild/linux-ppc64" "0.25.5" + "@esbuild/linux-riscv64" "0.25.5" + "@esbuild/linux-s390x" "0.25.5" + "@esbuild/linux-x64" "0.25.5" + "@esbuild/netbsd-arm64" "0.25.5" + "@esbuild/netbsd-x64" "0.25.5" + "@esbuild/openbsd-arm64" "0.25.5" + "@esbuild/openbsd-x64" "0.25.5" + "@esbuild/sunos-x64" "0.25.5" + "@esbuild/win32-arm64" "0.25.5" + "@esbuild/win32-ia32" "0.25.5" + "@esbuild/win32-x64" "0.25.5" estree-walker@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -focus-trap@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.5.4.tgz#6c4e342fe1dae6add9c2aa332a6e7a0bbd495ba2" - integrity sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w== +fdir@^6.4.4: + version "6.4.5" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz" + integrity sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw== + +focus-trap@^7, focus-trap@^7.6.4: + version "7.6.5" + resolved "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz" + integrity sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg== dependencies: tabbable "^6.2.0" fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +hast-util-to-html@^9.0.5: + version "9.0.5" + resolved "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + hookable@^5.5.3: version "5.5.3" - resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" + resolved "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz" integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + is-what@^4.1.8: version "4.1.16" - resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.16.tgz#1ad860a19da8b4895ad5495da3182ce2acdd7a6f" + resolved "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz" integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A== -magic-string@^0.30.10: - version "0.30.10" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" - integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== dependencies: - "@jridgewell/sourcemap-codec" "^1.4.15" + "@jridgewell/sourcemap-codec" "^1.5.0" mark.js@8.11.1: version "8.11.1" - resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5" + resolved "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz" integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== -minisearch@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/minisearch/-/minisearch-6.3.0.tgz#985a2f1ca3c73c2d65af94f0616bfe57164b0b6b" - integrity sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ== +mdast-util-to-hast@^13.0.0: + version "13.2.0" + resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + +minisearch@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz" + integrity sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA== mitt@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== -nanoid@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +oniguruma-parser@^0.12.1: + version "0.12.1" + resolved "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz" + integrity sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w== + +oniguruma-to-es@^4.3.3: + version "4.3.3" + resolved "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz" + integrity sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg== + dependencies: + oniguruma-parser "^0.12.1" + regex "^6.0.1" + regex-recursion "^6.0.2" perfect-debounce@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz" integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== -picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -postcss@^8.4.38: - version "8.4.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3" - integrity sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw== +"picomatch@^3 || ^4", picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + +postcss@^8, postcss@^8.5.3: + version "8.5.4" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz" + integrity sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w== dependencies: - nanoid "^3.3.7" - picocolors "^1.0.1" - source-map-js "^1.2.0" + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" preact@^10.0.0: version "10.22.0" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.22.0.tgz#a50f38006ae438d255e2631cbdaf7488e6dd4e16" + resolved "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz" integrity sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw== +property-information@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz" + integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== + +regex-recursion@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz" + integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== + dependencies: + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz" + integrity sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA== + dependencies: + regex-utilities "^2.3.0" + rfdc@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== -rollup@^4.13.0: - version "4.18.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.18.0.tgz#497f60f0c5308e4602cf41136339fbf87d5f5dda" - integrity sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg== +rollup@^4.34.9: + version "4.41.1" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz" + integrity sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw== dependencies: - "@types/estree" "1.0.5" + "@types/estree" "1.0.7" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.18.0" - "@rollup/rollup-android-arm64" "4.18.0" - "@rollup/rollup-darwin-arm64" "4.18.0" - "@rollup/rollup-darwin-x64" "4.18.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.18.0" - "@rollup/rollup-linux-arm-musleabihf" "4.18.0" - "@rollup/rollup-linux-arm64-gnu" "4.18.0" - "@rollup/rollup-linux-arm64-musl" "4.18.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.18.0" - "@rollup/rollup-linux-riscv64-gnu" "4.18.0" - "@rollup/rollup-linux-s390x-gnu" "4.18.0" - "@rollup/rollup-linux-x64-gnu" "4.18.0" - "@rollup/rollup-linux-x64-musl" "4.18.0" - "@rollup/rollup-win32-arm64-msvc" "4.18.0" - "@rollup/rollup-win32-ia32-msvc" "4.18.0" - "@rollup/rollup-win32-x64-msvc" "4.18.0" + "@rollup/rollup-android-arm-eabi" "4.41.1" + "@rollup/rollup-android-arm64" "4.41.1" + "@rollup/rollup-darwin-arm64" "4.41.1" + "@rollup/rollup-darwin-x64" "4.41.1" + "@rollup/rollup-freebsd-arm64" "4.41.1" + "@rollup/rollup-freebsd-x64" "4.41.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.41.1" + "@rollup/rollup-linux-arm-musleabihf" "4.41.1" + "@rollup/rollup-linux-arm64-gnu" "4.41.1" + "@rollup/rollup-linux-arm64-musl" "4.41.1" + "@rollup/rollup-linux-loongarch64-gnu" "4.41.1" + "@rollup/rollup-linux-powerpc64le-gnu" "4.41.1" + "@rollup/rollup-linux-riscv64-gnu" "4.41.1" + "@rollup/rollup-linux-riscv64-musl" "4.41.1" + "@rollup/rollup-linux-s390x-gnu" "4.41.1" + "@rollup/rollup-linux-x64-gnu" "4.41.1" + "@rollup/rollup-linux-x64-musl" "4.41.1" + "@rollup/rollup-win32-arm64-msvc" "4.41.1" + "@rollup/rollup-win32-ia32-msvc" "4.41.1" + "@rollup/rollup-win32-x64-msvc" "4.41.1" fsevents "~2.3.2" -shiki@1.10.0, shiki@^1.6.2: - version "1.10.0" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.10.0.tgz#304ab080a12458abc78eb0cb83eb0f7ace546215" - integrity sha512-YD2sXQ+TMD/F9BimV9Jn0wj35pqOvywvOG/3PB6hGHyGKlM7TJ9tyJ02jOb2kF8F0HfJwKNYrh3sW7jEcuRlXA== - dependencies: - "@shikijs/core" "1.10.0" +"search-insights@>= 1 < 3": + version "2.17.3" + resolved "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz" + integrity sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ== -source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +shiki@^3.2.2: + version "3.4.2" + resolved "https://registry.npmjs.org/shiki/-/shiki-3.4.2.tgz" + integrity sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ== + dependencies: + "@shikijs/core" "3.4.2" + "@shikijs/engine-javascript" "3.4.2" + "@shikijs/engine-oniguruma" "3.4.2" + "@shikijs/langs" "3.4.2" + "@shikijs/themes" "3.4.2" + "@shikijs/types" "3.4.2" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== speakingurl@^14.0.1: version "14.0.1" - resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53" + resolved "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz" integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== -superjson@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/superjson/-/superjson-2.2.1.tgz#9377a7fa80fedb10c851c9dbffd942d4bcf79733" - integrity sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA== +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + +superjson@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz" + integrity sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q== dependencies: copy-anything "^3.0.2" tabbable@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" + resolved "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz" integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== -vite@^5.2.12: - version "5.3.2" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.2.tgz#2f0a8531c71060467ed3e0a205a203f269b6d9c8" - integrity sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA== +tinyglobby@^0.2.13: + version "0.2.14" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz" + integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ== dependencies: - esbuild "^0.21.3" - postcss "^8.4.38" - rollup "^4.13.0" + fdir "^6.4.4" + picomatch "^4.0.2" + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + +"vite@^5.0.0 || ^6.0.0", vite@^6.3.2: + version "6.3.5" + resolved "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz" + integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ== + dependencies: + esbuild "^0.25.0" + fdir "^6.4.4" + picomatch "^4.0.2" + postcss "^8.5.3" + rollup "^4.34.9" + tinyglobby "^0.2.13" optionalDependencies: fsevents "~2.3.3" -vitepress@^1.0.0-rc.35: - version "1.2.3" - resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.2.3.tgz#a507d2f5e86c1fbcdb5ec2212f1db4828504df34" - integrity sha512-GvEsrEeNLiDE1+fuwDAYJCYLNZDAna+EtnXlPajhv/MYeTjbNK6Bvyg6NoTdO1sbwuQJ0vuJR99bOlH53bo6lg== +vitepress@^2.0.0-alpha.5: + version "2.0.0-alpha.5" + resolved "https://registry.npmjs.org/vitepress/-/vitepress-2.0.0-alpha.5.tgz" + integrity sha512-fhuGpJ4CETS/lrAHjKu3m88HwesZvAjZLFeIRr9Jejmewyogn1tm2L6lsVg7PWxPmOGoMfihzl3+L6jg6hrTnA== dependencies: - "@docsearch/css" "^3.6.0" - "@docsearch/js" "^3.6.0" - "@shikijs/core" "^1.6.2" - "@shikijs/transformers" "^1.6.2" - "@types/markdown-it" "^14.1.1" - "@vitejs/plugin-vue" "^5.0.5" - "@vue/devtools-api" "^7.2.1" - "@vue/shared" "^3.4.27" - "@vueuse/core" "^10.10.0" - "@vueuse/integrations" "^10.10.0" - focus-trap "^7.5.4" + "@docsearch/css" "^3.9.0" + "@docsearch/js" "^3.9.0" + "@iconify-json/simple-icons" "^1.2.32" + "@shikijs/core" "^3.2.2" + "@shikijs/transformers" "^3.2.2" + "@shikijs/types" "^3.2.2" + "@vitejs/plugin-vue" "^5.2.3" + "@vue/devtools-api" "^7.7.5" + "@vue/shared" "^3.5.13" + "@vueuse/core" "^13.1.0" + "@vueuse/integrations" "^13.1.0" + focus-trap "^7.6.4" mark.js "8.11.1" - minisearch "^6.3.0" - shiki "^1.6.2" - vite "^5.2.12" - vue "^3.4.27" + minisearch "^7.1.2" + shiki "^3.2.2" + vite "^6.3.2" + vue "^3.5.13" -vue-demi@>=0.14.8: - version "0.14.8" - resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.8.tgz#00335e9317b45e4a68d3528aaf58e0cec3d5640a" - integrity sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q== - -vue@^3.2.47, vue@^3.4.27: - version "3.4.31" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.31.tgz#83a3c4dab8302b0e974b0d4b92a2f6a6378ae797" - integrity sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ== +vue@^3.2.25, vue@^3.2.47, vue@^3.5.0, vue@^3.5.13, vue@3.5.16: + version "3.5.16" + resolved "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz" + integrity sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w== dependencies: - "@vue/compiler-dom" "3.4.31" - "@vue/compiler-sfc" "3.4.31" - "@vue/runtime-dom" "3.4.31" - "@vue/server-renderer" "3.4.31" - "@vue/shared" "3.4.31" + "@vue/compiler-dom" "3.5.16" + "@vue/compiler-sfc" "3.5.16" + "@vue/runtime-dom" "3.5.16" + "@vue/server-renderer" "3.5.16" + "@vue/shared" "3.5.16" + +zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==