Compare commits

..

56 Commits

Author SHA1 Message Date
DubbleClick
76d7002646 needed --libs --static 2025-07-21 09:33:29 +07:00
DubbleClick
38cc4cfe7e doesn't even work yet, there has to be a better way than to add 2913912309 libraries 2025-07-21 00:32:43 +07:00
Jerry Ma
f3400b8756 Merge pull request #822 from yoramdelangen/fix/workflow-reuse-upload-license-conflicts
fix: conflict upload names when reusing workflow more then once
2025-07-19 18:37:08 +08:00
Marc
e4921087d9 Merge pull request #823 from crazywhalecc/fix/curl-patch
Fix curl build for non-windows
2025-07-18 12:37:38 +07:00
crazywhalecc
005b78413c Fix curl build for non-windows 2025-07-18 13:23:46 +08:00
Yoram
0a1677af95 add build-meta to use php-version and os for artifacts upload name 2025-07-17 10:19:54 +02:00
Yoram
7ffbb10587 fix: conflict upload names when reusing workflow more then once 2025-07-17 09:48:22 +02:00
Jerry Ma
3d45890a2d Merge pull request #821 from crazywhalecc/fix/windows-curl-sspi
Fix/windows curl sspi
2025-07-17 13:58:31 +08:00
Jerry Ma
bb5eb64b28 Merge pull request #802 from crazywhalecc/feat/safe-pack
Check lib-suggest before packing libs
2025-07-17 13:34:15 +08:00
crazywhalecc
e6a050de82 Use SPC_EXTRA_LIBS instead of SPC_WINDOWS_EXEC_LIBS 2025-07-17 13:14:18 +08:00
crazywhalecc
7e7eb844a7 cs-fix 2025-07-17 12:26:30 +08:00
crazywhalecc
e56c628bec Add windows tests 2025-07-17 12:23:03 +08:00
crazywhalecc
c604f88685 Disallow alt for php-src 2025-07-17 12:21:50 +08:00
crazywhalecc
83db186972 Fix curl static build for Windows 2025-07-17 12:21:13 +08:00
Jerry Ma
9af3b745a2 Merge pull request #792 from crazywhalecc/fix/ext-imap-extraction
Skip re-extract ext-imap source
2025-07-10 21:39:34 +08:00
crazywhalecc
a5cd5fcb31 Fix test and bump docker image version 2025-07-10 20:20:29 +08:00
crazywhalecc
ec153fa6ff Workaround for swoole with alpine 3.21 2025-07-10 20:10:54 +08:00
crazywhalecc
dd72b32559 Merge 2025-07-10 12:59:27 +08:00
Marc
a5d26d6517 Merge pull request #813 from crazywhalecc/fix-native
fix g++ not found error
2025-07-01 22:12:09 +07:00
DubbleClick
e5ea32e9c0 fix in case it's not set 2025-07-01 17:57:36 +07:00
DubbleClick
5bc4504e37 fix g++ not found error 2025-07-01 17:53:35 +07:00
Jerry Ma
4cb4c9c6a5 Merge pull request #812 from crazywhalecc/sponsor-badge
Add special sponsors
2025-06-30 22:09:36 +08:00
Jerry Ma
9190335a70 Merge pull request #810 from crazywhalecc/refactor/spc-target
refactor: replace SPC_LIBC with SPC_TARGET and update related logic
2025-06-30 21:49:01 +08:00
crazywhalecc
4e244789a2 Add frankenphp SAPI 2025-06-30 21:45:45 +08:00
crazywhalecc
6de7b8e63b Add special sponsors 2025-06-30 21:41:48 +08:00
Marc
68e500eea0 Merge branch 'main' into refactor/spc-target 2025-06-30 20:16:59 +07:00
Marc
840e09a684 Merge pull request #811 from crazywhalecc/fix/micro-patches
Fix/micro patches
2025-06-30 20:16:19 +07:00
crazywhalecc
d00a5223d3 Add exception for checking patch file exist 2025-06-30 19:45:26 +08:00
crazywhalecc
3965a899c7 Add missing directory separator 2025-06-30 19:41:32 +08:00
DubbleClick
956667bf08 fix alpine doctor saying gettext-dev isn't installed 2025-06-30 13:40:51 +07:00
DubbleClick
51e23d21cd remove w32 patches (other branch isn't merged yet) 2025-06-30 12:56:12 +07:00
crazywhalecc
44c6d6288e Refactor LinuxMuslCheck to use MuslToolchain class for toolchain check 2025-06-30 13:04:49 +08:00
Jerry Ma
e5848086c3 Update bin/spc-gnu-docker
Co-authored-by: Marc <m@pyc.ac>
2025-06-30 12:19:17 +08:00
Jerry Ma
138e5588e9 Update bin/spc-gnu-docker
Co-authored-by: Marc <m@pyc.ac>
2025-06-30 12:18:49 +08:00
crazywhalecc
ab5828a560 Remove redundant path in gnu docker 2025-06-30 09:05:56 +08:00
crazywhalecc
e1e48922de Use isStatic instead of isStaticTarget 2025-06-29 22:49:48 +08:00
crazywhalecc
956688d9f4 Fix CI again 2025-06-29 19:53:22 +08:00
crazywhalecc
7f45f4aeec Fix phpunit, add SPC_SKIP_TOOLCHAIN_CHECK 2025-06-29 19:48:09 +08:00
crazywhalecc
fcaa7c5f42 Add no-env-check for builder 2025-06-29 16:21:22 +08:00
crazywhalecc
e36800b951 phpunit fix 2025-06-29 16:03:38 +08:00
crazywhalecc
977fbaa8ef Suggestions 2025-06-29 16:00:17 +08:00
crazywhalecc
0598eff9c5 Add suffix support for SPC_TARGET 2025-06-28 23:13:40 +08:00
crazywhalecc
a6364389ba Add suffix support for SPC_TARGET 2025-06-28 23:11:26 +08:00
crazywhalecc
c23c5ae614 Disable openmp for imagemagick 2025-06-28 23:08:17 +08:00
crazywhalecc
454b5a77ad Add SPC_LIBC mapping to SPC_TARGET 2025-06-28 22:59:02 +08:00
crazywhalecc
8145a7536b Fix spc-gnu-docker path 2025-06-28 19:05:52 +08:00
crazywhalecc
e41d7899c7 Add comments, re-trigger tests 2025-06-28 18:14:10 +08:00
Jerry Ma
88d99a7dea Update src/SPC/store/SourcePatcher.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-28 18:04:45 +08:00
crazywhalecc
90823e3b17 Use explict glibc toolchain target 2025-06-28 17:28:02 +08:00
crazywhalecc
3357f286ab Remove libgomp deps 2025-06-28 17:13:22 +08:00
crazywhalecc
ad080da026 Refactor micro patching logic in SourcePatcher 2025-06-28 17:06:51 +08:00
crazywhalecc
25c2def710 Remove unnecessary SPC_MICRO_PATCHES 2025-06-28 16:47:19 +08:00
crazywhalecc
604131b31d Use own exception 2025-06-28 16:45:20 +08:00
crazywhalecc
924da6ae69 extension test 2025-06-28 16:39:02 +08:00
crazywhalecc
12aadf18cc refactor: replace SPC_LIBC with SPC_TARGET and update related logic 2025-06-28 16:36:05 +08:00
crazywhalecc
1409d1b47b Check lib-suggest before packing libs 2025-06-22 13:52:53 +08:00
66 changed files with 694 additions and 195 deletions

View File

@@ -230,12 +230,12 @@ jobs:
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: "Upload License Files" name: "Upload License Files"
with: with:
name: license-files name: license-files-${{ inputs.php-version }}-${{ inputs.os }}
path: buildroot/license/ path: buildroot/license/
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: "Upload Build Metadata" name: "Upload Build Metadata"
with: with:
name: build-meta name: build-meta-${{ inputs.php-version }}-${{ inputs.os }}
path: | path: |
buildroot/build-extensions.json buildroot/build-extensions.json
buildroot/build-libraries.json buildroot/build-libraries.json

View File

@@ -9,7 +9,7 @@
**static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。 **static-php-cli**是一个用于静态编译、构建 PHP 解释器的工具,支持众多流行扩展。
目前 static-php-cli 支持 `cli``fpm``embed``micro` SAPI。 目前 static-php-cli 支持 `cli``fpm``embed``micro``frankenphp` SAPI。
**static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。 **static-php-cli**也支持将 PHP 代码和 PHP 运行时打包为一个文件并运行。
@@ -292,6 +292,12 @@ bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=sys
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。 你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。你捐赠的一部分将会被用于维护 **static-php.dev** 服务器。
**特别赞助商**:
<a href="https://beyondco.de/"><img src="/docs/public/images/beyondcode-seeklogo.png" width="300" alt="Beyond Code Logo" /></a>
<a href="https://nativephp.com/"><img src="/docs/public/images/nativephp-logo.svg" width="300" alt="NativePHP Logo" /></a>
## 开源协议 ## 开源协议
本项目采用 MIT License 许可开源,下面是类似的项目: 本项目采用 MIT License 许可开源,下面是类似的项目:

View File

@@ -10,7 +10,7 @@
**static-php-cli** is a powerful tool designed for building static, standalone PHP runtime **static-php-cli** is a powerful tool designed for building static, standalone PHP runtime
with popular extensions. with popular extensions.
Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed` and `micro` SAPI. Static PHP built by **static-php-cli** supports `cli`, `fpm`, `embed`, `micro` and `frankenphp` SAPI.
**static-php-cli** also has the ability to package PHP projects **static-php-cli** also has the ability to package PHP projects
along with the PHP interpreter into one single executable file. along with the PHP interpreter into one single executable file.
@@ -317,6 +317,12 @@ Now there is a [static-php](https://github.com/static-php) organization, which i
You can sponsor me or my project from [GitHub Sponsor](https://github.com/crazywhalecc). A portion of your donation will be used to maintain the **static-php.dev** server. You can sponsor me or my project from [GitHub Sponsor](https://github.com/crazywhalecc). A portion of your donation will be used to maintain the **static-php.dev** server.
**Special thanks to sponsors below**:
<a href="https://beyondco.de/"><img src="/docs/public/images/beyondcode-seeklogo.png" width="300" alt="Beyond Code Logo" /></a>
<a href="https://nativephp.com/"><img src="/docs/public/images/nativephp-logo.svg" width="300" alt="NativePHP Logo" /></a>
## Open-Source License ## Open-Source License
This project itself is based on MIT License, This project itself is based on MIT License,

View File

@@ -3,7 +3,7 @@
set -e set -e
# This file is using docker to run commands # This file is using docker to run commands
SPC_DOCKER_VERSION=v4 SPC_DOCKER_VERSION=v5
# Detect docker can run # Detect docker can run
if ! which docker >/dev/null; then if ! which docker >/dev/null; then
@@ -53,9 +53,9 @@ aarch64|arm64)
exit 1 exit 1
;; ;;
esac esac
# if ALPINE_FROM is not set, use alpine:edge # if ALPINE_FROM is not set, use alpine:3.21
if [ -z "$ALPINE_FROM" ]; then if [ -z "$ALPINE_FROM" ]; then
ALPINE_FROM=alpine:edge ALPINE_FROM=alpine:3.21
fi fi
if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then
echo "* Using different arch needs to setup qemu-static for docker !" echo "* Using different arch needs to setup qemu-static for docker !"
@@ -64,7 +64,7 @@ if [ "$SPC_USE_ARCH" != "$CURRENT_ARCH" ]; then
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null $DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
fi fi
else else
ALPINE_FROM=alpine:edge ALPINE_FROM=alpine:3.21
fi fi
if [ "$SPC_USE_MIRROR" = "yes" ]; then if [ "$SPC_USE_MIRROR" = "yes" ]; then

View File

@@ -64,7 +64,7 @@ fi
# Detect docker env is setup # Detect docker env is setup
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION; then
echo "Docker container does not exist. Building docker image ..." echo "Docker container does not exist. Building docker image ..."
$DOCKER_EXECUTABLE buildx build $PLATFORM_ARG --no-cache -t cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF $DOCKER_EXECUTABLE buildx build $PLATFORM_ARG -t cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION -f- . <<EOF
FROM centos:7 FROM centos:7
RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \ RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \
sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \ sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
@@ -96,7 +96,7 @@ RUN curl -fsSL -o patchelf.tgz https://github.com/NixOS/patchelf/releases/downlo
tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \ tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \
cp /patchelf/bin/patchelf /usr/bin/ cp /patchelf/bin/patchelf /usr/bin/
RUN curl -o cmake.tgz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \ RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \
mkdir /cmake && \ mkdir /cmake && \
tar -xzf cmake.tgz -C /cmake --strip-components 1 tar -xzf cmake.tgz -C /cmake --strip-components 1
@@ -107,8 +107,8 @@ ADD ./bin/setup-runtime /app/bin/setup-runtime
ADD ./bin/spc /app/bin/spc ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev RUN /app/bin/php /app/bin/composer install --no-dev
ENV PATH="/app/bin:/cmake/bin:$PATH"
ENV SPC_LIBC=glibc ENV SPC_LIBC=glibc
ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH"
ADD ./config/env.ini /app/config/env.ini ADD ./config/env.ini /app/config/env.ini
RUN CC=gcc bin/spc doctor --auto-fix --debug RUN CC=gcc bin/spc doctor --auto-fix --debug
@@ -128,7 +128,6 @@ RUN curl -o automake.tgz -fsSL https://ftp.gnu.org/gnu/automake/automake-1.17.ta
make && \ make && \
make install && \ make install && \
ln -sf /usr/local/bin/automake /usr/bin/automake ln -sf /usr/local/bin/automake /usr/bin/automake
EOF EOF
fi fi
@@ -154,11 +153,7 @@ if [ -f "$(pwd)/craft.yml" ]; then
fi fi
# Apply env in temp env file # Apply env in temp env file
echo 'CC=/opt/rh/devtoolset-10/root/usr/bin/gcc' > /tmp/spc-gnu-docker.env echo 'SPC_DEFAULT_C_FLAGS=-fPIC' > /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=-fPIC' >> /tmp/spc-gnu-docker.env
echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env echo 'SPC_LIBC=glibc' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"' >> /tmp/spc-gnu-docker.env
echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"' >> /tmp/spc-gnu-docker.env

View File

@@ -55,14 +55,29 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd
; EXTENSION_DIR= ; EXTENSION_DIR=
[windows] [windows]
; build target: win7-static
SPC_TARGET=native-windows
; php-sdk-binary-tools path ; php-sdk-binary-tools path
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path ; upx executable path
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:
; - musl (default): used for general linux distros, can build `musl-static` target only.
; - zig (WIP): used for general linux distros, can build `musl` and `glibc` targets.
; - musl-native: used for alpine linux, can build `musl-static` and `musl`(WIP) target.
; - gnu-native (assume): used for general linux distros, can build `glibc` target only and have portability issues.
; build 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. ; include PATH for musl libc.
SPC_LIBC=musl SPC_LIBC=musl
; compiler environments ; compiler environments
@@ -78,7 +93,7 @@ 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
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream SPC_MICRO_PATCHES=cli_checks,disable_huge_page
; *** default build command for building php *** ; *** default build command for building php ***
; buildconf command ; buildconf command
@@ -109,6 +124,9 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie" SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie"
[macos] [macos]
; build target: macho or macho (possibly we could support macho-universal in the future)
; Currently we do not support universal and cross-compilation for macOS.
SPC_TARGET=native-macos
; compiler environments ; compiler environments
CC=clang CC=clang
CXX=clang++ CXX=clang++
@@ -118,7 +136,7 @@ SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
SPC_EXTRA_LIBS= SPC_EXTRA_LIBS=
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv SPC_MICRO_PATCHES=cli_checks,macos_iconv
; *** default build command for building php *** ; *** default build command for building php ***
; buildconf command ; buildconf command

View File

@@ -186,12 +186,12 @@
"grpc": { "grpc": {
"source": "grpc", "source": "grpc",
"static-libs-unix": [ "static-libs-unix": [
"libgrpc.a", "libgrpc.a"
"libcares.a"
], ],
"lib-depends": [ "lib-depends": [
"zlib", "zlib",
"openssl" "openssl",
"libcares"
], ],
"frameworks": [ "frameworks": [
"CoreFoundation" "CoreFoundation"

View File

@@ -4,7 +4,8 @@
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} },
"alt": false
}, },
"amqp": { "amqp": {
"type": "url", "type": "url",

View File

@@ -47,6 +47,10 @@ export default {
nav: [], nav: [],
socialLinks: [ socialLinks: [
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'} {icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}
] ],
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2023-present crazywhalecc'
}
} }
} }

View File

@@ -2,6 +2,7 @@
import DefaultTheme from 'vitepress/theme' import DefaultTheme from 'vitepress/theme'
import {inBrowser, useData} from "vitepress"; import {inBrowser, useData} from "vitepress";
import {watchEffect} from "vue"; import {watchEffect} from "vue";
import './style.css';
export default { export default {
...DefaultTheme, ...DefaultTheme,
@@ -13,4 +14,4 @@ export default {
} }
}) })
} }
} }

View File

@@ -0,0 +1,6 @@
/** override default styles */
.vp-sponsor-grid-image {
max-height:36px !important;
max-width: 1000px !important;
}

View File

@@ -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. On Linux, you need to use static-php-cli with SPC_LIBC=glibc. 1. Xdebug is only buildable as a shared extension. You need to use a build target other than `musl-static` for SPC_TARGET.
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`.

View File

@@ -21,3 +21,16 @@ features:
- title: Dependency Management - title: Dependency Management
details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions. details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions.
--- ---
<script setup>
import {VPSponsors} from "vitepress/theme";
const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
];
</script>
## Special Sponsors
<VPSponsors :data="sponsors"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 764 100">
<path fill="#505b93" d="M29.5,98.4H0V1.2h29.5V98.4z"/>
<path fill="#00aaa6" d="M96.7,98.4H67.2V1.2h29.5V98.4z"/>
<path fill="#272d48" d="M96.7,98.4H29.5V1.2L96.7,98.4z"/>
<path fill="#272d48" d="M102.6,54.5c0-6.2,1.3-11.8,4.1-16.7c2.7-5,6.7-8.8,11.9-11.6c5.2-2.8,11.4-4.2,18.6-4.2c1.1,0,2.8,0.1,5.2,0.3 c4.9,0.4,9.1,1.3,12.5,2.8c3.4,1.5,6.4,3.4,9.1,5.9v-7.2h25.8v74.7h-25.8v-7.2c-2.5,2.6-5.6,4.6-9.4,6.1c-3.8,1.5-7.9,2.4-12.1,2.6 c-0.9,0.1-2.3,0.1-4.1,0.1c-7.2,0-13.4-1.4-18.8-4.1c-5.4-2.7-9.5-6.6-12.4-11.5c-2.9-4.9-4.3-10.5-4.3-16.6V54.5z M128.4,63.9 c0,9.2,5.9,13.8,17.7,13.8c11.8,0,17.7-4.6,17.7-13.8v-5.6c0-9.3-5.9-14-17.7-14c-11.8,0-17.7,4.7-17.7,14V63.9z M208.3,98.4V47h-12.8V23.7l12.8-0.2V1.2h25.9l0.1,22.4h18.3V47h-18.4v51.4 M287.3,98.4h-25.9V23.6h25.9V98.4z M351,98.4H325l-31-74.8H320L338,68l18-44.5h25.9L351,98.4z M454.9,87.6c-4.1,4.2-8.8,7.3-14.1,9.3c-5.3,2-11.8,2.9-19.5,2.9c-8,0-15.1-1.4-21.3-4.2 c-6.2-2.8-11.1-6.7-14.7-11.6c-3.5-5-5.3-10.6-5.3-17V54.6c0-6.5,1.7-12.2,5.2-17.1c3.4-4.9,8.2-8.7,14.2-11.3c6-2.7,12.7-4,20-4 c8,0,15,1.5,21.2,4.3c6.1,2.9,10.9,7,14.4,12.4c3.4,5.4,5.2,11.6,5.2,18.7c0,1.7-0.2,4.2-0.7,7.7l-54.2,0.1v2.1 c0,4.1,1.5,7.3,4.4,9.4c2.9,2.2,6.9,3.2,11.8,3.2c4.6,0,8.5-0.6,11.8-1.8c3.2-1.2,6.4-3.5,9.4-6.8L454.9,87.6z M434.4,51.4 c0-3.1-1.4-5.5-4.2-7.1c-2.8-1.6-6.4-2.4-10.8-2.4s-7.9,0.8-10.5,2.4c-2.6,1.6-3.8,4-3.8,7.1H434.4z M466,98.4V1.2h56c9.9,0,17.8,1.9,23.7,5.6c5.9,3.7,10,8.2,12.2,13.3c2.3,5.1,3.4,10.1,3.4,15 c0,4.8-1.2,9.7-3.5,14.7c-2.3,5-6.4,9.2-12.2,12.8c-5.8,3.6-13.7,5.4-23.7,5.4h-30.2v30.5H466z M522,44.6c9,0,13.6-3.3,13.6-9.9 c0-6.9-4.6-10.3-13.7-10.3h-30.1v20.2H522z M593,98.4h-25.8V1.2H593v38h43.9v-38h25.8v97.2h-25.8V59.9H593V98.4z M668.6,98.4V1.2h56c9.9,0,17.8,1.9,23.7,5.6c5.9,3.7,10,8.2,12.2,13.3c2.3,5.1,3.4,10.1,3.4,15 c0,4.8-1.2,9.7-3.5,14.7c-2.3,5-6.4,9.2-12.2,12.8c-5.8,3.6-13.7,5.4-23.7,5.4h-30.2v30.5H668.6z M724.6,44.6c9,0,13.6-3.3,13.6-9.9 c0-6.9-4.6-10.3-13.7-10.3h-30.1v20.2H724.6z M291.1,20.2h-33.5V0h33.5V20.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -76,7 +76,7 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli
## xdebug ## xdebug
1. Xdebug 只能作为共享扩展进行构建。在 Linux 上,您需要使用 static-php-cli 并设置 SPC_LIBC=glibc 1. Xdebug 只能作为共享扩展进行构建。您需要使用除了 `musl-static` 外的其他 `SPC_TARGET` 构建目标
2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。 2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php` 编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`

View File

@@ -4,5 +4,6 @@
> >
<php> <php>
<env name="SPC_IGNORE_BAD_HASH" value="yes" force="true" /> <env name="SPC_IGNORE_BAD_HASH" value="yes" force="true" />
<env name="SPC_SKIP_TOOLCHAIN_CHECK" value="yes" force="true" />
</php> </php>
</phpunit> </phpunit>

View File

@@ -9,6 +9,7 @@ use SPC\command\BuildPHPCommand;
use SPC\command\CraftCommand; use SPC\command\CraftCommand;
use SPC\command\DeleteDownloadCommand; use SPC\command\DeleteDownloadCommand;
use SPC\command\dev\AllExtCommand; use SPC\command\dev\AllExtCommand;
use SPC\command\dev\EnvCommand;
use SPC\command\dev\ExtVerCommand; use SPC\command\dev\ExtVerCommand;
use SPC\command\dev\GenerateExtDepDocsCommand; use SPC\command\dev\GenerateExtDepDocsCommand;
use SPC\command\dev\GenerateExtDocCommand; use SPC\command\dev\GenerateExtDocCommand;
@@ -70,6 +71,7 @@ final class ConsoleApplication extends Application
new GenerateExtDepDocsCommand(), new GenerateExtDepDocsCommand(),
new GenerateLibDepDocsCommand(), new GenerateLibDepDocsCommand(),
new PackLibCommand(), new PackLibCommand(),
new EnvCommand(),
] ]
); );
} }

View File

@@ -14,6 +14,7 @@ use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\store\SourceManager; use SPC\store\SourceManager;
use SPC\store\SourcePatcher;
use SPC\util\CustomExt; use SPC\util\CustomExt;
abstract class BuilderBase abstract class BuilderBase
@@ -203,6 +204,8 @@ abstract class BuilderBase
$this->emitPatchPoint('before-exts-extract'); $this->emitPatchPoint('before-exts-extract');
SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]); SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]);
$this->emitPatchPoint('after-exts-extract'); $this->emitPatchPoint('after-exts-extract');
// patch micro
SourcePatcher::patchMicro();
} }
foreach ([...$static_extensions, ...$shared_extensions] as $extension) { foreach ([...$static_extensions, ...$shared_extensions] as $extension) {

View File

@@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
class Extension class Extension
{ {
@@ -532,6 +533,11 @@ class Extension
$sharedLibString .= '-l' . $lib . ' '; $sharedLibString .= '-l' . $lib . ' ';
} }
} }
// move static libstdc++ to shared if we are on non-full-static build target
if (!SPCTarget::isStatic() && in_array(SPCTarget::getLibc(), SPCTarget::LIBC_LIST)) {
$staticLibString .= ' -lstdc++';
$sharedLibString = str_replace('-lstdc++', '', $sharedLibString);
}
return [trim($staticLibString), trim($sharedLibString)]; return [trim($staticLibString), trim($sharedLibString)];
} }

View File

@@ -332,6 +332,15 @@ abstract class LibraryBase
return false; return false;
} }
/**
* Patch php-config after embed was built
* Example: imap requires -lcrypt
*/
public function patchPhpConfig(): bool
{
return false;
}
/** /**
* Build this library. * Build this library.
* *

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\builder\linux\LinuxBuilder; use SPC\builder\linux\LinuxBuilder;
use SPC\builder\macos\MacOSBuilder; use SPC\builder\macos\MacOSBuilder;
use SPC\builder\windows\WindowsBuilder;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
@@ -57,6 +58,17 @@ class curl extends Extension
return true; return true;
} }
public function patchBeforeMake(): bool
{
$extra_libs = getenv('SPC_EXTRA_LIBS');
if ($this->builder instanceof WindowsBuilder && !str_contains($extra_libs, 'secur32.lib')) {
$extra_libs .= ' secur32.lib';
putenv('SPC_EXTRA_LIBS=' . trim($extra_libs));
return true;
}
return false;
}
public function patchBeforeSharedConfigure(): bool public function patchBeforeSharedConfigure(): bool
{ {
$file = $this->source_dir . '/config.m4'; $file = $this->source_dir . '/config.m4';

View File

@@ -36,9 +36,20 @@ class grpc extends Extension
} }
return false; return false;
} }
public function patchBeforeConfigure(): bool
{
$libs = ' -l' . join(' -l', $this->getLibraries());
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs);
return true;
}
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
$extra_libs = trim(getenv('SPC_EXTRA_LIBS'));
$alibs = join('.a ', $this->getLibraries()) . '.a';
$libs = BUILD_LIB_PATH . '/lib' . str_replace(' ', ' ' . BUILD_LIB_PATH . '/lib', $alibs);
$extra_libs = str_replace(BUILD_LIB_PATH . '/libgrpc.a', $libs, $extra_libs);
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
// 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;
@@ -48,4 +59,16 @@ class grpc extends Extension
{ {
return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH; return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH;
} }
private function getLibraries(): array
{
[, $out] = shell()->execWithResult('$PKG_CONFIG --libs --static grpc');
$libs = join(' ', $out) . ' -lupb -lupb_message_lib -lupb_json_lib -lupb_textformat_lib -lupb_mini_descriptor_lib -lupb_wire_lib -lupb_mem_lib -lupb_base_lib -lutf8_range';
$filtered = str_replace('-pthread', '', $libs);
$filtered = preg_replace('/-L\S+/', '', $filtered);
$filtered = preg_replace('/(?:\S*\/)?lib([a-zA-Z0-9_+-]+)\.a\b/', '-l$1', $filtered);
$out = str_replace('-l', '', $filtered);
$out = preg_replace('/\s+/', ' ', $out);
return explode(' ', trim($out));
}
} }

View File

@@ -10,34 +10,9 @@ use SPC\util\CustomExt;
#[CustomExt('imagick')] #[CustomExt('imagick')]
class imagick extends Extension class imagick extends Extension
{ {
public function patchBeforeMake(): bool
{
if (PHP_OS_FAMILY !== 'Linux') {
return false;
}
if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) {
return false;
}
// imagick with calls omp_pause_all, which requires openmp, on non-musl we build imagick without openmp
$extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lgomp');
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
return true;
}
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$disable_omp = !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) ? '' : ' 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 getStaticAndSharedLibs(): array
{
// on centos 7, it will use the symbol _ZTINSt6thread6_StateE, which is not defined in system libstdc++.so.6
[$static, $shared] = parent::getStaticAndSharedLibs();
if (getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10')) {
$static .= ' -lstdc++';
$shared = str_replace('-lstdc++', '', $shared);
}
return [$static, $shared];
}
} }

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@@ -41,4 +42,14 @@ class imap extends Extension
} }
return $arg; return $arg;
} }
public function patchBeforeMake(): bool
{
if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::isMuslDist()) {
return false;
}
$extra_libs = trim(getenv('SPC_EXTRA_LIBS') . ' -lcrypt');
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
return true;
}
} }

View File

@@ -25,22 +25,8 @@ class LinuxBuilder extends UnixBuilderBase
{ {
$this->options = $options; $this->options = $options;
// check musl-cross make installed if we use musl-cross-make
$arch = arch2gnu(php_uname('m'));
GlobalEnvManager::init(); GlobalEnvManager::init();
GlobalEnvManager::afterInit();
if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) {
$this->setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
$this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure;
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$configure}");
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {
throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
}
}
// concurrency // concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY')); $this->concurrency = intval(getenv('SPC_CONCURRENCY'));

View File

@@ -188,12 +188,12 @@ class SystemUtil
/** /**
* Get libc version string from ldd * Get libc version string from ldd
*/ */
public static function getLibcVersionIfExists(): ?string public static function getLibcVersionIfExists(string $libc): ?string
{ {
if (self::$libc_version !== null) { if (self::$libc_version !== null) {
return self::$libc_version; return self::$libc_version;
} }
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') { if ($libc === 'glibc') {
$result = shell()->execWithResult('ldd --version', false); $result = shell()->execWithResult('ldd --version', false);
if ($result[0] !== 0) { if ($result[0] !== 0) {
return null; return null;
@@ -208,7 +208,7 @@ class SystemUtil
} }
return null; return null;
} }
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_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 { } else {

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; namespace SPC\builder\linux\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCTarget;
class icu extends LinuxLibraryBase class icu extends LinuxLibraryBase
{ {
@@ -16,7 +17,7 @@ class icu extends LinuxLibraryBase
{ {
$cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"'; $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"';
$cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"'; $cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"';
$ldflags = getenv('SPC_LIBC') !== 'glibc' ? 'LDFLAGS="-static"' : ''; $ldflags = SPCTarget::isStatic() ? 'LDFLAGS="-static"' : '';
shell()->cd($this->source_dir . '/source')->initializeEnv($this) shell()->cd($this->source_dir . '/source')->initializeEnv($this)
->exec( ->exec(
"{$cppflags} {$cxxflags} {$ldflags} " . "{$cppflags} {$cxxflags} {$ldflags} " .

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\linux\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCTarget;
class imap extends LinuxLibraryBase class imap extends LinuxLibraryBase
{ {
@@ -34,6 +35,15 @@ class imap extends LinuxLibraryBase
return true; return true;
} }
public function patchPhpConfig(): bool
{
if (SPCTarget::getLibc() === 'glibc') {
FileSystem::replaceFileRegex(BUILD_BIN_PATH . '/php-config', '/^libs="(.*)"$/m', 'libs="$1 -lcrypt"');
return true;
}
return false;
}
/** /**
* @throws RuntimeException * @throws RuntimeException
*/ */
@@ -44,6 +54,8 @@ class imap extends LinuxLibraryBase
} else { } else {
$ssl_options = 'SSLTYPE=none'; $ssl_options = 'SSLTYPE=none';
} }
$libcVer = SPCTarget::getLibcVersion();
$extraLibs = $libcVer && version_compare($libcVer, '2.17', '<=') ? 'EXTRALDFLAGS="-ldl -lrt -lpthread"' : '';
shell()->cd($this->source_dir) shell()->cd($this->source_dir)
->exec('make clean') ->exec('make clean')
->exec('touch ip6') ->exec('touch ip6')
@@ -51,9 +63,7 @@ class imap extends LinuxLibraryBase
->exec('chmod +x tools/ua') ->exec('chmod +x tools/ua')
->exec('chmod +x src/osdep/unix/drivers') ->exec('chmod +x src/osdep/unix/drivers')
->exec('chmod +x src/osdep/unix/mkauths') ->exec('chmod +x src/osdep/unix/mkauths')
->exec( ->exec("yes | make slx {$ssl_options} EXTRACFLAGS='-fPIC -fpermissive' {$extraLibs}");
"yes | make slx {$ssl_options} EXTRACFLAGS='-fPIC -fpermissive'"
);
try { try {
shell() shell()
->exec("cp -rf {$this->source_dir}/c-client/c-client.a " . BUILD_LIB_PATH . '/libc-client.a') ->exec("cp -rf {$this->source_dir}/c-client/c-client.a " . BUILD_LIB_PATH . '/libc-client.a')

View File

@@ -29,6 +29,7 @@ class MacOSBuilder extends UnixBuilderBase
// apply global environment variables // apply global environment variables
GlobalEnvManager::init(); GlobalEnvManager::init();
GlobalEnvManager::afterInit();
// ---------- set necessary compile vars ---------- // ---------- set necessary compile vars ----------
// concurrency // concurrency

View File

@@ -17,6 +17,7 @@ use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
abstract class UnixBuilderBase extends BuilderBase abstract class UnixBuilderBase extends BuilderBase
{ {
@@ -200,7 +201,7 @@ abstract class UnixBuilderBase extends BuilderBase
$util = new SPCConfigUtil($this); $util = new SPCConfigUtil($this);
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}"; $lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { if (SPCTarget::isStatic()) {
$lens .= ' -static'; $lens .= ' -static';
} }
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
@@ -293,6 +294,11 @@ abstract class UnixBuilderBase extends BuilderBase
$php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str); $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
} }
foreach ($this->getLibs() as $lib) {
if ($lib->patchPhpConfig()) {
logger()->debug("Library {$lib->getName()} patched php-config");
}
}
} }
/** /**
@@ -334,7 +340,7 @@ abstract class UnixBuilderBase extends BuilderBase
$debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : '';
$extLdFlags = "-extldflags '-pie'"; $extLdFlags = "-extldflags '-pie'";
$muslTags = ''; $muslTags = '';
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { 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,';
} }

View File

@@ -4,22 +4,35 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\store\FileSystem; use SPC\util\executor\UnixCMakeExecutor;
trait grpc trait grpc
{ {
protected function build(): void protected function build(): void
{ {
shell()->cd($this->source_dir) UnixCMakeExecutor::create($this)
->exec('EXTRA_DEFINES=GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK EMBED_OPENSSL=false CXXFLAGS="-L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '" make static -j' . $this->builder->concurrency); ->addConfigureArgs(
copy($this->source_dir . '/libs/opt/libgrpc.a', BUILD_LIB_PATH . '/libgrpc.a'); '-DgRPC_SSL_PROVIDER=openssl',
copy($this->source_dir . '/libs/opt/libboringssl.a', BUILD_LIB_PATH . '/libboringssl.a'); '-DgRPC_INSTALL_BINDIR=' . BUILD_BIN_PATH,
if (!file_exists(BUILD_LIB_PATH . '/libcares.a')) { '-DgRPC_INSTALL_LIBDIR=' . BUILD_LIB_PATH,
copy($this->source_dir . '/libs/opt/libcares.a', BUILD_LIB_PATH . '/libcares.a'); '-DgRPC_INSTALL_SHAREDIR=' . BUILD_ROOT_PATH . '/share/grpc',
} '-DOPENSSL_ROOT_DIR=' . BUILD_ROOT_PATH,
FileSystem::copyDir($this->source_dir . '/include/grpc', BUILD_INCLUDE_PATH . '/grpc'); '-DOPENSSL_INCLUDE_DIR=' . BUILD_INCLUDE_PATH,
FileSystem::copyDir($this->source_dir . '/include/grpc++', BUILD_INCLUDE_PATH . '/grpc++'); '-DCMAKE_CXX_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK"'
FileSystem::copyDir($this->source_dir . '/include/grpcpp', BUILD_INCLUDE_PATH . '/grpcpp'); )
FileSystem::copyDir($this->source_dir . '/src/php/ext/grpc', BUILD_ROOT_PATH . '/grpc_php_ext_src'); ->build();
copy($this->source_dir . '/third_party/re2/re2.pc', BUILD_LIB_PATH . '/pkgconfig/re2.pc');
//shell()->cd($this->source_dir)
// ->exec('EXTRA_DEFINES=GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK EMBED_OPENSSL=false CXXFLAGS="-L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '" make static -j' . $this->builder->concurrency);
//copy($this->source_dir . '/libs/opt/libgrpc.a', BUILD_LIB_PATH . '/libgrpc.a');
//copy($this->source_dir . '/libs/opt/libboringssl.a', BUILD_LIB_PATH . '/libboringssl.a');
//if (!file_exists(BUILD_LIB_PATH . '/libcares.a')) {
// copy($this->source_dir . '/libs/opt/libcares.a', BUILD_LIB_PATH . '/libcares.a');
//}
//FileSystem::copyDir($this->source_dir . '/include/grpc', BUILD_INCLUDE_PATH . '/grpc');
//FileSystem::copyDir($this->source_dir . '/include/grpc++', BUILD_INCLUDE_PATH . '/grpc++');
//FileSystem::copyDir($this->source_dir . '/include/grpcpp', BUILD_INCLUDE_PATH . '/grpcpp');
//FileSystem::copyDir($this->source_dir . '/src/php/ext/grpc', BUILD_ROOT_PATH . '/grpc_php_ext_src');
} }
} }

View File

@@ -4,18 +4,19 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait imagemagick trait imagemagick
{ {
/** /**
* @throws RuntimeException * @throws RuntimeException
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException
*/ */
protected function build(): void protected function build(): void
{ {
@@ -32,17 +33,16 @@ 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'))
->addConfigureArgs( ->addConfigureArgs(
// TODO: glibc rh 10 toolset's libgomp.a was built without -fPIC so we can't use openmp without depending on libgomp.so '--disable-openmp',
getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10') ? '--disable-openmp' : '--enable-openmp',
'--without-jxl', '--without-jxl',
'--without-x', '--without-x',
); );
// special: linux musl needs `-static` // special: linux-static target needs `-static`
$ldflags = ($this instanceof LinuxLibraryBase) && getenv('SPC_LIBC') !== 'glibc' ? ('-static -ldl') : '-ldl'; $ldflags = SPCTarget::isStatic() ? ('-static -ldl') : '-ldl';
// special: macOS needs -iconv // special: macOS needs -iconv
$libs = $this instanceof MacOSLibraryBase ? '-liconv' : ''; $libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : '';
$ac->appendEnv([ $ac->appendEnv([
'LDFLAGS' => $ldflags, 'LDFLAGS' => $ldflags,

View File

@@ -6,12 +6,13 @@ 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 ldap trait ldap
{ {
public function patchBeforeBuild(): bool public function patchBeforeBuild(): bool
{ {
$extra = getenv('SPC_LIBC') === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : ''; $extra = SPCTarget::getLibc() === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : '';
FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra); FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra);
return true; return true;
} }

View File

@@ -33,7 +33,13 @@ trait libxslt
'--without-debugger', '--without-debugger',
"--with-libxml-prefix={$this->getBuildRootPath()}", "--with-libxml-prefix={$this->getBuildRootPath()}",
); );
$ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make(); if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) {
$ac->appendEnv([
'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'),
'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'),
]);
}
$ac->configure()->make();
$this->patchPkgconfPrefix(['libexslt.pc']); $this->patchPkgconfPrefix(['libexslt.pc']);
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\util\executor\UnixCMakeExecutor; use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget;
trait mimalloc trait mimalloc
{ {
@@ -13,9 +14,9 @@ trait mimalloc
$cmake = UnixCMakeExecutor::create($this) $cmake = UnixCMakeExecutor::create($this)
->addConfigureArgs( ->addConfigureArgs(
'-DMI_BUILD_SHARED=OFF', '-DMI_BUILD_SHARED=OFF',
'-DMI_INSTALL_TOPLEVEL=ON' '-DMI_INSTALL_TOPLEVEL=ON',
); );
if (getenv('SPC_LIBC') === 'musl') { if (SPCTarget::getLibc() === 'musl') {
$cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON'); $cmake->addConfigureArgs('-DMI_LIBC_MUSL=ON');
} }
$cmake->build(); $cmake->build();

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait pkgconfig trait pkgconfig
{ {
@@ -14,7 +14,7 @@ trait pkgconfig
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->appendEnv([ ->appendEnv([
'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '', 'CFLAGS' => PHP_OS_FAMILY !== 'Linux' ? '-Wimplicit-function-declaration -Wno-int-conversion' : '',
'LDFLAGS' => !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? '' : '--static', 'LDFLAGS' => SPCTarget::isStatic() ? '--static' : '',
]) ])
->configure( ->configure(
'--with-internal-glib', '--with-internal-glib',

View File

@@ -8,6 +8,7 @@ use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCTarget;
trait postgresql trait postgresql
{ {
@@ -50,7 +51,7 @@ trait postgresql
$error_exec_cnt += $output[0] === 0 ? 0 : 1; $error_exec_cnt += $output[0] === 0 ? 0 : 1;
if (!empty($output[1][0])) { if (!empty($output[1][0])) {
$ldflags = $output[1][0]; $ldflags = $output[1][0];
$envs .= !($this instanceof LinuxLibraryBase) || getenv('SPC_LIBC') === 'glibc' ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" "; $envs .= SPCTarget::isStatic() ? " LDFLAGS=\"{$ldflags} -static\" " : " LDFLAGS=\"{$ldflags}\" ";
} }
$output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}"); $output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}");
$error_exec_cnt += $output[0] === 0 ? 0 : 1; $error_exec_cnt += $output[0] === 0 ? 0 : 1;

View File

@@ -34,6 +34,7 @@ class WindowsBuilder extends BuilderBase
$this->options = $options; $this->options = $options;
GlobalEnvManager::init(); GlobalEnvManager::init();
GlobalEnvManager::afterInit();
// ---------- set necessary options ---------- // ---------- set necessary options ----------
// set sdk (require visual studio 16 or 17) // set sdk (require visual studio 16 or 17)
@@ -60,11 +61,6 @@ class WindowsBuilder extends BuilderBase
*/ */
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{ {
// ---------- Update extra-libs ----------
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI; $enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
@@ -161,8 +157,10 @@ class WindowsBuilder extends BuilderBase
{ {
SourcePatcher::patchWindowsCLITarget(); SourcePatcher::patchWindowsCLITarget();
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// add nmake wrapper // add nmake wrapper
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cli_wrapper.bat', "nmake /nologo LIBS_CLI=\"{$this->getOption('extra-libs')} ws2_32.lib shell32.lib\" EXTRA_LD_FLAGS_PROGRAM= %*"); FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cli_wrapper.bat', "nmake /nologo LIBS_CLI=\"ws2_32.lib shell32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cli_wrapper.bat --task-args php.exe"); cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cli_wrapper.bat --task-args php.exe");
@@ -196,9 +194,11 @@ class WindowsBuilder extends BuilderBase
} }
FileSystem::writeFile(SOURCE_PATH . '\php-src\Makefile', $makefile); FileSystem::writeFile(SOURCE_PATH . '\php-src\Makefile', $makefile);
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// add nmake wrapper // add nmake wrapper
$fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' /DPHP_MICRO_FAKE_CLI" ' : ''; $fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' /DPHP_MICRO_FAKE_CLI" ' : '';
$wrapper = "nmake /nologo LIBS_MICRO=\"{$this->getOption('extra-libs')} ws2_32.lib shell32.lib\" CFLAGS_MICRO=\"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1{$fake_cli}\" %*"; $wrapper = "nmake /nologo LIBS_MICRO=\"ws2_32.lib shell32.lib {$extra_libs}\" CFLAGS_MICRO=\"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1{$fake_cli}\" %*";
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_micro_wrapper.bat', $wrapper); FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_micro_wrapper.bat', $wrapper);
// phar patch for micro // phar patch for micro

View File

@@ -37,6 +37,7 @@ class curl extends WindowsLibraryBase
'-DBUILD_EXAMPLES=OFF ' . // disable examples '-DBUILD_EXAMPLES=OFF ' . // disable examples
'-DUSE_LIBIDN2=OFF ' . // disable libidn2 '-DUSE_LIBIDN2=OFF ' . // disable libidn2
'-DCURL_USE_LIBPSL=OFF ' . // disable libpsl '-DCURL_USE_LIBPSL=OFF ' . // disable libpsl
'-DUSE_WINDOWS_SSPI=ON ' . // use Schannel instead of OpenSSL
'-DCURL_USE_SCHANNEL=ON ' . // use Schannel instead of OpenSSL '-DCURL_USE_SCHANNEL=ON ' . // use Schannel instead of OpenSSL
'-DCURL_USE_OPENSSL=OFF ' . // disable openssl due to certificate issue '-DCURL_USE_OPENSSL=OFF ' . // disable openssl due to certificate issue
'-DCURL_ENABLE_SSL=ON ' . '-DCURL_ENABLE_SSL=ON ' .

View File

@@ -99,6 +99,7 @@ abstract class BaseCommand extends Command
// init GlobalEnv // init GlobalEnv
if (!$this instanceof BuildCommand) { if (!$this instanceof BuildCommand) {
GlobalEnvManager::init(); GlobalEnvManager::init();
f_putenv('SPC_SKIP_TOOLCHAIN_CHECK=yes');
} }
if ($this->shouldExecute()) { if ($this->shouldExecute()) {
try { try {

View File

@@ -13,6 +13,7 @@ use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\LicenseDumper; use SPC\util\LicenseDumper;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@@ -63,8 +64,8 @@ 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) && PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') !== 'glibc') { 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 glibc!'); $this->output->writeln('Linux does not support dynamic extension loading with musl-libc full-static build, please build with shared 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);
@@ -133,6 +134,8 @@ class BuildPHPCommand extends BuildCommand
// print info // print info
$indent_texts = [ $indent_texts = [
'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')', 'Build OS' => PHP_OS_FAMILY . ' (' . php_uname('m') . ')',
'Build Target' => getenv('SPC_TARGET'),
'Build Toolchain' => getenv('SPC_TOOLCHAIN'),
'Build SAPI' => $builder->getBuildTypeName($rule), 'Build SAPI' => $builder->getBuildTypeName($rule),
'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions), 'Static Extensions (' . count($static_extensions) . ')' => implode(',', $static_extensions),
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions), 'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),

View File

@@ -10,7 +10,7 @@ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
#[AsCommand('craft', 'Build static-php from craft.yml')] #[AsCommand('craft', 'Build static-php from craft.yml')]
class CraftCommand extends BaseCommand class CraftCommand extends BuildCommand
{ {
public function configure(): void public function configure(): void
{ {

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace SPC\command; namespace SPC\command;
use SPC\builder\linux\SystemUtil;
use SPC\builder\traits\UnixSystemUtilTrait; use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
@@ -14,6 +13,7 @@ use SPC\store\Config;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@@ -223,8 +223,8 @@ class DownloadCommand extends BaseCommand
'{name}' => $source, '{name}' => $source,
'{arch}' => arch2gnu(php_uname('m')), '{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY), '{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default', '{libc}' => SPCTarget::getLibc() ?? 'default',
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', '{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
]; ];
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern')); $find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
// find filename in asset list // find filename in asset list
@@ -245,11 +245,15 @@ class DownloadCommand extends BaseCommand
} }
// if download failed, we will try to download alternative sources // if download failed, we will try to download alternative sources
logger()->warning("Download failed: {$e->getMessage()}"); logger()->warning("Download failed: {$e->getMessage()}");
logger()->notice("Trying to download alternative sources for {$source}");
$alt_sources = Config::getSource($source)['alt'] ?? null; $alt_sources = Config::getSource($source)['alt'] ?? null;
if ($alt_sources === null) { if ($alt_sources === null) {
logger()->warning("No alternative sources found for {$source}, using default alternative source");
$alt_config = array_merge($config, $this->getDefaultAlternativeSource($source)); $alt_config = array_merge($config, $this->getDefaultAlternativeSource($source));
} elseif ($alt_sources === false) {
logger()->warning("No alternative sources found for {$source}, skipping alternative download");
throw $e;
} else { } else {
logger()->notice("Trying to download alternative sources for {$source}");
$alt_config = array_merge($config, $alt_sources); $alt_config = array_merge($config, $alt_sources);
} }
Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list)); Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list));

View File

@@ -11,7 +11,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
#[AsCommand('spc-config', 'Build dependencies')] #[AsCommand('spc-config', 'Build dependencies')]
class SPCConfigCommand extends BuildCommand class SPCConfigCommand extends BaseCommand
{ {
protected bool $no_motd = true; protected bool $no_motd = true;

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace SPC\command\dev;
use SPC\command\BaseCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('dev:env', 'Returns the internally defined environment variables')]
class EnvCommand extends BaseCommand
{
public function configure()
{
$this->addArgument('env', InputArgument::REQUIRED, 'The environment variable to show, if not set, all will be shown');
}
public function initialize(InputInterface $input, OutputInterface $output): void
{
$this->no_motd = true;
parent::initialize($input, $output);
}
public function handle(): int
{
$env = $this->getArgument('env');
if (($val = getenv($env)) === false) {
$this->output->writeln("<error>Environment variable '{$env}' is not set.</error>");
return static::FAILURE;
}
$this->output->writeln("<info>{$val}</info>");
return static::SUCCESS;
}
}

View File

@@ -6,7 +6,6 @@ namespace SPC\command\dev;
use SPC\builder\BuilderProvider; use SPC\builder\BuilderProvider;
use SPC\builder\LibraryBase; use SPC\builder\LibraryBase;
use SPC\builder\linux\SystemUtil;
use SPC\command\BuildCommand; use SPC\command\BuildCommand;
use SPC\exception\ExceptionHandler; use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
@@ -16,6 +15,7 @@ use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@@ -42,6 +42,15 @@ class PackLibCommand extends BuildCommand
$builder->proveLibs($libraries); $builder->proveLibs($libraries);
$builder->validateLibsAndExts(); $builder->validateLibsAndExts();
// before pack, check if the dependency tree contains lib-suggests
foreach ($libraries as $lib) {
if (Config::getLib($lib, 'lib-suggests', []) !== []) {
logger()->critical("The library {$lib} has lib-suggests, packing [{$lib_name}] is not safe, abort !");
return static::FAILURE;
}
}
foreach ($builder->getLibs() as $lib) { foreach ($builder->getLibs() as $lib) {
if ($lib->getName() !== $lib_name) { if ($lib->getName() !== $lib_name) {
// other dependencies: install or build, both ok // other dependencies: install or build, both ok
@@ -76,8 +85,8 @@ class PackLibCommand extends BuildCommand
'{name}' => $lib->getName(), '{name}' => $lib->getName(),
'{arch}' => arch2gnu(php_uname('m')), '{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY), '{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default', '{libc}' => SPCTarget::getLibc() ?? 'default',
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', '{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
]; ];
// detect suffix, for proper tar option // detect suffix, for proper tar option
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern')); $tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));

View File

@@ -72,6 +72,14 @@ final class CheckListHandler
{ {
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) { foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) {
$ref = new \ReflectionClass($class); $ref = new \ReflectionClass($class);
$optional = $ref->getAttributes(OptionalCheck::class)[0] ?? null;
if ($optional !== null) {
/** @var OptionalCheck $instance */
$instance = $optional->newInstance();
if (is_callable($instance->check) && !call_user_func($instance->check)) {
continue; // skip this class if optional check is false
}
}
foreach ($ref->getMethods() as $method) { foreach ($ref->getMethods() as $method) {
foreach ($method->getAttributes() as $a) { foreach ($method->getAttributes() as $a) {
if (is_a($a->getName(), AsCheckItem::class, true)) { if (is_a($a->getName(), AsCheckItem::class, true)) {

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace SPC\doctor;
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class OptionalCheck
{
public function __construct(public array $check) {}
}

View File

@@ -4,10 +4,10 @@ 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;
use SPC\doctor\OptionalCheck;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
@@ -16,20 +16,20 @@ use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\PackageManager; use SPC\store\PackageManager;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\toolchain\MuslToolchain;
#[OptionalCheck([self::class, 'optionalCheck'])]
class LinuxMuslCheck class LinuxMuslCheck
{ {
public static function optionalCheck(): bool
{
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class;
}
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */
#[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)] #[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)]
public function checkMusl(): CheckResult public function checkMusl(): CheckResult
{ {
if (SystemUtil::isMuslDist()) {
return CheckResult::ok('musl-based distro, skipped');
}
if (getenv('SPC_LIBC') === 'glibc') {
return CheckResult::ok('Building with glibc, skipped');
}
$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')) {
return CheckResult::ok(); return CheckResult::ok();
@@ -40,13 +40,6 @@ 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 (SystemUtil::isMuslDist()) {
return CheckResult::ok('musl-based distro, skipped');
}
if (getenv('SPC_LIBC') === 'glibc') {
return CheckResult::ok('Building with glibc, skipped');
}
$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

@@ -51,6 +51,7 @@ class LinuxToolCheckList
'binutils-gold' => 'ld.gold', 'binutils-gold' => 'ld.gold',
'base-devel' => 'automake', 'base-devel' => 'automake',
'gettext-devel' => 'gettextize', 'gettext-devel' => 'gettextize',
'gettext-dev' => 'gettextize',
]; ];
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */

View File

@@ -4,13 +4,13 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\builder\linux\SystemUtil;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage; use SPC\store\pkg\CustomPackage;
use SPC\store\source\CustomSourceBase; use SPC\store\source\CustomSourceBase;
use SPC\util\SPCTarget;
/** /**
* Source Downloader. * Source Downloader.
@@ -591,7 +591,12 @@ class Downloader
public static function getPreBuiltLockName(string $source): string public static function getPreBuiltLockName(string $source): string
{ {
return "{$source}-" . PHP_OS_FAMILY . '-' . getenv('GNU_ARCH') . '-' . (getenv('SPC_LIBC') ?: 'default') . '-' . (SystemUtil::getLibcVersionIfExists() ?? 'default'); $os_family = PHP_OS_FAMILY;
$gnu_arch = getenv('GNU_ARCH') ?: 'unknown';
$libc = SPCTarget::getLibc();
$libc_version = SPCTarget::getLibcVersion() ?? 'default';
return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}";
} }
/** /**

View File

@@ -66,7 +66,7 @@ class SourceManager
$check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source); $check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source);
// $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']); // $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']);
if (!is_dir($check)) { if (!is_dir($check)) {
logger()->debug('Extracting source [' . $source . '] to ' . $check . ' ...'); logger()->debug("Extracting source [{$source}] to {$check} ...");
$filename = LockFile::getLockFullPath($lock_content); $filename = LockFile::getLockFullPath($lock_content);
FileSystem::extractSource($source, $lock_content['source_type'], $filename, $check); FileSystem::extractSource($source, $lock_content['source_type'], $filename, $check);
LockFile::putLockSourceHash($lock_content, $check); LockFile::putLockSourceHash($lock_content, $check);
@@ -81,7 +81,14 @@ class SourceManager
// when source already extracted, detect if the extracted source hash is the same as the lock file one // when source already extracted, detect if the extracted source hash is the same as the lock file one
if (file_exists("{$check}/.spc-hash") && FileSystem::readFile("{$check}/.spc-hash") === $hash) { if (file_exists("{$check}/.spc-hash") && FileSystem::readFile("{$check}/.spc-hash") === $hash) {
logger()->debug('Source [' . $source . '] already extracted in ' . $check . ', skip !'); logger()->debug("Source [{$source}] already extracted in {$check}, skip !");
continue;
}
// ext imap was included in php < 8.4 which we should not extract,
// but since it's not simple to compare php version, for now we just skip it
if ($source === 'ext-imap') {
logger()->debug("Source [ext-imap] already extracted in {$check}, skip !");
continue; continue;
} }

View File

@@ -11,13 +11,13 @@ use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\SPCTarget;
class SourcePatcher class SourcePatcher
{ {
public static function init(): void public static function init(): void
{ {
// FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']); // FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']);
FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']);
FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']); FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']);
FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']); FileSystem::addSourceExtractHook('swoole', [SourcePatcher::class, 'patchSwoole']);
FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']); FileSystem::addSourceExtractHook('php-src', [SourcePatcher::class, 'patchPhpLibxml212']);
@@ -105,7 +105,7 @@ class SourcePatcher
* @throws RuntimeException * @throws RuntimeException
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function patchMicro(string $name = '', string $target = '', ?array $items = null): bool public static function patchMicro(?array $items = null): bool
{ {
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) { if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
return false; return false;
@@ -152,11 +152,7 @@ class SourcePatcher
foreach ($patches as $patch) { foreach ($patches as $patch) {
logger()->info("Patching micro with {$patch}"); logger()->info("Patching micro with {$patch}");
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, $patch); self::patchFile(SOURCE_PATH . "/php-src/{$patch}", SOURCE_PATH . '/php-src');
f_passthru(
'cd ' . SOURCE_PATH . '/php-src && ' .
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1 '
);
} }
return true; return true;
@@ -165,24 +161,32 @@ class SourcePatcher
/** /**
* Use existing patch file for patching * Use existing patch file for patching
* *
* @param string $patch_name Patch file name in src/globals/patch/ * @param string $patch_name Patch file name in src/globals/patch/ or absolute path
* @param string $cwd Working directory for patch command * @param string $cwd Working directory for patch command
* @param bool $reverse Reverse patches (default: False) * @param bool $reverse Reverse patches (default: False)
* @throws RuntimeException * @throws RuntimeException
*/ */
public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool
{ {
if (!file_exists(ROOT_DIR . "/src/globals/patch/{$patch_name}")) { if (FileSystem::isRelativePath($patch_name)) {
$patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}";
} else {
$patch_file = $patch_name;
}
if (!file_exists($patch_file)) {
return false; return false;
} }
$patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}"; $patch_str = FileSystem::convertPath($patch_file);
$patch_str = str_replace('/', DIRECTORY_SEPARATOR, $patch_file); if (!file_exists($patch_str)) {
throw new RuntimeException("Patch file [{$patch_str}] does not exist");
}
// Copy patch from phar // Copy patch from phar
if (\Phar::running() !== '') { if (str_starts_with($patch_str, 'phar://')) {
file_put_contents(SOURCE_PATH . '/' . $patch_name, file_get_contents($patch_file)); $filename = pathinfo($patch_file, PATHINFO_BASENAME);
$patch_str = str_replace('/', DIRECTORY_SEPARATOR, SOURCE_PATH . '/' . $patch_name); file_put_contents(SOURCE_PATH . "/{$filename}", file_get_contents($patch_file));
$patch_str = FileSystem::convertPath(SOURCE_PATH . "/{$filename}");
} }
// detect // detect
@@ -456,7 +460,7 @@ class SourcePatcher
public static function patchFfiCentos7FixO3strncmp(): bool public static function patchFfiCentos7FixO3strncmp(): bool
{ {
if (PHP_OS_FAMILY !== 'Linux' || SystemUtil::getLibcVersionIfExists() > '2.17') { if (!($ver = SPCTarget::getLibcVersion()) || version_compare($ver, '2.17', '>')) {
return false; return false;
} }
if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) { if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {

View File

@@ -7,7 +7,6 @@ namespace SPC\store\pkg;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\GlobalEnvManager;
class GoXcaddy extends CustomPackage class GoXcaddy extends CustomPackage
{ {
@@ -62,7 +61,6 @@ class GoXcaddy extends CustomPackage
FileSystem::extractPackage($name, $source_type, $filename, $extract); FileSystem::extractPackage($name, $source_type, $filename, $extract);
GlobalEnvManager::init();
// install xcaddy // install xcaddy
shell() shell()
->appendEnv([ ->appendEnv([

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
class ClangNativeToolchain implements ToolchainInterface
{
public function initEnv(): void
{
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=clang');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=clang++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld');
}
public function afterInit(): void
{
foreach (['CC', 'CXX', 'AR', 'LD'] as $env) {
$command = getenv($env);
if (!$command || is_file($command)) {
continue;
}
match (PHP_OS_FAMILY) {
'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
default => throw new \RuntimeException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
};
}
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
class GccNativeToolchain implements ToolchainInterface
{
public function initEnv(): void
{
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CC=gcc');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_CXX=g++');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_AR=ar');
GlobalEnvManager::putenv('SPC_LINUX_DEFAULT_LD=ld.gold');
}
public function afterInit(): void
{
foreach (['CC', 'CXX', 'AR', 'LD'] as $env) {
$command = getenv($env);
if (!$command || is_file($command)) {
continue;
}
match (PHP_OS_FAMILY) {
'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
default => throw new \RuntimeException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
};
}
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
class MSVCToolchain implements ToolchainInterface
{
public function initEnv(): void {}
public function afterInit(): void {}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
class MuslToolchain implements ToolchainInterface
{
public function initEnv(): void
{
$arch = getenv('GNU_ARCH');
// Set environment variables for musl toolchain
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar");
GlobalEnvManager::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld");
GlobalEnvManager::addPathIfNotExists('/usr/local/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_LINUX_DEFAULT_LIBRARY_PATH=/usr/local/musl/lib:/usr/local/musl/{$arch}-linux-musl/lib");
}
public function afterInit(): void
{
$arch = getenv('GNU_ARCH');
// append LD_LIBRARY_PATH to $configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$ld_library_path = getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH');
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")) {
throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
}
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
interface ToolchainInterface
{
/**
* Initialize the environment for the given target.
*/
public function initEnv(): void;
/**
* Perform actions after the environment has been initialized for the given target.
*/
public function afterInit(): void;
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
class ToolchainManager
{
public const array OS_DEFAULT_TOOLCHAIN = [
'Linux' => MuslToolchain::class, // use musl toolchain by default, after zig pr merged, change this to ZigToolchain::class
'Windows' => MSVCToolchain::class,
'Darwin' => ClangNativeToolchain::class,
'BSD' => ClangNativeToolchain::class,
];
/**
* @throws WrongUsageException
*/
public static function initToolchain(): void
{
$libc = getenv('SPC_LIBC');
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 */
(new $toolchainClass())->initEnv();
GlobalEnvManager::putenv("SPC_TOOLCHAIN={$toolchain}");
}
public static function afterInitToolchain(): void
{
if (!getenv('SPC_TOOLCHAIN')) {
throw new WrongUsageException('SPC_TOOLCHAIN not set');
}
$toolchain = getenv('SPC_TOOLCHAIN');
/* @var ToolchainInterface $toolchain */
(new $toolchain())->afterInit();
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\toolchain;
class ZigToolchain implements ToolchainInterface
{
public function initEnv(): void {}
public function afterInit(): void {}
}

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace SPC\util; namespace SPC\util;
use SPC\builder\linux\SystemUtil;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\toolchain\ToolchainManager;
/** /**
* Environment variable manager * Environment variable manager
@@ -40,24 +40,6 @@ class GlobalEnvManager
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig'); self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
} }
// Define env vars for linux
if (PHP_OS_FAMILY === 'Linux') {
$arch = getenv('GNU_ARCH');
if (SystemUtil::isMuslDist() || getenv('SPC_LIBC') === 'glibc') {
self::putenv('SPC_LINUX_DEFAULT_CC=gcc');
self::putenv('SPC_LINUX_DEFAULT_CXX=g++');
self::putenv('SPC_LINUX_DEFAULT_AR=ar');
self::putenv('SPC_LINUX_DEFAULT_LD=ld.gold');
} else {
self::putenv("SPC_LINUX_DEFAULT_CC={$arch}-linux-musl-gcc");
self::putenv("SPC_LINUX_DEFAULT_CXX={$arch}-linux-musl-g++");
self::putenv("SPC_LINUX_DEFAULT_AR={$arch}-linux-musl-ar");
self::putenv("SPC_LINUX_DEFAULT_LD={$arch}-linux-musl-ld");
self::addPathIfNotExists('/usr/local/musl/bin');
self::addPathIfNotExists("/usr/local/musl/{$arch}-linux-musl/bin");
}
}
$ini = self::readIniFile(); $ini = self::readIniFile();
$default_put_list = []; $default_put_list = [];
@@ -80,6 +62,9 @@ class GlobalEnvManager
self::putenv("{$k}={$v}"); self::putenv("{$k}={$v}");
} }
} }
ToolchainManager::initToolchain();
// apply second time // apply second time
$ini2 = self::readIniFile(); $ini2 = self::readIniFile();
@@ -108,13 +93,26 @@ class GlobalEnvManager
self::$env_cache[] = $val; self::$env_cache[] = $val;
} }
private static function addPathIfNotExists(string $path): void public static function addPathIfNotExists(string $path): void
{ {
if (is_unix() && !str_contains(getenv('PATH'), $path)) { if (is_unix() && !str_contains(getenv('PATH'), $path)) {
self::putenv("PATH={$path}:" . getenv('PATH')); self::putenv("PATH={$path}:" . getenv('PATH'));
} }
} }
/**
* Initialize the toolchain after the environment variables are set.
* The toolchain or environment availability check is done here.
*
* @throws WrongUsageException
*/
public static function afterInit(): void
{
if (!filter_var(getenv('SPC_SKIP_TOOLCHAIN_CHECK'), FILTER_VALIDATE_BOOL)) {
ToolchainManager::afterInitToolchain();
}
}
/** /**
* @throws WrongUsageException * @throws WrongUsageException
*/ */

View File

@@ -55,7 +55,7 @@ class SPCConfigUtil
ob_get_clean(); ob_get_clean();
$ldflags = $this->getLdflagsString(); $ldflags = $this->getLdflagsString();
$libs = $this->getLibsString($libraries, $with_dependencies); $libs = $this->getLibsString($libraries, $with_dependencies);
if (PHP_OS_FAMILY === 'Darwin') { if (SPCTarget::getTargetOS() === 'Darwin') {
$libs .= " {$this->getFrameworksString($extensions)}"; $libs .= " {$this->getFrameworksString($extensions)}";
} }
$cflags = $this->getIncludesString(); $cflags = $this->getIncludesString();
@@ -145,9 +145,8 @@ class SPCConfigUtil
} }
} }
} }
// patch: imagick (imagemagick wrapper) for linux needs libgomp if (in_array('imap', $libraries) && SPCTarget::getLibc() === 'glibc') {
if (in_array('imagemagick', $libraries) && PHP_OS_FAMILY === 'Linux' && !(getenv('SPC_LIBC') === 'glibc' && str_contains(getenv('CC'), 'devtoolset-10'))) { $short_name[] = '-lcrypt';
$short_name[] = '-lgomp';
} }
return implode(' ', $short_name); return implode(' ', $short_name);
} }

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace SPC\util;
use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException;
/**
* SPC build target constants and toolchain initialization.
* format: {target_name}[-{libc_subtype}]
*/
class SPCTarget
{
public const array LIBC_LIST = [
'musl',
'glibc',
];
/**
* Returns whether the target is a full-static target.
*/
public static function isStatic(): bool
{
$env = getenv('SPC_TARGET');
$libc = getenv('SPC_LIBC');
// if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released
if ($libc === 'musl') {
return true;
}
// TODO: add zig target parser here
return false;
}
/**
* Returns the libc type if set, for other OS, it will always return null.
*/
public static function getLibc(): ?string
{
$env = getenv('SPC_TARGET');
$libc = getenv('SPC_LIBC');
if ($libc !== false) {
return $libc;
}
// TODO: zig target parser
return null;
}
/**
* Returns the libc version if set, for other OS, it will always return null.
*/
public static function getLibcVersion(): ?string
{
$env = getenv('SPC_TARGET');
$libc = getenv('SPC_LIBC');
if ($libc !== false) {
// legacy method: get a version from system
return SystemUtil::getLibcVersionIfExists($libc);
}
// TODO: zig target parser
return null;
}
/**
* Returns the target OS family, e.g. Linux, Darwin, Windows, BSD.
* Currently, we only support native building.
*
* @return 'BSD'|'Darwin'|'Linux'|'Windows'
* @throws WrongUsageException
*/
public static function getTargetOS(): string
{
$target = getenv('SPC_TARGET');
if ($target === false) {
return PHP_OS_FAMILY;
}
// TODO: zig target parser like below?
return match (true) {
str_contains($target, 'linux') => 'Linux',
str_contains($target, 'macos') => 'Darwin',
str_contains($target, 'windows') => 'Windows',
default => throw new WrongUsageException('Cannot parse target.'),
};
}
}

View File

@@ -21,19 +21,19 @@ $test_php_version = [
// test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available) // test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available)
$test_os = [ $test_os = [
// 'macos-13', // 'macos-13', // bin/spc for x86_64
// 'macos-14', // 'macos-14', // bin/spc for arm64
// 'macos-15', // 'macos-15', // bin/spc for arm64
// 'ubuntu-latest', // 'ubuntu-latest', // bin/spc-alpine-docker for x86_64
// 'ubuntu-22.04', // 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
// 'ubuntu-24.04', // 'ubuntu-24.04', // bin/spc for x86_64
// 'ubuntu-22.04-arm', // 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
// 'ubuntu-24.04-arm', // 'ubuntu-24.04-arm', // bin/spc for arm64
'windows-latest', 'windows-latest', // .\bin\spc.ps1
]; ];
// whether enable thread safe // whether enable thread safe
$zts = true; $zts = false;
$no_strip = false; $no_strip = false;
@@ -44,17 +44,17 @@ $upx = false;
$frankenphp = false; $frankenphp = false;
// prefer downloading pre-built packages to speed up the build process // prefer downloading pre-built packages to speed up the build process
$prefer_pre_built = true; $prefer_pre_built = false;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) { $extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'curl', 'Linux', 'Darwin' => 'imap,swoole',
'Windows' => 'intl', 'Windows' => 'curl',
}; };
// If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`).
$shared_extensions = match (PHP_OS_FAMILY) { $shared_extensions = match (PHP_OS_FAMILY) {
'Linux' => 'uv', 'Linux' => '',
'Darwin' => '', 'Darwin' => '',
'Windows' => '', 'Windows' => '',
}; };