Compare commits

..

96 Commits

Author SHA1 Message Date
henderkes
5a5f54bdcd brilliant to test php 8.1 2026-03-30 01:37:08 +07:00
henderkes
8f7897e13b test 2026-03-30 01:06:31 +07:00
henderkes
daae5f2a7c libjpeg-turbo mustn't compile zlib symbols on its own 2026-03-30 00:56:51 +07:00
Marc
766f7fa34f hard code protobuf version while we're on v2 (#1075) 2026-03-26 12:37:32 +07:00
henderkes
ecf712b2b7 hard code protobuf version while we're on v2 2026-03-26 12:32:27 +07:00
Jerry Ma
d35cbd7bf8 Add Windows builders for libaom and brotli (#1072) 2026-03-23 16:21:46 +08:00
crazywhalecc
d076df6b04 Bump version number 2026-03-23 16:21:24 +08:00
crazywhalecc
a99b6bebae Remove freetype useless lib suggestions 2026-03-23 16:08:09 +08:00
Hendrik Mennen
864678ab46 Merge branch 'main' into feat/windows-libaom-brotli 2026-03-22 13:12:47 +01:00
Hendrik Mennen
c03508a84b Improve zlib Windows library detection for future zlib versions (#1070)
Co-authored-by: Hendrik Mennen <hmennen@gambio.ec1.de>
2026-03-22 20:11:29 +08:00
Hendrik Mennen
963e2a084a Add Windows builders for libaom and brotli libraries
Both libraries are listed in lib.json and used as transitive dependencies
(libaom via libavif, brotli via freetype/curl) but had no Windows builder,
causing builds to fail with "library [X] is in the lib.json list but not
supported to compile".

libaom: Uses builddir instead of build to avoid collision with the
source tree's build/cmake/ directory. Matches the Unix builder's
AOM_TARGET_CPU=generic setting for portability.

brotli: Standard CMake build with shared libs and tools disabled.

Also adds static-libs-windows entry for libaom in lib.json.
2026-03-22 09:07:47 +01:00
Marc
4c6b7a3d55 fix libde265 on ancient debian OS (#1064) 2026-03-20 19:48:01 +07:00
Marc
5404926a14 Merge branch 'main' into fix/lide265 2026-03-20 19:47:47 +07:00
Nils Silbernagel
295df19484 Add shared-extensions, frankenphp and zts to build-unix workflow (#1062)
Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2026-03-20 20:37:25 +08:00
Jerry Ma
b970bf8e3a Fix gd build on PHP 8.5 (#1043) 2026-03-20 20:00:42 +08:00
Jerry Ma
54915028d7 Fix zlib produced lib file names from different zlib version (#1066) 2026-03-20 19:35:51 +08:00
henderkes
823fe96942 attempt 2026-03-18 23:26:57 +07:00
henderkes
f2fa29809a why is it not failing here? 2026-03-18 18:37:16 +07:00
Marc
463ec546fa Merge branch 'main' into fix/lide265 2026-03-18 15:51:03 +07:00
henderkes
60b2aea09e fix libde265 on ancient debian OS? 2026-03-18 11:57:27 +07:00
Marc
4625c6a885 update default php version to 8.5 (#1058) 2026-03-11 15:09:07 +07:00
henderkes
85b0cd8b4b only disable when building ftp static, shared is fine 2026-03-11 13:54:24 +07:00
henderkes
1fcb74ad9b swoole-ftp conflicts with ftp 2026-03-11 13:42:38 +07:00
henderkes
1049a3ce66 curl is always supported now (swoole no longer supports php < 8.1) 2026-03-11 10:32:58 +07:00
henderkes
1b8b53d47f update swoole args for 6.2 2026-03-11 10:19:08 +07:00
henderkes
a232f578a4 test bulk 2026-03-11 10:11:39 +07:00
henderkes
70285cb53b actually update to 8.5 2026-03-11 09:48:50 +07:00
henderkes
a335d050cf cs fix 2026-03-11 09:46:41 +07:00
henderkes
ef4b2997a7 test 2026-03-11 09:45:56 +07:00
henderkes
901da8fa41 remove ldtl from odbc libs private (using built in ltdl) 2026-03-11 09:43:02 +07:00
henderkes
e49a5d7a50 make php 8.5 default 2026-03-11 09:42:39 +07:00
Jerry Ma
281b958075 Fix grpc build (#1055) 2026-03-10 20:35:51 +09:00
Marc
e31f64864e fix: FrankenPHP build args (#1057) 2026-03-10 18:13:34 +07:00
Kévin Dunglas
92f5b56c74 fix: FrankenPHP build args 2026-03-10 11:57:23 +01:00
Jerry Ma
2350d2d5ca Merge branch 'main' into fix/grpc-build 2026-03-10 14:52:21 +09:00
crazywhalecc
086c855a43 Use custom config.m4 for grpc extension 2026-03-10 13:49:53 +08:00
crazywhalecc
4fa5292913 Use custom config.m4 for grpc extension 2026-03-10 13:49:34 +08:00
Marc
9634b8bcda set custom binary name for frankenphp, allow linking against system openssl (fix mssql issues) (#1056) 2026-03-10 11:11:36 +07:00
Marc
5d5a50a33c Update src/SPC/builder/LibraryBase.php
Co-authored-by: Jerry Ma <jesse2061@outlook.com>
2026-03-10 10:57:49 +07:00
henderkes
1edf14e642 set custom binary name for frankenphp 2026-03-10 08:52:15 +07:00
henderkes
2277390a1a fix removeConfigureArgs in UnixAutoconfExecutor.php 2026-03-10 08:49:56 +07:00
henderkes
f93ad27c17 allow using some libs as system provided (work around mssql linking vs system openssl) 2026-03-10 08:47:38 +07:00
henderkes
b690566b39 simplify rm command 2026-03-10 08:43:48 +07:00
henderkes
16e772e1a8 add back in zig workaround as 0.16.x is not released yet 2026-03-10 08:42:17 +07:00
crazywhalecc
ad356b4a23 Fix grpc build 2026-03-09 20:12:14 +08:00
Jerry Ma
8c4e3d58a3 Add php-src mirror and use gmp mirror site (#1048) 2026-03-06 15:25:38 +09:00
Marc
8a51d64685 Add condition for ffi patch (#1050) 2026-03-06 12:59:23 +07:00
crazywhalecc
055bc7bc3c Add condition for ffi patch 2026-03-06 13:46:55 +08:00
Marc
7d7902e0e9 Update build flags for FrankenPHP in UnixBuilderBase (#1042) 2026-02-24 06:08:14 +07:00
Kévin Dunglas
2a8fa7d155 Update build flags for FrankenPHP in UnixBuilderBase 2026-02-23 16:29:43 +01:00
Marc
67ef8f6608 fix redownloading go-xcaddy every time, version 2.8.3 (#1034) 2026-02-17 22:36:27 +07:00
henderkes
d83a597689 unquote the string in case a shell script passes it stupidly 2026-02-17 21:49:30 +07:00
henderkes
5623fed37f fix redownloading go-xcaddy every time 2026-02-17 21:05:18 +07:00
Marc
38140d115f libavif needs at least one encoder to work (#1033) 2026-02-17 19:59:46 +07:00
henderkes
98117c3a04 remove pre built 2026-02-17 19:58:03 +07:00
Marc
b01d3ce12c Merge branch 'main' into feat/avif-dec 2026-02-17 19:18:38 +07:00
henderkes
608c915e14 should depend on it instead 2026-02-17 19:14:29 +07:00
henderkes
c680299654 libavif needs at least one encoder to work 2026-02-17 19:12:19 +07:00
Marc
794ab16b32 add input with-suggested-libs for build command (#1032) 2026-02-16 18:38:35 +07:00
tricker
661723c99a change logs name
Co-authored-by: Marc <m@pyc.ac>
2026-02-16 12:26:49 +01:00
Yoram
d9834d05c6 upload debug logs on 'build php' failures 2026-02-16 11:35:42 +01:00
Yoram
9a53ef3498 add input with-suggested-libs for build command 2026-02-13 14:35:01 +01:00
Marc
f680731f9d fix: Postgres build with ancient libc (#1029) 2026-02-11 17:42:36 +01:00
Jerry Ma
0fe1442f7e Bump version from 2.8.0 to 2.8.2 2026-02-12 00:02:38 +08:00
Jerry Ma
1e4780397b Update test-extensions.php for PHP versions and extensions
Commented out older PHP versions and Windows 2025 in the test configuration. Updated the extensions to test for Linux and Darwin.
2026-02-11 23:32:19 +08:00
Kévin Dunglas
6b67cb90fc fix: Postgres build with ancient libc 2026-02-11 16:24:13 +01:00
Jerry Ma
b89ff3c083 Add com_dotnet extension (#1023) 2026-02-03 19:08:19 +08:00
Marc
0cfa2036f0 fix spx shared libadd (#1022) 2026-01-30 20:23:18 +01:00
henderkes
c5882c1f8e fix gettext v1.0 release 2026-01-30 19:41:39 +01:00
henderkes
4531c9fe57 add option to allow linking musl dynamically on alpine 2026-01-27 00:57:58 +01:00
henderkes
223dd10ac6 fix spx shared libadd 2026-01-24 20:26:19 +01:00
Marc
1c28f0f455 bunch of fixes/changes to make packages build (#1006) 2026-01-19 12:46:24 +01:00
henderkes
b3c450291a up version 2026-01-19 12:00:06 +01:00
crazywhalecc
372760e469 Update patch point docs 2026-01-19 18:56:28 +08:00
henderkes
6cf4c40cd2 Merge remote-tracking branch 'origin/main' into henderkes-patch-1 2026-01-19 11:22:03 +01:00
henderkes
af75ffaf24 suggestions, change openssldir 2026-01-19 10:22:33 +01:00
Marc
ae0217b3a1 add excimer extension (#1018) 2026-01-19 09:25:30 +01:00
henderkes
1e2b4017ac test excimer 2026-01-17 11:28:47 +01:00
henderkes
19f941797e zig now supports -Wl,-exported_symbols_list 2026-01-17 11:28:05 +01:00
Marc
0b863cbc70 Merge branch 'main' into feat/excimer 2026-01-17 10:53:35 +01:00
henderkes
b09337de09 add excimer extension 2026-01-17 10:51:21 +01:00
henderkes
d902e70b4d fix arm64 builds 2026-01-16 12:28:41 +01:00
Jerry Ma
cd2dc5bce4 Fix nghttp2 and curl build configurations for static linking (#1014) 2026-01-13 16:51:57 +08:00
henderkes
34910d18e9 add patch point for shared ext build 2026-01-04 02:31:41 +01:00
henderkes
3a17cec521 deploy extensions with -release flag too 2026-01-03 19:15:57 +01:00
henderkes
94644d374f fix 2026-01-03 19:12:16 +01:00
henderkes
f8b0c2c980 add release thing to extension build too 2026-01-03 19:08:14 +01:00
henderkes
6bbb3c969c remove -release handling functionality 2026-01-03 17:03:43 +01:00
henderkes
76025b95c1 missing space 2026-01-03 16:46:14 +01:00
henderkes
1be353fd13 more concise message 2026-01-03 16:45:55 +01:00
henderkes
54001ab868 simplify logic a bit 2026-01-03 16:33:40 +01:00
henderkes
890ff475f1 our memcache patch prevents shared building 2026-01-03 14:09:16 +01:00
henderkes
559a2909a9 use little trick to order libargon2 before libsodium 2026-01-02 23:21:29 +01:00
henderkes
fff2484529 postgresql doesn't build under c23 2026-01-02 22:52:03 +01:00
henderkes
d1b194999d use OPENSSL_CONF directory for openssl default configuration 2026-01-02 21:13:22 +01:00
Jerry Ma
8650ce4f8f Add MACOSX_DEPLOYMENT_TARGET to env.ini (#1009) 2025-12-26 17:15:45 +08:00
57 changed files with 711 additions and 744 deletions

View File

@@ -29,6 +29,9 @@ on:
description: Extensions to build (comma separated) description: Extensions to build (comma separated)
required: true required: true
type: string type: string
shared-extensions:
description: Shared extensions to build (optional, comma separated)
type: string
extra-libs: extra-libs:
description: Extra libraries to build (optional, comma separated) description: Extra libraries to build (optional, comma separated)
type: string type: string
@@ -42,10 +45,22 @@ on:
build-fpm: build-fpm:
description: Build fpm binary description: Build fpm binary
type: boolean type: boolean
build-frankenphp:
description: Build frankenphp binary (requires ZTS)
type: boolean
default: false
enable-zts:
description: Enable ZTS
type: boolean
default: false
prefer-pre-built: prefer-pre-built:
description: Prefer pre-built binaries (reduce build time) description: Prefer pre-built binaries (reduce build time)
type: boolean type: boolean
default: true default: true
with-suggested-libs:
description: Build with suggested libs
type: boolean
default: false
debug: debug:
description: Show full build logs description: Show full build logs
type: boolean type: boolean
@@ -69,6 +84,9 @@ on:
description: Extensions to build (comma separated) description: Extensions to build (comma separated)
required: true required: true
type: string type: string
shared-extensions:
description: Shared extensions to build (optional, comma separated)
type: string
extra-libs: extra-libs:
description: Extra libraries to build (optional, comma separated) description: Extra libraries to build (optional, comma separated)
type: string type: string
@@ -82,10 +100,22 @@ on:
build-fpm: build-fpm:
description: Build fpm binary description: Build fpm binary
type: boolean type: boolean
build-frankenphp:
description: Build frankenphp binary (requires ZTS)
type: boolean
default: false
enable-zts:
description: Enable ZTS
type: boolean
default: false
prefer-pre-built: prefer-pre-built:
description: Prefer pre-built binaries (reduce build time) description: Prefer pre-built binaries (reduce build time)
type: boolean type: boolean
default: true default: true
with-suggested-libs:
description: Include suggested libs
type: boolean
default: false
debug: debug:
description: Show full build logs description: Show full build logs
type: boolean type: boolean
@@ -144,8 +174,19 @@ jobs:
RUNS_ON="macos-15" RUNS_ON="macos-15"
;; ;;
esac esac
DOWN_CMD="$DOWN_CMD --with-php=${{ inputs.php-version }} --for-extensions=${{ inputs.extensions }} --ignore-cache-sources=php-src" STATIC_EXTS="${{ inputs.extensions }}"
BUILD_CMD="$BUILD_CMD ${{ inputs.extensions }}" SHARED_EXTS="${{ inputs['shared-extensions'] }}"
BUILD_FRANKENPHP="${{ inputs['build-frankenphp'] }}"
ENABLE_ZTS="${{ inputs['enable-zts'] }}"
ALL_EXTS="$STATIC_EXTS"
if [ -n "$SHARED_EXTS" ]; then
ALL_EXTS="$ALL_EXTS,$SHARED_EXTS"
fi
DOWN_CMD="$DOWN_CMD --with-php=${{ inputs.php-version }} --for-extensions=$ALL_EXTS --ignore-cache-sources=php-src"
BUILD_CMD="$BUILD_CMD $STATIC_EXTS"
if [ -n "$SHARED_EXTS" ]; then
BUILD_CMD="$BUILD_CMD --build-shared=$SHARED_EXTS"
fi
if [ -n "${{ inputs.extra-libs }}" ]; then if [ -n "${{ inputs.extra-libs }}" ]; then
DOWN_CMD="$DOWN_CMD --for-libs=${{ inputs.extra-libs }}" DOWN_CMD="$DOWN_CMD --for-libs=${{ inputs.extra-libs }}"
BUILD_CMD="$BUILD_CMD --with-libs=${{ inputs.extra-libs }}" BUILD_CMD="$BUILD_CMD --with-libs=${{ inputs.extra-libs }}"
@@ -157,6 +198,9 @@ jobs:
if [ ${{ inputs.prefer-pre-built }} == true ]; then if [ ${{ inputs.prefer-pre-built }} == true ]; then
DOWN_CMD="$DOWN_CMD --prefer-pre-built" DOWN_CMD="$DOWN_CMD --prefer-pre-built"
fi fi
if [ ${{ inputs.with-suggested-libs }} == true ]; then
BUILD_CMD="$BUILD_CMD --with-suggested-libs"
fi
if [ ${{ inputs.build-cli }} == true ]; then if [ ${{ inputs.build-cli }} == true ]; then
BUILD_CMD="$BUILD_CMD --build-cli" BUILD_CMD="$BUILD_CMD --build-cli"
fi fi
@@ -166,6 +210,12 @@ jobs:
if [ ${{ inputs.build-fpm }} == true ]; then if [ ${{ inputs.build-fpm }} == true ]; then
BUILD_CMD="$BUILD_CMD --build-fpm" BUILD_CMD="$BUILD_CMD --build-fpm"
fi fi
if [ "$BUILD_FRANKENPHP" = "true" ]; then
BUILD_CMD="$BUILD_CMD --build-frankenphp"
fi
if [ "$ENABLE_ZTS" = "true" ]; then
BUILD_CMD="$BUILD_CMD --enable-zts"
fi
echo 'download='"$DOWN_CMD" >> "$GITHUB_OUTPUT" echo 'download='"$DOWN_CMD" >> "$GITHUB_OUTPUT"
echo 'build='"$BUILD_CMD" >> "$GITHUB_OUTPUT" echo 'build='"$BUILD_CMD" >> "$GITHUB_OUTPUT"
echo 'run='"$RUNS_ON" >> "$GITHUB_OUTPUT" echo 'run='"$RUNS_ON" >> "$GITHUB_OUTPUT"
@@ -188,6 +238,27 @@ jobs:
env: env:
phpts: nts phpts: nts
- if: ${{ inputs['build-frankenphp'] == true }}
name: "Install go-xcaddy for FrankenPHP"
run: |
case "${{ inputs.os }}" in
linux-x86_64|linux-aarch64)
./bin/spc-alpine-docker install-pkg go-xcaddy
;;
linux-x86_64-glibc|linux-aarch64-glibc)
./bin/spc-gnu-docker install-pkg go-xcaddy
;;
macos-x86_64|macos-aarch64)
composer update --no-dev --classmap-authoritative
./bin/spc doctor --auto-fix
./bin/spc install-pkg go-xcaddy
;;
*)
echo "Unsupported OS for go-xcaddy install: ${{ inputs.os }}"
exit 1
;;
esac
# Cache downloaded source # Cache downloaded source
- id: cache-download - id: cache-download
uses: actions/cache@v4 uses: actions/cache@v4
@@ -202,6 +273,14 @@ jobs:
# if: ${{ failure() }} # if: ${{ failure() }}
# uses: mxschmitt/action-tmate@v3 # uses: mxschmitt/action-tmate@v3
# Upload debug logs
- if: ${{ inputs.debug && failure() }}
name: "Upload build logs on failure"
uses: actions/upload-artifact@v4
with:
name: spc-logs-${{ inputs.php-version }}-${{ inputs.os }}
path: log/*.log
# Upload cli executable # Upload cli executable
- if: ${{ inputs.build-cli == true }} - if: ${{ inputs.build-cli == true }}
name: "Upload PHP cli SAPI" name: "Upload PHP cli SAPI"
@@ -226,7 +305,22 @@ jobs:
name: php-fpm-${{ inputs.php-version }}-${{ inputs.os }} name: php-fpm-${{ inputs.php-version }}-${{ inputs.os }}
path: buildroot/bin/php-fpm path: buildroot/bin/php-fpm
# Upload frankenphp executable
- if: ${{ inputs['build-frankenphp'] == true }}
name: "Upload FrankenPHP SAPI"
uses: actions/upload-artifact@v4
with:
name: php-frankenphp-${{ inputs.php-version }}-${{ inputs.os }}
path: buildroot/bin/frankenphp
# Upload extensions metadata # Upload extensions metadata
- if: ${{ inputs['shared-extensions'] != '' }}
name: "Upload shared extensions"
uses: actions/upload-artifact@v4
with:
name: php-shared-ext-${{ inputs.php-version }}-${{ inputs.os }}
path: |
buildroot/modules/*.so
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: "Upload License Files" name: "Upload License Files"
with: with:

View File

@@ -108,8 +108,7 @@ RUN apk update; \
wget \ wget \
xz \ xz \
gettext-dev \ gettext-dev \
binutils-gold \ binutils-gold
patchelf
RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \ RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \
chmod +x /usr/local/bin/php chmod +x /usr/local/bin/php

View File

@@ -92,11 +92,6 @@ RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc
RUN source /etc/bashrc RUN source /etc/bashrc
RUN yum install -y which RUN yum install -y which
RUN curl -fsSL -o patchelf.tgz https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-$SPC_USE_ARCH.tar.gz && \
mkdir -p /patchelf && \
tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \
cp /patchelf/bin/patchelf /usr/bin/
RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \ RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \
mkdir /cmake && \ mkdir /cmake && \
tar -xzf cmake.tgz -C /cmake --strip-components 1 tar -xzf cmake.tgz -C /cmake --strip-components 1

View File

@@ -75,8 +75,10 @@ SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime
; - musl-native: used for alpine linux, can build `musl` and `musl -dynamic` target. ; - musl-native: used for alpine linux, can build `musl` and `musl -dynamic` target.
; - gnu-native: used for general linux distros, can build gnu target for the installed glibc version only. ; - gnu-native: used for general linux distros, can build gnu target for the installed glibc version only.
; LEGACY option to specify the target ; option to specify the target, superceded by SPC_TARGET if set
SPC_LIBC=musl SPC_LIBC=musl
; uncomment to link libc dynamically on musl
; SPC_MUSL_DYNAMIC=true
; Recommended: specify your target here. Zig toolchain will be used. ; Recommended: specify your target here. Zig toolchain will be used.
; examples: ; examples:
@@ -115,6 +117,10 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
; optional, path to openssl conf. This affects where openssl will look for the default CA.
; default on Debian/Alpine: /etc/ssl, default on RHEL: /etc/pki/tls
OPENSSLDIR=""
[macos] [macos]
; build target: macho or macho (possibly we could support macho-universal in the future) ; build target: macho or macho (possibly we could support macho-universal in the future)
; Currently we do not support universal and cross-compilation for macOS. ; Currently we do not support universal and cross-compilation for macOS.
@@ -142,6 +148,10 @@ SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-
SPC_CMD_VAR_PHP_EMBED_TYPE="static" SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; EXTRA_CFLAGS for `configure` and `make` php ; EXTRA_CFLAGS for `configure` and `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Werror=unknown-warning-option ${SPC_DEFAULT_C_FLAGS}" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Werror=unknown-warning-option ${SPC_DEFAULT_C_FLAGS}"
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
; minimum compatible macOS version (LLVM vars, availability not guaranteed)
MACOSX_DEPLOYMENT_TARGET=12.0
[freebsd] [freebsd]
; compiler environments ; compiler environments

View File

@@ -43,6 +43,14 @@
"calendar": { "calendar": {
"type": "builtin" "type": "builtin"
}, },
"com_dotnet": {
"support": {
"BSD": "no",
"Linux": "no",
"Darwin": "no"
},
"type": "builtin"
},
"ctype": { "ctype": {
"type": "builtin" "type": "builtin"
}, },
@@ -127,6 +135,14 @@
"sockets" "sockets"
] ]
}, },
"excimer": {
"support": {
"Windows": "wip",
"BSD": "wip"
},
"type": "external",
"source": "ext-excimer"
},
"exif": { "exif": {
"type": "builtin" "type": "builtin"
}, },
@@ -236,6 +252,7 @@
"arg-type-unix": "enable-path", "arg-type-unix": "enable-path",
"cpp-extension": true, "cpp-extension": true,
"lib-depends": [ "lib-depends": [
"grpc",
"zlib", "zlib",
"openssl", "openssl",
"libcares" "libcares"
@@ -410,8 +427,7 @@
"ext-depends": [ "ext-depends": [
"zlib", "zlib",
"session" "session"
], ]
"build-with-php": true
}, },
"memcached": { "memcached": {
"support": { "support": {
@@ -500,7 +516,9 @@
"mysqlnd" "mysqlnd"
], ],
"lib-depends": [ "lib-depends": [
"libsodium", "libsodium"
],
"lib-suggests": [
"openssl" "openssl"
] ]
}, },
@@ -515,7 +533,9 @@
"mysqlnd" "mysqlnd"
], ],
"lib-depends": [ "lib-depends": [
"libsodium", "libsodium"
],
"lib-suggests": [
"openssl" "openssl"
] ]
}, },

View File

@@ -143,9 +143,7 @@
"zlib" "zlib"
], ],
"lib-suggests": [ "lib-suggests": [
"libpng", "libpng"
"bzip2",
"brotli"
] ]
}, },
"gettext": { "gettext": {
@@ -355,12 +353,18 @@
"static-libs-unix": [ "static-libs-unix": [
"libaom.a" "libaom.a"
], ],
"static-libs-windows": [
"aom.lib"
],
"cpp-library": true "cpp-library": true
}, },
"libargon2": { "libargon2": {
"source": "libargon2", "source": "libargon2",
"static-libs-unix": [ "static-libs-unix": [
"libargon2.a" "libargon2.a"
],
"lib-suggests": [
"libsodium"
] ]
}, },
"libavif": { "libavif": {
@@ -370,6 +374,15 @@
], ],
"static-libs-windows": [ "static-libs-windows": [
"avif.lib" "avif.lib"
],
"lib-depends": [
"libaom"
],
"lib-suggests": [
"libwebp",
"libjpeg",
"libxml2",
"libpng"
] ]
}, },
"libcares": { "libcares": {
@@ -481,7 +494,7 @@
"static-libs-windows": [ "static-libs-windows": [
"libjpeg_a.lib" "libjpeg_a.lib"
], ],
"lib-suggests-windows": [ "lib-depends": [
"zlib" "zlib"
] ]
}, },
@@ -850,6 +863,9 @@
}, },
"openssl": { "openssl": {
"source": "openssl", "source": "openssl",
"pkg-configs": [
"openssl"
],
"static-libs-unix": [ "static-libs-unix": [
"libssl.a", "libssl.a",
"libcrypto.a" "libcrypto.a"
@@ -962,6 +978,11 @@
}, },
"unixodbc": { "unixodbc": {
"source": "unixodbc", "source": "unixodbc",
"pkg-configs": [
"odbc",
"odbccr",
"odbcinst"
],
"static-libs-unix": [ "static-libs-unix": [
"libodbc.a", "libodbc.a",
"libodbccr.a", "libodbccr.a",
@@ -1003,6 +1024,9 @@
}, },
"zlib": { "zlib": {
"source": "zlib", "source": "zlib",
"pkg-configs": [
"zlib"
],
"static-libs-unix": [ "static-libs-unix": [
"libz.a" "libz.a"
], ],
@@ -1016,6 +1040,9 @@
}, },
"zstd": { "zstd": {
"source": "zstd", "source": "zstd",
"pkg-configs": [
"libzstd"
],
"static-libs-unix": [ "static-libs-unix": [
"libzstd.a" "libzstd.a"
], ],

View File

@@ -11,6 +11,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/amqp", "url": "https://pecl.php.net/get/amqp",
"path": "php-src/ext/amqp", "path": "php-src/ext/amqp",
"filename": "amqp.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -20,6 +21,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/APCu", "url": "https://pecl.php.net/get/APCu",
"path": "php-src/ext/apcu", "path": "php-src/ext/apcu",
"filename": "apcu.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -29,6 +31,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/ast", "url": "https://pecl.php.net/get/ast",
"path": "php-src/ext/ast", "path": "php-src/ext/ast",
"filename": "ast.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -85,6 +88,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/dio", "url": "https://pecl.php.net/get/dio",
"path": "php-src/ext/dio", "path": "php-src/ext/dio",
"filename": "dio.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -94,6 +98,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/ev", "url": "https://pecl.php.net/get/ev",
"path": "php-src/ext/ev", "path": "php-src/ext/ev",
"filename": "ev.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -113,6 +118,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/ds", "url": "https://pecl.php.net/get/ds",
"path": "php-src/ext/ds", "path": "php-src/ext/ds",
"filename": "ds.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -120,13 +126,23 @@
}, },
"ext-event": { "ext-event": {
"type": "url", "type": "url",
"url": "https://bitbucket.org/osmanov/pecl-event/get/3.0.8.tar.gz", "url": "https://bitbucket.org/osmanov/pecl-event/get/3.1.4.tar.gz",
"path": "php-src/ext/event", "path": "php-src/ext/event",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-excimer": {
"type": "url",
"url": "https://pecl.php.net/get/excimer",
"path": "php-src/ext/excimer",
"filename": "excimer.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-glfw": { "ext-glfw": {
"type": "git", "type": "git",
"url": "https://github.com/mario-deluna/php-glfw", "url": "https://github.com/mario-deluna/php-glfw",
@@ -149,6 +165,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/grpc", "url": "https://pecl.php.net/get/grpc",
"path": "php-src/ext/grpc", "path": "php-src/ext/grpc",
"filename": "grpc.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": [ "path": [
@@ -160,6 +177,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/imagick", "url": "https://pecl.php.net/get/imagick",
"path": "php-src/ext/imagick", "path": "php-src/ext/imagick",
"filename": "imagick.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -169,6 +187,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/imap", "url": "https://pecl.php.net/get/imap",
"path": "php-src/ext/imap", "path": "php-src/ext/imap",
"filename": "imap.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": [ "path": [
@@ -190,6 +209,7 @@
"ext-maxminddb": { "ext-maxminddb": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/maxminddb", "url": "https://pecl.php.net/get/maxminddb",
"filename": "ext-maxminddb.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -199,6 +219,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/memcache", "url": "https://pecl.php.net/get/memcache",
"path": "php-src/ext/memcache", "path": "php-src/ext/memcache",
"filename": "memcache.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -217,6 +238,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/simdjson", "url": "https://pecl.php.net/get/simdjson",
"path": "php-src/ext/simdjson", "path": "php-src/ext/simdjson",
"filename": "simdjson.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -236,6 +258,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/ssh2", "url": "https://pecl.php.net/get/ssh2",
"path": "php-src/ext/ssh2", "path": "php-src/ext/ssh2",
"filename": "ssh2.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -245,6 +268,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/trader", "url": "https://pecl.php.net/get/trader",
"path": "php-src/ext/trader", "path": "php-src/ext/trader",
"filename": "trader.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -254,6 +278,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/uuid", "url": "https://pecl.php.net/get/uuid",
"path": "php-src/ext/uuid", "path": "php-src/ext/uuid",
"filename": "uuid.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -263,6 +288,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/uv", "url": "https://pecl.php.net/get/uv",
"path": "php-src/ext/uv", "path": "php-src/ext/uv",
"filename": "uv.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -281,6 +307,7 @@
"ext-zip": { "ext-zip": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/zip", "url": "https://pecl.php.net/get/zip",
"filename": "ext-zip.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -334,7 +361,7 @@
}, },
"gmp": { "gmp": {
"type": "filelist", "type": "filelist",
"url": "https://gmplib.org/download/gmp/", "url": "https://ftp.gnu.org/gnu/gmp/",
"regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/", "regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"provide-pre-built": true, "provide-pre-built": true,
"alt": { "alt": {
@@ -388,6 +415,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/igbinary", "url": "https://pecl.php.net/get/igbinary",
"path": "php-src/ext/igbinary", "path": "php-src/ext/igbinary",
"filename": "igbinary.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "COPYING"
@@ -414,6 +442,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/inotify", "url": "https://pecl.php.net/get/inotify",
"path": "php-src/ext/inotify", "path": "php-src/ext/inotify",
"filename": "inotify.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -497,7 +526,7 @@
"libavif": { "libavif": {
"type": "ghtar", "type": "ghtar",
"repo": "AOMediaCodec/libavif", "repo": "AOMediaCodec/libavif",
"provide-pre-built": true, "provide-pre-built": false,
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -612,6 +641,7 @@
"libjpeg": { "libjpeg": {
"type": "ghtar", "type": "ghtar",
"repo": "libjpeg-turbo/libjpeg-turbo", "repo": "libjpeg-turbo/libjpeg-turbo",
"prefer-stable": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE.md" "path": "LICENSE.md"
@@ -693,7 +723,7 @@
"libsodium": { "libsodium": {
"type": "ghrel", "type": "ghrel",
"repo": "jedisct1/libsodium", "repo": "jedisct1/libsodium",
"match": "libsodium-\\d+(\\.\\d+)*\\.tar\\.gz", "match": "libsodium-(?!1\\.0\\.21)\\d+(\\.\\d+)*\\.tar\\.gz",
"prefer-stable": true, "prefer-stable": true,
"provide-pre-built": true, "provide-pre-built": true,
"license": { "license": {
@@ -818,6 +848,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/memcached", "url": "https://pecl.php.net/get/memcached",
"path": "php-src/ext/memcached", "path": "php-src/ext/memcached",
"filename": "memcached.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -858,6 +889,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/msgpack", "url": "https://pecl.php.net/get/msgpack",
"path": "php-src/ext/msgpack", "path": "php-src/ext/msgpack",
"filename": "msgpack.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -960,6 +992,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/opentelemetry", "url": "https://pecl.php.net/get/opentelemetry",
"path": "php-src/ext/opentelemetry", "path": "php-src/ext/opentelemetry",
"filename": "opentelemetry.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -969,6 +1002,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/parallel", "url": "https://pecl.php.net/get/parallel",
"path": "php-src/ext/parallel", "path": "php-src/ext/parallel",
"filename": "parallel.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -977,6 +1011,7 @@
"pcov": { "pcov": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/pcov", "url": "https://pecl.php.net/get/pcov",
"filename": "pcov.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -986,6 +1021,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/pdo_sqlsrv", "url": "https://pecl.php.net/get/pdo_sqlsrv",
"path": "php-src/ext/pdo_sqlsrv", "path": "php-src/ext/pdo_sqlsrv",
"filename": "pdo_sqlsrv.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1019,8 +1055,9 @@
}, },
"protobuf": { "protobuf": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/protobuf", "url": "https://pecl.php.net/get/protobuf-5.34.1.tgz",
"path": "php-src/ext/protobuf", "path": "php-src/ext/protobuf",
"filename": "protobuf.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1082,6 +1119,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/redis", "url": "https://pecl.php.net/get/redis",
"path": "php-src/ext/redis", "path": "php-src/ext/redis",
"filename": "redis.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": [ "path": [
@@ -1121,6 +1159,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/sqlsrv", "url": "https://pecl.php.net/get/sqlsrv",
"path": "php-src/ext/sqlsrv", "path": "php-src/ext/sqlsrv",
"filename": "sqlsrv.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1186,6 +1225,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/xhprof", "url": "https://pecl.php.net/get/xhprof",
"path": "php-src/ext/xhprof-src", "path": "php-src/ext/xhprof-src",
"filename": "xhprof.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1195,6 +1235,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/xlswriter", "url": "https://pecl.php.net/get/xlswriter",
"path": "php-src/ext/xlswriter", "path": "php-src/ext/xlswriter",
"filename": "xlswriter.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1215,6 +1256,7 @@
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/yac", "url": "https://pecl.php.net/get/yac",
"path": "php-src/ext/yac", "path": "php-src/ext/yac",
"filename": "yac.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"

View File

@@ -16,8 +16,10 @@ while also defining the extensions to compile.
1. Fork project. 1. Fork project.
2. Go to the Actions of the project and select `CI`. 2. Go to the Actions of the project and select `CI`.
3. Select `Run workflow`, fill in the PHP version you want to compile, the target type, and the list of extensions. (extensions comma separated, e.g. `bcmath,curl,mbstring`) 3. Select `Run workflow`, fill in the PHP version you want to compile, the target type, and the list of static extensions. (comma separated, e.g. `bcmath,curl,mbstring`)
4. After waiting for about a period of time, enter the corresponding task and get `Artifacts`. 4. If you need shared extensions (for example `xdebug`), set `shared-extensions` (comma separated, e.g. `xdebug`).
5. If you need FrankenPHP, enable `build-frankenphp` and also enable `enable-zts`.
6. After waiting for about a period of time, enter the corresponding task and get `Artifacts`.
If you enable `debug`, all logs will be output at build time, including compiled logs, for troubleshooting. If you enable `debug`, all logs will be output at build time, including compiled logs, for troubleshooting.

View File

@@ -549,22 +549,24 @@ otherwise it will be executed repeatedly in other events.
The following are the supported `patch_point` event names and corresponding locations: The following are the supported `patch_point` event names and corresponding locations:
| Event name | Event description | | Event name | Event description |
|------------------------------|----------------------------------------------------------------------------------------------------| |---------------------------------|----------------------------------------------------------------------------------------------------|
| before-libs-extract | Triggered before the dependent libraries extracted | | before-libs-extract | Triggered before the dependent libraries extracted |
| after-libs-extract | Triggered after the compiled dependent libraries extracted | | after-libs-extract | Triggered after the compiled dependent libraries extracted |
| before-php-extract | Triggered before PHP source code extracted | | before-php-extract | Triggered before PHP source code extracted |
| after-php-extract | Triggered after PHP source code extracted | | after-php-extract | Triggered after PHP source code extracted |
| before-micro-extract | Triggered before phpmicro extract | | before-micro-extract | Triggered before phpmicro extract |
| after-micro-extract | Triggered after phpmicro extracted | | after-micro-extract | Triggered after phpmicro extracted |
| before-exts-extract | Triggered before the extension (to be compiled) extracted to the PHP source directory | | before-exts-extract | Triggered before the extension (to be compiled) extracted to the PHP source directory |
| after-exts-extract | Triggered after the extension extracted to the PHP source directory | | after-exts-extract | Triggered after the extension extracted to the PHP source directory |
| before-library[*name*]-build | Triggered before the library named `name` is compiled (such as `before-library[postgresql]-build`) | | before-library[*name*]-build | Triggered before the library named `name` is compiled (such as `before-library[postgresql]-build`) |
| after-library[*name*]-build | Triggered after the library named `name` is compiled | | after-library[*name*]-build | Triggered after the library named `name` is compiled |
| before-php-buildconf | Triggered before compiling PHP command `./buildconf` | | after-shared-ext[*name*]-build | Triggered after the shared extension named `name` is compiled |
| before-php-configure | Triggered before compiling PHP command `./configure` | | before-shared-ext[*name*]-build | Triggered before the shared extension named `name` is compiled |
| before-php-make | Triggered before compiling PHP command `make` | | before-php-buildconf | Triggered before compiling PHP command `./buildconf` |
| before-sanity-check | Triggered after compiling PHP but before running extended checks | | before-php-configure | Triggered before compiling PHP command `./configure` |
| before-php-make | Triggered before compiling PHP command `make` |
| before-sanity-check | Triggered after compiling PHP but before running extended checks |
The following is a simple example of temporarily modifying the PHP source code. The following is a simple example of temporarily modifying the PHP source code.
Enable the CLI function to search for the `php.ini` configuration in the current working directory: Enable the CLI function to search for the `php.ini` configuration in the current working directory:

View File

@@ -14,7 +14,9 @@ Action 构建指的是直接使用 GitHub Action 进行编译。
1. Fork 本项目。 1. Fork 本项目。
2. 进入项目的 Actions选择 CI 开头的 Workflow根据你需要的操作系统选择 2. 进入项目的 Actions选择 CI 开头的 Workflow根据你需要的操作系统选择
3. 选择 `Run workflow`,填入你要编译的 PHP 版本、目标类型、扩展列表。(扩展列表使用英文逗号分割,例如 `bcmath,curl,mbstring` 3. 选择 `Run workflow`,填入你要编译的 PHP 版本、目标类型、扩展列表。(扩展列表使用英文逗号分割,例如 `bcmath,curl,mbstring`
4. 等待大约一段时间后,进入对应的任务中,获取 `Artifacts` 4. 如果需要共享扩展(例如 `xdebug`),请设置 `shared-extensions`(使用英文逗号分割,例如 `xdebug`
5. 如果需要 FrankenPHP请启用 `build-frankenphp`,同时也需要启用 `enable-zts`
6. 等待大约一段时间后,进入对应的任务中,获取 `Artifacts`
如果你选择了 `debug`,则会在构建时输出所有日志,包括编译的日志,以供排查错误。 如果你选择了 `debug`,则会在构建时输出所有日志,包括编译的日志,以供排查错误。

View File

@@ -500,6 +500,8 @@ bin/spc dev:sort-config ext
| after-exts-extract | 在要编译的扩展解压到 PHP 源码目录后触发 | | after-exts-extract | 在要编译的扩展解压到 PHP 源码目录后触发 |
| before-library[*name*]-build | 在名称为 `name` 的库编译前触发(如 `before-library[postgresql]-build` | | before-library[*name*]-build | 在名称为 `name` 的库编译前触发(如 `before-library[postgresql]-build` |
| after-library[*name*]-build | 在名称为 `name` 的库编译后触发 | | after-library[*name*]-build | 在名称为 `name` 的库编译后触发 |
| after-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译后触发(如 `after-shared-ext[redis]-build` |
| before-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译前触发 |
| before-php-buildconf | 在编译 PHP 命令 `./buildconf` 前触发 | | before-php-buildconf | 在编译 PHP 命令 `./buildconf` 前触发 |
| before-php-configure | 在编译 PHP 命令 `./configure` 前触发 | | before-php-configure | 在编译 PHP 命令 `./configure` 前触发 |
| before-php-make | 在编译 PHP 命令 `make` 前触发 | | before-php-make | 在编译 PHP 命令 `make` 前触发 |

View File

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

View File

@@ -160,7 +160,7 @@ abstract class BuilderBase
} }
if (!$skip_extract) { if (!$skip_extract) {
$this->emitPatchPoint('before-php-extract'); $this->emitPatchPoint('before-php-extract');
SourceManager::initSource(sources: [$this->getPhpSrcName()], source_only: true); SourceManager::initSource(sources: ['php-src'], source_only: true);
$this->emitPatchPoint('after-php-extract'); $this->emitPatchPoint('after-php-extract');
if ($this->getPHPVersionID() >= 80000) { if ($this->getPHPVersionID() >= 80000) {
$this->emitPatchPoint('before-micro-extract'); $this->emitPatchPoint('before-micro-extract');
@@ -319,7 +319,7 @@ abstract class BuilderBase
public function getPHPVersionFromArchive(?string $file = null): false|string public function getPHPVersionFromArchive(?string $file = null): false|string
{ {
if ($file === null) { if ($file === null) {
$lock = LockFile::get($this->getPhpSrcName()); $lock = LockFile::get('php-src');
if ($lock === null) { if ($lock === null) {
return false; return false;
} }
@@ -498,14 +498,6 @@ abstract class BuilderBase
} }
} }
/**
* Get the php-src name to use for lock file lookups (supports version-specific names like php-src-8.2)
*/
protected function getPhpSrcName(): string
{
return getenv('SPC_PHP_SRC_NAME') ?: 'php-src';
}
/** /**
* Generate micro extension test php code. * Generate micro extension test php code.
*/ */

View File

@@ -96,7 +96,8 @@ class Extension
fn ($x) => $x->getStaticLibFiles(), fn ($x) => $x->getStaticLibFiles(),
$this->getLibraryDependencies(recursive: true) $this->getLibraryDependencies(recursive: true)
); );
return implode(' ', $ret); $libs = implode(' ', $ret);
return deduplicate_flags($libs);
} }
/** /**
@@ -398,13 +399,12 @@ class Extension
$dependency->buildShared([...$visited, $this->getName()]); $dependency->buildShared([...$visited, $this->getName()]);
} }
} }
if (Config::getExt($this->getName(), 'type') === 'addon') { $this->builder->emitPatchPoint('before-shared-ext[' . $this->getName() . ']-build');
return;
}
match (PHP_OS_FAMILY) { match (PHP_OS_FAMILY) {
'Darwin', 'Linux' => $this->buildUnixShared(), 'Darwin', 'Linux' => $this->buildUnixShared(),
default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'), default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'),
}; };
$this->builder->emitPatchPoint('after-shared-ext[' . $this->getName() . ']-build');
} catch (SPCException $e) { } catch (SPCException $e) {
$e->bindExtensionInfo(['extension_name' => $this->getName()]); $e->bindExtensionInfo(['extension_name' => $this->getName()]);
throw $e; throw $e;
@@ -455,12 +455,17 @@ class Extension
// process *.so file // process *.so file
$soFile = BUILD_MODULES_PATH . '/' . $this->getName() . '.so'; $soFile = BUILD_MODULES_PATH . '/' . $this->getName() . '.so';
$soDest = $soFile;
preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches);
if (!empty($matches[1])) {
$soDest = str_replace('.so', '-' . $matches[1] . '.so', $soFile);
}
if (!file_exists($soFile)) { if (!file_exists($soFile)) {
throw new ValidationException("extension {$this->getName()} build failed: {$soFile} not found", validation_module: "Extension {$this->getName()} build"); throw new ValidationException("extension {$this->getName()} build failed: {$soFile} not found", validation_module: "Extension {$this->getName()} build");
} }
/** @var UnixBuilderBase $builder */ /** @var UnixBuilderBase $builder */
$builder = $this->builder; $builder = $this->builder;
$builder->deployBinary($soFile, $soFile, false); $builder->deployBinary($soFile, $soDest, false);
} }
/** /**
@@ -538,7 +543,7 @@ class Extension
*/ */
protected function getSharedExtensionEnv(): array protected function getSharedExtensionEnv(): array
{ {
$config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this); $config = (new SPCConfigUtil($this->builder, ['no_php' => true]))->getExtensionConfig($this);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group '; $preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group '; $postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
@@ -546,6 +551,7 @@ class Extension
'CFLAGS' => $config['cflags'], 'CFLAGS' => $config['cflags'],
'CXXFLAGS' => $config['cflags'], 'CXXFLAGS' => $config['cflags'],
'LDFLAGS' => $config['ldflags'], 'LDFLAGS' => $config['ldflags'],
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH, 'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
]; ];

View File

@@ -365,6 +365,27 @@ abstract class LibraryBase
protected function isLibraryInstalled(): bool protected function isLibraryInstalled(): bool
{ {
if ($pkg_configs = Config::getLib(static::NAME, 'pkg-configs', [])) {
$pkg_config_path = getenv('PKG_CONFIG_PATH') ?: '';
$search_paths = array_unique(array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)));
foreach ($pkg_configs as $name) {
$found = false;
foreach ($search_paths as $path) {
if (file_exists($path . "/{$name}.pc")) {
$found = true;
break;
}
}
if (!$found) {
return false;
}
}
// allow using system dependencies if pkg_config_path is explicitly defined
if (count($search_paths) > 1) {
return true;
}
}
foreach (Config::getLib(static::NAME, 'static-libs', []) as $name) { foreach (Config::getLib(static::NAME, 'static-libs', []) as $name) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
return false; return false;

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
#[CustomExt('com_dotnet')]
class com_dotnet extends Extension
{
public function getWindowsConfigureArg(bool $shared = false): string
{
return '--enable-com-dotnet=yes';
}
}

View File

@@ -5,11 +5,21 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\builder\linux\SystemUtil;
use SPC\store\SourcePatcher;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('ffi')] #[CustomExt('ffi')]
class ffi extends Extension class ffi extends Extension
{ {
public function patchBeforeBuildconf(): bool
{
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::getOSRelease()['dist'] === 'centos') {
return SourcePatcher::patchFfiCentos7FixO3strncmp();
}
return false;
}
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
return '--with-ffi' . ($shared ? '=shared' : '') . ' --enable-zend-signals'; return '--with-ffi' . ($shared ? '=shared' : '') . ' --enable-zend-signals';

View File

@@ -11,7 +11,6 @@ use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
#[CustomExt('grpc')] #[CustomExt('grpc')]
class grpc extends Extension class grpc extends Extension
@@ -21,18 +20,50 @@ class grpc extends Extension
if ($this->builder instanceof WindowsBuilder) { if ($this->builder instanceof WindowsBuilder) {
throw new ValidationException('grpc extension does not support windows yet'); throw new ValidationException('grpc extension does not support windows yet');
} }
// Fix deprecated PHP API usage in call.c
FileSystem::replaceFileStr( FileSystem::replaceFileStr(
$this->source_dir . '/src/php/ext/grpc/call.c', "{$this->source_dir}/src/php/ext/grpc/call.c",
'zend_exception_get_default(TSRMLS_C),', 'zend_exception_get_default(TSRMLS_C),',
'zend_ce_exception,', 'zend_ce_exception,',
); );
if (SPCTarget::getTargetOS() === 'Darwin') {
FileSystem::replaceFileRegex( $config_m4 = <<<'M4'
$this->source_dir . '/config.m4', PHP_ARG_ENABLE(grpc, [whether to enable grpc support], [AS_HELP_STRING([--enable-grpc], [Enable grpc support])])
'/GRPC_LIBDIR=.*$/m',
'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"' if test "$PHP_GRPC" != "no"; then
); PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/include)
} PHP_ADD_INCLUDE(PHP_EXT_SRCDIR()/src/php/ext/grpc)
GRPC_LIBDIR=@@build_lib_path@@
PHP_ADD_LIBPATH($GRPC_LIBDIR)
PHP_ADD_LIBRARY(grpc,,GRPC_SHARED_LIBADD)
LIBS="-lpthread $LIBS"
PHP_ADD_LIBRARY(pthread)
case $host in
*darwin*)
PHP_ADD_LIBRARY(c++,1,GRPC_SHARED_LIBADD)
;;
*)
PHP_ADD_LIBRARY(stdc++,1,GRPC_SHARED_LIBADD)
PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD)
PHP_ADD_LIBRARY(rt)
;;
esac
PHP_NEW_EXTENSION(grpc, @grpc_c_files@, $ext_shared, , -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1)
PHP_SUBST(GRPC_SHARED_LIBADD)
PHP_INSTALL_HEADERS([ext/grpc], [php_grpc.h])
fi
M4;
$replace = get_pack_replace();
// load grpc c files from src/php/ext/grpc
$c_files = glob($this->source_dir . '/src/php/ext/grpc/*.c');
$replace['@grpc_c_files@'] = implode(" \\\n ", array_map(fn ($f) => 'src/php/ext/grpc/' . basename($f), $c_files));
$config_m4 = str_replace(array_keys($replace), array_values($replace), $config_m4);
file_put_contents($this->source_dir . '/config.m4', $config_m4);
copy($this->source_dir . '/src/php/ext/grpc/php_grpc.h', $this->source_dir . '/php_grpc.h');
return true; return true;
} }
@@ -48,7 +79,6 @@ class grpc extends Extension
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
{ {
parent::patchBeforeMake(); parent::patchBeforeMake();
// add -Wno-strict-prototypes
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes'); GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true; return true;
} }

View File

@@ -18,6 +18,9 @@ class memcache extends Extension
public function patchBeforeBuildconf(): bool public function patchBeforeBuildconf(): bool
{ {
if (!$this->isBuildStatic()) {
return false;
}
FileSystem::replaceFileStr( FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4', SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
'if test -d $abs_srcdir/src ; then', 'if test -d $abs_srcdir/src ; then',
@@ -44,6 +47,24 @@ EOF
return true; return true;
} }
public function patchBeforeSharedConfigure(): bool
{
if (!$this->isBuildShared()) {
return false;
}
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
'if test -d $abs_srcdir/main ; then',
'if test -d $abs_srcdir/src ; then',
);
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/memcache/config9.m4',
'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"',
'export CPPFLAGS="$CPPFLAGS $INCLUDES"',
);
return true;
}
protected function getExtraEnv(): array protected function getExtraEnv(): array
{ {
return ['CFLAGS' => '-std=c17']; return ['CFLAGS' => '-std=c17'];

View File

@@ -24,25 +24,6 @@ class password_argon2 extends Extension
} }
} }
public function patchBeforeMake(): bool
{
$patched = parent::patchBeforeMake();
if ($this->builder->getLib('libsodium') !== null) {
$extraLibs = getenv('SPC_EXTRA_LIBS');
if ($extraLibs !== false) {
$extraLibs = str_replace(
[BUILD_LIB_PATH . '/libargon2.a', BUILD_LIB_PATH . '/libsodium.a'],
['', BUILD_LIB_PATH . '/libargon2.a ' . BUILD_LIB_PATH . '/libsodium.a'],
$extraLibs,
);
$extraLibs = trim(preg_replace('/\s+/', ' ', $extraLibs)); // normalize spacing
f_putenv('SPC_EXTRA_LIBS=' . $extraLibs);
return true;
}
}
return $patched;
}
public function getConfigureArg(bool $shared = false): string public function getConfigureArg(bool $shared = false): string
{ {
if ($this->builder->getLib('openssl') !== null) { if ($this->builder->getLib('openssl') !== null) {

View File

@@ -45,4 +45,11 @@ class spx extends Extension
FileSystem::copy($this->source_dir . '/src/php_spx.h', $this->source_dir . '/php_spx.h'); FileSystem::copy($this->source_dir . '/src/php_spx.h', $this->source_dir . '/php_spx.h');
return true; return true;
} }
public function getSharedExtensionEnv(): array
{
$env = parent::getSharedExtensionEnv();
$env['SPX_SHARED_LIBADD'] = $env['LIBS'];
return $env;
}
} }

View File

@@ -50,19 +50,16 @@ class swoole extends Extension
// commonly used feature: coroutine-time // commonly used feature: coroutine-time
$arg .= ' --enable-swoole-coro-time --with-pic'; $arg .= ' --enable-swoole-coro-time --with-pic';
$arg .= ' --enable-swoole-ssh --enable-swoole-curl';
$arg .= $this->builder->getOption('enable-zts') ? ' --enable-swoole-thread --disable-thread-context' : ' --disable-swoole-thread --enable-thread-context'; $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 features that only require libraries // additional features that only require libraries
$arg .= $this->builder->getLib('libcares') ? ' --enable-cares' : ''; $arg .= $this->builder->getLib('libcares') ? ' --enable-cares' : '';
$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('nghttp2') ? (' --with-nghttp2-dir=' . BUILD_ROOT_PATH) : '';
$arg .= $this->builder->getLib('zstd') ? ' --enable-zstd' : ''; $arg .= $this->builder->getLib('zstd') ? ' --enable-zstd' : '';
$arg .= $this->builder->getLib('liburing') ? ' --enable-iouring' : ''; $arg .= $this->builder->getLib('liburing') ? ' --enable-iouring --enable-uring-socket' : '';
$arg .= $this->builder->getExt('sockets') ? ' --enable-sockets' : ''; $arg .= $this->builder->getExt('sockets') ? ' --enable-sockets' : '';
// enable additional features that require the pdo extension, but conflict with pdo_* extensions // enable additional features that require the pdo extension, but conflict with pdo_* extensions
@@ -74,6 +71,7 @@ class swoole extends Extension
$config = (new SPCConfigUtil($this->builder))->getLibraryConfig($this->builder->getLib('unixodbc')); $config = (new SPCConfigUtil($this->builder))->getLibraryConfig($this->builder->getLib('unixodbc'));
$arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"'; $arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"';
} }
$arg .= $this->builder->getExt('ftp')?->isBuildStatic() ? ' --disable-swoole-ftp' : ' --enable-swoole-ftp';
if ($this->getExtVersion() >= '6.1.0') { if ($this->getExtVersion() >= '6.1.0') {
$arg .= ' --enable-swoole-stdext'; $arg .= ' --enable-swoole-stdext';

View File

@@ -162,7 +162,7 @@ class LinuxBuilder extends UnixBuilderBase
throw new WrongUsageException( throw new WrongUsageException(
"You're building against musl libc statically (the default on Linux), but you're trying to build shared extensions.\n" . "You're building against musl libc statically (the default on Linux), but you're trying to build shared extensions.\n" .
'Static musl libc does not implement `dlopen`, so your php binary is not able to load shared extensions.' . "\n" . 'Static musl libc does not implement `dlopen`, so your php binary is not able to load shared extensions.' . "\n" .
'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, or use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc`.' 'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc` or use SPC_MUSL_DYNAMIC=true on alpine.'
); );
} }
logger()->info('Building shared extensions...'); logger()->info('Building shared extensions...');
@@ -283,11 +283,14 @@ class LinuxBuilder extends UnixBuilderBase
// process libphp.so for shared embed // process libphp.so for shared embed
$libphpSo = BUILD_LIB_PATH . '/libphp.so'; $libphpSo = BUILD_LIB_PATH . '/libphp.so';
$libphpSoDest = BUILD_LIB_PATH . '/libphp.so';
if (file_exists($libphpSo)) { if (file_exists($libphpSo)) {
// post actions: rename libphp.so to libphp-<release>.so if -release is set in LDFLAGS
$this->processLibphpSoFile($libphpSo);
// deploy libphp.so // deploy libphp.so
$this->deployBinary($libphpSo, $libphpSo, false); preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches);
if (!empty($matches[1])) {
$libphpSoDest = str_replace('.so', '-' . $matches[1] . '.so', $libphpSo);
}
$this->deployBinary($libphpSo, $libphpSoDest, false);
} }
// process shared extensions build-with-php // process shared extensions build-with-php
@@ -324,74 +327,6 @@ class LinuxBuilder extends UnixBuilderBase
]); ]);
} }
private function processLibphpSoFile(string $libphpSo): void
{
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
$libDir = BUILD_LIB_PATH;
$modulesDir = BUILD_MODULES_PATH;
$realLibName = 'libphp.so';
$cwd = getcwd();
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
$release = $matches[1];
$realLibName = "libphp-{$release}.so";
$libphpRelease = "{$libDir}/{$realLibName}";
if (!file_exists($libphpRelease) && file_exists($libphpSo)) {
rename($libphpSo, $libphpRelease);
}
if (file_exists($libphpRelease)) {
chdir($libDir);
if (file_exists($libphpSo)) {
unlink($libphpSo);
}
symlink($realLibName, 'libphp.so');
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg($realLibName),
escapeshellarg($libphpRelease)
));
}
if (is_dir($modulesDir)) {
chdir($modulesDir);
foreach ($this->getExts() as $ext) {
if (!$ext->isBuildShared()) {
continue;
}
$name = $ext->getName();
$versioned = "{$name}-{$release}.so";
$unversioned = "{$name}.so";
$src = "{$modulesDir}/{$versioned}";
$dst = "{$modulesDir}/{$unversioned}";
if (is_file($src)) {
rename($src, $dst);
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg($unversioned),
escapeshellarg($dst)
));
}
}
}
chdir($cwd);
}
$target = "{$libDir}/{$realLibName}";
if (file_exists($target)) {
[, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target));
$output = implode("\n", $output);
if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) {
$currentSoname = $sonameMatch[1];
if ($currentSoname !== basename($target)) {
shell()->exec(sprintf(
'patchelf --set-soname %s %s',
escapeshellarg(basename($target)),
escapeshellarg($target)
));
}
}
}
}
/** /**
* Patch micro.sfx after UPX compression. * Patch micro.sfx after UPX compression.
* micro needs special section handling in LinuxBuilder. * micro needs special section handling in LinuxBuilder.

View File

@@ -21,6 +21,7 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; namespace SPC\builder\linux\library;
use SPC\builder\linux\SystemUtil;
use SPC\store\FileSystem; use SPC\store\FileSystem;
class openssl extends LinuxLibraryBase class openssl extends LinuxLibraryBase
@@ -51,6 +52,9 @@ class openssl extends LinuxLibraryBase
$zlib_extra = ''; $zlib_extra = '';
} }
$openssl_dir = getenv('OPENSSLDIR') ?: null;
// TODO: in v3 use the following: $openssl_dir ??= SystemUtil::getOSRelease()['dist'] === 'redhat' ? '/etc/pki/tls' : '/etc/ssl';
$openssl_dir ??= '/etc/ssl';
$ex_lib = trim($ex_lib); $ex_lib = trim($ex_lib);
shell()->cd($this->source_dir)->initializeEnv($this) shell()->cd($this->source_dir)->initializeEnv($this)
@@ -58,7 +62,7 @@ class openssl extends LinuxLibraryBase
"{$env} ./Configure no-shared {$extra} " . "{$env} ./Configure no-shared {$extra} " .
'--prefix=' . BUILD_ROOT_PATH . ' ' . '--prefix=' . BUILD_ROOT_PATH . ' ' .
'--libdir=' . BUILD_LIB_PATH . ' ' . '--libdir=' . BUILD_LIB_PATH . ' ' .
'--openssldir=/etc/ssl ' . "--openssldir={$openssl_dir} " .
"{$zlib_extra}" . "{$zlib_extra}" .
'enable-pie ' . 'enable-pie ' .
'no-legacy ' . 'no-legacy ' .

View File

@@ -74,10 +74,10 @@ trait UnixSystemUtilTrait
} }
// https://github.com/ziglang/zig/issues/24662 // https://github.com/ziglang/zig/issues/24662
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
return '-Wl,--export-dynamic'; return '-Wl,--export-dynamic'; // needs release 0.16, can be removed then
} }
// macOS // macOS/zig
if (SPCTarget::getTargetOS() !== 'Linux') { if (SPCTarget::getTargetOS() !== 'Linux' || ToolchainManager::getToolchainClass() === ZigToolchain::class) {
return "-Wl,-exported_symbols_list,{$symbol_file}"; return "-Wl,-exported_symbols_list,{$symbol_file}";
} }
return "-Wl,--dynamic-list={$symbol_file}"; return "-Wl,--dynamic-list={$symbol_file}";

View File

@@ -235,8 +235,10 @@ abstract class UnixBuilderBase extends BuilderBase
$lens .= ' -static'; $lens .= ' -static';
} }
$dynamic_exports = ''; $dynamic_exports = '';
$embedType = 'static';
// if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH // if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$embedType = 'shared';
if (PHP_OS_FAMILY === 'Darwin') { if (PHP_OS_FAMILY === 'Darwin') {
$ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH '; $ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH ';
} else { } else {
@@ -255,18 +257,19 @@ abstract class UnixBuilderBase extends BuilderBase
} }
} }
$cc = getenv('CC'); $cc = getenv('CC');
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult("{$cc} -o embed embed.c {$lens} {$dynamic_exports}"); [$ret, $out] = shell()->cd($sample_file_path)->execWithResult("{$cc} -o embed embed.c {$lens} {$dynamic_exports}");
if ($ret !== 0) { if ($ret !== 0) {
throw new ValidationException( throw new ValidationException(
'embed failed sanity check: build failed. Error message: ' . implode("\n", $out), 'embed failed to build. Error message: ' . implode("\n", $out),
validation_module: 'static libphp.a sanity check' validation_module: $embedType . ' libphp embed build sanity check'
); );
} }
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed'); [$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new ValidationException( throw new ValidationException(
'embed failed sanity check: run failed. Error message: ' . implode("\n", $output), 'embed failed to run. Error message: ' . implode("\n", $output),
validation_module: 'static libphp.a sanity check' validation_module: $embedType . ' libphp embed run sanity check'
); );
} }
} }
@@ -362,6 +365,7 @@ abstract class UnixBuilderBase extends BuilderBase
$frankenphpAppPath = $this->getOption('with-frankenphp-app'); $frankenphpAppPath = $this->getOption('with-frankenphp-app');
if ($frankenphpAppPath) { if ($frankenphpAppPath) {
$frankenphpAppPath = trim($frankenphpAppPath, "\"'");
if (!is_dir($frankenphpAppPath)) { if (!is_dir($frankenphpAppPath)) {
throw new WrongUsageException("The path provided to --with-frankenphp-app is not a valid directory: {$frankenphpAppPath}"); throw new WrongUsageException("The path provided to --with-frankenphp-app is not a valid directory: {$frankenphpAppPath}");
} }
@@ -452,6 +456,8 @@ abstract class UnixBuilderBase extends BuilderBase
'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}", 'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}",
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' . 'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' .
'-X \'github.com/caddyserver/caddy/v2/modules/caddyhttp.ServerHeader=FrankenPHP Caddy\' ' .
'-X \'github.com/caddyserver/caddy/v2.CustomBinaryName=frankenphp\' ' .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' . '-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
"v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . "v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",

View File

@@ -13,8 +13,8 @@ trait freetype
{ {
$cmake = UnixCMakeExecutor::create($this) $cmake = UnixCMakeExecutor::create($this)
->optionalLib('libpng', ...cmake_boolean_args('FT_DISABLE_PNG', true)) ->optionalLib('libpng', ...cmake_boolean_args('FT_DISABLE_PNG', true))
->optionalLib('bzip2', ...cmake_boolean_args('FT_DISABLE_BZIP2', true)) ->addConfigureArgs('-DFT_DISABLE_BZIP2=ON')
->optionalLib('brotli', ...cmake_boolean_args('FT_DISABLE_BROTLI', true)) ->addConfigureArgs('-DFT_DISABLE_BROTLI=ON')
->addConfigureArgs('-DFT_DISABLE_HARFBUZZ=ON'); ->addConfigureArgs('-DFT_DISABLE_HARFBUZZ=ON');
// fix cmake 4.0 compatibility // fix cmake 4.0 compatibility

View File

@@ -16,7 +16,11 @@ trait gettext
->addConfigureArgs( ->addConfigureArgs(
'--disable-java', '--disable-java',
'--disable-c++', '--disable-c++',
'--with-included-gettext', '--disable-d',
'--disable-rpath',
'--disable-modula2',
'--disable-libasprintf',
'--with-included-libintl',
"--with-iconv-prefix={$this->getBuildRootPath()}", "--with-iconv-prefix={$this->getBuildRootPath()}",
); );

View File

@@ -11,6 +11,11 @@ trait libavif
protected function build(): void protected function build(): void
{ {
UnixCMakeExecutor::create($this) UnixCMakeExecutor::create($this)
->optionalLib('libaom', '-DAVIF_CODEC_AOM=SYSTEM', '-DAVIF_CODEC_AOM=OFF')
->optionalLib('libsharpyuv', '-DAVIF_LIBSHARPYUV=SYSTEM', '-DAVIF_LIBSHARPYUV=OFF')
->optionalLib('libjpeg', '-DAVIF_JPEG=SYSTEM', '-DAVIF_JPEG=OFF')
->optionalLib('libxml2', '-DAVIF_LIBXML2=SYSTEM', '-DAVIF_LIBXML2=OFF')
->optionalLib('libpng', '-DAVIF_LIBPNG=SYSTEM', '-DAVIF_LIBPNG=OFF')
->addConfigureArgs('-DAVIF_LIBYUV=OFF') ->addConfigureArgs('-DAVIF_LIBYUV=OFF')
->build(); ->build();
// patch pkgconfig // patch pkgconfig

View File

@@ -11,7 +11,10 @@ trait libde265
protected function build(): void protected function build(): void
{ {
UnixCMakeExecutor::create($this) UnixCMakeExecutor::create($this)
->addConfigureArgs('-DENABLE_SDL=OFF') ->addConfigureArgs(
'-DENABLE_SDL=OFF',
'-DENABLE_DECODER=OFF'
)
->build(); ->build();
$this->patchPkgconfPrefix(['libde265.pc']); $this->patchPkgconfPrefix(['libde265.pc']);
} }

View File

@@ -14,6 +14,7 @@ trait libjpeg
->addConfigureArgs( ->addConfigureArgs(
'-DENABLE_STATIC=ON', '-DENABLE_STATIC=ON',
'-DENABLE_SHARED=OFF', '-DENABLE_SHARED=OFF',
'-DWITH_SYSTEM_ZLIB=ON'
) )
->build(); ->build();
// patch pkgconfig // patch pkgconfig

View File

@@ -4,29 +4,14 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\PkgConfigUtil; use SPC\util\PkgConfigUtil;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
trait postgresql trait postgresql
{ {
public function patchBeforeBuild(): bool public function patchBeforeBuild(): bool
{ {
// fix aarch64 build on glibc 2.17 (e.g. CentOS 7)
if (SPCTarget::getLibcVersion() === '2.17' && GNU_ARCH === 'aarch64') {
try {
FileSystem::replaceFileStr("{$this->source_dir}/src/port/pg_popcount_aarch64.c", 'HWCAP_SVE', '0');
FileSystem::replaceFileStr(
"{$this->source_dir}/src/port/pg_crc32c_armv8_choose.c",
'#if defined(__linux__) && !defined(__aarch64__) && !defined(HWCAP2_CRC32)',
'#if defined(__linux__) && !defined(HWCAP_CRC32)'
);
} catch (FileSystemException) {
// allow file not-existence to make it compatible with old and new version
}
}
// skip the test on platforms where libpq infrastructure may be provided by statically-linked libraries // skip the test on platforms where libpq infrastructure may be provided by statically-linked libraries
FileSystem::replaceFileStr("{$this->source_dir}/src/interfaces/libpq/Makefile", 'invokes exit\'; exit 1;', 'invokes exit\';'); FileSystem::replaceFileStr("{$this->source_dir}/src/interfaces/libpq/Makefile", 'invokes exit\'; exit 1;', 'invokes exit\';');
// disable shared libs build // disable shared libs build
@@ -50,7 +35,7 @@ trait postgresql
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false)); $config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false));
$env_vars = [ $env_vars = [
'CFLAGS' => $config['cflags'], 'CFLAGS' => $config['cflags'] . ' -std=c17',
'CPPFLAGS' => '-DPIC', 'CPPFLAGS' => '-DPIC',
'LDFLAGS' => $config['ldflags'], 'LDFLAGS' => $config['ldflags'],
'LIBS' => $config['libs'], 'LIBS' => $config['libs'],
@@ -108,8 +93,7 @@ trait postgresql
// remove dynamic libs // remove dynamic libs
shell()->cd($this->source_dir . '/build') shell()->cd($this->source_dir . '/build')
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so.*") ->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so*")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.so")
->exec("rm -rf {$this->getBuildRootPath()}/lib/*.dylib"); ->exec("rm -rf {$this->getBuildRootPath()}/lib/*.dylib");
FileSystem::replaceFileStr("{$this->getLibDir()}/pkgconfig/libpq.pc", '-lldap', '-lldap -llber'); FileSystem::replaceFileStr("{$this->getLibDir()}/pkgconfig/libpq.pc", '-lldap', '-lldap -llber');

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
trait unixodbc trait unixodbc
@@ -30,7 +31,15 @@ trait unixodbc
'--enable-gui=no', '--enable-gui=no',
) )
->make(); ->make();
$this->patchPkgconfPrefix(['odbc.pc', 'odbccr.pc', 'odbcinst.pc']); $pkgConfigs = ['odbc.pc', 'odbccr.pc', 'odbcinst.pc'];
$this->patchPkgconfPrefix($pkgConfigs);
foreach ($pkgConfigs as $file) {
FileSystem::replaceFileStr(
BUILD_LIB_PATH . "/pkgconfig/{$file}",
'$(top_build_prefix)libltdl/libltdlc.la',
''
);
}
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();
} }
} }

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class brotli extends WindowsLibraryBase
{
public const NAME = 'brotli';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBROTLI_BUILD_TOOLS=OFF ' .
'-DBROTLI_BUNDLED_MODE=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -30,7 +30,6 @@ class curl extends WindowsLibraryBase
'-DCMAKE_BUILD_TYPE=Release ' . '-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_SHARED_LIBS=OFF ' .
'-DBUILD_STATIC_LIBS=ON ' . '-DBUILD_STATIC_LIBS=ON ' .
'-DCURL_STATICLIB=ON ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' . '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' .
'-DBUILD_CURL_EXE=OFF ' . // disable curl.exe '-DBUILD_CURL_EXE=OFF ' . // disable curl.exe
'-DBUILD_TESTING=OFF ' . // disable tests '-DBUILD_TESTING=OFF ' . // disable tests
@@ -42,9 +41,9 @@ class curl extends WindowsLibraryBase
'-DCURL_USE_OPENSSL=OFF ' . // disable openssl due to certificate issue '-DCURL_USE_OPENSSL=OFF ' . // disable openssl due to certificate issue
'-DCURL_ENABLE_SSL=ON ' . '-DCURL_ENABLE_SSL=ON ' .
'-DUSE_NGHTTP2=ON ' . // enable nghttp2 '-DUSE_NGHTTP2=ON ' . // enable nghttp2
'-DSHARE_LIB_OBJECT=OFF ' . // disable shared lib object
'-DCURL_USE_LIBSSH2=ON ' . // enable libssh2 '-DCURL_USE_LIBSSH2=ON ' . // enable libssh2
'-DENABLE_IPV6=ON ' . // enable ipv6 '-DENABLE_IPV6=ON ' . // enable ipv6
'-DNGHTTP2_CFLAGS="/DNGHTTP2_STATICLIB" ' .
$alt $alt
) )
->execWithWrapper( ->execWithWrapper(
@@ -53,5 +52,7 @@ class curl extends WindowsLibraryBase
); );
// move libcurl.lib to libcurl_a.lib // move libcurl.lib to libcurl_a.lib
rename(BUILD_LIB_PATH . '\libcurl.lib', BUILD_LIB_PATH . '\libcurl_a.lib'); rename(BUILD_LIB_PATH . '\libcurl.lib', BUILD_LIB_PATH . '\libcurl_a.lib');
FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\curl\curl.h', '#ifdef CURL_STATICLIB', '#if 1');
} }
} }

View File

@@ -24,6 +24,8 @@ class freetype extends WindowsLibraryBase
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' . '-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_SHARED_LIBS=OFF ' .
'-DFT_DISABLE_BROTLI=TRUE ' .
'-DFT_DISABLE_BZIP2=TRUE ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
) )
->execWithWrapper( ->execWithWrapper(

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class libaom extends WindowsLibraryBase
{
public const NAME = 'libaom';
protected function build(): void
{
// libaom source tree contains a build/cmake/ directory with its own
// cmake modules, so we must use a different name for the build dir.
FileSystem::resetDir($this->source_dir . '\builddir');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-S . -B builddir ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DAOM_TARGET_CPU=generic ' .
'-DENABLE_DOCS=OFF ' .
'-DENABLE_EXAMPLES=OFF ' .
'-DENABLE_TESTDATA=OFF ' .
'-DENABLE_TESTS=OFF ' .
'-DENABLE_TOOLS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build builddir --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -29,11 +29,16 @@ class nghttp2 extends WindowsLibraryBase
'-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_SHARED_LIBS=OFF ' .
'-DENABLE_STATIC_CRT=ON ' . '-DENABLE_STATIC_CRT=ON ' .
'-DENABLE_LIB_ONLY=ON ' . '-DENABLE_LIB_ONLY=ON ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' .
'-DENABLE_STATIC_CRT=ON ' .
'-DENABLE_DOC=OFF ' .
'-DBUILD_TESTING=OFF '
) )
->execWithWrapper( ->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'), $this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}" "--build build --config Release --target install -j{$this->builder->concurrency}"
); );
FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\nghttp2\nghttp2.h', '#ifdef NGHTTP2_STATICLIB', '#if 1');
} }
} }

View File

@@ -31,8 +31,24 @@ class zlib extends WindowsLibraryBase
$this->builder->makeSimpleWrapper('cmake'), $this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}" "--build build --config Release --target install -j{$this->builder->concurrency}"
); );
copy(BUILD_LIB_PATH . '\zlibstatic.lib', BUILD_LIB_PATH . '\zlib_a.lib'); $detect_list = [
unlink(BUILD_ROOT_PATH . '\bin\zlib.dll'); 'zlibstatic.lib',
unlink(BUILD_LIB_PATH . '\zlib.lib'); 'zs.lib',
'libzs.lib',
'libz.lib',
];
foreach ($detect_list as $item) {
if (file_exists(BUILD_LIB_PATH . '\\' . $item)) {
FileSystem::copy(BUILD_LIB_PATH . '\\' . $item, BUILD_LIB_PATH . '\zlib_a.lib');
FileSystem::copy(BUILD_LIB_PATH . '\\' . $item, BUILD_LIB_PATH . '\zlibstatic.lib');
break;
}
}
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '\bin\zlib.dll');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\zlib.lib');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\libz.dll');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\libz.lib');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\z.lib');
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '\z.dll');
} }
} }

View File

@@ -49,7 +49,6 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('with-micro-logo', null, InputOption::VALUE_REQUIRED, 'Use custom .ico for micro.sfx (windows only)'); $this->addOption('with-micro-logo', null, InputOption::VALUE_REQUIRED, 'Use custom .ico for micro.sfx (windows only)');
$this->addOption('enable-micro-win32', null, null, 'Enable win32 mode for phpmicro (Windows only)'); $this->addOption('enable-micro-win32', null, null, 'Enable win32 mode for phpmicro (Windows only)');
$this->addOption('with-frankenphp-app', null, InputOption::VALUE_REQUIRED, 'Path to a folder to be embedded in FrankenPHP'); $this->addOption('with-frankenphp-app', null, InputOption::VALUE_REQUIRED, 'Path to a folder to be embedded in FrankenPHP');
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'PHP version to build (e.g., 8.2, 8.3, 8.4). Uses php-src-X.Y if available, otherwise php-src');
} }
public function handle(): int public function handle(): int
@@ -121,31 +120,6 @@ class BuildPHPCommand extends BuildCommand
logger()->warning('Some cases micro.sfx cannot be packed via UPX due to dynamic size bug, be aware!'); logger()->warning('Some cases micro.sfx cannot be packed via UPX due to dynamic size bug, be aware!');
} }
} }
// Determine which php-src to use based on --with-php option
$php_version = $this->getOption('with-php');
if ($php_version !== null) {
// Check if version-specific php-src exists in lock file
$version_specific_name = "php-src-{$php_version}";
$lock_file_path = DOWNLOAD_PATH . '/.lock.json';
if (file_exists($lock_file_path)) {
$lock_content = json_decode(file_get_contents($lock_file_path), true);
if (isset($lock_content[$version_specific_name])) {
// Use version-specific php-src
f_putenv("SPC_PHP_SRC_NAME={$version_specific_name}");
logger()->info("Building with PHP {$php_version} (using {$version_specific_name})");
} else {
logger()->error('No php-src found in downloads. Please run download command first.');
return static::FAILURE;
}
} else {
logger()->error('Lock file not found. Please download sources first.');
return static::FAILURE;
}
} else {
f_putenv('SPC_PHP_SRC_NAME=php-src');
}
// create builder // create builder
$builder = BuilderProvider::makeBuilderByInput($this->input); $builder = BuilderProvider::makeBuilderByInput($this->input);
$include_suggest_ext = $this->getOption('with-suggested-exts'); $include_suggest_ext = $this->getOption('with-suggested-exts');

View File

@@ -9,9 +9,7 @@ use SPC\exception\DownloaderException;
use SPC\exception\SPCException; use SPC\exception\SPCException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\store\source\CustomSourceBase;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
@@ -29,10 +27,10 @@ class DownloadCommand extends BaseCommand
public function configure(): void public function configure(): void
{ {
$this->addArgument('sources', InputArgument::OPTIONAL, 'The sources will be compiled, comma separated'); $this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated');
$this->addOption('shallow-clone', null, null, 'Clone shallow'); $this->addOption('shallow-clone', null, null, 'Clone shallow');
$this->addOption('with-openssl11', null, null, 'Use openssl 1.1'); $this->addOption('with-openssl11', null, null, 'Use openssl 1.1');
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format, comma-separated for multiple versions (default 8.4)', '8.4'); $this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format (default 8.5)', '8.5');
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch'); $this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed'); $this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"'); $this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');
@@ -45,31 +43,10 @@ class DownloadCommand extends BaseCommand
$this->addOption('retry', 'R', InputOption::VALUE_REQUIRED, 'Set retry time when downloading failed (default: 0)', '0'); $this->addOption('retry', 'R', InputOption::VALUE_REQUIRED, 'Set retry time when downloading failed (default: 0)', '0');
$this->addOption('prefer-pre-built', 'P', null, 'Download pre-built libraries when available'); $this->addOption('prefer-pre-built', 'P', null, 'Download pre-built libraries when available');
$this->addOption('no-alt', null, null, 'Do not download alternative sources'); $this->addOption('no-alt', null, null, 'Do not download alternative sources');
$this->addOption('update', null, null, 'Check and update downloaded sources');
} }
public function initialize(InputInterface $input, OutputInterface $output): void public function initialize(InputInterface $input, OutputInterface $output): void
{ {
// mode: --update
if ($input->getOption('update') && empty($input->getArgument('sources')) && empty($input->getOption('for-extensions')) && empty($input->getOption('for-libs'))) {
if (!file_exists(LockFile::LOCK_FILE)) {
parent::initialize($input, $output);
return;
}
$lock_content = json_decode(file_get_contents(LockFile::LOCK_FILE), true);
if (is_array($lock_content)) {
// Filter out pre-built sources
$sources_to_check = array_filter($lock_content, function ($name) {
return
!str_contains($name, '-Linux-') &&
!str_contains($name, '-Windows-') &&
!str_contains($name, '-Darwin-');
}, ARRAY_FILTER_USE_KEY);
$input->setArgument('sources', implode(',', array_keys($sources_to_check)));
}
parent::initialize($input, $output);
return;
}
// mode: --all // mode: --all
if ($input->getOption('all')) { if ($input->getOption('all')) {
$input->setArgument('sources', implode(',', array_keys(Config::getSources()))); $input->setArgument('sources', implode(',', array_keys(Config::getSources())));
@@ -117,29 +94,17 @@ class DownloadCommand extends BaseCommand
return $this->downloadFromZip($path); return $this->downloadFromZip($path);
} }
if ($this->getOption('update')) { // Define PHP major version
return $this->handleUpdate(); $ver = $this->php_major_ver = $this->getOption('with-php');
} define('SPC_BUILD_PHP_VERSION', $ver);
if ($ver !== 'git' && !preg_match('/^\d+\.\d+$/', $ver)) {
// Define PHP major version(s) // If not git, we need to check the version format
$php_versions_str = $this->getOption('with-php'); if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $ver)) {
$php_versions = array_map('trim', explode(',', $php_versions_str)); logger()->error("bad version arg: {$ver}, x.y or x.y.z required!");
return static::FAILURE;
// Validate all versions
foreach ($php_versions as $ver) {
if ($ver !== 'git' && !preg_match('/^\d+\.\d+$/', $ver)) {
// If not git, we need to check the version format
if (!preg_match('/^\d+\.\d+(\.\d+)?$/', $ver)) {
logger()->error("bad version arg: {$ver}, x.y or x.y.z required!");
return static::FAILURE;
}
} }
} }
// Set the first version as the default for backward compatibility
$this->php_major_ver = $php_versions[0];
define('SPC_BUILD_PHP_VERSION', $this->php_major_ver);
// retry // retry
$retry = (int) $this->getOption('retry'); $retry = (int) $this->getOption('retry');
f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry); f_putenv('SPC_DOWNLOAD_RETRIES=' . $retry);
@@ -160,20 +125,6 @@ class DownloadCommand extends BaseCommand
$chosen_sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources')))); $chosen_sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources'))));
// Handle multiple PHP versions
// If php-src is in the sources, replace it with version-specific sources
if (in_array('php-src', $chosen_sources)) {
// Remove php-src from the list
$chosen_sources = array_diff($chosen_sources, ['php-src']);
// Add version-specific php-src for each version
foreach ($php_versions as $ver) {
$version_specific_name = "php-src-{$ver}";
$chosen_sources[] = $version_specific_name;
// Store the version for this specific php-src
f_putenv("SPC_PHP_VERSION_{$version_specific_name}={$ver}");
}
}
$sss = $this->getOption('ignore-cache-sources'); $sss = $this->getOption('ignore-cache-sources');
if ($sss === false) { if ($sss === false) {
// false is no-any-ignores, that is, default. // false is no-any-ignores, that is, default.
@@ -250,16 +201,7 @@ class DownloadCommand extends BaseCommand
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom git: {$new_config['url']}"); logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom git: {$new_config['url']}");
Downloader::downloadSource($source, $new_config, true); Downloader::downloadSource($source, $new_config, true);
} else { } else {
// Handle version-specific php-src (php-src-8.2, php-src-8.3, etc.) $config = Config::getSource($source);
if (preg_match('/^php-src-[\d.]+$/', $source)) {
$config = Config::getSource('php-src');
if ($config === null) {
logger()->error('php-src configuration not found in source.json');
return static::FAILURE;
}
} else {
$config = Config::getSource($source);
}
// Prefer pre-built, we need to search pre-built library // Prefer pre-built, we need to search pre-built library
if ($this->getOption('prefer-pre-built') && ($config['provide-pre-built'] ?? false) === true) { if ($this->getOption('prefer-pre-built') && ($config['provide-pre-built'] ?? false) === true) {
// We need to replace pattern // We need to replace pattern
@@ -280,9 +222,8 @@ class DownloadCommand extends BaseCommand
logger()->warning("Pre-built content not found for {$source}, fallback to source download"); logger()->warning("Pre-built content not found for {$source}, fallback to source download");
} }
logger()->info("[{$ni}/{$cnt}] Downloading source {$source}"); logger()->info("[{$ni}/{$cnt}] Downloading source {$source}");
$force_download = $force_all || in_array($source, $force_list) || str_starts_with($source, 'php-src-') && in_array('php-src', $force_list);
try { try {
Downloader::downloadSource($source, $config, $force_download); Downloader::downloadSource($source, $config, $force_all || in_array($source, $force_list));
} catch (SPCException $e) { } catch (SPCException $e) {
// if `--no-alt` option is set, we will not download alternative sources // if `--no-alt` option is set, we will not download alternative sources
if ($this->getOption('no-alt')) { if ($this->getOption('no-alt')) {
@@ -300,7 +241,7 @@ class DownloadCommand extends BaseCommand
logger()->notice("Trying to download alternative sources for {$source}"); logger()->notice("Trying to download alternative sources for {$source}");
$alt_config = array_merge($config, $alt_sources); $alt_config = array_merge($config, $alt_sources);
} }
Downloader::downloadSource($source, $alt_config, $force_download); Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list));
} }
} }
} }
@@ -421,284 +362,4 @@ class DownloadCommand extends BaseCommand
} }
return static::FAILURE; return static::FAILURE;
} }
private function handleUpdate(): int
{
logger()->info('Checking sources for updates...');
// Get lock file content
$lock_file_path = LockFile::LOCK_FILE;
if (!file_exists($lock_file_path)) {
logger()->warning('No lock file found. Please download sources first using "bin/spc download"');
return static::FAILURE;
}
$lock_content = json_decode(file_get_contents($lock_file_path), true);
if ($lock_content === null || !is_array($lock_content)) {
logger()->error('Failed to parse lock file');
return static::FAILURE;
}
// Filter sources to check
$sources_arg = $this->getArgument('sources');
if (!empty($sources_arg)) {
$requested_sources = array_map('trim', array_filter(explode(',', $sources_arg)));
$sources_to_check = [];
foreach ($requested_sources as $source) {
if (isset($lock_content[$source])) {
$sources_to_check[$source] = $lock_content[$source];
} else {
logger()->warning("Source '{$source}' not found in lock file, skipping");
}
}
} else {
$sources_to_check = $lock_content;
}
// Filter out pre-built sources (they are derivatives)
$sources_to_check = array_filter($sources_to_check, function ($lock_item, $name) {
// Skip pre-built sources (they contain OS/arch in the name)
if (str_contains($name, '-Linux-') || str_contains($name, '-Windows-') || str_contains($name, '-Darwin-')) {
logger()->debug("Skipping pre-built source: {$name}");
return false;
}
return true;
}, ARRAY_FILTER_USE_BOTH);
if (empty($sources_to_check)) {
logger()->warning('No sources to check');
return static::FAILURE;
}
$total = count($sources_to_check);
$current = 0;
$updated_sources = [];
foreach ($sources_to_check as $name => $lock_item) {
++$current;
try {
// Handle version-specific php-src (php-src-8.2, php-src-8.3, etc.)
if (preg_match('/^php-src-[\d.]+$/', $name)) {
$config = Config::getSource('php-src');
} else {
$config = Config::getSource($name);
}
if ($config === null) {
logger()->warning("[{$current}/{$total}] Source '{$name}' not found in source config, skipping");
continue;
}
// Check and update based on source type
$source_type = $lock_item['source_type'] ?? 'unknown';
if ($source_type === SPC_SOURCE_ARCHIVE) {
if ($this->checkArchiveSourceUpdate($name, $lock_item, $config, $current, $total)) {
$updated_sources[] = $name;
}
} elseif ($source_type === SPC_SOURCE_GIT) {
if ($this->checkGitSourceUpdate($name, $lock_item, $config, $current, $total)) {
$updated_sources[] = $name;
}
} elseif ($source_type === SPC_SOURCE_LOCAL) {
logger()->debug("[{$current}/{$total}] Source '{$name}' is local, skipping");
} else {
logger()->warning("[{$current}/{$total}] Unknown source type '{$source_type}' for '{$name}', skipping");
}
} catch (\Throwable $e) {
logger()->error("[{$current}/{$total}] Error checking '{$name}': {$e->getMessage()}");
continue;
}
}
// Output summary
if (empty($updated_sources)) {
logger()->info('All sources are up to date.');
} else {
logger()->info('Updated sources: ' . implode(', ', $updated_sources));
// Write updated sources to file
$date = date('Y-m-d');
$update_file = DOWNLOAD_PATH . '/.update-' . $date . '.txt';
if (file_exists($update_file)) {
$existing_content = file_get_contents($update_file);
$existing_sources = array_map('trim', explode(',', $existing_content));
$updated_sources = array_unique(array_merge($existing_sources, $updated_sources));
}
$content = implode(',', $updated_sources);
file_put_contents($update_file, $content);
logger()->debug("Updated sources written to: {$update_file}");
}
return static::SUCCESS;
}
private function checkCustomSourceUpdate(string $name, array $lock, array $config, int $current, int $total): ?array
{
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
foreach ($classes as $class) {
// Support php-src and php-src-X.Y patterns
$matches = ($class::NAME === $name) ||
($class::NAME === 'php-src' && preg_match('/^php-src(-[\d.]+)?$/', $name));
if (is_a($class, CustomSourceBase::class, true) && $matches) {
try {
$config['source_name'] = $name;
return (new $class())->update($lock, $config);
} catch (\Throwable $e) {
logger()->warning("[{$current}/{$total}] Failed to check '{$name}': {$e->getMessage()}");
return null;
}
}
}
logger()->debug("[{$current}/{$total}] Custom source handler for '{$name}' not found");
return null;
}
/**
* Check and update an archive source
*
* @param string $name Source name
* @param array $lock Lock file entry
* @param array $config Source configuration
* @param int $current Current progress number
* @param int $total Total sources to check
* @return bool True if updated, false otherwise
*/
private function checkArchiveSourceUpdate(string $name, array $lock, array $config, int $current, int $total): bool
{
$type = $config['type'] ?? 'unknown';
$locked_filename = $lock['filename'] ?? '';
// Skip local types that don't support version detection
if (in_array($type, ['local', 'unknown'])) {
logger()->debug("[{$current}/{$total}] Source '{$name}' (type: {$type}) doesn't support version detection, skipping");
return false;
}
try {
$latest_info = match ($type) {
'ghtar' => Downloader::getLatestGithubTarball($name, $config),
'ghtagtar' => Downloader::getLatestGithubTarball($name, $config, 'tags'),
'ghrel' => Downloader::getLatestGithubRelease($name, $config),
'pie' => Downloader::getPIEInfo($name, $config),
'bitbuckettag' => Downloader::getLatestBitbucketTag($name, $config),
'filelist' => Downloader::getFromFileList($name, $config),
'url' => Downloader::getLatestUrlInfo($name, $config),
'custom' => $this->checkCustomSourceUpdate($name, $lock, $config, $current, $total),
default => null,
};
if ($latest_info === null) {
logger()->warning("[{$current}/{$total}] Could not get version info for '{$name}' (type: {$type})");
return false;
}
$latest_filename = $latest_info[1] ?? '';
// Compare filenames
if ($locked_filename !== $latest_filename) {
logger()->info("[{$current}/{$total}] Update available for '{$name}': {$locked_filename}{$latest_filename}");
$this->downloadSourceForUpdate($name, $config, $current, $total);
return true;
}
logger()->info("[{$current}/{$total}] Source '{$name}' is up to date");
return false;
} catch (DownloaderException $e) {
logger()->warning("[{$current}/{$total}] Failed to check '{$name}': {$e->getMessage()}");
return false;
}
}
/**
* Check and update a git source
*
* @param string $name Source name
* @param array $lock Lock file entry
* @param array $config Source configuration
* @param int $current Current progress number
* @param int $total Total sources to check
* @return bool True if updated, false otherwise
*/
private function checkGitSourceUpdate(string $name, array $lock, array $config, int $current, int $total): bool
{
$locked_hash = $lock['hash'] ?? '';
$url = $config['url'] ?? '';
$branch = $config['rev'] ?? 'main';
if (empty($url)) {
logger()->warning("[{$current}/{$total}] No URL found for git source '{$name}'");
return false;
}
try {
$remote_hash = $this->getRemoteGitCommit($url, $branch);
if ($remote_hash === null) {
logger()->warning("[{$current}/{$total}] Could not fetch remote commit for '{$name}'");
return false;
}
// Compare hashes (use first 7 chars for display)
$locked_short = substr($locked_hash, 0, 7);
$remote_short = substr($remote_hash, 0, 7);
if ($locked_hash !== $remote_hash) {
logger()->info("[{$current}/{$total}] Update available for '{$name}': {$locked_short}{$remote_short}");
$this->downloadSourceForUpdate($name, $config, $current, $total);
return true;
}
logger()->info("[{$current}/{$total}] Source '{$name}' is up to date");
return false;
} catch (\Throwable $e) {
logger()->warning("[{$current}/{$total}] Failed to check '{$name}': {$e->getMessage()}");
return false;
}
}
/**
* Download a source after removing old lock entry
*
* @param string $name Source name
* @param array $config Source configuration
* @param int $current Current progress number
* @param int $total Total sources to check
*/
private function downloadSourceForUpdate(string $name, array $config, int $current, int $total): void
{
logger()->info("[{$current}/{$total}] Downloading '{$name}'...");
// Remove old lock entry
LockFile::put($name, null);
// Download new version
Downloader::downloadSource($name, $config, true);
}
/**
* Get remote git commit hash without cloning
*
* @param string $url Git repository URL
* @param string $branch Branch or tag to check
* @return null|string Remote commit hash or null on failure
*/
private function getRemoteGitCommit(string $url, string $branch): ?string
{
try {
$cmd = SPC_GIT_EXEC . ' ls-remote ' . escapeshellarg($url) . ' ' . escapeshellarg($branch);
f_exec($cmd, $output, $ret);
if ($ret !== 0 || empty($output)) {
return null;
}
// Output format: "commit_hash\trefs/heads/branch" or "commit_hash\tHEAD"
$parts = preg_split('/\s+/', $output[0]);
return $parts[0] ?? null;
} catch (\Throwable $e) {
logger()->debug("Failed to fetch remote git commit: {$e->getMessage()}");
return null;
}
}
} }

View File

@@ -20,9 +20,9 @@ class SwitchPhpVersionCommand extends BaseCommand
$this->addArgument( $this->addArgument(
'php-major-version', 'php-major-version',
InputArgument::REQUIRED, InputArgument::REQUIRED,
'PHP major version (supported: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4)', 'PHP major version (supported: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5)',
null, null,
fn () => ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] fn () => ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
); );
$this->no_motd = true; $this->no_motd = true;
@@ -32,7 +32,7 @@ class SwitchPhpVersionCommand extends BaseCommand
public function handle(): int public function handle(): int
{ {
$php_ver = $this->input->getArgument('php-major-version'); $php_ver = $this->input->getArgument('php-major-version');
if (!in_array($php_ver, ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'])) { if (!in_array($php_ver, ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'])) {
// match x.y.z // match x.y.z
preg_match('/^\d+\.\d+\.\d+$/', $php_ver, $matches); preg_match('/^\d+\.\d+\.\d+$/', $php_ver, $matches);
if (!$matches) { if (!$matches) {

View File

@@ -22,7 +22,6 @@ class LinuxToolCheckList
'bzip2', 'cmake', 'gcc', 'bzip2', 'cmake', 'gcc',
'g++', 'patch', 'binutils-gold', 'g++', 'patch', 'binutils-gold',
'libtoolize', 'which', 'libtoolize', 'which',
'patchelf',
]; ];
public const TOOLS_DEBIAN = [ public const TOOLS_DEBIAN = [
@@ -31,7 +30,6 @@ class LinuxToolCheckList
'tar', 'unzip', 'gzip', 'gcc', 'g++', 'tar', 'unzip', 'gzip', 'gcc', 'g++',
'bzip2', 'cmake', 'patch', 'bzip2', 'cmake', 'patch',
'xz', 'libtoolize', 'which', 'xz', 'libtoolize', 'which',
'patchelf',
]; ];
public const TOOLS_RHEL = [ public const TOOLS_RHEL = [
@@ -39,8 +37,7 @@ class LinuxToolCheckList
'git', 'autoconf', 'automake', 'git', 'autoconf', 'automake',
'tar', 'unzip', 'gzip', 'gcc', 'g++', 'tar', 'unzip', 'gzip', 'gcc', 'g++',
'bzip2', 'cmake', 'patch', 'which', 'bzip2', 'cmake', 'patch', 'which',
'xz', 'libtool', 'gettext-devel', 'xz', 'libtool', 'gettext-devel', 'file',
'patchelf', 'file',
]; ];
public const TOOLS_ARCH = [ public const TOOLS_ARCH = [

View File

@@ -220,39 +220,6 @@ class Downloader
return [$source['url'] . end($versions), end($versions), key($versions)]; return [$source['url'] . end($versions), end($versions), key($versions)];
} }
/**
* Get latest version from direct URL (detect redirect and filename)
*
* @param string $name Source name
* @param array $source Source meta info: [url]
* @return array<int, string> [url, filename]
*/
public static function getLatestUrlInfo(string $name, array $source): array
{
logger()->debug("finding {$name} source from direct url");
$url = $source['url'];
$headers = self::curlExec(
url: $url,
method: 'HEAD',
retries: self::getRetryAttempts()
);
// Find redirect location if any
if (preg_match('/^location:\s+(?<url>.+)$/im', $headers, $matches)) {
$url = trim($matches['url']);
// If it's a relative URL, we need to handle it, but usually it's absolute for downloads
}
// Find filename from content-disposition
if (preg_match('/^content-disposition:\s+attachment;\s*filename=("?)(?<filename>.+)\1/im', $headers, $matches)) {
$filename = trim($matches['filename']);
} else {
$filename = $source['filename'] ?? basename($url);
}
return [$url, $filename];
}
/** /**
* Download file from URL * Download file from URL
* *
@@ -280,7 +247,7 @@ class Downloader
if ($download_as === SPC_DOWNLOAD_PRE_BUILT) { if ($download_as === SPC_DOWNLOAD_PRE_BUILT) {
$name = self::getPreBuiltLockName($name); $name = self::getPreBuiltLockName($name);
} }
LockFile::lockSource($name, ['source_type' => SPC_SOURCE_ARCHIVE, 'url' => $url, 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]); LockFile::lockSource($name, ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]);
} }
/** /**
@@ -339,7 +306,7 @@ class Downloader
} }
// Lock // Lock
logger()->debug("Locking git source {$name}"); logger()->debug("Locking git source {$name}");
LockFile::lockSource($name, ['source_type' => SPC_SOURCE_GIT, 'url' => $url, 'rev' => $branch, 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]); LockFile::lockSource($name, ['source_type' => SPC_SOURCE_GIT, 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]);
/* /*
// 复制目录过去 // 复制目录过去
@@ -689,7 +656,8 @@ class Downloader
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as); self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
break; break;
case 'url': // Direct download URL case 'url': // Direct download URL
[$url, $filename] = self::getLatestUrlInfo($name, $conf); $url = $conf['url'];
$filename = $conf['filename'] ?? basename($conf['url']);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as); self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
break; break;
case 'git': // Git repo case 'git': // Git repo
@@ -699,8 +667,6 @@ class Downloader
LockFile::lockSource($name, [ LockFile::lockSource($name, [
'source_type' => SPC_SOURCE_LOCAL, 'source_type' => SPC_SOURCE_LOCAL,
'dirname' => $conf['dirname'], 'dirname' => $conf['dirname'],
'url' => null,
'path' => $conf['path'] ?? null,
'move_path' => $conf['path'] ?? $conf['extract'] ?? null, 'move_path' => $conf['path'] ?? $conf['extract'] ?? null,
'lock_as' => $download_as, 'lock_as' => $download_as,
]); ]);
@@ -716,11 +682,7 @@ class Downloader
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'), ...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'),
]; ];
foreach ($classes as $class) { foreach ($classes as $class) {
// Support php-src and php-src-X.Y patterns if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
$matches = ($class::NAME === $name) ||
($class::NAME === 'php-src' && preg_match('/^php-src(-[\d.]+)?$/', $name));
if (is_a($class, CustomSourceBase::class, true) && $matches) {
$conf['source_name'] = $name; // Pass the actual source name
(new $class())->fetch($force, $conf, $download_as); (new $class())->fetch($force, $conf, $download_as);
break; break;
} }

View File

@@ -155,7 +155,6 @@ class LockFile
* @param string $name Source name * @param string $name Source name
* @param array{ * @param array{
* source_type: string, * source_type: string,
* url: ?string,
* dirname?: ?string, * dirname?: ?string,
* filename?: ?string, * filename?: ?string,
* move_path: ?string, * move_path: ?string,

View File

@@ -39,14 +39,7 @@ class SourceManager
// start check // start check
foreach ($sources_extracted as $source => $item) { foreach ($sources_extracted as $source => $item) {
$extract_dir_name = $source; if (Config::getSource($source) === null) {
// Handle version-specific php-src (php-src-8.2, php-src-8.3, etc.)
$source_config = Config::getSource($source);
if ($source_config === null && preg_match('/^php-src-[\d.]+$/', $source)) {
$source_config = Config::getSource('php-src');
$extract_dir_name = 'php-src';
}
if ($source_config === null) {
throw new WrongUsageException("Source [{$source}] does not exist, please check the name and correct it !"); throw new WrongUsageException("Source [{$source}] does not exist, please check the name and correct it !");
} }
// check source downloaded // check source downloaded
@@ -63,12 +56,12 @@ class SourceManager
$lock_content = LockFile::get($lock_name); $lock_content = LockFile::get($lock_name);
// check source dir exist // check source dir exist
$check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $extract_dir_name); $check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source);
// $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']); // $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']);
if (!is_dir($check)) { if (!is_dir($check)) {
logger()->debug("Extracting source [{$source}] to {$check} ..."); logger()->debug("Extracting source [{$source}] to {$check} ...");
$filename = LockFile::getLockFullPath($lock_content); $filename = LockFile::getLockFullPath($lock_content);
FileSystem::extractSource($extract_dir_name, $lock_content['source_type'], $filename, $check); FileSystem::extractSource($source, $lock_content['source_type'], $filename, $check);
LockFile::putLockSourceHash($lock_content, $check); LockFile::putLockSourceHash($lock_content, $check);
continue; continue;
} }
@@ -96,7 +89,7 @@ class SourceManager
logger()->notice("Source [{$source}] hash mismatch, removing old source dir and extracting again ..."); logger()->notice("Source [{$source}] hash mismatch, removing old source dir and extracting again ...");
FileSystem::removeDir($check); FileSystem::removeDir($check);
$filename = LockFile::getLockFullPath($lock_content); $filename = LockFile::getLockFullPath($lock_content);
$move_path = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $extract_dir_name); $move_path = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source);
FileSystem::extractSource($source, $lock_content['source_type'], $filename, $move_path); FileSystem::extractSource($source, $lock_content['source_type'], $filename, $move_path);
LockFile::putLockSourceHash($lock_content, $check); LockFile::putLockSourceHash($lock_content, $check);
} }

View File

@@ -22,7 +22,7 @@ class SourcePatcher
FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']); FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchGDWin32']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchGDWin32']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']); // FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']);
FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVPhp85']); FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVPhp85']);
@@ -634,7 +634,13 @@ class SourcePatcher
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gd/libgd/gdft.c', '#ifndef MSWIN32', '#ifndef _WIN32'); FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gd/libgd/gdft.c', '#ifndef MSWIN32', '#ifndef _WIN32');
} }
// custom config.w32, because official config.w32 is hard-coded many things // custom config.w32, because official config.w32 is hard-coded many things
$origin = $ver_id >= 80100 ? file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_81.w32') : file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_80.w32'); if ($ver_id >= 80500) {
$origin = file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_85.w32');
} elseif ($ver_id >= 80100) {
$origin = file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_81.w32');
} else {
$origin = file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_80.w32');
}
file_put_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32.bak', file_get_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32')); file_put_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32.bak', file_get_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32'));
return file_put_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32', $origin) !== false; return file_put_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32', $origin) !== false;
} }

View File

@@ -30,8 +30,8 @@ class GoXcaddy extends CustomPackage
public function fetch(string $name, bool $force = false, ?array $config = null): void public function fetch(string $name, bool $force = false, ?array $config = null): void
{ {
$pkgroot = PKG_ROOT_PATH; $pkgroot = PKG_ROOT_PATH;
$go_exec = "{$pkgroot}/{$name}/bin/go"; $go_exec = "{$pkgroot}/go-xcaddy/bin/go";
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy"; $xcaddy_exec = "{$pkgroot}/go-xcaddy/bin/xcaddy";
if ($force) { if ($force) {
FileSystem::removeDir("{$pkgroot}/{$name}"); FileSystem::removeDir("{$pkgroot}/{$name}");
} }

View File

@@ -25,13 +25,4 @@ abstract class CustomSourceBase
* @param int $lock_as Lock type constant * @param int $lock_as Lock type constant
*/ */
abstract public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void; abstract public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void;
/**
* Update the source from its repository
*
* @param array $lock Lock file entry
* @param array $config Optional configuration array
* @return null|array Latest version info [url, filename], or null if no update needed
*/
abstract public function update(array $lock, ?array $config = null): ?array;
} }

View File

@@ -6,55 +6,26 @@ namespace SPC\store\source;
use JetBrains\PhpStorm\ArrayShape; use JetBrains\PhpStorm\ArrayShape;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\SPCException;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\LockFile;
class PhpSource extends CustomSourceBase class PhpSource extends CustomSourceBase
{ {
public const NAME = 'php-src'; public const string NAME = 'php-src';
public const array WEB_PHP_DOMAINS = [
'https://www.php.net',
'https://phpmirror.static-php.dev',
];
public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void
{ {
$source_name = $config['source_name'] ?? 'php-src'; $major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.5';
// Try to extract version from source name (e.g., "php-src-8.2" -> "8.2")
if (preg_match('/^php-src-([\d.]+)$/', $source_name, $matches)) {
$major = $matches[1];
} else {
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.4';
}
if ($major === 'git') { if ($major === 'git') {
Downloader::downloadSource($source_name, ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force); Downloader::downloadSource('php-src', ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force);
} else { } else {
Downloader::downloadSource($source_name, $this->getLatestPHPInfo($major), $force); Downloader::downloadSource('php-src', $this->getLatestPHPInfo($major), $force);
} }
if ($source_name !== 'php-src') {
LockFile::put('php-src', LockFile::get($source_name));
}
}
public function update(array $lock, ?array $config = null): ?array
{
$source_name = $config['source_name'] ?? 'php-src';
// Try to extract version from source name (e.g., "php-src-8.2" -> "8.2")
if (preg_match('/^php-src-([\d.]+)$/', $source_name, $matches)) {
$major = $matches[1];
} else {
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.4';
}
if ($major === 'git') {
return null;
}
$latest_php = $this->getLatestPHPInfo($major);
$latest_url = $latest_php['url'];
$filename = basename($latest_url);
return [$latest_url, $filename];
} }
/** /**
@@ -63,21 +34,26 @@ class PhpSource extends CustomSourceBase
#[ArrayShape(['type' => 'string', 'path' => 'string', 'rev' => 'string', 'url' => 'string'])] #[ArrayShape(['type' => 'string', 'path' => 'string', 'rev' => 'string', 'url' => 'string'])]
public function getLatestPHPInfo(string $major_version): array public function getLatestPHPInfo(string $major_version): array
{ {
// 查找最新的小版本号 foreach (self::WEB_PHP_DOMAINS as $domain) {
$info = json_decode(Downloader::curlExec( try {
url: "https://www.php.net/releases/index.php?json&version={$major_version}", $info = json_decode(Downloader::curlExec(
retries: (int) getenv('SPC_DOWNLOAD_RETRIES') ?: 0 url: "{$domain}/releases/index.php?json&version={$major_version}",
), true); retries: (int) getenv('SPC_DOWNLOAD_RETRIES') ?: 0
if (!isset($info['version'])) { ), true);
throw new DownloaderException("Version {$major_version} not found."); if (!isset($info['version'])) {
throw new DownloaderException("Version {$major_version} not found.");
}
$version = $info['version'];
return [
'type' => 'url',
'url' => "{$domain}/distributions/php-{$version}.tar.xz",
];
} catch (SPCException) {
logger()->warning('Failed to fetch latest PHP version for major version {$major_version} from {$domain}, trying next mirror if available.');
continue;
}
} }
// exception if all mirrors failed
$version = $info['version']; throw new DownloaderException("Failed to fetch latest PHP version for major version {$major_version} from all tried mirrors.");
// 从官网直接下载
return [
'type' => 'url',
'url' => "https://www.php.net/distributions/php-{$version}.tar.xz",
];
} }
} }

View File

@@ -15,13 +15,6 @@ class PostgreSQLSource extends CustomSourceBase
Downloader::downloadSource('postgresql', self::getLatestInfo(), $force); Downloader::downloadSource('postgresql', self::getLatestInfo(), $force);
} }
public function update(array $lock, ?array $config = null): ?array
{
$latest = $this->getLatestInfo();
$filename = basename($latest['url']);
return [$latest['url'], $filename];
}
public function getLatestInfo(): array public function getLatestInfo(): array
{ {
[, $filename, $version] = Downloader::getFromFileList('postgresql', [ [, $filename, $version] = Downloader::getFromFileList('postgresql', [

View File

@@ -393,7 +393,7 @@ class ConfigValidator
} }
// check php-version // check php-version
if (isset($craft['php-version'])) { if (isset($craft['php-version'])) {
// validdate version, accept 8.x, 7.x, 8.x.x, 7.x.x, 8, 7 // validate version, accept 8.x, 7.x, 8.x.x, 7.x.x, 8, 7
$version = strval($craft['php-version']); $version = strval($craft['php-version']);
if (!preg_match('/^(\d+)(\.\d+)?(\.\d+)?$/', $version, $matches)) { if (!preg_match('/^(\d+)(\.\d+)?(\.\d+)?$/', $version, $matches)) {
throw new ValidationException('Craft file php-version is invalid'); throw new ValidationException('Craft file php-version is invalid');

View File

@@ -80,7 +80,6 @@ class SPCConfigUtil
$libs = $this->getLibsString($libraries, !$this->absolute_libs); $libs = $this->getLibsString($libraries, !$this->absolute_libs);
// additional OS-specific libraries (e.g. macOS -lresolv) // additional OS-specific libraries (e.g. macOS -lresolv)
// embed
if ($extra_libs = SPCTarget::getRuntimeLibs()) { if ($extra_libs = SPCTarget::getRuntimeLibs()) {
$libs .= " {$extra_libs}"; $libs .= " {$extra_libs}";
} }

View File

@@ -27,10 +27,10 @@ class SPCTarget
return true; return true;
} }
if (ToolchainManager::getToolchainClass() === GccNativeToolchain::class) { if (ToolchainManager::getToolchainClass() === GccNativeToolchain::class) {
return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist(); return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() && !getenv('SPC_MUSL_DYNAMIC');
} }
if (ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) { if (ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) {
return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist(); return PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() && !getenv('SPC_MUSL_DYNAMIC');
} }
// if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released // if SPC_LIBC is set, it means the target is static, remove it when 3.0 is released
if ($target = getenv('SPC_TARGET')) { if ($target = getenv('SPC_TARGET')) {

View File

@@ -16,12 +16,11 @@ class UnixAutoconfExecutor extends Executor
protected array $configure_args = []; protected array $configure_args = [];
protected array $ignore_args = [];
public function __construct(protected BSDLibraryBase|LinuxLibraryBase|MacOSLibraryBase $library) public function __construct(protected BSDLibraryBase|LinuxLibraryBase|MacOSLibraryBase $library)
{ {
parent::__construct($library); parent::__construct($library);
$this->initShell(); $this->initShell();
$this->configure_args = $this->getDefaultConfigureArgs();
} }
/** /**
@@ -29,19 +28,12 @@ class UnixAutoconfExecutor extends Executor
*/ */
public function configure(...$args): static public function configure(...$args): static
{ {
// remove all the ignored args $args = array_merge($args, $this->configure_args);
$args = array_merge($args, $this->getDefaultConfigureArgs(), $this->configure_args);
$args = array_diff($args, $this->ignore_args);
$configure_args = implode(' ', $args); $configure_args = implode(' ', $args);
return $this->seekLogFileOnException(fn () => $this->shell->exec("./configure {$configure_args}")); return $this->seekLogFileOnException(fn () => $this->shell->exec("./configure {$configure_args}"));
} }
public function getConfigureArgsString(): string
{
return implode(' ', array_merge($this->getDefaultConfigureArgs(), $this->configure_args));
}
/** /**
* Run make * Run make
* *
@@ -111,7 +103,7 @@ class UnixAutoconfExecutor extends Executor
*/ */
public function removeConfigureArgs(...$args): static public function removeConfigureArgs(...$args): static
{ {
$this->ignore_args = [...$this->ignore_args, ...$args]; $this->configure_args = array_diff($this->configure_args, $args);
return $this; return $this;
} }
@@ -133,8 +125,8 @@ class UnixAutoconfExecutor extends Executor
private function getDefaultConfigureArgs(): array private function getDefaultConfigureArgs(): array
{ {
return [ return [
'--disable-shared',
'--enable-static', '--enable-static',
'--disable-shared',
"--prefix={$this->library->getBuildRootPath()}", "--prefix={$this->library->getBuildRootPath()}",
'--with-pic', '--with-pic',
'--enable-pic', '--enable-pic',

View File

@@ -0,0 +1,94 @@
// vim:ft=javascript
ARG_WITH("gd", "Bundled GD support", "yes");
if (PHP_GD != "no") {
// check for gd.h (required)
if (!CHECK_HEADER_ADD_INCLUDE("gd.h", "CFLAGS_GD", PHP_GD + ";ext\\gd\\libgd")) {
ERROR("gd not enabled; libraries and headers not found");
}
// zlib ext support (required)
if (!CHECK_LIB("zlib_a.lib;zlib.lib", "gd", PHP_GD)) {
ERROR("gd not enabled; zlib not enabled");
}
// libjpeg lib support
if (CHECK_LIB("libjpeg_a.lib;libjpeg.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("jpeglib.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include")) {
AC_DEFINE("HAVE_LIBJPEG", 1, "JPEG support");
AC_DEFINE("HAVE_GD_JPG", 1, "JPEG support");
}
// libpng16 lib support
if (CHECK_LIB("libpng_a.lib;libpng.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("png.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\libpng16")) {
AC_DEFINE("HAVE_LIBPNG", 1, "PNG support");
AC_DEFINE("HAVE_GD_PNG", 1, "PNG support");
}
// freetype lib support
if (CHECK_LIB("libfreetype_a.lib;libfreetype.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("ft2build.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\freetype2;" + PHP_PHP_BUILD + "\\include\\freetype")) {
AC_DEFINE("HAVE_LIBFREETYPE", 1, "FreeType support");
AC_DEFINE("HAVE_GD_FREETYPE", 1, "FreeType support");
}
// xpm lib support
if (CHECK_LIB("libXpm_a.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("xpm.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\X11")) {
AC_DEFINE("HAVE_LIBXPM", 1, "XPM support");
AC_DEFINE("HAVE_GD_XPM", 1, "XPM support");
}
// iconv lib support
if ((CHECK_LIB("libiconv_a.lib;libiconv.lib", "gd", PHP_GD) || CHECK_LIB("iconv_a.lib;iconv.lib", "gd", PHP_GD)) &&
CHECK_HEADER_ADD_INCLUDE("iconv.h", "CFLAGS_GD", PHP_GD)) {
AC_DEFINE("HAVE_LIBICONV", 1, "Iconv support");
}
// libwebp lib support
if ((CHECK_LIB("libwebp_a.lib", "gd", PHP_GD) || CHECK_LIB("libwebp.lib", "gd", PHP_GD)) &&
CHECK_LIB("libsharpyuv.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("decode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp") &&
CHECK_HEADER_ADD_INCLUDE("encode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp")) {
AC_DEFINE("HAVE_LIBWEBP", 1, "WebP support");
AC_DEFINE("HAVE_GD_WEBP", 1, "WebP support");
}
// libavif lib support
if (CHECK_LIB("avif_a.lib", "gd", PHP_GD) &&
CHECK_LIB("aom_a.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("avif.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\avif")) {
ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBAVIF /D HAVE_GD_AVIF");
} else if (CHECK_LIB("avif.lib", "gd", PHP_GD) &&
CHECK_HEADER_ADD_INCLUDE("avif.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\avif")) {
ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBAVIF /D HAVE_GD_AVIF");
}
CHECK_LIB("User32.lib", "gd", PHP_GD);
CHECK_LIB("Gdi32.lib", "gd", PHP_GD);
EXTENSION("gd", "gd.c", null, "-Iext/gd/libgd");
ADD_SOURCES("ext/gd/libgd", "gd.c \
gdcache.c gdfontg.c gdfontl.c gdfontmb.c gdfonts.c gdfontt.c \
gdft.c gd_gd2.c gd_gd.c gd_gif_in.c gd_gif_out.c gdhelpers.c gd_io.c gd_io_dp.c \
gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_png.c gd_ss.c \
gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c gd_xbm.c gd_security.c gd_transform.c \
gd_filter.c gd_rotate.c gd_color_match.c gd_webp.c gd_avif.c \
gd_crop.c gd_interpolation.c gd_matrix.c gd_bmp.c gd_tga.c", "gd");
AC_DEFINE('HAVE_LIBGD', 1, 'GD support');
AC_DEFINE('HAVE_GD_BUNDLED', 1, "Bundled GD");
AC_DEFINE('HAVE_GD_BMP', 1, "BMP support");
AC_DEFINE('HAVE_GD_TGA', 1, "TGA support");
ADD_FLAG("CFLAGS_GD", " \
/D PHP_GD_EXPORTS=1 \
/D HAVE_GD_GET_INTERPOLATION \
");
if (ICC_TOOLSET) {
ADD_FLAG("LDFLAGS_GD", "/nodefaultlib:libcmt");
}
PHP_INSTALL_HEADERS("", "ext/gd ext/gd/libgd");
}

View File

@@ -14,8 +14,8 @@ declare(strict_types=1);
// test php version (8.1 ~ 8.4 available, multiple for matrix) // test php version (8.1 ~ 8.4 available, multiple for matrix)
$test_php_version = [ $test_php_version = [
// '8.1', // '8.1',
'8.2', // '8.2',
'8.3', // '8.3',
'8.4', '8.4',
'8.5', '8.5',
// 'git', // 'git',
@@ -23,15 +23,15 @@ $test_php_version = [
// test os (macos-15-intel, macos-15, ubuntu-latest, windows-latest are available) // test os (macos-15-intel, macos-15, ubuntu-latest, windows-latest are available)
$test_os = [ $test_os = [
'macos-15-intel', // bin/spc for x86_64 // 'macos-15-intel', // bin/spc for x86_64
'macos-15', // bin/spc for arm64 'macos-15', // bin/spc for arm64
'ubuntu-latest', // bin/spc-alpine-docker for x86_64 'ubuntu-latest', // bin/spc-alpine-docker for x86_64
'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
'ubuntu-24.04', // bin/spc for x86_64 // 'ubuntu-24.04', // bin/spc for x86_64
// 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64 '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-2022', // .\bin\spc.ps1 // 'windows-2022', // .\bin\spc.ps1
// 'windows-2025', 'windows-2025',
]; ];
// whether enable thread safe // whether enable thread safe
@@ -42,7 +42,7 @@ $no_strip = false;
// compress with upx // compress with upx
$upx = false; $upx = false;
// whether to test frankenphp build, only available for macos and linux // whether to test frankenphp build, only available for macOS and linux
$frankenphp = false; $frankenphp = false;
// prefer downloading pre-built packages to speed up the build process // prefer downloading pre-built packages to speed up the build process
@@ -50,23 +50,23 @@ $prefer_pre_built = false;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) { $extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'mysqli,gmp', 'Linux', 'Darwin' => 'zlib',
'Windows' => 'bcmath', 'Windows' => 'gd,zlib,mbstring,filter',
}; };
// If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`).
$shared_extensions = match (PHP_OS_FAMILY) { $shared_extensions = match (PHP_OS_FAMILY) {
'Linux' => 'grpc,mysqlnd_parsec,mysqlnd_ed25519', 'Linux' => '',
'Darwin' => '', 'Darwin' => '',
'Windows' => '', 'Windows' => '',
}; };
// If you want to test lib-suggests for all extensions and libraries, set it to true. // 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. // 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) { $with_libs = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'libwebp', 'Linux', 'Darwin' => 'libjpeg',
'Windows' => '', 'Windows' => '',
}; };