Merge branch 'main' into feat/intl-win

This commit is contained in:
Marc 2025-06-25 10:28:45 +07:00 committed by GitHub
commit 2963ced1d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
164 changed files with 3374 additions and 1744 deletions

View File

@ -7,7 +7,11 @@
> If your PR involves the changes mentioned below and completed the action, please tick the corresponding option. > 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 a modification is not involved, please skip it directly.
- [ ] If you modified `*.php`, run `composer cs-fix` at local machine. - If you modified `*.php` or `*.json`, run them locally to ensure your changes are valid:
- [ ] If it's an extension or dependency update, make sure adding related extensions in `src/global/test-extensions.php`. - [ ] `PHP_CS_FIXER_IGNORE_ENV=1 composer cs-fix`
- [ ] If you changed the behavior of static-php-cli, update docs in `./docs/`. - [ ] `composer analyse`
- [ ] If you updated `config/xxx.json` content, run `bin/spc dev:sort-config xxx`. - [ ] `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.

View File

@ -1,16 +1,14 @@
name: "Extension matrix tests" name: "Extension Matrix Tests"
on: on:
workflow_dispatch: workflow_dispatch:
pull_request: push:
branches: [ "main" ]
paths:
- '.github/workflows/ext-matrix-tests.yml'
jobs: jobs:
test: test:
name: "${{ matrix.extension }} (PHP ${{ matrix.php-version }} on ${{ matrix.operating-system }})" name: "${{ matrix.extension }} (PHP ${{ matrix.php-version }} on ${{ matrix.operating-system }})"
runs-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: strategy:
fail-fast: false fail-fast: false
matrix: matrix:

3
.gitignore vendored
View File

@ -10,6 +10,9 @@ docker/source/
# default source extract directory # default source extract directory
/source/** /source/**
# built by shared embed tests
/locale/
# default source download directory # default source download directory
/downloads/** /downloads/**

View File

@ -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` 来获取编译时的配置。 如果你知道 [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)。
## 贡献 ## 贡献

View File

@ -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 may require the introduction of other libraries during compilation,
you can use `buildroot/bin/php-config` to obtain the compile-time configuration. 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 ## Contribution

View File

@ -1,12 +1,14 @@
#!/usr/bin/env sh #!/usr/bin/env bash
set -e
# This file is using docker to run commands # This file is using docker to run commands
SPC_DOCKER_VERSION=v3 SPC_DOCKER_VERSION=v4
# Detect docker can run # Detect docker can run
if ! which docker >/dev/null; then if ! which docker >/dev/null; then
echo "Docker is not installed, please install docker first !" echo "Docker is not installed, please install docker first !"
exit 1 exit 1
fi fi
DOCKER_EXECUTABLE="docker" DOCKER_EXECUTABLE="docker"
# shellcheck disable=SC2046 # shellcheck disable=SC2046
@ -22,27 +24,48 @@ if [ $(id -u) -ne 0 ]; then
fi fi
fi fi
# Convert uname to gnu arch
CURRENT_ARCH=$(uname -m)
# to check if qemu-docker run if [ "$CURRENT_ARCH" = "arm64" ]; then
if [ "$SPC_USE_ARCH" = "" ]; then CURRENT_ARCH=aarch64
SPC_USE_ARCH=x86_64
fi fi
if [ -z "$SPC_USE_ARCH" ]; then
SPC_USE_ARCH=$CURRENT_ARCH
fi
# parse SPC_USE_ARCH
case $SPC_USE_ARCH in case $SPC_USE_ARCH in
x86_64) x86_64|amd64)
ALPINE_FROM=alpine:edge 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) aarch64|arm64)
ALPINE_FROM=multiarch/alpine:aarch64-edge SPC_USE_ARCH=aarch64
# shellcheck disable=SC2039 if [ "$CURRENT_ARCH" != "aarch64" ]; then
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m" PLATFORM_ARG="--platform linux/arm64"
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null ALPINE_FROM=multiarch/alpine:aarch64-edge
fi
;; ;;
*) *)
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH" echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
exit 1 exit 1
;; ;;
esac 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 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" 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 # Detect docker env is setup
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then
echo "Docker container does not exist. Building docker image ..." echo "Docker container does not exist. Building docker image ..."
$DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF $DOCKER_EXECUTABLE build $PLATFORM_ARG -t cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF
FROM $ALPINE_FROM FROM $ALPINE_FROM
$SPC_USE_MIRROR $SPC_USE_MIRROR
RUN apk update; \ RUN apk update; \
@ -84,7 +107,8 @@ RUN apk update; \
wget \ wget \
xz \ xz \
gettext-dev \ gettext-dev \
binutils-gold binutils-gold \
patchelf
RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \ RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \
chmod +x /usr/local/bin/php chmod +x /usr/local/bin/php
@ -147,7 +171,7 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
echo "* ./pkgroot: $(pwd)/pkgroot" echo "* ./pkgroot: $(pwd)/pkgroot"
echo "*" echo "*"
set -ex set -ex
$DOCKER_EXECUTABLE run --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash
else else
$DOCKER_EXECUTABLE run --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@ $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@
fi fi

View File

@ -1,5 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
# This file is using docker to run commands
SPC_DOCKER_VERSION=v4
# Detect docker can run # Detect docker can run
if ! which docker >/dev/null; then if ! which docker >/dev/null; then
echo "Docker is not installed, please install docker first !" echo "Docker is not installed, please install docker first !"
@ -19,35 +24,47 @@ if [ $(id -u) -ne 0 ]; then
fi fi
fi fi
# Convert uname to gnu arch
CURRENT_ARCH=$(uname -m)
# to check if qemu-docker run if [ "$CURRENT_ARCH" = "arm64" ]; then
if [ "$SPC_USE_ARCH" = "" ]; then CURRENT_ARCH=aarch64
SPC_USE_ARCH=current
fi fi
if [ -z "$SPC_USE_ARCH" ]; then
SPC_USE_ARCH=$CURRENT_ARCH
fi
# parse SPC_USE_ARCH
case $SPC_USE_ARCH in case $SPC_USE_ARCH in
current) x86_64|amd64)
BASE_ARCH=$(uname -m) SPC_USE_ARCH=x86_64
if [ "$BASE_ARCH" = "arm64" ]; then SPC_USE_ARCH_DOCKER=amd64
BASE_ARCH=aarch64 if [ "$CURRENT_ARCH" != "x86_64" ]; then
PLATFORM_ARG="--platform linux/amd64"
fi fi
;; ;;
aarch64) aarch64|arm64)
BASE_ARCH=aarch64 SPC_USE_ARCH=aarch64
# shellcheck disable=SC2039 SPC_USE_ARCH_DOCKER=arm64
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m" if [ "$CURRENT_ARCH" != "aarch64" ]; then
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null PLATFORM_ARG="--platform linux/arm64"
fi
;; ;;
*) *)
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH" echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
exit 1 exit 1
;; ;;
esac 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 # 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 ..." echo "Docker container does not exist. Building docker image ..."
$DOCKER_EXECUTABLE build -t cwcc-spc-gnu-$SPC_USE_ARCH -f- . <<EOF $DOCKER_EXECUTABLE buildx build $PLATFORM_ARG --no-cache -t cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF
FROM centos:7 FROM centos:7
RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \ RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \
sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \ sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
@ -59,7 +76,7 @@ RUN yum clean all && \
RUN yum install -y centos-release-scl RUN yum install -y centos-release-scl
RUN if [ "$BASE_ARCH" = "aarch64" ]; then \ RUN if [ "$SPC_USE_ARCH" = "aarch64" ]; then \
sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo ; \ sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo ; \
sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl.repo ; \ sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl.repo ; \
else \ else \
@ -72,8 +89,14 @@ RUN yum update -y && \
yum install -y devtoolset-10-gcc-* yum install -y devtoolset-10-gcc-*
RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc
RUN source /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 && \ mkdir /cmake && \
tar -xzf cmake.tgz -C /cmake --strip-components 1 tar -xzf cmake.tgz -C /cmake --strip-components 1
@ -88,7 +111,7 @@ ENV PATH="/app/bin:/cmake/bin:$PATH"
ENV SPC_LIBC=glibc ENV SPC_LIBC=glibc
ADD ./config/env.ini /app/config/env.ini 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 && \ RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \
tar -zxvf make.tgz && \ 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 '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 '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 '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_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_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 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 "* ./pkgroot: $(pwd)/pkgroot"
echo "*" echo "*"
set -ex 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 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 fi

View File

@ -42,6 +42,9 @@ SPC_CONCURRENCY=${CPU_COUNT}
SPC_SKIP_PHP_VERSION_CHECK="no" 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") ; 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="" 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 ; 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 ; only useful for builds targeting not pure-static linking
; default paths ; default paths
@ -66,10 +69,10 @@ SPC_LIBC=musl
CC=${SPC_LINUX_DEFAULT_CC} CC=${SPC_LINUX_DEFAULT_CC}
CXX=${SPC_LINUX_DEFAULT_CXX} CXX=${SPC_LINUX_DEFAULT_CXX}
AR=${SPC_LINUX_DEFAULT_AR} 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 ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
SPC_DEFAULT_C_FLAGS="-fPIC" SPC_DEFAULT_C_FLAGS="-fPIC -Os"
SPC_DEFAULT_CXX_FLAGS= 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) ; 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= SPC_EXTRA_LIBS=
; upx executable path ; upx executable path
@ -81,7 +84,7 @@ SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime
; buildconf command ; buildconf command
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force" SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command ; 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 ; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}" SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}"
; embed type for php, static (libphp.a) or shared (libphp.so) ; 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 ; LIBS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm" SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
; EXTRA_CFLAGS for `make` php ; 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 ; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm" 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 ; EXTRA_LDFLAGS_PROGRAM for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" 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 CC=clang
CXX=clang++ CXX=clang++
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
SPC_DEFAULT_C_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" 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) ; 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= SPC_EXTRA_LIBS=
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; 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 ; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}" SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; EXTRA_CFLAGS for `make` php ; 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 ; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv" SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv"
; embed type for php, static (libphp.a) or shared (libphp.dylib) ; embed type for php, static (libphp.a) or shared (libphp.dylib)

View File

@ -24,6 +24,14 @@
"bcmath": { "bcmath": {
"type": "builtin" "type": "builtin"
}, },
"brotli": {
"type": "external",
"source": "ext-brotli",
"arg-type": "enable",
"lib-depends": [
"brotli"
]
},
"bz2": { "bz2": {
"type": "builtin", "type": "builtin",
"arg-type-unix": "with-prefix", "arg-type-unix": "with-prefix",
@ -95,7 +103,10 @@
"ev": { "ev": {
"type": "external", "type": "external",
"source": "ev", "source": "ev",
"arg-type-windows": "with" "arg-type-windows": "with",
"ext-depends": [
"sockets"
]
}, },
"event": { "event": {
"support": { "support": {
@ -124,10 +135,6 @@
"Linux": "partial", "Linux": "partial",
"BSD": "wip" "BSD": "wip"
}, },
"target": [
"static",
"shared"
],
"notes": true, "notes": true,
"arg-type": "custom", "arg-type": "custom",
"type": "builtin", "type": "builtin",
@ -334,6 +341,9 @@
"ext-depends": [ "ext-depends": [
"xml" "xml"
] ]
"target": [
"static"
]
}, },
"lz4": { "lz4": {
"support": { "support": {
@ -350,6 +360,9 @@
"mbregex": { "mbregex": {
"type": "builtin", "type": "builtin",
"arg-type": "custom", "arg-type": "custom",
"target": [
"static"
],
"ext-depends": [ "ext-depends": [
"mbstring" "mbstring"
], ],
@ -382,13 +395,13 @@
"ext-depends": [ "ext-depends": [
"zlib", "zlib",
"session" "session"
] ],
"build-with-php": true
}, },
"memcached": { "memcached": {
"support": { "support": {
"Windows": "wip", "Windows": "wip",
"BSD": "wip", "BSD": "wip"
"Linux": "no"
}, },
"type": "external", "type": "external",
"source": "memcached", "source": "memcached",
@ -397,6 +410,10 @@
"lib-depends": [ "lib-depends": [
"libmemcached" "libmemcached"
], ],
"lib-depends-unix": [
"libmemcached",
"fastlz"
],
"ext-depends": [ "ext-depends": [
"session", "session",
"zlib" "zlib"
@ -415,6 +432,10 @@
"openssl", "openssl",
"zstd", "zstd",
"zlib" "zlib"
],
"frameworks": [
"CoreFoundation",
"Security"
] ]
}, },
"msgpack": { "msgpack": {
@ -424,7 +445,10 @@
"type": "external", "type": "external",
"source": "msgpack", "source": "msgpack",
"arg-type-unix": "with", "arg-type-unix": "with",
"arg-type-win": "enable" "arg-type-win": "enable",
"ext-depends": [
"session"
]
}, },
"mysqli": { "mysqli": {
"type": "builtin", "type": "builtin",
@ -463,7 +487,8 @@
}, },
"opcache": { "opcache": {
"type": "builtin", "type": "builtin",
"arg-type-unix": "custom" "arg-type-unix": "custom",
"zend-extension": true
}, },
"openssl": { "openssl": {
"notes": true, "notes": true,
@ -652,6 +677,9 @@
"arg-type": "with-prefix", "arg-type": "with-prefix",
"lib-depends": [ "lib-depends": [
"readline" "readline"
],
"target": [
"static"
] ]
}, },
"redis": { "redis": {
@ -671,10 +699,12 @@
] ]
}, },
"session": { "session": {
"type": "builtin" "type": "builtin",
"build-with-php": true
}, },
"shmop": { "shmop": {
"type": "builtin" "type": "builtin",
"build-with-php": true
}, },
"simdjson": { "simdjson": {
"type": "external", "type": "external",
@ -692,7 +722,8 @@
], ],
"ext-depends-windows": [ "ext-depends-windows": [
"xml" "xml"
] ],
"build-with-php": true
}, },
"snappy": { "snappy": {
"support": { "support": {
@ -785,7 +816,7 @@
"lib-depends": [ "lib-depends": [
"libssh2" "libssh2"
], ],
"ext-depends-windows": [ "ext-depends": [
"openssl", "openssl",
"zlib" "zlib"
] ]
@ -795,9 +826,6 @@
"Windows": "no", "Windows": "no",
"BSD": "wip" "BSD": "wip"
}, },
"target": [
"static"
],
"notes": true, "notes": true,
"type": "external", "type": "external",
"source": "swoole", "source": "swoole",
@ -915,7 +943,8 @@
] ]
}, },
"tokenizer": { "tokenizer": {
"type": "builtin" "type": "builtin",
"build-with-php": true
}, },
"uuid": { "uuid": {
"support": { "support": {
@ -956,7 +985,8 @@
"Darwin": "partial", "Darwin": "partial",
"Linux": "partial" "Linux": "partial"
}, },
"notes": true "notes": true,
"zend-extension": true
}, },
"xhprof": { "xhprof": {
"support": { "support": {
@ -968,7 +998,8 @@
"source": "xhprof", "source": "xhprof",
"ext-depends": [ "ext-depends": [
"ctype" "ctype"
] ],
"build-with-php": true
}, },
"xlswriter": { "xlswriter": {
"support": { "support": {
@ -998,7 +1029,8 @@
], ],
"ext-depends-windows": [ "ext-depends-windows": [
"iconv" "iconv"
] ],
"build-with-php": true
}, },
"xmlreader": { "xmlreader": {
"support": { "support": {
@ -1012,7 +1044,8 @@
"ext-depends-windows": [ "ext-depends-windows": [
"xml", "xml",
"dom" "dom"
] ],
"build-with-php": true
}, },
"xmlwriter": { "xmlwriter": {
"support": { "support": {
@ -1025,7 +1058,8 @@
], ],
"ext-depends-windows": [ "ext-depends-windows": [
"xml" "xml"
] ],
"build-with-php": true
}, },
"xsl": { "xsl": {
"support": { "support": {
@ -1042,6 +1076,14 @@
"dom" "dom"
] ]
}, },
"xz": {
"type": "external",
"source": "ext-xz",
"arg-type": "with",
"lib-depends": [
"xz"
]
},
"yac": { "yac": {
"support": { "support": {
"BSD": "wip" "BSD": "wip"
@ -1049,6 +1091,9 @@
"type": "external", "type": "external",
"source": "yac", "source": "yac",
"arg-type-unix": "custom", "arg-type-unix": "custom",
"lib-depends-unix": [
"fastlz"
],
"ext-depends-unix": [ "ext-depends-unix": [
"igbinary" "igbinary"
] ]
@ -1069,9 +1114,6 @@
"support": { "support": {
"BSD": "wip" "BSD": "wip"
}, },
"target": [
"static"
],
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-prefix",
"arg-type-windows": "enable", "arg-type-windows": "enable",
@ -1095,6 +1137,9 @@
"arg-type-windows": "enable", "arg-type-windows": "enable",
"lib-depends": [ "lib-depends": [
"zlib" "zlib"
],
"target": [
"static"
] ]
}, },
"zstd": { "zstd": {

View File

@ -101,6 +101,15 @@
"SystemConfiguration" "SystemConfiguration"
] ]
}, },
"fastlz": {
"source": "fastlz",
"static-libs-unix": [
"libfastlz.a"
],
"headers": [
"fastlz/fastlz.h"
]
},
"freetype": { "freetype": {
"source": "freetype", "source": "freetype",
"static-libs-unix": [ "static-libs-unix": [
@ -246,8 +255,8 @@
"ldap": { "ldap": {
"source": "ldap", "source": "ldap",
"static-libs-unix": [ "static-libs-unix": [
"liblber.a", "libldap.a",
"libldap.a" "liblber.a"
], ],
"lib-depends": [ "lib-depends": [
"openssl", "openssl",
@ -401,7 +410,9 @@
"source": "libmemcached", "source": "libmemcached",
"static-libs-unix": [ "static-libs-unix": [
"libmemcached.a", "libmemcached.a",
"libmemcachedutil.a" "libmemcachedprotocol.a",
"libmemcachedutil.a",
"libhashkit.a"
] ]
}, },
"libpng": { "libpng": {
@ -855,5 +866,14 @@
"zstd.h", "zstd.h",
"zstd_errors.h" "zstd_errors.h"
] ]
},
"watcher": {
"source": "watcher",
"static-libs-unix": [
"libwatcher-c.a"
],
"headers": [
"wtr/watcher-c.h"
]
} }
} }

View File

@ -42,5 +42,17 @@
"extract-files": { "extract-files": {
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe" "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"
} }
} }

View File

@ -50,8 +50,9 @@
} }
}, },
"brotli": { "brotli": {
"type": "ghtar", "type": "ghtagtar",
"repo": "google/brotli", "repo": "google/brotli",
"match": "v1\\.\\d.*",
"provide-pre-built": true, "provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
@ -102,6 +103,16 @@
"path": "LICENSE" "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": { "ext-ds": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/ds", "url": "https://pecl.php.net/get/ds",
@ -241,6 +252,16 @@
"path": "LICENSE" "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": { "ext-zstd": {
"type": "git", "type": "git",
"path": "php-src/ext/zstd", "path": "php-src/ext/zstd",
@ -251,6 +272,15 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"fastlz": {
"type": "git",
"url": "https://github.com/ariya/FastLZ.git",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE.MIT"
}
},
"freetype": { "freetype": {
"type": "git", "type": "git",
"rev": "VER-2-13-2", "rev": "VER-2-13-2",
@ -515,12 +545,12 @@
} }
}, },
"libmemcached": { "libmemcached": {
"type": "git", "type": "ghtagtar",
"url": "https://github.com/static-php/libmemcached-macos.git", "repo": "awesomized/libmemcached",
"rev": "master", "match": "1.\\d.\\d",
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "LICENSE"
} }
}, },
"libpng": { "libpng": {
@ -809,8 +839,9 @@
} }
}, },
"postgresql": { "postgresql": {
"type": "url", "type": "ghtagtar",
"url": "https://ftp.postgresql.org/pub/source/v16.2/postgresql-16.2.tar.bz2", "repo": "postgres/postgres",
"match": "REL_16_\\d+",
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYRIGHT" "path": "COPYRIGHT"
@ -1047,5 +1078,14 @@
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
},
"watcher": {
"type": "ghtar",
"repo": "e-dant/watcher",
"prefer-stable": true,
"license": {
"type": "file",
"path": "license"
}
} }
} }

View File

@ -30,6 +30,7 @@
</div> </div>
</div> </div>
<div class="my-btn" v-if="selectedSystem !== 'windows'" @click="selectCommon">{{ I18N[lang].selectCommon }}</div> <div class="my-btn" v-if="selectedSystem !== 'windows'" @click="selectCommon">{{ I18N[lang].selectCommon }}</div>
<div class="my-btn" v-if="selectedSystem !== 'windows'" @click="selectAll">{{ I18N[lang].selectAll }}</div>
<div class="my-btn" @click="checkedExts = []">{{ I18N[lang].selectNone }}</div> <div class="my-btn" @click="checkedExts = []">{{ I18N[lang].selectNone }}</div>
<details class="details custom-block" open> <details class="details custom-block" open>
@ -44,6 +45,7 @@
<div class="tip custom-block"> <div class="tip custom-block">
<p class="custom-block-title">TIP</p> <p class="custom-block-title">TIP</p>
<p>{{ I18N[lang].depTips }}</p> <p>{{ I18N[lang].depTips }}</p>
<p>{{ I18N[lang].depTips2 }}</p>
</div> </div>
<h2>{{ I18N[lang].buildTarget }}</h2> <h2>{{ I18N[lang].buildTarget }}</h2>
<div class="box"> <div class="box">
@ -252,6 +254,7 @@ const I18N = {
no: '否', no: '否',
resultShow: '结果展示', resultShow: '结果展示',
selectCommon: '选择常用扩展', selectCommon: '选择常用扩展',
selectAll: '选择全部',
selectNone: '全部取消选择', selectNone: '全部取消选择',
useZTS: '是否编译线程安全版', useZTS: '是否编译线程安全版',
hardcodedINI: '硬编码 INI 选项', hardcodedINI: '硬编码 INI 选项',
@ -267,6 +270,7 @@ const I18N = {
selectedSystem: '选择操作系统', selectedSystem: '选择操作系统',
buildLibs: '要构建的库', buildLibs: '要构建的库',
depTips: '选择扩展后,不可选中的项目为必需的依赖,编译的依赖库列表中可选的为现有扩展和依赖库的可选依赖。选择可选依赖后,将生成 --with-libs 参数。', depTips: '选择扩展后,不可选中的项目为必需的依赖,编译的依赖库列表中可选的为现有扩展和依赖库的可选依赖。选择可选依赖后,将生成 --with-libs 参数。',
depTips2: '无法同时构建所有扩展,因为有些扩展之间相互冲突。请根据需要选择扩展。',
microUnavailable: 'micro 不支持 PHP 7.4 及更早版本!', microUnavailable: 'micro 不支持 PHP 7.4 及更早版本!',
windowsSAPIUnavailable: 'Windows 目前不支持 fpm、embed 构建!', windowsSAPIUnavailable: 'Windows 目前不支持 fpm、embed 构建!',
useUPX: '是否开启 UPX 压缩(减小二进制体积)', useUPX: '是否开启 UPX 压缩(减小二进制体积)',
@ -286,6 +290,7 @@ const I18N = {
no: 'No', no: 'No',
resultShow: 'Result', resultShow: 'Result',
selectCommon: 'Select common extensions', selectCommon: 'Select common extensions',
selectAll: 'Select all',
selectNone: 'Unselect all', selectNone: 'Unselect all',
useZTS: 'Enable ZTS', useZTS: 'Enable ZTS',
hardcodedINI: 'Hardcoded INI options', hardcodedINI: 'Hardcoded INI options',
@ -301,6 +306,7 @@ const I18N = {
selectedSystem: 'Select Build OS', selectedSystem: 'Select Build OS',
buildLibs: 'Select Dependencies', 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.', 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!', microUnavailable: 'Micro does not support PHP 7.4 and earlier versions!',
windowsSAPIUnavailable: 'Windows does not support fpm and embed build!', windowsSAPIUnavailable: 'Windows does not support fpm and embed build!',
useUPX: 'Enable UPX compression (reduce binary size)', useUPX: 'Enable UPX compression (reduce binary size)',
@ -337,6 +343,10 @@ const selectCommon = () => {
]; ];
}; };
const selectAll = () => {
checkedExts.value = extFilter.value;
};
const extList = computed(() => { const extList = computed(() => {
return checkedExts.value.join(','); return checkedExts.value.join(',');
}); });
@ -364,7 +374,7 @@ const checkedTargets = ref(['cli']);
const selectedEnv = ref('spc'); const selectedEnv = ref('spc');
// chosen php version // chosen php version
const selectedPhpVersion = ref('8.2'); const selectedPhpVersion = ref('8.4');
// chosen debug // chosen debug
const debug = ref(0); const debug = ref(0);

View File

@ -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) # 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 enable-micro-win32: false
# Build options for shared extensions (list or comma-separated are both accepted)
shared-extensions: [ ]
# Download options # Download options
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") # Use custom url for specified sources, format: "{source-name}:{url}" (e.g. "php-src:https://example.com/php-8.4.0.tar.gz")

View File

@ -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`. 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`. 1. Build shared extension `xxx.so` using: `--build-shared=XXX` option. e.g. `bin/spc build bcmath,zlib --build-shared=xdebug --build-cli`
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. 2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`.
3. Use the `phpize && ./configure && make` command to compile the extensions you want to use. 3. The `xdebug.so` file could be used for php that version and thread-safe are the same.
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
```
## Can it support Oracle database extension? ## Can it support Oracle database extension?

View File

@ -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, 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. 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, 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.
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.

View File

@ -151,8 +151,7 @@ Parallel is only supported on PHP 8.0 ZTS and above.
## spx ## spx
1. The [SPX extension](https://github.com/NoiseByNorthwest/php-spx) only supports NTS mode. 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).
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).
## mimalloc ## mimalloc

View File

@ -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`. - 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). - 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). - 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, 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. 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-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-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-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 - `--build-all`: build all above sapi
```bash ```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++`) - `--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 - `--with-clean`: clean up old make files before compiling PHP
- `--enable-zts`: Make compiled PHP thread-safe version (default is NTS version) - `--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-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-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) - `--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: Here is an example of installing the tool:
- Download and install UPX (Linux and Windows only): `bin/spc install-pkg upx` - 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 ## Command - del-download

View File

@ -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 平台来说macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 的系统库:`/usr/lib/libresolv.9.dylib``/usr/lib/libSystem.B.dylib`
所以在 macOS 系统下,在特定的编译条件下可以使用静态编译的 php 二进制文件,可使用动态链接的扩展: 因此,在 macOS 上,您可以直接构建出使用静态编译的 PHP 二进制文件和动态链接的扩展:
1. 使用 `--no-strip` 参数,将不会对二进制文件去除调试符号等信息,以供使用 `Xdebug` 等外部 Zend 扩展。 1. 使用 `--build-shared=XXX` 选项构建共享扩展 `xxx.so`。例如:`bin/spc build bcmath,zlib --build-shared=xdebug --build-cli`
2. 如果要编译某些 Zend 扩展,使用 Homebrew、MacPorts、源码编译的形式在所在的操作系统安装一个普通版本的 PHP。 2. 您将获得 `buildroot/modules/xdebug.so``buildroot/bin/php`
3. 使用 `phpize && ./configure && make` 命令编译想要使用的扩展。 3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
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
```
## 可以支持 Oracle 数据库扩展吗 ## 可以支持 Oracle 数据库扩展吗

View File

@ -51,5 +51,4 @@ glibc 构建为扩展的特性,不属于默认 static-php 的支持范围。
如果你需要不使用 Docker 构建基于 glibc 的二进制,请参考 `bin/spc-gnu-docker` 脚本,手动构建一个类似的环境。 如果你需要不使用 Docker 构建基于 glibc 的二进制,请参考 `bin/spc-gnu-docker` 脚本,手动构建一个类似的环境。
由于 glibc 二进制不是项目的主要目标,一般情况下我们不会额外测试 glibc 下的各个库和扩展的兼容性。 请注意,我们仅支持使用 bin/spc-gnu-docker 构建的 glibc 版本。已在 RHEL 9 和 10 上进行了编译测试并验证其稳定性,但如果您遇到问题,我们可能不会进行修复。
任何特定库如果在 musl-libc 上构建成功,但在 glibc 上构建失败,请提交 issue我们将会单独解决。

View File

@ -141,8 +141,7 @@ parallel 扩展只支持 PHP 8.0 及以上版本,并只支持 ZTS 构建(`--
## spx ## spx
1. [SPX 扩展](https://github.com/NoiseByNorthwest/php-spx) 只支持非线程模式。 1. SPX 目前不支持 Windows且官方仓库也不支持静态编译static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。
2. SPX 目前不支持 Windows且官方仓库也不支持静态编译static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。
## mimalloc ## mimalloc

View File

@ -145,6 +145,7 @@ bin/spc craft --debug
- fpm: 构建结果为 `buildroot/bin/php-fpm` - fpm: 构建结果为 `buildroot/bin/php-fpm`
- micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。 - micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。
- embed: 参见 [embed 使用](./manual-build#embed-使用)。 - embed: 参见 [embed 使用](./manual-build#embed-使用)。
- frankenphp: 构建结果为 `buildroot/bin/frankenphp`
如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。 如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。
@ -250,6 +251,7 @@ bin/spc doctor --auto-fix
- `--build-fpm`: 构建一个 fpm sapiphp-fpm用于和其他传统的 fpm 架构的软件如 nginx 配合使用) - `--build-fpm`: 构建一个 fpm sapiphp-fpm用于和其他传统的 fpm 架构的软件如 nginx 配合使用)
- `--build-micro`: 构建一个 micro sapi用于构建一个包含 PHP 代码的独立可执行二进制) - `--build-micro`: 构建一个 micro sapi用于构建一个包含 PHP 代码的独立可执行二进制)
- `--build-embed`: 构建一个 embed sapi用于嵌入到其他 C 语言程序中) - `--build-embed`: 构建一个 embed sapi用于嵌入到其他 C 语言程序中)
- `--build-frankenphp`: 构建一个 [frankenphp](https://github.com/php/frankenphp) 二进制
- `--build-all`: 构建以上所有 sapi - `--build-all`: 构建以上所有 sapi
```bash ```bash
@ -294,7 +296,7 @@ bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli
- `--cxx=XXX`: 指定 C++ 语言编译器的执行命令Linux 默认 `g++`macOS 默认 `clang++` - `--cxx=XXX`: 指定 C++ 语言编译器的执行命令Linux 默认 `g++`macOS 默认 `clang++`
- `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件 - `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本) - `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积(不裁剪的 macOS 二进制文件可使用动态链接的第三方扩展) - `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等) - `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等)
- `--with-config-file-path=XXX` 查找 `php.ini` 的路径(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径) - `--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-的路径是什么) 查看默认路径) - `--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` - 下载安装 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 - 删除已下载的资源 ## 命令 del-download - 删除已下载的资源

View File

@ -5,7 +5,7 @@
"docs:preview": "vitepress preview docs" "docs:preview": "vitepress preview docs"
}, },
"devDependencies": { "devDependencies": {
"vitepress": "^1.0.0-rc.35", "vitepress": "^2.0.0-alpha.5",
"vue": "^3.2.47" "vue": "^3.2.47"
} }
} }

View File

@ -2,4 +2,7 @@
<phpunit <phpunit
bootstrap="tests/bootstrap.php" bootstrap="tests/bootstrap.php"
> >
</phpunit> <php>
<env name="SPC_IGNORE_BAD_HASH" value="yes" force="true" />
</php>
</phpunit>

View File

@ -33,7 +33,7 @@ use Symfony\Component\Console\Application;
*/ */
final class ConsoleApplication extends Application final class ConsoleApplication extends Application
{ {
public const VERSION = '2.5.2'; public const VERSION = '2.6.1';
public function __construct() public function __construct()
{ {

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\builder; namespace SPC\builder;
use PharIo\FileSystem\File;
use SPC\exception\ExceptionHandler; use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\InterruptException; use SPC\exception\InterruptException;
@ -11,6 +12,7 @@ use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile;
use SPC\store\SourceManager; use SPC\store\SourceManager;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@ -127,7 +129,7 @@ abstract class BuilderBase
if ($including_shared) { if ($including_shared) {
return $this->exts; 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); 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 public function buildSharedExts(): void
{ {
foreach ($this->getExts() as $ext) { $lines = file(BUILD_BIN_PATH . '/php-config');
if (!$ext->isBuildShared()) { $extension_dir_line = null;
continue; 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 public function makeStaticExtensionArgs(): string
{ {
$ret = []; $ret = [];
foreach ($this->getExts(false) as $ext) { foreach ($this->getExts() as $ext) {
logger()->info($ext->getName() . ' is using ' . $ext->getConfigureArg()); $arg = $ext->getConfigureArg();
$ret[] = trim($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)); logger()->debug('Using configure: ' . implode(' ', $ret));
return implode(' ', $ret); return implode(' ', $ret);
@ -311,15 +351,11 @@ abstract class BuilderBase
public function getPHPVersionFromArchive(?string $file = null): false|string public function getPHPVersionFromArchive(?string $file = null): false|string
{ {
if ($file === null) { if ($file === null) {
$lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false; $lock = LockFile::get('php-src');
if ($lock === false) { if ($lock === null) {
return false;
}
$lock = json_decode($lock, true);
$file = $lock['php-src']['filename'] ?? null;
if ($file === null) {
return false; return false;
} }
$file = LockFile::getLockFullPath($lock);
} }
if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) { if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) {
return $match[1]; return $match[1];
@ -365,6 +401,9 @@ abstract class BuilderBase
if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) { if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
$ls[] = 'embed'; $ls[] = 'embed';
} }
if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
$ls[] = 'frankenphp';
}
return implode(', ', $ls); 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. * Generate micro extension test php code.
*/ */

View File

@ -53,26 +53,26 @@ class Extension
} }
} }
public function getFrameworks(): array
{
return Config::getExt($this->getName(), 'frameworks', []);
}
/** /**
* 获取开启该扩展的 PHP 编译添加的参数 * 获取开启该扩展的 PHP 编译添加的参数
* *
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
public function getConfigureArg(): string public function getConfigureArg(bool $shared = false): string
{ {
$arg = $this->getEnableArg(); return match (PHP_OS_FAMILY) {
switch (PHP_OS_FAMILY) { 'Windows' => $this->getWindowsConfigureArg($shared),
case 'Windows': 'Darwin',
$arg .= $this->getWindowsConfigureArg(); 'Linux',
break; 'BSD' => $this->getUnixConfigureArg($shared),
case 'Darwin': default => throw new WrongUsageException(PHP_OS_FAMILY . ' build is not supported yet'),
case 'Linux': };
case 'BSD':
$arg .= $this->getUnixConfigureArg();
break;
}
return $arg;
} }
/** /**
@ -81,13 +81,13 @@ class Extension
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
public function getEnableArg(): string public function getEnableArg(bool $shared = false): string
{ {
$_name = str_replace('_', '-', $this->name); $_name = str_replace('_', '-', $this->name);
return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) { return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) {
'enable' => '--enable-' . $_name . ' ', 'enable' => '--enable-' . $_name . ($shared ? '=shared' : '') . ' ',
'with' => '--with-' . $_name . ' ', 'with' => '--with-' . $_name . ($shared ? '=shared' : '') . ' ',
'with-prefix' => '--with-' . $_name . '="' . BUILD_ROOT_PATH . '" ', 'with-prefix' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . '"' . BUILD_ROOT_PATH . '" ',
'none', 'custom' => '', 'none', 'custom' => '',
default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-prefix] ."), default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-prefix] ."),
}; };
@ -147,15 +147,15 @@ class Extension
return $this->name; return $this->name;
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
return ''; return $this->getEnableArg();
// Windows is not supported yet // Windows is not supported yet
} }
public function getUnixConfigureArg(bool $shared = false): string 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 * Patch code before shared extension phpize
* @throws RuntimeException * 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()); return false;
if ($ret !== 0) { }
throw new RuntimeException($this->getName() . '.so failed to load');
/**
* 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 // 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 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 // 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) { 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')) { 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') 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 ($ret !== 0) {
if ($this->builder->getOption('debug')) { if ($this->builder->getOption('debug')) {
var_dump($out); var_dump($out);
@ -275,6 +341,30 @@ class Extension
*/ */
public function buildShared(): void 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) { match (PHP_OS_FAMILY) {
'Darwin', 'Linux' => $this->buildUnixShared(), 'Darwin', 'Linux' => $this->buildUnixShared(),
default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'), default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'),
@ -292,26 +382,58 @@ class Extension
*/ */
public function buildUnixShared(): void 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 = [ $env = [
'CFLAGS' => $config['cflags'], 'CFLAGS' => $config['cflags'],
'CXXFLAGS' => $config['cflags'],
'LDFLAGS' => $config['ldflags'], '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 // prepare configure args
shell()->cd($this->source_dir) shell()->cd($this->source_dir)
->setEnv($env) ->setEnv($env)
->exec(BUILD_BIN_PATH . '/phpize') ->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);
// copy shared library if ($this->patchBeforeSharedConfigure()) {
copy($this->source_dir . '/modules/' . $this->getDistName() . '.so', BUILD_LIB_PATH . '/' . $this->getDistName() . '.so'); logger()->info("Extension [{$this->getName()}] patched before shared configure");
// check shared extension with php-cli
if (file_exists(BUILD_BIN_PATH . '/php')) {
$this->runSharedExtensionCheckUnix();
} }
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 private function getLibraryDependencies(bool $recursive = false): array
{ {
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase); $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; return $deps;
} }
} }

View File

@ -10,10 +10,14 @@ use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile;
use SPC\store\SourceManager; use SPC\store\SourceManager;
use SPC\util\GlobalValueTrait;
abstract class LibraryBase abstract class LibraryBase
{ {
use GlobalValueTrait;
/** @var string */ /** @var string */
public const NAME = 'unknown'; public const NAME = 'unknown';
@ -31,7 +35,7 @@ abstract class LibraryBase
if (static::NAME === 'unknown') { if (static::NAME === 'unknown') {
throw new RuntimeException('no 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 public function setup(bool $force = false): int
{ {
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
$source = Config::getLib(static::NAME, 'source'); $source = Config::getLib(static::NAME, 'source');
// if source is locked as pre-built, we just tryInstall it // if source is locked as pre-built, we just tryInstall it
$pre_built_name = Downloader::getPreBuiltLockName($source); $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) { if (($lock = LockFile::get($pre_built_name)) && $lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT) {
return $this->tryInstall($lock[$pre_built_name]['filename'], $force); return $this->tryInstall($lock, $force);
} }
return $this->tryBuild($force); return $this->tryBuild($force);
} }
@ -163,14 +166,15 @@ abstract class LibraryBase
* @throws WrongUsageException * @throws WrongUsageException
* @throws FileSystemException * @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) { if ($force_install) {
logger()->info('Installing required library [' . static::NAME . '] from pre-built binaries'); logger()->info('Installing required library [' . static::NAME . '] from pre-built binaries');
// Extract files // Extract files
try { 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(); $this->install();
return LIB_STATUS_OK; return LIB_STATUS_OK;
} catch (FileSystemException|RuntimeException $e) { } catch (FileSystemException|RuntimeException $e) {
@ -180,19 +184,19 @@ abstract class LibraryBase
} }
foreach ($this->getStaticLibs() as $name) { foreach ($this->getStaticLibs() as $name) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
$this->tryInstall($install_file, true); $this->tryInstall($lock, true);
return LIB_STATUS_OK; return LIB_STATUS_OK;
} }
} }
foreach ($this->getHeaders() as $name) { foreach ($this->getHeaders() as $name) {
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
$this->tryInstall($install_file, true); $this->tryInstall($lock, true);
return LIB_STATUS_OK; return LIB_STATUS_OK;
} }
} }
// pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists // 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')) { 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_OK;
} }
return LIB_STATUS_ALREADY; return LIB_STATUS_ALREADY;
@ -328,21 +332,6 @@ abstract class LibraryBase
return false; 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. * Build this library.
* *

View File

@ -25,10 +25,10 @@ class amqp extends Extension
public function getUnixConfigureArg(bool $shared = false): string 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'; return '--with-amqp';
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\builder\linux\LinuxBuilder;
use SPC\builder\macos\MacOSBuilder; use SPC\builder\macos\MacOSBuilder;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
@ -21,7 +22,7 @@ class curl extends Extension
{ {
logger()->info('patching before-configure for curl checks'); logger()->info('patching before-configure for curl checks');
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])"; $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], [ $file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
save_old_LDFLAGS=$LDFLAGS save_old_LDFLAGS=$LDFLAGS
ac_stuff="$5" ac_stuff="$5"
@ -40,7 +41,7 @@ class curl extends Extension
$4 $4
])dnl ])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; return true;
} }
@ -52,6 +53,72 @@ class curl extends Extension
{ {
$frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : ''; $frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : '';
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lcurl/', $this->getLibFilesString() . $frameworks); FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lcurl/', $this->getLibFilesString() . $frameworks);
$this->patchBeforeSharedConfigure();
return true; 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']
);
}
}
} }

View File

@ -12,11 +12,11 @@ class dba extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . BUILD_ROOT_PATH) : ''; $qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH) : '';
return '--enable-dba' . $qdbm; return '--enable-dba' . ($shared ? '=shared' : '') . $qdbm;
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
$qdbm = $this->builder->getLib('qdbm') ? ' --with-qdbm' : ''; $qdbm = $this->builder->getLib('qdbm') ? ' --with-qdbm' : '';
return '--with-dba' . $qdbm; return '--with-dba' . $qdbm;

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('dom')]
class dom extends Extension
{
/**
* @throws RuntimeException
*/
public function getUnixConfigureArg(bool $shared = false): string
{
$arg = '--enable-dom' . ($shared ? '=shared' : '');
$arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '"';
return $arg;
}
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/win32/build/config.w32', 'dllmain.c ', '');
return true;
}
public function getWindowsConfigureArg($shared = false): string
{
return '--with-dom';
}
}

View File

@ -12,10 +12,10 @@ class ffi extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
return '--with-ffi --enable-zend-signals'; return '--with-ffi' . ($shared ? '=shared' : '') . ' --enable-zend-signals';
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
return '--with-ffi'; return '--with-ffi';
} }

View File

@ -12,7 +12,7 @@ class gd extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$arg = '--enable-gd'; $arg = '--enable-gd' . ($shared ? '=shared' : '');
$arg .= $this->builder->getLib('freetype') ? ' --with-freetype' : ''; $arg .= $this->builder->getLib('freetype') ? ' --with-freetype' : '';
$arg .= $this->builder->getLib('libjpeg') ? ' --with-jpeg' : ''; $arg .= $this->builder->getLib('libjpeg') ? ' --with-jpeg' : '';
$arg .= $this->builder->getLib('libwebp') ? ' --with-webp' : ''; $arg .= $this->builder->getLib('libwebp') ? ' --with-webp' : '';

View File

@ -35,7 +35,7 @@ class glfw extends Extension
return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH; return '--enable-glfw --with-glfw-dir=' . BUILD_ROOT_PATH;
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
return '--enable-glfw=static'; return '--enable-glfw=static';
} }

View File

@ -12,10 +12,13 @@ class imagick extends Extension
{ {
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
if (getenv('SPC_LIBC') !== 'musl') { if (PHP_OS_FAMILY !== 'Linux') {
return false; 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'); $extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lgomp');
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
return true; return true;
@ -23,7 +26,18 @@ class imagick extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$disable_omp = getenv('SPC_LIBC') === 'musl' ? '' : ' ac_cv_func_omp_pause_resource_all=no'; $disable_omp = !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) ? '' : ' ac_cv_func_omp_pause_resource_all=no';
return '--with-imagick=' . BUILD_ROOT_PATH . $disable_omp; 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];
} }
} }

View File

@ -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 ", true,',
'EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", PHP_INTL_SHARED,' 'EXTENSION("intl", "php_intl.c intl_convert.c intl_convertcpp.cpp intl_error.c ", PHP_INTL_SHARED,'
); );
} else { return true;
// 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 false;
}
public function patchBeforeSharedPhpize(): bool
{
return $this->patchBeforeBuildconf();
} }
} }

View File

@ -15,7 +15,7 @@ class lz4 extends Extension
return '--enable-lz4' . ($shared ? '=shared' : '') . ' --with-lz4-includedir=' . BUILD_ROOT_PATH; 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'; return '--enable-lz4';
} }

View File

@ -16,7 +16,7 @@ class mbregex extends Extension
return 'mbstring'; return 'mbstring';
} }
public function getConfigureArg(): string public function getConfigureArg(bool $shared = false): string
{ {
return ''; return '';
} }
@ -26,7 +26,8 @@ class mbregex extends Extension
*/ */
public function runCliCheckUnix(): void 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) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !'); throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !');
} }

View File

@ -10,9 +10,20 @@ use SPC\util\CustomExt;
#[CustomExt('mbstring')] #[CustomExt('mbstring')]
class mbstring extends Extension 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) { if ($this->builder->getExt('mbregex') === null) {
$arg .= ' --disable-mbregex'; $arg .= ' --disable-mbregex';
} else { } else {

View File

@ -14,7 +14,7 @@ class memcache extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string 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;
} }
/** /**

View File

@ -12,8 +12,11 @@ class memcached extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$rootdir = BUILD_ROOT_PATH; return '--enable-memcached' . ($shared ? '=shared' : '') . ' ' .
$zlib_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : "--with-zlib-dir={$rootdir}"; '--with-zlib-dir=' . BUILD_ROOT_PATH . ' ' .
return "--enable-memcached {$zlib_dir} --with-libmemcached-dir={$rootdir} --disable-memcached-sasl --enable-memcached-json"; '--with-libmemcached-dir=' . BUILD_ROOT_PATH . ' ' .
'--disable-memcached-sasl ' .
'--enable-memcached-json ' .
'--with-system-fastlz';
} }
} }

View File

@ -12,6 +12,6 @@ class odbc extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
return '--with-unixODBC=' . BUILD_ROOT_PATH; return '--with-unixODBC=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH;
} }
} }

View File

@ -26,6 +26,6 @@ class openssl extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$openssl_dir = $this->builder->getPHPVersionID() >= 80400 ? '' : ' --with-openssl-dir=' . BUILD_ROOT_PATH; $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;
} }
} }

View File

@ -19,10 +19,10 @@ class pdo_odbc extends Extension
public function getUnixConfigureArg(bool $shared = false): string 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'; return '--with-pdo-odbc';
} }

View File

@ -10,7 +10,7 @@ use SPC\util\CustomExt;
#[CustomExt('pdo_pgsql')] #[CustomExt('pdo_pgsql')]
class pdo_pgsql extends Extension class pdo_pgsql extends Extension
{ {
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
return '--with-pdo-pgsql=yes'; return '--with-pdo-pgsql=yes';
} }

View File

@ -36,16 +36,21 @@ class pgsql extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
if ($this->builder->getPHPVersionID() >= 80400) { 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 WrongUsageException
* @throws RuntimeException * @throws RuntimeException
*/ */
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
if ($this->builder->getPHPVersionID() >= 80400) { if ($this->builder->getPHPVersionID() >= 80400) {
return '--with-pgsql'; return '--with-pgsql';

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\builder\linux\LinuxBuilder;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('phar')]
class phar extends Extension
{
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']
);
}
}
}

View File

@ -15,8 +15,9 @@ class protobuf extends Extension
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') { 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'); throw new \RuntimeException('The latest protobuf extension requires PHP 8.0 or later');
} }
$grpc = $this->builder->getExt('grpc');
// protobuf conflicts with 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'); throw new \RuntimeException('protobuf conflicts with grpc, please remove grpc or protobuf extension');
} }
} }

View File

@ -11,6 +11,13 @@ use SPC\util\CustomExt;
#[CustomExt('rdkafka')] #[CustomExt('rdkafka')]
class rdkafka extends Extension 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 public function patchBeforeMake(): bool
{ {
// when compiling rdkafka with inline builds, it shows some errors, I don't know why. // 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; 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 = shell()->execWithResult('pkg-config --libs --static rdkafka')[1];
$pkgconf_libs = trim(implode('', $pkgconf_libs)); $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 . '"';
} }
} }

View File

@ -24,4 +24,18 @@ class readline extends Extension
); );
return true; 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();
}
} }

View File

@ -24,7 +24,7 @@ class redis extends Extension
return $arg; return $arg;
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
$arg = '--enable-redis'; $arg = '--enable-redis';
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session'; $arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';

View File

@ -5,28 +5,28 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\WrongUsageException; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('spx')] #[CustomExt('spx')]
class spx extends Extension 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 public function getUnixConfigureArg(bool $shared = false): string
{ {
$arg = '--enable-spx'; $arg = '--enable-spx' . ($shared ? '=shared' : '');
if ($this->builder->getExt('zlib') === null) { if ($this->builder->getLib('zlib') !== null) {
$arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH; $arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH;
} }
return $arg; 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;
}
} }

View File

@ -29,7 +29,7 @@ class swoole_hook_mysql extends Extension
if ($this->builder->getExt('swoole') === null) { if ($this->builder->getExt('swoole') === null) {
return; 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); $out = implode('', $out);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret);

View File

@ -37,7 +37,8 @@ class swoole_hook_pgsql extends Extension
if ($this->builder->getExt('swoole') === null) { if ($this->builder->getExt('swoole') === null) {
return; 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); $out = implode('', $out);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret);

View File

@ -37,7 +37,8 @@ class swoole_hook_sqlite extends Extension
if ($this->builder->getExt('swoole') === null) { if ($this->builder->getExt('swoole') === null) {
return; 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); $out = implode('', $out);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret);

View File

@ -18,7 +18,7 @@ class swow extends Extension
} }
} }
public function getConfigureArg(): string public function getConfigureArg(bool $shared = false): string
{ {
$arg = '--enable-swow'; $arg = '--enable-swow';
$arg .= $this->builder->getLib('openssl') ? ' --enable-swow-ssl' : ' --disable-swow-ssl'; $arg .= $this->builder->getLib('openssl') ? ' --enable-swow-ssl' : ' --disable-swow-ssl';

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('uv')] #[CustomExt('uv')]
@ -16,4 +17,13 @@ class uv extends Extension
throw new \RuntimeException('The latest uv extension requires PHP 8.0 or later'); 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;
}
} }

View File

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\RuntimeException;
use SPC\util\CustomExt;
#[CustomExt('xdebug')]
class xdebug extends Extension
{
public function runSharedExtensionCheckUnix(): void
{
[$ret] = shell()->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.');
}
}
}

View File

@ -20,7 +20,7 @@ class xlswriter extends Extension
return $arg; return $arg;
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
return '--with-xlswriter'; return '--with-xlswriter';
} }

View File

@ -13,7 +13,6 @@ use SPC\util\CustomExt;
#[CustomExt('soap')] #[CustomExt('soap')]
#[CustomExt('xmlreader')] #[CustomExt('xmlreader')]
#[CustomExt('xmlwriter')] #[CustomExt('xmlwriter')]
#[CustomExt('dom')]
#[CustomExt('simplexml')] #[CustomExt('simplexml')]
class xml extends Extension class xml extends Extension
{ {
@ -27,11 +26,10 @@ class xml extends Extension
'soap' => '--enable-soap', 'soap' => '--enable-soap',
'xmlreader' => '--enable-xmlreader', 'xmlreader' => '--enable-xmlreader',
'xmlwriter' => '--enable-xmlwriter', 'xmlwriter' => '--enable-xmlwriter',
'dom' => '--enable-dom',
'simplexml' => '--enable-simplexml', 'simplexml' => '--enable-simplexml',
default => throw new RuntimeException('Not accept non-xml extension'), 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; return $arg;
} }
@ -41,14 +39,13 @@ class xml extends Extension
return true; return true;
} }
public function getWindowsConfigureArg(): string public function getWindowsConfigureArg(bool $shared = false): string
{ {
$arg = match ($this->name) { $arg = match ($this->name) {
'xml' => '--with-xml', 'xml' => '--with-xml',
'soap' => '--enable-soap', 'soap' => '--enable-soap',
'xmlreader' => '--enable-xmlreader', 'xmlreader' => '--enable-xmlreader',
'xmlwriter' => '--enable-xmlwriter', 'xmlwriter' => '--enable-xmlwriter',
'dom' => '--with-dom',
'simplexml' => '--with-simplexml', 'simplexml' => '--with-simplexml',
default => throw new RuntimeException('Not accept non-xml extension'), default => throw new RuntimeException('Not accept non-xml extension'),
}; };

View File

@ -21,6 +21,6 @@ class yac extends Extension
public function getUnixConfigureArg(bool $shared = false): string 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';
} }
} }

View File

@ -96,6 +96,7 @@ class BSDBuilder extends UnixBuilderBase
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec( ->exec(
@ -143,7 +144,14 @@ class BSDBuilder extends UnixBuilderBase
} }
$this->buildEmbed(); $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')) { if (php_uname('m') === $this->getOption('arch')) {
$this->emitPatchPoint('before-sanity-check'); $this->emitPatchPoint('before-sanity-check');
$this->sanityCheck($build_target); $this->sanityCheck($build_target);

View File

@ -10,9 +10,9 @@ class curl extends BSDLibraryBase
public const NAME = 'curl'; 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')) { if ($this->builder->getLib('openssl')) {
$this->builder->setOption('extra-libs', $this->builder->getOption('extra-libs') . ' /usr/lib/libpthread.a /usr/lib/libdl.a'); $this->builder->setOption('extra-libs', $this->builder->getOption('extra-libs') . ' /usr/lib/libpthread.a /usr/lib/libdl.a');
} }

View File

@ -48,7 +48,7 @@ class openssl extends BSDLibraryBase
$ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib); $ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib);
} }
shell()->cd($this->source_dir) shell()->cd($this->source_dir)->initializeEnv($this)
->exec( ->exec(
"./Configure no-shared {$extra} " . "./Configure no-shared {$extra} " .
'--prefix=/ ' . // use prefix=/ '--prefix=/ ' . // use prefix=/

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\freebsd\library;
class watcher extends BSDLibraryBase
{
use \SPC\builder\unix\library\watcher;
public const NAME = 'watcher';
}

View File

@ -33,7 +33,6 @@ class LinuxBuilder extends UnixBuilderBase
if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) { if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) {
$this->setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\""); $this->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\""); $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 = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure; $configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure;
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$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) ? $config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : ''; ('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
$enable_cli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI; $enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enable_fpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enable_embed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $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 ' : ''; $mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
// prepare build php envs // prepare build php envs
@ -125,7 +125,7 @@ class LinuxBuilder extends UnixBuilderBase
]); ]);
// process micro upx patch if micro sapi enabled // process micro upx patch if micro sapi enabled
if ($enable_micro) { if ($enableMicro) {
if (version_compare($this->getMicroVersion(), '0.2.0') < 0) { if (version_compare($this->getMicroVersion(), '0.2.0') < 0) {
// for phpmicro 0.1.x // for phpmicro 0.1.x
$this->processMicroUPXLegacy(); $this->processMicroUPXLegacy();
@ -137,10 +137,10 @@ class LinuxBuilder extends UnixBuilderBase
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec( ->exec(
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' . getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enable_cli ? '--enable-cli ' : '--disable-cli ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enable_fpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') . ($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
($enable_embed ? "--enable-embed={$embed_type} " : '--disable-embed ') . ($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
($enable_micro ? '--enable-micro=all-static ' : '--disable-micro ') . ($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
$config_file_path . $config_file_path .
$config_file_scan_dir . $config_file_scan_dir .
$disable_jit . $disable_jit .
@ -156,26 +156,33 @@ class LinuxBuilder extends UnixBuilderBase
$this->cleanMake(); $this->cleanMake();
if ($enable_cli) { if ($enableCli) {
logger()->info('building cli'); logger()->info('building cli');
$this->buildCli(); $this->buildCli();
} }
if ($enable_fpm) { if ($enableFpm) {
logger()->info('building fpm'); logger()->info('building fpm');
$this->buildFpm(); $this->buildFpm();
} }
if ($enable_micro) { if ($enableMicro) {
logger()->info('building micro'); logger()->info('building micro');
$this->buildMicro(); $this->buildMicro();
} }
if ($enable_embed) { if ($enableEmbed) {
logger()->info('building embed'); logger()->info('building embed');
if ($enable_micro) { if ($enableMicro) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la'); FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
} }
$this->buildEmbed(); $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->emitPatchPoint('before-sanity-check');
$this->sanityCheck($build_target); $this->sanityCheck($build_target);
} }
@ -189,9 +196,10 @@ class LinuxBuilder extends UnixBuilderBase
protected function buildCli(): void protected function buildCli(): void
{ {
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->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')) { if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli') shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
@ -227,10 +235,11 @@ class LinuxBuilder extends UnixBuilderBase
// patch fake cli for micro // patch fake cli for micro
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli; $vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
$vars = SystemUtil::makeEnvVarString($vars); $vars = SystemUtil::makeEnvVarString($vars);
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->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(); $this->processMicroUPX();
@ -250,9 +259,10 @@ class LinuxBuilder extends UnixBuilderBase
protected function buildFpm(): void protected function buildFpm(): void
{ {
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->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')) { if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm') shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
@ -275,7 +285,38 @@ class LinuxBuilder extends UnixBuilderBase
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->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"); ->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(); $this->patchPhpScripts();
} }
@ -284,6 +325,7 @@ class LinuxBuilder extends UnixBuilderBase
return [ return [
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), '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_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'), 'EXTRA_LDFLAGS_PROGRAM' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM'),
]; ];
} }

View File

@ -11,6 +11,8 @@ class SystemUtil
{ {
use UnixSystemUtilTrait; use UnixSystemUtilTrait;
public static ?string $libc_version = null;
/** @noinspection PhpMissingBreakStatementInspection */ /** @noinspection PhpMissingBreakStatementInspection */
public static function getOSRelease(): array public static function getOSRelease(): array
{ {
@ -188,6 +190,9 @@ class SystemUtil
*/ */
public static function getLibcVersionIfExists(): ?string public static function getLibcVersionIfExists(): ?string
{ {
if (self::$libc_version !== null) {
return self::$libc_version;
}
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') { if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') {
$result = shell()->execWithResult('ldd --version', false); $result = shell()->execWithResult('ldd --version', false);
if ($result[0] !== 0) { if ($result[0] !== 0) {
@ -198,7 +203,8 @@ class SystemUtil
// match ldd version: "ldd (some useless text) 2.17" match 2.17 // match ldd version: "ldd (some useless text) 2.17" match 2.17
$pattern = '/ldd\s+\(.*?\)\s+(\d+\.\d+)/'; $pattern = '/ldd\s+\(.*?\)\s+(\d+\.\d+)/';
if (preg_match($pattern, $first_line, $matches)) { if (preg_match($pattern, $first_line, $matches)) {
return $matches[1]; self::$libc_version = $matches[1];
return self::$libc_version;
} }
return null; return null;
} }
@ -212,7 +218,8 @@ class SystemUtil
// match ldd version: "Version 1.2.3" match 1.2.3 // match ldd version: "Version 1.2.3" match 1.2.3
$pattern = '/Version\s+(\d+\.\d+\.\d+)/'; $pattern = '/Version\s+(\d+\.\d+\.\d+)/';
if (preg_match($pattern, $result[1][1] ?? '', $matches)) { if (preg_match($pattern, $result[1][1] ?? '', $matches)) {
return $matches[1]; self::$libc_version = $matches[1];
return self::$libc_version;
} }
} }
return null; return null;

View File

@ -10,9 +10,9 @@ class curl extends LinuxLibraryBase
public const NAME = 'curl'; 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')) { if ($this->builder->getLib('openssl')) {
$libs .= ' -ldl -lpthread'; $libs .= ' -ldl -lpthread';
} }

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class fastlz extends LinuxLibraryBase
{
use \SPC\builder\unix\library\fastlz;
public const NAME = 'fastlz';
}

View File

@ -14,10 +14,10 @@ class icu extends LinuxLibraryBase
protected function build(): void protected function build(): void
{ {
$cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -fPIC -fPIE -fno-ident"'; $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"';
$cxxflags = 'CXXFLAGS="-std=c++17"'; $cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"';
$ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : ''; $ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : '';
shell()->cd($this->source_dir . '/source') shell()->cd($this->source_dir . '/source')->initializeEnv($this)
->exec( ->exec(
"{$cppflags} {$cxxflags} {$ldflags} " . "{$cppflags} {$cxxflags} {$ldflags} " .
'./runConfigureICU Linux ' . './runConfigureICU Linux ' .

View File

@ -6,6 +6,7 @@ namespace SPC\builder\linux\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\util\executor\UnixAutoconfExecutor;
class libffi extends LinuxLibraryBase class libffi extends LinuxLibraryBase
{ {
@ -17,23 +18,14 @@ class libffi extends LinuxLibraryBase
*/ */
public function build(): void public function build(): void
{ {
[$lib, , $destdir] = SEPARATED_PATH;
$arch = getenv('SPC_ARCH'); $arch = getenv('SPC_ARCH');
UnixAutoconfExecutor::create($this)
shell()->cd($this->source_dir) ->configure(
->initializeEnv($this) "--host={$arch}-unknown-linux",
->exec( "--target={$arch}-unknown-linux",
'./configure ' . "--libdir={$this->getLibDir()}"
'--enable-static ' .
'--disable-shared ' .
"--host={$arch}-unknown-linux " .
"--target={$arch}-unknown-linux " .
'--prefix= ' .
"--libdir={$lib}"
) )
->exec('make clean') ->make();
->exec("make -j{$this->builder->concurrency}")
->exec("make install DESTDIR={$destdir}");
if (is_file(BUILD_ROOT_PATH . '/lib64/libffi.a')) { if (is_file(BUILD_ROOT_PATH . '/lib64/libffi.a')) {
copy(BUILD_ROOT_PATH . '/lib64/libffi.a', BUILD_ROOT_PATH . '/lib/libffi.a'); copy(BUILD_ROOT_PATH . '/lib64/libffi.a', BUILD_ROOT_PATH . '/lib/libffi.a');

View File

@ -4,17 +4,14 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; 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 class libmemcached extends LinuxLibraryBase
{ {
public const NAME = 'libmemcached'; 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();
} }
} }

View File

@ -24,6 +24,7 @@ namespace SPC\builder\linux\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\executor\UnixAutoconfExecutor;
class libpng extends LinuxLibraryBase class libpng extends LinuxLibraryBase
{ {
@ -36,28 +37,22 @@ class libpng extends LinuxLibraryBase
*/ */
public function build(): void public function build(): void
{ {
$optimizations = match (getenv('SPC_ARCH')) { UnixAutoconfExecutor::create($this)
'x86_64' => '--enable-intel-sse ',
'aarch64' => '--enable-arm-neon ',
default => '',
};
shell()->cd($this->source_dir)->initializeEnv($this)
->exec('chmod +x ./configure') ->exec('chmod +x ./configure')
->exec('chmod +x ./install-sh') ->exec('chmod +x ./install-sh')
->exec( ->appendEnv(['LDFLAGS' => "-L{$this->getLibDir()}"])
'LDFLAGS="-L' . BUILD_LIB_PATH . '" ' . ->configure(
'./configure ' . '--enable-hardware-optimizations',
'--disable-shared ' . "--with-zlib-prefix={$this->getBuildRootPath()}",
'--enable-static ' . match (getenv('SPC_ARCH')) {
'--enable-hardware-optimizations ' . 'x86_64' => '--enable-intel-sse',
'--with-zlib-prefix="' . BUILD_ROOT_PATH . '" ' . 'aarch64' => '--enable-arm-neon',
$optimizations . default => '',
'--prefix=' }
) )
->exec('make clean') ->make('libpng16.la', 'install-libLTLIBRARIES install-data-am', after_env_vars: ['DEFAULT_INCLUDES' => "-I{$this->source_dir} -I{$this->getIncludeDir()}"]);
->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);
$this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX);
$this->cleanLaFiles(); $this->patchLaDependencyPrefix();
} }
} }

View File

@ -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")'); 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)) { if (!str_contains('-ldl -lpthread', $libFiles)) {
$libFiles .= ' -ldl -lpthread'; $libFiles .= ' -ldl -lpthread';
} }

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class watcher extends LinuxLibraryBase
{
use \SPC\builder\unix\library\watcher;
public const NAME = 'watcher';
}

View File

@ -67,6 +67,10 @@ class MacOSBuilder extends UnixBuilderBase
array_push($frameworks, ...$lib->getFrameworks()); array_push($frameworks, ...$lib->getFrameworks());
} }
foreach ($this->exts as $ext) {
array_push($frameworks, ...$ext->getFrameworks());
}
if ($asString) { if ($asString) {
return implode(' ', array_map(fn ($x) => "-framework {$x}", $frameworks)); 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; $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
// prepare build php envs // prepare build php envs
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : ''; $mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
@ -176,7 +181,14 @@ class MacOSBuilder extends UnixBuilderBase
} }
$this->buildEmbed(); $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->emitPatchPoint('before-sanity-check');
$this->sanityCheck($build_target); $this->sanityCheck($build_target);
} }
@ -192,9 +204,10 @@ class MacOSBuilder extends UnixBuilderBase
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$shell = shell()->cd(SOURCE_PATH . '/php-src'); $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)) { 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); $this->deployBinary(BUILD_TARGET_CLI);
} }
@ -221,12 +234,15 @@ class MacOSBuilder extends UnixBuilderBase
// patch fake cli for micro // patch fake cli for micro
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli; $vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
if ($this->getOption('no-strip', false)) {
$vars['STRIP'] = 'dsymutil -f ';
}
$vars = SystemUtil::makeEnvVarString($vars); $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); $this->deployBinary(BUILD_TARGET_MICRO);
@ -248,7 +264,7 @@ class MacOSBuilder extends UnixBuilderBase
$shell = shell()->cd(SOURCE_PATH . '/php-src'); $shell = shell()->cd(SOURCE_PATH . '/php-src');
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm"); $shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm");
if (!$this->getOption('no-strip', false)) { 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); $this->deployBinary(BUILD_TARGET_FPM);
} }
@ -263,15 +279,12 @@ class MacOSBuilder extends UnixBuilderBase
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install") ->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') if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
->exec('mkdir ' . BUILD_ROOT_PATH . '/lib/php-o') shell()->cd(SOURCE_PATH . '/php-src')
->cd(BUILD_ROOT_PATH . '/lib/php-o') ->exec('ar -t ' . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 ar d " . BUILD_LIB_PATH . '/libphp.a');
->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');
$this->patchPhpScripts(); $this->patchPhpScripts();
} }

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class fastlz extends MacOSLibraryBase
{
use \SPC\builder\unix\library\fastlz;
public const NAME = 'fastlz';
}

View File

@ -6,6 +6,7 @@ namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\util\executor\UnixAutoconfExecutor;
class libffi extends MacOSLibraryBase class libffi extends MacOSLibraryBase
{ {
@ -17,20 +18,13 @@ class libffi extends MacOSLibraryBase
*/ */
protected function build(): void protected function build(): void
{ {
[, , $destdir] = SEPARATED_PATH;
$arch = getenv('SPC_ARCH'); $arch = getenv('SPC_ARCH');
shell()->cd($this->source_dir) UnixAutoconfExecutor::create($this)
->exec( ->configure(
'./configure ' . "--host={$arch}-apple-darwin",
'--enable-static ' . "--target={$arch}-apple-darwin",
'--disable-shared ' .
"--host={$arch}-apple-darwin " .
"--target={$arch}-apple-darwin " .
'--prefix= ' // use prefix=/
) )
->exec('make clean') ->make();
->exec("make -j{$this->builder->concurrency}")
->exec("make install DESTDIR={$destdir}");
$this->patchPkgconfPrefix(['libffi.pc']); $this->patchPkgconfPrefix(['libffi.pc']);
} }
} }

View File

@ -4,24 +4,9 @@ declare(strict_types=1);
namespace SPC\builder\macos\library; namespace SPC\builder\macos\library;
use SPC\store\FileSystem;
class libheif extends MacOSLibraryBase class libheif extends MacOSLibraryBase
{ {
use \SPC\builder\unix\library\libheif; use \SPC\builder\unix\library\libheif;
public const NAME = '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;
}
} }

View File

@ -4,28 +4,14 @@ declare(strict_types=1);
namespace SPC\builder\macos\library; namespace SPC\builder\macos\library;
/** use SPC\util\executor\UnixCMakeExecutor;
* gmp is a template library class for unix
*/
class libmemcached extends MacOSLibraryBase class libmemcached extends MacOSLibraryBase
{ {
public const NAME = 'libmemcached'; public const NAME = 'libmemcached';
public function build(): void public function build(): void
{ {
$rootdir = BUILD_ROOT_PATH; UnixCMakeExecutor::create($this)->build();
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');
} }
} }

View File

@ -24,6 +24,7 @@ namespace SPC\builder\macos\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\executor\UnixAutoconfExecutor;
class libpng extends MacOSLibraryBase class libpng extends MacOSLibraryBase
{ {
@ -36,29 +37,25 @@ class libpng extends MacOSLibraryBase
*/ */
protected function build(): void protected function build(): void
{ {
$optimizations = match (php_uname('m')) { $arch = arch2gnu(php_uname('m'));
'x86_64' => '--enable-intel-sse ', UnixAutoconfExecutor::create($this)
'arm64' => '--enable-arm-neon ',
default => '',
};
shell()->cd($this->source_dir)
->exec('chmod +x ./configure') ->exec('chmod +x ./configure')
->exec('chmod +x ./install-sh') ->exec('chmod +x ./install-sh')
->exec( ->appendEnv(['LDFLAGS' => "-L{$this->getLibDir()}"])
'./configure ' . ->configure(
'--host=' . arch2gnu(php_uname('m')) . '-apple-darwin ' . "--host={$arch}-apple-darwin",
'--disable-shared ' . '--enable-hardware-optimizations',
'--enable-static ' . "--with-zlib-prefix={$this->getBuildRootPath()}",
'--enable-hardware-optimizations ' . match (getenv('SPC_ARCH')) {
$optimizations . 'x86_64' => '--enable-intel-sse',
'--prefix=' 'aarch64' => '--enable-arm-neon',
default => '',
}
) )
->exec('make clean') ->make('libpng16.la', 'install-libLTLIBRARIES install-data-am', after_env_vars: ['DEFAULT_INCLUDES' => "-I{$this->source_dir} -I{$this->getIncludeDir()}"]);
->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) shell()->cd(BUILD_LIB_PATH)->exec('ln -sf libpng16.a libpng.a');
->cd(BUILD_LIB_PATH)
->exec('ln -sf libpng16.a libpng.a');
$this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX);
$this->cleanLaFiles(); $this->patchLaDependencyPrefix();
} }
} }

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class watcher extends MacOSLibraryBase
{
use \SPC\builder\unix\library\watcher;
public const NAME = 'watcher';
}

View File

@ -17,9 +17,9 @@ trait UnixLibraryTrait
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @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) { if ($recursive) {
array_unshift($libs, ...array_values($this->getDependencies(recursive: true))); array_unshift($libs, ...array_values($this->getDependencies(recursive: true)));
} }
@ -84,19 +84,34 @@ trait UnixLibraryTrait
} }
} }
/** public function patchLaDependencyPrefix(?array $files = null): void
* remove libtool archive files
*
* @throws FileSystemException
* @throws WrongUsageException
*/
public function cleanLaFiles(): void
{ {
foreach ($this->getStaticLibs() as $lib) { logger()->info('Patching library [' . static::NAME . '] la files');
$filename = pathinfo($lib, PATHINFO_FILENAME) . '.la'; $throwOnMissing = true;
if (file_exists(BUILD_LIB_PATH . '/' . $filename)) { if ($files === null) {
unlink(BUILD_LIB_PATH . '/' . $filename); $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);
} }
} }

View File

@ -12,6 +12,8 @@ use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\CurlHook;
use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
@ -153,13 +155,13 @@ abstract class UnixBuilderBase extends BuilderBase
// sanity check for php-cli // sanity check for php-cli
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('running cli sanity check'); 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); $raw_output = implode('', $output);
if ($ret !== 0 || trim($raw_output) !== 'hello') { if ($ret !== 0 || trim($raw_output) !== 'hello') {
throw new RuntimeException("cli failed sanity check: ret[{$ret}]. out[{$raw_output}]"); 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()); logger()->debug('testing ext: ' . $ext->getName());
$ext->runCliCheckUnix(); $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)); 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), default => throw new RuntimeException('Deployment does not accept type ' . $type),
}; };
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
FileSystem::createDir(BUILD_ROOT_PATH . '/bin'); FileSystem::createDir(BUILD_BIN_PATH);
shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '/bin/')); shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_BIN_PATH));
return true; return true;
} }
@ -263,6 +280,7 @@ abstract class UnixBuilderBase extends BuilderBase
logger()->debug('Patching phpize prefix'); logger()->debug('Patching phpize prefix');
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'"); FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'");
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#'); 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 // patch php-config
if (file_exists(BUILD_BIN_PATH . '/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); 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}");
}
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\util\executor\UnixAutoconfExecutor;
trait attr trait attr
{ {
@ -13,14 +14,11 @@ trait attr
*/ */
protected function build(): void protected function build(): void
{ {
shell()->cd($this->source_dir)->initializeEnv($this) UnixAutoconfExecutor::create($this)
->appendEnv(['CFLAGS' => "-I{$this->getIncludeDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}"])
->exec('libtoolize --force --copy') ->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if') ->exec('./autogen.sh || autoreconf -if')
->exec('./configure --prefix= --enable-static --disable-shared --with-pic --disable-nls') ->configure('--disable-nls')
->exec("make -j {$this->builder->concurrency}") ->make();
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX);
} }
} }

View File

@ -23,6 +23,8 @@ trait brotli
->build(); ->build();
$this->patchPkgconfPrefix(['libbrotlicommon.pc', 'libbrotlidec.pc', 'libbrotlienc.pc']); $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'); shell()->cd(BUILD_ROOT_PATH . '/lib')->exec('ln -sf libbrotlicommon.a libbrotli.a');
foreach (FileSystem::scanDirFiles(BUILD_ROOT_PATH . '/lib/', false, true) as $filename) { 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'))) { if (str_starts_with($filename, 'libbrotli') && (str_contains($filename, '.so') || str_ends_with($filename, '.dylib'))) {

View File

@ -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('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('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('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('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true))
->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD')) ->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD'))
->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2')) ->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2'))

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
trait fastlz
{
protected function build(): void
{
shell()->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');
}
}
}

View File

@ -38,7 +38,5 @@ trait freetype
' -L/lib ', ' -L/lib ',
' -L' . BUILD_ROOT_PATH . '/lib ' ' -L' . BUILD_ROOT_PATH . '/lib '
); );
$this->cleanLaFiles();
} }
} }

View File

@ -4,34 +4,34 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\util\executor\UnixAutoconfExecutor;
trait gettext trait gettext
{ {
protected function build(): void protected function build(): void
{ {
$extra = $this->builder->getLib('ncurses') ? ('--with-libncurses-prefix=' . BUILD_ROOT_PATH . ' ') : ''; $autoconf = UnixAutoconfExecutor::create($this)
$extra .= $this->builder->getLib('libxml2') ? ('--with-libxml2-prefix=' . BUILD_ROOT_PATH . ' ') : ''; ->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' : ''; $autoconf->configure()->make(with_clean: true);
$ldflags = $this->builder->getOption('enable-zts') ? '-lpthread' : ''; $this->patchLaDependencyPrefix();
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');
} }
} }

View File

@ -6,6 +6,7 @@ namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\util\executor\UnixAutoconfExecutor;
trait gmp trait gmp
{ {
@ -15,15 +16,7 @@ trait gmp
*/ */
protected function build(): void protected function build(): void
{ {
shell()->cd($this->source_dir)->initializeEnv($this) UnixAutoconfExecutor::create($this)->configure()->make();
->exec(
'./configure ' .
'--enable-static --disable-shared ' .
'--prefix='
)
->exec('make clean')
->exec("make -j{$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['gmp.pc']); $this->patchPkgconfPrefix(['gmp.pc']);
} }
} }

View File

@ -9,6 +9,7 @@ use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait imagemagick trait imagemagick
{ {
@ -18,45 +19,39 @@ trait imagemagick
*/ */
protected function build(): void 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 $ac = UnixAutoconfExecutor::create($this)
$openmp = getenv('SPC_LIBC') === 'musl' ? '--enable-openmp' : '--disable-openmp'; ->optionalLib('libzip', ...ac_with_args('zip'))
$extra = "--without-jxl --without-x {$openmp} "; ->optionalLib('libjpeg', ...ac_with_args('jpeg'))
$required_libs = ''; ->optionalLib('libpng', ...ac_with_args('png'))
$optional_libs = [ ->optionalLib('libwebp', ...ac_with_args('webp'))
'libzip' => 'zip', ->optionalLib('libxml2', ...ac_with_args('xml'))
'libjpeg' => 'jpeg', ->optionalLib('libheif', ...ac_with_args('heic'))
'libpng' => 'png', ->optionalLib('zlib', ...ac_with_args('zlib'))
'libwebp' => 'webp', ->optionalLib('xz', ...ac_with_args('lzma'))
'libxml2' => 'xml', ->optionalLib('zstd', ...ac_with_args('zstd'))
'libheif' => 'heic', ->optionalLib('freetype', ...ac_with_args('freetype'))
'zlib' => 'zlib', ->optionalLib('bzip2', ...ac_with_args('bzlib'))
'xz' => 'lzma', ->addConfigureArgs(
'zstd' => 'zstd', // TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so
'freetype' => 'freetype', getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10') ? '--disable-openmp' : '--enable-openmp',
'bzip2' => 'bzlib', '--without-jxl',
]; '--without-x',
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();
}
}
// special: linux musl needs `-static`
$ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl'; $ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl';
// libxml iconv patch // special: macOS needs -iconv
$required_libs .= $this instanceof MacOSLibraryBase ? ('-liconv') : ''; $libs = $this instanceof MacOSLibraryBase ? '-liconv' : '';
shell()->cd($this->source_dir)->initializeEnv($this)
->appendEnv(['LDFLAGS' => $ldflags, 'LIBS' => $required_libs, 'PKG_CONFIG' => '$PKG_CONFIG --static']) $ac->appendEnv([
->exec( 'LDFLAGS' => $ldflags,
'./configure ' . 'LIBS' => $libs,
'--enable-static --disable-shared ' . 'PKG_CONFIG' => '$PKG_CONFIG --static',
$extra . ]);
'--prefix='
) $ac->configure()->make();
->exec('make clean')
->exec("make -j{$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$filelist = [ $filelist = [
'ImageMagick.pc', 'ImageMagick.pc',
'ImageMagick-7.Q16HDRI.pc', 'ImageMagick-7.Q16HDRI.pc',
@ -75,5 +70,6 @@ trait imagemagick
'includearchdir=${prefix}/include/ImageMagick-7' 'includearchdir=${prefix}/include/ImageMagick-7'
); );
} }
$this->patchLaDependencyPrefix();
} }
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait ldap trait ldap
{ {
@ -17,33 +18,25 @@ trait ldap
protected function build(): void protected function build(): void
{ {
$alt = ''; UnixAutoconfExecutor::create($this)
// openssl support ->optionalLib('openssl', '--with-tls=openssl')
$alt .= $this->builder->getLib('openssl') ? '--with-tls=openssl ' : ''; ->optionalLib('gmp', '--with-mp=gmp')
// gmp support ->optionalLib('libsodium', '--with-argon2=libsodium', '--enable-argon2=no')
$alt .= $this->builder->getLib('gmp') ? '--with-mp=gmp ' : ''; ->addConfigureArgs(
// libsodium support '--disable-slapd',
$alt .= $this->builder->getLib('libsodium') ? '--with-argon2=libsodium ' : '--enable-argon2=no '; '--without-systemd',
f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config'); '--without-cyrus-sasl',
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='
) )
->exec('make clean') ->appendEnv([
// remove tests and doc to prevent compile failed with error: soelim not found '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('sed -i -e "s/SUBDIRS= include libraries clients servers tests doc/SUBDIRS= include libraries clients servers/g" Makefile')
->exec("make -j{$this->builder->concurrency}") ->make();
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
FileSystem::replaceFileLineContainsString(BUILD_LIB_PATH . '/pkgconfig/ldap.pc', 'Libs: -L${libdir} -lldap', 'Libs: -L${libdir} -lldap -llber');
$this->patchPkgconfPrefix(['ldap.pc', 'lber.pc']); $this->patchPkgconfPrefix(['ldap.pc', 'lber.pc']);
$this->patchLaDependencyPrefix();
} }
} }

View File

@ -7,6 +7,7 @@ namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait libacl trait libacl
{ {
@ -29,14 +30,11 @@ trait libacl
*/ */
protected function build(): void protected function build(): void
{ {
shell()->cd($this->source_dir)->initializeEnv($this) UnixAutoconfExecutor::create($this)
->appendEnv(['CFLAGS' => "-I{$this->getIncludeDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}"])
->exec('libtoolize --force --copy') ->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if') ->exec('./autogen.sh || autoreconf -if')
->exec('./configure --prefix= --enable-static --disable-shared --disable-tests --disable-nls') ->configure('--disable-nls', '--disable-tests')
->exec("make -j {$this->builder->concurrency}") ->make();
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX);
} }
} }

View File

@ -23,6 +23,5 @@ trait libavif
->build(); ->build();
// patch pkgconfig // patch pkgconfig
$this->patchPkgconfPrefix(['libavif.pc']); $this->patchPkgconfPrefix(['libavif.pc']);
$this->cleanLaFiles();
} }
} }

View File

@ -6,6 +6,7 @@ namespace SPC\builder\unix\library;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait libcares trait libcares
{ {
@ -24,11 +25,7 @@ trait libcares
*/ */
protected function build(): void protected function build(): void
{ {
shell()->cd($this->source_dir)->initializeEnv($this) UnixAutoconfExecutor::create($this)->configure('--disable-tests')->make();
->exec('./configure --prefix= --enable-static --disable-shared --disable-tests --with-pic')
->exec("make -j {$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libcares.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libcares.pc'], PKGCONF_PATCH_PREFIX);
} }
} }

View File

@ -40,15 +40,19 @@ trait libevent
*/ */
protected function build(): void protected function build(): void
{ {
UnixCMakeExecutor::create($this) $cmake = UnixCMakeExecutor::create($this)
->addConfigureArgs( ->addConfigureArgs(
'-DEVENT__LIBRARY_TYPE=STATIC', '-DEVENT__LIBRARY_TYPE=STATIC',
'-DEVENT__DISABLE_BENCHMARK=ON', '-DEVENT__DISABLE_BENCHMARK=ON',
'-DEVENT__DISABLE_THREAD_SUPPORT=ON', '-DEVENT__DISABLE_THREAD_SUPPORT=ON',
'-DEVENT__DISABLE_TESTS=ON', '-DEVENT__DISABLE_TESTS=ON',
'-DEVENT__DISABLE_SAMPLES=ON', '-DEVENT__DISABLE_SAMPLES=ON',
) '-DEVENT__DISABLE_MBEDTLS=ON ',
->build(); );
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']); $this->patchPkgconfPrefix(['libevent.pc', 'libevent_core.pc', 'libevent_extra.pc', 'libevent_openssl.pc']);

View File

@ -6,10 +6,24 @@ namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\executor\UnixCMakeExecutor; use SPC\util\executor\UnixCMakeExecutor;
trait libheif 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 RuntimeException
* @throws FileSystemException * @throws FileSystemException

View File

@ -4,26 +4,13 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\util\executor\UnixAutoconfExecutor;
trait libiconv trait libiconv
{ {
protected function build(): void protected function build(): void
{ {
[,,$destdir] = SEPARATED_PATH; UnixAutoconfExecutor::create($this)->configure('--enable-extra-encodings')->make();
$this->patchLaDependencyPrefix();
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');
}
} }
} }

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