Compare commits

...

146 Commits

Author SHA1 Message Date
Marc
79ab6490fd fix CustomPackage handling (#897) 2025-09-18 13:37:26 +02:00
henderkes
191d345250 update paths to only install once 2025-09-18 12:41:36 +02:00
henderkes
311ee40ae4 update LinuxToolCheckList.php to install perl-Time-Piece to make openssl work 2025-09-18 11:58:58 +02:00
henderkes
808d224b08 build frankenphp before shared extensions again 2025-09-18 09:07:18 +02:00
Marc
4247883664 Merge branch 'main' into fix-zig 2025-09-18 08:06:23 +02:00
Marc
3198cc40c1 Remove icu backend from libxml2 (#903) 2025-09-17 10:46:05 +02:00
Marc Henderkes
8680e83af3 fix dba building shared 2025-09-16 14:43:49 +02:00
Marc
0156f33a20 Merge branch 'main' into remove-icu-backend 2025-09-16 13:35:46 +02:00
Marc Henderkes
df8b2dbf16 better patching for libstdc++ when extensions forget they need it 2025-09-16 13:10:10 +02:00
Marc Henderkes
30e174ac95 use any available english locale instead of requiring en_US.utf-8 2025-09-16 13:10:09 +02:00
Marc Henderkes
b9f8f02d98 don't use libicu backend for libxml2
(it's discouraged from being used, the first path always goes through iconv, icu is only the fallback)
2025-09-16 13:10:09 +02:00
henderkes
0b0ae270da we lost a v somehow 2025-09-10 23:35:17 +07:00
henderkes
893ce31b17 only install pkgs for crafting when necessary 2025-09-10 23:28:20 +07:00
crazywhalecc
e35d6c2651 Oh, we also support windows here 2025-09-09 19:43:08 +08:00
crazywhalecc
8dd0512335 Fix macOS lib-depends 2025-09-09 19:43:08 +08:00
henderkes
5c1194ea92 fix custompackage handling 2025-09-09 12:10:06 +07:00
Marc
953ed83df5 Merge pull request #896 from crazywhalecc/fix-zig
Fix zig -isystem overriding
2025-09-08 11:00:03 +02:00
henderkes
c330d02e78 update pkg paths to not double the $arch-$os 2025-09-08 14:18:03 +07:00
henderkes
af6a23011c fix zig relative directory 2025-09-08 13:33:44 +07:00
Abi أب
882ae07deb libxml2 arg typo 2025-09-07 22:56:41 +08:00
crazywhalecc
4bc30b0b6f Add php-src/config.log for SPCException 2025-09-07 11:50:51 +08:00
Marc
8d303348d9 Merge pull request #883 from crazywhalecc/sapi/cgi
Add cgi support for macOS and Linux
2025-09-05 11:52:26 +02:00
Marc
2a2f4120b9 Merge pull request #889 from crazywhalecc/franken
use our toolchain for go mod
2025-09-05 11:47:15 +02:00
Marc
15f8887f14 Update config/lib.json 2025-09-05 16:25:15 +07:00
crazywhalecc
fe945ab3ea Merge branch 'main' into sapi/cgi
# Conflicts:
#	composer.lock
2025-09-05 17:15:28 +08:00
crazywhalecc
e2b6f4cedd Merge branch 'main' into sapi/cgi 2025-09-05 17:14:27 +08:00
Jerry Ma
d82c86cf62 Fix docker doctor fix pkgroot volume overwrite bug (#882)
* Fix docker doctor fix pkgroot volume overwrite bug

* cs fix

* Remove pkgroot-private

* Use arch-specified pkgroot

* Update setup-runtime php version

* Add docker-entrypoint.sh

* Update dependencies

* cs fix

* test
2025-09-05 17:13:40 +08:00
henderkes
9df2867175 just always require libxml2 until we rework sapis to require their own stuff 2025-09-05 15:52:07 +07:00
henderkes
5c803d1553 fix cs 2025-09-05 15:31:30 +07:00
henderkes
d45b1853f8 fix cs 2025-09-05 15:28:51 +07:00
henderkes
1ac621fb9c use our toolchain for go mod 2025-09-05 15:20:24 +07:00
crazywhalecc
4e74ac1937 test 2025-09-05 15:02:45 +08:00
crazywhalecc
959734ac31 cs fix 2025-09-05 11:17:45 +08:00
crazywhalecc
166f3de52f Merge branch 'main' into feat/cgi 2025-09-05 11:17:34 +08:00
crazywhalecc
e3adfff16f Update dependencies 2025-09-05 11:14:56 +08:00
crazywhalecc
7cbc374278 Add docker-entrypoint.sh 2025-09-05 11:04:42 +08:00
crazywhalecc
129041402d Update setup-runtime php version 2025-09-05 11:00:57 +08:00
crazywhalecc
9ac578dfd6 Use arch-specified pkgroot 2025-09-05 11:00:46 +08:00
crazywhalecc
ef709a169b Remove pkgroot-private 2025-09-05 11:00:25 +08:00
Marc
2eeba33f94 Merge pull request #885 from crazywhalecc/dumb_github
switch getting frankenphp version to go mod (bypass rate limits)
2025-09-04 18:15:40 +02:00
henderkes
f4b2b9ae7d fix cs 2025-09-04 17:45:13 +07:00
henderkes
e44efb2a54 use go mod to bypass github rate limiting 2025-09-04 17:42:17 +07:00
henderkes
598f6d55c5 keep retrying more 2025-09-04 16:28:17 +07:00
crazywhalecc
a2c5c7e6e6 Add cgi SAPI 2025-09-04 14:05:00 +08:00
crazywhalecc
4f6e646812 cs fix 2025-09-04 12:13:57 +08:00
crazywhalecc
a79564f685 Fix docker doctor fix pkgroot volume overwrite bug 2025-09-04 12:03:13 +08:00
crazywhalecc
a77e49cbc9 Stubborn phpstan 2025-09-02 00:17:44 +08:00
crazywhalecc
cefb737fd2 Update version to 2.7.3 2025-09-02 00:17:44 +08:00
crazywhalecc
8c8aba2dd5 Fix unixodbc driver config searching path 2025-09-02 00:17:44 +08:00
Marc
97b18e9121 Merge pull request #877 from crazywhalecc/franken-retries
retry fetching frankenphp release info
2025-09-01 15:29:21 +02:00
gemini
ddc9cc2237 Fix cli generator: prefer-pre-built belongs to download-options rather than build-options 2025-09-01 20:44:37 +08:00
DubbleClick
aff803f334 retry fetching frankenphp release info 2025-09-01 19:07:29 +07:00
Marc
38ec03fe30 Merge pull request #871 from crazywhalecc/fix/frankenphp-dynamic-exports
build frankenphp and embed after shared extensions
2025-08-31 15:06:11 +02:00
Marc
55836771c9 Merge pull request #869 from crazywhalecc/pkg-config-pkg
turn pkg-config into a package
2025-08-31 15:04:41 +02:00
crazywhalecc
08a68796bf Call export in dynamic symbol getter 2025-08-31 18:05:09 +08:00
crazywhalecc
3b9efcb2d4 Put it into toolchain manager to skip in unit test 2025-08-31 15:24:29 +08:00
crazywhalecc
022ba3dce4 Merge branch 'main' into pkg-config-pkg
# Conflicts:
#	src/globals/test-extensions.php
2025-08-31 15:20:50 +08:00
crazywhalecc
86973e622d sort config 2025-08-31 15:06:59 +08:00
crazywhalecc
0fa38bad6d Use existing pkg-config builds and pre-built contents for packages 2025-08-31 15:04:34 +08:00
crazywhalecc
465bd3ce85 Use separated functions for exporting symbols 2025-08-31 14:24:28 +08:00
Jerry Ma
0c9130ab6c Update node version in vitepress-deploy.yml 2025-08-31 13:44:37 +08:00
crazywhalecc
6c75ecf0c4 Add docs search 2025-08-31 11:47:41 +08:00
DubbleClick
d533a0591b skip line before preg_replacing 2025-08-30 19:31:22 +07:00
DubbleClick
3f74d58503 fix macos, for real this time 2025-08-30 14:12:13 +07:00
DubbleClick
1ba92ccc99 fix patch version stripping (.\d -> .\d+) 2025-08-30 12:02:15 +07:00
DubbleClick
75db184077 fix macos command to print defined symbols? 2025-08-29 17:50:47 +07:00
DubbleClick
66902d74c4 cs fix 2025-08-29 16:38:48 +07:00
DubbleClick
f6cc6af39c macos? 3 2025-08-29 16:36:43 +07:00
DubbleClick
0da8dcf04a macos? 2 2025-08-29 15:14:54 +07:00
DubbleClick
190be6c7b9 macos? 2025-08-29 14:12:00 +07:00
DubbleClick
2972ab31d7 don't add empty dynamic list x) 2025-08-29 12:15:56 +07:00
DubbleClick
4b4ae9b014 write to a --dynamic-list file
see https://sourceware.org/binutils/docs-2.36/ld/Options.html
2025-08-29 10:53:38 +07:00
DubbleClick
20db00afcc no need to test all static targets, nothing changes for them 2025-08-29 10:26:46 +07:00
DubbleClick
56c90ca4a3 use all symbols from libphp.a, so we don't need to build shared extensions 2025-08-29 10:24:51 +07:00
DubbleClick
fad2314539 found ENABLE_SHARED option in liburing! 2025-08-29 09:25:32 +07:00
DubbleClick
460eb02086 build frankenphp and embed after shared extensions, make sure the linker doesn't drop libphp.a symbols that extensions need 2025-08-29 08:52:55 +07:00
Marc
5a30d509e7 Merge pull request #864 from crazywhalecc/swoolehooks
swoole hooks can be compiled if pgsql/sqlite are not compiled in statically
2025-08-29 03:46:30 +02:00
crazywhalecc
0f0050115c Update cli generator, add frankenphp SAPI, add copy-to-clipboard func 2025-08-28 22:35:51 +08:00
crazywhalecc
bf55db959e Add swoole-hook-odbc notes 2025-08-28 22:35:23 +08:00
DubbleClick
39a9840d49 why is phpstan so stupid about this constant? 2025-08-28 12:14:26 +07:00
DubbleClick
866ca264d9 only disable it for x86_64 2025-08-28 11:35:46 +07:00
DubbleClick
f18725083a disable opcache jit automatically on alpine target (only required for php < 8.5) 2025-08-28 11:32:33 +07:00
crazywhalecc
20fbbb1dbe test 2025-08-28 11:01:58 +08:00
crazywhalecc
1a7bf2d070 test 2025-08-28 10:49:02 +08:00
crazywhalecc
fc7e8ebc53 extension test 2025-08-28 10:29:22 +08:00
crazywhalecc
532360886e Merge branch 'main' into swoolehooks 2025-08-28 10:29:07 +08:00
crazywhalecc
117a54d077 extension test 2025-08-28 10:14:47 +08:00
DubbleClick
f2c131a2cc wno-int-conversion 2025-08-27 17:01:07 +07:00
DubbleClick
6f0310e8f7 cs fix 2025-08-27 16:46:58 +07:00
DubbleClick
c7141003db oops 2025-08-27 16:46:09 +07:00
DubbleClick
d32bdb2825 all env vars 2025-08-27 16:45:47 +07:00
DubbleClick
a66abfa583 test all OS 2025-08-27 16:35:24 +07:00
DubbleClick
2551772de4 use system cc 2025-08-27 16:34:29 +07:00
DubbleClick
25fe794acc trim all items 2025-08-27 14:36:05 +07:00
DubbleClick
694fd2f1e0 turn pkg-config into a package instead of a library 2025-08-27 14:33:39 +07:00
DubbleClick
5a4b920ae2 pkg-config patch 2025-08-27 13:04:08 +07:00
DubbleClick
95f1b65bca fix perl installation by checking for FindBin 2025-08-27 13:03:57 +07:00
DubbleClick
25401e5959 cs fix 2025-08-27 12:43:48 +07:00
DubbleClick
3da58d5c24 filter micro patches to not be '' 2025-08-27 11:48:59 +07:00
DubbleClick
08ab3c17d6 fix perl-IPC-Cmd check so it doesn't try installing it every time 2025-08-27 11:40:06 +07:00
DubbleClick
ecdb94b429 suggestions 2025-08-27 08:31:48 +07:00
DubbleClick
00892c20fa fix odbc libs when iconv is built too 2025-08-26 20:44:30 +07:00
DubbleClick
c433aed521 fix macos? 2025-08-26 19:35:10 +07:00
DubbleClick
1243fb9678 don't test macos as it just fails either way, add odbc test 2025-08-26 18:55:22 +07:00
DubbleClick
d9c2247f99 fix alpine realpath -s issue 2025-08-26 18:16:44 +07:00
DubbleClick
0d4d4283d1 only install library for liburing 2025-08-26 18:04:43 +07:00
DubbleClick
effefd4844 string 2025-08-26 14:51:07 +07:00
DubbleClick
2d409db2f9 we don't need a min glibc version, just don't use --use-libc! 2025-08-26 14:42:34 +07:00
DubbleClick
00f262571c cs fix 2025-08-26 14:08:09 +07:00
DubbleClick
4eac953c71 ensure liburing is only pulled in by suggested libs when glibc >= 2.30 (or musl) 2025-08-26 14:07:49 +07:00
DubbleClick
43352ab986 don't print repeated --ri swoole check 2025-08-26 01:01:45 +07:00
DubbleClick
9803bf6c3f prevent warning message from addons 2025-08-26 00:59:54 +07:00
DubbleClick
ba326973e5 prevent infinite recursion if ext suggests addons that depend on the extension 2025-08-26 00:56:57 +07:00
DubbleClick
a1f2126c00 update comments 2025-08-26 00:38:35 +07:00
DubbleClick
b1da64d46b add swoole-hook-odbc to work the same way as the other hooks 2025-08-26 00:24:40 +07:00
DubbleClick
2694dd9e21 add liburing for swoole 2025-08-25 22:57:04 +07:00
Marc
37e0f1d3f3 Update src/SPC/command/BuildPHPCommand.php 2025-08-25 19:50:41 +07:00
DubbleClick
f80aee5133 get rid of $rt 2025-08-25 19:31:15 +07:00
DubbleClick
65ee7479ac suggestion and clean up frankenphp build 2025-08-25 19:29:10 +07:00
DubbleClick
868f6d408b only feed -lgcov to the go driver 2025-08-25 18:44:03 +07:00
DubbleClick
efdfbf437a use format string 2025-08-25 18:23:04 +07:00
DubbleClick
2b57bca55c remove useless import 2025-08-25 15:30:04 +07:00
DubbleClick
99ccbf8a40 @crazywhalecc please look what's wrong with macos 2025-08-25 15:28:53 +07:00
DubbleClick
9fe322375f use 8.4 for tests only 2025-08-25 14:55:30 +07:00
DubbleClick
d13e369994 test swoole 2025-08-25 13:11:40 +07:00
DubbleClick
a46ad7b81e cs fix 2025-08-25 12:58:12 +07:00
DubbleClick
324ba0d3dc use type=addon and arg-type=none 2025-08-25 12:57:49 +07:00
DubbleClick
1c7fa0132d remove space 2025-08-25 12:44:34 +07:00
DubbleClick
93223a9d44 too much pgo, too little pdo 2025-08-25 12:43:21 +07:00
DubbleClick
b6d4af218b zts -> --enable-swoole-thread 2025-08-25 12:42:36 +07:00
DubbleClick
448941f741 don't request configurearg if it's not used (ease debugging) 2025-08-25 12:36:10 +07:00
DubbleClick
7e0e9091be explicitly enable swoole-mysql too 2025-08-25 12:11:56 +07:00
DubbleClick
8104ff7236 update checks for shared swoole-hook extensions 2025-08-25 12:08:53 +07:00
DubbleClick
75f734daa8 cs fix 2025-08-25 11:22:35 +07:00
DubbleClick
779a724875 go's gcc driver is the dumbest shit to ever exist 2025-08-25 11:21:08 +07:00
DubbleClick
e975e151f9 swoole hooks can be compiled if pgsql/sqlite are not compiled in statically 2025-08-25 11:09:02 +07:00
Marc
a5351e1546 Merge pull request #863 from crazywhalecc/brotli-watcher
fix gcc version detection
2025-08-25 05:32:46 +02:00
DubbleClick
af90b840ee format string 2025-08-25 09:59:27 +07:00
DubbleClick
2558c02470 oopsie 2025-08-25 09:59:06 +07:00
DubbleClick
df60ccff0e also make sure clang version uses dots 2025-08-25 09:52:03 +07:00
DubbleClick
2ed8bbf391 add cflags and ldflags to frankenphp build 2025-08-25 09:29:19 +07:00
DubbleClick
ef3025798d fix gcc compiler version 2025-08-25 09:29:06 +07:00
Marc
bea3bd8392 Merge pull request #861 from crazywhalecc/fix/macos-ar
Add macOS AR and LD
2025-08-22 07:23:49 +02:00
crazywhalecc
99e8900aba Add macOS AR and LD 2025-08-22 13:19:42 +08:00
Marc
1c4a6ca02e Merge pull request #860 from crazywhalecc/brotli-watcher
add brotli and watcher to lib-suggests
2025-08-21 15:31:35 +02:00
DubbleClick
b5fa7be53f add brotli and watcher to lib-suggests 2025-08-20 13:40:54 +07:00
72 changed files with 2223 additions and 1348 deletions

View File

@@ -12,5 +12,5 @@ trim_trailing_whitespace = true
trim_trailing_whitespace = false
indent_size = 2
[*.{yml,yaml, vue}]
[*.{yml,yaml,vue,ts}]
indent_size = 2

View File

@@ -65,7 +65,7 @@ jobs:
- sqlsrv
- ssh2
- swoole
- swoole,swoole-hook-pgsql,swoole-hook-mysql,swoole-hook-sqlite
- swoole,swoole-hook-pgsql,swoole-hook-mysql,swoole-hook-sqlite,swoole-hook-odbc
- swow
- sysvmsg,sysvsem,sysvshm
- tidy
@@ -82,7 +82,7 @@ jobs:
- zlib
- zstd
php-version:
- "8.5"
- "8.4"
operating-system:
- "ubuntu-latest"
#- "macos-13"

View File

@@ -21,7 +21,6 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn
- run: yarn install --frozen-lockfile

1
.gitignore vendored
View File

@@ -34,6 +34,7 @@ packlib_files.txt
/bin/*
!/bin/spc*
!/bin/setup-runtime*
!/bin/docker-entrypoint.sh
# exclude windows build tools
/php-sdk-binary-tools/

View File

@@ -207,6 +207,7 @@ bin/spc --version
- `--build-micro`:构建 phpmicro 自执行二进制
- `--build-fpm`:构建 fpm
- `--build-embed`:构建 embedlibphp
- `--build-cgi`: 构建 cgi不推荐
- `--build-all`:构建所有
如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误:

View File

@@ -218,6 +218,7 @@ Now we support `cli`, `micro`, `fpm` and `embed` SAPI. You can use one or more o
- `--build-micro`: build static phpmicro self-extracted executable
- `--build-fpm`: build static fpm binary
- `--build-embed`: build embed (libphp)
- `--build-cgi`: build cgi binary (not recommended)
- `--build-all`: build all
If anything goes wrong, use `--debug` option to display full terminal output:

11
bin/docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
TARGET_DIR="/app/pkgroot/$(uname -m)-linux"
BACKUP_DIR="/app/pkgroot-private"
# copy private pkgroot to pkgroot if pkgroot is empty
if [ ! -d "$TARGET_DIR" ] || [ -z "$(ls -A "$TARGET_DIR")" ]; then
echo "* Copying private pkgroot to pkgroot ..."
rm -rf "$TARGET_DIR"
cp -r "$BACKUP_DIR" "$TARGET_DIR"
fi
exec "$@"

View File

@@ -25,7 +25,7 @@ __DIR__=$(cd "$(dirname "$0")" && pwd)
__PROJECT__=$(cd "${__DIR__}"/../ && pwd)
# set download dir
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.12-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__COMPOSER_URL__="https://getcomposer.org/download/latest-stable/composer.phar"
# use china mirror
@@ -45,7 +45,7 @@ done
case "$mirror" in
china)
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/bulk/php-8.4.12-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__COMPOSER_URL__="https://mirrors.tencent.com/composer/composer.phar"
;;

View File

@@ -3,7 +3,7 @@
set -e
# This file is using docker to run commands
SPC_DOCKER_VERSION=v5
SPC_DOCKER_VERSION=v6
# Detect docker can run
if ! which docker >/dev/null; then
@@ -122,6 +122,14 @@ ADD ./src /app/src
COPY ./composer.* /app/
ADD ./bin /app/bin
RUN composer install --no-dev
ADD ./config /app/config
RUN bin/spc doctor --auto-fix
RUN bin/spc install-pkg upx
RUN mv /app/pkgroot/\$(uname -m)-linux /app/pkgroot-private
ADD bin/docker-entrypoint.sh /bin/docker-entrypoint.sh
RUN chmod +x /bin/docker-entrypoint.sh
ENTRYPOINT ["/bin/docker-entrypoint.sh"]
EOF
fi

View File

@@ -3,7 +3,7 @@
set -e
# This file is using docker to run commands
SPC_DOCKER_VERSION=v5
SPC_DOCKER_VERSION=v6
# Detect docker can run
if ! which docker >/dev/null; then
@@ -113,6 +113,7 @@ ENV PATH="/app/bin:/cmake/bin:/opt/rh/devtoolset-10/root/usr/bin:\$PATH"
ADD ./config /app/config
RUN CC=gcc bin/spc doctor --auto-fix --debug
RUN bin/spc install-pkg upx
RUN if [ -f /app/buildroot/bin/re2c ]; then \
cp /app/buildroot/bin/re2c /usr/local/bin/re2c ;\
fi
@@ -132,6 +133,11 @@ RUN curl -o automake.tgz -fsSL https://ftp.gnu.org/gnu/automake/automake-1.17.ta
make && \
make install && \
ln -sf /usr/local/bin/automake /usr/bin/automake
RUN mv /app/pkgroot/\$(uname -m)-linux /app/pkgroot-private
ADD bin/docker-entrypoint.sh /bin/docker-entrypoint.sh
RUN chmod +x /bin/docker-entrypoint.sh
ENTRYPOINT ["/bin/docker-entrypoint.sh"]
EOF
fi

372
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -103,7 +103,7 @@ SPC_MICRO_PATCHES=cli_checks,disable_huge_page
; buildconf command
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-cgi --disable-phpdbg --with-pic"
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-phpdbg --with-pic"
; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
@@ -124,6 +124,8 @@ SPC_TARGET=native-macos
; compiler environments
CC=clang
CXX=clang++
AR=ar
LD=ld
; 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_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
@@ -135,7 +137,7 @@ SPC_MICRO_PATCHES=cli_checks,macos_iconv
; buildconf command
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg"
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-phpdbg"
; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"

View File

@@ -414,9 +414,17 @@
"libmemcached",
"fastlz"
],
"lib-suggests": [
"zstd"
],
"ext-depends": [
"session",
"zlib"
],
"ext-suggests": [
"igbinary",
"msgpack",
"session"
]
},
"mongodb": {
@@ -842,14 +850,23 @@
"nghttp2",
"zlib"
],
"lib-suggests": [
"zstd"
],
"lib-suggests-linux": [
"zstd",
"liburing"
],
"ext-depends": [
"openssl",
"curl"
],
"ext-suggests": [
"sockets",
"swoole-hook-pgsql",
"swoole-hook-mysql",
"swoole-hook-sqlite"
"swoole-hook-sqlite",
"swoole-hook-odbc"
]
},
"swoole-hook-mysql": {
@@ -859,11 +876,12 @@
},
"notes": true,
"type": "addon",
"arg-type": "custom",
"arg-type": "none",
"ext-depends": [
"mysqlnd",
"pdo",
"pdo_mysql"
"pdo_mysql",
"swoole"
],
"ext-suggests": [
"mysqli"
@@ -877,10 +895,11 @@
},
"notes": true,
"type": "addon",
"arg-type": "custom",
"arg-type": "none",
"ext-depends": [
"pgsql",
"pdo"
"pdo",
"swoole"
]
},
"swoole-hook-sqlite": {
@@ -890,10 +909,27 @@
},
"notes": true,
"type": "addon",
"arg-type": "custom",
"arg-type": "none",
"ext-depends": [
"sqlite3",
"pdo"
"pdo",
"swoole"
]
},
"swoole-hook-odbc": {
"support": {
"Windows": "no",
"BSD": "wip"
},
"notes": true,
"type": "addon",
"arg-type": "none",
"ext-depends": [
"pdo",
"swoole"
],
"lib-depends": [
"unixodbc"
]
},
"swow": {

View File

@@ -1,9 +1,6 @@
{
"lib-base": {
"type": "root",
"lib-depends-unix": [
"pkg-config"
]
"type": "root"
},
"php": {
"type": "root",
@@ -12,8 +9,19 @@
"lib-base",
"micro"
],
"lib-depends-macos": [
"lib-base",
"micro",
"libxml2"
],
"lib-suggests-linux": [
"libacl"
"libacl",
"brotli",
"watcher"
],
"lib-suggests-macos": [
"brotli",
"watcher"
]
},
"micro": {
@@ -444,6 +452,7 @@
},
"libmemcached": {
"source": "libmemcached",
"cpp-library": true,
"static-libs-unix": [
"libmemcached.a",
"libmemcachedprotocol.a",
@@ -588,7 +597,6 @@
],
"lib-suggests-unix": [
"xz",
"icu",
"zlib"
],
"lib-depends-windows": [
@@ -919,5 +927,20 @@
"zstd.h",
"zstd_errors.h"
]
},
"liburing": {
"source": "liburing",
"pkg-configs": [
"liburing",
"liburing-ffi"
],
"static-libs-linux": [
"liburing.a",
"liburing-ffi.a"
],
"headers-linux": [
"liburing/",
"liburing.h"
]
}
}

View File

@@ -1,4 +1,16 @@
{
"go-xcaddy-aarch64-linux": {
"type": "custom"
},
"go-xcaddy-aarch64-macos": {
"type": "custom"
},
"go-xcaddy-x86_64-linux": {
"type": "custom"
},
"go-xcaddy-x86_64-macos": {
"type": "custom"
},
"musl-toolchain-aarch64-linux": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz"
@@ -15,6 +27,38 @@
"nasm-2.16.01/ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
}
},
"pkg-config-aarch64-linux": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-aarch64-linux-musl-1.2.5.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"pkg-config-aarch64-macos": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-aarch64-darwin.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"pkg-config-x86_64-linux": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-x86_64-linux-musl-1.2.5.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"pkg-config-x86_64-macos": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-x86_64-darwin.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"strawberry-perl-x86_64-win": {
"type": "url",
"url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip"
@@ -43,30 +87,18 @@
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
}
},
"go-xcaddy-x86_64-linux": {
"zig-aarch64-linux": {
"type": "custom"
},
"go-xcaddy-aarch64-linux": {
"type": "custom"
},
"go-xcaddy-x86_64-macos": {
"type": "custom"
},
"go-xcaddy-aarch64-macos": {
"zig-aarch64-macos": {
"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

@@ -1136,5 +1136,14 @@
"type": "file",
"path": "LICENSE"
}
},
"liburing": {
"type": "ghtar",
"repo": "axboe/liburing",
"prefer-stable": true,
"license": {
"type": "file",
"path": "COPYING"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,14 @@ export default {
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2023-present crazywhalecc'
}
},
search: {
provider: 'algolia',
options: {
appId: 'IHJHUB1SF1',
apiKey: '8266d31cc2ffbd0e059f1c6e5bdaf8fc',
indexName: 'static-php docs',
},
},
}
}

View File

@@ -46,6 +46,14 @@ If you use `swoole,swoole-hook-sqlite`, you will enable the coroutine mode of Sw
swoole-hook-sqlite conflicts with the `pdo_sqlite` extension. If you want to use Swoole and `pdo_sqlite`, please delete the pdo_sqlite extension and enable `swoole` and `swoole-hook-sqlite`.
This extension contains an implementation of the coroutine environment for `pdo_sqlite`.
## swoole-hook-odbc
swoole-hook-odbc is not an extension, it's a Hook feature of Swoole.
If you use `swoole,swoole-hook-odbc`, you will enable the coroutine mode of Swoole's `odbc` extension.
swoole-hook-odbc conflicts with the `pdo_odbc` extension. If you want to use Swoole and `pdo_odbc`, please delete the `pdo_odbc` extension and enable `swoole` and `swoole-hook-odbc`.
This extension contains an implementation of the coroutine environment for `pdo_odbc`.
## swow
1. Only PHP 8.0+ is supported.

View File

@@ -289,6 +289,7 @@ You need to specify a compilation target, choose from the following parameters:
- `--build-cli`: Build a cli sapi (command line interface, which can execute PHP code on the command line)
- `--build-fpm`: Build a fpm sapi (php-fpm, used in conjunction with other traditional fpm architecture software such as nginx)
- `--build-cgi`: Build a cgi sapi (cgi, rarely used)
- `--build-micro`: Build a micro sapi (used to build a standalone executable binary containing PHP code)
- `--build-embed`: Build an embed sapi (used to embed into other C language programs)
- `--build-frankenphp`: Build a [FrankenPHP](https://github.com/php/frankenphp) executable

View File

@@ -43,6 +43,14 @@ swoole-hook-sqlite 不是一个扩展,而是 Swoole 的 Hook 特性。
swoole-hook-sqlite 与 `pdo_sqlite` 扩展冲突。如需使用 Swoole 和 `pdo_sqlite`,请删除 pdo_sqlite 扩展,启用 `swoole``swoole-hook-sqlite` 即可。
该扩展包含了 `pdo_sqlite` 的协程环境的实现。
## swoole-hook-odbc
swoole-hook-odbc 不是一个扩展,而是 Swoole 的 Hook 特性。
如果你在编译时添加了 `swoole,swoole-hook-odbc`,你将启用 Swoole 的 `odbc` 扩展的协程模式。
swoole-hook-odbc 与 `pdo_odbc` 扩展冲突。如需使用 Swoole 和 `pdo_odbc`,请删除 `pdo_odbc` 扩展,启用 `swoole``swoole-hook-odbc` 即可。
该扩展包含了 `pdo_odbc` 的协程环境的实现。
## swow
1. swow 仅支持 PHP 8.0+ 版本。

View File

@@ -249,6 +249,7 @@ bin/spc doctor --auto-fix
- `--build-cli`: 构建一个 cli sapi命令行界面可在命令行执行 PHP 代码)
- `--build-fpm`: 构建一个 fpm sapiphp-fpm用于和其他传统的 fpm 架构的软件如 nginx 配合使用)
- `--build-cgi`: 构建一个 cgi sapicgi可用于传统的 cgi 架构的软件如 apache 配合使用)
- `--build-micro`: 构建一个 micro sapi用于构建一个包含 PHP 代码的独立可执行二进制)
- `--build-embed`: 构建一个 embed sapi用于嵌入到其他 C 语言程序中)
- `--build-frankenphp`: 构建一个 [frankenphp](https://github.com/php/frankenphp) 二进制

View File

@@ -9,6 +9,7 @@ parameters:
- '#Attribute class JetBrains\\PhpStorm\\ArrayShape does not exist#'
- '#Function Swoole\\Coroutine\\run not found.#'
- '#Static call to instance method ZM\\Logger\\ConsoleColor#'
- '#Constant GNU_ARCH not found#'
dynamicConstantNames:
- PHP_OS_FAMILY
excludePaths:

View File

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

View File

@@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\store\LockFile;
use SPC\store\pkg\GoXcaddy;
use SPC\store\SourceManager;
use SPC\store\SourcePatcher;
use SPC\util\AttributeMapper;
@@ -268,11 +269,11 @@ abstract class BuilderBase
{
$ret = [];
foreach ($this->getExts() as $ext) {
$arg = $ext->getConfigureArg();
$arg = null;
if ($ext->isBuildShared() && !$ext->isBuildStatic()) {
if (
(Config::getExt($ext->getName(), 'type') === 'builtin' &&
!file_exists(SOURCE_PATH . '/php-src/ext/' . $ext->getName() . '/config.m4')) ||
!file_exists(SOURCE_PATH . '/php-src/ext/' . $ext->getName() . '/config.m4')) ||
Config::getExt($ext->getName(), 'build-with-php') === true
) {
$arg = $ext->getConfigureArg(true);
@@ -280,6 +281,7 @@ abstract class BuilderBase
continue;
}
}
$arg ??= $ext->getConfigureArg();
logger()->info($ext->getName() . ' is using ' . $arg);
$ret[] = trim($arg);
}
@@ -391,6 +393,9 @@ abstract class BuilderBase
if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
$ls[] = 'frankenphp';
}
if (($type & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
$ls[] = 'cgi';
}
return implode(', ', $ls);
}
@@ -503,8 +508,7 @@ abstract class BuilderBase
throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!');
}
// frankenphp needs package go-xcaddy installed
$pkg_dir = PKG_ROOT_PATH . '/go-xcaddy-' . arch2gnu(php_uname('m')) . '-' . osfamily2shortname();
if (!file_exists("{$pkg_dir}/bin/go") || !file_exists("{$pkg_dir}/bin/xcaddy")) {
if (!GoXcaddy::isInstalled()) {
global $argv;
throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy");
}

View File

@@ -5,11 +5,14 @@ declare(strict_types=1);
namespace SPC\builder;
use SPC\exception\EnvironmentException;
use SPC\exception\FileSystemException;
use SPC\exception\SPCException;
use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\toolchain\ClangNativeToolchain;
use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\SPCConfigUtil;
@@ -252,12 +255,15 @@ class Extension
$order = [];
$resolve = function ($extension) use (&$resolve, &$loaded, &$order) {
if (!$extension instanceof Extension) {
return;
}
if (isset($loaded[$extension->getName()])) {
return;
}
$loaded[$extension->getName()] = true;
foreach ($this->dependencies as $dependency) {
foreach ($extension->dependencies as $dependency) {
$resolve($dependency);
}
@@ -269,6 +275,9 @@ class Extension
$ret = '';
foreach ($order as $ext) {
if ($ext instanceof self && $ext->isBuildShared()) {
if (Config::getExt($ext->getName(), 'type', false) === 'addon') {
continue;
}
if (Config::getExt($ext->getName(), 'zend-extension', false) === true) {
$ret .= " -d \"zend_extension={$ext->getName()}\"";
} else {
@@ -350,7 +359,7 @@ class Extension
/**
* Build shared extension
*/
public function buildShared(): void
public function buildShared(array $visited = []): void
{
try {
if (Config::getExt($this->getName(), 'type') === 'builtin' || Config::getExt($this->getName(), 'build-with-php') === true) {
@@ -366,17 +375,21 @@ class Extension
}
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)');
return;
}
logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)');
foreach ($this->dependencies as $dependency) {
if (!$dependency instanceof Extension) {
continue;
}
if (!$dependency->isBuildStatic()) {
if (!$dependency->isBuildStatic() && !in_array($dependency->getName(), $visited)) {
logger()->info('extension ' . $this->getName() . ' requires extension ' . $dependency->getName());
$dependency->buildShared();
$dependency->buildShared([...$visited, $this->getName()]);
}
}
if (Config::getExt($this->getName(), 'type') === 'addon') {
return;
}
match (PHP_OS_FAMILY) {
'Darwin', 'Linux' => $this->buildUnixShared(),
default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'),
@@ -408,8 +421,24 @@ class Extension
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && SPCTarget::getTargetOS() === 'Linux') {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
if (str_contains($env['LIBS'], '-lstdc++') && SPCTarget::getTargetOS() === 'Linux') {
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
} elseif (ToolchainManager::getToolchainClass() === GccNativeToolchain::class || ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) {
try {
$content = FileSystem::readFile($this->source_dir . '/config.m4');
if ($content && !str_contains($content, 'PHP_ADD_LIBRARY(stdc++')) {
$pattern = '/(PHP_NEW_EXTENSION\(' . $this->name . ',.*\))/m';
$replacement = "$1\nPHP_ADD_LIBRARY(stdc++, 1, " . strtoupper($this->name) . '_SHARED_LIBADD)';
FileSystem::replaceFileRegex(
$this->source_dir . '/config.m4',
$pattern,
$replacement
);
}
} catch (FileSystemException) {
}
}
}
if ($this->patchBeforeSharedPhpize()) {

View File

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

View File

@@ -17,6 +17,10 @@ class memcached extends Extension
'--with-libmemcached-dir=' . BUILD_ROOT_PATH . ' ' .
'--disable-memcached-sasl ' .
'--enable-memcached-json ' .
($this->builder->getLib('zstd') ? '--with-zstd ' : '') .
($this->builder->getExt('igbinary') ? '--enable-memcached-igbinary ' : '') .
($this->builder->getExt('session') ? '--enable-memcached-session ' : '') .
($this->builder->getExt('msgpack') ? '--enable-memcached-msgpack ' : '') .
'--with-system-fastlz';
}
}

View File

@@ -12,9 +12,9 @@ class mongodb extends Extension
{
public function getUnixConfigureArg(bool $shared = false): string
{
$arg = ' --enable-mongodb ';
$arg = ' --enable-mongodb' . ($shared ? '=shared' : '') . ' ';
$arg .= ' --with-mongodb-system-libs=no --with-mongodb-client-side-encryption=no ';
$arg .= ' --with-mongodb-sasl=no ';
$arg .= ' --with-mongodb-sasl=no ';
if ($this->builder->getLib('openssl')) {
$arg .= '--with-mongodb-ssl=openssl';
}
@@ -22,6 +22,6 @@ class mongodb extends Extension
$arg .= $this->builder->getLib('zstd') ? ' --with-mongodb-zstd=yes ' : ' --with-mongodb-zstd=no ';
// $arg .= $this->builder->getLib('snappy') ? ' --with-mongodb-snappy=yes ' : ' --with-mongodb-snappy=no ';
$arg .= $this->builder->getLib('zlib') ? ' --with-mongodb-zlib=yes ' : ' --with-mongodb-zlib=bundled ';
return $arg;
return clean_spaces($arg);
}
}

View File

@@ -8,6 +8,7 @@ use SPC\builder\Extension;
use SPC\exception\WrongUsageException;
use SPC\store\SourcePatcher;
use SPC\util\CustomExt;
use SPC\util\SPCTarget;
#[CustomExt('opcache')]
class opcache extends Extension
@@ -46,7 +47,18 @@ class opcache extends Extension
public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-opcache';
$phpVersionID = $this->builder->getPHPVersionID();
$opcache_jit = ' --enable-opcache-jit';
if ((SPCTarget::getTargetOS() === 'Linux' &&
SPCTarget::getLibc() === 'musl' &&
$this->builder->getOption('enable-zts') &&
arch2gnu(php_uname('m')) === 'x86_64' &&
$phpVersionID < 80500) ||
$this->builder->getOption('disable-opcache-jit')
) {
$opcache_jit = ' --disable-opcache-jit';
}
return '--enable-opcache' . ($shared ? '=shared' : '') . $opcache_jit;
}
public function getDistName(): string

View File

@@ -8,6 +8,8 @@ use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
#[CustomExt('swoole')]
class swoole extends Extension
@@ -43,32 +45,40 @@ class swoole extends Extension
public function getUnixConfigureArg(bool $shared = false): string
{
// enable swoole
$arg = '--enable-swoole';
$arg = '--enable-swoole' . ($shared ? '=shared' : '');
// commonly-used feature: coroutine-time, disable-thread-context
$arg .= ' --enable-swoole-coro-time --disable-thread-context';
// commonly used feature: coroutine-time
$arg .= ' --enable-swoole-coro-time --with-pic';
// required feature: curl, openssl (but curl hook is buggy for php 8.0)
$arg .= $this->builder->getOption('enable-zts') ? ' --enable-swoole-thread --disable-thread-context' : ' --disable-swoole-thread --enable-thread-context';
// required features: curl, openssl (but curl hook is buggy for php 8.0)
$arg .= $this->builder->getPHPVersionID() >= 80100 ? ' --enable-swoole-curl' : ' --disable-swoole-curl';
$arg .= ' --enable-openssl';
// additional feature: c-ares, brotli, nghttp2 (can be disabled, but we enable it by default in config to support full network feature)
// additional features that only require libraries
$arg .= $this->builder->getLib('libcares') ? ' --enable-cares' : '';
if (!$shared) {
$arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : '';
}
$arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : '';
$arg .= $this->builder->getLib('nghttp2') ? (' --with-nghttp2-dir=' . BUILD_ROOT_PATH) : '';
$arg .= $this->builder->getLib('zstd') ? ' --enable-zstd' : '';
$arg .= $this->builder->getLib('liburing') ? ' --enable-iouring' : '';
$arg .= $this->builder->getExt('sockets') ? ' --enable-sockets' : '';
// additional feature: swoole-pgsql, it should depend on lib [postgresql], but it will lack of CFLAGS etc.
// so this is a tricky way (enable ext [pgsql,pdo] to add postgresql hook and pdo_pgsql support)
$arg .= $this->builder->getExt('swoole-hook-pgsql') ? '' : ' --disable-swoole-pgsql';
// enable additional features that require the pdo extension, but conflict with pdo_* extensions
// to make sure everything works as it should, this is done in fake addon extensions
$arg .= $this->builder->getExt('swoole-hook-pgsql') ? ' --enable-swoole-pgsql' : ' --disable-swoole-pgsql';
$arg .= $this->builder->getExt('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd';
$arg .= $this->builder->getExt('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite';
// enable this feature , need remove pdo_sqlite
// more info : https://wenda.swoole.com/detail/109023
$arg .= $this->builder->getExt('swoole-hook-sqlite') ? '' : ' --disable-swoole-sqlite';
if ($this->builder->getExt('swoole-hook-odbc')) {
$config = (new SPCConfigUtil($this->builder, ['libs_only_deps' => true]))->config([], ['unixodbc']);
$arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"';
}
if (SPCTarget::getTargetOS() === 'Darwin') {
$arg .= ' ac_cv_lib_pthread_pthread_barrier_init=no';
}
// enable this feature , need stop pdo_*
// $arg .= $this->builder->getLib('unixodbc') ? ' --with-swoole-odbc=unixODBC,' : ' ';
return $arg;
}
}

View File

@@ -16,26 +16,15 @@ class swoole_hook_mysql extends Extension
return 'swoole';
}
public function getUnixConfigureArg(bool $shared = false): string
{
// pdo_mysql doesn't need to be disabled
// enable swoole-hook-mysql will enable mysqli, pdo, pdo_mysql, we don't need to add any additional options
return '';
}
public function runCliCheckUnix(): void
{
// skip if not enable swoole
if ($this->builder->getExt('swoole') === null) {
return;
}
[$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $this->getSharedExtensionLoadString() . ' --ri "swoole"', false);
$out = implode('', $out);
if ($ret !== 0) {
throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: 'extension swoole_hook_mysql sanity check');
}
if (!str_contains($out, 'mysqlnd')) {
throw new ValidationException('swoole mysql hook is not enabled correctly.', validation_module: 'Extension swoole mysql hook avilability check');
throw new ValidationException('swoole mysql hook is not enabled correctly.', validation_module: 'Extension swoole mysql hook availability check');
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException;
use SPC\util\CustomExt;
#[CustomExt('swoole-hook-odbc')]
class swoole_hook_odbc extends Extension
{
public function getDistName(): string
{
return 'swoole';
}
public function validate(): void
{
// pdo_pgsql need to be disabled
if ($this->builder->getExt('pdo_odbc')?->isBuildStatic()) {
throw new WrongUsageException('swoole-hook-odbc provides pdo_odbc, if you enable odbc hook for swoole, you must remove pdo_odbc extension.');
}
}
public function runCliCheckUnix(): void
{
$sharedExtensions = $this->getSharedExtensionLoadString();
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"', false);
$out = implode('', $out);
if ($ret !== 0) {
throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check");
}
if (!str_contains($out, 'coroutine_odbc')) {
throw new ValidationException('swoole odbc hook is not enabled correctly.', validation_module: 'Extension swoole odbc hook availability check');
}
}
}

View File

@@ -20,25 +20,15 @@ class swoole_hook_pgsql extends Extension
public function validate(): void
{
// pdo_pgsql need to be disabled
if ($this->builder->getExt('pdo_pgsql') !== null) {
if ($this->builder->getExt('pdo_pgsql')?->isBuildStatic()) {
throw new WrongUsageException('swoole-hook-pgsql provides pdo_pgsql, if you enable pgsql hook for swoole, you must remove pdo_pgsql extension.');
}
}
public function getUnixConfigureArg(bool $shared = false): string
{
// enable swoole pgsql hook
return '--enable-swoole-pgsql';
}
public function runCliCheckUnix(): void
{
// skip if not enable swoole
if ($this->builder->getExt('swoole') === null) {
return;
}
$sharedExtensions = $this->getSharedExtensionLoadString();
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"', false);
$out = implode('', $out);
if ($ret !== 0) {
throw new ValidationException(

View File

@@ -20,25 +20,15 @@ class swoole_hook_sqlite extends Extension
public function validate(): void
{
// pdo_pgsql need to be disabled
if ($this->builder->getExt('pdo_sqlite') !== null) {
if ($this->builder->getExt('pdo_sqlite')?->isBuildStatic()) {
throw new WrongUsageException('swoole-hook-sqlite provides pdo_sqlite, if you enable sqlite hook for swoole, you must remove pdo_sqlite extension.');
}
}
public function getUnixConfigureArg(bool $shared = false): string
{
// enable swoole pgsql hook
return '--enable-swoole-sqlite';
}
public function runCliCheckUnix(): void
{
// skip if not enable swoole
if ($this->builder->getExt('swoole') === null) {
return;
}
$sharedExtensions = $this->getSharedExtensionLoadString();
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"', false);
$out = implode('', $out);
if ($ret !== 0) {
throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check");

View File

@@ -25,10 +25,6 @@ class BSDBuilder extends UnixBuilderBase
f_putenv('CXX=' . $this->getOption('cxx', 'clang++'));
// set PATH
f_putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
// set PKG_CONFIG
f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config');
// set PKG_CONFIG_PATH
f_putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig/');
// set arch (default: current)
$this->setOptionIfNotExist('arch', php_uname('m'));
@@ -122,6 +118,11 @@ class BSDBuilder extends UnixBuilderBase
}
$this->buildEmbed();
}
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...');
$this->buildSharedExts();
}
if ($enableFrankenphp) {
logger()->info('building frankenphp');
$this->buildFrankenphp();

View File

@@ -64,10 +64,7 @@ class LinuxBuilder extends UnixBuilderBase
// if opcache_jit is enabled for 8.5 or opcache enabled,
// we need to disable undefined behavior sanitizer.
f_putenv('SPC_COMPILER_EXTRA=-fno-sanitize=undefined');
} elseif ($opcache_jit) {
$opcache_jit = false;
}
$opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit ' : '--disable-opcache-jit ';
if ($this->getOption('enable-zts', false)) {
$maxExecutionTimers = $phpVersionID >= 80100 ? '--enable-zend-max-execution-timers ' : '';
@@ -87,6 +84,7 @@ class LinuxBuilder extends UnixBuilderBase
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
// prepare build php envs
// $musl_flag = SPCTarget::getLibc() === 'musl' ? ' -D__MUSL__' : ' -U__MUSL__';
@@ -105,22 +103,21 @@ class LinuxBuilder extends UnixBuilderBase
);
}
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
$php_configure_env . ' ' .
$this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
$php_configure_env . ' ' .
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_path .
$config_file_scan_dir .
$opcache_jit_arg .
$json_74 .
$zts .
$maxExecutionTimers .
$this->makeStaticExtensionArgs() . ' '
);
));
$this->emitPatchPoint('before-php-make');
SourcePatcher::patchBeforeMake($this);
@@ -135,6 +132,10 @@ class LinuxBuilder extends UnixBuilderBase
logger()->info('building fpm');
$this->buildFpm();
}
if ($enableCgi) {
logger()->info('building cgi');
$this->buildCgi();
}
if ($enableMicro) {
logger()->info('building micro');
$this->buildMicro();
@@ -150,6 +151,11 @@ class LinuxBuilder extends UnixBuilderBase
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...');
$this->buildSharedExts();
}
}
public function testPHP(int $build_target = BUILD_TARGET_NONE)
@@ -180,6 +186,25 @@ class LinuxBuilder extends UnixBuilderBase
$this->deployBinary(BUILD_TARGET_CLI);
}
protected function buildCgi(): void
{
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cgi");
if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cgi')->exec('strip --strip-unneeded php-cgi');
}
if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cgi')
->exec(getenv('UPX_EXEC') . ' --best php-cgi');
}
$this->deployBinary(BUILD_TARGET_CGI);
}
/**
* Build phpmicro sapi
*/
@@ -300,7 +325,7 @@ class LinuxBuilder extends UnixBuilderBase
$target = "{$libDir}/{$realLibName}";
if (file_exists($target)) {
[, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target));
$output = join("\n", $output);
$output = implode("\n", $output);
if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) {
$currentSoname = $sonameMatch[1];
if ($currentSoname !== basename($target)) {
@@ -316,6 +341,8 @@ class LinuxBuilder extends UnixBuilderBase
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');
// export dynamic symbols
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
}
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
class liburing extends LinuxLibraryBase
{
public const NAME = 'liburing';
public function patchBeforeBuild(): bool
{
if (!SystemUtil::isMuslDist()) {
return false;
}
FileSystem::replaceFileStr($this->source_dir . '/configure', 'realpath -s', 'realpath');
return true;
}
protected function build(): void
{
$use_libc = SPCTarget::getLibc() !== 'glibc' || version_compare(SPCTarget::getLibcVersion(), '2.30', '>=');
$make = UnixAutoconfExecutor::create($this);
if (!$use_libc) {
$make->appendEnv([
'CC' => 'gcc', // libc-less version fails to compile with clang or zig
'CXX' => 'g++',
'AR' => 'ar',
'LD' => 'ld',
]);
} else {
$make->appendEnv([
'CFLAGS' => '-D_GNU_SOURCE',
]);
}
$make
->removeConfigureArgs(
'--disable-shared',
'--enable-static',
'--with-pic',
'--enable-pic',
)
->addConfigureArgs(
$use_libc ? '--use-libc' : '',
)
->configure()
->make('library', 'install ENABLE_SHARED=0', with_clean: false);
$this->patchPkgconfPrefix();
}
}

View File

@@ -90,13 +90,6 @@ class MacOSBuilder extends UnixBuilderBase
$json_74 = $phpVersionID < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : '';
$opcache_jit = !$this->getOption('disable-opcache-jit', false);
// disable opcache jit for PHP < 8.5.0 when opcache is not enabled
if ($opcache_jit && $phpVersionID < 80500 && !$this->getExt('opcache')) {
$opcache_jit = false;
}
$opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit ' : '--disable-opcache-jit ';
$config_file_path = $this->getOption('with-config-file-path', false) ?
('--with-config-file-path=' . $this->getOption('with-config-file-path') . ' ') : '';
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
@@ -107,6 +100,7 @@ class MacOSBuilder extends UnixBuilderBase
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
// prepare build php envs
$envs_build_php = SystemUtil::makeEnvVarString([
@@ -124,21 +118,20 @@ class MacOSBuilder extends UnixBuilderBase
}
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
$this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$opcache_jit_arg .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_path .
$config_file_scan_dir .
$json_74 .
$zts .
$this->makeStaticExtensionArgs() . ' ' .
$envs_build_php
);
));
$this->emitPatchPoint('before-php-make');
SourcePatcher::patchBeforeMake($this);
@@ -153,6 +146,10 @@ class MacOSBuilder extends UnixBuilderBase
logger()->info('building fpm');
$this->buildFpm();
}
if ($enableCgi) {
logger()->info('building cgi');
$this->buildCgi();
}
if ($enableMicro) {
logger()->info('building micro');
$this->buildMicro();
@@ -168,6 +165,11 @@ class MacOSBuilder extends UnixBuilderBase
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...');
$this->buildSharedExts();
}
}
public function testPHP(int $build_target = BUILD_TARGET_NONE)
@@ -192,6 +194,19 @@ class MacOSBuilder extends UnixBuilderBase
$this->deployBinary(BUILD_TARGET_CLI);
}
protected function buildCgi(): void
{
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$shell = shell()->cd(SOURCE_PATH . '/php-src');
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make';
$shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cgi");
if (!$this->getOption('no-strip', false)) {
$shell->exec('dsymutil -f sapi/cgi/php-cgi')->exec('strip -S sapi/cgi/php-cgi');
}
$this->deployBinary(BUILD_TARGET_CGI);
}
/**
* Build phpmicro sapi
*/
@@ -255,6 +270,8 @@ class MacOSBuilder extends UnixBuilderBase
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');
// export dynamic symbols
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
}
$this->patchPhpScripts();
}

View File

@@ -4,21 +4,101 @@ declare(strict_types=1);
namespace SPC\builder\traits;
/**
* Unix 系统的工具函数 Trait适用于 Linux、macOS
*/
use SPC\exception\ExecutionException;
use SPC\exception\SPCInternalException;
use SPC\exception\WrongUsageException;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\SPCTarget;
trait UnixSystemUtilTrait
{
/**
* @param string $name 命令名称
* @param array $paths 寻找的目标路径(如果不传入,则使用环境变量 PATH
* @return null|string 找到了返回命令路径,找不到返回 null
* Export static library dynamic symbols to a .dynsym file.
* It will export to "/path/to/libxxx.a.dynsym".
*
* @param string $lib_file Static library file path (e.g. /path/to/libxxx.a)
*/
public static function exportDynamicSymbols(string $lib_file): void
{
// check
if (!is_file($lib_file)) {
throw new WrongUsageException("The lib archive file {$lib_file} does not exist, please build it first.");
}
// shell out
$cmd = 'nm -g --defined-only -P ' . escapeshellarg($lib_file);
$result = shell()->execWithResult($cmd);
if ($result[0] !== 0) {
throw new ExecutionException($cmd, 'Failed to get defined symbols from ' . $lib_file);
}
// parse shell output and filter
$defined = [];
foreach ($result[1] as $line) {
$line = trim($line);
if ($line === '' || str_ends_with($line, '.o:') || str_ends_with($line, '.o]:')) {
continue;
}
$name = strtok($line, " \t");
if (!$name) {
continue;
}
$name = preg_replace('/@.*$/', '', $name);
if ($name !== '' && $name !== false) {
$defined[] = $name;
}
}
$defined = array_unique($defined);
sort($defined);
// export
if (SPCTarget::getTargetOS() === 'Linux') {
file_put_contents("{$lib_file}.dynsym", "{\n" . implode("\n", array_map(fn ($x) => " {$x};", $defined)) . "};\n");
} else {
file_put_contents("{$lib_file}.dynsym", implode("\n", $defined) . "\n");
}
}
/**
* Get linker flag to export dynamic symbols from a static library.
*
* @param string $lib_file Static library file path (e.g. /path/to/libxxx.a)
* @return null|string Linker flag to export dynamic symbols, null if no .dynsym file found
*/
public static function getDynamicExportedSymbols(string $lib_file): ?string
{
$symbol_file = "{$lib_file}.dynsym";
if (!is_file($symbol_file)) {
self::exportDynamicSymbols($lib_file);
}
if (!is_file($symbol_file)) {
throw new SPCInternalException("The symbol file {$symbol_file} does not exist, please check if nm command is available.");
}
// https://github.com/ziglang/zig/issues/24662
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
return '-Wl,--export-dynamic';
}
// macOS
if (SPCTarget::getTargetOS() !== 'Linux') {
return "-Wl,-exported_symbols_list,{$symbol_file}";
}
return "-Wl,--dynamic-list={$symbol_file}";
}
/**
* Find a command in given paths or system PATH.
* If $name is an absolute path, check if it exists.
*
* @param string $name Command name or absolute path
* @param array $paths Paths to search, if empty, use system PATH
* @return null|string Absolute path of the command if found, null otherwise
*/
public static function findCommand(string $name, array $paths = []): ?string
{
if (!$paths) {
$paths = explode(PATH_SEPARATOR, getenv('PATH'));
}
if (str_starts_with($name, '/')) {
return file_exists($name) ? $name : null;
}
foreach ($paths as $path) {
if (file_exists($path . DIRECTORY_SEPARATOR . $name)) {
return $path . DIRECTORY_SEPARATOR . $name;
@@ -28,6 +108,8 @@ trait UnixSystemUtilTrait
}
/**
* Make environment variable string for shell command.
*
* @param array $vars Variables, like: ["CFLAGS" => "-Ixxx"]
* @return string like: CFLAGS="-Ixxx"
*/

View File

@@ -5,14 +5,16 @@ declare(strict_types=1);
namespace SPC\builder\unix;
use SPC\builder\BuilderBase;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\exception\SPCException;
use SPC\exception\SPCInternalException;
use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\CurlHook;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\pkg\GoXcaddy;
use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\ToolchainManager;
use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
@@ -119,6 +121,16 @@ abstract class UnixBuilderBase extends BuilderBase
}
}
// sanity check for php-cgi
if (($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
logger()->info('running cgi sanity check');
[$ret, $output] = shell()->execWithResult("echo '<?php echo \"<h1>Hello, World!</h1>\";' | " . BUILD_BIN_PATH . '/php-cgi -n');
$raw_output = implode('', $output);
if ($ret !== 0 || !str_contains($raw_output, 'Hello, World!') || !str_contains($raw_output, 'text/html')) {
throw new ValidationException("cgi failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cgi sanity check');
}
}
// sanity check for embed
if (($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
logger()->info('running embed sanity check');
@@ -135,6 +147,7 @@ abstract class UnixBuilderBase extends BuilderBase
if (SPCTarget::isStatic()) {
$lens .= ' -static';
}
$dynamic_exports = '';
// if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
if (PHP_OS_FAMILY === 'Darwin') {
@@ -149,8 +162,13 @@ abstract class UnixBuilderBase extends BuilderBase
foreach (glob(BUILD_LIB_PATH . "/libphp*.{$suffix}") as $file) {
unlink($file);
}
// calling linux system util in other unix OS is okay
if ($dynamic_exports = LinuxSystemUtil::getDynamicExportedSymbols(BUILD_LIB_PATH . '/libphp.a')) {
$dynamic_exports = ' ' . $dynamic_exports;
}
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
$cc = getenv('CC');
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult("{$cc} -o embed embed.c {$lens} {$dynamic_exports}");
if ($ret !== 0) {
throw new ValidationException(
'embed failed sanity check: build failed. Error message: ' . implode("\n", $out),
@@ -200,6 +218,7 @@ abstract class UnixBuilderBase extends BuilderBase
BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php',
BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx',
BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm',
BUILD_TARGET_CGI => SOURCE_PATH . '/php-src/sapi/cgi/php-cgi',
default => throw new SPCInternalException("Deployment does not accept type {$type}"),
};
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
@@ -248,6 +267,7 @@ abstract class UnixBuilderBase extends BuilderBase
protected function buildFrankenphp(): void
{
GlobalEnvManager::addPathIfNotExists(GoXcaddy::getPath());
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
@@ -259,45 +279,49 @@ abstract class UnixBuilderBase extends BuilderBase
logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.');
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
}
$lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : '';
$releaseInfo = json_decode(Downloader::curlExec(
'https://api.github.com/repos/php/frankenphp/releases/latest',
hooks: [[CurlHook::class, 'setupGithubToken']],
), true);
$frankenPhpVersion = $releaseInfo['tag_name'];
[, $out] = shell()->execWithResult('go list -m github.com/dunglas/frankenphp@latest');
$frankenPhpVersion = str_replace('github.com/dunglas/frankenphp v', '', $out[0]);
$libphpVersion = $this->getPHPVersion();
$dynamic_exports = '';
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion);
$libphpVersion = preg_replace('/\.\d+$/', '', $libphpVersion);
} else {
if ($dynamicSymbolsArgument = LinuxSystemUtil::getDynamicExportedSymbols(BUILD_LIB_PATH . '/libphp.a')) {
$dynamic_exports = ' ' . $dynamicSymbolsArgument;
}
}
$debugFlags = $this->getOption('no-strip') ? '-w -s ' : '';
$extLdFlags = "-extldflags '-pie'";
$extLdFlags = "-extldflags '-pie{$dynamic_exports}'";
$muslTags = '';
$staticFlags = '';
if (SPCTarget::isStatic()) {
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports}'";
$muslTags = 'static_build,';
$staticFlags = '-static-pie';
}
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list);
$env = [
$cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS');
$libs = $config['libs'];
$libs .= PHP_OS_FAMILY === 'Linux' ? ' -lrt' : '';
// Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix.
if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') ||
str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), '-fprofile')) &&
ToolchainManager::getToolchainClass() === GccNativeToolchain::class) {
$cflags .= ' -Wno-error=missing-profile';
$libs .= ' -lgcov';
}
$env = [...[
'CGO_ENABLED' => '1',
'CGO_CFLAGS' => $config['cflags'],
'CGO_LDFLAGS' => "{$staticFlags} {$config['ldflags']} {$config['libs']} {$lrt}",
'CGO_CFLAGS' => clean_spaces($cflags),
'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}",
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
"{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
"v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
foreach (GoXcaddy::getEnvironment() as $key => $value) {
if ($key === 'PATH') {
GlobalEnvManager::addPathIfNotExists($value);
} else {
$env[$key] = $value;
}
}
], ...GoXcaddy::getEnvironment()];
shell()->cd(BUILD_BIN_PATH)
->setEnv($env)
->exec("xcaddy build --output frankenphp {$xcaddyModules}");
@@ -310,4 +334,20 @@ abstract class UnixBuilderBase extends BuilderBase
}
}
}
/**
* Seek php-src/config.log when building PHP, add it to exception.
*/
protected function seekPhpSrcLogFileOnException(callable $callback): void
{
try {
$callback();
} catch (SPCException $e) {
if (file_exists(SOURCE_PATH . '/php-src/config.log')) {
$e->addExtraLogFile('php-src config.log', 'php-src.config.log');
copy(SOURCE_PATH . '/php-src/config.log', SPC_LOGS_DIR . '/php-src.config.log');
}
throw $e;
}
}
}

View File

@@ -20,17 +20,17 @@ trait libxml2
"-DZLIB_INCLUDE_DIR={$this->getIncludeDir()}",
'-DLIBXML2_WITH_ZLIB=OFF',
)
->optionalLib('icu', ...cmake_boolean_args('LIBXML2_WITH_ICU'))
->optionalLib('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA'))
->addConfigureArgs(
'-DLIBXML2_WITH_ICONV=ON',
'-DLIBXML2_WITH_ICU=OFF', // optional, but discouraged: https://gitlab.gnome.org/GNOME/libxml2/-/blob/master/README.md
'-DLIBXML2_WITH_PYTHON=OFF',
'-DLIBXML2_WITH_PROGRAMS=OFF',
'-DLIBXML2_WITH_TESTS=OFF',
);
if ($this instanceof LinuxLibraryBase) {
$cmake->addConfigureArgs('-DIconv_IS_BUILD_IN=OFF');
$cmake->addConfigureArgs('-DIconv_IS_BUILT_IN=OFF');
}
$cmake->build();

View File

@@ -4,18 +4,29 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\WrongUsageException;
use SPC\util\executor\UnixAutoconfExecutor;
trait unixodbc
{
protected function build(): void
{
$sysconf_selector = match (PHP_OS_FAMILY) {
'Darwin' => match (GNU_ARCH) {
'x86_64' => '/usr/local/etc',
'aarch64' => '/opt/homebrew/etc',
default => throw new WrongUsageException('Unsupported architecture: ' . GNU_ARCH),
},
'Linux' => '/etc',
default => throw new WrongUsageException('Unsupported OS: ' . PHP_OS_FAMILY),
};
UnixAutoconfExecutor::create($this)
->configure(
'--disable-debug',
'--disable-dependency-tracking',
"--with-libiconv-prefix={$this->getBuildRootPath()}",
'--with-included-ltdl',
"--sysconfdir={$sysconf_selector}",
'--enable-gui=no',
)
->make();

View File

@@ -33,6 +33,7 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
$this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
$this->addOption('build-cgi', null, null, 'Build cgi SAPI (not available on Windows)');
$this->addOption('build-all', null, null, 'Build all SAPI');
$this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
@@ -211,12 +212,6 @@ class BuildPHPCommand extends BuildCommand
// start to build
$builder->buildPHP($rule);
// build dynamic extensions if needed
if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...');
$builder->buildSharedExts();
}
$builder->testPHP($rule);
// compile stopwatch :P
@@ -252,7 +247,7 @@ class BuildPHPCommand extends BuildCommand
$path = FileSystem::convertPath("{$build_root_path}/modules/{$ext}.so");
if (file_exists(BUILD_MODULES_PATH . "/{$ext}.so")) {
logger()->info("Shared extension [{$ext}] path{$fixed}: {$path}");
} else {
} elseif (Config::getExt($ext, 'type') !== 'addon') {
logger()->warning("Shared extension [{$ext}] not found, please check!");
}
}
@@ -280,6 +275,7 @@ class BuildPHPCommand extends BuildCommand
$rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
$rule |= $this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE;
$rule |= ($this->getOption('build-frankenphp') ? (BUILD_TARGET_FRANKENPHP | BUILD_TARGET_EMBED) : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-cgi') ? BUILD_TARGET_CGI : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
return $rule;
}

View File

@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace SPC\command;
use SPC\exception\ValidationException;
use SPC\store\pkg\GoXcaddy;
use SPC\store\pkg\Zig;
use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain;
use SPC\util\ConfigValidator;
@@ -63,7 +65,7 @@ class CraftCommand extends BuildCommand
}
}
// install go and xcaddy for frankenphp
if (in_array('frankenphp', $craft['sapi'])) {
if (in_array('frankenphp', $craft['sapi']) && !GoXcaddy::isInstalled()) {
$retcode = $this->runCommand('install-pkg', 'go-xcaddy');
if ($retcode !== 0) {
$this->output->writeln('<error>craft go-xcaddy failed</error>');
@@ -71,7 +73,7 @@ class CraftCommand extends BuildCommand
}
}
// install zig if requested
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && !Zig::isInstalled()) {
$retcode = $this->runCommand('install-pkg', 'zig');
if ($retcode !== 0) {
$this->output->writeln('<error>craft zig failed</error>');

View File

@@ -40,7 +40,7 @@ class LinuxToolCheckList
'tar', 'unzip', 'gzip', 'gcc',
'bzip2', 'cmake', 'patch', 'which',
'xz', 'libtool', 'gettext-devel',
'perl', 'patchelf',
'patchelf',
];
public const TOOLS_ARCH = [
@@ -48,10 +48,13 @@ class LinuxToolCheckList
];
private const PROVIDED_COMMAND = [
'perl' => '/usr/share/perl5/FindBin.pm',
'binutils-gold' => 'ld.gold',
'base-devel' => 'automake',
'gettext-devel' => 'gettextize',
'gettext-dev' => 'gettextize',
'perl-IPC-Cmd' => '/usr/share/perl5/vendor_perl/IPC/Cmd.pm',
'perl-Time-Piece' => '/usr/lib64/perl5/Time/Piece.pm',
];
/** @noinspection PhpUnused */
@@ -63,7 +66,7 @@ class LinuxToolCheckList
$required = match ($distro['dist']) {
'alpine' => self::TOOLS_ALPINE,
'redhat' => self::TOOLS_RHEL,
'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd']),
'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd', 'perl-Time-Piece']),
'arch' => self::TOOLS_ARCH,
default => self::TOOLS_DEBIAN,
};

View File

@@ -0,0 +1,50 @@
<?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\store\PackageManager;
use SPC\util\PkgConfigUtil;
#[OptionalCheck([self::class, 'optionalCheck'])]
class PkgConfigCheck
{
public static function optionalCheck(): bool
{
return PHP_OS_FAMILY !== 'Windows';
}
/** @noinspection PhpUnused */
#[AsCheckItem('if pkg-config is installed or built', level: 800)]
public function checkPkgConfig(): CheckResult
{
if (!($pkgconf = PkgConfigUtil::findPkgConfig())) {
return CheckResult::fail('pkg-config is not installed', 'install-pkgconfig');
}
return CheckResult::ok($pkgconf);
}
#[AsCheckItem('if pkg-config is working', level: 799)]
public function checkPkgConfigFunctional(): CheckResult
{
$pkgconf = PkgConfigUtil::findPkgConfig();
// check if pkg-config is functional
[$ret, $output] = shell()->execWithResult("{$pkgconf} --version", false);
if ($ret === 0) {
return CheckResult::ok(implode(' ', $output));
}
return CheckResult::fail('pkg-config is not functional', 'install-pkgconfig');
}
#[AsFixItem('install-pkgconfig')]
public function installPkgConfig(): bool
{
PackageManager::installPackage('pkg-config');
return true;
}
}

View File

@@ -14,9 +14,9 @@ use SPC\builder\Extension;
*/
class ValidationException extends SPCException
{
private null|array|string $validation_module = null;
private array|string|null $validation_module = null;
public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null, null|array|string $validation_module = null)
public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null, array|string|null $validation_module = null)
{
parent::__construct($message, $code, $previous);

View File

@@ -274,7 +274,7 @@ class FileSystem
public static function convertWinPathToMinGW(string $path): string
{
if (preg_match('/^[A-Za-z]:/', $path)) {
$path = '/' . strtolower(substr($path, 0, 1)) . '/' . str_replace('\\', '/', substr($path, 2));
$path = '/' . strtolower($path[0]) . '/' . str_replace('\\', '/', substr($path, 2));
}
return $path;
}
@@ -314,8 +314,13 @@ class FileSystem
$sub_file = self::convertPath($dir . '/' . $v);
if (is_dir($sub_file) && $recursive) {
# 如果是 目录 且 递推 , 则递推添加下级文件
$list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative));
} elseif (is_file($sub_file) || is_dir($sub_file) && !$recursive && $include_dir) {
$sub_list = self::scanDirFiles($sub_file, $recursive, $relative);
if (is_array($sub_list)) {
foreach ($sub_list as $item) {
$list[] = $item;
}
}
} elseif (is_file($sub_file) || (is_dir($sub_file) && !$recursive && $include_dir)) {
# 如果是 文件 或 (是 目录 且 不递推 且 包含目录)
if (is_string($relative) && mb_strpos($sub_file, $relative) === 0) {
$list[] = ltrim(mb_substr($sub_file, mb_strlen($relative)), '/\\');
@@ -440,7 +445,7 @@ class FileSystem
public static function writeFile(string $path, mixed $content, ...$args): bool|int|string
{
$dir = pathinfo(self::convertPath($path), PATHINFO_DIRNAME);
if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
throw new FileSystemException('Write file failed, cannot create parent directory: ' . $dir);
}
return file_put_contents($path, $content, ...$args);

View File

@@ -30,6 +30,7 @@ class SourcePatcher
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']);
FileSystem::addSourceExtractHook('ext-imagick', [__CLASS__, 'patchImagickWith84']);
FileSystem::addSourceExtractHook('libaom', [__CLASS__, 'patchLibaomForAlpine']);
FileSystem::addSourceExtractHook('pkg-config', [__CLASS__, 'patchPkgConfigForGcc15']);
FileSystem::addSourceExtractHook('attr', [__CLASS__, 'patchAttrForAlpine']);
FileSystem::addSourceExtractHook('gmssl', [__CLASS__, 'patchGMSSL']);
}
@@ -155,6 +156,7 @@ class SourcePatcher
$spc_micro_patches = getenv('SPC_MICRO_PATCHES');
$spc_micro_patches = $spc_micro_patches === false ? [] : explode(',', $spc_micro_patches);
}
$spc_micro_patches = array_filter($spc_micro_patches, fn ($item) => trim((string) $item) !== '');
$patch_list = $spc_micro_patches;
$patches = [];
$serial = ['80', '81', '82', '83', '84', '85'];
@@ -269,7 +271,12 @@ class SourcePatcher
if ($version === '5.1.3') {
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');
if (version_compare($version, '6.0.0', '>=') && version_compare($version, '6.1.0', '<')) {
// remove when https://github.com/swoole/swoole-src/pull/5848 is merged
self::patchFile('swoole_fix_date_time.patch', SOURCE_PATH . '/php-src/ext/swoole');
// remove when https://github.com/swoole/swoole-src/pull/5847 is merged
self::patchFile('swoole_fix_odbclibs.patch', SOURCE_PATH . '/php-src/ext/swoole');
}
return true;
}
@@ -493,6 +500,12 @@ class SourcePatcher
return true;
}
public static function patchPkgConfigForGcc15(): bool
{
self::patchFile('pkg-config_gcc15.patch', SOURCE_PATH . '/pkg-config');
return true;
}
public static function patchLibaomForAlpine(): bool
{
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) {

View File

@@ -30,10 +30,14 @@ abstract class CustomPackage
/**
* Get the environment variables this package needs to be usable.
* PATH needs to be appended, rather than replaced.
*/
abstract public static function getEnvironment(): array;
/**
* Get the PATH required to use this package.
*/
abstract public static function getPath(): ?string;
abstract public static function isInstalled(): bool;
/**

View File

@@ -13,18 +13,7 @@ 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}";
$folder = PKG_ROOT_PATH . '/go-xcaddy';
return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy");
}
@@ -59,7 +48,7 @@ class GoXcaddy extends CustomPackage
'macos' => 'darwin',
default => throw new \InvalidArgumentException('Unsupported OS: ' . $name),
};
$go_version = '1.24.4';
$go_version = '1.25.0';
$config = [
'type' => 'url',
'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz",
@@ -70,15 +59,15 @@ class GoXcaddy extends CustomPackage
public function extract(string $name): void
{
$pkgroot = PKG_ROOT_PATH;
$go_exec = "{$pkgroot}/{$name}/bin/go";
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy";
$go_exec = "{$pkgroot}/go-xcaddy/bin/go";
$xcaddy_exec = "{$pkgroot}/go-xcaddy/bin/xcaddy";
if (file_exists($go_exec) && file_exists($xcaddy_exec)) {
return;
}
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = $lock[$name]['move_path'] ?? "{$pkgroot}/{$name}";
$extract = $lock[$name]['move_path'] ?? "{$pkgroot}/go-xcaddy";
FileSystem::extractPackage($name, $source_type, $filename, $extract);
@@ -91,9 +80,9 @@ class GoXcaddy extends CustomPackage
// install xcaddy without using musl tools, xcaddy build requires dynamic linking
shell()
->appendEnv([
'PATH' => "{$pkgroot}/{$name}/bin:" . $sanitizedPath,
'GOROOT' => "{$pkgroot}/{$name}",
'GOBIN' => "{$pkgroot}/{$name}/bin",
'PATH' => "{$pkgroot}/go-xcaddy/bin:" . $sanitizedPath,
'GOROOT' => "{$pkgroot}/go-xcaddy",
'GOBIN' => "{$pkgroot}/go-xcaddy/bin",
'GOPATH' => "{$pkgroot}/go",
])
->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
@@ -101,22 +90,17 @@ class GoXcaddy extends CustomPackage
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}";
$packageName = 'go-xcaddy';
$pkgroot = PKG_ROOT_PATH;
return [
'PATH' => "{$pkgroot}/{$packageName}/bin",
'GOROOT' => "{$pkgroot}/{$packageName}",
'GOBIN' => "{$pkgroot}/{$packageName}/bin",
'GOPATH' => "{$pkgroot}/go",
];
}
public static function getPath(): ?string
{
return PKG_ROOT_PATH . '/go-xcaddy/bin';
}
}

View File

@@ -103,7 +103,7 @@ class Zig extends CustomPackage
public function extract(string $name): void
{
$pkgroot = PKG_ROOT_PATH;
$zig_bin_dir = "{$pkgroot}/{$name}";
$zig_bin_dir = "{$pkgroot}/zig";
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
$all_exist = true;
@@ -120,7 +120,7 @@ class Zig extends CustomPackage
$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}";
$extract = "{$pkgroot}/zig";
FileSystem::extractPackage($name, $source_type, $filename, $extract);
@@ -129,34 +129,12 @@ class Zig extends CustomPackage
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,
];
return [];
}
private static function getPath(): string
public 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}";
return PKG_ROOT_PATH . '/zig';
}
private function createZigCcScript(string $bin_dir): void

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../buildroot/include" 2>/dev/null || true)"
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../../buildroot/include" 2>/dev/null || true)"
PARSED_ARGS=()
while [[ $# -gt 0 ]]; do

View File

@@ -45,7 +45,7 @@ class ClangNativeToolchain implements ToolchainInterface
$compiler = getenv('CC') ?: 'clang';
$version = shell(false)->execWithResult("{$compiler} --version", false);
$head = pathinfo($compiler, PATHINFO_BASENAME);
if ($version[0] === 0 && preg_match('/clang version (\d+.\d+.\d+)/', $version[1][0], $match)) {
if ($version[0] === 0 && preg_match('/clang version (\d+\.\d+\.\d+)/', $version[1][0], $match)) {
return "{$head} {$match[1]}";
}
return $head;

View File

@@ -42,7 +42,7 @@ class GccNativeToolchain implements ToolchainInterface
$compiler = getenv('CC') ?: 'gcc';
$version = shell(false)->execWithResult("{$compiler} --version", false);
$head = pathinfo($compiler, PATHINFO_BASENAME);
if ($version[0] === 0 && preg_match('/gcc.*(\d+.\d+.\d+)/', $version[1][0], $match)) {
if ($version[0] === 0 && preg_match('/gcc.*?(\d+\.\d+\.\d+)/', $version[1][0], $match)) {
return "{$head} {$match[1]}";
}
return $head;

View File

@@ -7,6 +7,7 @@ namespace SPC\toolchain;
use SPC\builder\linux\SystemUtil;
use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager;
use SPC\util\PkgConfigUtil;
use SPC\util\SPCTarget;
class ToolchainManager
@@ -56,6 +57,15 @@ class ToolchainManager
if (SPCTarget::getLibc() === 'glibc' && SystemUtil::isMuslDist()) {
throw new WrongUsageException('You are linking against glibc dynamically, which is only supported on glibc distros.');
}
// init pkg-config for unix
if (is_unix()) {
if (($found = PkgConfigUtil::findPkgConfig()) === null) {
throw new WrongUsageException('Cannot find pkg-config executable. Please run `doctor` to fix this.');
}
GlobalEnvManager::putenv("PKG_CONFIG={$found}");
}
$toolchain = getenv('SPC_TOOLCHAIN');
/* @var ToolchainInterface $toolchain */
$instance = new $toolchain();

View File

@@ -42,10 +42,10 @@ class ZigToolchain implements ToolchainInterface
public function afterInit(): void
{
if (!is_dir(Zig::getEnvironment()['PATH'])) {
if (!Zig::isInstalled()) {
throw new EnvironmentException('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']);
GlobalEnvManager::addPathIfNotExists(Zig::getPath());
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') ?: '';

View File

@@ -39,8 +39,8 @@ class GlobalEnvManager
// Define env vars for unix
if (is_unix()) {
self::addPathIfNotExists(BUILD_BIN_PATH);
self::putenv('PKG_CONFIG=' . BUILD_BIN_PATH . '/pkg-config');
self::putenv('PKG_CONFIG_PATH=' . BUILD_ROOT_PATH . '/lib/pkgconfig');
self::addPathIfNotExists(PKG_ROOT_PATH . '/bin');
self::putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig');
}
$ini = self::readIniFile();

View File

@@ -117,7 +117,7 @@ class LicenseDumper
/**
* Loads a source license file from the specified path.
*/
private function loadSourceFile(string $source_name, int $index, null|array|string $in_path, ?string $custom_base_path = null): string
private function loadSourceFile(string $source_name, int $index, array|string|null $in_path, ?string $custom_base_path = null): string
{
if (is_null($in_path)) {
throw new SPCInternalException("source [{$source_name}] license file is not set, please check config/source.json");

View File

@@ -14,6 +14,28 @@ use SPC\exception\ExecutionException;
*/
class PkgConfigUtil
{
/**
* Find the pkg-config executable which is compatible with static builds.
*
* @return null|string Path to pkg-config executable, or null if not found
*/
public static function findPkgConfig(): ?string
{
// Find pkg-config executable
$find_list = [
PKG_ROOT_PATH . '/bin/pkg-config',
BUILD_BIN_PATH . '/pkg-config',
];
$found = null;
foreach ($find_list as $file) {
if (file_exists($file) && is_executable($file)) {
$found = $file;
break;
}
}
return $found;
}
/**
* Returns the version of a module.
* This method uses `pkg-config --modversion` to get the version of the specified module.

View File

@@ -52,6 +52,15 @@ class SPCConfigUtil
*/
public function config(array $extensions = [], array $libraries = [], bool $include_suggest_ext = false, bool $include_suggest_lib = false): array
{
$extra_exts = [];
foreach ($extensions as $ext) {
$extra_exts = array_merge($extra_exts, Config::getExt($ext, 'ext-suggests', []));
}
foreach ($extra_exts as $ext) {
if ($this->builder?->getExt($ext) && !in_array($ext, $extensions)) {
$extensions[] = $ext;
}
}
[$extensions, $libraries] = DependencyUtil::getExtsAndLibs($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
ob_start();

View File

@@ -100,6 +100,10 @@ class SPCTarget
public static function getLibcVersion(): ?string
{
if (PHP_OS_FAMILY === 'Linux') {
$target = (string) getenv('SPC_TARGET');
if (str_contains($target, '-gnu.2.')) {
return preg_match('/-gnu\.(2\.\d+)/', $target, $matches) ? $matches[1] : null;
}
$libc = self::getLibc();
return SystemUtil::getLibcVersionIfExists($libc);
}

View File

@@ -8,6 +8,7 @@ use SPC\builder\freebsd\library\BSDLibraryBase;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\store\FileSystem;
use SPC\util\PkgConfigUtil;
use SPC\util\shell\UnixShell;
/**
@@ -184,6 +185,7 @@ class UnixCMakeExecutor extends Executor
$cxx = getenv('CCX');
logger()->debug("making cmake tool chain file for {$os} {$target_arch} with CFLAGS='{$cflags}'");
$root = BUILD_ROOT_PATH;
$pkgConfigExecutable = PkgConfigUtil::findPkgConfig();
$ccLine = '';
if ($cc) {
$ccLine = 'SET(CMAKE_C_COMPILER ' . $cc . ')';
@@ -202,7 +204,7 @@ SET(CMAKE_PREFIX_PATH "{$root}")
SET(CMAKE_INSTALL_PREFIX "{$root}")
SET(CMAKE_INSTALL_LIBDIR "lib")
set(PKG_CONFIG_EXECUTABLE "{$root}/bin/pkg-config")
set(PKG_CONFIG_EXECUTABLE "{$pkgConfigExecutable}")
list(APPEND PKG_CONFIG_EXECUTABLE "--static")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

View File

@@ -63,7 +63,8 @@ const BUILD_TARGET_MICRO = 2; // build micro
const BUILD_TARGET_FPM = 4; // build fpm
const BUILD_TARGET_EMBED = 8; // build embed
const BUILD_TARGET_FRANKENPHP = 16; // build frankenphp
const BUILD_TARGET_ALL = BUILD_TARGET_CLI | BUILD_TARGET_MICRO | BUILD_TARGET_FPM | BUILD_TARGET_EMBED | BUILD_TARGET_FRANKENPHP; // build all
const BUILD_TARGET_CGI = 32; // build cgi
const BUILD_TARGET_ALL = BUILD_TARGET_CLI | BUILD_TARGET_MICRO | BUILD_TARGET_FPM | BUILD_TARGET_EMBED | BUILD_TARGET_FRANKENPHP | BUILD_TARGET_CGI; // build all
// doctor error fix policy
const FIX_POLICY_DIE = 1; // die directly

View File

@@ -4,20 +4,40 @@ declare(strict_types=1);
assert(function_exists('gettext'));
assert(function_exists('bindtextdomain'));
assert(function_exists('bind_textdomain_codeset'));
assert(function_exists('textdomain'));
if (!is_dir('locale/en_US/LC_MESSAGES/')) {
mkdir('locale/en_US/LC_MESSAGES/', 0755, true);
foreach (['en_US', 'en_GB'] as $lc) {
$dir = "locale/{$lc}/LC_MESSAGES";
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSytaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';
$path = "{$dir}/test.mo";
if (!file_exists($path)) {
file_put_contents($path, base64_decode($mo));
}
}
if (!file_exists('locale/en_US/LC_MESSAGES/test.mo')) {
$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSStaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';
file_put_contents('locale/en_US/LC_MESSAGES/test.mo', base64_decode($mo));
}
putenv('LANG=en_US');
assert(setlocale(LC_ALL, 'en_US.utf-8') === 'en_US.utf-8');
// Probe for an available English locale
$candidates = [
'en_US.UTF-8', 'en_US.utf8', 'en_US.utf-8', 'en_US',
'en_GB.UTF-8', 'en_GB.utf8', 'en_GB.utf-8', 'en_GB',
'English_United States.65001', 'English_United States.1252',
'English_United Kingdom.65001', 'English_United Kingdom.1252',
];
$locale = setlocale(LC_ALL, $candidates);
assert($locale !== false);
putenv('LC_ALL=' . $locale);
putenv('LANG=' . $locale);
putenv('LANGUAGE=' . (stripos($locale, 'US') !== false ? 'en_US:en_GB' : 'en_GB:en_US'));
$domain = 'test';
bindtextdomain($domain, 'locale/');
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
assert(gettext(json_decode('"\u793a\u4f8b"', true)) === 'Example');
$src = json_decode('"\u793a\u4f8b"', true);
assert(gettext($src) === 'Example');

View File

@@ -22,7 +22,10 @@ define('BUILD_BIN_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_BI
// output path for shared extensions
define('BUILD_MODULES_PATH', FileSystem::convertPath(is_string($a = getenv('BUILD_MODULES_PATH')) ? $a : (BUILD_ROOT_PATH . '/modules')));
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . '/pkgroot')));
// pkg arch name
$_pkg_arch_name = arch2gnu(php_uname('m')) . '-' . strtolower(PHP_OS_FAMILY);
define('PKG_ROOT_PATH', FileSystem::convertPath(is_string($a = getenv('PKG_ROOT_PATH')) ? $a : (WORKING_DIR . "/pkgroot/{$_pkg_arch_name}")));
define('SOURCE_PATH', FileSystem::convertPath(is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source')));
define('DOWNLOAD_PATH', FileSystem::convertPath(is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads')));
define('CPU_COUNT', match (PHP_OS_FAMILY) {

View File

@@ -0,0 +1,22 @@
diff --git a/glib/glib/goption.c b/glib/glib/goption.c
index 0a22f6f..f439fd4 100644
--- a/glib/glib/goption.c
+++ b/glib/glib/goption.c
@@ -166,7 +166,7 @@ typedef struct
gpointer arg_data;
union
{
- gboolean bool;
+ gboolean boolean;
gint integer;
gchar *str;
gchar **array;
@@ -1600,7 +1600,7 @@ free_changes_list (GOptionContext *context,
switch (change->arg_type)
{
case G_OPTION_ARG_NONE:
- *(gboolean *)change->arg_data = change->prev.bool;
+ *(gboolean *)change->arg_data = change->prev.boolean;
break;
case G_OPTION_ARG_INT:
*(gint *)change->arg_data = change->prev.integer;

View File

@@ -0,0 +1,90 @@
From d4942a590a301a2552f8885962640f38d60de27b Mon Sep 17 00:00:00 2001
From: DubbleClick <m@pyc.ac>
Date: Tue, 26 Aug 2025 00:08:32 +0700
Subject: [PATCH] add SWOOLE_ODBC_LIBC variable if the odbc library needs more
than to link against a single library
---
config.m4 | 57 ++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 44 insertions(+), 13 deletions(-)
diff --git a/config.m4 b/config.m4
index 1583cb81c8..683d8891af 100644
--- a/config.m4
+++ b/config.m4
@@ -641,31 +641,62 @@ EOF
AC_MSG_ERROR([Cannot find header file(s) for pdo_odbc])
fi
+ if test -n "$SWOOLE_ODBC_LIBS"; then
+ ODBC_LIBS="$SWOOLE_ODBC_LIBS"
+ else
+ ODBC_LIBS="-l$pdo_odbc_def_lib"
+ fi
+
PDO_ODBC_INCLUDE="$pdo_odbc_def_cflags -I$PDO_ODBC_INCDIR -DPDO_ODBC_TYPE=\\\"$pdo_odbc_flavour\\\""
- PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR -l$pdo_odbc_def_lib"
+ PDO_ODBC_LDFLAGS="$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR $ODBC_LIBS"
PHP_EVAL_LIBLINE([$PDO_ODBC_LDFLAGS], [SWOOLE_SHARED_LIBADD])
EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_ODBC_INCLUDE"
dnl Check first for an ODBC 1.0 function to assert that the libraries work
- PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLBindCol,
- [
- dnl And now check for an ODBC 3.0 function to assert that they are *good*
- dnl libraries.
- PHP_CHECK_LIBRARY($pdo_odbc_def_lib, SQLAllocHandle,
- [], [
- AC_MSG_ERROR([
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $PDO_ODBC_LDFLAGS"
+
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <sql.h>
+ #include <sqlext.h>]],
+ [[
+ SQLLEN ind = 0;
+ char buf[1];
+ SQLBindCol((SQLHSTMT)0, (SQLUSMALLINT)1, (SQLSMALLINT)SQL_C_CHAR,
+ (SQLPOINTER)buf, (SQLLEN)sizeof(buf), &ind);
+ return 0;
+ ]])],
+ [
+ dnl And now check for an ODBC 3.0 function to assert that they are *good*
+ dnl libraries.
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <sql.h>
+ #include <sqlext.h>]],
+ [[
+ SQLHANDLE out = SQL_NULL_HANDLE;
+ SQLAllocHandle((SQLSMALLINT)SQL_HANDLE_ENV,
+ (SQLHANDLE)SQL_NULL_HANDLE, &out);
+ return 0;
+ ]])],
+ [],
+ [AC_MSG_ERROR([
Your ODBC library does not appear to be ODBC 3 compatible.
You should consider using iODBC or unixODBC instead, and loading your
libraries as a driver in that environment; it will emulate the
functions required for PDO support.
- ])], $PDO_ODBC_LDFLAGS)
- ],[
- AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information])
- ], $PDO_ODBC_LDFLAGS)
+ ])]
+ )
+ ],
+ [AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information])]
+ )
+
+ LIBS="$save_LIBS"
- AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support])
+ AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support])
fi
dnl odbc end

View File

@@ -18,19 +18,19 @@ $test_php_version = [
// '8.3',
'8.4',
// '8.5',
'git',
// 'git',
];
// test os (macos-13, macos-14, macos-15, ubuntu-latest, windows-latest are available)
$test_os = [
// 'macos-13', // bin/spc for x86_64
'macos-13', // bin/spc for x86_64
// 'macos-14', // bin/spc for arm64
// 'macos-15', // bin/spc for arm64
'macos-15', // bin/spc for arm64
'ubuntu-latest', // bin/spc-alpine-docker for x86_64
// 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
// 'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
'ubuntu-24.04-arm', // bin/spc for arm64
// 'ubuntu-24.04-arm', // bin/spc for arm64
// 'windows-latest', // .\bin\spc.ps1
];
@@ -50,19 +50,19 @@ $prefer_pre_built = false;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pdo,pdo_mysql,pdo_sqlite,phar,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip',
'Linux', 'Darwin' => 'bcmath',
'Windows' => 'bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pdo,pdo_mysql,pdo_sqlite,phar,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip',
};
// If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`).
$shared_extensions = match (PHP_OS_FAMILY) {
'Linux' => '',
'Linux' => 'zip',
'Darwin' => '',
'Windows' => '',
};
// If you want to test lib-suggests for all extensions and libraries, set it to true.
$with_suggested_libs = false;
$with_suggested_libs = true;
// If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true.
$with_libs = match (PHP_OS_FAMILY) {

View File

@@ -160,7 +160,7 @@ class BuilderTest extends TestCase
[BUILD_TARGET_MICRO, 'micro'],
[BUILD_TARGET_EMBED, 'embed'],
[BUILD_TARGET_FRANKENPHP, 'frankenphp'],
[BUILD_TARGET_ALL, 'cli, micro, fpm, embed, frankenphp'],
[BUILD_TARGET_ALL, 'cli, micro, fpm, embed, frankenphp, cgi'],
[BUILD_TARGET_CLI | BUILD_TARGET_EMBED, 'cli, embed'],
];
}

1134
yarn.lock

File diff suppressed because it is too large Load Diff