Merge branch 'main' into php-85

# Conflicts:
#	src/SPC/builder/linux/LinuxBuilder.php
#	src/SPC/store/source/PhpSource.php
#	src/globals/test-extensions.php
This commit is contained in:
crazywhalecc 2025-07-31 23:46:34 +08:00
commit 1047d471ed
No known key found for this signature in database
GPG Key ID: 1F4BDD59391F2680
88 changed files with 1133 additions and 682 deletions

View File

@ -8,7 +8,7 @@
> If a modification is not involved, please skip it directly. > If a modification is not involved, please skip it directly.
- If you modified `*.php` or `*.json`, run them locally to ensure your changes are valid: - If you modified `*.php` or `*.json`, run them locally to ensure your changes are valid:
- [ ] `PHP_CS_FIXER_IGNORE_ENV=1 composer cs-fix` - [ ] `composer cs-fix`
- [ ] `composer analyse` - [ ] `composer analyse`
- [ ] `composer test` - [ ] `composer test`
- [ ] `bin/spc dev:sort-config` - [ ] `bin/spc dev:sort-config`

View File

@ -198,9 +198,9 @@ jobs:
run: ${{ needs.define-build.outputs.download }} run: ${{ needs.define-build.outputs.download }}
- name: "Build PHP" - name: "Build PHP"
run: ${{ needs.define-build.outputs.build }} run: ${{ needs.define-build.outputs.build }}
#- name: Setup tmate session # - name: Setup tmate session
# if: ${{ failure() }} # if: ${{ failure() }}
# uses: mxschmitt/action-tmate@v3 # uses: mxschmitt/action-tmate@v3
# Upload cli executable # Upload cli executable
- if: ${{ inputs.build-cli == true }} - if: ${{ inputs.build-cli == true }}

View File

@ -39,7 +39,7 @@ jobs:
tools: pecl, composer, php-cs-fixer tools: pecl, composer, php-cs-fixer
- name: Run PHP-CS-Fixer fix - name: Run PHP-CS-Fixer fix
run: PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --dry-run --diff --ansi run: php-cs-fixer fix --dry-run --diff --ansi
phpstan: phpstan:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -55,6 +55,8 @@ jobs:
extensions: curl, openssl, mbstring extensions: curl, openssl, mbstring
ini-values: memory_limit=-1 ini-values: memory_limit=-1
tools: composer tools: composer
env:
phpts: zts
- name: "Cache Composer packages" - name: "Cache Composer packages"
id: composer-cache id: composer-cache
@ -135,7 +137,7 @@ jobs:
build: build:
name: "Build PHP Test (PHP ${{ matrix.php }} ${{ matrix.os }})" name: "Build PHP Test (PHP ${{ matrix.php }} ${{ matrix.os }})"
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
needs: define-matrix needs: [define-matrix, php-cs-fixer, phpstan, phpunit]
timeout-minutes: 120 timeout-minutes: 120
strategy: strategy:
matrix: matrix:

3
.gitignore vendored
View File

@ -34,9 +34,6 @@ packlib_files.txt
/bin/* /bin/*
!/bin/spc* !/bin/spc*
!/bin/setup-runtime* !/bin/setup-runtime*
!/bin/spc-alpine-docker
!/bin/php-cs-fixer-wrapper
!/bin/build-static-frankenphp
# exclude windows build tools # exclude windows build tools
/php-sdk-binary-tools/ /php-sdk-binary-tools/

View File

@ -70,4 +70,5 @@ return (new PhpCsFixer\Config())
]) ])
->setFinder( ->setFinder(
PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/SPC']) PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/SPC'])
); )
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect());

View File

@ -1,158 +0,0 @@
#!/usr/bin/env bash
# This file is using docker to run commands
set -e
# Detect docker can run
if ! which docker >/dev/null; then
echo "Docker is not installed, please install docker first !"
exit 1
fi
DOCKER_EXECUTABLE="docker"
# shellcheck disable=SC2046
if [ $(id -u) -ne 0 ]; then
if ! docker info > /dev/null 2>&1; then
if [ "$SPC_USE_SUDO" != "yes" ]; then
echo "Docker command requires sudo"
# shellcheck disable=SC2039
echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again'
exit 1
fi
DOCKER_EXECUTABLE="sudo docker"
fi
fi
# to check if qemu-docker run
if [ "$SPC_USE_ARCH" = "" ]; then
SPC_USE_ARCH=current
fi
case $SPC_USE_ARCH in
current)
BASE_ARCH=$(uname -m)
if [ "$BASE_ARCH" = "arm64" ]; then
BASE_ARCH=aarch64
GO_ARCH=arm64
else
GO_ARCH=amd64
fi
;;
aarch64)
BASE_ARCH=aarch64
GO_ARCH=arm64
# shellcheck disable=SC2039
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m"
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
;;
*)
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
exit 1
;;
esac
# Detect docker env is setup
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-frankenphp-gnu-$SPC_USE_ARCH; then
echo "Docker container does not exist. Building docker image ..."
$DOCKER_EXECUTABLE build -t cwcc-frankenphp-gnu-$SPC_USE_ARCH -f- . <<EOF
FROM centos:7
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/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/*.repo
RUN yum clean all && \
yum makecache && \
yum update -y && \
localedef -c -i en_US -f UTF-8 en_US.UTF-8
RUN yum install -y centos-release-scl
RUN if [ "$BASE_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.repo ; \
else \
sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo ; \
fi
RUN sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/*.repo
RUN yum update -y && \
yum install -y devtoolset-10-gcc-*
RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc
RUN source /etc/bashrc
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 && \
mkdir /cmake && \
tar -xzf cmake.tgz -C /cmake --strip-components 1
WORKDIR /app
ADD ./src /app/src
COPY ./composer.* /app/
ADD ./bin/setup-runtime /app/bin/setup-runtime
ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative
ENV PATH="/app/bin:/cmake/bin:/usr/local/go/bin:$PATH"
ENV SPC_LIBC=glibc
ADD ./config/env.ini /app/config/env.ini
RUN bin/spc doctor --auto-fix --debug
RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \
tar -zxvf make.tgz && \
cd make-4.4 && \
./configure && \
make && \
make install && \
ln -sf /usr/local/bin/make /usr/bin/make
RUN curl -o automake.tgz -fsSL https://ftp.gnu.org/gnu/automake/automake-1.17.tar.xz && \
tar -xvf automake.tgz && \
cd automake-1.17 && \
./configure && \
make && \
make install && \
ln -sf /usr/local/bin/automake /usr/bin/automake
RUN git clone https://github.com/static-php/gnu-frankenphp --depth=1 /frankenphp
WORKDIR /frankenphp
RUN curl -o go.tgz -fsSL https://go.dev/dl/go1.24.1.linux-$GO_ARCH.tar.gz && \
rm -rf /usr/local/go && tar -C /usr/local -xzf go.tgz
EOF
fi
# Check if in ci (local terminal can execute with -it)
if [ -t 0 ]; then
INTERACT=-it
else
INTERACT=''
fi
# Mounting volumes
MOUNT_LIST=""
# shellcheck disable=SC2089
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/config:/app/config"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/src:/app/src"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/buildroot:/app/buildroot"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/source:/app/source"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/frankenphp-dist:/frankenphp/dist"
# Apply env in temp env file
echo 'CC=/opt/rh/devtoolset-10/root/usr/bin/gcc' > /tmp/spc-gnu-docker.env
echo 'CXX=/opt/rh/devtoolset-10/root/usr/bin/g++' >> /tmp/spc-gnu-docker.env
echo 'AR=/opt/rh/devtoolset-10/root/usr/bin/ar' >> /tmp/spc-gnu-docker.env
echo 'LD=/opt/rh/devtoolset-10/root/usr/bin/ld' >> /tmp/spc-gnu-docker.env
echo 'SPC_DEFAULT_C_FLAGS=-fPIE -fPIC' >> /tmp/spc-gnu-docker.env
echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env
# Run docker
# shellcheck disable=SC2068
# shellcheck disable=SC2086
# shellcheck disable=SC2090
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-frankenphp-gnu-$SPC_USE_ARCH ./build-static.sh

View File

@ -1,4 +0,0 @@
#!/usr/bin/env bash
# get parent dir, and run the php-cs-fixer
PHP_CS_FIXER_IGNORE_ENV=1 "$(dirname "$0")/../vendor/bin/php-cs-fixer" "$@"

View File

@ -83,7 +83,8 @@ RUN if [ "$SPC_USE_ARCH" = "aarch64" ]; then \
sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo ; \ sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo ; \
fi fi
RUN sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \ RUN sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/*.repo sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/*.repo && \
sed -i 's|http://|https://|g' /etc/yum.repos.d/*.repo
RUN yum update -y && \ RUN yum update -y && \
yum install -y devtoolset-10-gcc-* devtoolset-10-libatomic-devel yum install -y devtoolset-10-gcc-* devtoolset-10-libatomic-devel
@ -158,8 +159,6 @@ fi
# Apply env in temp env file # Apply env in temp env file
echo 'SPC_DEFAULT_C_FLAGS=-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_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env
# Environment variable passthrough # Environment variable passthrough
ENV_LIST="" ENV_LIST=""

View File

@ -62,23 +62,26 @@ PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe" UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
; 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
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
; Windows static linking system libs
SPC_EXTRA_LIBS=""
[linux] [linux]
; Linux can use different build toolchain, but the toolchain can not be changed in this file: ; Linux can use different build toolchains.
; - musl (default): used for general linux distros, can build `musl-static` target only. ; - musl (default, when SPC_LIBC=musl): used for general linux distros, can build `musl` (statically linked) only.
; - zig (WIP): used for general linux distros, can build `musl` and `glibc` targets. ; - zig (will become default): usable on all Linux distros, can build `-musl`, `arch-linux-musl -dynamic` and `arch-linux-gnu` targets. Can specify version such as `x86_64-linux-gnu.2.17`.
; - musl-native: used for alpine linux, can build `musl-static` and `musl`(WIP) target. ; - musl-native: used for alpine linux, can build `musl` and `musl -dynamic` target.
; - gnu-native (assume): used for general linux distros, can build `glibc` target only and have portability issues. ; - gnu-native: used for general linux distros, can build gnu target for the installed glibc version only.
; build target: ; LEGACY option to specify the target
; - musl-static (default): pure static linking, using musl-libc, can run on any linux distro.
; - musl: static linking with dynamic linking to musl-libc, can run on musl-based linux distro.
; - glibc: static linking with dynamic linking to glibc, can run on glibc-based linux distro.
; include PATH for musl libc.
SPC_LIBC=musl SPC_LIBC=musl
; Recommended: specify your target here. Zig toolchain will be used.
; examples:
; `native-native-gnu` - links against glibc, current OS version
; `native-native-gnu.2.17` - links against glibc, version 2.17
; `native-native` - links against system libc dynamically
; `native-native-musl` - links against musl libc statically
; `native-native-musl -dynamic` - links against musl libc dynamically
; SPC_TARGET=
; compiler environments ; compiler environments
CC=${SPC_LINUX_DEFAULT_CC} CC=${SPC_LINUX_DEFAULT_CC}
CXX=${SPC_LINUX_DEFAULT_CXX} CXX=${SPC_LINUX_DEFAULT_CXX}
@ -87,8 +90,7 @@ 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 -Os" SPC_DEFAULT_C_FLAGS="-fPIC -Os"
SPC_DEFAULT_CXX_FLAGS="-fPIC -Os" SPC_DEFAULT_CXX_FLAGS="-fPIC -Os"
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_DEFAULT_LD_FLAGS=""
SPC_EXTRA_LIBS=""
; upx executable path ; upx executable path
UPX_EXEC=${PKG_ROOT_PATH}/bin/upx UPX_EXEC=${PKG_ROOT_PATH}/bin/upx
; 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
@ -107,16 +109,10 @@ SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
SPC_CMD_VAR_PHP_EMBED_TYPE="static" SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; CFLAGS for configuring php ; CFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE" SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE"
; CPPFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; LIBS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
; EXTRA_CFLAGS for `make` php ; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_DEFAULT_C_FLAGS} -g -fstack-protector-strong -fno-ident -fPIE" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}"
; EXTRA_LIBS for `make` php ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm" SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
[macos] [macos]
; build target: macho or macho (possibly we could support macho-universal in the future) ; build target: macho or macho (possibly we could support macho-universal in the future)
@ -128,8 +124,7 @@ 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 -Os" SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) SPC_DEFAULT_LD_FLAGS=""
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
SPC_MICRO_PATCHES=cli_checks,macos_iconv SPC_MICRO_PATCHES=cli_checks,macos_iconv
@ -146,14 +141,8 @@ SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
SPC_CMD_VAR_PHP_EMBED_TYPE="static" SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; CFLAGS for configuring php ; CFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option" SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option"
; CPPFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; EXTRA_CFLAGS for `make` php ; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_DEFAULT_C_FLAGS} -g -fstack-protector-strong -fpic -fpie" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie ${SPC_DEFAULT_C_FLAGS}"
; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv"
[freebsd] [freebsd]
; compiler environments ; compiler environments

View File

@ -495,6 +495,7 @@
"type": "builtin", "type": "builtin",
"arg-type": "custom", "arg-type": "custom",
"arg-type-windows": "with", "arg-type-windows": "with",
"build-with-php": true,
"lib-depends": [ "lib-depends": [
"openssl", "openssl",
"zlib" "zlib"

View File

@ -661,7 +661,7 @@
"mimalloc": { "mimalloc": {
"source": "mimalloc", "source": "mimalloc",
"static-libs-unix": [ "static-libs-unix": [
"mimalloc.o" "libmimalloc.a"
] ]
}, },
"ncurses": { "ncurses": {
@ -744,8 +744,8 @@
"openssl": { "openssl": {
"source": "openssl", "source": "openssl",
"static-libs-unix": [ "static-libs-unix": [
"libcrypto.a", "libssl.a",
"libssl.a" "libcrypto.a"
], ],
"static-libs-windows": [ "static-libs-windows": [
"libssl.lib", "libssl.lib",

View File

@ -54,5 +54,20 @@
}, },
"go-xcaddy-aarch64-macos": { "go-xcaddy-aarch64-macos": {
"type": "custom" "type": "custom"
},
"zig-x86_64-linux": {
"type": "custom"
},
"zig-aarch64-linux": {
"type": "custom"
},
"zig-x86_64-macos": {
"type": "custom"
},
"zig-aarch64-macos": {
"type": "custom"
},
"zig-x86_64-win": {
"type": "custom"
} }
} }

View File

@ -996,33 +996,28 @@
}, },
"swoole": { "swoole": {
"path": "php-src/ext/swoole", "path": "php-src/ext/swoole",
"type": "git", "type": "ghtar",
"rev": "master", "repo": "swoole/swoole-src",
"url": "https://github.com/swoole/swoole-src.git", "prefer-stable": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
},
"alt": {
"type": "ghtar",
"repo": "swoole/swoole-src",
"prefer-stable": true
} }
}, },
"swow": { "swow": {
"type": "git",
"path": "php-src/ext/swow-src", "path": "php-src/ext/swow-src",
"rev": "ci", "type": "ghtar",
"url": "https://github.com/swow/swow", "repo": "swow/swow",
"prefer-stable": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"tidy": { "tidy": {
"type": "url", "type": "ghtar",
"url": "https://github.com/htacg/tidy-html5/archive/refs/tags/5.8.0.tar.gz", "repo": "htacg/tidy-html5",
"filename": "tidy-html5.tgz", "prefer-stable": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "README/LICENSE.md" "path": "README/LICENSE.md"

View File

@ -48,17 +48,17 @@ This extension contains an implementation of the coroutine environment for `pdo_
## swow ## swow
1. Only PHP 8.0 ~ 8.4 is supported. 1. Only PHP 8.0+ is supported.
## imagick ## imagick
1. The imagick extension currently only has openmp support on musl libc. This means that multithreading is disabled on glibc or other operating systems. The extension is still fully functional. 1. OpenMP support is disabled, this is recommended by the maintainers and also the case system packages.
## imap ## imap
1. Kerberos is not supported 1. Kerberos is not supported
2. ext-imap is not thread safe due to the underlying c-client. It's not possible to use it in --enable-zts builds. 2. ext-imap is not thread safe due to the underlying c-client. It's not possible to use it in `--enable-zts` builds.
3. Because the extension may be dropped from php, we recommend you look for an alternative implementation, such as [Webklex/php-imap](https://github.com/Webklex/php-imap) 3. The extension was dropped from php 8.4, we recommend you look for an alternative implementation, such as [Webklex/php-imap](https://github.com/Webklex/php-imap)
## gd ## gd
@ -82,7 +82,7 @@ and this extension cannot be compiled into php by static linking, so it cannot b
## xdebug ## xdebug
1. Xdebug is only buildable as a shared extension. You need to use a build target other than `musl-static` for SPC_TARGET. 1. Xdebug is only buildable as a shared extension. On Linux, you'll need to use a SPC_TARGET like `native-native -dynamic` or `native-native-gnu`.
2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug". 2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug".
The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`. The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
@ -122,8 +122,8 @@ For details on the solution, see [FAQ - Unable to use ssl](../faq/#unable-to-use
## password-argon2 ## password-argon2
1. password-argon2 is not a standard extension, it is an additional algorithm for the `password_hash` function. 1. password-argon2 is not a standard extension. The algorithm `PASSWORD_ARGON2ID` for the `password_hash` function needs libsodium or libargon2 to work.
2. On Linux systems, `password-argon2` dependency `libargon2` conflicts with the `libsodium` library. 2. using password-argon2 enables multithread support for this.
## ffi ## ffi

View File

@ -45,17 +45,17 @@ swoole-hook-sqlite 与 `pdo_sqlite` 扩展冲突。如需使用 Swoole 和 `pdo_
## swow ## swow
1. swow 仅支持 PHP 8.0 ~ 8.4 版本。 1. swow 仅支持 PHP 8.0+ 版本。
## imagick ## imagick
imagick 扩展目前仅在 musl libc 上支持 OpenMPlibgomp。使用 glibc 方式构建的 imagick 扩展无法支持多线程特性 1. OpenMP 支持已被禁用,这是维护者推荐的做法,系统软件包也是如此配置
## imap ## imap
1. 该扩展目前不支持 Kerberos。 1. 该扩展目前不支持 Kerberos。
2. 由于底层的 c-client、ext-imap 不是线程安全的。 无法在 `--enable-zts` 构建中使用它。 2. 由于底层的 c-client、ext-imap 不是线程安全的。 无法在 `--enable-zts` 构建中使用它。
3. 由于该扩展可能会从未来的 PHP 中删除,因此我们建议您寻找替代实现,例如 [Webklex/php-imap](https://github.com/Webklex/php-imap)。 3. 该扩展已在 PHP 8.4 中被移除,因此我们建议您寻找替代实现,例如 [Webklex/php-imap](https://github.com/Webklex/php-imap)。
## gd ## gd
@ -114,8 +114,8 @@ pgsql 16.2 修复了这个 Bug现在正常工作了。
## password-argon2 ## password-argon2
1. password-argon2不是一个标准的扩展,它是 `password_hash` 函数的额外算法 1. password-argon2不是一个标准的扩展`password_hash` 函数的 `PASSWORD_ARGON2ID` 算法需要 libsodium 或 libargon2 才能工作
2. 在Linux系统password-argon2 的依赖库 `libargon2``libsodium` 库冲突 2. 使用 password-argon2 可以为此启用多线程支持
## ffi ## ffi

View File

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

View File

@ -264,6 +264,7 @@ abstract class BuilderBase
} }
} }
file_put_contents(BUILD_BIN_PATH . '/php-config', implode('', $lines)); file_put_contents(BUILD_BIN_PATH . '/php-config', implode('', $lines));
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', 'test "[$]$1" = "no" && $1=yes', '# test "[$]$1" = "no" && $1=yes');
FileSystem::createDir(BUILD_MODULES_PATH); FileSystem::createDir(BUILD_MODULES_PATH);
try { try {
foreach ($this->getExts() as $ext) { foreach ($this->getExts() as $ext) {
@ -277,6 +278,7 @@ abstract class BuilderBase
throw $e; throw $e;
} }
FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line); FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', $extension_dir_line);
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', '# test "[$]$1" = "no" && $1=yes', 'test "[$]$1" = "no" && $1=yes');
} }
/** /**

View File

@ -9,7 +9,10 @@ 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\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
class Extension class Extension
{ {
@ -187,6 +190,14 @@ class Extension
*/ */
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
if (SPCTarget::getTargetOS() === 'Linux' && $this->isBuildShared() && ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS'))) {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/Makefile',
"/^(shared_objects_{$this->getName()}\\s*=.*)$/m",
"$1 {$objs}",
);
return true;
}
return false; return false;
} }
@ -217,7 +228,21 @@ class Extension
*/ */
public function patchBeforeSharedMake(): bool public function patchBeforeSharedMake(): bool
{ {
return false; $config = (new SPCConfigUtil($this->builder))->config([$this->getName()], array_map(fn ($l) => $l->getName(), $this->builder->getLibs()));
[$staticLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
'/^(.*_SHARED_LIBADD\s*=.*)$/m',
'$1 ' . trim($staticLibs)
);
if ($objs = getenv('SPC_EXTRA_RUNTIME_OBJECTS')) {
FileSystem::replaceFileRegex(
$this->source_dir . '/Makefile',
"/^(shared_objects_{$this->getName()}\\s*=.*)$/m",
"$1 {$objs}",
);
}
return true;
} }
/** /**
@ -251,7 +276,7 @@ class Extension
$ret = ''; $ret = '';
foreach ($order as $ext) { foreach ($order as $ext) {
if ($ext instanceof Extension && $ext->isBuildShared()) { if ($ext instanceof self && $ext->isBuildShared()) {
if (Config::getExt($ext->getName(), 'zend-extension', false) === true) { if (Config::getExt($ext->getName(), 'zend-extension', false) === true) {
$ret .= " -d \"zend_extension={$ext->getName()}\""; $ret .= " -d \"zend_extension={$ext->getName()}\"";
} else { } else {
@ -294,9 +319,7 @@ class Extension
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' -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')) { var_dump($out);
var_dump($out);
}
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check'); throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check');
} }
} }
@ -384,20 +407,25 @@ class Extension
*/ */
public function buildUnixShared(): void public function buildUnixShared(): void
{ {
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true); $config = (new SPCConfigUtil($this->builder))->config(
[$staticLibString, $sharedLibString] = $this->getStaticAndSharedLibs(); [$this->getName()],
array_map(fn ($l) => $l->getName(), $this->getLibraryDependencies(recursive: true)),
// macOS ld64 doesn't understand these, while Linux and BSD do $this->builder->getOption('with-suggested-exts'),
// use them to make sure that all symbols are picked up, even if a library has already been visited before $this->builder->getOption('with-suggested-libs'),
$preStatic = PHP_OS_FAMILY !== 'Darwin' ? '-Wl,-Bstatic -Wl,--start-group ' : ''; );
$postStatic = PHP_OS_FAMILY !== 'Darwin' ? ' -Wl,--end-group -Wl,-Bdynamic ' : ' '; [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
$env = [ $env = [
'CFLAGS' => $config['cflags'], 'CFLAGS' => $config['cflags'],
'CXXFLAGS' => $config['cflags'], 'CXXFLAGS' => $config['cflags'],
'LDFLAGS' => $config['ldflags'], 'LDFLAGS' => $config['ldflags'],
'LIBS' => $preStatic . $staticLibString . $postStatic . $sharedLibString, 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH, 'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
]; ];
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && SPCTarget::getTargetOS() === 'Linux') {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
}
if ($this->patchBeforeSharedPhpize()) { if ($this->patchBeforeSharedPhpize()) {
logger()->info("Extension [{$this->getName()}] patched before shared phpize"); logger()->info("Extension [{$this->getName()}] patched before shared phpize");
@ -406,6 +434,7 @@ class Extension
// prepare configure args // prepare configure args
shell()->cd($this->source_dir) shell()->cd($this->source_dir)
->setEnv($env) ->setEnv($env)
->appendEnv($this->getExtraEnv())
->exec(BUILD_BIN_PATH . '/phpize'); ->exec(BUILD_BIN_PATH . '/phpize');
if ($this->patchBeforeSharedConfigure()) { if ($this->patchBeforeSharedConfigure()) {
@ -414,25 +443,20 @@ class Extension
shell()->cd($this->source_dir) shell()->cd($this->source_dir)
->setEnv($env) ->setEnv($env)
->appendEnv($this->getExtraEnv())
->exec( ->exec(
'./configure ' . $this->getUnixConfigureArg(true) . './configure ' . $this->getUnixConfigureArg(true) .
' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' . ' --with-php-config=' . BUILD_BIN_PATH . '/php-config ' .
'--enable-shared --disable-static' '--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()) { if ($this->patchBeforeSharedMake()) {
logger()->info("Extension [{$this->getName()}] patched before shared make"); logger()->info("Extension [{$this->getName()}] patched before shared make");
} }
shell()->cd($this->source_dir) shell()->cd($this->source_dir)
->setEnv($env) ->setEnv($env)
->appendEnv($this->getExtraEnv())
->exec('make clean') ->exec('make clean')
->exec('make -j' . $this->builder->concurrency) ->exec('make -j' . $this->builder->concurrency)
->exec('make install'); ->exec('make install');
@ -506,38 +530,35 @@ class Extension
} }
} }
/** protected function getExtraEnv(): array
* Get required static and shared libraries as a pair of strings in format -l{libname} -l{libname2} {
* return [];
* @return array [staticLibString, sharedLibString] }
*/
protected function getStaticAndSharedLibs(): array /**
* Splits a given string of library flags into static and shared libraries.
*
* @param string $allLibs A space-separated string of library flags (e.g., -lxyz).
* @return array an array containing two elements: the first is a space-separated string
* of static library flags, and the second is a space-separated string
* of shared library flags
*/
protected function splitLibsIntoStaticAndShared(string $allLibs): array
{ {
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], with_dependencies: true);
$sharedLibString = '';
$staticLibString = ''; $staticLibString = '';
$staticLibs = $this->getLibFilesString(); $sharedLibString = '';
$staticLibs = str_replace([BUILD_LIB_PATH . '/lib', '.a'], ['-l', ''], $staticLibs); $libs = explode(' ', $allLibs);
$staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']); foreach ($libs as $lib) {
foreach ($staticLibs as $lib) { $staticLib = BUILD_LIB_PATH . '/lib' . str_replace('-l', '', $lib) . '.a';
$lib = trim($lib); if (str_starts_with($lib, BUILD_LIB_PATH . '/lib') && str_ends_with($lib, '.a')) {
if ($lib === '') { $staticLib = $lib;
continue;
} }
$static_lib = 'lib' . $lib . '.a'; if ($lib === '-lphp' || !file_exists($staticLib)) {
if (file_exists(BUILD_LIB_PATH . '/' . $static_lib) && !str_contains($static_lib, 'libphp')) { $sharedLibString .= " {$lib}";
if (!str_contains($staticLibString, '-l' . $lib . ' ')) { } else {
$staticLibString .= '-l' . $lib . ' '; $staticLibString .= " {$lib}";
}
} elseif (!str_contains($sharedLibString, '-l' . $lib . ' ')) {
$sharedLibString .= '-l' . $lib . ' ';
} }
} }
// move -lstdc++ to static libraries because centos 7 the shared libstdc++ is incomplete
if (str_contains((string) getenv('PATH'), 'rh/devtoolset-10')) {
$staticLibString .= ' -lstdc++';
$sharedLibString = str_replace('-lstdc++', '', $sharedLibString);
}
return [trim($staticLibString), trim($sharedLibString)]; return [trim($staticLibString), trim($sharedLibString)];
} }

View File

@ -13,6 +13,7 @@ class amqp extends Extension
{ {
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
if (PHP_OS_FAMILY === 'Windows') { if (PHP_OS_FAMILY === 'Windows') {
FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp.h', '/^#warning.*/m', ''); FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp.h', '/^#warning.*/m', '');
FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp_framing.h', '/^#warning.*/m', ''); FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp_framing.h', '/^#warning.*/m', '');
@ -20,7 +21,7 @@ class amqp extends Extension
FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp_tcp_socket.h', '/^#warning.*/m', ''); FileSystem::replaceFileRegex(BUILD_INCLUDE_PATH . '\amqp_tcp_socket.h', '/^#warning.*/m', '');
return true; return true;
} }
return false; return $patched;
} }
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string

View File

@ -60,13 +60,14 @@ class curl extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
$extra_libs = getenv('SPC_EXTRA_LIBS'); $extra_libs = getenv('SPC_EXTRA_LIBS');
if ($this->builder instanceof WindowsBuilder && !str_contains($extra_libs, 'secur32.lib')) { if ($this->builder instanceof WindowsBuilder && !str_contains($extra_libs, 'secur32.lib')) {
$extra_libs .= ' secur32.lib'; $extra_libs .= ' secur32.lib';
putenv('SPC_EXTRA_LIBS=' . trim($extra_libs)); putenv('SPC_EXTRA_LIBS=' . trim($extra_libs));
return true; return true;
} }
return false; return $patched;
} }
public function patchBeforeSharedConfigure(): bool public function patchBeforeSharedConfigure(): bool

View File

@ -41,10 +41,12 @@ class event extends Extension
*/ */
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
// Prevent event extension compile error on macOS // Prevent event extension compile error on macOS
if ($this->builder instanceof MacOSBuilder) { if ($this->builder instanceof MacOSBuilder) {
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', ''); FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
return true;
} }
return true; return $patched;
} }
} }

View File

@ -50,6 +50,7 @@ class grpc extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
parent::patchBeforeMake();
// add -Wno-strict-prototypes // add -Wno-strict-prototypes
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes'); GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true; return true;

View File

@ -15,4 +15,14 @@ class imagick extends Extension
$disable_omp = ' ac_cv_func_omp_pause_resource_all=no'; $disable_omp = ' ac_cv_func_omp_pause_resource_all=no';
return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; return '--with-imagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp;
} }
protected function splitLibsIntoStaticAndShared(string $allLibs): array
{
[$static, $shared] = parent::splitLibsIntoStaticAndShared($allLibs);
if (str_contains(getenv('PATH'), 'rh/devtoolset-10')) {
$static .= ' -l:libstdc++.a';
$shared = str_replace('-lstdc++', '', $shared);
}
return [clean_spaces($static), clean_spaces($shared)];
}
} }

View File

@ -45,8 +45,9 @@ class imap extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::isMuslDist()) { if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::isMuslDist()) {
return false; return $patched;
} }
$extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lcrypt'); $extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lcrypt');
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs); f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);

View File

@ -12,6 +12,7 @@ class openssl extends Extension
{ {
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
// patch openssl3 with php8.0 bug // patch openssl3 with php8.0 bug
if ($this->builder->getPHPVersionID() < 80100) { if ($this->builder->getPHPVersionID() < 80100) {
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c'); $openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
@ -20,7 +21,7 @@ class openssl extends Extension
return true; return true;
} }
return false; return $patched;
} }
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string

View File

@ -34,6 +34,7 @@ class opentelemetry extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
parent::patchBeforeMake();
// add -Wno-strict-prototypes // add -Wno-strict-prototypes
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes'); GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true; return true;

View File

@ -23,4 +23,23 @@ class password_argon2 extends Extension
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check'); throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check');
} }
} }
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
if ($this->builder->getLib('libsodium') !== null) {
$extraLibs = getenv('SPC_EXTRA_LIBS');
if ($extraLibs !== false) {
$extraLibs = str_replace(
[BUILD_LIB_PATH . '/libargon2.a', BUILD_LIB_PATH . '/libsodium.a'],
['', BUILD_LIB_PATH . '/libargon2.a ' . BUILD_LIB_PATH . '/libsodium.a'],
$extraLibs,
);
$extraLibs = trim(preg_replace('/\s+/', ' ', $extraLibs)); // normalize spacing
f_putenv('SPC_EXTRA_LIBS=' . $extraLibs);
return true;
}
}
return $patched;
}
} }

View File

@ -57,4 +57,11 @@ class pgsql extends Extension
} }
return '--with-pgsql=' . BUILD_ROOT_PATH; return '--with-pgsql=' . BUILD_ROOT_PATH;
} }
protected function getExtraEnv(): array
{
return [
'CFLAGS' => '-Wno-int-conversion',
];
}
} }

View File

@ -20,6 +20,7 @@ class rdkafka extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
parent::patchBeforeMake();
// 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.
FileSystem::replaceFileStr( FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/rdkafka/rdkafka.c', SOURCE_PATH . '/php-src/ext/rdkafka/rdkafka.c',

View File

@ -27,7 +27,12 @@ class readline extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
return '--without-libedit --with-readline=' . BUILD_ROOT_PATH; $enable = '--without-libedit --with-readline=' . BUILD_ROOT_PATH;
if ($this->builder->getPHPVersionID() < 84000) {
// the check uses `char rl_pending_input()` instead of `extern int rl_pending_input`, which makes LTO fail
$enable .= ' ac_cv_lib_readline_rl_pending_input=yes';
}
return $enable;
} }
public function buildUnixShared(): void public function buildUnixShared(): void

View File

@ -13,8 +13,13 @@ class redis extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$arg = '--enable-redis'; $arg = '--enable-redis';
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session'; if ($this->isBuildStatic()) {
$arg .= $this->builder->getExt('igbinary') ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary'; $arg .= $this->builder->getExt('session')?->isBuildStatic() ? ' --enable-redis-session' : ' --disable-redis-session';
$arg .= $this->builder->getExt('igbinary')?->isBuildStatic() ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
} else {
$arg .= $this->builder->getExt('session') ? ' --enable-redis-session' : ' --disable-redis-session';
$arg .= $this->builder->getExt('igbinary') ? ' --enable-redis-igbinary' : ' --disable-redis-igbinary';
}
if ($this->builder->getLib('zstd')) { if ($this->builder->getLib('zstd')) {
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"'; $arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"';
} }

View File

@ -14,13 +14,18 @@ class swoole extends Extension
{ {
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
if ($this->builder instanceof MacOSBuilder) { if ($this->builder instanceof MacOSBuilder) {
// Fix swoole with event extension <util.h> conflict bug // Fix swoole with event extension <util.h> conflict bug
$util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h'; $util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h';
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/swoole/thirdparty/php/standard/proc_open.cc', 'include <util.h>', 'include "' . $util_path . '"'); FileSystem::replaceFileStr(
"{$this->source_dir}/thirdparty/php/standard/proc_open.cc",
'include <util.h>',
'include "' . $util_path . '"',
);
return true; return true;
} }
return false; return $patched;
} }
public function getExtVersion(): ?string public function getExtVersion(): ?string

View File

@ -20,6 +20,7 @@ class uv extends Extension
public function patchBeforeSharedMake(): bool public function patchBeforeSharedMake(): bool
{ {
parent::patchBeforeSharedMake();
if (PHP_OS_FAMILY !== 'Linux' || arch2gnu(php_uname('m')) !== 'aarch64') { if (PHP_OS_FAMILY !== 'Linux' || arch2gnu(php_uname('m')) !== 'aarch64') {
return false; return false;
} }

View File

@ -27,16 +27,17 @@ class xlswriter extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$patched = parent::patchBeforeMake();
if (PHP_OS_FAMILY === 'Windows') { if (PHP_OS_FAMILY === 'Windows') {
// fix windows build with openssl extension duplicate symbol bug // fix windows build with openssl extension duplicate symbol bug
SourcePatcher::patchFile('spc_fix_xlswriter_win32.patch', $this->source_dir); SourcePatcher::patchFile('spc_fix_xlswriter_win32.patch', $this->source_dir);
$content = file_get_contents($this->source_dir . '/library/libxlsxwriter/src/theme.c'); $content = file_get_contents($this->source_dir . '/library/libxlsxwriter/src/theme.c');
$bom = pack('CCC', 0xEF, 0xBB, 0xBF); $bom = pack('CCC', 0xEF, 0xBB, 0xBF);
if (substr($content, 0, 3) !== $bom) { if (!str_starts_with($content, $bom)) {
file_put_contents($this->source_dir . '/library/libxlsxwriter/src/theme.c', $bom . $content); file_put_contents($this->source_dir . '/library/libxlsxwriter/src/theme.c', $bom . $content);
} }
return true; return true;
} }
return false; return $patched;
} }
} }

View File

@ -198,7 +198,7 @@ class BSDBuilder extends UnixBuilderBase
->exec("make -j{$this->concurrency} {$vars} micro"); ->exec("make -j{$this->concurrency} {$vars} micro");
if (!$this->getOption('no-strip', false)) { if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx'); shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-unneeded micro.sfx');
} }
$this->deployBinary(BUILD_TARGET_MICRO); $this->deployBinary(BUILD_TARGET_MICRO);

View File

@ -31,17 +31,11 @@ class LinuxBuilder extends UnixBuilderBase
GlobalEnvManager::afterInit(); GlobalEnvManager::afterInit();
// concurrency // concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY')); $this->concurrency = (int) getenv('SPC_CONCURRENCY');
// cflags // cflags
$this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS'); $this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS');
$this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS'); $this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS');
$this->arch_ld_flags = getenv('SPC_DEFAULT_LD_FLAGS');
// cross-compiling is not supported yet
/*if (php_uname('m') !== $this->arch) {
$this->cross_compile_prefix = SystemUtil::getCrossCompilePrefix($this->cc, $this->arch);
logger()->info('using cross compile prefix: ' . $this->cross_compile_prefix);
$this->configure_env .= " CROSS_COMPILE='{$this->cross_compile_prefix}'";
}*/
// create pkgconfig and include dir (some libs cannot create them automatically) // create pkgconfig and include dir (some libs cannot create them automatically)
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true); f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
@ -79,7 +73,10 @@ class LinuxBuilder extends UnixBuilderBase
$maxExecutionTimers = ''; $maxExecutionTimers = '';
$zts = ''; $zts = '';
} }
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
if (!$disable_jit && $this->getExt('opcache')) {
f_putenv('SPC_COMPILER_EXTRA=-fno-sanitize=undefined');
}
$config_file_path = $this->getOption('with-config-file-path', false) ? $config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : ''; ('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ? $config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
@ -91,27 +88,25 @@ class LinuxBuilder extends UnixBuilderBase
$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; $enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
// prepare build php envs // prepare build php envs
$envs_build_php = SystemUtil::makeEnvVarString([ $php_configure_env = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'), 'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'LDFLAGS' => '-L' . BUILD_LIB_PATH, 'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'), // 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions!
]); ]);
// process micro upx patch if micro sapi enabled $embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
if ($enableMicro) { if ($embed_type !== 'static' && SPCTarget::isStatic()) {
if (version_compare($this->getMicroVersion(), '0.2.0') < 0) { throw new WrongUsageException(
// for phpmicro 0.1.x 'Linux does not support loading shared libraries when linking libc statically. ' .
$this->processMicroUPXLegacy(); 'Change SPC_CMD_VAR_PHP_EMBED_TYPE to static.'
} );
// micro latest needs do strip and upx pack later (strip, upx, cut binary manually supported)
} }
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec( ->exec(
$php_configure_env . ' ' .
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' . getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') . ($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
@ -119,11 +114,11 @@ class LinuxBuilder extends UnixBuilderBase
($enableMicro ? '--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 .
$json_74 . $json_74 .
$zts . $zts .
$maxExecutionTimers . $maxExecutionTimers .
$this->makeStaticExtensionArgs() . $this->makeStaticExtensionArgs() . ' '
' ' . $envs_build_php . ' '
); );
$this->emitPatchPoint('before-php-make'); $this->emitPatchPoint('before-php-make');
@ -176,12 +171,12 @@ class LinuxBuilder extends UnixBuilderBase
->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('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-unneeded php');
}
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')
->exec('strip --strip-all php')
->exec(getenv('UPX_EXEC') . ' --best php'); ->exec(getenv('UPX_EXEC') . ' --best php');
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php');
} }
$this->deployBinary(BUILD_TARGET_CLI); $this->deployBinary(BUILD_TARGET_CLI);
@ -239,12 +234,12 @@ class LinuxBuilder extends UnixBuilderBase
->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('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-unneeded php-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')
->exec('strip --strip-all php-fpm')
->exec(getenv('UPX_EXEC') . ' --best php-fpm'); ->exec(getenv('UPX_EXEC') . ' --best php-fpm');
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm');
} }
$this->deployBinary(BUILD_TARGET_FPM); $this->deployBinary(BUILD_TARGET_FPM);
} }
@ -264,34 +259,79 @@ class LinuxBuilder extends UnixBuilderBase
->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') ?: ''; $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
$libDir = BUILD_LIB_PATH;
$modulesDir = BUILD_MODULES_PATH;
$libphpSo = "{$libDir}/libphp.so";
$realLibName = 'libphp.so';
$cwd = getcwd();
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) { if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
$release = $matches[1]; $release = $matches[1];
$realLibName = 'libphp-' . $release . '.so'; $realLibName = "libphp-{$release}.so";
$realLib = BUILD_LIB_PATH . '/' . $realLibName; $libphpRelease = "{$libDir}/{$realLibName}";
rename(BUILD_LIB_PATH . '/libphp.so', $realLib); if (!file_exists($libphpRelease) && file_exists($libphpSo)) {
$cwd = getcwd(); rename($libphpSo, $libphpRelease);
chdir(BUILD_LIB_PATH); }
symlink($realLibName, 'libphp.so'); if (file_exists($libphpRelease)) {
chdir(BUILD_MODULES_PATH); chdir($libDir);
foreach ($this->getExts() as $ext) { if (file_exists($libphpSo)) {
if (!$ext->isBuildShared()) { unlink($libphpSo);
continue;
} }
$name = $ext->getName(); symlink($realLibName, 'libphp.so');
$versioned = "{$name}-{$release}.so"; shell()->exec(sprintf(
$unversioned = "{$name}.so"; 'patchelf --set-soname %s %s',
if (is_file(BUILD_MODULES_PATH . "/{$versioned}")) { escapeshellarg($realLibName),
rename(BUILD_MODULES_PATH . "/{$versioned}", BUILD_MODULES_PATH . "/{$unversioned}"); escapeshellarg($libphpRelease)
shell()->cd(BUILD_MODULES_PATH) ));
->exec(sprintf( }
if (is_dir($modulesDir)) {
chdir($modulesDir);
foreach ($this->getExts() as $ext) {
if (!$ext->isBuildShared()) {
continue;
}
$name = $ext->getName();
$versioned = "{$name}-{$release}.so";
$unversioned = "{$name}.so";
$src = "{$modulesDir}/{$versioned}";
$dst = "{$modulesDir}/{$unversioned}";
if (is_file($src)) {
rename($src, $dst);
shell()->exec(sprintf(
'patchelf --set-soname %s %s', 'patchelf --set-soname %s %s',
escapeshellarg($unversioned), escapeshellarg($unversioned),
escapeshellarg($unversioned) escapeshellarg($dst)
)); ));
}
} }
} }
chdir($cwd); chdir($cwd);
} }
$target = "{$libDir}/{$realLibName}";
if (file_exists($target)) {
[, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target));
$output = join("\n", $output);
if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) {
$currentSoname = $sonameMatch[1];
if ($currentSoname !== basename($target)) {
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg(basename($target)),
escapeshellarg($target)
));
}
}
}
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
$AR = getenv('AR') ?: 'ar';
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
}
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {
shell()->cd(BUILD_LIB_PATH)->exec("strip --strip-unneeded {$realLibName}");
}
$this->patchPhpScripts(); $this->patchPhpScripts();
} }
@ -303,58 +343,22 @@ 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' => $config['libs'], 'EXTRA_LIBS' => $config['libs'],
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie", 'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie",
]; ];
} }
/** /**
* Apply option --no-strip and --with-upx-pack for micro sapi (only for phpmicro 0.1.x) * Strip micro.sfx for Linux.
* The micro.sfx does not support UPX directly, but we can remove UPX-info segment to adapt.
* This will also make micro.sfx with upx-packed more like a malware fore antivirus :(
* *
* @throws FileSystemException * @throws RuntimeException
*/ */
private function processMicroUPXLegacy(): void
{
// upx pack and strip for micro
// but always restore Makefile.frag.bak first
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.bak')) {
copy(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.bak', SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag');
}
if ($this->getOption('with-upx-pack', false)) {
// judge $(MAKE) micro_2s_objs SFX_FILESIZE=`$(STAT_SIZE) $(SAPI_MICRO_PATH)` count
// if 2, replace src/globals/extra/micro-triple-Makefile.frag file content
if (substr_count(FileSystem::readFile(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag'), '$(MAKE) micro_2s_objs SFX_FILESIZE=`$(STAT_SIZE) $(SAPI_MICRO_PATH)`') === 2) {
// bak first
copy(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag', SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.bak');
// replace Makefile.frag content
FileSystem::writeFile(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag', FileSystem::readFile(ROOT_DIR . '/src/globals/extra/micro-triple-Makefile.frag'));
}
// with upx pack always need strip
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH) && ' . getenv('UPX_EXEC') . ' --best \$(SAPI_MICRO_PATH)',
);
} elseif (!$this->getOption('no-strip', false)) {
// not-no-strip means strip (default behavior)
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH)',
);
} else {
// just no strip
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=true',
);
}
}
private function processMicroUPX(): void private function processMicroUPX(): void
{ {
if (version_compare($this->getMicroVersion(), '0.2.0') >= 0 && !$this->getOption('no-strip', false)) { if (version_compare($this->getMicroVersion(), '0.2.0') >= 0 && !$this->getOption('no-strip', false)) {
shell()->exec('strip --strip-all ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx'); shell()->exec('strip --strip-unneeded ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
if ($this->getOption('with-upx-pack')) { if ($this->getOption('with-upx-pack')) {
// strip first // strip first

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace SPC\builder\linux; namespace SPC\builder\linux;
use SPC\builder\traits\UnixSystemUtilTrait; use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException;
class SystemUtil class SystemUtil
{ {
@ -75,37 +74,6 @@ class SystemUtil
return $ncpu; return $ncpu;
} }
/**
* @throws RuntimeException
*/
public static function getCCType(string $cc): string
{
return match (true) {
str_ends_with($cc, 'c++'), str_ends_with($cc, 'cc'), str_ends_with($cc, 'g++'), str_ends_with($cc, 'gcc') => 'gcc',
$cc === 'clang++', $cc === 'clang', str_starts_with($cc, 'musl-clang') => 'clang',
default => throw new RuntimeException("unknown cc type: {$cc}"),
};
}
/**
* @throws RuntimeException
* @noinspection PhpUnused
*/
public static function getCrossCompilePrefix(string $cc, string $arch): string
{
return match (static::getCCType($cc)) {
// guessing clang toolchains
'clang' => match ($arch) {
'x86_64' => 'x86_64-linux-gnu-',
'arm64', 'aarch64' => 'aarch64-linux-gnu-',
default => throw new RuntimeException('unsupported arch: ' . $arch),
},
// remove gcc postfix
'gcc' => str_replace('-cc', '', str_replace('-gcc', '', $cc)) . '-',
default => throw new RuntimeException('unsupported cc'),
};
}
public static function findStaticLib(string $name): ?array public static function findStaticLib(string $name): ?array
{ {
$paths = getenv('LIBPATH'); $paths = getenv('LIBPATH');
@ -188,7 +156,7 @@ class SystemUtil
/** /**
* Get libc version string from ldd * Get libc version string from ldd
*/ */
public static function getLibcVersionIfExists(string $libc): ?string public static function getLibcVersionIfExists(?string $libc = null): ?string
{ {
if (self::$libc_version !== null) { if (self::$libc_version !== null) {
return self::$libc_version; return self::$libc_version;
@ -211,8 +179,11 @@ class SystemUtil
if ($libc === 'musl') { if ($libc === 'musl') {
if (self::isMuslDist()) { if (self::isMuslDist()) {
$result = shell()->execWithResult('ldd 2>&1', false); $result = shell()->execWithResult('ldd 2>&1', false);
} else { } elseif (is_file('/usr/local/musl/lib/libc.so')) {
$result = shell()->execWithResult('/usr/local/musl/lib/libc.so 2>&1', false); $result = shell()->execWithResult('/usr/local/musl/lib/libc.so 2>&1', false);
} else {
$arch = php_uname('m');
$result = shell()->execWithResult("/lib/ld-musl-{$arch}.so.1 2>&1", false);
} }
// Match Version * line // Match Version * line
// match ldd version: "Version 1.2.3" match 1.2.3 // match ldd version: "Version 1.2.3" match 1.2.3

View File

@ -18,14 +18,8 @@ class libffi extends LinuxLibraryBase
*/ */
public function build(): void public function build(): void
{ {
$arch = getenv('SPC_ARCH');
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->configure( ->configure()->make();
"--host={$arch}-unknown-linux",
"--target={$arch}-unknown-linux",
"--libdir={$this->getLibDir()}"
)
->make();
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

@ -12,6 +12,8 @@ class libmemcached extends LinuxLibraryBase
public function build(): void public function build(): void
{ {
UnixCMakeExecutor::create($this)->build(); UnixCMakeExecutor::create($this)
->addConfigureArgs('-DCMAKE_INSTALL_RPATH=""')
->build();
} }
} }

View File

@ -21,7 +21,6 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
@ -62,8 +61,6 @@ class openssl extends LinuxLibraryBase
$ex_lib = trim($ex_lib); $ex_lib = trim($ex_lib);
$clang_postfix = SystemUtil::getCCType(getenv('CC')) === 'clang' ? '-clang' : '';
shell()->cd($this->source_dir)->initializeEnv($this) shell()->cd($this->source_dir)->initializeEnv($this)
->exec( ->exec(
"{$env} ./Configure no-shared {$extra} " . "{$env} ./Configure no-shared {$extra} " .
@ -71,8 +68,9 @@ class openssl extends LinuxLibraryBase
'--libdir=' . BUILD_LIB_PATH . ' ' . '--libdir=' . BUILD_LIB_PATH . ' ' .
'--openssldir=/etc/ssl ' . '--openssldir=/etc/ssl ' .
"{$zlib_extra}" . "{$zlib_extra}" .
'enable-pie ' .
'no-legacy ' . 'no-legacy ' .
"linux-{$arch}{$clang_postfix}" "linux-{$arch}"
) )
->exec('make clean') ->exec('make clean')
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")

View File

@ -38,6 +38,7 @@ class MacOSBuilder extends UnixBuilderBase
// cflags // cflags
$this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS'); $this->arch_c_flags = getenv('SPC_DEFAULT_C_FLAGS');
$this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS'); $this->arch_cxx_flags = getenv('SPC_DEFAULT_CXX_FLAGS');
$this->arch_ld_flags = getenv('SPC_DEFAULT_LD_FLAGS');
// create pkgconfig and include dir (some libs cannot create them automatically) // create pkgconfig and include dir (some libs cannot create them automatically)
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true); f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
@ -113,12 +114,10 @@ class MacOSBuilder extends UnixBuilderBase
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP; $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 ' : '';
$envs_build_php = SystemUtil::makeEnvVarString([ $envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'), 'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'LDFLAGS' => '-L' . BUILD_LIB_PATH, 'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
]); ]);
if ($this->getLib('postgresql')) { if ($this->getLib('postgresql')) {
@ -270,8 +269,8 @@ class MacOSBuilder extends UnixBuilderBase
->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");
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
shell()->cd(SOURCE_PATH . '/php-src') $AR = getenv('AR') ?: 'ar';
->exec('ar -t ' . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 ar d " . BUILD_LIB_PATH . '/libphp.a'); f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
} }
$this->patchPhpScripts(); $this->patchPhpScripts();
} }

View File

@ -92,27 +92,31 @@ trait UnixLibraryTrait
{ {
$env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: ''; $env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: '';
if (!str_contains($env, $this->builder->arch_c_flags)) { if (!str_contains($env, $this->builder->arch_c_flags)) {
$env .= $this->builder->arch_c_flags; $env .= ' ' . $this->builder->arch_c_flags;
} }
return $env; return trim($env);
}
public function getLibExtraLdFlags(): string
{
return getenv($this->getSnakeCaseName() . '_LDFLAGS') ?: '';
}
public function getLibExtraLibs(): string
{
return getenv($this->getSnakeCaseName() . '_LIBS') ?: '';
} }
public function getLibExtraCXXFlags(): string public function getLibExtraCXXFlags(): string
{ {
$env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: ''; $env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: '';
if (!str_contains($env, $this->builder->arch_cxx_flags)) { if (!str_contains($env, $this->builder->arch_cxx_flags)) {
$env .= $this->builder->arch_cxx_flags; $env .= ' ' . $this->builder->arch_cxx_flags;
} }
return $env; return trim($env);
}
public function getLibExtraLdFlags(): string
{
$env = getenv($this->getSnakeCaseName() . '_LDFLAGS') ?: '';
if (!str_contains($env, $this->builder->arch_ld_flags)) {
$env .= ' ' . $this->builder->arch_ld_flags;
}
return trim($env);
}
public function getLibExtraLibs(): string
{
return getenv($this->getSnakeCaseName() . '_LIBS') ?: '';
} }
} }

View File

@ -12,7 +12,9 @@ use SPC\store\Config;
use SPC\store\CurlHook; use SPC\store\CurlHook;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\pkg\GoXcaddy;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
@ -24,6 +26,9 @@ abstract class UnixBuilderBase extends BuilderBase
/** @var string C++ flags */ /** @var string C++ flags */
public string $arch_cxx_flags; public string $arch_cxx_flags;
/** @var string LD flags */
public string $arch_ld_flags;
public function proveLibs(array $sorted_libraries): void public function proveLibs(array $sorted_libraries): void
{ {
// search all supported libs // search all supported libs
@ -122,11 +127,7 @@ abstract class UnixBuilderBase extends BuilderBase
if (SPCTarget::isStatic()) { if (SPCTarget::isStatic()) {
$lens .= ' -static'; $lens .= ' -static';
} }
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); // if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH
if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
}
// if someone changed to --enable-embed=shared, we need to add LD_LIBRARY_PATH
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
if (PHP_OS_FAMILY === 'Darwin') { if (PHP_OS_FAMILY === 'Darwin') {
$ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH '; $ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH ';
@ -211,7 +212,6 @@ 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')) {
@ -237,18 +237,6 @@ abstract class UnixBuilderBase extends BuilderBase
*/ */
protected function buildFrankenphp(): void 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' : ''; $nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : ''; $nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES'); $xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
@ -261,30 +249,30 @@ abstract class UnixBuilderBase extends BuilderBase
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules); $xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
} }
$lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : ''; $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); $releaseInfo = json_decode(Downloader::curlExec(
'https://api.github.com/repos/php/frankenphp/releases/latest',
hooks: [[CurlHook::class, 'setupGithubToken']],
), true);
$frankenPhpVersion = $releaseInfo['tag_name']; $frankenPhpVersion = $releaseInfo['tag_name'];
$libphpVersion = $this->getPHPVersion(); $libphpVersion = $this->getPHPVersion();
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion); $libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion);
} }
$debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $debugFlags = $this->getOption('no-strip') ? '-w -s ' : '';
$extLdFlags = "-extldflags '-pie'"; $extLdFlags = "-extldflags '-pie'";
$muslTags = ''; $muslTags = '';
$staticFlags = '';
if (SPCTarget::isStatic()) { if (SPCTarget::isStatic()) {
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
$muslTags = 'static_build,'; $muslTags = 'static_build,';
$staticFlags = '-static-pie';
} }
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list, with_dependencies: true); $config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list);
$env = [ $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_ENABLED' => '1',
'CGO_CFLAGS' => $config['cflags'], 'CGO_CFLAGS' => $config['cflags'],
'CGO_LDFLAGS' => "{$config['ldflags']} {$config['libs']} {$lrt}", 'CGO_LDFLAGS' => "{$staticFlags} {$config['ldflags']} {$config['libs']} {$lrt}",
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' . 'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags . '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' . '-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
@ -292,8 +280,23 @@ abstract class UnixBuilderBase extends BuilderBase
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",
'LD_LIBRARY_PATH' => BUILD_LIB_PATH, 'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
]; ];
foreach (GoXcaddy::getEnvironment() as $key => $value) {
if ($key === 'PATH') {
GlobalEnvManager::addPathIfNotExists($value);
} else {
$env[$key] = $value;
}
}
shell()->cd(BUILD_BIN_PATH) shell()->cd(BUILD_BIN_PATH)
->setEnv($env) ->setEnv($env)
->exec("{$xcaddy_exec} build --output frankenphp {$xcaddyModules}"); ->exec("xcaddy build --output frankenphp {$xcaddyModules}");
if (!$this->getOption('no-strip', false) && file_exists(BUILD_BIN_PATH . '/frankenphp')) {
if (PHP_OS_FAMILY === 'Linux') {
shell()->cd(BUILD_BIN_PATH)->exec('strip --strip-unneeded frankenphp');
} else { // macOS doesn't understand strip-unneeded
shell()->cd(BUILD_BIN_PATH)->exec('strip -S frankenphp');
}
}
} }
} }

View File

@ -15,6 +15,9 @@ trait attr
protected function build(): void protected function build(): void
{ {
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->appendEnv([
'CFLAGS' => '-Wno-int-conversion -Wno-implicit-function-declaration',
])
->exec('libtoolize --force --copy') ->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if') ->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls') ->configure('--disable-nls')

View File

@ -31,7 +31,7 @@ trait gettext
$autoconf->addConfigureArgs('--disable-threads'); $autoconf->addConfigureArgs('--disable-threads');
} }
$autoconf->configure()->make(with_clean: true); $autoconf->configure()->make();
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();
} }
} }

View File

@ -33,6 +33,7 @@ trait imagemagick
->optionalLib('freetype', ...ac_with_args('freetype')) ->optionalLib('freetype', ...ac_with_args('freetype'))
->optionalLib('bzip2', ...ac_with_args('bzlib')) ->optionalLib('bzip2', ...ac_with_args('bzlib'))
->optionalLib('libjxl', ...ac_with_args('jxl')) ->optionalLib('libjxl', ...ac_with_args('jxl'))
->optionalLib('jbig', ...ac_with_args('jbig'))
->addConfigureArgs( ->addConfigureArgs(
'--disable-openmp', '--disable-openmp',
'--without-x', '--without-x',

View File

@ -27,8 +27,10 @@ trait ldap
'--disable-slapd', '--disable-slapd',
'--without-systemd', '--without-systemd',
'--without-cyrus-sasl', '--without-cyrus-sasl',
'ac_cv_func_pthread_kill_other_threads_np=no'
) )
->appendEnv([ ->appendEnv([
'CFLAGS' => '-Wno-date-time',
'LDFLAGS' => "-L{$this->getLibDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}",
'CPPFLAGS' => "-I{$this->getIncludeDir()}", 'CPPFLAGS' => "-I{$this->getIncludeDir()}",
]) ])

View File

@ -4,22 +4,24 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException; use SPC\toolchain\ToolchainManager;
use SPC\exception\RuntimeException; use SPC\toolchain\ZigToolchain;
use SPC\util\executor\UnixCMakeExecutor; use SPC\util\executor\UnixCMakeExecutor;
trait libaom trait libaom
{ {
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void protected function build(): void
{ {
$extra = getenv('SPC_COMPILER_EXTRA');
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$new = trim($extra . ' -D_GNU_SOURCE');
f_putenv("SPC_COMPILER_EXTRA={$new}");
}
UnixCMakeExecutor::create($this) UnixCMakeExecutor::create($this)
->setBuildDir("{$this->source_dir}/builddir") ->setBuildDir("{$this->source_dir}/builddir")
->addConfigureArgs('-DAOM_TARGET_CPU=generic') ->addConfigureArgs('-DAOM_TARGET_CPU=generic')
->build(); ->build();
f_putenv("SPC_COMPILER_EXTRA={$extra}");
$this->patchPkgconfPrefix(['aom.pc']); $this->patchPkgconfPrefix(['aom.pc']);
} }
} }

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\executor\UnixCMakeExecutor; use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
@ -11,18 +13,31 @@ trait libjxl
{ {
protected function build(): void protected function build(): void
{ {
UnixCMakeExecutor::create($this) $cmake = UnixCMakeExecutor::create($this)
->addConfigureArgs('-DJPEGXL_ENABLE_TOOLS=OFF') ->addConfigureArgs(
->addConfigureArgs('-DJPEGXL_ENABLE_EXAMPLES=OFF') '-DJPEGXL_ENABLE_TOOLS=OFF',
->addConfigureArgs('-DJPEGXL_ENABLE_MANPAGES=OFF') '-DJPEGXL_ENABLE_EXAMPLES=OFF',
->addConfigureArgs('-DJPEGXL_ENABLE_BENCHMARK=OFF') '-DJPEGXL_ENABLE_MANPAGES=OFF',
->addConfigureArgs('-DJPEGXL_ENABLE_PLUGINS=OFF') '-DJPEGXL_ENABLE_BENCHMARK=OFF',
->addConfigureArgs('-DJPEGXL_ENABLE_SJPOEG=ON') '-DJPEGXL_ENABLE_PLUGINS=OFF',
->addConfigureArgs('-DJPEGXL_ENABLE_JNI=OFF') '-DJPEGXL_ENABLE_SJPEG=ON',
->addConfigureArgs('-DJPEGXL_ENABLE_TRANSCODE_JPEG=ON') '-DJPEGXL_ENABLE_JNI=OFF',
->addConfigureArgs('-DJPEGXL_STATIC=' . (SPCTarget::isStatic() ? 'ON' : 'OFF')) '-DJPEGXL_ENABLE_TRANSCODE_JPEG=ON',
->addConfigureArgs('-DJPEGXL_FORCE_SYSTEM_BROTLI=ON') '-DJPEGXL_STATIC=' . (SPCTarget::isStatic() ? 'ON' : 'OFF'),
->addConfigureArgs('-DBUILD_TESTING=OFF') '-DJPEGXL_FORCE_SYSTEM_BROTLI=ON',
->build(); '-DBUILD_TESTING=OFF'
);
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$cmake->addConfigureArgs(
'-DCXX_MAVX512F_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512DQ_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512CD_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512BW_SUPPORTED:BOOL=FALSE',
'-DCXX_MAVX512VL_SUPPORTED:BOOL=FALSE'
);
}
$cmake->build();
} }
} }

View File

@ -19,7 +19,11 @@ trait liblz4
{ {
shell()->cd($this->source_dir)->initializeEnv($this) shell()->cd($this->source_dir)->initializeEnv($this)
->exec("make PREFIX='' clean") ->exec("make PREFIX='' clean")
->exec("make -j{$this->builder->concurrency} PREFIX=''") ->exec("make lib -j{$this->builder->concurrency} PREFIX=''");
FileSystem::replaceFileStr($this->source_dir . '/Makefile', '$(MAKE) -C $(PRGDIR) $@', '');
shell()->cd($this->source_dir)
->exec("make install PREFIX='' DESTDIR=" . BUILD_ROOT_PATH); ->exec("make install PREFIX='' DESTDIR=" . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['liblz4.pc']); $this->patchPkgconfPrefix(['liblz4.pc']);

View File

@ -6,10 +6,26 @@ 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\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
trait librdkafka trait librdkafka
{ {
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileStr(
$this->source_dir . '/lds-gen.py',
"funcs.append('rd_ut_coverage_check')",
''
);
FileSystem::replaceFileStr(
$this->source_dir . '/src/rd.h',
'#error "IOV_MAX not defined"',
"#define IOV_MAX 1024\n#define __GNU__"
);
return true;
}
/** /**
* @throws FileSystemException * @throws FileSystemException
* @throws RuntimeException * @throws RuntimeException
@ -17,6 +33,7 @@ trait librdkafka
protected function build(): void protected function build(): void
{ {
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->appendEnv(['CFLAGS' => '-Wno-int-conversion -Wno-unused-but-set-variable -Wno-unused-variable'])
->optionalLib( ->optionalLib(
'zstd', 'zstd',
function ($lib) { function ($lib) {

View File

@ -35,18 +35,19 @@ trait libxslt
'--without-debugger', '--without-debugger',
"--with-libxml-prefix={$this->getBuildRootPath()}", "--with-libxml-prefix={$this->getBuildRootPath()}",
); );
if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) { if (getenv('SPC_LD_LIBRARY_PATH') && getenv('SPC_LIBRARY_PATH')) {
$ac->appendEnv([ $ac->appendEnv([
'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'), 'LD_LIBRARY_PATH' => getenv('SPC_LD_LIBRARY_PATH'),
'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'), 'LIBRARY_PATH' => getenv('SPC_LIBRARY_PATH'),
]); ]);
} }
$ac->configure()->make(); $ac->configure()->make();
$this->patchPkgconfPrefix(['libexslt.pc', 'libxslt.pc']); $this->patchPkgconfPrefix(['libexslt.pc', 'libxslt.pc']);
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();
$AR = getenv('AR') ?: 'ar';
shell()->cd(BUILD_LIB_PATH) shell()->cd(BUILD_LIB_PATH)
->exec("ar -t libxslt.a | grep '\\.a$' | xargs -n1 ar d libxslt.a") ->exec("{$AR} -t libxslt.a | grep '\\.a$' | xargs -n1 {$AR} d libxslt.a")
->exec("ar -t libexslt.a | grep '\\.a$' | xargs -n1 ar d libexslt.a"); ->exec("{$AR} -t libexslt.a | grep '\\.a$' | xargs -n1 {$AR} d libexslt.a");
} }
} }

View File

@ -14,6 +14,7 @@ trait mimalloc
$cmake = UnixCMakeExecutor::create($this) $cmake = UnixCMakeExecutor::create($this)
->addConfigureArgs( ->addConfigureArgs(
'-DMI_BUILD_SHARED=OFF', '-DMI_BUILD_SHARED=OFF',
'-DMI_BUILD_OBJECT=OFF',
'-DMI_INSTALL_TOPLEVEL=ON', '-DMI_INSTALL_TOPLEVEL=ON',
); );
if (SPCTarget::getLibc() === 'musl') { if (SPCTarget::getLibc() === 'musl') {

View File

@ -6,6 +6,7 @@ namespace SPC\builder\unix\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait ncurses trait ncurses
{ {
@ -14,6 +15,9 @@ trait ncurses
$filelist = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true); $filelist = FileSystem::scanDirFiles(BUILD_BIN_PATH, relative: true);
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->appendEnv([
'LDFLAGS' => SPCTarget::isStatic() ? '-static' : '',
])
->configure( ->configure(
'--enable-overwrite', '--enable-overwrite',
'--with-curses-h', '--with-curses-h',

View File

@ -48,6 +48,7 @@ trait ngtcp2
// on macOS, the static library may contain other static libraries? // on macOS, the static library may contain other static libraries?
// ld: archive member 'libssl.a' not a mach-o file in libngtcp2_crypto_ossl.a // ld: archive member 'libssl.a' not a mach-o file in libngtcp2_crypto_ossl.a
shell()->cd(BUILD_LIB_PATH)->exec("ar -t libngtcp2_crypto_ossl.a | grep '\\.a$' | xargs -n1 ar d libngtcp2_crypto_ossl.a"); $AR = getenv('AR') ?: 'ar';
shell()->cd(BUILD_LIB_PATH)->exec("{$AR} -t libngtcp2_crypto_ossl.a | grep '\\.a$' | xargs -n1 {$AR} d libngtcp2_crypto_ossl.a");
} }
} }

View File

@ -13,7 +13,7 @@ trait pkgconfig
{ {
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->appendEnv([ ->appendEnv([
'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '', 'CFLAGS' => '-Wimplicit-function-declaration -Wno-int-conversion',
'LDFLAGS' => SPCTarget::isStatic() ? '--static' : '', 'LDFLAGS' => SPCTarget::isStatic() ? '--static' : '',
]) ])
->configure( ->configure(

View File

@ -16,9 +16,13 @@ trait watcher
*/ */
protected function build(): void protected function build(): void
{ {
$cflags = $this->getLibExtraCXXFlags();
if (stripos($cflags, '-fpic') === false) {
$cflags .= ' -fPIC';
}
$ldflags = $this->getLibExtraLdFlags() ? ' ' . $this->getLibExtraLdFlags() : '';
shell()->cd($this->source_dir . '/watcher-c') shell()->cd($this->source_dir . '/watcher-c')
->initializeEnv($this) ->exec(getenv('CXX') . " -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra {$cflags}{$ldflags}")
->exec(getenv('CC') . ' -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra -fPIC')
->exec(getenv('AR') . ' rcs libwatcher-c.a libwatcher-c.o'); ->exec(getenv('AR') . ' rcs libwatcher-c.a libwatcher-c.o');
copy($this->source_dir . '/watcher-c/libwatcher-c.a', BUILD_LIB_PATH . '/libwatcher-c.a'); copy($this->source_dir . '/watcher-c/libwatcher-c.a', BUILD_LIB_PATH . '/libwatcher-c.a');

View File

@ -45,7 +45,7 @@ class WindowsBuilder extends BuilderBase
$this->zts = $this->getOption('enable-zts', false); $this->zts = $this->getOption('enable-zts', false);
// set concurrency // set concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY')); $this->concurrency = (int) getenv('SPC_CONCURRENCY');
// make cmake toolchain // make cmake toolchain
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile(); $this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile();

View File

@ -65,7 +65,7 @@ class BuildPHPCommand extends BuildCommand
// check dynamic extension build env // check dynamic extension build env
// linux must build with glibc // linux must build with glibc
if (!empty($shared_extensions) && SPCTarget::isStatic()) { if (!empty($shared_extensions) && SPCTarget::isStatic()) {
$this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with shared target!'); $this->output->writeln('Linux does not support dynamic extension loading with fully static builds, please build with a shared C runtime target!');
return static::FAILURE; return static::FAILURE;
} }
$static_and_shared = array_intersect($static_extensions, $shared_extensions); $static_and_shared = array_intersect($static_extensions, $shared_extensions);

View File

@ -5,6 +5,8 @@ declare(strict_types=1);
namespace SPC\command; namespace SPC\command;
use SPC\exception\ValidationException; use SPC\exception\ValidationException;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\ConfigValidator; use SPC\util\ConfigValidator;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
@ -71,7 +73,16 @@ class CraftCommand extends BuildCommand
$retcode = $this->runCommand('install-pkg', 'go-xcaddy'); $retcode = $this->runCommand('install-pkg', 'go-xcaddy');
if ($retcode !== 0) { if ($retcode !== 0) {
$this->output->writeln('<error>craft go-xcaddy failed</error>'); $this->output->writeln('<error>craft go-xcaddy failed</error>');
$this->log("craft go-xcaddy failed with code: {$retcode}", true); $this->log("craft: spc install-pkg go-xcaddy failed with code: {$retcode}", true);
return static::FAILURE;
}
}
// install zig if requested
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$retcode = $this->runCommand('install-pkg', 'zig');
if ($retcode !== 0) {
$this->output->writeln('<error>craft zig failed</error>');
$this->log("craft: spc install-pkg zig failed with code: {$retcode}", true);
return static::FAILURE; return static::FAILURE;
} }
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\doctor\item; namespace SPC\doctor\item;
use SPC\builder\linux\SystemUtil;
use SPC\doctor\AsCheckItem; use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
@ -17,13 +18,15 @@ use SPC\store\FileSystem;
use SPC\store\PackageManager; use SPC\store\PackageManager;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\toolchain\MuslToolchain; use SPC\toolchain\MuslToolchain;
use SPC\toolchain\ZigToolchain;
#[OptionalCheck([self::class, 'optionalCheck'])] #[OptionalCheck([self::class, 'optionalCheck'])]
class LinuxMuslCheck class LinuxMuslCheck
{ {
public static function optionalCheck(): bool public static function optionalCheck(): bool
{ {
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class; return getenv('SPC_TOOLCHAIN') === MuslToolchain::class ||
(getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !SystemUtil::isMuslDist());
} }
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */
@ -31,7 +34,7 @@ class LinuxMuslCheck
public function checkMusl(): CheckResult public function checkMusl(): CheckResult
{ {
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) { if (file_exists($musl_wrapper_lib) && (file_exists('/usr/local/musl/lib/libc.a') || getenv('SPC_TOOLCHAIN') === ZigToolchain::class)) {
return CheckResult::ok(); return CheckResult::ok();
} }
return CheckResult::fail('musl-wrapper is not installed on your system', 'fix-musl-wrapper'); return CheckResult::fail('musl-wrapper is not installed on your system', 'fix-musl-wrapper');
@ -40,6 +43,9 @@ class LinuxMuslCheck
#[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)] #[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)]
public function checkMuslCrossMake(): CheckResult public function checkMuslCrossMake(): CheckResult
{ {
if (getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !SystemUtil::isMuslDist()) {
return CheckResult::ok();
}
$arch = arch2gnu(php_uname('m')); $arch = arch2gnu(php_uname('m'));
$cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a";
$cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc"; $cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc";

View File

@ -69,7 +69,7 @@ class LinuxToolCheckList
}; };
$missing = []; $missing = [];
foreach ($required as $package) { foreach ($required as $package) {
if ($this->findCommand(self::PROVIDED_COMMAND[$package] ?? $package) === null) { if (self::findCommand(self::PROVIDED_COMMAND[$package] ?? $package) === null) {
$missing[] = $package; $missing[] = $package;
} }
} }

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace SPC\doctor\item;
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult;
use SPC\doctor\OptionalCheck;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\PackageManager;
use SPC\store\pkg\Zig;
use SPC\toolchain\ZigToolchain;
#[OptionalCheck([self::class, 'optionalCheck'])]
class ZigCheck
{
public static function optionalCheck(): bool
{
return getenv('SPC_TOOLCHAIN') === ZigToolchain::class;
}
/** @noinspection PhpUnused */
#[AsCheckItem('if zig is installed', level: 800)]
public function checkZig(): CheckResult
{
if (Zig::isInstalled()) {
return CheckResult::ok();
}
return CheckResult::fail('zig is not installed', 'install-zig');
}
/** @noinspection PhpUnused */
/**
* @throws FileSystemException
* @throws WrongUsageException
*/
#[AsFixItem('install-zig')]
public function installZig(): bool
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
PackageManager::installPackage("zig-{$arch}-{$os}");
return Zig::isInstalled();
}
}

View File

@ -389,8 +389,8 @@ class Downloader
$cls = new $class(); $cls = new $class();
if (in_array($name, $cls->getSupportName())) { if (in_array($name, $cls->getSupportName())) {
(new $class())->fetch($name, $force, $pkg); (new $class())->fetch($name, $force, $pkg);
break;
} }
break;
} }
} }
break; break;

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\builder\linux\LinuxBuilder;
use SPC\builder\linux\SystemUtil; use SPC\builder\linux\SystemUtil;
use SPC\builder\unix\UnixBuilderBase; use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
@ -17,21 +16,21 @@ class SourcePatcher
{ {
public static function init(): void public static function init(): void
{ {
// FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']); // FileSystem::addSourceExtractHook('swow', [__CLASS__, 'patchSwow']);
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']); FileSystem::addSourceExtractHook('openssl', [__CLASS__, 'patchOpenssl11Darwin']);
FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']); FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchGDWin32']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchGDWin32']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchFfiCentos7FixO3strncmp']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']);
FileSystem::addSourceExtractHook('sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('pdo_sqlsrv', [SourcePatcher::class, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('yaml', [SourcePatcher::class, 'patchYamlWin32']); FileSystem::addSourceExtractHook('yaml', [__CLASS__, 'patchYamlWin32']);
FileSystem::addSourceExtractHook('libyaml', [SourcePatcher::class, 'patchLibYaml']); FileSystem::addSourceExtractHook('libyaml', [__CLASS__, 'patchLibYaml']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchImapLicense']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']);
FileSystem::addSourceExtractHook('ext-imagick', [SourcePatcher::class, 'patchImagickWith84']); FileSystem::addSourceExtractHook('ext-imagick', [__CLASS__, 'patchImagickWith84']);
FileSystem::addSourceExtractHook('libaom', [SourcePatcher::class, 'patchLibaomForAlpine']); FileSystem::addSourceExtractHook('libaom', [__CLASS__, 'patchLibaomForAlpine']);
FileSystem::addSourceExtractHook('attr', [SourcePatcher::class, 'patchAttrForAlpine']); FileSystem::addSourceExtractHook('attr', [__CLASS__, 'patchAttrForAlpine']);
FileSystem::addSourceExtractHook('gmssl', [SourcePatcher::class, 'patchGMSSL']); FileSystem::addSourceExtractHook('gmssl', [__CLASS__, 'patchGMSSL']);
} }
/** /**
@ -73,9 +72,9 @@ class SourcePatcher
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC('); FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
if ($builder->getOption('enable-micro-win32')) { if ($builder->getOption('enable-micro-win32')) {
SourcePatcher::patchMicroWin32(); self::patchMicroWin32();
} else { } else {
SourcePatcher::unpatchMicroWin32(); self::unpatchMicroWin32();
} }
} }
@ -235,6 +234,7 @@ class SourcePatcher
'PHP_ADD_INCLUDE([$ext_srcdir])', 'PHP_ADD_INCLUDE([$ext_srcdir])',
"PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])" "PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])"
); );
// swoole 5.1.3 build fix // swoole 5.1.3 build fix
// get swoole version first // get swoole version first
$file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h'; $file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h';
@ -248,6 +248,7 @@ class SourcePatcher
if ($version === '5.1.3') { if ($version === '5.1.3') {
self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole'); self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole');
} }
self::patchFile('swoole_fix_date_time.patch', SOURCE_PATH . '/php-src/ext/swoole');
return true; return true;
} }
@ -256,14 +257,23 @@ class SourcePatcher
*/ */
public static function patchBeforeMake(BuilderBase $builder): void public static function patchBeforeMake(BuilderBase $builder): void
{ {
// Try to fix debian environment build lack HAVE_STRLCAT problem
if ($builder instanceof LinuxBuilder) {
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', '');
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', '');
}
if ($builder instanceof UnixBuilderBase) { if ($builder instanceof UnixBuilderBase) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', ''); FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
} }
if (!SPCTarget::isStatic() && SPCTarget::getLibc() === 'musl') {
// we need to patch the symbol to global visibility, otherwise extensions with `initial-exec` TLS model will fail to load
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
);
} else {
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
);
}
// no asan // no asan
// if (strpos(file_get_contents(SOURCE_PATH . '/php-src/Makefile'), 'CFLAGS_CLEAN = -g') === false) { // if (strpos(file_get_contents(SOURCE_PATH . '/php-src/Makefile'), 'CFLAGS_CLEAN = -g') === false) {
@ -271,7 +281,7 @@ class SourcePatcher
// } // }
// call extension patch before make // call extension patch before make
foreach ($builder->getExts(false) as $ext) { foreach ($builder->getExts() as $ext) {
if ($ext->patchBeforeMake() === true) { if ($ext->patchBeforeMake() === true) {
logger()->info("Extension [{$ext->getName()}] patched before make"); logger()->info("Extension [{$ext->getName()}] patched before make");
} }
@ -454,7 +464,7 @@ class SourcePatcher
} }
$extnum = intval($match[1]); $extnum = intval($match[1]);
if ($extnum < 30800) { if ($extnum < 30800) {
SourcePatcher::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick'); self::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
return true; return true;
} }
return false; return false;
@ -472,14 +482,14 @@ class SourcePatcher
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) { if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) {
return false; return false;
} }
SourcePatcher::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src'); self::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src');
return true; return true;
} }
public static function patchLibaomForAlpine(): bool public static function patchLibaomForAlpine(): bool
{ {
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) { if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) {
SourcePatcher::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom'); self::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom');
return true; return true;
} }
return false; return false;
@ -488,7 +498,7 @@ class SourcePatcher
public static function patchAttrForAlpine(): bool public static function patchAttrForAlpine(): bool
{ {
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() || PHP_OS_FAMILY === 'Darwin') { if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() || PHP_OS_FAMILY === 'Darwin') {
SourcePatcher::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr'); self::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr');
return true; return true;
} }
return false; return false;

View File

@ -28,14 +28,18 @@ abstract class CustomPackage
*/ */
abstract public function fetch(string $name, bool $force = false, ?array $config = null): void; abstract public function fetch(string $name, bool $force = false, ?array $config = null): void;
/**
* Get the environment variables this package needs to be usable.
* PATH needs to be appended, rather than replaced.
*/
abstract public static function getEnvironment(): array;
abstract public static function isInstalled(): bool;
/** /**
* Extract the downloaded package * Extract the downloaded package
* *
* @param string $name Package name * @param string $name Package name
* @throws \RuntimeException If extraction is not implemented
*/ */
public function extract(string $name): void abstract public function extract(string $name): void;
{
throw new \RuntimeException("Extract method not implemented for package: {$name}");
}
} }

View File

@ -4,12 +4,30 @@ declare(strict_types=1);
namespace SPC\store\pkg; namespace SPC\store\pkg;
use SPC\builder\linux\SystemUtil;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
class GoXcaddy extends CustomPackage class GoXcaddy extends CustomPackage
{ {
public static function isInstalled(): bool
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "go-xcaddy-{$arch}-{$os}";
$pkgroot = PKG_ROOT_PATH;
$folder = "{$pkgroot}/{$packageName}";
return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy");
}
public function getSupportName(): array public function getSupportName(): array
{ {
return [ return [
@ -25,6 +43,9 @@ class GoXcaddy extends CustomPackage
$pkgroot = PKG_ROOT_PATH; $pkgroot = PKG_ROOT_PATH;
$go_exec = "{$pkgroot}/{$name}/bin/go"; $go_exec = "{$pkgroot}/{$name}/bin/go";
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy"; $xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy";
if ($force) {
FileSystem::removeDir("{$pkgroot}/{$name}");
}
if (file_exists($go_exec) && file_exists($xcaddy_exec)) { if (file_exists($go_exec) && file_exists($xcaddy_exec)) {
return; return;
} }
@ -57,18 +78,45 @@ class GoXcaddy extends CustomPackage
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true); $lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type']; $source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']); $filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = $lock[$name]['move_path'] === null ? "{$pkgroot}/{$name}" : $lock[$name]['move_path']; $extract = $lock[$name]['move_path'] ?? "{$pkgroot}/{$name}";
FileSystem::extractPackage($name, $source_type, $filename, $extract); FileSystem::extractPackage($name, $source_type, $filename, $extract);
// install xcaddy $sanitizedPath = getenv('PATH');
if (PHP_OS_FAMILY === 'Linux' && !SystemUtil::isMuslDist()) {
$sanitizedPath = preg_replace('#(:?/?[^:]*musl[^:]*)#', '', $sanitizedPath);
$sanitizedPath = preg_replace('#^:|:$|::#', ':', $sanitizedPath); // clean up colons
}
// install xcaddy without using musl tools, xcaddy build requires dynamic linking
shell() shell()
->appendEnv([ ->appendEnv([
'PATH' => "{$pkgroot}/{$name}/bin:" . getenv('PATH'), 'PATH' => "{$pkgroot}/{$name}/bin:" . $sanitizedPath,
'GOROOT' => "{$pkgroot}/{$name}", 'GOROOT' => "{$pkgroot}/{$name}",
'GOBIN' => "{$pkgroot}/{$name}/bin", 'GOBIN' => "{$pkgroot}/{$name}/bin",
'GOPATH' => "{$pkgroot}/go", 'GOPATH' => "{$pkgroot}/go",
]) ])
->exec("{$go_exec} install github.com/caddyserver/xcaddy/cmd/xcaddy@latest"); ->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
}
public static function getEnvironment(): array
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "go-xcaddy-{$arch}-{$os}";
$pkgroot = PKG_ROOT_PATH;
return [
'PATH' => "{$pkgroot}/{$packageName}/bin",
'GOROOT' => "{$pkgroot}/{$packageName}",
'GOBIN' => "{$pkgroot}/{$packageName}/bin",
'GOPATH' => "{$pkgroot}/go",
];
} }
} }

185
src/SPC/store/pkg/Zig.php Normal file
View File

@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace SPC\store\pkg;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\CurlHook;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\LockFile;
class Zig extends CustomPackage
{
public static function isInstalled(): bool
{
$path = self::getPath();
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
foreach ($files as $file) {
if (!file_exists("{$path}/{$file}")) {
return false;
}
}
return true;
}
public function getSupportName(): array
{
return [
'zig-x86_64-linux',
'zig-aarch64-linux',
'zig-x86_64-macos',
'zig-aarch64-macos',
'zig-x86_64-win',
];
}
public function fetch(string $name, bool $force = false, ?array $config = null): void
{
$pkgroot = PKG_ROOT_PATH;
$zig_exec = match (PHP_OS_FAMILY) {
'Windows' => "{$pkgroot}/{$name}/zig.exe",
default => "{$pkgroot}/{$name}/zig",
};
if ($force) {
FileSystem::removeDir("{$pkgroot}/{$name}");
}
if (file_exists($zig_exec)) {
return;
}
$parts = explode('-', $name);
$arch = $parts[1];
$os = $parts[2];
$zig_arch = match ($arch) {
'x86_64', 'aarch64' => $arch,
default => throw new WrongUsageException('Unsupported architecture: ' . $arch),
};
$zig_os = match ($os) {
'linux' => 'linux',
'macos' => 'macos',
'win' => 'windows',
default => throw new WrongUsageException('Unsupported OS: ' . $os),
};
$index_json = json_decode(Downloader::curlExec('https://ziglang.org/download/index.json', hooks: [[CurlHook::class, 'setupGithubToken']]), true);
$latest_version = null;
foreach ($index_json as $version => $data) {
$latest_version = $version;
break;
}
if (!$latest_version) {
throw new RuntimeException('Could not determine latest Zig version');
}
logger()->info("Installing Zig version {$latest_version}");
$platform_key = "{$zig_arch}-{$zig_os}";
if (!isset($index_json[$latest_version][$platform_key])) {
throw new RuntimeException("No download available for {$platform_key} in Zig version {$latest_version}");
}
$download_info = $index_json[$latest_version][$platform_key];
$url = $download_info['tarball'];
$filename = basename($url);
$pkg = [
'type' => 'url',
'url' => $url,
'filename' => $filename,
];
Downloader::downloadPackage($name, $pkg, $force);
}
public function extract(string $name): void
{
$pkgroot = PKG_ROOT_PATH;
$zig_bin_dir = "{$pkgroot}/{$name}";
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
$all_exist = true;
foreach ($files as $file) {
if (!file_exists("{$zig_bin_dir}/{$file}")) {
$all_exist = false;
break;
}
}
if ($all_exist) {
return;
}
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = "{$pkgroot}/{$name}";
FileSystem::extractPackage($name, $source_type, $filename, $extract);
$this->createZigCcScript($zig_bin_dir);
}
public static function getEnvironment(): array
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "zig-{$arch}-{$os}";
$path = PKG_ROOT_PATH . "/{$packageName}";
return [
'PATH' => $path,
];
}
/**
* @throws WrongUsageException
*/
private static function getPath(): string
{
$arch = arch2gnu(php_uname('m'));
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "zig-{$arch}-{$os}";
return PKG_ROOT_PATH . "/{$packageName}";
}
private function createZigCcScript(string $bin_dir): void
{
$script_path = __DIR__ . '/../scripts/zig-cc.sh';
$script_content = file_get_contents($script_path);
file_put_contents("{$bin_dir}/zig-cc", $script_content);
chmod("{$bin_dir}/zig-cc", 0755);
$script_content = str_replace('zig cc', 'zig c++', $script_content);
file_put_contents("{$bin_dir}/zig-c++", $script_content);
file_put_contents("{$bin_dir}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@");
file_put_contents("{$bin_dir}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@");
file_put_contents("{$bin_dir}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@");
file_put_contents("{$bin_dir}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@");
chmod("{$bin_dir}/zig-c++", 0755);
chmod("{$bin_dir}/zig-ar", 0755);
chmod("{$bin_dir}/zig-ld.lld", 0755);
chmod("{$bin_dir}/zig-ranlib", 0755);
chmod("{$bin_dir}/zig-objcopy", 0755);
}
}

View File

@ -0,0 +1,54 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../buildroot/include" 2>/dev/null || true)"
PARSED_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
-isystem)
shift
ARG="$1"
shift
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG")
;;
-isystem*)
ARG="${1#-isystem}"
shift
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG")
;;
-march=*|-mcpu=*) # replace -march=x86-64 with -march=x86_64
OPT_NAME="${1%%=*}"
OPT_VALUE="${1#*=}"
OPT_VALUE="${OPT_VALUE//-/_}"
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
shift
;;
*)
PARSED_ARGS+=("$1")
shift
;;
esac
done
[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET=""
if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then
output=$(zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}" 2>&1)
status=$?
if [[ $status -eq 0 ]]; then
echo "$output"
exit 0
fi
if echo "$output" | grep -qE "version '.*' in target triple"; then
filtered_output=$(echo "$output" | grep -vE "version '.*' in target triple")
echo "$filtered_output"
exit 0
fi
fi
exec zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}"

View File

@ -7,6 +7,7 @@ namespace SPC\store\source;
use JetBrains\PhpStorm\ArrayShape; use JetBrains\PhpStorm\ArrayShape;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\Downloader; use SPC\store\Downloader;
class PhpSource extends CustomSourceBase class PhpSource extends CustomSourceBase
@ -16,6 +17,7 @@ class PhpSource extends CustomSourceBase
/** /**
* @throws DownloaderException * @throws DownloaderException
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException
*/ */
public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void
{ {

View File

@ -10,6 +10,9 @@ use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
/**
* Toolchain implementation for system clang compiler.
*/
class ClangNativeToolchain implements ToolchainInterface class ClangNativeToolchain implements ToolchainInterface
{ {
public function initEnv(): void public function initEnv(): void
@ -20,6 +23,9 @@ class ClangNativeToolchain implements ToolchainInterface
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
} }
/**
* @throws WrongUsageException
*/
public function afterInit(): void public function afterInit(): void
{ {
foreach (['CC', 'CXX', 'AR', 'LD'] as $env) { foreach (['CC', 'CXX', 'AR', 'LD'] as $env) {

View File

@ -17,7 +17,7 @@ class GccNativeToolchain implements ToolchainInterface
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld.gold'); GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
} }
public function afterInit(): void public function afterInit(): void

View File

@ -20,8 +20,8 @@ class MuslToolchain implements ToolchainInterface
GlobalEnvManager::addPathIfNotExists('/usr/local/musl/bin'); GlobalEnvManager::addPathIfNotExists('/usr/local/musl/bin');
GlobalEnvManager::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin"); GlobalEnvManager::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib"); GlobalEnvManager::putenv("SPC_LD_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib"); GlobalEnvManager::putenv("SPC_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
} }
public function afterInit(): void public function afterInit(): void
@ -29,7 +29,7 @@ class MuslToolchain implements ToolchainInterface
$arch = getenv('GNU_ARCH'); $arch = getenv('GNU_ARCH');
// append LD_LIBRARY_PATH to $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE'); // append LD_LIBRARY_PATH to $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE'); $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$ld_library_path = getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'); $ld_library_path = getenv('SPC_LD_LIBRARY_PATH');
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$configure}"); GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$configure}");
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) { if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {

View File

@ -7,43 +7,57 @@ namespace SPC\toolchain;
use SPC\builder\linux\SystemUtil; use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\SPCTarget;
class ToolchainManager class ToolchainManager
{ {
public const array OS_DEFAULT_TOOLCHAIN = [ public const array OS_DEFAULT_TOOLCHAIN = [
'Linux' => MuslToolchain::class, // use musl toolchain by default, after zig pr merged, change this to ZigToolchain::class 'Linux' => ZigToolchain::class,
'Windows' => MSVCToolchain::class, 'Windows' => MSVCToolchain::class,
'Darwin' => ClangNativeToolchain::class, 'Darwin' => ClangNativeToolchain::class,
'BSD' => ClangNativeToolchain::class, 'BSD' => ClangNativeToolchain::class,
]; ];
public static function getToolchainClass(): string
{
if ($tc = getenv('SPC_TOOLCHAIN')) {
return $tc;
}
$libc = getenv('SPC_LIBC');
if ($libc && !getenv('SPC_TARGET')) {
// trigger_error('Setting SPC_LIBC is deprecated, please use SPC_TARGET instead.', E_USER_DEPRECATED);
return match ($libc) {
'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class,
'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_LIBC must be musl for musl dist.'),
default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc),
};
}
return self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY];
}
/** /**
* @throws WrongUsageException * @throws WrongUsageException
*/ */
public static function initToolchain(): void public static function initToolchain(): void
{ {
$libc = getenv('SPC_LIBC'); $toolchainClass = self::getToolchainClass();
if ($libc !== false) {
// uncomment this when zig pr is merged
// logger()->warning('SPC_LIBC is deprecated, please use SPC_TARGET instead.');
$toolchain = match ($libc) {
'musl' => SystemUtil::isMuslDist() ? GccNativeToolchain::class : MuslToolchain::class,
'glibc' => !SystemUtil::isMuslDist() ? GccNativeToolchain::class : throw new WrongUsageException('SPC_TARGET must be musl-static or musl for musl dist.'),
default => throw new WrongUsageException('Unsupported SPC_LIBC value: ' . $libc),
};
} else {
$toolchain = self::OS_DEFAULT_TOOLCHAIN[PHP_OS_FAMILY];
}
$toolchainClass = $toolchain;
/* @var ToolchainInterface $toolchainClass */ /* @var ToolchainInterface $toolchainClass */
(new $toolchainClass())->initEnv(); (new $toolchainClass())->initEnv();
GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}"); GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchainClass}");
} }
public static function afterInitToolchain(): void public static function afterInitToolchain(): void
{ {
if (!getenv('SPC_TOOLCHAIN')) { if (!getenv('SPC_TOOLCHAIN')) {
throw new WrongUsageException('SPC_TOOLCHAIN not set'); throw new WrongUsageException('SPC_TOOLCHAIN was not properly set. Please contact the developers.');
}
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
if (SPCTarget::getLibc() === 'musl' && !SPCTarget::isStatic() && !file_exists($musl_wrapper_lib)) {
throw new WrongUsageException('You are linking against musl libc dynamically, but musl libc is not installed. Please use `bin/spc doctor` to install it.');
}
if (SPCTarget::getLibc() === 'glibc' && SystemUtil::isMuslDist()) {
throw new WrongUsageException('You are linking against glibc dynamically, which is only supported on glibc distros.');
} }
$toolchain = getenv('SPC_TOOLCHAIN'); $toolchain = getenv('SPC_TOOLCHAIN');
/* @var ToolchainInterface $toolchain */ /* @var ToolchainInterface $toolchain */

View File

@ -4,9 +4,68 @@ declare(strict_types=1);
namespace SPC\toolchain; namespace SPC\toolchain;
use SPC\exception\WrongUsageException;
use SPC\store\pkg\Zig;
use SPC\util\GlobalEnvManager;
class ZigToolchain implements ToolchainInterface class ZigToolchain implements ToolchainInterface
{ {
public function initEnv(): void {} public function initEnv(): void
{
// Set environment variables for zig toolchain
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=zig-cc');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=zig-c++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=zig-ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=zig-ld.lld');
public function afterInit(): void {} // Generate additional objects needed for zig toolchain
$paths = ['/usr/lib/gcc', '/usr/local/lib/gcc'];
$objects = ['crtbeginS.o', 'crtendS.o'];
$found = [];
foreach ($objects as $obj) {
$located = null;
foreach ($paths as $base) {
$output = shell_exec("find {$base} -name {$obj} 2>/dev/null | grep -v '/32/' | head -n 1");
$line = trim((string) $output);
if ($line !== '') {
$located = $line;
break;
}
}
if ($located) {
$found[] = $located;
}
}
GlobalEnvManager::putenv('SPC_EXTRA_RUNTIME_OBJECTS=' . implode(' ', $found));
}
/**
* @throws WrongUsageException
*/
public function afterInit(): void
{
if (!is_dir(Zig::getEnvironment()['PATH'])) {
throw new WrongUsageException('You are building with zig, but zig is not installed, please install zig first. (You can use `doctor` command to install it)');
}
GlobalEnvManager::addPathIfNotExists(Zig::getEnvironment()['PATH']);
f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough
$cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: '';
$cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: '';
$extraCflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') ?: '';
$cflags = trim($cflags . ' -Wno-date-time');
$cxxflags = trim($cxxflags . ' -Wno-date-time');
$extraCflags = trim($extraCflags . ' -Wno-date-time');
GlobalEnvManager::putenv("SPC_DEFAULT_C_FLAGS={$cflags}");
GlobalEnvManager::putenv("SPC_DEFAULT_CXX_FLAGS={$cxxflags}");
GlobalEnvManager::putenv("SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS={$extraCflags}");
GlobalEnvManager::putenv('RANLIB=zig-ranlib');
GlobalEnvManager::putenv('OBJCOPY=zig-objcopy');
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
if (!str_contains($extra_libs, '-lunwind')) {
// Add unwind library if not already present
$extra_libs = trim($extra_libs . ' -lunwind');
GlobalEnvManager::putenv("SPC_EXTRA_LIBS={$extra_libs}");
}
}
} }

View File

@ -16,6 +16,8 @@ class GlobalEnvManager
{ {
private static array $env_cache = []; private static array $env_cache = [];
private static bool $initialized = false;
public static function getInitializedEnv(): array public static function getInitializedEnv(): array
{ {
return self::$env_cache; return self::$env_cache;
@ -29,6 +31,9 @@ class GlobalEnvManager
*/ */
public static function init(): void public static function init(): void
{ {
if (self::$initialized) {
return;
}
// Check pre-defined env vars exists // Check pre-defined env vars exists
if (getenv('BUILD_ROOT_PATH') === false) { if (getenv('BUILD_ROOT_PATH') === false) {
throw new RuntimeException('You must include src/globals/internal-env.php before using GlobalEnvManager'); throw new RuntimeException('You must include src/globals/internal-env.php before using GlobalEnvManager');
@ -86,6 +91,7 @@ class GlobalEnvManager
self::putenv("{$k}={$v}"); self::putenv("{$k}={$v}");
} }
} }
self::$initialized = true;
} }
public static function putenv(string $val): void public static function putenv(string $val): void

View File

@ -57,7 +57,7 @@ class SPCConfigUtil
* @throws WrongUsageException * @throws WrongUsageException
* @throws \Throwable * @throws \Throwable
*/ */
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false, bool $with_dependencies = false): array public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
{ {
[$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib); [$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
@ -73,9 +73,9 @@ class SPCConfigUtil
$libs = $this->getLibsString($libraries, !$this->absolute_libs); $libs = $this->getLibsString($libraries, !$this->absolute_libs);
// additional OS-specific libraries (e.g. macOS -lresolv) // additional OS-specific libraries (e.g. macOS -lresolv)
$extra_env = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'); // embed
if (is_string($extra_env)) { if ($extra_libs = SPCTarget::getRuntimeLibs()) {
$libs .= ' ' . trim($extra_env, '"'); $libs .= " {$extra_libs}";
} }
$extra_env = getenv('SPC_EXTRA_LIBS'); $extra_env = getenv('SPC_EXTRA_LIBS');
if (is_string($extra_env) && !empty($extra_env)) { if (is_string($extra_env) && !empty($extra_env)) {
@ -86,14 +86,21 @@ class SPCConfigUtil
$libs .= " {$this->getFrameworksString($extensions)}"; $libs .= " {$this->getFrameworksString($extensions)}";
} }
if ($this->builder->hasCpp()) { if ($this->builder->hasCpp()) {
$libs .= SPCTarget::getTargetOS() === 'Darwin' ? ' -lc++' : ' -lstdc++'; $libcpp = SPCTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++';
if (!str_contains($libs, $libcpp)) {
$libs .= " {$libcpp}";
}
} }
if ($this->libs_only_deps) { if ($this->libs_only_deps) {
// mimalloc must come first
if ($this->builder->getLib('mimalloc') && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
$libs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $libs);
}
return [ return [
'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags), 'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags), 'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
'libs' => trim(getenv('LIBS') . ' ' . $libs), 'libs' => clean_spaces(getenv('LIBS') . ' ' . $libs),
]; ];
} }
@ -105,14 +112,14 @@ class SPCConfigUtil
$allLibs = getenv('LIBS') . ' ' . $libs; $allLibs = getenv('LIBS') . ' ' . $libs;
// mimalloc must come first // mimalloc must come first
if (str_contains($libs, BUILD_LIB_PATH . '/mimalloc.o')) { if ($this->builder->getLib('mimalloc') && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
$allLibs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $allLibs); $allLibs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $allLibs);
} }
return [ return [
'cflags' => trim(getenv('CFLAGS') . ' ' . $cflags), 'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
'ldflags' => trim(getenv('LDFLAGS') . ' ' . $ldflags), 'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
'libs' => trim($allLibs), 'libs' => clean_spaces($allLibs),
]; ];
} }
@ -179,7 +186,7 @@ class SPCConfigUtil
$lib_names = [...$lib_names, ...$pc_libs]; $lib_names = [...$lib_names, ...$pc_libs];
} }
// convert all static-libs to short names // convert all static-libs to short names
$libs = Config::getLib($library, 'static-libs', []); $libs = array_reverse(Config::getLib($library, 'static-libs', []));
foreach ($libs as $lib) { foreach ($libs as $lib) {
// check file existence // check file existence
if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) { if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) {

View File

@ -5,7 +5,10 @@ declare(strict_types=1);
namespace SPC\util; namespace SPC\util;
use SPC\builder\linux\SystemUtil; use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException; use SPC\toolchain\ClangNativeToolchain;
use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\MuslToolchain;
use SPC\toolchain\ToolchainManager;
/** /**
* SPC build target constants and toolchain initialization. * SPC build target constants and toolchain initialization.
@ -13,23 +16,44 @@ use SPC\exception\WrongUsageException;
*/ */
class SPCTarget class SPCTarget
{ {
public const array LIBC_LIST = [ public const array LIBC_LIST = ['musl', 'glibc'];
'musl',
'glibc',
];
/** /**
* Returns whether the target is a full-static target. * Returns whether we link the C runtime in statically.
*/ */
public static function isStatic(): bool public static function isStatic(): bool
{ {
$env = getenv('SPC_TARGET'); if (ToolchainManager::getToolchainClass() === MuslToolchain::class) {
$libc = getenv('SPC_LIBC'); return true;
// if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released }
if ($libc === 'musl') { if (ToolchainManager::getToolchainClass() === GccNativeToolchain::class) {
return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist();
}
if (ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) {
return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist();
}
// if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released
if ($target = getenv('SPC_TARGET')) {
if (str_contains($target, '-macos') || str_contains($target, '-native') && PHP_OS_FAMILY === 'Darwin') {
return false;
}
if (str_contains($target, '-gnu')) {
return false;
}
if (str_contains($target, '-dynamic')) {
return false;
}
if (str_contains($target, '-musl')) {
return true;
}
if (PHP_OS_FAMILY === 'Linux') {
return SystemUtil::isMuslDist();
}
return true;
}
if (getenv('SPC_LIBC') === 'musl') {
return true; return true;
} }
// TODO: add zig target parser here
return false; return false;
} }
@ -38,29 +62,45 @@ class SPCTarget
*/ */
public static function getLibc(): ?string public static function getLibc(): ?string
{ {
$env = getenv('SPC_TARGET'); if ($target = getenv('SPC_TARGET')) {
if (str_contains($target, '-gnu')) {
return 'glibc';
}
if (str_contains($target, '-musl')) {
return 'musl';
}
if (PHP_OS_FAMILY === 'Linux') {
return SystemUtil::isMuslDist() ? 'musl' : 'glibc';
}
}
$libc = getenv('SPC_LIBC'); $libc = getenv('SPC_LIBC');
if ($libc !== false) { if ($libc !== false) {
return $libc; return $libc;
} }
// TODO: zig target parser if (PHP_OS_FAMILY === 'Linux') {
return SystemUtil::isMuslDist() ? 'musl' : 'glibc';
}
return null; return null;
} }
public static function getRuntimeLibs(): string
{
if (PHP_OS_FAMILY === 'Linux') {
return self::getLibc() === 'musl' ? '-ldl -lpthread -lm' : '-ldl -lrt -lpthread -lm -lresolv -lutil';
}
if (PHP_OS_FAMILY === 'Darwin') {
return '-lresolv';
}
return '';
}
/** /**
* Returns the libc version if set, for other OS, it will always return null. * Returns the libc version if set, for other OS, it will always return null.
*/ */
public static function getLibcVersion(): ?string public static function getLibcVersion(): ?string
{ {
$env = getenv('SPC_TARGET'); $libc = self::getLibc();
$libc = getenv('SPC_LIBC'); return SystemUtil::getLibcVersionIfExists($libc);
if ($libc !== false) {
// legacy method: get a version from system
return SystemUtil::getLibcVersionIfExists($libc);
}
// TODO: zig target parser
return null;
} }
/** /**
@ -68,20 +108,16 @@ class SPCTarget
* Currently, we only support native building. * Currently, we only support native building.
* *
* @return 'BSD'|'Darwin'|'Linux'|'Windows' * @return 'BSD'|'Darwin'|'Linux'|'Windows'
* @throws WrongUsageException
*/ */
public static function getTargetOS(): string public static function getTargetOS(): string
{ {
$target = getenv('SPC_TARGET'); $target = (string) getenv('SPC_TARGET');
if ($target === false || $target === '') {
return PHP_OS_FAMILY;
}
// TODO: zig target parser like below?
return match (true) { return match (true) {
str_contains($target, 'linux') => 'Linux', str_contains($target, '-linux') => 'Linux',
str_contains($target, 'macos') => 'Darwin', str_contains($target, '-macos') => 'Darwin',
str_contains($target, 'windows') => 'Windows', str_contains($target, '-windows') => 'Windows',
default => throw new WrongUsageException('Cannot parse target.'), str_contains($target, '-native') => PHP_OS_FAMILY,
default => PHP_OS_FAMILY,
}; };
} }
} }

View File

@ -61,9 +61,9 @@ class UnixShell
{ {
$this->setEnv([ $this->setEnv([
'CFLAGS' => $library->getLibExtraCFlags(), 'CFLAGS' => $library->getLibExtraCFlags(),
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
'LDFLAGS' => $library->getLibExtraLdFlags(), 'LDFLAGS' => $library->getLibExtraLdFlags(),
'LIBS' => $library->getLibExtraLibs(), 'LIBS' => $library->getLibExtraLibs(),
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
]); ]);
return $this; return $this;
} }

View File

@ -135,6 +135,7 @@ class UnixAutoconfExecutor extends Executor
{ {
$this->shell = shell()->cd($this->library->getSourceDir())->initializeEnv($this->library)->appendEnv([ $this->shell = shell()->cd($this->library->getSourceDir())->initializeEnv($this->library)->appendEnv([
'CFLAGS' => "-I{$this->library->getIncludeDir()}", 'CFLAGS' => "-I{$this->library->getIncludeDir()}",
'CXXFLAGS' => "-I{$this->library->getIncludeDir()}",
'LDFLAGS' => "-L{$this->library->getLibDir()}", 'LDFLAGS' => "-L{$this->library->getLibDir()}",
]); ]);
} }

View File

@ -8,10 +8,10 @@ int main(int argc,char **argv){
zend_stream_init_filename(&file_handle,"embed.php"); zend_stream_init_filename(&file_handle,"embed.php");
if(php_execute_script(&file_handle) == FAILURE){ if(!php_execute_script(&file_handle)){
php_printf("Failed to execute PHP script.\n"); php_printf("Failed to execute PHP script.\n");
} }
PHP_EMBED_END_BLOCK() PHP_EMBED_END_BLOCK()
return 0; return 0;
} }

View File

@ -242,3 +242,14 @@ function get_pack_replace(): array
BUILD_ROOT_PATH => '@build_root_path@', BUILD_ROOT_PATH => '@build_root_path@',
]; ];
} }
/**
* Remove duplicate spaces from a string.
*
* @param string $string Input string that may contain unnecessary spaces (e.g., " -la -lb").
* @return string The trimmed string with only single spaces (e.g., "-la -lb").
*/
function clean_spaces(string $string): string
{
return trim(preg_replace('/\s+/', ' ', $string));
}

View File

@ -0,0 +1,19 @@
--- a/config.m4
+++ b/config.m4
@@ -426,6 +426,7 @@
AX_CHECK_COMPILE_FLAG(-Wlogical-op-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-op-parentheses")
AX_CHECK_COMPILE_FLAG(-Wloop-analysis, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wloop-analysis")
AX_CHECK_COMPILE_FLAG(-Wuninitialized, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wuninitialized")
+ AX_CHECK_COMPILE_FLAG(-Wno-date-time, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-date-time")
AX_CHECK_COMPILE_FLAG(-Wno-missing-field-initializers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-missing-field-initializers")
AX_CHECK_COMPILE_FLAG(-Wno-sign-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-sign-compare")
AX_CHECK_COMPILE_FLAG(-Wno-unused-const-variable, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-const-variable")
@@ -1307,7 +1308,7 @@
PHP_REQUIRE_CXX()
- CXXFLAGS="$CXXFLAGS -Wall -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations"
+ CXXFLAGS="$CXXFLAGS -Wall -Wno-date-time -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations"
if test "$SW_OS" = "CYGWIN" || test "$SW_OS" = "MINGW"; then
CXXFLAGS="$CXXFLAGS -std=gnu++14"

View File

@ -73,7 +73,7 @@ $with_libs = match (PHP_OS_FAMILY) {
// You can use `common`, `bulk`, `minimal` or `none`. // You can use `common`, `bulk`, `minimal` or `none`.
// note: combination is only available for *nix platform. Windows must use `none` combination // note: combination is only available for *nix platform. Windows must use `none` combination
$base_combination = match (PHP_OS_FAMILY) { $base_combination = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'minimal', 'Linux', 'Darwin' => 'none',
'Windows' => 'none', 'Windows' => 'none',
}; };
@ -157,6 +157,9 @@ if ($shared_extensions) {
case 'ubuntu-22.04-arm': case 'ubuntu-22.04-arm':
$shared_cmd = ' --build-shared=' . quote2($shared_extensions) . ' '; $shared_cmd = ' --build-shared=' . quote2($shared_extensions) . ' ';
break; break;
case 'ubuntu-24.04':
case 'ubuntu-24.04-arm':
break;
case 'macos-13': case 'macos-13':
case 'macos-14': case 'macos-14':
case 'macos-15': case 'macos-15':

View File

@ -43,16 +43,6 @@ class SystemUtilTest extends TestCase
$this->assertIsArray(SystemUtil::findHeader('elf.h')); $this->assertIsArray(SystemUtil::findHeader('elf.h'));
} }
public function testGetCrossCompilePrefix()
{
$this->assertIsString(SystemUtil::getCrossCompilePrefix('gcc', 'x86_64'));
}
public function testGetCCType()
{
$this->assertEquals('gcc', SystemUtil::getCCType('xjfoiewjfoewof-gcc'));
}
public function testGetSupportedDistros() public function testGetSupportedDistros()
{ {
$this->assertIsArray(SystemUtil::getSupportedDistros()); $this->assertIsArray(SystemUtil::getSupportedDistros());

View File

@ -23,6 +23,10 @@ final class GlobalEnvManagerTest extends TestCase
'SPC_TARGET' => getenv('SPC_TARGET'), 'SPC_TARGET' => getenv('SPC_TARGET'),
'SPC_LIBC' => getenv('SPC_LIBC'), 'SPC_LIBC' => getenv('SPC_LIBC'),
]; ];
// Temporarily set private GlobalEnvManager::$initialized to false (use reflection)
$reflection = new \ReflectionClass(GlobalEnvManager::class);
$property = $reflection->getProperty('initialized');
$property->setValue(null, false);
} }
protected function tearDown(): void protected function tearDown(): void
@ -35,6 +39,10 @@ final class GlobalEnvManagerTest extends TestCase
putenv("{$key}={$value}"); putenv("{$key}={$value}");
} }
} }
// Temporarily set private GlobalEnvManager::$initialized to false (use reflection)
$reflection = new \ReflectionClass(GlobalEnvManager::class);
$property = $reflection->getProperty('initialized');
$property->setValue(null, true);
} }
public function testGetInitializedEnv(): void public function testGetInitializedEnv(): void

View File

@ -57,18 +57,18 @@ class SPCConfigUtilTest extends TestCase
$result = (new SPCConfigUtil())->config(['rar']); $result = (new SPCConfigUtil())->config(['rar']);
$this->assertStringContainsString(PHP_OS_FAMILY === 'Darwin' ? '-lc++' : '-lstdc++', $result['libs']); $this->assertStringContainsString(PHP_OS_FAMILY === 'Darwin' ? '-lc++' : '-lstdc++', $result['libs']);
// has mimalloc.o in lib dir // has libmimalloc.a in lib dir
// backup first // backup first
if (file_exists(BUILD_LIB_PATH . '/mimalloc.o')) { if (file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
$bak = file_get_contents(BUILD_LIB_PATH . '/mimalloc.o'); $bak = file_get_contents(BUILD_LIB_PATH . '/libmimalloc.a');
@unlink(BUILD_LIB_PATH . '/mimalloc.o'); @unlink(BUILD_LIB_PATH . '/libmimalloc.a');
} }
file_put_contents(BUILD_LIB_PATH . '/mimalloc.o', ''); file_put_contents(BUILD_LIB_PATH . '/libmimalloc.a', '');
$result = (new SPCConfigUtil())->config(['bcmath'], ['mimalloc']); $result = (new SPCConfigUtil())->config(['bcmath'], ['mimalloc']);
$this->assertStringStartsWith(BUILD_LIB_PATH . '/mimalloc.o', $result['libs']); $this->assertStringStartsWith(BUILD_LIB_PATH . '/libmimalloc.a', $result['libs']);
@unlink(BUILD_LIB_PATH . '/mimalloc.o'); @unlink(BUILD_LIB_PATH . '/libmimalloc.a');
if (isset($bak)) { if (isset($bak)) {
file_put_contents(BUILD_LIB_PATH . '/mimalloc.o', $bak); file_put_contents(BUILD_LIB_PATH . '/libmimalloc.a', $bak);
} }
} }
} }

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace SPC\Tests\util; namespace SPC\Tests\util;
use SPC\exception\WrongUsageException;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
/** /**
@ -35,17 +34,6 @@ final class SPCTargetTest extends TestBase
} }
} }
/**
* @dataProvider libcProvider
*/
public function testIsStatic(string $libc, bool $expected): void
{
putenv("SPC_LIBC={$libc}");
$result = SPCTarget::isStatic();
$this->assertEquals($expected, $result);
}
/** /**
* @dataProvider libcProvider * @dataProvider libcProvider
*/ */
@ -85,19 +73,6 @@ final class SPCTargetTest extends TestBase
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
/**
* @dataProvider invalidTargetProvider
*/
public function testGetTargetOSWithInvalidTarget(string $target): void
{
putenv("SPC_TARGET={$target}");
$this->expectException(WrongUsageException::class);
$this->expectExceptionMessage('Cannot parse target.');
SPCTarget::getTargetOS();
}
public function testLibcListConstant(): void public function testLibcListConstant(): void
{ {
$this->assertIsArray(SPCTarget::LIBC_LIST); $this->assertIsArray(SPCTarget::LIBC_LIST);
@ -117,22 +92,13 @@ final class SPCTargetTest extends TestBase
public function targetOSProvider(): array public function targetOSProvider(): array
{ {
return [ return [
'linux-target' => ['linux-x86_64', 'Linux'], 'linux-target' => ['native-linux', 'Linux'],
'macos-target' => ['macos-x86_64', 'Darwin'], 'macos-target' => ['native-macos', 'Darwin'],
'windows-target' => ['windows-x86_64', 'Windows'], 'windows-target' => ['native-windows', 'Windows'],
'empty-target' => ['', PHP_OS_FAMILY], 'empty-target' => ['', PHP_OS_FAMILY],
]; ];
} }
public function invalidTargetProvider(): array
{
return [
'invalid-target' => ['invalid-target'],
'unknown-target' => ['unknown-target'],
'mixed-target' => ['mixed-target'],
];
}
private function assertIsStringOrNull($value): void private function assertIsStringOrNull($value): void
{ {
$this->assertTrue(is_string($value) || is_null($value), 'Value must be string or null'); $this->assertTrue(is_string($value) || is_null($value), 'Value must be string or null');