Compare commits

...

235 Commits

Author SHA1 Message Date
crazywhalecc
020a30315d Fix error handling in loadFromFile methods and update exception types in tests 2025-12-10 11:33:39 +08:00
crazywhalecc
bde1440617 Refactor test structure and update paths for improved organization 2025-12-10 11:15:44 +08:00
Jerry Ma
78375632b4 V3 feat/re2c (#992)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 10:07:39 +08:00
Jerry Ma
f68adc3256 Update src/Package/Target/php.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 09:52:59 +08:00
Jerry Ma
4a968757ba Update src/Package/Library/ncurses.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 09:49:20 +08:00
crazywhalecc
bcaef59a15 Support full --no-ansi options 2025-12-09 16:54:29 +08:00
crazywhalecc
b0f630f95f Add package outputs, colorize motd 2025-12-09 16:34:43 +08:00
crazywhalecc
ac01867e9c Refactor stage execution to use method references for improved clarity 2025-12-09 15:01:41 +08:00
crazywhalecc
808aed2a66 Refactor package stage handling and update class structures for improved flexibility 2025-12-09 14:58:11 +08:00
crazywhalecc
e004d10861 Fix phpstan 2025-12-08 17:04:00 +08:00
crazywhalecc
0db26be826 Correct SAPI-packages to be installed 2025-12-08 17:02:12 +08:00
crazywhalecc
a4bd2a79a9 Add shared extension build support 2025-12-08 17:01:58 +08:00
crazywhalecc
7b16f683fc Allow package implementation using parent class functions 2025-12-08 17:01:29 +08:00
crazywhalecc
78234ef147 Add missing patchPkgconfPrefix function 2025-12-08 17:00:56 +08:00
crazywhalecc
80128edd39 Add patch description display 2025-12-08 17:00:34 +08:00
crazywhalecc
b384345723 Add php-micro patch for embed mode 2025-12-08 17:00:18 +08:00
crazywhalecc
f4bb0263f6 Fix ncurses static-libs 2025-12-08 17:00:03 +08:00
crazywhalecc
321f2e13e8 Allow all types of package can be built 2025-12-08 16:59:38 +08:00
crazywhalecc
11e7a590c8 Add ncurses package build 2025-12-08 16:58:47 +08:00
crazywhalecc
20e0711747 Add libedit package build 2025-12-08 16:58:42 +08:00
crazywhalecc
80d922ab3b Use patch for current package exclusively 2025-12-08 16:58:04 +08:00
crazywhalecc
a1cadecc54 Refactor re2c fix-item 2025-12-08 12:45:12 +08:00
Jerry Ma
127c935106 Refactor BUILDROOT_ABS initialization to provide a default path (#991) 2025-12-08 12:33:37 +08:00
crazywhalecc
eab105965d Remove redundant path 2025-12-08 11:06:50 +08:00
crazywhalecc
abd6c2fa3a Add PackageInstaller::isPackageInstalled() API 2025-12-08 11:06:50 +08:00
crazywhalecc
df6c27c98d Allow absolute paths for configs 2025-12-08 11:06:50 +08:00
crazywhalecc
3ff762c4c8 Fix wrong namespace in go-xcaddy package 2025-12-08 11:06:50 +08:00
crazywhalecc
6775cb4674 Fix pkg-config doctor fix using source bug 2025-12-08 11:06:50 +08:00
crazywhalecc
88b86d3eaf Fix artifact downloade does not accept boolean options bug 2025-12-08 11:06:50 +08:00
crazywhalecc
dbc6dbee53 Add Zig package support with downloader and installation checks 2025-12-08 11:06:50 +08:00
crazywhalecc
baddd60113 Add dev commands: is-installed, shell (for debugging package status) 2025-12-08 11:06:50 +08:00
crazywhalecc
2f09ace82f Add LinuxToolCheck 2025-12-08 11:06:50 +08:00
Jerry Ma
d3b0f5de79 Fix argument naming in InstallPackageCommand for clarity (#989) 2025-12-06 16:57:16 +08:00
Jerry Ma
9ad7147155 Enhance musl-wrapper and musl-toolchain installation process (#988) 2025-12-06 16:50:36 +08:00
Jerry Ma
106b55d4e7 [v3] Add musl-wrapper and musl-toolchain installation support (#984) 2025-12-05 15:32:32 +08:00
crazywhalecc
93a697ebbf Fix artifact downloader constants and improve error message for hosted package-bin 2025-12-05 15:31:56 +08:00
crazywhalecc
7fa6fd08d4 Add HostedPackageBin downloader and enhance artifact handling 2025-12-05 15:31:56 +08:00
crazywhalecc
52553fb5ed Fix PHPStan errors 2025-12-05 15:31:56 +08:00
crazywhalecc
c925914925 Add version getter and checksum for go-xcaddy artifact 2025-12-05 15:31:56 +08:00
crazywhalecc
d16f5a972c Add --with-packages option for spc-config command 2025-12-04 21:21:48 +08:00
crazywhalecc
ee46c1c387 Fix switch-php-version command not working bug 2025-12-04 21:21:27 +08:00
crazywhalecc
64fde5fd8c Allow loading config dir from registry 2025-12-04 21:20:44 +08:00
crazywhalecc
dc5bf6dc98 Correct install-pkg argument name, add alias 2025-12-04 21:20:22 +08:00
crazywhalecc
20892ab194 Auto-append prefix for php-extension packages 2025-12-04 21:19:55 +08:00
crazywhalecc
e9d3f7e7eb Change wrong option name 2025-12-04 21:19:34 +08:00
crazywhalecc
2f8570b59e Implement missing legacy options 2025-12-04 21:19:11 +08:00
crazywhalecc
71d803d36f cs fix 2025-12-04 10:54:05 +08:00
crazywhalecc
daa87e1350 Add DirDiff utility and enhance package build process
- Introduced DirDiff class for tracking directory file changes.
- Updated ConsoleApplication to use addCommand for build targets.
- Enhanced PackageBuilder with methods for deploying binaries and extracting debug info.
- Improved package installation logic to support shared extensions.
- Added readline extension with patching for static builds.
2025-12-04 10:53:49 +08:00
crazywhalecc
c38f174a6b Forward-port #978 2025-12-02 13:42:46 +08:00
crazywhalecc
9903c2294c Merge branch 'main' into v3-dev 2025-12-02 13:39:19 +08:00
Marc
59a6e27532 add pcov extension (shared only, like xdebug) (#979) 2025-12-01 08:12:39 +01:00
Marc
6b3b841c0e Fix cross-device warning in rename function (#978) 2025-11-30 16:08:09 +01:00
crazywhalecc
14bfb4198a v3 base 2025-11-30 15:35:04 +08:00
henderkes
e6591ffe9c add pcov extension (shared only, like xdebug) 2025-11-29 14:03:17 +01:00
crazywhalecc
f6c818d3c0 v3 base 2025-11-29 18:58:16 +08:00
crazywhalecc
e316971764 Fix cross-device warning in rename function 2025-11-29 14:08:34 +08:00
Marc
d55278714f we need to change source_path for shared build (#977) 2025-11-26 16:10:27 +07:00
Jerry Ma
cb7eca9049 Bump version from 2.7.8 to 2.7.9 2025-11-26 17:08:49 +08:00
henderkes
5b8c9e6f09 we need to change source_path for shared build 2025-11-26 09:37:21 +01:00
Jerry Ma
5d1043334d Add extension maxminddb support for macOS and Linux (#975) 2025-11-26 15:32:26 +08:00
Marc
f24cbcf909 fix silly mistake in trader config.m4 (#973) 2025-11-25 08:12:50 +01:00
henderkes
76a07c32ba fix silly mistake in trader config.m4 2025-11-24 14:24:12 +01:00
Marc
e0c69086dc don't download musl if zig isn't building for musl (#972) 2025-11-22 10:07:35 +01:00
henderkes
5606b70c1c don't download musl if zig isn't building for musl 2025-11-22 09:04:17 +01:00
Marc
28ae4242a6 add extra libs for curl (psl, krb5, unistring, idn2) (#968) 2025-11-21 15:05:40 +01:00
Jerry Ma
f14df1925c Store original source directory before modification 2025-11-21 22:03:41 +08:00
Marc
e9ad3c0011 Merge branch 'main' into feat/libcurl-extra 2025-11-21 09:21:22 +01:00
Marc
e2ad31e858 Support PHP 8.5 release (#971) 2025-11-21 09:16:13 +01:00
Marc
af614cd7ce Fix indentation in PhpSource.php 2025-11-21 08:51:30 +01:00
Marc
e21b0ec89d Apply suggestion from @henderkes 2025-11-20 18:21:39 +01:00
Marc
3069b51c09 we have 8.5! 2025-11-20 18:19:41 +01:00
henderkes
1662ac4cf8 framework! 2025-11-19 15:20:05 +01:00
henderkes
1e09017549 frameworks? 2025-11-19 14:49:37 +01:00
henderkes
84e9f13688 framework? 2025-11-19 14:48:10 +01:00
henderkes
2b0a0bdad9 fix cs 2025-11-19 13:54:45 +01:00
henderkes
e8d1970f55 secure_getenv not available on macos 2025-11-19 13:54:17 +01:00
henderkes
6861e9c2c7 append ld_library_path for musl toolchain 2025-11-19 11:59:09 +01:00
henderkes
376b8e7569 remove psl 2025-11-19 10:48:24 +01:00
Marc Henderkes
875e1d05cd libedit instead of readline 2025-11-18 17:50:49 +01:00
Marc Henderkes
9382161b6f Revert "add gsasl"
This reverts commit a1b8d201ae.
2025-11-18 17:49:10 +01:00
Marc Henderkes
a1b8d201ae add gsasl 2025-11-18 17:47:26 +01:00
Marc Henderkes
1fed8f2802 add required libs to krb5 2025-11-18 17:22:27 +01:00
Marc Henderkes
ecea6c12cd libs for macos too 2025-11-18 14:35:58 +01:00
Marc Henderkes
81dce02431 use lgpl 2025-11-18 14:06:15 +01:00
Marc Henderkes
a33ca44ca9 test curl 2025-11-18 11:28:25 +01:00
Marc
aee733b51f Merge branch 'main' into feat/libcurl-extra 2025-11-18 11:22:05 +01:00
Marc Henderkes
54ceca8680 add extra libs for curl (psl, krb5, unistring, idn2) 2025-11-18 11:17:12 +01:00
Jerry Ma
90981e3877 Export php binary external debug symbol files (#953) 2025-11-17 11:53:30 +08:00
henderkes
d69826eb4a update php 8.5 to RC5 2025-11-16 11:30:53 +01:00
Marc
dc83282019 remove SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS, use eu-strip (#966) 2025-11-12 20:51:06 +01:00
henderkes
23c0d6f4aa simplify deployBinary a little bit 2025-11-12 10:11:29 +01:00
henderkes
8e4d4b7be5 suggestion 2025-11-11 16:51:30 +01:00
henderkes
ee906aaff9 Merge remote-tracking branch 'origin/feat/no-strip' into frankenphp/mbed 2025-11-11 15:56:10 +01:00
henderkes
e2b80e7f03 update building of frankenphp 2025-11-11 15:53:44 +01:00
henderkes
09073c5517 sort config and remove lonesome configure cflags 2025-11-11 15:51:28 +01:00
Marc
00050f4d0e Merge branch 'main' into feat/no-strip 2025-11-11 15:17:16 +01:00
crazywhalecc
cff6ec17ea Remove escape backslashes 2025-11-11 15:02:21 +01:00
henderkes
64079d9331 simplify regex 2025-11-11 15:02:20 +01:00
henderkes
541889d17b update to rc4 2025-11-11 15:02:20 +01:00
henderkes
c91128995d update freetype download to get latest version 2025-11-11 15:02:20 +01:00
henderkes
fd2b7af1dc make --with-frankenphp-app=dir work with docker scripts 2025-11-11 15:02:20 +01:00
henderkes
081e2d2846 fix debugflags being backwards 2025-11-11 15:02:19 +01:00
DubbleClick
9edb9417a1 add --with-frankenphp-app option to embed an app
# Conflicts:
#	config/lib.json
#	src/SPC/builder/unix/UnixBuilderBase.php
2025-11-11 15:02:17 +01:00
Marc
4ae4165ba2 add --with-frankenphp-app=/path/to/app option (#891) 2025-11-10 10:37:07 +01:00
crazywhalecc
e441a575ea Remove escape backslashes 2025-11-10 13:26:17 +08:00
henderkes
1575016885 simplify regex 2025-11-09 23:35:49 +01:00
crazywhalecc
987ad4b846 Use diff to detect and deploy-patch new shared extensions that built with php 2025-11-09 17:13:41 +08:00
crazywhalecc
f4b03ae835 Introduce standalone DirDiff util class 2025-11-09 17:12:22 +08:00
henderkes
d6de01d05c update to rc4 2025-11-09 10:01:19 +01:00
henderkes
09b7159119 update freetype download to get latest version 2025-11-08 09:41:12 +01:00
henderkes
4198ddd5d1 make --with-frankenphp-app=dir work with docker scripts 2025-11-07 11:06:50 +01:00
Marc
8332ed87e0 Merge branch 'main' into frankenphp/mbed 2025-11-07 10:51:55 +01:00
crazywhalecc
f6b091498f Fix missing debug link and debug option 2025-11-06 16:51:46 +08:00
crazywhalecc
a45f314447 Remove for pure test 2025-11-06 16:31:24 +08:00
crazywhalecc
8c8cb70174 phpstan fix 2025-11-06 16:27:11 +08:00
crazywhalecc
f09c18e78f Use separated deploy functions 2025-11-06 16:24:59 +08:00
crazywhalecc
f5d93d2f54 Merge branch 'main' into feat/no-strip 2025-11-06 14:55:54 +08:00
Marc
7402fbf7c1 Use cmake to build librdkafka, add helper function for SPCConfigUtil (#955) 2025-11-04 14:58:58 +01:00
crazywhalecc
2c590e5895 Define dependencies with kv array 2025-11-04 15:52:52 +08:00
crazywhalecc
463a98b1bf Support suggested libs not specified by with-suggested-libs 2025-11-04 13:51:11 +08:00
crazywhalecc
6d1c6d7f61 Add PHPDocs 2025-11-04 13:38:11 +08:00
crazywhalecc
08362fb6e5 Add helper function for SPCConfigUtil 2025-11-04 13:36:29 +08:00
crazywhalecc
4d5641f6ec Change librdkafka to cmake 2025-11-04 13:35:43 +08:00
crazywhalecc
f34ecf9468 Use pkg-configs for librdkafka 2025-11-04 13:35:31 +08:00
crazywhalecc
aa5c829fae Add missing file and line for Unhandled exception 2025-11-04 13:35:15 +08:00
Marc
fc118d709e Remove deduplication of pkg-config libraries
Removed deduplication of pkg-config libraries.
2025-11-03 21:58:46 +01:00
henderkes
589a4a9803 test rdkafka with all suggested libs 2025-11-03 21:13:47 +01:00
Marc
944e314bab Merge branch 'main' into feat/rdkafka-ssl 2025-11-03 20:48:19 +01:00
henderkes
0b17ce9e61 test rdkafka with all suggested libs 2025-11-03 20:47:42 +01:00
henderkes
ed4978bb89 add frankenphp version 2025-11-03 20:40:50 +01:00
henderkes
7a4f28e939 add frankenphp version 2025-11-03 20:39:26 +01:00
henderkes
5cb6a75e7d add extra features to librdkafka (curl, ssl, sasl 2025-11-03 20:29:15 +01:00
crazywhalecc
757af25d8f Suggestions 2025-11-01 00:49:50 +08:00
crazywhalecc
5e3e7eccbf Update version to 2.7.7 2025-10-31 16:48:55 +08:00
crazywhalecc
9738fcd6cd Update env.ini docs 2025-10-31 16:48:36 +08:00
crazywhalecc
5a6a33303c Export php binary debug symbols for mac, linux and windows 2025-10-31 16:48:21 +08:00
Marc
ae15d6c5f5 fix postgresql libraries when --with-suggested-libs is false (#952) 2025-10-31 09:46:21 +01:00
henderkes
ff15973a25 suggestions 2025-10-31 09:08:15 +01:00
henderkes
b88a68dab8 fix postgresql libraries when --with-suggested-libs is false 2025-10-29 18:55:26 +01:00
Jerry Ma
b05bdcd83d Add snmp and net-snmp support for macOS and Linux (#946)
Co-authored-by: Marc <m@pyc.ac>
2025-10-29 11:56:36 +08:00
Jerry Ma
6a4ad34324 Add trader extension support (#951) 2025-10-29 11:54:35 +08:00
henderkes
9a2d94cc33 fix debugflags being backwards 2025-10-28 18:50:16 +01:00
Jerry Ma
f1d1d4fe10 Beautify docs (#948) 2025-10-28 18:56:18 +08:00
crazywhalecc
fa6fa1c425 Add docs 2025-10-27 09:56:47 +08:00
Jerry Ma
f8c8300c9c Add Windows CGI SAPI build support (#927) 2025-10-25 16:04:00 +08:00
crazywhalecc
09198b431f Use unified configure args format 2025-10-25 15:41:48 +08:00
crazywhalecc
f426ced789 Use phpmicro patches instead 2025-10-25 13:42:04 +08:00
crazywhalecc
bab330b64e Unify CGI deploy functions 2025-10-25 03:03:40 +08:00
crazywhalecc
b59a06face Add backward patches for win32 2025-10-25 02:56:38 +08:00
crazywhalecc
bb44e88c3b Merge branch 'refs/heads/main' into sapi/cgi-win
# Conflicts:
#	src/globals/test-extensions.php
2025-10-25 00:11:45 +08:00
Marc
8649068159 Merge branch 'main' into frankenphp/mbed 2025-10-24 16:50:28 +02:00
Marc
5476385553 Update PHP 8.5 to RC3, enable swoole-stdext for upcoming 6.1.0 release. (#944) 2025-10-24 12:09:46 +02:00
henderkes
56bac35768 do both in one go 2025-10-24 12:09:24 +02:00
henderkes
4cdc6a07ae fix php 8.5 2025-10-24 11:49:11 +02:00
crazywhalecc
c30b34ae5c Fix gettext missing symbols on macOS 2025-10-24 17:02:15 +08:00
Marc
da5c6fd084 Merge branch 'main' into fix/php85 2025-10-24 10:47:29 +02:00
Jerry Ma
a4b6499530 Fix libevent build, use newer cmake options (#945) 2025-10-24 16:34:07 +08:00
henderkes
eb4445ea59 use release tarballs from Daniel rather than automatically generated ones from github 2025-10-24 10:06:29 +02:00
crazywhalecc
be51bcfdfc Fix libevent build, use newer cmake options 2025-10-24 13:55:33 +08:00
henderkes
7bc4131c02 use correct license for gettext as we only build libs now, update gmp to dynamically check version 2025-10-23 23:11:30 +02:00
henderkes
da8debdade test php 8.5 with gettext 2025-10-23 22:19:53 +02:00
henderkes
ef5e664981 don't build iconv program, or gettext programs 2025-10-23 22:14:57 +02:00
henderkes
8e50af3a7e update to rc3 2025-10-23 21:28:49 +02:00
henderkes
8e96c64918 add stdext to swoole 2025-10-23 21:28:48 +02:00
henderkes
8a9c8a279d why does this fix compilation? 2025-10-23 21:28:48 +02:00
Marc
72ca0cecd1 Update license path for attr package (#941) 2025-10-23 13:32:43 +02:00
Marc
3564f6d0a7 Fix zip archive strip function for cross-device move (#942) 2025-10-23 13:32:12 +02:00
henderkes
d3e2b4b5b2 don't even build tools in the first place 2025-10-23 13:11:20 +02:00
crazywhalecc
c6de6e7056 test zip unarchive 2025-10-23 16:29:46 +08:00
crazywhalecc
55322a282c Fix rename cross-device link bug (using copy-delete) 2025-10-23 15:19:45 +08:00
Marc
6789ea81ff Update license path for acl in source.json 2025-10-22 19:52:21 +02:00
Marc
ac8a9af89c Update license path for attr package 2025-10-22 19:43:54 +02:00
Marc
553b817b2a prevent libedit from building strlcat/strlcpy, no idea why their sour… (#937) 2025-10-21 21:14:39 +07:00
henderkes
96592bce3e fix 2025-10-21 14:26:44 +02:00
henderkes
4e393886aa no idea why our source has that uncommented 2025-10-21 14:23:55 +02:00
henderkes
2e13be2a7a prevent libedit from building strlcat/strlcpy, no idea why their soure code doesn't prevent this correctly itself 2025-10-21 13:54:34 +02:00
Jerry Ma
41fb29eba4 Feat/pie (#936) 2025-10-21 18:58:04 +08:00
Jerry Ma
b519291fa2 PIE download support & Downloader and ConfigValidator enhance (#934) 2025-10-21 16:20:13 +08:00
crazywhalecc
dd752cd5be Fix windows 7z unzip strip function, fix windows pkg extract files path 2025-10-21 15:50:12 +08:00
Marc
6a153f9aa0 also install-modules of course... (#933) 2025-10-21 10:06:56 +07:00
henderkes
bba390dbcc add runtime libs to unixshell (debian 11 is stupid and doesn't add them automatically) 2025-10-20 23:04:33 +02:00
henderkes
32efeb970c don't build ossfuzzer for zip 2025-10-20 23:00:23 +02:00
henderkes
310335813f use libedit for postgreqsql 2025-10-20 21:24:21 +02:00
crazywhalecc
487980c9a8 phpunit fix 2025-10-20 13:48:52 +08:00
crazywhalecc
f8801e224f phpstan fix 2025-10-20 13:43:05 +08:00
crazywhalecc
5e229a0b01 Update dependencies 2025-10-20 13:41:41 +08:00
crazywhalecc
49cfcbe92d Update docs 2025-10-20 13:41:33 +08:00
crazywhalecc
4e4ce282db Fix zip extract not strip dir bug 2025-10-20 13:41:25 +08:00
crazywhalecc
e2fd3e18d6 Refactor ConfigValidator, make it more strict 2025-10-20 13:41:02 +08:00
crazywhalecc
605c06f85c Add pie support for downloading sources 2025-10-20 13:39:13 +08:00
Marc
4cdefeab81 test with modules to build
linux should use install-modules now, macOS should not (zip is not built with php)
2025-10-19 11:03:39 +02:00
henderkes
8ab09898f0 conditional install-modules 2025-10-19 10:41:14 +02:00
henderkes
9c8b4d627c also install-modules of course... 2025-10-19 09:08:50 +02:00
Marc
8923077120 don't use full install target, don't install-binaries (#932) 2025-10-18 21:21:42 +07:00
henderkes
29c31d90e5 also only do that for macos (saves build time) 2025-10-18 11:35:41 +02:00
henderkes
e281d26a3a Merge remote-tracking branch 'origin' into fix/frankenphp-readline 2025-10-18 11:35:14 +02:00
henderkes
8ed68f481a don't pass unused envs 2025-10-18 11:31:20 +02:00
henderkes
b1abff61a5 don't use full install target, don't install-binaries 2025-10-18 11:08:55 +02:00
crazywhalecc
15638cea4c Add log dir expose, unify SPC_FIX_DEPLOY_ROOT parsing 2025-10-18 12:52:59 +08:00
Marc
9ed77c10e0 use the spx upstream version (#931) 2025-10-17 23:55:37 +07:00
crazywhalecc
ccf262d202 Update test workflow to trigger on pull request events only 2025-10-17 13:28:00 +08:00
crazywhalecc
4be894bc10 Remove ds and gd test 2025-10-17 10:15:05 +08:00
crazywhalecc
6440863ce4 Remove igbinary 2025-10-17 09:25:44 +08:00
henderkes
25d7c72b6e use the upstream version 2025-10-16 23:19:24 +07:00
crazywhalecc
dc4dd6ffa4 Full test 2025-10-16 20:52:20 +08:00
crazywhalecc
bf79134405 Fix tsrmls cache define patches 2025-10-16 20:42:26 +08:00
crazywhalecc
4f8b9d0f81 Fix patches 2025-10-16 02:18:24 +08:00
crazywhalecc
70bda268e5 Fix patches, add more debug comments 2025-10-16 01:51:22 +08:00
crazywhalecc
e559dce9d5 Reduce tests 2025-10-16 01:35:30 +08:00
crazywhalecc
6a98a6bf5e Use BUILD_BIN_PATH instead 2025-10-16 01:32:56 +08:00
crazywhalecc
9b53133ba4 test 2025-10-16 01:19:10 +08:00
crazywhalecc
5b319b0df1 test 2025-10-16 01:16:22 +08:00
crazywhalecc
572bf919aa test 2025-10-16 01:13:26 +08:00
crazywhalecc
1d960a9084 test 2025-10-16 01:05:06 +08:00
crazywhalecc
4b28d1c2df test 2025-10-16 01:04:42 +08:00
crazywhalecc
9c8fd4d45d cs fix 2025-10-16 00:59:33 +08:00
crazywhalecc
b62f029da7 cs fix 2025-10-16 00:56:28 +08:00
crazywhalecc
c711a3666e Add Windows CGI SAPI build support 2025-10-16 00:55:21 +08:00
Marc
487c6da4ac switch readline to libedit (#919) 2025-10-15 10:21:00 +02:00
Marc
e942b13d73 Merge branch 'main' into libedit 2025-10-13 21:28:15 +02:00
Marc
26ccaa4449 Merge branch 'main' into libedit 2025-10-12 11:34:11 +02:00
henderkes
f8d77b9b50 remove pointless comments 2025-10-08 11:55:21 +02:00
henderkes
6594811536 remove pointless SPC_CMD_PREFIX_PHP_MAKE 2025-10-08 11:48:40 +02:00
henderkes
cc7eb7cd84 Merge remote-tracking branch 'origin/main' into libedit 2025-10-08 11:37:54 +02:00
henderkes
330c3486af extra readline check to make sure -a works 2025-10-08 10:45:23 +02:00
henderkes
b9dfb5afe3 maybe better this way 2025-10-08 10:17:14 +02:00
henderkes
565ac87b65 only apply readline fix for CLI compilation on linux 2025-10-08 10:05:02 +02:00
henderkes
f0c39c1770 fix #19871 2025-10-08 09:46:20 +02:00
henderkes
896cf889e4 test readline 2025-10-08 09:27:04 +02:00
henderkes
6b91570054 fix clang musl compilation 2025-10-08 09:26:20 +02:00
henderkes
31906b36e5 reorder stuff for readline 2025-10-08 08:48:30 +02:00
henderkes
4e2d4f3f05 fix ncurses build error (?: [] in case of false) 2025-10-08 08:47:48 +02:00
henderkes
ffdc2dc85a fix #918 (use COPYING.LIB instead of COPYING for libiconv) 2025-10-08 08:18:43 +02:00
DubbleClick
911bc74bf4 add libedit 2025-10-08 08:16:54 +02:00
DubbleClick
4d3501118e add --with-frankenphp-app option to embed an app 2025-09-05 21:47:41 +07:00
310 changed files with 29246 additions and 4486 deletions

View File

@@ -1,13 +1,9 @@
name: Tests name: Tests
on: on:
push:
branches:
- main
paths:
- 'src/globals/test-extensions.php'
pull_request: pull_request:
branches: [ "main" ] branches: [ "main", "v3" ]
types: [ opened, synchronize, reopened ]
paths: paths:
- 'src/**' - 'src/**'
- 'config/**' - 'config/**'
@@ -179,7 +175,7 @@ jobs:
key: php-dependencies-${{ matrix.os }} key: php-dependencies-${{ matrix.os }}
- name: "Install Dependencies" - name: "Install Dependencies"
run: composer update -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist run: composer update -vvv --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --no-plugins
- name: "Run Build Tests (doctor)" - name: "Run Build Tests (doctor)"
run: php src/globals/test-extensions.php doctor_cmd ${{ matrix.os }} ${{ matrix.php }} run: php src/globals/test-extensions.php doctor_cmd ${{ matrix.os }} ${{ matrix.php }}

11
.gitignore vendored
View File

@@ -1,8 +1,8 @@
.idea .idea
runtime/ /runtime/
docker/libraries/ /docker/libraries/
docker/extensions/ /docker/extensions/
docker/source/ /docker/source/
# Vendor files # Vendor files
/vendor/** /vendor/**
@@ -22,6 +22,9 @@ docker/source/
# default package root directory # default package root directory
/pkgroot/** /pkgroot/**
# Windows PHP SDK binary tools
/php-sdk-binary-tools/**
# default pack:lib and release directory # default pack:lib and release directory
/dist/** /dist/**
packlib_files.txt packlib_files.txt

View File

@@ -69,6 +69,6 @@ return (new PhpCsFixer\Config())
'php_unit_data_provider_method_order' => false, 'php_unit_data_provider_method_order' => false,
]) ])
->setFinder( ->setFinder(
PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/SPC']) PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/StaticPHP'])
) )
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()); ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect());

23
bin/spc
View File

@@ -1,13 +1,9 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
use SPC\ConsoleApplication; use StaticPHP\ConsoleApplication;
use SPC\exception\ExceptionHandler; use StaticPHP\Exception\ExceptionHandler;
use StaticPHP\Exception\SPCException;
// Load custom php if exists
if (PHP_OS_FAMILY !== 'Windows' && PHP_BINARY !== (__DIR__ . '/php') && file_exists(__DIR__ . '/php') && is_executable(__DIR__ . '/php')) {
pcntl_exec(__DIR__ . '/php', $argv);
}
if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) { if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
// Current: ./bin (git/project mode) // Current: ./bin (git/project mode)
@@ -17,11 +13,6 @@ if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
require_once dirname(__DIR__, 3) . '/autoload.php'; require_once dirname(__DIR__, 3) . '/autoload.php';
} }
// 防止 Micro 打包状态下不支持中文的显示(虽然这个项目目前好像没输出过中文?)
if (PHP_OS_FAMILY === 'Windows' && Phar::running()) {
exec('CHCP 65001');
}
// Print deprecation notice on PHP < 8.4, use red and highlight background // Print deprecation notice on PHP < 8.4, use red and highlight background
if (PHP_VERSION_ID < 80400) { if (PHP_VERSION_ID < 80400) {
echo "\e[43mDeprecation Notice: PHP < 8.4 is deprecated, please upgrade your PHP version.\e[0m\n"; echo "\e[43mDeprecation Notice: PHP < 8.4 is deprecated, please upgrade your PHP version.\e[0m\n";
@@ -29,7 +20,11 @@ if (PHP_VERSION_ID < 80400) {
try { try {
(new ConsoleApplication())->run(); (new ConsoleApplication())->run();
} catch (Exception $e) { } catch (SPCException $e) {
ExceptionHandler::getInstance()->handle($e); ExceptionHandler::handleSPCException($e);
exit(1);
} catch (\Throwable $e) {
ExceptionHandler::handleDefaultException($e);
exit(1); exit(1);
} }

View File

@@ -3,7 +3,7 @@
set -e set -e
# This file is using docker to run commands # This file is using docker to run commands
SPC_DOCKER_VERSION=v6 SPC_DOCKER_VERSION=v7
# Detect docker can run # Detect docker can run
if ! which docker >/dev/null; then if ! which docker >/dev/null; then
@@ -123,6 +123,7 @@ COPY ./composer.* /app/
ADD ./bin /app/bin ADD ./bin /app/bin
RUN composer install --no-dev RUN composer install --no-dev
ADD ./config /app/config ADD ./config /app/config
ADD ./spc.registry.json /app/spc.registry.json
RUN bin/spc doctor --auto-fix RUN bin/spc doctor --auto-fix
RUN bin/spc install-pkg upx RUN bin/spc install-pkg upx
@@ -150,6 +151,7 @@ MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/source:/app/source"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/log:/app/log"
if [ -f "$(pwd)/craft.yml" ]; then if [ -f "$(pwd)/craft.yml" ]; then
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/craft.yml:/app/craft.yml" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/craft.yml:/app/craft.yml"
fi fi
@@ -161,6 +163,47 @@ if [ ! -z "$GITHUB_TOKEN" ]; then
ENV_LIST="$ENV_LIST -e GITHUB_TOKEN=$GITHUB_TOKEN" ENV_LIST="$ENV_LIST -e GITHUB_TOKEN=$GITHUB_TOKEN"
fi fi
# Intercept and rewrite --with-frankenphp-app option, and mount host path to /app/app
FRANKENPHP_APP_PATH=""
NEW_ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
--with-frankenphp-app=*)
FRANKENPHP_APP_PATH="${1#*=}"
NEW_ARGS+=("--with-frankenphp-app=/app/app")
shift
;;
--with-frankenphp-app)
if [ -n "${2:-}" ]; then
FRANKENPHP_APP_PATH="$2"
NEW_ARGS+=("--with-frankenphp-app=/app/app")
shift 2
else
NEW_ARGS+=("$1")
shift
fi
;;
*)
NEW_ARGS+=("$1")
shift
;;
esac
done
# Normalize the path and add mount if provided
if [ -n "$FRANKENPHP_APP_PATH" ]; then
# expand ~ to $HOME
if [ "${FRANKENPHP_APP_PATH#~}" != "$FRANKENPHP_APP_PATH" ]; then
FRANKENPHP_APP_PATH="$HOME${FRANKENPHP_APP_PATH#~}"
fi
# make absolute if relative
case "$FRANKENPHP_APP_PATH" in
/*) ABS_APP_PATH="$FRANKENPHP_APP_PATH" ;;
*) ABS_APP_PATH="$(pwd)/$FRANKENPHP_APP_PATH" ;;
esac
MOUNT_LIST="$MOUNT_LIST -v $ABS_APP_PATH:/app/app"
fi
# Run docker # Run docker
# shellcheck disable=SC2068 # shellcheck disable=SC2068
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@@ -182,5 +225,5 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
set -ex set -ex
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash
else else
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@ $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST $MOUNT_LIST cwcc-spc-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc "${NEW_ARGS[@]}"
fi fi

4
bin/spc-debug Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
# This script runs the 'spc' command with Xdebug enabled for debugging purposes.
php -d xdebug.mode=debug -d xdebug.client_host=127.0.0.1 -d xdebug.client_port=9003 -d xdebug.start_with_request=yes "$(dirname "$0")/../bin/spc" "$@"

View File

@@ -158,6 +158,7 @@ MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/source:/app/source"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/log:/app/log"
if [ -f "$(pwd)/craft.yml" ]; then if [ -f "$(pwd)/craft.yml" ]; then
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/craft.yml:/app/craft.yml" MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/craft.yml:/app/craft.yml"
fi fi
@@ -173,6 +174,47 @@ if [ ! -z "$GITHUB_TOKEN" ]; then
ENV_LIST="$ENV_LIST -e GITHUB_TOKEN=$GITHUB_TOKEN" ENV_LIST="$ENV_LIST -e GITHUB_TOKEN=$GITHUB_TOKEN"
fi fi
# Intercept and rewrite --with-frankenphp-app option, and mount host path to /app/app
FRANKENPHP_APP_PATH=""
NEW_ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
--with-frankenphp-app=*)
FRANKENPHP_APP_PATH="${1#*=}"
NEW_ARGS+=("--with-frankenphp-app=/app/app")
shift
;;
--with-frankenphp-app)
if [ -n "${2:-}" ]; then
FRANKENPHP_APP_PATH="$2"
NEW_ARGS+=("--with-frankenphp-app=/app/app")
shift 2
else
NEW_ARGS+=("$1")
shift
fi
;;
*)
NEW_ARGS+=("$1")
shift
;;
esac
done
# Normalize the path and add mount if provided
if [ -n "$FRANKENPHP_APP_PATH" ]; then
# expand ~ to $HOME
if [ "${FRANKENPHP_APP_PATH#~}" != "$FRANKENPHP_APP_PATH" ]; then
FRANKENPHP_APP_PATH="$HOME${FRANKENPHP_APP_PATH#~}"
fi
# make absolute if relative
case "$FRANKENPHP_APP_PATH" in
/*) ABS_APP_PATH="$FRANKENPHP_APP_PATH" ;;
*) ABS_APP_PATH="$(pwd)/$FRANKENPHP_APP_PATH" ;;
esac
MOUNT_LIST="$MOUNT_LIST -v $ABS_APP_PATH:/app/app"
fi
# Run docker # Run docker
# shellcheck disable=SC2068 # shellcheck disable=SC2068
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@@ -195,5 +237,5 @@ if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
set -ex set -ex
$DOCKER_EXECUTABLE run $PLATFORM_ARG --privileged --rm -it $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash $DOCKER_EXECUTABLE run $PLATFORM_ARG --privileged --rm -it $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION /bin/bash
else else
$DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc $@ $DOCKER_EXECUTABLE run $PLATFORM_ARG --rm $INTERACT $ENV_LIST --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH-$SPC_DOCKER_VERSION bin/spc "${NEW_ARGS[@]}"
fi fi

View File

@@ -9,14 +9,16 @@
} }
], ],
"require": { "require": {
"php": ">= 8.3", "php": ">=8.4",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-zlib": "*", "ext-zlib": "*",
"laravel/prompts": "^0.1.12", "laravel/prompts": "~0.1",
"nette/php-generator": "^4.2",
"php-di/php-di": "^7.1",
"symfony/console": "^5.4 || ^6 || ^7", "symfony/console": "^5.4 || ^6 || ^7",
"symfony/process": "^7.2", "symfony/process": "^7.2",
"symfony/yaml": "^7.2", "symfony/yaml": "^7.2",
"zhamao/logger": "^1.1.3" "zhamao/logger": "^1.1.4"
}, },
"require-dev": { "require-dev": {
"captainhook/captainhook-phar": "^5.23", "captainhook/captainhook-phar": "^5.23",
@@ -28,7 +30,9 @@
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"SPC\\": "src/SPC" "SPC\\": "src/SPC",
"StaticPHP\\": "src/StaticPHP",
"Package\\": "src/Package"
}, },
"files": [ "files": [
"src/globals/defines.php", "src/globals/defines.php",
@@ -37,7 +41,7 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"SPC\\Tests\\": "tests/SPC" "Tests\\StaticPHP\\": "tests/StaticPHP"
} }
}, },
"bin": [ "bin": [

1274
composer.lock generated

File diff suppressed because it is too large Load Diff

1057
config/artifact.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,42 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; static-php-cli (spc) env configuration ; static-php-cli (spc) env configuration
; ;
; This file is used to set default env vars for static-php-cli build. ; This file is used to set default env vars for static-php-cli build.
; As dynamic build process, some of these vars can be overwritten by CLI options. ; As dynamic build process, some of these vars can be overwritten by CLI options.
; And you can also overwrite these vars by setting them in your shell environment. ; And you can also overwrite these vars by setting them in your shell environment.
; The value should be changed only if you know what you are doing. Otherwise, please leave them as default.
; ;
; We need to use some pre-defined internal env vars, like `BUILD_ROOT_PATH`, `DOWNLOAD_PATH`, etc. ; We need to use some pre-defined internal env vars, like `BUILD_ROOT_PATH`, `DOWNLOAD_PATH`, etc.
; Please note that these vars cannot be defined in this file, they are only be defined before static-php-cli running. ; Please note that these vars cannot be defined in this file, they should only be defined before static-php-cli running.
; ;
; Here's a list of env vars, these value cannot be changed anywhere: ; Here's a list of env vars, these variables will be defined if not defined:
; ;
; SPC_VERSION: the version of static-php-cli.
; WORKING_DIR: the working directory of the build process. (default: `$(pwd)`)
; ROOT_DIR: the root directory of static-php-cli. (default: `/path/to/static-php-cli`, when running in phar or micro mode: `phar://path/to/spc.phar`)
; BUILD_ROOT_PATH: the root path of the build process. (default: `$(pwd)/buildroot`) ; BUILD_ROOT_PATH: the root path of the build process. (default: `$(pwd)/buildroot`)
; BUILD_INCLUDE_PATH: the path of the include files. (default: `$BUILD_ROOT_PATH/include`) ; BUILD_INCLUDE_PATH: the path of the include files. (default: `$BUILD_ROOT_PATH/include`)
; BUILD_LIB_PATH: the path of the lib files. (default: `$BUILD_ROOT_PATH/lib`) ; BUILD_LIB_PATH: the path of the lib files. (default: `$BUILD_ROOT_PATH/lib`)
; BUILD_BIN_PATH: the path of the bin files. (default: `$BUILD_ROOT_PATH/bin`) ; BUILD_BIN_PATH: the path of the bin files. (default: `$BUILD_ROOT_PATH/bin`)
; PKG_ROOT_PATH: the root path of the package files. (default: `$(pwd)/pkgroot`) ; BUILD_MODULES_PATH: the path of the php modules (shared extensions) files. (default: `$BUILD_ROOT_PATH/modules`)
; PKG_ROOT_PATH: the root path of the package files. (default: `$(pwd)/pkgroot/$GNU_ARCH-{darwin|linux|windows}`)
; SOURCE_PATH: the path of the source files. (default: `$(pwd)/source`) ; SOURCE_PATH: the path of the source files. (default: `$(pwd)/source`)
; DOWNLOAD_PATH: the path of the download files. (default: `$(pwd)/downloads`) ; DOWNLOAD_PATH: the path of the download files. (default: `$(pwd)/downloads`)
; CPU_COUNT: the count of the CPU cores. (default: `$(nproc)`) ; PATH: (*nix only) static-php-cli will add `$BUILD_BIN_PATH` to PATH.
; SPC_ARCH: the arch of the current system, for some libraries needed `--host=XXX` args. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`, `arm64`) ; PKG_CONFIG_PATH: (*nix only) static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH.
; GNU_ARCH: the GNU arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`)
; MAC_ARCH: the MAC arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `arm64`)
; ;
; * These vars are only be defined in Unix (macOS, Linux, FreeBSD)Builder and cannot be changed anywhere: ; Here's a list of env vars, these variables is defined in SPC and cannot be changed anywhere:
; PATH: static-php-cli will add `$BUILD_BIN_PATH` to PATH.
; PKG_CONFIG: static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG.
; PKG_CONFIG_PATH: static-php-cli will set `$BUILD_LIB_PATH/pkgconfig` to PKG_CONFIG_PATH.
; ;
; * These vars are only be defined in LinuxBuilder and cannot be changed anywhere: ; SPC_VERSION: the version of static-php-cli.
; SPC_LINUX_DEFAULT_CC: the default compiler for linux. (For alpine linux: `gcc`, default: `$GNU_ARCH-linux-musl-gcc`) ; WORKING_DIR: the working directory of the build process. (default: `$(pwd)`)
; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`) ; ROOT_DIR: the root directory of static-php-cli. (default: `/path/to/static-php-cli`, when running in phar or micro mode: `phar://path/to/spc.phar`)
; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`) ; CPU_COUNT: the count of the CPU cores. (default: `$(nproc)`)
; SPC_EXTRA_PHP_VARS: the extra vars for building php, used in `configure` and `make` command. ; SPC_ARCH: the arch of the current system, for some libraries needed `--host=XXX` args. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`, `arm64`)
; GNU_ARCH: the GNU arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `aarch64`)
; MAC_ARCH: the MAC arch of the current system. (default: `$(uname -m)`, e.g. `x86_64`, `arm64`)
; PKG_CONFIG: (*nix only) static-php-cli will set `$BUILD_BIN_PATH/pkg-config` to PKG_CONFIG.
; SPC_DEFAULT_CC: (*nix only) the default compiler for selected toolchain.
; SPC_DEFAULT_CXX: (*nix only) the default c++ compiler selected toolchain.
; SPC_DEFAULT_AR: (*nix only) the default archiver for selected toolchain.
; SPC_DEFAULT_LD: (*nix only) the default linker for selected toolchain.
; SPC_EXTRA_PHP_VARS: (linux only) the extra vars for building php, used in `configure` and `make` command.
[global] [global]
; Build concurrency for make -jN, default is CPU_COUNT, this value are used in every libs. ; Build concurrency for make -jN, default is CPU_COUNT, this value are used in every libs.
@@ -44,9 +46,15 @@ SPC_SKIP_PHP_VERSION_CHECK="no"
; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed") ; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed")
SPC_SKIP_DOCTOR_CHECK_ITEMS="" SPC_SKIP_DOCTOR_CHECK_ITEMS=""
; extra modules that xcaddy will include in the FrankenPHP build ; extra modules that xcaddy will include in the FrankenPHP build
SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/caddy --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli" SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli"
; The display message for php version output (PHP >= 8.4 available) ; The display message for php version output (PHP >= 8.4 available)
PHP_BUILD_PROVIDER="static-php-cli ${SPC_VERSION}" PHP_BUILD_PROVIDER="static-php-cli ${SPC_VERSION}"
; Whether to enable log file (if you are using vendor mode)
SPC_ENABLE_LOG_FILE="yes"
; The LOG DIR for spc logs
SPC_LOGS_DIR="${WORKING_DIR}/log"
; Preserve old logs when running new builds
SPC_PRESERVE_LOGS="no"
; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them ; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them
; only useful for builds targeting not pure-static linking ; only useful for builds targeting not pure-static linking
@@ -65,7 +73,7 @@ PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path ; upx executable path
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe" UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static,win32_api
[linux] [linux]
; Linux can use different build toolchains. ; Linux can use different build toolchains.
@@ -105,15 +113,11 @@ SPC_MICRO_PATCHES=cli_checks,disable_huge_page
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force" SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command ; configure command
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-phpdbg --with-pic" SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-phpdbg --with-pic"
; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
; *** default build vars for building php *** ; *** default build vars for building php ***
; embed type for php, static (libphp.a) or shared (libphp.so) ; embed type for php, static (libphp.a) or shared (libphp.so)
SPC_CMD_VAR_PHP_EMBED_TYPE="static" SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; CFLAGS for configuring php ; EXTRA_CFLAGS for `configure` and `make` php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE"
; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}"
; 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=""
@@ -123,11 +127,12 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
; Currently we do not support universal and cross-compilation for macOS. ; Currently we do not support universal and cross-compilation for macOS.
SPC_TARGET=native-macos SPC_TARGET=native-macos
; compiler environments ; compiler environments
CC=clang CC=${SPC_LINUX_DEFAULT_CC}
CXX=clang++ CXX=${SPC_LINUX_DEFAULT_CXX}
AR=ar AR=${SPC_LINUX_DEFAULT_AR}
LD=ld LD=${SPC_LINUX_DEFAULT_LD}
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
; this will be added to all CFLAGS and CXXFLAGS for the library builds
SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
SPC_DEFAULT_LD_FLAGS="" SPC_DEFAULT_LD_FLAGS=""
@@ -139,18 +144,9 @@ SPC_MICRO_PATCHES=cli_checks,macos_iconv
SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force" SPC_CMD_PREFIX_PHP_BUILDCONF="./buildconf --force"
; configure command ; configure command
SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-phpdbg" SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-phpdbg"
; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
; *** default build vars for building php *** ; *** default build vars for building php ***
; embed type for php, static (libphp.a) or shared (libphp.dylib) ; embed type for php, static (libphp.a) or shared (libphp.dylib)
SPC_CMD_VAR_PHP_EMBED_TYPE="static" SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; CFLAGS for configuring php ; EXTRA_CFLAGS for `configure` and `make` php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Werror=unknown-warning-option ${SPC_DEFAULT_C_FLAGS}"
; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie ${SPC_DEFAULT_C_FLAGS}"
[freebsd]
; compiler environments
CC=clang
CXX=clang++

View File

@@ -341,6 +341,7 @@
"ext-depends": [ "ext-depends": [
"xml" "xml"
], ],
"build-with-php": true,
"target": [ "target": [
"static" "static"
] ]
@@ -357,6 +358,18 @@
"liblz4" "liblz4"
] ]
}, },
"maxminddb": {
"support": {
"BSD": "wip",
"Windows": "wip"
},
"type": "external",
"source": "ext-maxminddb",
"arg-type": "with",
"lib-depends": [
"libmaxminddb"
]
},
"mbregex": { "mbregex": {
"type": "builtin", "type": "builtin",
"arg-type": "custom", "arg-type": "custom",
@@ -453,7 +466,7 @@
"type": "external", "type": "external",
"source": "msgpack", "source": "msgpack",
"arg-type-unix": "with", "arg-type-unix": "with",
"arg-type-win": "enable", "arg-type-windows": "enable",
"ext-depends": [ "ext-depends": [
"session" "session"
] ]
@@ -461,6 +474,7 @@
"mysqli": { "mysqli": {
"type": "builtin", "type": "builtin",
"arg-type": "with", "arg-type": "with",
"build-with-php": true,
"ext-depends": [ "ext-depends": [
"mysqlnd" "mysqlnd"
] ]
@@ -468,6 +482,7 @@
"mysqlnd": { "mysqlnd": {
"type": "builtin", "type": "builtin",
"arg-type-windows": "with", "arg-type-windows": "with",
"build-with-php": true,
"lib-depends": [ "lib-depends": [
"zlib" "zlib"
] ]
@@ -552,6 +567,13 @@
"type": "builtin", "type": "builtin",
"unix-only": true "unix-only": true
}, },
"pcov": {
"type": "external",
"source": "pcov",
"target": [
"shared"
]
},
"pdo": { "pdo": {
"type": "builtin" "type": "builtin"
}, },
@@ -687,7 +709,7 @@
"type": "builtin", "type": "builtin",
"arg-type": "with-path", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"readline" "libedit"
], ],
"target": [ "target": [
"static" "static"
@@ -753,6 +775,18 @@
"apcu" "apcu"
] ]
}, },
"snmp": {
"support": {
"Windows": "wip",
"BSD": "wip"
},
"type": "builtin",
"arg-type-unix": "with",
"arg-type-windows": "with",
"lib-depends": [
"net-snmp"
]
},
"soap": { "soap": {
"support": { "support": {
"BSD": "wip" "BSD": "wip"
@@ -797,6 +831,7 @@
"type": "builtin", "type": "builtin",
"arg-type": "with-path", "arg-type": "with-path",
"arg-type-windows": "with", "arg-type-windows": "with",
"build-with-php": true,
"lib-depends": [ "lib-depends": [
"sqlite" "sqlite"
] ]
@@ -885,6 +920,22 @@
"mysqli" "mysqli"
] ]
}, },
"swoole-hook-odbc": {
"support": {
"Windows": "no",
"BSD": "wip"
},
"notes": true,
"type": "addon",
"arg-type": "none",
"ext-depends": [
"pdo",
"swoole"
],
"lib-depends": [
"unixodbc"
]
},
"swoole-hook-pgsql": { "swoole-hook-pgsql": {
"support": { "support": {
"Windows": "no", "Windows": "no",
@@ -914,22 +965,6 @@
"swoole" "swoole"
] ]
}, },
"swoole-hook-odbc": {
"support": {
"Windows": "no",
"BSD": "wip"
},
"notes": true,
"type": "addon",
"arg-type": "none",
"ext-depends": [
"pdo",
"swoole"
],
"lib-depends": [
"unixodbc"
]
},
"swow": { "swow": {
"support": { "support": {
"BSD": "wip" "BSD": "wip"
@@ -984,6 +1019,14 @@
"type": "builtin", "type": "builtin",
"build-with-php": true "build-with-php": true
}, },
"trader": {
"support": {
"BSD": "wip",
"Windows": "wip"
},
"type": "external",
"source": "ext-trader"
},
"uuid": { "uuid": {
"support": { "support": {
"Windows": "wip", "Windows": "wip",
@@ -1177,6 +1220,7 @@
"lib-depends": [ "lib-depends": [
"zlib" "zlib"
], ],
"build-with-php": true,
"target": [ "target": [
"static" "static"
] ]

View File

@@ -7,12 +7,14 @@
"source": "php-src", "source": "php-src",
"lib-depends": [ "lib-depends": [
"lib-base", "lib-base",
"micro" "micro",
"frankenphp"
], ],
"lib-depends-macos": [ "lib-depends-macos": [
"lib-base", "lib-base",
"micro", "micro",
"libxml2" "libxml2",
"frankenphp"
], ],
"lib-suggests-linux": [ "lib-suggests-linux": [
"libacl", "libacl",
@@ -24,6 +26,10 @@
"watcher" "watcher"
] ]
}, },
"frankenphp": {
"source": "frankenphp",
"type": "target"
},
"micro": { "micro": {
"type": "target", "type": "target",
"source": "micro" "source": "micro"
@@ -98,7 +104,9 @@
"ngtcp2", "ngtcp2",
"zstd", "zstd",
"libcares", "libcares",
"ldap" "ldap",
"idn2",
"krb5"
], ],
"lib-suggests-windows": [ "lib-suggests-windows": [
"brotli", "brotli",
@@ -203,7 +211,6 @@
"libcares" "libcares"
], ],
"cpp-library": true, "cpp-library": true,
"provide-pre-built": true,
"frameworks": [ "frameworks": [
"CoreFoundation" "CoreFoundation"
] ]
@@ -229,6 +236,24 @@
"unicode" "unicode"
] ]
}, },
"idn2": {
"source": "libidn2",
"pkg-configs": [
"libidn2"
],
"headers": [
"idn2.h"
],
"lib-suggests-unix": [
"libiconv",
"gettext",
"libunistring"
],
"lib-depends-macos": [
"libiconv",
"gettext"
]
},
"imagemagick": { "imagemagick": {
"source": "imagemagick", "source": "imagemagick",
"cpp-library": true, "cpp-library": true,
@@ -276,6 +301,26 @@
"jbig_ar.h" "jbig_ar.h"
] ]
}, },
"krb5": {
"source": "krb5",
"pkg-configs": [
"krb5-gssapi"
],
"headers": [
"krb5.h",
"gssapi/gssapi.h"
],
"lib-depends": [
"openssl"
],
"lib-suggests": [
"ldap",
"libedit"
],
"frameworks": [
"Kerberos"
]
},
"ldap": { "ldap": {
"source": "ldap", "source": "ldap",
"pkg-configs": [ "pkg-configs": [
@@ -345,6 +390,15 @@
], ],
"cpp-library": true "cpp-library": true
}, },
"libedit": {
"source": "libedit",
"static-libs-unix": [
"libedit.a"
],
"lib-depends": [
"ncurses"
]
},
"libevent": { "libevent": {
"source": "libevent", "source": "libevent",
"static-libs-unix": [ "static-libs-unix": [
@@ -452,6 +506,16 @@
"liblz4.a" "liblz4.a"
] ]
}, },
"libmaxminddb": {
"source": "libmaxminddb",
"static-libs-unix": [
"libmaxminddb.a"
],
"headers": [
"maxminddb.h",
"maxminddb_config.h"
]
},
"libmemcached": { "libmemcached": {
"source": "libmemcached", "source": "libmemcached",
"cpp-library": true, "cpp-library": true,
@@ -498,13 +562,16 @@
}, },
"librdkafka": { "librdkafka": {
"source": "librdkafka", "source": "librdkafka",
"static-libs-unix": [ "pkg-configs": [
"librdkafka.a", "rdkafka++-static",
"librdkafka++.a", "rdkafka-static"
"librdkafka-static.a"
], ],
"cpp-library": true, "cpp-library": true,
"lib-suggests": [ "lib-suggests": [
"curl",
"liblz4",
"openssl",
"zlib",
"zstd" "zstd"
] ]
}, },
@@ -551,6 +618,31 @@
"zstd" "zstd"
] ]
}, },
"libunistring": {
"source": "libunistring",
"static-libs-unix": [
"libunistring.a"
],
"headers": [
"unistr.h",
"unistring/"
]
},
"liburing": {
"source": "liburing",
"pkg-configs": [
"liburing",
"liburing-ffi"
],
"static-libs-linux": [
"liburing.a",
"liburing-ffi.a"
],
"headers-linux": [
"liburing/",
"liburing.h"
]
},
"libuuid": { "libuuid": {
"source": "libuuid", "source": "libuuid",
"static-libs-unix": [ "static-libs-unix": [
@@ -584,8 +676,8 @@
}, },
"libxml2": { "libxml2": {
"source": "libxml2", "source": "libxml2",
"static-libs-unix": [ "pkg-configs": [
"libxml2.a" "libxml-2.0"
], ],
"static-libs-windows": [ "static-libs-windows": [
"libxml2s.lib", "libxml2s.lib",
@@ -674,6 +766,17 @@
"libncurses.a" "libncurses.a"
] ]
}, },
"net-snmp": {
"source": "net-snmp",
"pkg-configs": [
"netsnmp",
"netsnmp-agent"
],
"lib-depends": [
"openssl",
"zlib"
]
},
"nghttp2": { "nghttp2": {
"source": "nghttp2", "source": "nghttp2",
"static-libs-unix": [ "static-libs-unix": [
@@ -772,7 +875,7 @@
"libxml2", "libxml2",
"openssl", "openssl",
"zlib", "zlib",
"readline" "libedit"
], ],
"lib-suggests": [ "lib-suggests": [
"icu", "icu",
@@ -931,20 +1034,5 @@
"zstd.h", "zstd.h",
"zstd_errors.h" "zstd_errors.h"
] ]
},
"liburing": {
"source": "liburing",
"pkg-configs": [
"liburing",
"liburing-ffi"
],
"static-libs-linux": [
"liburing.a",
"liburing-ffi.a"
],
"headers-linux": [
"liburing/",
"liburing.h"
]
} }
} }

1541
config/pkg.ext.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,105 +0,0 @@
{
"go-xcaddy-aarch64-linux": {
"type": "custom"
},
"go-xcaddy-aarch64-macos": {
"type": "custom"
},
"go-xcaddy-x86_64-linux": {
"type": "custom"
},
"go-xcaddy-x86_64-macos": {
"type": "custom"
},
"musl-toolchain-aarch64-linux": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz"
},
"musl-toolchain-x86_64-linux": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz"
},
"nasm-x86_64-win": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/nasm/nasm-2.16.01-win64.zip",
"extract-files": {
"nasm-2.16.01/nasm.exe": "{php_sdk_path}/bin/nasm.exe",
"nasm-2.16.01/ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
}
},
"pkg-config-aarch64-linux": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-aarch64-linux-musl-1.2.5.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"pkg-config-aarch64-macos": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-aarch64-darwin.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"pkg-config-x86_64-linux": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-x86_64-linux-musl-1.2.5.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"pkg-config-x86_64-macos": {
"type": "ghrel",
"repo": "static-php/static-php-cli-hosted",
"match": "pkg-config-x86_64-darwin.txz",
"extract-files": {
"bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
}
},
"strawberry-perl-x86_64-win": {
"type": "url",
"url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip"
},
"upx-aarch64-linux": {
"type": "ghrel",
"repo": "upx/upx",
"match": "upx.+-arm64_linux\\.tar\\.xz",
"extract-files": {
"upx": "{pkg_root_path}/bin/upx"
}
},
"upx-x86_64-linux": {
"type": "ghrel",
"repo": "upx/upx",
"match": "upx.+-amd64_linux\\.tar\\.xz",
"extract-files": {
"upx": "{pkg_root_path}/bin/upx"
}
},
"upx-x86_64-win": {
"type": "ghrel",
"repo": "upx/upx",
"match": "upx.+-win64\\.zip",
"extract-files": {
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
}
},
"zig-aarch64-linux": {
"type": "custom"
},
"zig-aarch64-macos": {
"type": "custom"
},
"zig-x86_64-linux": {
"type": "custom"
},
"zig-x86_64-macos": {
"type": "custom"
},
"zig-x86_64-win": {
"type": "custom"
}
}

992
config/pkg.lib.json Normal file
View File

@@ -0,0 +1,992 @@
{
"attr": {
"type": "library",
"artifact": "attr",
"license": {
"type": "file",
"path": "doc/COPYING.LGPL"
}
},
"brotli": {
"type": "library",
"headers": [
"brotli"
],
"pkg-configs": [
"libbrotlicommon",
"libbrotlidec",
"libbrotlienc"
],
"artifact": "brotli",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"bzip2": {
"type": "library",
"headers": [
"bzlib.h"
],
"artifact": "bzip2",
"license": {
"type": "text",
"text": "This program, \"bzip2\", the associated library \"libbzip2\", and all documentation, are copyright (C) 1996-2010 Julian R Seward. All rights reserved. \n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nJulian Seward, jseward@bzip.org bzip2/libbzip2 version 1.0.6 of 6 September 2010\n\nPATENTS: To the best of my knowledge, bzip2 and libbzip2 do not use any patented algorithms. However, I do not have the resources to carry out a patent search. Therefore I cannot give any guarantee of the above statement."
}
},
"curl": {
"type": "library",
"depends@windows": [
"zlib",
"libssh2",
"nghttp2"
],
"depends": [
"openssl",
"zlib"
],
"suggests@windows": [
"brotli",
"zstd"
],
"suggests": [
"libssh2",
"brotli",
"nghttp2",
"nghttp3",
"ngtcp2",
"zstd",
"libcares",
"ldap"
],
"headers": [
"curl"
],
"frameworks": [
"CoreFoundation",
"CoreServices",
"SystemConfiguration"
],
"artifact": "curl",
"license": {
"type": "file",
"path": "COPYING"
}
},
"fastlz": {
"type": "library",
"headers": [
"fastlz/fastlz.h"
],
"artifact": "fastlz",
"license": {
"type": "file",
"path": "LICENSE.MIT"
}
},
"freetype": {
"type": "library",
"depends": [
"zlib"
],
"suggests": [
"libpng",
"bzip2",
"brotli"
],
"headers": [
"freetype2/freetype/freetype.h",
"freetype2/ft2build.h"
],
"artifact": "freetype",
"license": {
"type": "file",
"path": "LICENSE.TXT"
}
},
"gettext": {
"type": "library",
"depends": [
"libiconv"
],
"suggests": [
"ncurses",
"libxml2"
],
"frameworks": [
"CoreFoundation"
],
"artifact": "gettext",
"license": {
"type": "file",
"path": "gettext-runtime/intl/COPYING.LIB"
}
},
"glfw": {
"type": "library",
"frameworks": [
"CoreVideo",
"OpenGL",
"Cocoa",
"IOKit"
],
"artifact": "ext-glfw",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"gmp": {
"type": "library",
"headers": [
"gmp.h"
],
"artifact": "gmp",
"license": {
"type": "text",
"text": "Since version 6, GMP is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2. These licenses make the library free to use, share, and improve, and allow you to pass on the result. The GNU licenses give freedoms, but also set firm restrictions on the use with non-free programs."
}
},
"gmssl": {
"type": "library",
"frameworks": [
"Security"
],
"artifact": "gmssl",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"grpc": {
"type": "library",
"depends": [
"zlib",
"openssl",
"libcares"
],
"pkg-configs": [
"grpc"
],
"frameworks": [
"CoreFoundation"
],
"artifact": "grpc",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"icu": {
"type": "library",
"pkg-configs": [
"icu-uc",
"icu-i18n",
"icu-io"
],
"artifact": "icu",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"icu-static-win": {
"type": "library",
"headers@windows": [
"unicode"
],
"artifact": "icu-static-win",
"license": {
"type": "text",
"text": "none"
}
},
"imagemagick": {
"type": "library",
"depends": [
"zlib",
"libjpeg",
"libjxl",
"libpng",
"libwebp",
"freetype",
"libtiff",
"libheif",
"bzip2"
],
"suggests": [
"zstd",
"xz",
"libzip",
"libxml2"
],
"pkg-configs": [
"Magick++-7.Q16HDRI",
"MagickCore-7.Q16HDRI",
"MagickWand-7.Q16HDRI"
],
"artifact": "imagemagick",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"imap": {
"type": "library",
"suggests": [
"openssl"
],
"artifact": "imap",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"jbig": {
"type": "library",
"headers": [
"jbig.h",
"jbig85.h",
"jbig_ar.h"
],
"artifact": "jbig",
"license": {
"type": "file",
"path": "COPYING"
}
},
"ldap": {
"type": "library",
"depends": [
"openssl",
"zlib",
"gmp",
"libsodium"
],
"pkg-configs": [
"ldap",
"lber"
],
"artifact": "ldap",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"lerc": {
"type": "library",
"artifact": "lerc",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libacl": {
"type": "library",
"depends": [
"attr"
],
"artifact": "libacl",
"license": {
"type": "file",
"path": "doc/COPYING.LGPL"
}
},
"libaom": {
"type": "library",
"artifact": "libaom",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libargon2": {
"type": "library",
"artifact": "libargon2",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libavif": {
"type": "library",
"artifact": "libavif",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libcares": {
"type": "library",
"headers": [
"ares.h",
"ares_dns.h",
"ares_nameser.h"
],
"artifact": "libcares",
"license": {
"type": "file",
"path": "LICENSE.md"
}
},
"libde265": {
"type": "library",
"artifact": "libde265",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libedit": {
"type": "library",
"depends": [
"ncurses"
],
"artifact": "libedit",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libevent": {
"type": "library",
"depends": [
"openssl"
],
"artifact": "libevent",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libffi": {
"type": "library",
"headers@windows": [
"ffi.h",
"fficonfig.h",
"ffitarget.h"
],
"headers": [
"ffi.h",
"ffitarget.h"
],
"artifact": "libffi",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libffi-win": {
"type": "library",
"headers@windows": [
"ffi.h",
"ffitarget.h",
"fficonfig.h"
],
"artifact": "libffi-win",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libheif": {
"type": "library",
"depends": [
"libde265",
"libwebp",
"libaom",
"zlib",
"brotli"
],
"artifact": "libheif",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libiconv": {
"type": "library",
"headers": [
"iconv.h",
"libcharset.h",
"localcharset.h"
],
"artifact": "libiconv",
"license": {
"type": "file",
"path": "COPYING.LIB"
}
},
"libiconv-win": {
"type": "library",
"artifact": "libiconv-win",
"license": {
"type": "file",
"path": "source/COPYING"
}
},
"libjpeg": {
"type": "library",
"suggests@windows": [
"zlib"
],
"artifact": "libjpeg",
"license": {
"type": "file",
"path": "LICENSE.md"
}
},
"libjxl": {
"type": "library",
"depends": [
"brotli",
"libjpeg",
"libpng",
"libwebp"
],
"pkg-configs": [
"libjxl",
"libjxl_cms",
"libjxl_threads",
"libhwy"
],
"artifact": "libjxl",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"liblz4": {
"type": "library",
"artifact": "liblz4",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libmemcached": {
"type": "library",
"artifact": "libmemcached",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libpng": {
"type": "library",
"depends": [
"zlib"
],
"headers@windows": [
"png.h",
"pngconf.h"
],
"headers": [
"png.h",
"pngconf.h",
"pnglibconf.h"
],
"artifact": "libpng",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"librabbitmq": {
"type": "library",
"depends": [
"openssl"
],
"artifact": "librabbitmq",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"librdkafka": {
"type": "library",
"suggests": [
"curl",
"liblz4",
"openssl",
"zlib",
"zstd"
],
"pkg-configs": [
"rdkafka++-static",
"rdkafka-static"
],
"artifact": "librdkafka",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libsodium": {
"type": "library",
"artifact": "libsodium",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libssh2": {
"type": "library",
"depends": [
"openssl"
],
"headers": [
"libssh2.h",
"libssh2_publickey.h",
"libssh2_sftp.h"
],
"artifact": "libssh2",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libtiff": {
"type": "library",
"depends": [
"zlib",
"libjpeg"
],
"suggests": [
"lerc",
"libwebp",
"jbig",
"xz",
"zstd"
],
"artifact": "libtiff",
"license": {
"type": "file",
"path": "LICENSE.md"
}
},
"liburing": {
"type": "library",
"headers@linux": [
"liburing/",
"liburing.h"
],
"pkg-configs": [
"liburing",
"liburing-ffi"
],
"artifact": "liburing",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libuuid": {
"type": "library",
"headers": [
"uuid/uuid.h"
],
"artifact": "libuuid",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libuv": {
"type": "library",
"artifact": "libuv",
"license": [
{
"type": "file",
"path": "LICENSE"
},
{
"type": "file",
"path": "LICENSE-extra"
}
]
},
"libwebp": {
"type": "library",
"pkg-configs": [
"libwebp",
"libwebpdecoder",
"libwebpdemux",
"libwebpmux",
"libsharpyuv"
],
"artifact": "libwebp",
"license": {
"type": "file",
"path": "COPYING"
}
},
"libxml2": {
"type": "library",
"depends@windows": [
"libiconv-win"
],
"depends": [
"libiconv"
],
"suggests@windows": [
"zlib"
],
"suggests": [
"xz",
"zlib"
],
"headers": [
"libxml2"
],
"pkg-configs": [
"libxml-2.0"
],
"artifact": "libxml2",
"license": {
"type": "file",
"path": "Copyright"
}
},
"libxslt": {
"type": "library",
"depends": [
"libxml2"
],
"artifact": "libxslt",
"license": {
"type": "file",
"path": "Copyright"
}
},
"libyaml": {
"type": "library",
"headers": [
"yaml.h"
],
"artifact": "libyaml",
"license": {
"type": "file",
"path": "License"
}
},
"libzip": {
"type": "library",
"depends@windows": [
"zlib",
"bzip2",
"xz"
],
"depends": [
"zlib"
],
"suggests@windows": [
"zstd",
"openssl"
],
"suggests": [
"bzip2",
"xz",
"zstd",
"openssl"
],
"headers": [
"zip.h",
"zipconf.h"
],
"artifact": "libzip",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"mimalloc": {
"type": "library",
"artifact": "mimalloc",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ncurses": {
"type": "library",
"artifact": "ncurses",
"static-libs@unix": [
"libncurses.a"
],
"license": {
"type": "file",
"path": "COPYING"
}
},
"net-snmp": {
"type": "library",
"depends": [
"openssl",
"zlib"
],
"pkg-configs": [
"netsnmp",
"netsnmp-agent"
],
"artifact": "net-snmp",
"license": {
"type": "file",
"path": "COPYING"
}
},
"nghttp2": {
"type": "library",
"depends": [
"zlib",
"openssl"
],
"suggests": [
"libxml2",
"nghttp3",
"ngtcp2"
],
"headers": [
"nghttp2"
],
"artifact": "nghttp2",
"license": {
"type": "file",
"path": "COPYING"
}
},
"nghttp3": {
"type": "library",
"depends": [
"openssl"
],
"headers": [
"nghttp3"
],
"artifact": "nghttp3",
"license": {
"type": "file",
"path": "COPYING"
}
},
"ngtcp2": {
"type": "library",
"depends": [
"openssl"
],
"suggests": [
"nghttp3",
"brotli"
],
"headers": [
"ngtcp2"
],
"artifact": "ngtcp2",
"license": {
"type": "file",
"path": "COPYING"
}
},
"onig": {
"type": "library",
"headers": [
"oniggnu.h",
"oniguruma.h"
],
"artifact": "onig",
"license": {
"type": "file",
"path": "COPYING"
}
},
"openssl": {
"type": "library",
"depends": [
"zlib"
],
"headers": [
"openssl"
],
"artifact": "openssl",
"license": {
"type": "file",
"path": "LICENSE.txt"
}
},
"postgresql": {
"type": "library",
"depends": [
"libiconv",
"libxml2",
"openssl",
"zlib",
"libedit"
],
"suggests": [
"icu",
"libxslt",
"ldap",
"zstd"
],
"pkg-configs": [
"libpq"
],
"artifact": "postgresql",
"license": {
"type": "file",
"path": "COPYRIGHT"
}
},
"postgresql-win": {
"type": "library",
"artifact": "postgresql-win",
"license": {
"type": "text",
"text": "PostgreSQL Database Management System\n(also known as Postgres, formerly as Postgres95)\n\nPortions Copyright (c) 1996-2025, The PostgreSQL Global Development Group\n\nPortions Copyright (c) 1994, The Regents of the University of California\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose, without fee, and without a written\nagreement is hereby granted, provided that the above copyright notice\nand this paragraph and the following two paragraphs appear in all\ncopies.\n\nIN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY\nFOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,\nINCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS\nDOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n\nTHE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,\nINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS\nON AN \"AS IS\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS\nTO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
}
},
"pthreads4w": {
"type": "library",
"artifact": "pthreads4w",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"qdbm": {
"type": "library",
"headers@windows": [
"depot.h"
],
"artifact": "qdbm",
"license": {
"type": "file",
"path": "COPYING"
}
},
"re2c": {
"type": "library",
"artifact": "re2c",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"readline": {
"type": "library",
"depends": [
"ncurses"
],
"artifact": "readline",
"license": {
"type": "file",
"path": "COPYING"
}
},
"snappy": {
"type": "library",
"depends": [
"zlib"
],
"headers": [
"snappy.h",
"snappy-c.h",
"snappy-sinksource.h",
"snappy-stubs-public.h"
],
"artifact": "snappy",
"license": {
"type": "file",
"path": "COPYING"
}
},
"sqlite": {
"type": "library",
"headers": [
"sqlite3.h",
"sqlite3ext.h"
],
"artifact": "sqlite",
"license": {
"type": "text",
"text": "The author disclaims copyright to this source code. In place of\na legal notice, here is a blessing:\n\n * May you do good and not evil.\n * May you find forgiveness for yourself and forgive others.\n * May you share freely, never taking more than you give."
}
},
"tidy": {
"type": "library",
"artifact": "tidy",
"license": {
"type": "file",
"path": "README/LICENSE.md"
}
},
"unixodbc": {
"type": "library",
"depends": [
"libiconv"
],
"artifact": "unixodbc",
"license": {
"type": "file",
"path": "COPYING"
}
},
"watcher": {
"type": "library",
"headers": [
"wtr/watcher-c.h"
],
"artifact": "watcher",
"license": {
"type": "file",
"path": "license"
}
},
"xz": {
"type": "library",
"depends": [
"libiconv"
],
"headers@windows": [
"lzma",
"lzma.h"
],
"headers": [
"lzma"
],
"artifact": "xz",
"license": {
"type": "file",
"path": "COPYING"
}
},
"zlib": {
"type": "library",
"headers": [
"zlib.h",
"zconf.h"
],
"artifact": "zlib",
"license": {
"type": "text",
"text": "(C) 1995-2022 Jean-loup Gailly and Mark Adler\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n claim that you wrote the original software. If you use this software\n in a product, an acknowledgment in the product documentation would be\n appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\nJean-loup Gailly Mark Adler\njloup@gzip.org madler@alumni.caltech.edu"
}
},
"zstd": {
"type": "library",
"headers@windows": [
"zstd.h",
"zstd_errors.h"
],
"headers": [
"zdict.h",
"zstd.h",
"zstd_errors.h"
],
"artifact": "zstd",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}

95
config/pkg.target.json Normal file
View File

@@ -0,0 +1,95 @@
{
"vswhere": {
"type": "target",
"artifact": "vswhere"
},
"pkg-config": {
"type": "target",
"static-bins": [
"pkg-config"
],
"artifact": "pkg-config"
},
"php": {
"type": "target",
"artifact": "php-src",
"depends@macos": [
"libxml2"
]
},
"php-cli": {
"type": "virtual-target",
"depends": [
"php"
]
},
"php-micro": {
"type": "virtual-target",
"artifact": "micro",
"depends": [
"php"
]
},
"php-cgi": {
"type": "virtual-target",
"depends": [
"php"
]
},
"php-fpm": {
"type": "virtual-target",
"depends": [
"php"
]
},
"php-embed": {
"type": "virtual-target",
"depends": [
"php"
]
},
"frankenphp": {
"type": "virtual-target",
"artifact": "frankenphp",
"depends": [
"php-embed",
"go-xcaddy"
],
"depends@macos": [
"php-embed",
"go-xcaddy",
"libxml2"
]
},
"go-xcaddy": {
"type": "target",
"artifact": "go-xcaddy",
"static-bins": [
"xcaddy"
]
},
"musl-toolchain": {
"type": "target",
"artifact": "musl-toolchain"
},
"strawberry-perl": {
"type": "target",
"artifact": "strawberry-perl"
},
"upx": {
"type": "target",
"artifact": "upx"
},
"zig": {
"type": "target",
"artifact": "zig"
},
"nasm": {
"type": "target",
"artifact": "nasm"
},
"php-sdk-binary-tools": {
"type": "target",
"artifact": "php-sdk-binary-tools"
}
}

View File

@@ -47,7 +47,7 @@
"provide-pre-built": true, "provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "doc/COPYING" "path": "doc/COPYING.LGPL"
} }
}, },
"brotli": { "brotli": {
@@ -184,6 +184,15 @@
] ]
} }
}, },
"ext-maxminddb": {
"type": "url",
"url": "https://pecl.php.net/get/maxminddb",
"filename": "ext-maxminddb.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-memcache": { "ext-memcache": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/memcache", "url": "https://pecl.php.net/get/memcache",
@@ -233,6 +242,16 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-trader": {
"type": "url",
"url": "https://pecl.php.net/get/trader",
"path": "php-src/ext/trader",
"filename": "trader.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-uuid": { "ext-uuid": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/uuid", "url": "https://pecl.php.net/get/uuid",
@@ -291,10 +310,19 @@
"path": "LICENSE.MIT" "path": "LICENSE.MIT"
} }
}, },
"frankenphp": {
"type": "ghtar",
"repo": "php/frankenphp",
"prefer-stable": true,
"license": {
"type": "file",
"path": "LICENSE"
}
},
"freetype": { "freetype": {
"type": "git", "type": "ghtagtar",
"rev": "VER-2-13-2", "repo": "freetype/freetype",
"url": "https://github.com/freetype/freetype", "match": "VER-2-\\d+-\\d+",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE.TXT" "path": "LICENSE.TXT"
@@ -306,16 +334,17 @@
"regex": "/href=\"(?<file>gettext-(?<version>[^\"]+)\\.tar\\.xz)\"/", "regex": "/href=\"(?<file>gettext-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "gettext-runtime/intl/COPYING.LIB"
} }
}, },
"gmp": { "gmp": {
"type": "url", "type": "filelist",
"url": "https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz", "url": "https://gmplib.org/download/gmp/",
"regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"provide-pre-built": true, "provide-pre-built": true,
"alt": { "alt": {
"type": "ghtagtar", "type": "url",
"repo": "alisw/GMP" "url": "https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz"
}, },
"license": { "license": {
"type": "text", "type": "text",
@@ -410,6 +439,16 @@
"path": "COPYING" "path": "COPYING"
} }
}, },
"krb5": {
"type": "ghtagtar",
"repo": "krb5/krb5",
"match": "krb5.+-final",
"prefer-stable": true,
"license": {
"type": "file",
"path": "NOTICE"
}
},
"ldap": { "ldap": {
"type": "filelist", "type": "filelist",
"url": "https://www.openldap.org/software/download/OpenLDAP/openldap-release/", "url": "https://www.openldap.org/software/download/OpenLDAP/openldap-release/",
@@ -439,7 +478,7 @@
"provide-pre-built": true, "provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "doc/COPYING" "path": "doc/COPYING.LGPL"
} }
}, },
"libaom": { "libaom": {
@@ -498,6 +537,16 @@
"path": "COPYING" "path": "COPYING"
} }
}, },
"libedit": {
"type": "filelist",
"url": "https://thrysoee.dk/editline/",
"regex": "/href=\"(?<file>libedit-(?<version>[^\"]+)\\.tar\\.gz)\"/",
"provide-pre-built": true,
"license": {
"type": "file",
"path": "COPYING"
}
},
"libevent": { "libevent": {
"type": "ghrel", "type": "ghrel",
"repo": "libevent/libevent", "repo": "libevent/libevent",
@@ -546,7 +595,7 @@
"provide-pre-built": true, "provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "COPYING.LIB"
} }
}, },
"libiconv-win": { "libiconv-win": {
@@ -558,6 +607,15 @@
"path": "source/COPYING" "path": "source/COPYING"
} }
}, },
"libidn2": {
"type": "filelist",
"url": "https://ftp.gnu.org/gnu/libidn/",
"regex": "/href=\"(?<file>libidn2-(?<version>[^\"]+)\\.tar\\.gz)\"/",
"license": {
"type": "file",
"path": "COPYING.LESSERv3"
}
},
"libjpeg": { "libjpeg": {
"type": "ghtar", "type": "ghtar",
"repo": "libjpeg-turbo/libjpeg-turbo", "repo": "libjpeg-turbo/libjpeg-turbo",
@@ -592,6 +650,16 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"libmaxminddb": {
"type": "ghrel",
"repo": "maxmind/libmaxminddb",
"match": "libmaxminddb-.+\\.tar\\.gz",
"prefer-stable": true,
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libmemcached": { "libmemcached": {
"type": "ghtagtar", "type": "ghtagtar",
"repo": "awesomized/libmemcached", "repo": "awesomized/libmemcached",
@@ -659,10 +727,30 @@
"path": "LICENSE.md" "path": "LICENSE.md"
} }
}, },
"libunistring": {
"type": "filelist",
"url": "https://ftp.gnu.org/gnu/libunistring/",
"regex": "/href=\"(?<file>libunistring-(?<version>[^\"]+)\\.tar\\.gz)\"/",
"provide-pre-built": true,
"license": {
"type": "file",
"path": "COPYING.LIB"
}
},
"liburing": {
"type": "ghtar",
"repo": "axboe/liburing",
"prefer-stable": true,
"license": {
"type": "file",
"path": "COPYING"
}
},
"libuuid": { "libuuid": {
"type": "git", "type": "git",
"url": "https://github.com/static-php/libuuid.git", "url": "https://github.com/static-php/libuuid.git",
"rev": "master", "rev": "master",
"provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "COPYING"
@@ -742,7 +830,7 @@
"micro": { "micro": {
"type": "git", "type": "git",
"path": "php-src/sapi/micro", "path": "php-src/sapi/micro",
"rev": "php-85-win", "rev": "master",
"url": "https://github.com/static-php/phpmicro", "url": "https://github.com/static-php/phpmicro",
"license": { "license": {
"type": "file", "type": "file",
@@ -790,6 +878,14 @@
"path": "COPYING" "path": "COPYING"
} }
}, },
"net-snmp": {
"type": "ghtagtar",
"repo": "net-snmp/net-snmp",
"license": {
"type": "file",
"path": "COPYING"
}
},
"nghttp2": { "nghttp2": {
"type": "ghrel", "type": "ghrel",
"repo": "nghttp2/nghttp2", "repo": "nghttp2/nghttp2",
@@ -867,6 +963,15 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"pcov": {
"type": "url",
"url": "https://pecl.php.net/get/pcov",
"filename": "pcov.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"pdo_sqlsrv": { "pdo_sqlsrv": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/pdo_sqlsrv", "url": "https://pecl.php.net/get/pdo_sqlsrv",
@@ -980,7 +1085,6 @@
}, },
"snappy": { "snappy": {
"type": "git", "type": "git",
"repo": "google/snappy",
"rev": "main", "rev": "main",
"url": "https://github.com/google/snappy", "url": "https://github.com/google/snappy",
"license": { "license": {
@@ -989,9 +1093,8 @@
} }
}, },
"spx": { "spx": {
"type": "git", "type": "pie",
"rev": "master", "repo": "noisebynorthwest/php-spx",
"url": "https://github.com/static-php/php-spx.git",
"path": "php-src/ext/spx", "path": "php-src/ext/spx",
"license": { "license": {
"type": "file", "type": "file",
@@ -1145,14 +1248,5 @@
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
},
"liburing": {
"type": "ghtar",
"repo": "axboe/liburing",
"prefer-stable": true,
"license": {
"type": "file",
"path": "COPYING"
}
} }
} }

View File

@@ -744,10 +744,26 @@ h2 {
} }
select { select {
border-radius: 4px; border-radius: 8px;
border: 1px solid var(--vp-c-divider); border: 1px solid var(--vp-c-divider);
padding: 0 4px; padding: 8px 12px;
width: 300px; width: 300px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 14px;
transition: all 0.2s ease;
cursor: pointer;
outline: none;
}
select:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg);
}
select:focus {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
} }
.my-btn { .my-btn {
@@ -781,17 +797,160 @@ select {
.textarea { .textarea {
border: 1px solid var(--vp-c-divider); border: 1px solid var(--vp-c-divider);
border-radius: 4px; border-radius: 8px;
width: calc(100% - 12px); width: calc(100% - 24px);
padding: 4px 8px; padding: 12px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 14px;
font-family: var(--vp-font-family-mono);
line-height: 1.5;
transition: all 0.2s ease;
outline: none;
resize: vertical;
}
.textarea:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg);
}
.textarea:focus {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
} }
.input { .input {
display: block; display: block;
border: 1px solid var(--vp-c-divider); border: 1px solid var(--vp-c-divider);
border-radius: 4px; border-radius: 8px;
width: 100%; width: 100%;
padding: 4px 8px; padding: 10px 12px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 14px;
transition: all 0.2s ease;
outline: none;
box-sizing: border-box;
}
.input:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg);
}
.input:focus {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
}
/* Radio button styles */
input[type="radio"] {
appearance: none;
width: 18px;
height: 18px;
border: 2px solid var(--vp-c-border);
border-radius: 50%;
background-color: var(--vp-c-bg);
cursor: pointer;
position: relative;
vertical-align: middle;
margin-right: 6px;
transition: all 0.2s ease;
}
input[type="radio"]:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg-soft);
}
input[type="radio"]:checked {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-1);
}
input[type="radio"]:checked::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--vp-c-bg);
}
input[type="radio"]:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Checkbox styles */
input[type="checkbox"] {
appearance: none;
width: 18px;
height: 18px;
border: 2px solid var(--vp-c-border);
border-radius: 4px;
background-color: var(--vp-c-bg);
cursor: pointer;
position: relative;
vertical-align: middle;
margin-right: 6px;
transition: all 0.2s ease;
}
input[type="checkbox"]:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg-soft);
}
input[type="checkbox"]:checked {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-1);
}
input[type="checkbox"]:checked::after {
content: '';
position: absolute;
top: 2px;
left: 5px;
width: 4px;
height: 8px;
border: solid var(--vp-c-bg);
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
input[type="checkbox"]:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Label styles */
label {
cursor: pointer;
user-select: none;
color: var(--vp-c-text-1);
font-size: 14px;
line-height: 1.5;
transition: color 0.2s ease;
}
label:hover {
color: var(--vp-c-brand-1);
}
input[type="radio"]:disabled + label,
input[type="checkbox"]:disabled + label {
opacity: 0.5;
cursor: not-allowed;
}
input[type="radio"]:disabled + label:hover,
input[type="checkbox"]:disabled + label:hover {
color: var(--vp-c-text-1);
} }
.command-container { .command-container {

View File

@@ -0,0 +1,219 @@
<template>
<div class="contributors-container">
<div class="contributors-header">
<h2>Contributors</h2>
<p class="contributors-description">
Thanks to all the amazing people who have contributed to this project!
</p>
</div>
<div v-if="loading" class="loading-state">
<div class="spinner"></div>
<p>Loading contributors...</p>
</div>
<div v-else-if="error" class="error-state">
<p>{{ error }}</p>
</div>
<div v-else class="contributors-grid">
<a
v-for="contributor in contributors"
:key="contributor.id"
:href="contributor.html_url"
target="_blank"
rel="noopener noreferrer"
class="contributor-card"
:title="contributor.login"
>
<img
:src="contributor.avatar_url"
:alt="contributor.login"
class="contributor-avatar"
loading="lazy"
/>
<div class="contributor-name">{{ contributor.login }}</div>
</a>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
interface Contributor {
id: number;
login: string;
avatar_url: string;
html_url: string;
contributions: number;
}
const contributors = ref<Contributor[]>([]);
const loading = ref(true);
const error = ref('');
const fetchContributors = async () => {
try {
loading.value = true;
error.value = '';
const response = await fetch(
'https://api.github.com/repos/crazywhalecc/static-php-cli/contributors?per_page=24'
);
if (!response.ok) {
throw new Error('Failed to fetch contributors');
}
const data = await response.json();
contributors.value = data;
} catch (err) {
error.value = 'Failed to load contributors. Please try again later.';
console.error('Error fetching contributors:', err);
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchContributors();
});
</script>
<style scoped>
.contributors-container {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}
.contributors-header {
text-align: center;
margin-bottom: 24px;
}
.contributors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.contributors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
.loading-state,
.error-state {
text-align: center;
padding: 40px 20px;
color: var(--vp-c-text-2);
}
.spinner {
width: 40px;
height: 40px;
margin: 0 auto 16px;
border: 4px solid var(--vp-c-divider);
border-top-color: var(--vp-c-brand-1);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.contributors-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
gap: 16px;
}
.contributor-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px;
background: var(--vp-c-bg);
border-radius: 12px;
border: 1px solid var(--vp-c-divider);
transition: all 0.3s ease;
text-decoration: none;
color: var(--vp-c-text-1);
}
.contributor-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
border-color: var(--vp-c-brand-1);
}
.contributor-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
border: 2px solid var(--vp-c-divider);
transition: all 0.3s ease;
margin-bottom: 8px;
}
.contributor-card:hover .contributor-avatar {
border-color: var(--vp-c-brand-1);
transform: scale(1.05);
}
.contributor-name {
font-size: 12px;
font-weight: 500;
text-align: center;
word-break: break-word;
max-width: 100%;
}
@media (max-width: 768px) {
.contributors-container {
margin: 32px 16px;
padding: 24px 16px;
}
.contributors-header h2 {
font-size: 1.25rem;
}
.contributors-description {
font-size: 0.9rem;
}
.contributors-grid {
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
gap: 12px;
}
.contributor-card {
padding: 8px;
}
.contributor-avatar {
width: 48px;
height: 48px;
}
.contributor-name {
font-size: 11px;
}
}
</style>

View File

@@ -4,7 +4,7 @@ import sidebarZh from "./sidebar.zh";
// https://vitepress.dev/reference/site-config // https://vitepress.dev/reference/site-config
export default { export default {
title: "static-php-cli", title: "Static PHP",
description: "Build single static PHP binary, with PHP project together, with popular extensions included.", description: "Build single static PHP binary, with PHP project together, with popular extensions included.",
locales: { locales: {
en: { en: {
@@ -44,6 +44,7 @@ export default {
}, },
themeConfig: { themeConfig: {
// https://vitepress.dev/reference/default-theme-config // https://vitepress.dev/reference/default-theme-config
logo: '/images/static-php_nobg.png',
nav: [], nav: [],
socialLinks: [ socialLinks: [
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'} {icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}

View File

@@ -4,3 +4,21 @@
max-width: 1000px !important; max-width: 1000px !important;
} }
.vp-doc .contributors-header h2 {
padding-top: 0;
border-top: none;
}
.vp-doc .sponsors-header h2 {
padding-top: 0;
border-top: none;
}
.dark .VPImage.logo {
filter: contrast(0.7);
}
.dark .VPImage.image-src {
filter: contrast(0.7);
}

View File

@@ -36,6 +36,7 @@ The following is the source download configuration corresponding to the `libeven
The most important field here is `type`. Currently, the types it supports are: The most important field here is `type`. Currently, the types it supports are:
- `url`: Directly use URL to download, for example: `https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`. - `url`: Directly use URL to download, for example: `https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`.
- `pie`: Download PHP extensions from Packagist using the PIE (PHP Installer for Extensions) standard.
- `ghrel`: Use the GitHub Release API to download, download the artifacts uploaded from the latest version released by maintainers. - `ghrel`: Use the GitHub Release API to download, download the artifacts uploaded from the latest version released by maintainers.
- `ghtar`: Use the GitHub Release API to download. - `ghtar`: Use the GitHub Release API to download.
Different from `ghrel`, `ghtar` is downloaded from the `source code (tar.gz)` in the latest Release of the project. Different from `ghrel`, `ghtar` is downloaded from the `source code (tar.gz)` in the latest Release of the project.
@@ -89,6 +90,37 @@ Example (download the imagick extension and extract it to the extension storage
} }
``` ```
## Download type - pie
PIE (PHP Installer for Extensions) type sources refer to downloading PHP extensions from Packagist that follow the PIE standard.
This method automatically fetches extension information from the Packagist repository and downloads the appropriate distribution file.
The parameters included are:
- `repo`: The Packagist vendor/package name, such as `vendor/package-name`
Example (download a PHP extension from Packagist using PIE):
```json
{
"ext-example": {
"type": "pie",
"repo": "vendor/example-extension",
"path": "php-src/ext/example",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
::: tip
The PIE download type will automatically detect the extension information from Packagist metadata,
including the download URL, version, and distribution type.
The extension must be marked as `type: php-ext` or contain `php-ext` metadata in its Packagist package definition.
:::
## Download type - ghrel ## Download type - ghrel
ghrel will download files from Assets uploaded in GitHub Release. ghrel will download files from Assets uploaded in GitHub Release.

View File

@@ -44,6 +44,11 @@ So on macOS, you can **directly** use SPC to build statically compiled PHP binar
2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`. 2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`.
3. The `xdebug.so` file could be used for php that version and thread-safe are the same. 3. The `xdebug.so` file could be used for php that version and thread-safe are the same.
For the Windows platform, since officially built extensions (such as `php_yaml.dll`) force the use of the `php8.dll` dynamic library as a link, and statically built PHP does not include any dynamic libraries other than system libraries,
php.exe built by static-php cannot load officially built dynamic extensions. Since static-php-cli does not yet support building dynamic extensions, there is currently no way to load dynamic extensions with static-php.
However, Windows can normally use the `FFI` extension to load other dll files and call them.
## Can it support Oracle database extension? ## Can it support Oracle database extension?
Some extensions that rely on closed source libraries, such as `oci8`, `sourceguardian`, etc., Some extensions that rely on closed source libraries, such as `oci8`, `sourceguardian`, etc.,

View File

@@ -3,11 +3,14 @@
layout: home layout: home
hero: hero:
name: "static-php-cli" name: "Static PHP"
tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included." tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included."
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions: actions:
- theme: brand - theme: brand
text: Guide text: Get Started
link: ./guide/ link: ./guide/
features: features:
@@ -19,3 +22,121 @@ features:
details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions. details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions.
--- ---
<script setup>
import {VPSponsors} from "vitepress/theme";
import Contributors from '../.vitepress/components/Contributors.vue';
const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
];
</script>
<div class="sponsors-section">
<div class="sponsors-header">
<h2>Special Sponsors</h2>
<p class="sponsors-description">
Thank you to our amazing sponsors for supporting this project!
</p>
</div>
<VPSponsors :data="sponsors"/>
</div>
<style scoped>
.sponsors-section {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.sponsors-section:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.sponsors-header {
text-align: center;
margin-bottom: 24px;
}
.sponsors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sponsors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
@media (max-width: 768px) {
.sponsors-section {
margin: 32px 16px;
padding: 24px 16px;
}
.sponsors-header h2 {
font-size: 1.25rem;
}
.sponsors-description {
font-size: 0.9rem;
}
}
/* Hero logo styling */
:deep(.VPImage.image-src) {
border-radius: 20px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:deep(.VPImage.image-src:hover) {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Dark mode adjustments for logo */
.dark :deep(.VPImage.image-src) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0.9;
}
.dark :deep(.VPImage.image-src:hover) {
opacity: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
/* Additional styling for the logo image itself */
:deep(.VPImage.image-src img) {
max-height: 280px;
width: auto;
}
@media (max-width: 768px) {
:deep(.VPImage.image-src) {
padding: 24px;
}
:deep(.VPImage.image-src img) {
max-height: 200px;
}
}
</style>
<Contributors />

View File

@@ -3,8 +3,11 @@
layout: home layout: home
hero: hero:
name: "static-php-cli" name: "Static PHP"
tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included." tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included."
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions: actions:
- theme: brand - theme: brand
text: Get Started text: Get Started
@@ -24,13 +27,121 @@ features:
<script setup> <script setup>
import {VPSponsors} from "vitepress/theme"; import {VPSponsors} from "vitepress/theme";
import Contributors from './.vitepress/components/Contributors.vue';
const sponsors = [ const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' }, { name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' }, { name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
]; ];
</script> </script>
## Special Sponsors <div class="sponsors-section">
<div class="sponsors-header">
<h2>Special Sponsors</h2>
<p class="sponsors-description">
Thank you to our amazing sponsors for supporting this project!
</p>
</div>
<VPSponsors :data="sponsors"/>
</div>
<VPSponsors :data="sponsors"/> <style scoped>
.sponsors-section {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.sponsors-section:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.sponsors-header {
text-align: center;
margin-bottom: 24px;
}
.sponsors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sponsors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
@media (max-width: 768px) {
.sponsors-section {
margin: 32px 16px;
padding: 24px 16px;
}
.sponsors-header h2 {
font-size: 1.25rem;
}
.sponsors-description {
font-size: 0.9rem;
}
}
/* Hero logo styling */
:deep(.VPImage.image-src) {
border-radius: 20px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:deep(.VPImage.image-src:hover) {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Dark mode adjustments for logo */
.dark :deep(.VPImage.image-src) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0.9;
}
.dark :deep(.VPImage.image-src:hover) {
opacity: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
/* Additional styling for the logo image itself */
:deep(.VPImage.image-src img) {
max-height: 280px;
width: auto;
}
@media (max-width: 768px) {
:deep(.VPImage.image-src) {
padding: 24px;
}
:deep(.VPImage.image-src img) {
max-height: 200px;
}
}
</style>
<Contributors />

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 KiB

View File

@@ -30,6 +30,7 @@ static-php-cli 的下载资源模块是一个主要的功能,它包含了所
这里最主要的字段是 `type`,目前它支持的类型有: 这里最主要的字段是 `type`,目前它支持的类型有:
- `url`: 直接使用 URL 下载,例如:`https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz` - `url`: 直接使用 URL 下载,例如:`https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`
- `pie`: 使用 PIEPHP Installer for Extensions标准从 Packagist 下载 PHP 扩展。
- `ghrel`: 使用 GitHub Release API 下载,即从 GitHub 项目发布的最新版本中上传的附件下载。 - `ghrel`: 使用 GitHub Release API 下载,即从 GitHub 项目发布的最新版本中上传的附件下载。
- `ghtar`: 使用 GitHub Release API 下载,与 `ghrel` 不同的是,`ghtar` 是从项目的最新 Release 中找 `source code (tar.gz)` 下载的。 - `ghtar`: 使用 GitHub Release API 下载,与 `ghrel` 不同的是,`ghtar` 是从项目的最新 Release 中找 `source code (tar.gz)` 下载的。
- `ghtagtar`: 使用 GitHub Release API 下载,与 `ghtar` 相比,`ghtagtar` 可以从 `tags` 列表找最新的,并下载 `tar.gz` 格式的源码(因为有些项目只使用了 `tag` 发布版本)。 - `ghtagtar`: 使用 GitHub Release API 下载,与 `ghtar` 相比,`ghtagtar` 可以从 `tags` 列表找最新的,并下载 `tar.gz` 格式的源码(因为有些项目只使用了 `tag` 发布版本)。
@@ -77,6 +78,36 @@ url 类型的资源指的是从 URL 直接下载文件。
} }
``` ```
## 下载类型 - pie
PIEPHP Installer for Extensions类型的资源是从 Packagist 下载遵循 PIE 标准的 PHP 扩展。
该方法会自动从 Packagist 仓库获取扩展信息,并下载相应的分发文件。
包含的参数有:
- `repo`: Packagist 的 vendor/package 名称,如 `vendor/package-name`
例子(使用 PIE 从 Packagist 下载 PHP 扩展):
```json
{
"ext-example": {
"type": "pie",
"repo": "vendor/example-extension",
"path": "php-src/ext/example",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
::: tip
PIE 下载类型会自动从 Packagist 元数据中检测扩展信息,包括下载 URL、版本和分发类型。
扩展必须在其 Packagist 包定义中标记为 `type: php-ext` 或包含 `php-ext` 元数据。
:::
## 下载类型 - ghrel ## 下载类型 - ghrel
ghrel 会从 GitHub Release 中上传的 Assets 下载文件。首先使用 GitHub Release API 获取最新版本,然后根据正则匹配方式下载相应的文件。 ghrel 会从 GitHub Release 中上传的 Assets 下载文件。首先使用 GitHub Release API 获取最新版本,然后根据正则匹配方式下载相应的文件。

View File

@@ -41,6 +41,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" -
2. 你将获得 `buildroot/modules/xdebug.so``buildroot/bin/php` 2. 你将获得 `buildroot/modules/xdebug.so``buildroot/bin/php`
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。 3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
对于 Windows 平台,由于官方构建的扩展(如 `php_yaml.dll`)强制使用了 `php8.dll` 动态库作为链接,静态构建的 PHP 不包含任何系统库以外的动态库,
所以 Windows 下无法加载官方构建的动态扩展。 由于 static-php-cli 还暂未支持构建动态扩展,所以目前还没有让 static-php 加载动态扩展的方法。
不过Windows 可以正常使用 `FFI` 扩展加载其他的 dll 文件并调用。
## 可以支持 Oracle 数据库扩展吗? ## 可以支持 Oracle 数据库扩展吗?
部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so` 部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so`

View File

@@ -3,18 +3,140 @@
layout: home layout: home
hero: hero:
name: "static-php-cli" name: "Static PHP"
tagline: "在 Linux、macOS、FreeBSD、Windows 上与 PHP 项目一起构建独立的 PHP 二进制文件,并包含流行的扩展。" tagline: "在 Linux、macOS、FreeBSD、Windows 上与 PHP 项目一起构建独立的 PHP 二进制文件,并包含流行的扩展。"
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions: actions:
- theme: brand - theme: brand
text: 指南 text: 开始使用
link: ./guide/ link: ./guide/
features: features:
- title: 静态二进制 - title: 静态 CLI 二进制
details: 您可以轻松地编译一个独立的 PHP 二进制文件以供嵌入程序使用包括 cli、fpm、micro details: 您可以轻松地编译一个独立的 PHP 二进制文件以供通用使用包括 CLI、FPM SAPI
- title: phpmicro 自执行二进制 - title: Micro 自解压可执行文件
details: 您可以使用 micro SAPI 编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包为一个文件 details: 您可以编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包在一起
- title: 依赖管理 - title: 依赖管理
details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展和不同的依赖库 details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展。
--- ---
<script setup>
import {VPSponsors} from "vitepress/theme";
import Contributors from '../.vitepress/components/Contributors.vue';
const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
];
</script>
<div class="sponsors-section">
<div class="sponsors-header">
<h2>特别赞助商</h2>
<p class="sponsors-description">
感谢我们出色的赞助商对本项目的支持!
</p>
</div>
<VPSponsors :data="sponsors"/>
</div>
<style scoped>
.sponsors-section {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.sponsors-section:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.sponsors-header {
text-align: center;
margin-bottom: 24px;
}
.sponsors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sponsors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
@media (max-width: 768px) {
.sponsors-section {
margin: 32px 16px;
padding: 24px 16px;
}
.sponsors-header h2 {
font-size: 1.25rem;
}
.sponsors-description {
font-size: 0.9rem;
}
}
/* Hero logo styling */
:deep(.VPImage.image-src) {
border-radius: 20px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:deep(.VPImage.image-src:hover) {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Dark mode adjustments for logo */
.dark :deep(.VPImage.image-src) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0.9;
}
.dark :deep(.VPImage.image-src:hover) {
opacity: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
/* Additional styling for the logo image itself */
:deep(.VPImage.image-src img) {
max-height: 280px;
width: auto;
}
@media (max-width: 768px) {
:deep(.VPImage.image-src) {
padding: 24px;
}
:deep(.VPImage.image-src img) {
max-height: 200px;
}
}
</style>
<Contributors />

View File

@@ -17,3 +17,4 @@ parameters:
- ./src/globals/ext-tests/swoole.php - ./src/globals/ext-tests/swoole.php
- ./src/globals/ext-tests/swoole.phpt - ./src/globals/ext-tests/swoole.phpt
- ./src/globals/test-extensions.php - ./src/globals/test-extensions.php
- ./src/SPC/

32
spc.registry.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "internal",
"autoload": "vendor/autoload.php",
"doctor": {
"psr-4": {
"StaticPHP\\Doctor\\Item": "src/StaticPHP/Doctor/Item"
}
},
"package": {
"psr-4": {
"Package": "src/Package"
},
"config": [
"config/pkg.ext.json",
"config/pkg.lib.json",
"config/pkg.target.json"
]
},
"artifact": {
"config": [
"config/artifact.json"
],
"psr-4": {
"Package\\Artifact": "src/Package/Artifact"
}
},
"command": {
"psr-4": {
"Package\\Command": "src/Package/Command"
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace Package\Artifact;
use StaticPHP\Artifact\ArtifactDownloader;
use StaticPHP\Artifact\Downloader\DownloadResult;
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
use StaticPHP\Attribute\Artifact\CustomBinary;
use StaticPHP\Exception\DownloaderException;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\GlobalEnvManager;
use StaticPHP\Util\System\LinuxUtil;
class go_xcaddy
{
#[CustomBinary('go-xcaddy', [
'linux-x86_64',
'linux-aarch64',
'macos-x86_64',
'macos-aarch64',
])]
public function downBinary(ArtifactDownloader $downloader): DownloadResult
{
$pkgroot = PKG_ROOT_PATH;
$name = SystemTarget::getCurrentPlatformString();
$arch = match (explode('-', $name)[1]) {
'x86_64' => 'amd64',
'aarch64' => 'arm64',
default => throw new DownloaderException('Unsupported architecture: ' . $name),
};
$os = match (explode('-', $name)[0]) {
'linux' => 'linux',
'macos' => 'darwin',
default => throw new DownloaderException('Unsupported OS: ' . $name),
};
// get version and hash
[$version] = explode("\n", default_shell()->executeCurl('https://go.dev/VERSION?m=text') ?: '');
if ($version === '') {
throw new DownloaderException('Failed to get latest Go version from https://go.dev/VERSION?m=text');
}
$page = default_shell()->executeCurl('https://go.dev/dl/');
if ($page === '' || $page === false) {
throw new DownloaderException('Failed to get Go download page from https://go.dev/dl/');
}
$version_regex = str_replace('.', '\.', $version);
$pattern = "/href=\"\\/dl\\/{$version_regex}\\.{$os}-{$arch}\\.tar\\.gz\">.*?<tt>([a-f0-9]{64})<\\/tt>/s";
if (preg_match($pattern, $page, $matches)) {
$hash = $matches[1];
} else {
throw new DownloaderException("Failed to find download hash for Go {$version} {$os}-{$arch}");
}
$url = "https://go.dev/dl/{$version}.{$os}-{$arch}.tar.gz";
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . "{$version}.{$os}-{$arch}.tar.gz";
default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry());
// verify hash
$file_hash = hash_file('sha256', $path);
if ($file_hash !== $hash) {
throw new DownloaderException("Hash mismatch for downloaded go-xcaddy binary. Expected {$hash}, got {$file_hash}");
}
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: "{$pkgroot}/go-xcaddy", verified: true, version: $version);
}
#[AfterBinaryExtract('go-xcaddy', [
'linux-x86_64',
'linux-aarch64',
'macos-x86_64',
'macos-aarch64',
])]
public function afterExtract(string $target_path): void
{
if (file_exists("{$target_path}/bin/go") && file_exists("{$target_path}/bin/xcaddy")) {
return;
}
$sanitizedPath = getenv('PATH');
if (PHP_OS_FAMILY === 'Linux' && !LinuxUtil::isMuslDist()) {
$sanitizedPath = preg_replace('#(:?/?[^:]*musl[^:]*)#', '', $sanitizedPath);
$sanitizedPath = preg_replace('#^:|:$|::#', ':', $sanitizedPath); // clean up colons
}
shell()->appendEnv([
'PATH' => "{$target_path}/bin:{$sanitizedPath}",
'GOROOT' => "{$target_path}",
'GOBIN' => "{$target_path}/bin",
'GOPATH' => "{$target_path}/go",
])->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
GlobalEnvManager::addPathIfNotExists("{$target_path}/bin");
}
}

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace Package\Artifact;
use StaticPHP\Artifact\ArtifactDownloader;
use StaticPHP\Artifact\Downloader\DownloadResult;
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
use StaticPHP\Attribute\Artifact\CustomBinary;
use StaticPHP\Exception\DownloaderException;
use StaticPHP\Runtime\SystemTarget;
class zig
{
#[CustomBinary('zig', [
'linux-x86_64',
'linux-aarch64',
'macos-x86_64',
'macos-aarch64',
])]
public function downBinary(ArtifactDownloader $downloader): DownloadResult
{
$index_json = default_shell()->executeCurl('https://ziglang.org/download/index.json', retries: $downloader->getRetry());
$index_json = json_decode($index_json ?: '', true);
$latest_version = null;
foreach ($index_json as $version => $data) {
$latest_version = $version;
break;
}
if (!$latest_version) {
throw new DownloaderException('Could not determine latest Zig version');
}
$zig_arch = SystemTarget::getTargetArch();
$zig_os = match (SystemTarget::getTargetOS()) {
'Windows' => 'win',
'Darwin' => 'macos',
'Linux' => 'linux',
default => throw new DownloaderException('Unsupported OS for Zig: ' . SystemTarget::getTargetOS()),
};
$platform_key = "{$zig_arch}-{$zig_os}";
if (!isset($index_json[$latest_version][$platform_key])) {
throw new DownloaderException("No download available for {$platform_key} in Zig version {$latest_version}");
}
$download_info = $index_json[$latest_version][$platform_key];
$url = $download_info['tarball'];
$sha256 = $download_info['shasum'];
$filename = basename($url);
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename;
default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry());
// verify hash
$file_hash = hash_file('sha256', $path);
if ($file_hash !== $sha256) {
throw new DownloaderException("Hash mismatch for downloaded Zig binary. Expected {$sha256}, got {$file_hash}");
}
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $latest_version], extract: PKG_ROOT_PATH . '/zig', verified: true, version: $latest_version);
}
#[AfterBinaryExtract('zig', [
'linux-x86_64',
'linux-aarch64',
'macos-x86_64',
'macos-aarch64',
])]
public function postExtractZig(string $target_path): void
{
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
$all_exist = true;
foreach ($files as $file) {
if (!file_exists("{$target_path}/{$file}")) {
$all_exist = false;
break;
}
}
if ($all_exist) {
return;
}
$script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh';
$script_content = file_get_contents($script_path);
file_put_contents("{$target_path}/zig-cc", $script_content);
chmod("{$target_path}/zig-cc", 0755);
$script_content = str_replace('zig cc', 'zig c++', $script_content);
file_put_contents("{$target_path}/zig-c++", $script_content);
file_put_contents("{$target_path}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@");
file_put_contents("{$target_path}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@");
file_put_contents("{$target_path}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@");
file_put_contents("{$target_path}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@");
chmod("{$target_path}/zig-c++", 0755);
chmod("{$target_path}/zig-ar", 0755);
chmod("{$target_path}/zig-ld.lld", 0755);
chmod("{$target_path}/zig-ranlib", 0755);
chmod("{$target_path}/zig-objcopy", 0755);
}
}

View File

@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace Package\Command;
use StaticPHP\Artifact\ArtifactCache;
use StaticPHP\Artifact\ArtifactDownloader;
use StaticPHP\Artifact\DownloaderOptions;
use StaticPHP\Command\BaseCommand;
use StaticPHP\DI\ApplicationContext;
use StaticPHP\Registry\PackageLoader;
use StaticPHP\Util\FileSystem;
use StaticPHP\Util\InteractiveTerm;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
#[AsCommand('switch-php-version', description: 'Switch downloaded PHP version')]
class SwitchPhpVersionCommand extends BaseCommand
{
protected bool $no_motd = true;
public function configure(): void
{
$this->addArgument(
'php-version',
InputArgument::REQUIRED,
'PHP version (e.g., 8.4, 8.3, 8.2, 8.1, 8.0, 7.4, or specific like 8.4.5)',
);
// Downloader options
$this->getDefinition()->addOptions(DownloaderOptions::getConsoleOptions());
// Additional options
$this->addOption('keep-source', null, null, 'Keep extracted source directory (do not remove source/php-src)');
}
public function handle(): int
{
$php_ver = $this->getArgument('php-version');
// Validate version format
if (!$this->isValidPhpVersion($php_ver)) {
$this->output->writeln("<error>Invalid PHP version '{$php_ver}'!</error>");
$this->output->writeln('<comment>Supported formats: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, or specific version like 8.4.5</comment>');
return static::FAILURE;
}
$cache = ApplicationContext::get(ArtifactCache::class);
// Check if php-src is already locked
$source_info = $cache->getSourceInfo('php-src');
if ($source_info !== null) {
$current_version = $source_info['version'] ?? 'unknown';
$this->output->writeln("<info>Current PHP version: {$current_version}, removing old PHP source cache...");
// Remove cache entry and optionally the downloaded file
$cache->removeSource('php-src', delete_file: true);
}
// Remove extracted source directory if exists and --keep-source not set
$source_dir = SOURCE_PATH . '/php-src';
if (!$this->getOption('keep-source') && is_dir($source_dir)) {
$this->output->writeln('<info>Removing extracted PHP source directory...</info>');
InteractiveTerm::indicateProgress('Removing: ' . $source_dir);
FileSystem::removeDir($source_dir);
InteractiveTerm::finish('Removed: ' . $source_dir);
}
// Download new PHP source
$this->output->writeln("<info>Downloading PHP {$php_ver} source...</info>");
$this->input->setOption('with-php', $php_ver);
$downloaderOptions = DownloaderOptions::extractFromConsoleOptions($this->input->getOptions());
$downloader = new ArtifactDownloader($downloaderOptions);
// Get php-src artifact from php package
$php_package = PackageLoader::getPackage('php');
$artifact = $php_package->getArtifact();
if ($artifact === null) {
$this->output->writeln('<error>Failed to get php-src artifact!</error>');
return static::FAILURE;
}
$downloader->add($artifact);
$downloader->download();
// Get the new version info
$new_source_info = $cache->getSourceInfo('php-src');
$new_version = $new_source_info['version'] ?? $php_ver;
$this->output->writeln('');
$this->output->writeln("<info>Successfully switched to PHP {$new_version}!</info>");
return static::SUCCESS;
}
/**
* Validate PHP version format.
*
* Accepts:
* - Major.Minor format: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4
* - Full version format: 8.4.5, 8.3.12, etc.
*/
private function isValidPhpVersion(string $version): bool
{
// Check major.minor format (e.g., 8.4)
if (in_array($version, ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'], true)) {
return true;
}
// Check full version format (e.g., 8.4.5)
if (preg_match('/^\d+\.\d+\.\d+$/', $version)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Package\Extension;
use Package\Target\php;
use StaticPHP\Attribute\Package\AfterStage;
use StaticPHP\Attribute\Package\BeforeStage;
use StaticPHP\Attribute\Package\Extension;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Package\PackageInstaller;
use StaticPHP\Toolchain\Interface\ToolchainInterface;
use StaticPHP\Util\SourcePatcher;
#[Extension('readline')]
class readline
{
#[BeforeStage('php', [php::class, 'makeCliForUnix'], 'ext-readline')]
#[PatchDescription('Fix readline static build with musl')]
public function beforeMakeLinuxCli(PackageInstaller $installer, ToolchainInterface $toolchain): void
{
if ($toolchain->isStatic()) {
$php_src = $installer->getBuildPackage('php')->getSourceDir();
SourcePatcher::patchFile('musl_static_readline.patch', $php_src);
}
}
#[AfterStage('php', [php::class, 'makeCliForUnix'], 'ext-readline')]
public function afterMakeLinuxCli(PackageInstaller $installer, ToolchainInterface $toolchain): void
{
if ($toolchain->isStatic()) {
$php_src = $installer->getBuildPackage('php')->getSourceDir();
SourcePatcher::patchFile('musl_static_readline.patch', $php_src, true);
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Package\Library;
use Package\Target\php;
use StaticPHP\Attribute\Package\AfterStage;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\FileSystem;
#[Library('imap')]
class imap
{
#[AfterStage('php', [php::class, 'patchEmbedScripts'], 'imap')]
#[PatchDescription('Fix missing -lcrypt in php-config libs on glibc systems')]
public function afterPatchScripts(): void
{
if (SystemTarget::getLibc() === 'glibc') {
FileSystem::replaceFileRegex(BUILD_BIN_PATH . '/php-config', '/^libs="(.*)"$/m', 'libs="$1 -lcrypt"');
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Package\Library;
use StaticPHP\Attribute\Package\BeforeStage;
use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
use StaticPHP\Util\FileSystem;
#[Library('libedit')]
class libedit extends LibraryPackage
{
#[BeforeStage(stage: 'build')]
public function patchBeforeBuild(): void
{
FileSystem::replaceFileRegex(
"{$this->getSourceDir()}/src/sys.h",
'|//#define\s+strl|',
'#define strl'
);
}
#[BuildFor('Darwin')]
#[BuildFor('Linux')]
public function build(): void
{
UnixAutoconfExecutor::create($this)
->appendEnv(['CFLAGS' => '-D__STDC_ISO_10646__=201103L'])
->configure()
->make();
$this->patchPkgconfPrefix(['libedit.pc']);
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Package\Library;
use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
#[Library('libiconv')]
class libiconv
{
#[BuildFor('Darwin')]
public function build(LibraryPackage $package): void
{
UnixAutoconfExecutor::create($package)
->configure(
'--enable-extra-encodings',
'--enable-year2038',
)
->make('install-lib', with_install: false)
->make('install-lib', with_install: false, dir: "{$package->getSourceDir()}/libcharset");
$package->patchLaDependencyPrefix();
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Package\Library;
use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixCMakeExecutor;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\FileSystem;
#[Library('libxml2')]
class libxml2
{
#[BuildFor('Darwin')]
public function build(LibraryPackage $package): void
{
$cmake = UnixCMakeExecutor::create($package)
->optionalPackage(
'zlib',
'-DLIBXML2_WITH_ZLIB=ON ' .
"-DZLIB_LIBRARY={$package->getLibDir()}/libz.a " .
"-DZLIB_INCLUDE_DIR={$package->getIncludeDir()}",
'-DLIBXML2_WITH_ZLIB=OFF',
)
->optionalPackage('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA'))
->addConfigureArgs(
'-DLIBXML2_WITH_ICONV=ON',
'-DLIBXML2_WITH_ICU=OFF', // optional, but discouraged: https://gitlab.gnome.org/GNOME/libxml2/-/blob/master/README.md
'-DLIBXML2_WITH_PYTHON=OFF',
'-DLIBXML2_WITH_PROGRAMS=OFF',
'-DLIBXML2_WITH_TESTS=OFF',
);
if (SystemTarget::getTargetOS() === 'Linux') {
$cmake->addConfigureArgs('-DIconv_IS_BUILT_IN=OFF');
}
$cmake->build();
FileSystem::replaceFileStr(
BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc',
'-lxml2 -liconv',
'-lxml2'
);
FileSystem::replaceFileStr(
BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc',
'-lxml2',
'-lxml2 -liconv'
);
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Package\Library;
use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
use StaticPHP\Toolchain\Interface\ToolchainInterface;
use StaticPHP\Util\DirDiff;
use StaticPHP\Util\FileSystem;
#[Library('ncurses')]
class ncurses
{
#[BuildFor('Darwin')]
#[BuildFor('Linux')]
public function build(LibraryPackage $package, ToolchainInterface $toolchain): void
{
$dirdiff = new DirDiff(BUILD_BIN_PATH);
UnixAutoconfExecutor::create($package)
->appendEnv([
'LDFLAGS' => $toolchain->isStatic() ? '-static' : '',
])
->configure(
'--enable-overwrite',
'--with-curses-h',
'--enable-pc-files',
'--enable-echo',
'--disable-widec',
'--with-normal',
'--with-ticlib',
'--without-tests',
'--without-dlsym',
'--without-debug',
'--enable-symlinks',
"--bindir={$package->getBinDir()}",
"--includedir={$package->getIncludeDir()}",
"--libdir={$package->getLibDir()}",
"--prefix={$package->getBuildRootPath()}",
)
->make();
$new_files = $dirdiff->getIncrementFiles(true);
foreach ($new_files as $file) {
@unlink(BUILD_BIN_PATH . '/' . $file);
}
shell()->cd(BUILD_ROOT_PATH)->exec('rm -rf share/terminfo');
shell()->cd(BUILD_ROOT_PATH)->exec('rm -rf lib/terminfo');
$pkgconf_list = ['form.pc', 'menu.pc', 'ncurses++.pc', 'ncurses.pc', 'panel.pc', 'tic.pc'];
$package->patchPkgconfPrefix($pkgconf_list);
foreach ($pkgconf_list as $pkgconf) {
FileSystem::replaceFileStr("{$package->getLibDir()}/pkgconfig/{$pkgconf}", "-L{$package->getLibDir()}", '-L${libdir}');
}
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Package\Library;
use Package\Target\php;
use StaticPHP\Attribute\Package\BeforeStage;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Package\TargetPackage;
#[Library('postgresql')]
class postgresql
{
#[BeforeStage('php', [php::class, 'configureForUnix'], 'postgresql')]
#[PatchDescription('Patch to avoid explicit_bzero detection issues on some systems')]
public function patchBeforePHPConfigure(TargetPackage $package): void
{
shell()->cd($package->getSourceDir())
->exec('sed -i.backup "s/ac_cv_func_explicit_bzero\" = xyes/ac_cv_func_explicit_bzero\" = x_fake_yes/" ./configure');
}
}

3
src/Package/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Package Implementation
This directory contains the implementation of the `Package` module, which provides functionality for managing and manipulating packages within the system.

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Package\Target;
use StaticPHP\Attribute\Package\InitPackage;
use StaticPHP\Attribute\Package\Target;
use StaticPHP\Util\GlobalEnvManager;
#[Target('go-xcaddy')]
class go_xcaddy
{
#[InitPackage]
public function init(): void
{
if (is_dir(PKG_ROOT_PATH . '/go-xcaddy/bin')) {
GlobalEnvManager::addPathIfNotExists(PKG_ROOT_PATH . '/go-xcaddy/bin');
GlobalEnvManager::putenv('GOROOT=' . PKG_ROOT_PATH . '/go-xcaddy');
GlobalEnvManager::putenv('GOBIN=' . PKG_ROOT_PATH . '/go-xcaddy/bin');
GlobalEnvManager::putenv('GOPATH=' . PKG_ROOT_PATH . '/go-xcaddy/go');
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Package\Target;
use StaticPHP\Attribute\Package\BeforeStage;
use StaticPHP\Attribute\Package\Target;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Package\TargetPackage;
use StaticPHP\Util\FileSystem;
#[Target('php-micro')]
class micro
{
#[BeforeStage('php', [php::class, 'makeEmbedForUnix'], 'php-micro')]
#[PatchDescription('Patch Makefile to build only libphp.la for embedding')]
public function patchBeforeEmbed(TargetPackage $package): void
{
FileSystem::replaceFileStr("{$package->getSourceDir()}/Makefile", 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
}
}

684
src/Package/Target/php.php Normal file
View File

@@ -0,0 +1,684 @@
<?php
declare(strict_types=1);
namespace Package\Target;
use StaticPHP\Attribute\Package\BeforeStage;
use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Info;
use StaticPHP\Attribute\Package\InitPackage;
use StaticPHP\Attribute\Package\ResolveBuild;
use StaticPHP\Attribute\Package\Stage;
use StaticPHP\Attribute\Package\Target;
use StaticPHP\Attribute\Package\Validate;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Config\PackageConfig;
use StaticPHP\DI\ApplicationContext;
use StaticPHP\Exception\SPCException;
use StaticPHP\Exception\WrongUsageException;
use StaticPHP\Package\Package;
use StaticPHP\Package\PackageBuilder;
use StaticPHP\Package\PackageInstaller;
use StaticPHP\Package\PhpExtensionPackage;
use StaticPHP\Package\TargetPackage;
use StaticPHP\Registry\ArtifactLoader;
use StaticPHP\Registry\PackageLoader;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Toolchain\Interface\ToolchainInterface;
use StaticPHP\Toolchain\ToolchainManager;
use StaticPHP\Util\DirDiff;
use StaticPHP\Util\FileSystem;
use StaticPHP\Util\InteractiveTerm;
use StaticPHP\Util\SourcePatcher;
use StaticPHP\Util\SPCConfigUtil;
use StaticPHP\Util\System\UnixUtil;
use StaticPHP\Util\V2CompatLayer;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use ZM\Logger\ConsoleColor;
#[Target('php')]
#[Target('php-cli')]
#[Target('php-fpm')]
#[Target('php-micro')]
#[Target('php-cgi')]
#[Target('php-embed')]
#[Target('frankenphp')]
class php extends TargetPackage
{
public static function getPHPVersionID(): int
{
$artifact = ArtifactLoader::getArtifactInstance('php-src');
if (!file_exists("{$artifact->getSourceDir()}/main/php_version.h")) {
throw new WrongUsageException('PHP source files are not available, you need to download them first');
}
$file = file_get_contents("{$artifact->getSourceDir()}/main/php_version.h");
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0) {
return intval($match[1]);
}
throw new WrongUsageException('PHP version file format is malformed, please remove "./source/php-src" dir and download/extract again');
}
#[InitPackage]
public function init(TargetPackage $package): void
{
// universal build options (may move to base class later)
$package->addBuildOption('with-added-patch', 'P', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Inject patch script outside');
// basic build argument and options for PHP
$package->addBuildArgument('extensions', InputArgument::REQUIRED, 'Comma-separated list of static extensions to build');
$package->addBuildOption('no-strip', null, null, 'build without strip, keep symbols to debug');
$package->addBuildOption('with-upx-pack', null, null, 'Compress / pack binary using UPX tool (linux/windows only)');
// php configure and extra patch options
$package->addBuildOption('disable-opcache-jit', null, null, 'Disable opcache jit');
$package->addBuildOption('with-config-file-path', null, InputOption::VALUE_REQUIRED, 'Set the path in which to look for php.ini', PHP_OS_FAMILY === 'Windows' ? null : '/usr/local/etc/php');
$package->addBuildOption('with-config-file-scan-dir', null, InputOption::VALUE_REQUIRED, 'Set the directory to scan for .ini files after reading php.ini', PHP_OS_FAMILY === 'Windows' ? null : '/usr/local/etc/php/conf.d');
$package->addBuildOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
$package->addBuildOption('enable-zts', null, null, 'Enable thread safe support');
// phpmicro build options
if ($package->getName() === 'php' || $package->getName() === 'php-micro') {
$package->addBuildOption('with-micro-fake-cli', null, null, 'Let phpmicro\'s PHP_SAPI use "cli" instead of "micro"');
$package->addBuildOption('without-micro-ext-test', null, null, 'Disable phpmicro with extension test code');
$package->addBuildOption('with-micro-logo', null, InputOption::VALUE_REQUIRED, 'Use custom .ico for micro.sfx (windows only)');
$package->addBuildOption('enable-micro-win32', null, null, 'Enable win32 mode for phpmicro (Windows only)');
}
// frankenphp build options
if ($package->getName() === 'php' || $package->getName() === 'frankenphp') {
$package->addBuildOption('with-frankenphp-app', null, InputOption::VALUE_REQUIRED, 'Path to a folder to be embedded in FrankenPHP');
}
// embed build options
if ($package->getName() === 'php' || $package->getName() === 'php-embed') {
$package->addBuildOption('build-shared', 'D', InputOption::VALUE_REQUIRED, 'Shared extensions to build, comma separated', '');
}
// legacy php target build options
V2CompatLayer::addLegacyBuildOptionsForPhp($package);
if ($package->getName() === 'php') {
$package->addBuildOption('build-micro', null, null, 'Build micro SAPI');
$package->addBuildOption('build-cli', null, null, 'Build cli SAPI');
$package->addBuildOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
$package->addBuildOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
$package->addBuildOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
$package->addBuildOption('build-cgi', null, null, 'Build cgi SAPI');
$package->addBuildOption('build-all', null, null, 'Build all SAPI');
}
}
#[ResolveBuild]
public function resolveBuild(TargetPackage $package, PackageInstaller $installer): array
{
// Parse extensions and additional packages for all php-* targets
$static_extensions = parse_extension_list($package->getBuildArgument('extensions'));
$additional_libraries = parse_comma_list($package->getBuildOption('with-libs'));
$additional_packages = parse_comma_list($package->getBuildOption('with-packages'));
$additional_packages = array_merge($additional_libraries, $additional_packages);
$shared_extensions = parse_extension_list($package->getBuildOption('build-shared') ?? []);
$extensions_pkg = array_map(
fn ($x) => "ext-{$x}",
array_values(array_unique([...$static_extensions, ...$shared_extensions]))
);
// get instances
foreach ($extensions_pkg as $extension) {
$extname = substr($extension, 4);
$config = PackageConfig::get($extension, 'php-extension', []);
if (!PackageLoader::hasPackage($extension)) {
throw new WrongUsageException("Extension [{$extname}] does not exist. Please check your extension name.");
}
$instance = PackageLoader::getPackage($extension);
if (!$instance instanceof PhpExtensionPackage) {
throw new WrongUsageException("Package [{$extension}] is not a PHP extension package");
}
// set build static/shared
if (in_array($extname, $static_extensions)) {
if (($config['build-static'] ?? true) === false) {
throw new WrongUsageException("Extension [{$extname}] cannot be built as static extension.");
}
$instance->setBuildStatic();
}
if (in_array($extname, $shared_extensions)) {
if (($config['build-shared'] ?? true) === false) {
throw new WrongUsageException("Extension [{$extname}] cannot be built as shared extension, please remove it from --build-shared option.");
}
$instance->setBuildShared();
$instance->setBuildWithPhp($config['build-with-php'] ?? false);
}
}
// building shared extensions need embed SAPI
if (!empty($shared_extensions) && !$package->getBuildOption('build-embed', false) && $package->getName() === 'php') {
$installer->addBuildPackage('php-embed');
}
return [...$extensions_pkg, ...$additional_packages];
}
#[Validate]
public function validate(Package $package): void
{
// frankenphp
if ($package->getName() === 'frankenphp' && $package instanceof TargetPackage) {
if (!$package->getBuildOption('enable-zts')) {
throw new WrongUsageException('FrankenPHP SAPI requires ZTS enabled PHP, build with `--enable-zts`!');
}
// frankenphp doesn't support windows, BSD is currently not supported by static-php-cli
if (!in_array(PHP_OS_FAMILY, ['Linux', 'Darwin'])) {
throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!');
}
}
// linux does not support loading shared libraries when target is pure static
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
if (SystemTarget::getTargetOS() === 'Linux' && ApplicationContext::get(ToolchainInterface::class)->isStatic() && $embed_type === 'shared') {
throw new WrongUsageException(
'Linux does not support loading shared libraries when linking libc statically. ' .
'Change SPC_CMD_VAR_PHP_EMBED_TYPE to static.'
);
}
}
#[Info]
public function info(Package $package, PackageInstaller $installer): array
{
/** @var TargetPackage $package */
if ($package->getName() !== 'php') {
return [];
}
$sapis = array_filter([
$installer->isPackageResolved('php-cli') ? 'cli' : null,
$installer->isPackageResolved('php-fpm') ? 'fpm' : null,
$installer->isPackageResolved('php-micro') ? 'micro' : null,
$installer->isPackageResolved('php-cgi') ? 'cgi' : null,
$installer->isPackageResolved('php-embed') ? 'embed' : null,
$installer->isPackageResolved('frankenphp') ? 'frankenphp' : null,
]);
$static_extensions = array_filter($installer->getResolvedPackages(), fn ($x) => $x instanceof PhpExtensionPackage && $x->isBuildStatic());
$shared_extensions = parse_extension_list($package->getBuildOption('build-shared') ?? []);
$install_packages = array_filter($installer->getResolvedPackages(), fn ($x) => $x->getType() !== 'php-extension' && $x->getName() !== 'php' && !str_starts_with($x->getName(), 'php-'));
return [
'Build OS' => SystemTarget::getTargetOS() . ' (' . SystemTarget::getTargetArch() . ')',
'Build Target' => getenv('SPC_TARGET') ?: '',
'Build Toolchain' => ToolchainManager::getToolchainClass(),
'Build SAPI' => implode(', ', $sapis),
'Static Extensions (' . count($static_extensions) . ')' => implode(',', array_map(fn ($x) => substr($x->getName(), 4), $static_extensions)),
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
'Install Packages (' . count($install_packages) . ')' => implode(',', array_map(fn ($x) => $x->getName(), $install_packages)),
];
}
#[BeforeStage('php', 'build')]
public function beforeBuild(PackageBuilder $builder, Package $package): void
{
// Process -I option
$custom_ini = [];
foreach ($builder->getOption('with-hardcoded-ini', []) as $value) {
[$source_name, $ini_value] = explode('=', $value, 2);
$custom_ini[$source_name] = $ini_value;
logger()->info("Adding hardcoded INI [{$source_name} = {$ini_value}]");
}
if (!empty($custom_ini)) {
SourcePatcher::patchHardcodedINI($package->getSourceDir(), $custom_ini);
}
// Patch StaticPHP version
// detect patch (remove this when 8.3 deprecated)
$file = FileSystem::readFile("{$package->getSourceDir()}/main/main.c");
if (!str_contains($file, 'static-php-cli.version')) {
$version = SPC_VERSION;
logger()->debug('Inserting static-php-cli.version to php-src');
$file = str_replace('PHP_INI_BEGIN()', "PHP_INI_BEGIN()\n\tPHP_INI_ENTRY(\"static-php-cli.version\",\t\"{$version}\",\tPHP_INI_ALL,\tNULL)", $file);
FileSystem::writeFile("{$package->getSourceDir()}/main/main.c", $file);
}
// clean old modules that may conflict with the new php build
FileSystem::removeDir(BUILD_MODULES_PATH);
}
#[BeforeStage('php', [self::class, 'buildconfForUnix'], 'php')]
#[PatchDescription('Patch configure.ac for musl and musl-toolchain')]
#[PatchDescription('Let php m4 tools use static pkg-config')]
public function patchBeforeBuildconf(TargetPackage $package): void
{
// patch configure.ac for musl and musl-toolchain
$musl = SystemTarget::getTargetOS() === 'Linux' && SystemTarget::getLibc() === 'musl';
FileSystem::backupFile(SOURCE_PATH . '/php-src/configure.ac');
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/configure.ac',
'if command -v ldd >/dev/null && ldd --version 2>&1 | grep ^musl >/dev/null 2>&1',
'if ' . ($musl ? 'true' : 'false')
);
// let php m4 tools use static pkg-config
FileSystem::replaceFileStr("{$package->getSourceDir()}/build/php.m4", 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
}
#[Stage]
public function buildconfForUnix(TargetPackage $package): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./buildconf'));
V2CompatLayer::emitPatchPoint('before-php-buildconf');
shell()->cd($package->getSourceDir())->exec(getenv('SPC_CMD_PREFIX_PHP_BUILDCONF'));
}
#[Stage]
public function configureForUnix(TargetPackage $package, PackageInstaller $installer): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./configure'));
V2CompatLayer::emitPatchPoint('before-php-configure');
$cmd = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$args = [];
$version_id = self::getPHPVersionID();
// PHP JSON extension is built-in since PHP 8.0
if ($version_id < 80000) {
$args[] = '--enable-json';
}
// zts
if ($package->getBuildOption('enable-zts', false)) {
$args[] = '--enable-zts --disable-zend-signals';
if ($version_id >= 80100 && SystemTarget::getTargetOS() === 'Linux') {
$args[] = '--enable-zend-max-execution-timers';
}
}
// config-file-path and config-file-scan-dir
if ($option = $package->getBuildOption('with-config-file-path', false)) {
$args[] = "--with-config-file-path={$option}";
}
if ($option = $package->getBuildOption('with-config-file-scan-dir', false)) {
$args[] = "--with-config-file-scan-dir={$option}";
}
// perform enable cli options
$args[] = $installer->isPackageResolved('php-cli') ? '--enable-cli' : '--disable-cli';
$args[] = $installer->isPackageResolved('php-fpm') ? '--enable-fpm' : '--disable-fpm';
$args[] = $installer->isPackageResolved('php-micro') ? match (SystemTarget::getTargetOS()) {
'Linux' => '--enable-micro=all-static',
default => '--enable-micro',
} : null;
$args[] = $installer->isPackageResolved('php-cgi') ? '--enable-cgi' : '--disable-cgi';
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
$args[] = $installer->isPackageResolved('php-embed') ? "--enable-embed={$embed_type}" : '--disable-embed';
$args[] = getenv('SPC_EXTRA_PHP_VARS') ?: null;
$args = implode(' ', array_filter($args));
$static_extension_str = $this->makeStaticExtensionString($installer);
// run ./configure with args
$this->seekPhpSrcLogFileOnException(fn () => shell()->cd($package->getSourceDir())->setEnv([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'CPPFLAGS' => "-I{$package->getIncludeDir()}",
'LDFLAGS' => "-L{$package->getLibDir()} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
])->exec("{$cmd} {$args} {$static_extension_str}"), $package->getSourceDir());
}
#[Stage]
public function makeForUnix(TargetPackage $package, PackageInstaller $installer): void
{
V2CompatLayer::emitPatchPoint('before-php-make');
logger()->info('cleaning up php-src build files');
shell()->cd($package->getSourceDir())->exec('make clean');
if ($installer->isPackageResolved('php-cli')) {
$package->runStage([self::class, 'makeCliForUnix']);
}
if ($installer->isPackageResolved('php-cgi')) {
$package->runStage([self::class, 'makeCgiForUnix']);
}
if ($installer->isPackageResolved('php-fpm')) {
$package->runStage([self::class, 'makeFpmForUnix']);
}
if ($installer->isPackageResolved('php-micro')) {
$package->runStage([self::class, 'makeMicroForUnix']);
}
if ($installer->isPackageResolved('php-embed')) {
$package->runStage([self::class, 'makeEmbedForUnix']);
}
}
#[Stage]
public function makeCliForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make cli'));
$concurrency = $builder->concurrency;
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("make -j{$concurrency} cli");
$builder->deployBinary("{$package->getSourceDir()}/sapi/cli/php", BUILD_BIN_PATH . '/php');
$package->setOutput('Binary path for cli SAPI', BUILD_BIN_PATH . '/php');
}
#[Stage]
public function makeCgiForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make cgi'));
$concurrency = $builder->concurrency;
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("make -j{$concurrency} cgi");
$builder->deployBinary("{$package->getSourceDir()}/sapi/cgi/php-cgi", BUILD_BIN_PATH . '/php-cgi');
$package->setOutput('Binary path for cgi SAPI', BUILD_BIN_PATH . '/php-cgi');
}
#[Stage]
public function makeFpmForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make fpm'));
$concurrency = $builder->concurrency;
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("make -j{$concurrency} fpm");
$builder->deployBinary("{$package->getSourceDir()}/sapi/fpm/php-fpm", BUILD_BIN_PATH . '/php-fpm');
$package->setOutput('Binary path for fpm SAPI', BUILD_BIN_PATH . '/php-fpm');
}
#[Stage]
#[PatchDescription('Patch phar extension for micro SAPI to support compressed phar')]
public function makeMicroForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
{
$phar_patched = false;
try {
if ($installer->isPackageResolved('ext-phar')) {
$phar_patched = true;
SourcePatcher::patchMicroPhar(self::getPHPVersionID());
}
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make micro'));
// apply --with-micro-fake-cli option
$vars = $this->makeVars($installer);
$vars['EXTRA_CFLAGS'] .= $package->getBuildOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
// build
shell()->cd($package->getSourceDir())
->setEnv($vars)
->exec("make -j{$builder->concurrency} micro");
$builder->deployBinary($package->getSourceDir() . '/sapi/micro/micro.sfx', BUILD_BIN_PATH . '/micro.sfx');
$package->setOutput('Binary path for micro SAPI', BUILD_BIN_PATH . '/micro.sfx');
} finally {
if ($phar_patched) {
SourcePatcher::unpatchMicroPhar();
}
}
}
#[Stage]
public function makeEmbedForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make embed'));
$shared_exts = array_filter(
$installer->getResolvedPackages(),
static fn ($x) => $x instanceof PhpExtensionPackage && $x->isBuildShared() && $x->isBuildWithPhp()
);
$install_modules = $shared_exts ? 'install-modules' : '';
// detect changes in module path
$diff = new DirDiff(BUILD_MODULES_PATH, true);
$root = BUILD_ROOT_PATH;
$sed_prefix = SystemTarget::getTargetOS() === 'Darwin' ? 'sed -i ""' : 'sed -i';
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("{$sed_prefix} \"s|^EXTENSION_DIR = .*|EXTENSION_DIR = /" . basename(BUILD_MODULES_PATH) . '|" Makefile')
->exec("make -j{$builder->concurrency} INSTALL_ROOT={$root} install-sapi {$install_modules} install-build install-headers install-programs");
// ------------- SPC_CMD_VAR_PHP_EMBED_TYPE=shared -------------
// process libphp.so for shared embed
$suffix = SystemTarget::getTargetOS() === 'Darwin' ? 'dylib' : 'so';
$libphp_so = "{$package->getLibDir()}/libphp.{$suffix}";
if (file_exists($libphp_so)) {
// rename libphp.so if -release is set
if (SystemTarget::getTargetOS() === 'Linux') {
$this->processLibphpSoFile($libphp_so, $installer);
}
// deploy
$builder->deployBinary($libphp_so, $libphp_so, false);
$package->setOutput('Library path for embed SAPI', $libphp_so);
}
// process shared extensions that built-with-php
$increment_files = $diff->getChangedFiles();
$files = [];
foreach ($increment_files as $increment_file) {
$builder->deployBinary($increment_file, $increment_file, false);
$files[] = basename($increment_file);
}
if (!empty($files)) {
$package->setOutput('Built shared extensions', implode(', ', $files));
}
// ------------- SPC_CMD_VAR_PHP_EMBED_TYPE=static -------------
// process libphp.a for static embed
if (!file_exists("{$package->getLibDir()}/libphp.a")) {
return;
}
$ar = getenv('AR') ?: 'ar';
$libphp_a = "{$package->getLibDir()}/libphp.a";
shell()->exec("{$ar} -t {$libphp_a} | grep '\\.a$' | xargs -n1 {$ar} d {$libphp_a}");
UnixUtil::exportDynamicSymbols($libphp_a);
// deploy embed php scripts
$package->runStage([$this, 'patchEmbedScripts']);
}
#[Stage]
public function unixBuildSharedExt(PackageInstaller $installer, ToolchainInterface $toolchain): void
{
// collect shared extensions
/** @var PhpExtensionPackage[] $shared_extensions */
$shared_extensions = array_filter(
$installer->getResolvedPackages(PhpExtensionPackage::class),
fn ($x) => $x->isBuildShared() && !$x->isBuildWithPhp()
);
if (!empty($shared_extensions)) {
if ($toolchain->isStatic()) {
throw new WrongUsageException(
"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" .
'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`.'
);
}
FileSystem::createDir(BUILD_MODULES_PATH);
// backup
FileSystem::backupFile(BUILD_BIN_PATH . '/php-config');
FileSystem::backupFile(BUILD_LIB_PATH . '/php/build/phpize.m4');
FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', 'extension_dir="' . BUILD_MODULES_PATH . '"');
FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', 'test "[$]$1" = "no" && $1=yes', '# test "[$]$1" = "no" && $1=yes');
}
try {
logger()->debug('Building shared extensions...');
foreach ($shared_extensions as $extension) {
InteractiveTerm::setMessage('Building shared PHP extension: ' . ConsoleColor::yellow($extension->getName()));
$extension->buildShared();
}
} finally {
// restore php-config
if (!empty($shared_extensions)) {
FileSystem::restoreBackupFile(BUILD_BIN_PATH . '/php-config');
FileSystem::restoreBackupFile(BUILD_LIB_PATH . '/php/build/phpize.m4');
}
}
}
#[BuildFor('Darwin')]
#[BuildFor('Linux')]
public function build(TargetPackage $package): void
{
// virtual target, do nothing
if ($package->getName() !== 'php') {
return;
}
$package->runStage([$this, 'buildconfForUnix']);
$package->runStage([$this, 'configureForUnix']);
$package->runStage([$this, 'makeForUnix']);
$package->runStage([$this, 'unixBuildSharedExt']);
}
/**
* Patch phpize and php-config if needed
*/
#[Stage]
public function patchEmbedScripts(): void
{
// patch phpize
if (file_exists(BUILD_BIN_PATH . '/phpize')) {
logger()->debug('Patching phpize prefix');
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'");
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#');
$this->setOutput('phpize script path for embed SAPI', BUILD_BIN_PATH . '/phpize');
}
// patch php-config
if (file_exists(BUILD_BIN_PATH . '/php-config')) {
logger()->debug('Patching php-config prefix and libs order');
$php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config');
$php_config_str = str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str);
// move mimalloc to the beginning of libs
$php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str);
// move lstdc++ to the end of libs
$php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
$this->setOutput('php-config script path for embed SAPI', BUILD_BIN_PATH . '/php-config');
}
}
/**
* Seek php-src/config.log when building PHP, add it to exception.
*/
protected function seekPhpSrcLogFileOnException(callable $callback, string $source_dir): void
{
try {
$callback();
} catch (SPCException $e) {
if (file_exists("{$source_dir}/config.log")) {
$e->addExtraLogFile('php-src config.log', 'php-src.config.log');
copy("{$source_dir}/config.log", SPC_LOGS_DIR . '/php-src.config.log');
}
throw $e;
}
}
private function makeStaticExtensionString(PackageInstaller $installer): string
{
$arg = [];
foreach ($installer->getResolvedPackages() as $package) {
/** @var PhpExtensionPackage $package */
if ($package->getType() !== 'php-extension' || !$package instanceof PhpExtensionPackage) {
continue;
}
// build-shared=true, build-static=false, build-with-php=true
if ($package->isBuildShared() && !$package->isBuildStatic() && $package->isBuildWithPhp()) {
$arg[] = $package->getPhpConfigureArg(SystemTarget::getTargetOS(), true);
} elseif ($package->isBuildStatic()) {
$arg[] = $package->getPhpConfigureArg(SystemTarget::getTargetOS(), false);
}
}
$str = implode(' ', $arg);
logger()->debug("Static extension configure args: {$str}");
return $str;
}
/**
* Make environment variables for php make.
* This will call SPCConfigUtil to generate proper LDFLAGS and LIBS for static linking.
*/
private function makeVars(PackageInstaller $installer): array
{
$config = (new SPCConfigUtil(['libs_only_deps' => true]))->config(array_map(fn ($x) => $x->getName(), $installer->getResolvedPackages()));
$static = ApplicationContext::get(ToolchainInterface::class)->isStatic() ? '-all-static' : '';
$pie = SystemTarget::getTargetOS() === 'Linux' ? '-pie' : '';
return array_filter([
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LDFLAGS_PROGRAM' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') . "{$config['ldflags']} {$static} {$pie}",
'EXTRA_LDFLAGS' => $config['ldflags'],
'EXTRA_LIBS' => $config['libs'],
]);
}
/**
* Rename libphp.so to libphp-<release>.so if -release is set in LDFLAGS.
*/
private function processLibphpSoFile(string $libphpSo, PackageInstaller $installer): 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 ($installer->getResolvedPackages(PhpExtensionPackage::class) 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)
));
}
}
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Package\Target;
use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\InitPackage;
use StaticPHP\Attribute\Package\Target;
use StaticPHP\DI\ApplicationContext;
use StaticPHP\Package\TargetPackage;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
use StaticPHP\Toolchain\Interface\ToolchainInterface;
#[Target('pkg-config')]
class pkgconfig
{
#[InitPackage]
public function resolveBuild(): void
{
ApplicationContext::set('elephant', true);
}
#[BuildFor('Linux')]
#[BuildFor('Darwin')]
public function build(TargetPackage $package, ToolchainInterface $toolchain): void
{
UnixAutoconfExecutor::create($package)
->appendEnv([
'CFLAGS' => '-Wimplicit-function-declaration -Wno-int-conversion',
'LDFLAGS' => $toolchain->isStatic() ? '--static' : '',
])
->configure(
'--with-internal-glib',
'--disable-host-tool',
'--without-sysroot',
'--without-system-include-path',
'--without-system-library-path',
'--without-pc-path',
)
->make(with_install: 'install-exec');
shell()->exec('strip ' . BUILD_ROOT_PATH . '/bin/pkg-config');
}
}

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.5'; public const string VERSION = '3.0.0-dev';
public function __construct() public function __construct()
{ {

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\builder; namespace SPC\builder;
use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\EnvironmentException; use SPC\exception\EnvironmentException;
use SPC\exception\SPCException; use SPC\exception\SPCException;
use SPC\exception\ValidationException; use SPC\exception\ValidationException;
@@ -220,7 +221,7 @@ class Extension
*/ */
public function patchBeforeSharedMake(): bool public function patchBeforeSharedMake(): bool
{ {
$config = (new SPCConfigUtil($this->builder))->config([$this->getName()], array_map(fn ($l) => $l->getName(), $this->builder->getLibs())); $config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this);
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']); [$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
$lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') ? '-l:libstdc++.a' : null; $lstdcpp = str_contains($sharedLibs, '-l:libstdc++.a') ? '-l:libstdc++.a' : null;
$lstdcpp ??= str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : ''; $lstdcpp ??= str_contains($sharedLibs, '-lstdc++') ? '-lstdc++' : '';
@@ -305,7 +306,7 @@ class Extension
// Run compile check if build target is cli // Run compile check if build target is cli
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php // If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
$sharedExtensions = $this->getSharedExtensionLoadString(); $sharedExtensions = $this->getSharedExtensionLoadString();
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); [$ret] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
if ($ret !== 0) { if ($ret !== 0) {
throw new ValidationException( throw new ValidationException(
"extension {$this->getName()} failed compile check: php-cli returned {$ret}", "extension {$this->getName()} failed compile check: php-cli returned {$ret}",
@@ -335,7 +336,7 @@ class Extension
{ {
// Run compile check if build target is cli // Run compile check if build target is cli
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php // If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
[$ret] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php.exe -n --ri "' . $this->getDistName() . '"', false); [$ret] = cmd()->execWithResult(BUILD_BIN_PATH . '/php.exe -n --ri "' . $this->getDistName() . '"', false);
if ($ret !== 0) { if ($ret !== 0) {
throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check"); throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check");
} }
@@ -448,6 +449,15 @@ class Extension
->exec('make clean') ->exec('make clean')
->exec('make -j' . $this->builder->concurrency) ->exec('make -j' . $this->builder->concurrency)
->exec('make install'); ->exec('make install');
// process *.so file
$soFile = BUILD_MODULES_PATH . '/' . $this->getName() . '.so';
if (!file_exists($soFile)) {
throw new ValidationException("extension {$this->getName()} build failed: {$soFile} not found", validation_module: "Extension {$this->getName()} build");
}
/** @var UnixBuilderBase $builder */
$builder = $this->builder;
$builder->deployBinary($soFile, $soFile, false);
} }
/** /**
@@ -486,18 +496,46 @@ class Extension
return $this->build_static; return $this->build_static;
} }
/**
* Get the library dependencies that current extension depends on.
*
* @param bool $recursive Whether it includes dependencies recursively
*/
public function getLibraryDependencies(bool $recursive = false): array
{
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
if (!$recursive) {
return $ret;
}
$deps = [];
$added = 1;
while ($added !== 0) {
$added = 0;
foreach ($ret as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!array_key_exists($depdepName, $deps)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!array_key_exists($depName, $deps)) {
$deps[$depName] = $dep;
}
}
}
return $deps;
}
/** /**
* Returns the environment variables a shared extension needs to be built. * Returns the environment variables a shared extension needs to be built.
* CFLAGS, CXXFLAGS, LDFLAGS and so on. * CFLAGS, CXXFLAGS, LDFLAGS and so on.
*/ */
protected function getSharedExtensionEnv(): array protected function getSharedExtensionEnv(): array
{ {
$config = (new SPCConfigUtil($this->builder))->config( $config = (new SPCConfigUtil($this->builder))->getExtensionConfig($this);
[$this->getName()],
array_map(fn ($l) => $l->getName(), $this->getLibraryDependencies(recursive: true)),
$this->builder->getOption('with-suggested-exts'),
$this->builder->getOption('with-suggested-libs'),
);
[$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 ';
@@ -519,7 +557,7 @@ class Extension
} }
logger()->info("enabling {$this->name} without library {$name}"); logger()->info("enabling {$this->name} without library {$name}");
} else { } else {
$this->dependencies[] = $depLib; $this->dependencies[$name] = $depLib;
} }
} }
@@ -532,7 +570,7 @@ class Extension
} }
logger()->info("enabling {$this->name} without extension {$name}"); logger()->info("enabling {$this->name} without extension {$name}");
} else { } else {
$this->dependencies[] = $depExt; $this->dependencies[$name] = $depExt;
} }
} }
@@ -567,37 +605,4 @@ class Extension
} }
return [trim($staticLibString), trim($sharedLibString)]; return [trim($staticLibString), trim($sharedLibString)];
} }
private function getLibraryDependencies(bool $recursive = false): array
{
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
if (!$recursive) {
return $ret;
}
$deps = [];
$added = 1;
while ($added !== 0) {
$added = 0;
foreach ($ret as $depName => $dep) {
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
if (!array_key_exists($depdepName, $deps)) {
$deps[$depdepName] = $depdep;
++$added;
}
}
if (!array_key_exists($depName, $deps)) {
$deps[$depName] = $dep;
}
}
}
if (array_key_exists(0, $deps)) {
$zero = [0 => $deps[0]];
unset($deps[0]);
return $zero + $deps;
}
return $deps;
}
} }

View File

@@ -15,7 +15,11 @@ class gettext extends Extension
public function patchBeforeBuildconf(): bool public function patchBeforeBuildconf(): bool
{ {
if ($this->builder instanceof MacOSBuilder) { if ($this->builder instanceof MacOSBuilder) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gettext/config.m4', 'AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB(intl'); FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/gettext/config.m4',
['AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB([$GETTEXT_CHECK_IN_LIB'],
['AC_CHECK_LIB(intl', 'AC_CHECK_LIB([intl'] // new php versions use a bracket
);
} }
return true; return true;
} }

View File

@@ -43,7 +43,7 @@ class grpc extends Extension
public function patchBeforeConfigure(): bool public function patchBeforeConfigure(): bool
{ {
$util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]); $util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]);
$config = $util->config(['grpc']); $config = $util->getExtensionConfig($this);
$libs = $config['libs']; $libs = $config['libs'];
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs); FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs);
return true; return true;

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('maxminddb')]
class maxminddb extends Extension
{
public function patchBeforeBuildconf(): bool
{
if (!is_dir(SOURCE_PATH . '/php-src/ext/maxminddb')) {
$original = $this->source_dir;
FileSystem::copyDir($original . '/ext', SOURCE_PATH . '/php-src/ext/maxminddb');
$this->source_dir = SOURCE_PATH . '/php-src/ext/maxminddb';
return true;
}
$this->source_dir = SOURCE_PATH . '/php-src/ext/maxminddb';
return false;
}
}

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\SPCConfigUtil;
#[CustomExt('rdkafka')] #[CustomExt('rdkafka')]
class rdkafka extends Extension class rdkafka extends Extension
@@ -15,6 +16,7 @@ class rdkafka extends Extension
{ {
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n"); FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\n", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm \$RDKAFKA_LIBS\n");
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"'); FileSystem::replaceFileStr("{$this->source_dir}/config.m4", "-L\$RDKAFKA_DIR/\$PHP_LIBDIR -lm\"\n", '-L$RDKAFKA_DIR/$PHP_LIBDIR -lm $RDKAFKA_LIBS"');
FileSystem::replaceFileStr("{$this->source_dir}/config.m4", 'PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,', 'AC_CHECK_LIB([$LIBNAME], [$LIBSYMBOL],');
return true; return true;
} }
@@ -37,8 +39,7 @@ class rdkafka extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$pkgconf_libs = shell()->execWithResult('pkg-config --libs --static rdkafka')[1]; $pkgconf_libs = (new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]))->getExtensionConfig($this);
$pkgconf_libs = trim(implode('', $pkgconf_libs)); return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . " RDKAFKA_LIBS=\"{$pkgconf_libs['libs']}\"";
return '--with-rdkafka=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . ' RDKAFKA_LIBS="' . $pkgconf_libs . '"';
} }
} }

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\ValidationException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@@ -23,12 +24,7 @@ class readline extends Extension
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$enable = '--without-libedit --with-readline=' . BUILD_ROOT_PATH; return '--with-libedit --without-readline';
if ($this->builder->getPHPVersionID() < 84000) {
// the check uses `char rl_pending_input()` instead of `extern int rl_pending_input`, which makes LTO fail
$enable .= ' ac_cv_lib_readline_rl_pending_input=yes';
}
return $enable;
} }
public function buildUnixShared(): void public function buildUnixShared(): void
@@ -39,4 +35,13 @@ class readline extends Extension
} }
parent::buildUnixShared(); parent::buildUnixShared();
} }
public function runCliCheckUnix(): void
{
parent::runCliCheckUnix();
[$ret, $out] = shell()->execWithResult('printf "exit\n" | ' . BUILD_BIN_PATH . '/php -a');
if ($ret !== 0 || !str_contains(implode("\n", $out), 'Interactive shell')) {
throw new ValidationException("readline extension failed sanity check. Code: {$ret}, output: " . implode("\n", $out));
}
}
} }

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\PkgConfigUtil;
#[CustomExt('snmp')]
class snmp extends Extension
{
public function patchBeforeBuildconf(): bool
{
// Overwrite m4 config using newer PHP version
if ($this->builder->getPHPVersionID() < 80400) {
FileSystem::copy(ROOT_DIR . '/src/globals/extra/snmp-ext-config-old.m4', "{$this->source_dir}/config.m4");
}
$libs = implode(' ', PkgConfigUtil::getLibsArray('netsnmp'));
FileSystem::replaceFileStr(
"{$this->source_dir}/config.m4",
'PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])',
"SNMP_LIBS=\"{$libs}\"\nPHP_EVAL_LIBLINE([\$SNMP_LIBS], [SNMP_SHARED_LIBADD])"
);
return true;
}
}

View File

@@ -13,7 +13,7 @@ class spx extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string public function getUnixConfigureArg(bool $shared = false): string
{ {
$arg = '--enable-spx' . ($shared ? '=shared' : ''); $arg = '--enable-SPX' . ($shared ? '=shared' : '');
if ($this->builder->getLib('zlib') !== null) { if ($this->builder->getLib('zlib') !== null) {
$arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH; $arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH;
} }
@@ -29,4 +29,20 @@ class spx extends Extension
); );
return true; return true;
} }
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFileStr(
$this->source_dir . '/config.m4',
'CFLAGS="$CFLAGS -Werror -Wall -O3 -pthread -std=gnu90"',
'CFLAGS="$CFLAGS -pthread"'
);
FileSystem::replaceFileStr(
$this->source_dir . '/src/php_spx.h',
"extern zend_module_entry spx_module_entry;\n",
"extern zend_module_entry spx_module_entry;;\n#define phpext_spx_ptr &spx_module_entry\n"
);
FileSystem::copy($this->source_dir . '/src/php_spx.h', $this->source_dir . '/php_spx.h');
return true;
}
} }

View File

@@ -69,12 +69,15 @@ class swoole extends Extension
$arg .= $this->builder->getExt('swoole-hook-pgsql') ? ' --enable-swoole-pgsql' : ' --disable-swoole-pgsql'; $arg .= $this->builder->getExt('swoole-hook-pgsql') ? ' --enable-swoole-pgsql' : ' --disable-swoole-pgsql';
$arg .= $this->builder->getExt('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd'; $arg .= $this->builder->getExt('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd';
$arg .= $this->builder->getExt('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite'; $arg .= $this->builder->getExt('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite';
if ($this->builder->getExt('swoole-hook-odbc')) { if ($this->builder->getExt('swoole-hook-odbc')) {
$config = (new SPCConfigUtil($this->builder, ['libs_only_deps' => true]))->config([], ['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'] . '"';
} }
if ($this->getExtVersion() >= '6.1.0') {
$arg .= ' --enable-swoole-stdext';
}
if (SPCTarget::getTargetOS() === 'Darwin') { if (SPCTarget::getTargetOS() === 'Darwin') {
$arg .= ' ac_cv_lib_pthread_pthread_barrier_init=no'; $arg .= ' ac_cv_lib_pthread_pthread_barrier_init=no';
} }

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
#[CustomExt('trader')]
class trader extends Extension
{
public function patchBeforeBuildconf(): bool
{
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/trader/config.m4', 'PHP_TA', 'PHP_TRADER');
return true;
}
}

View File

@@ -153,7 +153,7 @@ class BSDBuilder extends UnixBuilderBase
if (!$this->getOption('no-strip', false)) { if (!$this->getOption('no-strip', false)) {
$shell->exec('strip sapi/cli/php'); $shell->exec('strip sapi/cli/php');
} }
$this->deployBinary(BUILD_TARGET_CLI); $this->deploySAPIBinary(BUILD_TARGET_CLI);
} }
/** /**
@@ -184,7 +184,7 @@ class BSDBuilder extends UnixBuilderBase
if (!$this->getOption('no-strip', false)) { if (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-unneeded micro.sfx'); shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-unneeded micro.sfx');
} }
$this->deployBinary(BUILD_TARGET_MICRO); $this->deploySAPIBinary(BUILD_TARGET_MICRO);
if ($this->phar_patched) { if ($this->phar_patched) {
SourcePatcher::unpatchMicroPhar(); SourcePatcher::unpatchMicroPhar();
@@ -206,7 +206,7 @@ class BSDBuilder extends UnixBuilderBase
if (!$this->getOption('no-strip', false)) { if (!$this->getOption('no-strip', false)) {
$shell->exec('strip sapi/fpm/php-fpm'); $shell->exec('strip sapi/fpm/php-fpm');
} }
$this->deployBinary(BUILD_TARGET_FPM); $this->deploySAPIBinary(BUILD_TARGET_FPM);
} }
/** /**

View File

@@ -7,6 +7,8 @@ namespace SPC\builder\linux;
use SPC\builder\unix\UnixBuilderBase; use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\PatchException; use SPC\exception\PatchException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\DirDiff;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@@ -89,7 +91,7 @@ class LinuxBuilder extends UnixBuilderBase
// prepare build php envs // prepare build php envs
// $musl_flag = SPCTarget::getLibc() === 'musl' ? ' -D__MUSL__' : ' -U__MUSL__'; // $musl_flag = SPCTarget::getLibc() === 'musl' ? ' -D__MUSL__' : ' -U__MUSL__';
$php_configure_env = SystemUtil::makeEnvVarString([ $php_configure_env = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH, // . ' -Dsomethinghere', // . $musl_flag, 'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH, // . ' -Dsomethinghere', // . $musl_flag,
'LDFLAGS' => '-L' . BUILD_LIB_PATH, 'LDFLAGS' => '-L' . BUILD_LIB_PATH,
// 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions! // 'LIBS' => SPCTarget::getRuntimeLibs(), // do not pass static libraries here yet, they may contain polyfills for libc functions!
@@ -107,19 +109,19 @@ class LinuxBuilder extends UnixBuilderBase
$this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec( $this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
$php_configure_env . ' ' . $php_configure_env . ' ' .
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' . getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') . ($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') . ($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') . ($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') . ($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_path . $config_file_path .
$config_file_scan_dir . $config_file_scan_dir .
$json_74 . $json_74 .
$zts . $zts .
$maxExecutionTimers . $maxExecutionTimers .
"{$phpvars} " . $phpvars . ' ' .
$this->makeStaticExtensionArgs() . ' ' $this->makeStaticExtensionArgs() . ' '
)); ));
$this->emitPatchPoint('before-php-make'); $this->emitPatchPoint('before-php-make');
@@ -179,47 +181,32 @@ class LinuxBuilder extends UnixBuilderBase
*/ */
protected function buildCli(): void protected function buildCli(): void
{ {
if ($this->getExt('readline')) { if ($this->getExt('readline') && SPCTarget::isStatic()) {
SourcePatcher::patchFile('musl_static_readline.patch', SOURCE_PATH . '/php-src'); SourcePatcher::patchFile('musl_static_readline.patch', SOURCE_PATH . '/php-src');
} }
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli"); ->exec("make {$concurrency} {$vars} cli");
if ($this->getExt('readline')) { if ($this->getExt('readline') && SPCTarget::isStatic()) {
SourcePatcher::patchFile('musl_static_readline.patch', SOURCE_PATH . '/php-src', true); SourcePatcher::patchFile('musl_static_readline.patch', SOURCE_PATH . '/php-src', true);
} }
if (!$this->getOption('no-strip', false)) { $this->deploySAPIBinary(BUILD_TARGET_CLI);
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-unneeded php');
}
if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
->exec(getenv('UPX_EXEC') . ' --best php');
}
$this->deployBinary(BUILD_TARGET_CLI);
} }
protected function buildCgi(): void protected function buildCgi(): void
{ {
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cgi"); ->exec("make {$concurrency} {$vars} cgi");
if (!$this->getOption('no-strip', false)) { $this->deploySAPIBinary(BUILD_TARGET_CGI);
shell()->cd(SOURCE_PATH . '/php-src/sapi/cgi')->exec('strip --strip-unneeded php-cgi');
}
if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cgi')
->exec(getenv('UPX_EXEC') . ' --best php-cgi');
}
$this->deployBinary(BUILD_TARGET_CGI);
} }
/** /**
@@ -230,29 +217,33 @@ class LinuxBuilder extends UnixBuilderBase
if ($this->getPHPVersionID() < 80000) { if ($this->getPHPVersionID() < 80000) {
throw new WrongUsageException('phpmicro only support PHP >= 8.0!'); throw new WrongUsageException('phpmicro only support PHP >= 8.0!');
} }
if ($this->getExt('phar')) { try {
$this->phar_patched = true; if ($this->getExt('phar')) {
SourcePatcher::patchMicroPhar($this->getPHPVersionID()); $this->phar_patched = true;
} SourcePatcher::patchMicroPhar($this->getPHPVersionID());
}
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : ''; $enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
$vars = $this->getMakeExtraVars(); $vars = $this->getMakeExtraVars();
// patch fake cli for micro // patch fake cli for micro
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli; $vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
$vars = SystemUtil::makeEnvVarString($vars); $vars = SystemUtil::makeEnvVarString($vars);
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} micro"); ->exec("make {$concurrency} {$vars} micro");
$this->processMicroUPX(); // deploy micro.sfx
$dst = $this->deploySAPIBinary(BUILD_TARGET_MICRO);
$this->deployBinary(BUILD_TARGET_MICRO); // patch after UPX-ed micro.sfx
$this->processUpxedMicroSfx($dst);
if ($this->phar_patched) { } finally {
SourcePatcher::unpatchMicroPhar(); if ($this->phar_patched) {
SourcePatcher::unpatchMicroPhar();
}
} }
} }
@@ -262,19 +253,12 @@ class LinuxBuilder extends UnixBuilderBase
protected function buildFpm(): void protected function buildFpm(): void
{ {
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} fpm"); ->exec("make {$concurrency} {$vars} fpm");
if (!$this->getOption('no-strip', false)) { $this->deploySAPIBinary(BUILD_TARGET_FPM);
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-unneeded php-fpm');
}
if ($this->getOption('with-upx-pack')) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
->exec(getenv('UPX_EXEC') . ' --best php-fpm');
}
$this->deployBinary(BUILD_TARGET_FPM);
} }
/** /**
@@ -282,17 +266,69 @@ class LinuxBuilder extends UnixBuilderBase
*/ */
protected function buildEmbed(): void protected function buildEmbed(): void
{ {
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $sharedExts = array_filter($this->exts, static fn ($ext) => $ext->isBuildShared());
$sharedExts = array_filter($sharedExts, static function ($ext) {
return Config::getExt($ext->getName(), 'build-with-php') === true;
});
$install_modules = $sharedExts ? 'install-modules' : '';
// detect changes in module path
$diff = new DirDiff(BUILD_MODULES_PATH, true);
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile') ->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec('sed -i "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile') ->exec('sed -i "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile')
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install"); ->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi {$install_modules} install-build install-headers install-programs");
// process libphp.so for shared embed
$libphpSo = BUILD_LIB_PATH . '/libphp.so';
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
$this->deployBinary($libphpSo, $libphpSo, false);
}
// process shared extensions build-with-php
$increment_files = $diff->getChangedFiles();
foreach ($increment_files as $increment_file) {
$this->deployBinary($increment_file, $increment_file, false);
}
// process libphp.a for static embed
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
$AR = getenv('AR') ?: 'ar';
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
// export dynamic symbols
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
}
// patch embed php scripts
$this->patchPhpScripts();
}
/**
* Return extra variables for php make command.
*/
private function getMakeExtraVars(): array
{
$config = (new SPCConfigUtil($this, ['libs_only_deps' => true, 'absolute_libs' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
$static = SPCTarget::isStatic() ? '-all-static' : '';
$lib = BUILD_LIB_PATH;
return array_filter([
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LIBS' => $config['libs'],
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'),
'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie",
]);
}
private function processLibphpSoFile(string $libphpSo): void
{
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: ''; $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
$libDir = BUILD_LIB_PATH; $libDir = BUILD_LIB_PATH;
$modulesDir = BUILD_MODULES_PATH; $modulesDir = BUILD_MODULES_PATH;
$libphpSo = "{$libDir}/libphp.so";
$realLibName = 'libphp.so'; $realLibName = 'libphp.so';
$cwd = getcwd(); $cwd = getcwd();
@@ -354,60 +390,29 @@ class LinuxBuilder extends UnixBuilderBase
} }
} }
} }
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
$AR = getenv('AR') ?: 'ar';
f_passthru("{$AR} -t " . BUILD_LIB_PATH . "/libphp.a | grep '\\.a$' | xargs -n1 {$AR} d " . BUILD_LIB_PATH . '/libphp.a');
// export dynamic symbols
SystemUtil::exportDynamicSymbols(BUILD_LIB_PATH . '/libphp.a');
}
if (!$this->getOption('no-strip', false) && file_exists(BUILD_LIB_PATH . '/' . $realLibName)) {
shell()->cd(BUILD_LIB_PATH)->exec("strip --strip-unneeded {$realLibName}");
}
$this->patchPhpScripts();
} }
/** /**
* Return extra variables for php make command. * Patch micro.sfx after UPX compression.
* micro needs special section handling in LinuxBuilder.
* The micro.sfx does not support UPX directly, but we can remove UPX
* info segment to adapt.
* This will also make micro.sfx with upx-packed more like a malware fore antivirus
*/ */
private function getMakeExtraVars(): array private function processUpxedMicroSfx(string $dst): void
{ {
$config = (new SPCConfigUtil($this, ['libs_only_deps' => true, 'absolute_libs' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); if ($this->getOption('with-upx-pack') && version_compare($this->getMicroVersion(), '0.2.0') >= 0) {
$static = SPCTarget::isStatic() ? '-all-static' : ''; // strip first
$lib = BUILD_LIB_PATH; // cut binary with readelf
return [ [$ret, $out] = shell()->execWithResult("readelf -l {$dst} | awk '/LOAD|GNU_STACK/ {getline; print \$1, \$2, \$3, \$4, \$6, \$7}'");
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), $out[1] = explode(' ', $out[1]);
'EXTRA_LIBS' => $config['libs'], $offset = $out[1][0];
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), if ($ret !== 0 || !str_starts_with($offset, '0x')) {
'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie", throw new PatchException('phpmicro UPX patcher', 'Cannot find offset in readelf output');
];
}
/**
* Strip micro.sfx for Linux.
* The micro.sfx does not support UPX directly, but we can remove UPX-info segment to adapt.
* This will also make micro.sfx with upx-packed more like a malware fore antivirus :(
*/
private function processMicroUPX(): void
{
if (version_compare($this->getMicroVersion(), '0.2.0') >= 0 && !$this->getOption('no-strip', false)) {
shell()->exec('strip --strip-unneeded ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
if ($this->getOption('with-upx-pack')) {
// strip first
shell()->exec(getenv('UPX_EXEC') . ' --best ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
// cut binary with readelf
[$ret, $out] = shell()->execWithResult('readelf -l ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx | awk \'/LOAD|GNU_STACK/ {getline; print $1, $2, $3, $4, $6, $7}\'');
$out[1] = explode(' ', $out[1]);
$offset = $out[1][0];
if ($ret !== 0 || !str_starts_with($offset, '0x')) {
throw new PatchException('phpmicro UPX patcher', 'Cannot find offset in readelf output');
}
$offset = hexdec($offset);
// remove upx extra wastes
file_put_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx', substr(file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx'), 0, $offset));
} }
$offset = hexdec($offset);
// remove upx extra wastes
file_put_contents($dst, substr(file_get_contents($dst), 0, $offset));
} }
} }
} }

View File

@@ -141,7 +141,7 @@ class SystemUtil
{ {
return [ return [
// debian-like // debian-like
'debian', 'ubuntu', 'Deepin', 'debian', 'ubuntu', 'Deepin', 'neon',
// rhel-like // rhel-like
'redhat', 'redhat',
// centos // centos

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -78,7 +78,7 @@ class openssl extends LinuxLibraryBase
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
} }
} }

View File

@@ -4,9 +4,6 @@ declare(strict_types=1);
namespace SPC\builder\linux\library; namespace SPC\builder\linux\library;
/**
* gmp is a template library class for unix
*/
class readline extends LinuxLibraryBase class readline extends LinuxLibraryBase
{ {
use \SPC\builder\unix\library\readline; use \SPC\builder\unix\library\readline;

View File

@@ -7,6 +7,8 @@ namespace SPC\builder\macos;
use SPC\builder\macos\library\MacOSLibraryBase; use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\builder\unix\UnixBuilderBase; use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\DirDiff;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@@ -104,7 +106,7 @@ class MacOSBuilder extends UnixBuilderBase
// prepare build php envs // prepare build php envs
$envs_build_php = SystemUtil::makeEnvVarString([ $envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH, 'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
'LDFLAGS' => '-L' . BUILD_LIB_PATH, 'LDFLAGS' => '-L' . BUILD_LIB_PATH,
]); ]);
@@ -186,12 +188,9 @@ class MacOSBuilder extends UnixBuilderBase
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$shell = shell()->cd(SOURCE_PATH . '/php-src'); $shell = shell()->cd(SOURCE_PATH . '/php-src');
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
$shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cli"); $shell->exec("make {$concurrency} {$vars} cli");
if (!$this->getOption('no-strip', false)) { $this->deploySAPIBinary(BUILD_TARGET_CLI);
$shell->exec('dsymutil -f sapi/cli/php')->exec('strip -S sapi/cli/php');
}
$this->deployBinary(BUILD_TARGET_CLI);
} }
protected function buildCgi(): void protected function buildCgi(): void
@@ -199,12 +198,9 @@ class MacOSBuilder extends UnixBuilderBase
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$shell = shell()->cd(SOURCE_PATH . '/php-src'); $shell = shell()->cd(SOURCE_PATH . '/php-src');
$SPC_CMD_PREFIX_PHP_MAKE = getenv('SPC_CMD_PREFIX_PHP_MAKE') ?: 'make'; $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
$shell->exec("{$SPC_CMD_PREFIX_PHP_MAKE} {$vars} cgi"); $shell->exec("make {$concurrency} {$vars} cgi");
if (!$this->getOption('no-strip', false)) { $this->deploySAPIBinary(BUILD_TARGET_CGI);
$shell->exec('dsymutil -f sapi/cgi/php-cgi')->exec('strip -S sapi/cgi/php-cgi');
}
$this->deployBinary(BUILD_TARGET_CGI);
} }
/** /**
@@ -215,30 +211,30 @@ class MacOSBuilder extends UnixBuilderBase
if ($this->getPHPVersionID() < 80000) { if ($this->getPHPVersionID() < 80000) {
throw new WrongUsageException('phpmicro only support PHP >= 8.0!'); throw new WrongUsageException('phpmicro only support PHP >= 8.0!');
} }
if ($this->getExt('phar')) {
$this->phar_patched = true;
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
}
$enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : ''; try {
$vars = $this->getMakeExtraVars(); if ($this->getExt('phar')) {
$this->phar_patched = true;
SourcePatcher::patchMicroPhar($this->getPHPVersionID());
}
// patch fake cli for micro $enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
$vars['EXTRA_CFLAGS'] .= $enable_fake_cli; $vars = $this->getMakeExtraVars();
$vars = SystemUtil::makeEnvVarString($vars);
$shell = shell()->cd(SOURCE_PATH . '/php-src'); // patch fake cli for micro
// build $vars['EXTRA_CFLAGS'] .= $enable_fake_cli;
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} micro"); $vars = SystemUtil::makeEnvVarString($vars);
// strip
if (!$this->getOption('no-strip', false)) {
$shell->exec('dsymutil -f sapi/micro/micro.sfx')->exec('strip -S sapi/micro/micro.sfx');
}
$this->deployBinary(BUILD_TARGET_MICRO); $shell = shell()->cd(SOURCE_PATH . '/php-src');
// build
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
$shell->exec("make {$concurrency} {$vars} micro");
if ($this->phar_patched) { $this->deploySAPIBinary(BUILD_TARGET_MICRO);
SourcePatcher::unpatchMicroPhar(); } finally {
if ($this->phar_patched) {
SourcePatcher::unpatchMicroPhar();
}
} }
} }
@@ -250,11 +246,9 @@ class MacOSBuilder extends UnixBuilderBase
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$shell = shell()->cd(SOURCE_PATH . '/php-src'); $shell = shell()->cd(SOURCE_PATH . '/php-src');
$shell->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . " {$vars} fpm"); $concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
if (!$this->getOption('no-strip', false)) { $shell->exec("make {$concurrency} {$vars} fpm");
$shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip -S sapi/fpm/php-fpm'); $this->deploySAPIBinary(BUILD_TARGET_FPM);
}
$this->deployBinary(BUILD_TARGET_FPM);
} }
/** /**
@@ -262,10 +256,31 @@ class MacOSBuilder extends UnixBuilderBase
*/ */
protected function buildEmbed(): void protected function buildEmbed(): void
{ {
$sharedExts = array_filter($this->exts, static fn ($ext) => $ext->isBuildShared());
$sharedExts = array_filter($sharedExts, static function ($ext) {
return Config::getExt($ext->getName(), 'build-with-php') === true;
});
$install_modules = $sharedExts ? 'install-modules' : '';
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars()); $vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
$diff = new DirDiff(BUILD_MODULES_PATH, true);
shell()->cd(SOURCE_PATH . '/php-src') shell()->cd(SOURCE_PATH . '/php-src')
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install"); ->exec('sed -i "" "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile')
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi {$install_modules} install-build install-headers install-programs");
$libphp = BUILD_LIB_PATH . '/libphp.dylib';
if (file_exists($libphp)) {
$this->deployBinary($libphp, $libphp, false);
// macOS currently have no -release option for dylib, so we just rename it here
}
// process shared extensions build-with-php
$increment_files = $diff->getChangedFiles();
foreach ($increment_files as $increment_file) {
$this->deployBinary($increment_file, $increment_file, false);
}
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
$AR = getenv('AR') ?: 'ar'; $AR = getenv('AR') ?: 'ar';
@@ -279,10 +294,10 @@ class MacOSBuilder extends UnixBuilderBase
private function getMakeExtraVars(): array private function getMakeExtraVars(): array
{ {
$config = (new SPCConfigUtil($this, ['libs_only_deps' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $config = (new SPCConfigUtil($this, ['libs_only_deps' => true]))->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
return [ return array_filter([
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LDFLAGS_PROGRAM' => '-L' . BUILD_LIB_PATH, 'EXTRA_LDFLAGS_PROGRAM' => '-L' . BUILD_LIB_PATH,
'EXTRA_LIBS' => $config['libs'], 'EXTRA_LIBS' => $config['libs'],
]; ]);
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -63,7 +63,7 @@ class openssl extends MacOSLibraryBase
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
} }
} }

View File

@@ -4,9 +4,6 @@ declare(strict_types=1);
namespace SPC\builder\macos\library; namespace SPC\builder\macos\library;
/**
* gmp is a template library class for unix
*/
class readline extends MacOSLibraryBase class readline extends MacOSLibraryBase
{ {
use \SPC\builder\unix\library\readline; use \SPC\builder\unix\library\readline;

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix; namespace SPC\builder\unix;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\builder\linux\SystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\exception\SPCException; use SPC\exception\SPCException;
use SPC\exception\SPCInternalException; use SPC\exception\SPCInternalException;
@@ -13,6 +14,7 @@ use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\pkg\GoXcaddy; use SPC\store\pkg\GoXcaddy;
use SPC\store\SourceManager;
use SPC\toolchain\GccNativeToolchain; use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\ToolchainManager; use SPC\toolchain\ToolchainManager;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
@@ -78,6 +80,92 @@ abstract class UnixBuilderBase extends BuilderBase
$this->lib_list = $sorted_libraries; $this->lib_list = $sorted_libraries;
} }
/**
* Strip unneeded symbols from binary file.
*/
public function stripBinary(string $binary_path): void
{
shell()->exec(match (PHP_OS_FAMILY) {
'Darwin' => "strip -S {$binary_path}",
'Linux' => "strip --strip-unneeded {$binary_path}",
default => throw new SPCInternalException('stripBinary is only supported on Linux and macOS'),
});
}
/**
* Extract debug information from binary file.
*
* @param string $binary_path the path to the binary file, including executables, shared libraries, etc
*/
public function extractDebugInfo(string $binary_path): string
{
$target_dir = BUILD_ROOT_PATH . '/debug';
FileSystem::createDir($target_dir);
$basename = basename($binary_path);
$debug_file = "{$target_dir}/{$basename}" . (PHP_OS_FAMILY === 'Darwin' ? '.dwarf' : '.debug');
if (PHP_OS_FAMILY === 'Darwin') {
shell()->exec("dsymutil -f {$binary_path} -o {$debug_file}");
} elseif (PHP_OS_FAMILY === 'Linux') {
if ($eu_strip = SystemUtil::findCommand('eu-strip')) {
shell()
->exec("{$eu_strip} -f {$debug_file} {$binary_path}")
->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}");
} else {
shell()
->exec("objcopy --only-keep-debug {$binary_path} {$debug_file}")
->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}");
}
} else {
throw new SPCInternalException('extractDebugInfo is only supported on Linux and macOS');
}
return $debug_file;
}
/**
* Deploy the binary file from src to dst.
*/
public function deployBinary(string $src, string $dst, bool $executable = true): string
{
logger()->debug('Deploying binary from ' . $src . ' to ' . $dst);
// file must exists
if (!file_exists($src)) {
throw new SPCInternalException("Deploy failed. Cannot find file: {$src}");
}
// dst dir must exists
FileSystem::createDir(dirname($dst));
// ignore copy to self
if (realpath($src) !== realpath($dst)) {
shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg($dst));
}
// file exist
if (!file_exists($dst)) {
throw new SPCInternalException("Deploy failed. Cannot find file after copy: {$dst}");
}
// extract debug info
$this->extractDebugInfo($dst);
// strip
if (!$this->getOption('no-strip')) {
$this->stripBinary($dst);
}
// UPX for linux
$upx_option = $this->getOption('with-upx-pack');
if ($upx_option && PHP_OS_FAMILY === 'Linux' && $executable) {
if ($this->getOption('no-strip')) {
logger()->warning('UPX compression is not recommended when --no-strip is enabled.');
}
logger()->info("Compressing {$dst} with UPX");
shell()->exec(getenv('UPX_EXEC') . " --best {$dst}");
}
return $dst;
}
/** /**
* Sanity check after build complete. * Sanity check after build complete.
*/ */
@@ -208,23 +296,20 @@ abstract class UnixBuilderBase extends BuilderBase
} }
/** /**
* Deploy the binary file to the build bin path. * Deploy binaries that produces executable SAPI
*
* @param int $type Type integer, one of BUILD_TARGET_CLI, BUILD_TARGET_MICRO, BUILD_TARGET_FPM
*/ */
protected function deployBinary(int $type): bool protected function deploySAPIBinary(int $type): string
{ {
$src = match ($type) { $src = match ($type) {
BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php', BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php',
BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx', BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx',
BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm', BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm',
BUILD_TARGET_CGI => SOURCE_PATH . '/php-src/sapi/cgi/php-cgi', BUILD_TARGET_CGI => SOURCE_PATH . '/php-src/sapi/cgi/php-cgi',
BUILD_TARGET_FRANKENPHP => BUILD_BIN_PATH . '/frankenphp',
default => throw new SPCInternalException("Deployment does not accept type {$type}"), default => throw new SPCInternalException("Deployment does not accept type {$type}"),
}; };
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); $dst = BUILD_BIN_PATH . '/' . basename($src);
FileSystem::createDir(BUILD_BIN_PATH); return $this->deployBinary($src, $dst);
shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_BIN_PATH));
return true;
} }
/** /**
@@ -265,22 +350,75 @@ abstract class UnixBuilderBase extends BuilderBase
} }
} }
/**
* Process the --with-frankenphp-app option
* Creates app.tar and app.checksum in source/frankenphp directory
*/
protected function processFrankenphpApp(): void
{
$frankenphpSourceDir = getenv('FRANKENPHP_SOURCE_PATH') ?: SOURCE_PATH . '/frankenphp';
if (!is_dir($frankenphpSourceDir)) {
SourceManager::initSource(['frankenphp'], ['frankenphp']);
}
$frankenphpAppPath = $this->getOption('with-frankenphp-app');
if ($frankenphpAppPath) {
if (!is_dir($frankenphpAppPath)) {
throw new WrongUsageException("The path provided to --with-frankenphp-app is not a valid directory: {$frankenphpAppPath}");
}
$appTarPath = $frankenphpSourceDir . '/app.tar';
logger()->info("Creating app.tar from {$frankenphpAppPath}");
shell()->exec('tar -cf ' . escapeshellarg($appTarPath) . ' -C ' . escapeshellarg($frankenphpAppPath) . ' .');
$checksum = hash_file('md5', $appTarPath);
file_put_contents($frankenphpSourceDir . '/app_checksum.txt', $checksum);
} else {
FileSystem::removeFileIfExists($frankenphpSourceDir . '/app.tar');
FileSystem::removeFileIfExists($frankenphpSourceDir . '/app_checksum.txt');
file_put_contents($frankenphpSourceDir . '/app.tar', '');
file_put_contents($frankenphpSourceDir . '/app_checksum.txt', '');
}
}
protected function getFrankenPHPVersion(): string
{
if ($version = getenv('FRANKENPHP_VERSION')) {
return $version;
}
$frankenphpSourceDir = getenv('FRANKENPHP_SOURCE_PATH') ?: SOURCE_PATH . '/frankenphp';
$goModPath = $frankenphpSourceDir . '/caddy/go.mod';
if (!file_exists($goModPath)) {
throw new SPCInternalException("FrankenPHP caddy/go.mod file not found at {$goModPath}, why did we not download FrankenPHP?");
}
$content = file_get_contents($goModPath);
if (preg_match('/github\.com\/dunglas\/frankenphp\s+v?(\d+\.\d+\.\d+)/', $content, $matches)) {
return $matches[1];
}
throw new SPCInternalException('Could not find FrankenPHP version in caddy/go.mod');
}
protected function buildFrankenphp(): void protected function buildFrankenphp(): void
{ {
GlobalEnvManager::addPathIfNotExists(GoXcaddy::getPath()); GlobalEnvManager::addPathIfNotExists(GoXcaddy::getPath());
$this->processFrankenphpApp();
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : ''; $nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : ''; $nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES'); $xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
// make it possible to build from a different frankenphp directory! $frankenphpSourceDir = getenv('FRANKENPHP_SOURCE_PATH') ?: SOURCE_PATH . '/frankenphp';
if (!str_contains($xcaddyModules, '--with github.com/dunglas/frankenphp')) {
$xcaddyModules = '--with github.com/dunglas/frankenphp ' . $xcaddyModules; $xcaddyModules = preg_replace('#--with github.com/dunglas/frankenphp\S*#', '', $xcaddyModules);
} $xcaddyModules = "--with github.com/dunglas/frankenphp={$frankenphpSourceDir} " .
"--with github.com/dunglas/frankenphp/caddy={$frankenphpSourceDir}/caddy {$xcaddyModules}";
if ($this->getLib('brotli') === null && str_contains($xcaddyModules, '--with github.com/dunglas/caddy-cbrotli')) { if ($this->getLib('brotli') === null && str_contains($xcaddyModules, '--with github.com/dunglas/caddy-cbrotli')) {
logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.'); logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.');
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules); $xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
} }
[, $out] = shell()->execWithResult('go list -m github.com/dunglas/frankenphp@latest');
$frankenPhpVersion = str_replace('github.com/dunglas/frankenphp v', '', $out[0]); $frankenPhpVersion = $this->getFrankenPHPVersion();
$libphpVersion = $this->getPHPVersion(); $libphpVersion = $this->getPHPVersion();
$dynamic_exports = ''; $dynamic_exports = '';
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
@@ -290,7 +428,6 @@ abstract class UnixBuilderBase extends BuilderBase
$dynamic_exports = ' ' . $dynamicSymbolsArgument; $dynamic_exports = ' ' . $dynamicSymbolsArgument;
} }
} }
$debugFlags = $this->getOption('no-strip') ? '-w -s ' : '';
$extLdFlags = "-extldflags '-pie{$dynamic_exports} {$this->arch_ld_flags}'"; $extLdFlags = "-extldflags '-pie{$dynamic_exports} {$this->arch_ld_flags}'";
$muslTags = ''; $muslTags = '';
$staticFlags = ''; $staticFlags = '';
@@ -301,7 +438,7 @@ abstract class UnixBuilderBase extends BuilderBase
} }
$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list); $config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list);
$cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'); $cflags = "{$this->arch_c_flags} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -DFRANKENPHP_VERSION=' . $frankenPhpVersion;
$libs = $config['libs']; $libs = $config['libs'];
// Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix. // Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix.
if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') || if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') ||
@@ -315,7 +452,7 @@ abstract class UnixBuilderBase extends BuilderBase
'CGO_CFLAGS' => clean_spaces($cflags), 'CGO_CFLAGS' => clean_spaces($cflags),
'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 . ' ' . $debugFlags . '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' .
'-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}",
@@ -325,13 +462,7 @@ abstract class UnixBuilderBase extends BuilderBase
->setEnv($env) ->setEnv($env)
->exec("xcaddy build --output frankenphp {$xcaddyModules}"); ->exec("xcaddy build --output frankenphp {$xcaddyModules}");
if (!$this->getOption('no-strip', false) && file_exists(BUILD_BIN_PATH . '/frankenphp')) { $this->deploySAPIBinary(BUILD_TARGET_FRANKENPHP);
if (PHP_OS_FAMILY === 'Linux') {
shell()->cd(BUILD_BIN_PATH)->exec('strip --strip-unneeded frankenphp');
} else { // macOS doesn't understand strip-unneeded
shell()->cd(BUILD_BIN_PATH)->exec('strip -S frankenphp');
}
}
} }
/** /**

View File

@@ -17,7 +17,7 @@ trait attr
->exec('libtoolize --force --copy') ->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if') ->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls') ->configure('--disable-nls')
->make(); ->make('install-attributes_h install-data install-libattr_h install-libLTLIBRARIES install-pkgincludeHEADERS install-pkgconfDATA', with_install: false);
$this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX);
} }
} }

View File

@@ -23,6 +23,8 @@ trait curl
->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD')) ->optionalLib('zstd', ...cmake_boolean_args('CURL_ZSTD'))
->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2')) ->optionalLib('idn2', ...cmake_boolean_args('USE_LIBIDN2'))
->optionalLib('psl', ...cmake_boolean_args('CURL_USE_LIBPSL')) ->optionalLib('psl', ...cmake_boolean_args('CURL_USE_LIBPSL'))
->optionalLib('krb5', ...cmake_boolean_args('CURL_USE_GSSAPI'))
->optionalLib('idn2', ...cmake_boolean_args('CURL_USE_IDN2'))
->optionalLib('libcares', '-DENABLE_ARES=ON') ->optionalLib('libcares', '-DENABLE_ARES=ON')
->addConfigureArgs( ->addConfigureArgs(
'-DBUILD_CURL_EXE=OFF', '-DBUILD_CURL_EXE=OFF',

View File

@@ -31,7 +31,7 @@ trait gettext
$autoconf->addConfigureArgs('--disable-threads'); $autoconf->addConfigureArgs('--disable-threads');
} }
$autoconf->configure()->make(); $autoconf->configure()->make(dir: $this->getSourceDir() . '/gettext-runtime/intl');
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();
} }
} }

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\util\executor\UnixAutoconfExecutor;
trait idn2
{
protected function build(): void
{
UnixAutoconfExecutor::create($this)
->configure(
'--disable-nls',
'--disable-doc',
'--enable-year2038',
'--disable-rpath'
)
->optionalLib('libiconv', "--with-libiconv-prefix={$this->getBuildRootPath()}")
->optionalLib('libunistring', "--with-libunistring-prefix={$this->getBuildRootPath()}")
->optionalLib('gettext', "--with-libnintl-prefix={$this->getBuildRootPath()}")
->make();
$this->patchPkgconfPrefix(['libidn2.pc']);
$this->patchLaDependencyPrefix();
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCConfigUtil;
trait krb5
{
protected function build(): void
{
$origin_source_dir = $this->source_dir;
$this->source_dir .= '/src';
shell()->cd($this->source_dir)->exec('autoreconf -if');
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies(true));
$spc = new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]);
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false));
$extraEnv = [
'CFLAGS' => '-fcommon',
'LIBS' => $config['libs'],
];
if (getenv('SPC_LD_LIBRARY_PATH') && getenv('SPC_LIBRARY_PATH')) {
$extraEnv = [...$extraEnv, ...[
'LD_LIBRARY_PATH' => getenv('SPC_LD_LIBRARY_PATH'),
'LIBRARY_PATH' => getenv('SPC_LIBRARY_PATH'),
]];
}
$args = [
'--disable-nls',
'--disable-rpath',
'--without-system-verto',
];
if (PHP_OS_FAMILY === 'Darwin') {
$extraEnv['LDFLAGS'] = '-framework Kerberos';
$args[] = 'ac_cv_func_secure_getenv=no';
}
UnixAutoconfExecutor::create($this)
->appendEnv($extraEnv)
->optionalLib('ldap', '--with-ldap', '--without-ldap')
->optionalLib('libedit', '--with-libedit', '--without-libedit')
->configure(...$args)
->make();
$this->patchPkgconfPrefix([
'krb5-gssapi.pc',
'krb5.pc',
'kadm-server.pc',
'kadm-client.pc',
'kdb.pc',
'mit-krb5-gssapi.pc',
'mit-krb5.pc',
'gssrpc.pc',
]);
$this->source_dir = $origin_source_dir;
}
}

View File

@@ -26,7 +26,7 @@ trait libacl
->exec('libtoolize --force --copy') ->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if') ->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls', '--disable-tests') ->configure('--disable-nls', '--disable-tests')
->make(); ->make('install-acl_h install-libacl_h install-data install-libLTLIBRARIES install-pkgincludeHEADERS install-sysincludeHEADERS install-pkgconfDATA', with_install: false);
$this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX);
} }
} }

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait libedit
{
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileRegex(
$this->source_dir . '/src/sys.h',
'|//#define\s+strl|',
'#define strl'
);
return true;
}
protected function build(): void
{
UnixAutoconfExecutor::create($this)
->appendEnv(['CFLAGS' => '-D__STDC_ISO_10646__=201103L'])
->configure()
->make();
$this->patchPkgconfPrefix(['libedit.pc']);
}
}

View File

@@ -10,7 +10,13 @@ trait libiconv
{ {
protected function build(): void protected function build(): void
{ {
UnixAutoconfExecutor::create($this)->configure('--enable-extra-encodings')->make(); UnixAutoconfExecutor::create($this)
->configure(
'--enable-extra-encodings',
'--enable-year2038',
)
->make('install-lib', with_install: false)
->make('install-lib', with_install: false, dir: $this->getSourceDir() . '/libcharset');
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();
} }
} }

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\util\executor\UnixCMakeExecutor;
trait libmaxminddb
{
protected function build(): void
{
UnixCMakeExecutor::create($this)
->addConfigureArgs(
'-DBUILD_TESTING=OFF',
'-DMAXMINDDB_BUILD_BINARIES=OFF',
)
->build();
}
}

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixCMakeExecutor;
trait librdkafka trait librdkafka
{ {
@@ -26,34 +26,18 @@ trait librdkafka
protected function build(): void protected function build(): void
{ {
UnixAutoconfExecutor::create($this) UnixCMakeExecutor::create($this)
->appendEnv(['CFLAGS' => '-Wno-int-conversion -Wno-unused-but-set-variable -Wno-unused-variable']) ->optionalLib('zstd', ...cmake_boolean_args('WITH_ZSTD'))
->optionalLib( ->optionalLib('curl', ...cmake_boolean_args('WITH_CURL'))
'zstd', ->optionalLib('openssl', ...cmake_boolean_args('WITH_SSL'))
function ($lib) { ->optionalLib('zlib', ...cmake_boolean_args('WITH_ZLIB'))
putenv("STATIC_LIB_libzstd={$lib->getLibDir()}/libzstd.a"); ->optionalLib('liblz4', ...cmake_boolean_args('ENABLE_LZ4_EXT'))
return ''; ->addConfigureArgs(
}, '-DWITH_SASL=OFF',
'--disable-zstd' '-DRDKAFKA_BUILD_STATIC=ON',
'-DRDKAFKA_BUILD_EXAMPLES=OFF',
'-DRDKAFKA_BUILD_TESTS=OFF',
) )
->removeConfigureArgs( ->build();
'--with-pic',
'--enable-pic',
)
->configure(
'--disable-curl',
'--disable-sasl',
'--disable-valgrind',
'--disable-zlib',
'--disable-ssl',
)
->make();
$this->patchPkgconfPrefix(['rdkafka.pc', 'rdkafka-static.pc', 'rdkafka++.pc', 'rdkafka++-static.pc']);
// remove dynamic libs
shell()
->exec("rm -rf {$this->getLibDir()}/*.so.*")
->exec("rm -rf {$this->getLibDir()}/*.so")
->exec("rm -rf {$this->getLibDir()}/*.dylib");
} }
} }

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\util\executor\UnixAutoconfExecutor;
trait libunistring
{
protected function build(): void
{
UnixAutoconfExecutor::create($this)
->configure('--disable-nls')
->make();
$this->patchLaDependencyPrefix();
}
}

View File

@@ -22,6 +22,7 @@ trait libzip
'-DBUILD_EXAMPLES=OFF', '-DBUILD_EXAMPLES=OFF',
'-DBUILD_REGRESS=OFF', '-DBUILD_REGRESS=OFF',
'-DBUILD_TOOLS=OFF', '-DBUILD_TOOLS=OFF',
'-DBUILD_OSSFUZZ=OFF',
) )
->build(); ->build();
$this->patchPkgconfPrefix(['libzip.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(['libzip.pc'], PKGCONF_PATCH_PREFIX);

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait net_snmp
{
public function patchBeforeBuild(): bool
{
if (PHP_OS_FAMILY === 'Linux') {
FileSystem::replaceFileStr("{$this->source_dir}/configure", 'LIBS="-lssl ${OPENSSL_LIBS}"', 'LIBS="-lssl ${OPENSSL_LIBS} -lpthread -ldl"');
return true;
}
return false;
}
protected function build(): void
{
// use --static for PKG_CONFIG
UnixAutoconfExecutor::create($this)
->setEnv(['PKG_CONFIG' => getenv('PKG_CONFIG') . ' --static'])
->configure(
'--disable-mibs',
'--without-nl',
'--disable-agent',
'--disable-applications',
'--disable-manuals',
'--disable-scripts',
'--disable-embedded-perl',
'--without-perl-modules',
'--with-out-mib-modules="if-mib host disman/event-mib ucd-snmp/diskio mibII"',
'--with-out-transports="Unix"',
'--with-mib-modules=""',
'--enable-mini-agent',
'--with-default-snmp-version="3"',
'--with-sys-contact="@@no.where"',
'--with-sys-location="Unknown"',
'--with-logfile="/var/log/snmpd.log"',
'--with-persistent-directory="/var/lib/net-snmp"',
'--with-openssl=' . BUILD_ROOT_PATH,
'--with-zlib=' . BUILD_ROOT_PATH,
)->make(with_install: 'installheaders installlibs install_pkgconfig');
$this->patchPkgconfPrefix();
}
}

View File

@@ -45,9 +45,9 @@ trait postgresql
protected function build(): void protected function build(): void
{ {
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies()); $libs = array_map(fn ($x) => $x->getName(), $this->getDependencies(true));
$spc = new SPCConfigUtil($this->getBuilder(), ['no_php' => true, 'libs_only_deps' => true]); $spc = new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]);
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs')); $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'],

View File

@@ -57,6 +57,7 @@ class WindowsBuilder extends BuilderBase
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
SourcePatcher::patchBeforeBuildconf($this); SourcePatcher::patchBeforeBuildconf($this);
@@ -66,21 +67,6 @@ class WindowsBuilder extends BuilderBase
$zts = $this->zts ? '--enable-zts=yes ' : '--enable-zts=no '; $zts = $this->zts ? '--enable-zts=yes ' : '--enable-zts=no ';
// with-upx-pack for phpmicro
if ($enableMicro && version_compare($this->getMicroVersion(), '0.2.0') < 0) {
$makefile = FileSystem::convertPath(SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag.w32');
if ($this->getOption('with-upx-pack', false)) {
if (!file_exists($makefile . '.originfile')) {
copy($makefile, $makefile . '.originfile');
FileSystem::replaceFileStr($makefile, '$(MICRO_SFX):', '_MICRO_UPX = ' . getenv('UPX_EXEC') . " --best $(MICRO_SFX)\n$(MICRO_SFX):");
FileSystem::replaceFileStr($makefile, '@$(_MICRO_MT)', "@$(_MICRO_MT)\n\t@$(_MICRO_UPX)");
}
} elseif (file_exists($makefile . '.originfile')) {
copy($makefile . '.originfile', $makefile);
unlink($makefile . '.originfile');
}
}
$opcache_jit = !$this->getOption('disable-opcache-jit', false); $opcache_jit = !$this->getOption('disable-opcache-jit', false);
$opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit=yes ' : '--enable-opcache-jit=no '; $opcache_jit_arg = $opcache_jit ? '--enable-opcache-jit=yes ' : '--enable-opcache-jit=no ';
@@ -102,13 +88,13 @@ class WindowsBuilder extends BuilderBase
->exec( ->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" . "{$this->sdk_prefix} configure.bat --task-args \"" .
'--disable-all ' . '--disable-all ' .
'--disable-cgi ' .
'--with-php-build=' . BUILD_ROOT_PATH . ' ' . '--with-php-build=' . BUILD_ROOT_PATH . ' ' .
'--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' . '--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' .
'--with-extra-libs=' . BUILD_LIB_PATH . ' ' . '--with-extra-libs=' . BUILD_LIB_PATH . ' ' .
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') . ($enableMicro ? ('--enable-micro ' . $micro_logo . $micro_w32) : '--disable-micro ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') . ($enableEmbed ? '--enable-embed ' : '--disable-embed ') .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_scan_dir . $config_file_scan_dir .
$opcache_jit_arg . $opcache_jit_arg .
"{$this->makeStaticExtensionArgs()} " . "{$this->makeStaticExtensionArgs()} " .
@@ -127,6 +113,10 @@ class WindowsBuilder extends BuilderBase
if ($enableFpm) { if ($enableFpm) {
logger()->warning('Windows does not support fpm SAPI, I will skip it.'); logger()->warning('Windows does not support fpm SAPI, I will skip it.');
} }
if ($enableCgi) {
logger()->info('building cgi');
$this->buildCgi();
}
if ($enableMicro) { if ($enableMicro) {
logger()->info('building micro'); logger()->info('building micro');
$this->buildMicro(); $this->buildMicro();
@@ -140,7 +130,7 @@ class WindowsBuilder extends BuilderBase
} }
} }
public function testPHP(int $build_target = BUILD_TARGET_NONE) public function testPHP(int $build_target = BUILD_TARGET_NONE): void
{ {
$this->sanityCheck($build_target); $this->sanityCheck($build_target);
} }
@@ -151,12 +141,52 @@ class WindowsBuilder extends BuilderBase
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: ''; $extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// Add debug symbols for release build if --no-strip is specified
// We need to modify CFLAGS to replace /Ox with /Zi and add /DEBUG to LDFLAGS
$debug_overrides = '';
if ($this->getOption('no-strip', false)) {
// Read current CFLAGS from Makefile and replace optimization flags
$makefile_content = file_get_contents(SOURCE_PATH . '\php-src\Makefile');
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
$cflags = $matches[1];
// Replace /Ox (full optimization) with /Zi (debug info) and /Od (disable optimization)
// Keep optimization for speed: /O2 /Zi instead of /Od /Zi
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_CLI=/DEBUG" ';
}
}
// add nmake wrapper // add nmake wrapper
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cli_wrapper.bat', "nmake /nologo LIBS_CLI=\"ws2_32.lib shell32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*"); FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cli_wrapper.bat', "nmake /nologo {$debug_overrides}LIBS_CLI=\"ws2_32.lib shell32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cli_wrapper.bat --task-args php.exe"); cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cli_wrapper.bat --task-args php.exe");
$this->deployBinary(BUILD_TARGET_CLI); $this->deploySAPIBinary(BUILD_TARGET_CLI);
}
public function buildCgi(): void
{
SourcePatcher::patchWindowsCGITarget();
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// Add debug symbols for release build if --no-strip is specified
$debug_overrides = '';
if ($this->getOption('no-strip', false)) {
$makefile_content = file_get_contents(SOURCE_PATH . '\php-src\Makefile');
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
$cflags = $matches[1];
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_CGI=/DEBUG" ';
}
}
// add nmake wrapper
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cgi_wrapper.bat', "nmake /nologo {$debug_overrides}LIBS_CGI=\"ws2_32.lib kernel32.lib advapi32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cgi_wrapper.bat --task-args php-cgi.exe");
$this->deploySAPIBinary(BUILD_TARGET_CGI);
} }
public function buildEmbed(): void public function buildEmbed(): void
@@ -183,9 +213,20 @@ class WindowsBuilder extends BuilderBase
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: ''; $extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// Add debug symbols for release build if --no-strip is specified
$debug_overrides = '';
if ($this->getOption('no-strip', false) && !$this->getOption('debug', false)) {
$makefile_content = file_get_contents(SOURCE_PATH . '\php-src\Makefile');
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
$cflags = $matches[1];
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_MICRO=/DEBUG" ';
}
}
// add nmake wrapper // add nmake wrapper
$fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' /DPHP_MICRO_FAKE_CLI" ' : ''; $fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' /DPHP_MICRO_FAKE_CLI" ' : '';
$wrapper = "nmake /nologo LIBS_MICRO=\"ws2_32.lib shell32.lib {$extra_libs}\" CFLAGS_MICRO=\"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1{$fake_cli}\" %*"; $wrapper = "nmake /nologo {$debug_overrides}LIBS_MICRO=\"ws2_32.lib shell32.lib {$extra_libs}\" CFLAGS_MICRO=\"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1{$fake_cli}\" %*";
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_micro_wrapper.bat', $wrapper); FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_micro_wrapper.bat', $wrapper);
// phar patch for micro // phar patch for micro
@@ -202,7 +243,7 @@ class WindowsBuilder extends BuilderBase
} }
} }
$this->deployBinary(BUILD_TARGET_MICRO); $this->deploySAPIBinary(BUILD_TARGET_MICRO);
} }
public function proveLibs(array $sorted_libraries): void public function proveLibs(array $sorted_libraries): void
@@ -265,7 +306,7 @@ class WindowsBuilder extends BuilderBase
// sanity check for php-cli // sanity check for php-cli
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('running cli sanity check'); logger()->info('running cli sanity check');
[$ret, $output] = cmd()->execWithResult(BUILD_ROOT_PATH . '\bin\php.exe -n -r "echo \"hello\";"'); [$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php.exe -n -r "echo \"hello\";"');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check'); throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check');
} }
@@ -284,7 +325,7 @@ class WindowsBuilder extends BuilderBase
if (file_exists($test_file)) { if (file_exists($test_file)) {
@unlink($test_file); @unlink($test_file);
} }
file_put_contents($test_file, file_get_contents(BUILD_ROOT_PATH . '\bin\micro.sfx') . $task['content']); file_put_contents($test_file, file_get_contents(BUILD_BIN_PATH . '\micro.sfx') . $task['content']);
chmod($test_file, 0755); chmod($test_file, 0755);
[$ret, $out] = cmd()->execWithResult($test_file); [$ret, $out] = cmd()->execWithResult($test_file);
foreach ($task['conditions'] as $condition => $closure) { foreach ($task['conditions'] as $condition => $closure) {
@@ -298,6 +339,17 @@ class WindowsBuilder extends BuilderBase
} }
} }
} }
// sanity check for php-cgi
if (($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
logger()->info('running cgi sanity check');
FileSystem::writeFile(SOURCE_PATH . '\php-cgi-test.php', '<?php echo "<h1>Hello, World!</h1>"; ?>');
[$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php-cgi.exe -n -f ' . SOURCE_PATH . '\php-cgi-test.php');
$raw_output = implode("\n", $output);
if ($ret !== 0 || !str_contains($raw_output, 'Hello, World!')) {
throw new ValidationException("cgi failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cgi sanity check');
}
}
} }
/** /**
@@ -305,27 +357,53 @@ class WindowsBuilder extends BuilderBase
* *
* @param int $type Deploy type * @param int $type Deploy type
*/ */
public function deployBinary(int $type): bool public function deploySAPIBinary(int $type): void
{ {
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
$debug_dir = BUILD_ROOT_PATH . '\debug';
$bin_path = BUILD_BIN_PATH;
// create dirs
FileSystem::createDir($debug_dir);
FileSystem::createDir($bin_path);
// determine source path for different SAPI
$rel_type = 'Release'; // TODO: Debug build support
$ts = $this->zts ? '_TS' : ''; $ts = $this->zts ? '_TS' : '';
$src = match ($type) { $src = match ($type) {
BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe", BUILD_TARGET_CLI => [SOURCE_PATH . "\\php-src\\x64\\{$rel_type}{$ts}", 'php.exe', 'php.pdb'],
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx", BUILD_TARGET_MICRO => [SOURCE_PATH . "\\php-src\\x64\\{$rel_type}{$ts}", 'micro.sfx', 'micro.pdb'],
BUILD_TARGET_CGI => [SOURCE_PATH . "\\php-src\\x64\\{$rel_type}{$ts}", 'php-cgi.exe', 'php-cgi.pdb'],
default => throw new SPCInternalException("Deployment does not accept type {$type}"), default => throw new SPCInternalException("Deployment does not accept type {$type}"),
}; };
// with-upx-pack for cli and micro $src = "{$src[0]}\\{$src[1]}";
if ($this->getOption('with-upx-pack', false)) { $dst = BUILD_BIN_PATH . '\\' . basename($src);
if ($type === BUILD_TARGET_CLI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($src)); // file must exists
} if (!file_exists($src)) {
throw new SPCInternalException("Deploy failed. Cannot find file: {$src}");
}
// dst dir must exists
FileSystem::createDir(dirname($dst));
// copy file
if (realpath($src) !== realpath($dst)) {
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg($dst));
} }
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); // extract debug info in buildroot/debug
FileSystem::createDir(BUILD_ROOT_PATH . '\bin'); if ($this->getOption('no-strip', false) && file_exists("{$src[0]}\\{$src[2]}")) {
cmd()->exec('copy ' . escapeshellarg("{$src[0]}\\{$src[2]}") . ' ' . escapeshellarg($debug_dir));
}
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '\bin\\')); // with-upx-pack for cli and micro
return true; if ($this->getOption('with-upx-pack', false)) {
if ($type === BUILD_TARGET_CLI || $type === BUILD_TARGET_CGI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($dst));
}
}
} }
/** /**

View File

@@ -33,7 +33,7 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)'); $this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)'); $this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
$this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)'); $this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
$this->addOption('build-cgi', null, null, 'Build cgi SAPI (not available on Windows)'); $this->addOption('build-cgi', null, null, 'Build cgi SAPI');
$this->addOption('build-all', null, null, 'Build all SAPI'); $this->addOption('build-all', null, null, 'Build all SAPI');
$this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug'); $this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit'); $this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
@@ -48,6 +48,7 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('with-upx-pack', null, null, 'Compress / pack binary using UPX tool (linux/windows only)'); $this->addOption('with-upx-pack', null, null, 'Compress / pack binary using UPX tool (linux/windows only)');
$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');
} }
public function handle(): int public function handle(): int
@@ -222,11 +223,9 @@ class BuildPHPCommand extends BuildCommand
// ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ---------- // ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ----------
$build_root_path = BUILD_ROOT_PATH; $build_root_path = BUILD_ROOT_PATH;
$cwd = getcwd();
$fixed = ''; $fixed = '';
$build_root_path = get_display_path($build_root_path);
if (!empty(getenv('SPC_FIX_DEPLOY_ROOT'))) { if (!empty(getenv('SPC_FIX_DEPLOY_ROOT'))) {
str_replace($cwd, '', $build_root_path);
$build_root_path = getenv('SPC_FIX_DEPLOY_ROOT') . '/' . basename($build_root_path);
$fixed = ' (host system)'; $fixed = ' (host system)';
} }
if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {

View File

@@ -22,7 +22,7 @@ class LinuxMuslCheck
public static function optionalCheck(): bool public static function optionalCheck(): bool
{ {
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class || return getenv('SPC_TOOLCHAIN') === MuslToolchain::class ||
(getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !SystemUtil::isMuslDist()); (getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !SystemUtil::isMuslDist() && !str_contains((string) getenv('SPC_TARGET'), 'gnu'));
} }
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */

View File

@@ -40,7 +40,7 @@ class LinuxToolCheckList
'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',
'patchelf', 'patchelf', 'file',
]; ];
public const TOOLS_ARCH = [ public const TOOLS_ARCH = [
@@ -82,14 +82,14 @@ class LinuxToolCheckList
return CheckResult::ok(); return CheckResult::ok();
} }
#[AsCheckItem('if cmake version >= 3.18', limit_os: 'Linux')] #[AsCheckItem('if cmake version >= 3.22', limit_os: 'Linux')]
public function checkCMakeVersion(): ?CheckResult public function checkCMakeVersion(): ?CheckResult
{ {
$ver = get_cmake_version(); $ver = get_cmake_version();
if ($ver === null) { if ($ver === null) {
return CheckResult::fail('Failed to get cmake version'); return CheckResult::fail('Failed to get cmake version');
} }
if (version_compare($ver, '3.18.0') < 0) { if (version_compare($ver, '3.22.0') < 0) {
return CheckResult::fail('cmake version is too low (' . $ver . '), please update it manually!'); return CheckResult::fail('cmake version is too low (' . $ver . '), please update it manually!');
} }
return CheckResult::ok($ver); return CheckResult::ok($ver);
@@ -112,7 +112,7 @@ class LinuxToolCheckList
public function fixBuildTools(array $distro, array $missing): bool public function fixBuildTools(array $distro, array $missing): bool
{ {
$install_cmd = match ($distro['dist']) { $install_cmd = match ($distro['dist']) {
'ubuntu', 'debian', 'Deepin' => 'apt-get install -y', 'ubuntu', 'debian', 'Deepin', 'neon' => 'apt-get install -y',
'alpine' => 'apk add', 'alpine' => 'apk add',
'redhat' => 'dnf install -y', 'redhat' => 'dnf install -y',
'centos' => 'yum install -y', 'centos' => 'yum install -y',
@@ -128,7 +128,7 @@ class LinuxToolCheckList
logger()->warning('Current user (' . $user . ') is not root, using sudo for running command (may require password input)'); logger()->warning('Current user (' . $user . ') is not root, using sudo for running command (may require password input)');
} }
$is_debian = in_array($distro['dist'], ['debian', 'ubuntu', 'Deepin']); $is_debian = in_array($distro['dist'], ['debian', 'ubuntu', 'Deepin', 'neon']);
$to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing; $to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing;
// debian, alpine libtool -> libtoolize // debian, alpine libtool -> libtoolize
$to_install = str_replace('libtoolize', 'libtool', $to_install); $to_install = str_replace('libtoolize', 'libtool', $to_install);

View File

@@ -137,13 +137,18 @@ class ExceptionHandler
self::logError("\n----------------------------------------\n"); self::logError("\n----------------------------------------\n");
self::logError('⚠ The ' . ConsoleColor::cyan('console output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none(SPC_OUTPUT_LOG)); // convert log file path if in docker
$spc_log_convert = get_display_path(SPC_OUTPUT_LOG);
$shell_log_convert = get_display_path(SPC_SHELL_LOG);
$spc_logs_dir_convert = get_display_path(SPC_LOGS_DIR);
self::logError('⚠ The ' . ConsoleColor::cyan('console output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none($spc_log_convert));
if (file_exists(SPC_SHELL_LOG)) { if (file_exists(SPC_SHELL_LOG)) {
self::logError('⚠ The ' . ConsoleColor::cyan('shell output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none(SPC_SHELL_LOG)); self::logError('⚠ The ' . ConsoleColor::cyan('shell output log') . ConsoleColor::red(' is saved in ') . ConsoleColor::none($shell_log_convert));
} }
if ($e->getExtraLogFiles() !== []) { if ($e->getExtraLogFiles() !== []) {
foreach ($e->getExtraLogFiles() as $key => $file) { foreach ($e->getExtraLogFiles() as $key => $file) {
self::logError("⚠ Log file [{$key}] is saved in: " . ConsoleColor::none(SPC_LOGS_DIR . "/{$file}")); self::logError("⚠ Log file [{$key}] is saved in: " . ConsoleColor::none("{$spc_logs_dir_convert}/{$file}"));
} }
} }
if (!defined('DEBUG_MODE')) { if (!defined('DEBUG_MODE')) {
@@ -154,7 +159,9 @@ class ExceptionHandler
public static function handleDefaultException(\Throwable $e): void public static function handleDefaultException(\Throwable $e): void
{ {
$class = get_class($e); $class = get_class($e);
self::logError("✗ Unhandled exception {$class}:\n\t{$e->getMessage()}\n"); $file = $e->getFile();
$line = $e->getLine();
self::logError("✗ Unhandled exception {$class} on {$file} line {$line}:\n\t{$e->getMessage()}\n");
self::logError('Stack trace:'); self::logError('Stack trace:');
self::logError(ConsoleColor::gray($e->getTraceAsString()) . PHP_EOL, 4); self::logError(ConsoleColor::gray($e->getTraceAsString()) . PHP_EOL, 4);
self::logError('⚠ Please report this exception to: https://github.com/crazywhalecc/static-php-cli/issues'); self::logError('⚠ Please report this exception to: https://github.com/crazywhalecc/static-php-cli/issues');

95
src/SPC/store/DirDiff.php Normal file
View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace SPC\store;
/**
* A util class to diff directory file increments.
*/
class DirDiff
{
protected array $before = [];
protected array $before_file_hashes = [];
public function __construct(protected string $dir, protected bool $track_content_changes = false)
{
$this->reset();
}
/**
* Reset the baseline to current state.
*/
public function reset(): void
{
$this->before = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
if ($this->track_content_changes) {
$this->before_file_hashes = [];
foreach ($this->before as $file) {
$this->before_file_hashes[$file] = md5_file($this->dir . DIRECTORY_SEPARATOR . $file);
}
}
}
/**
* Get the list of incremented files.
*
* @param bool $relative Return relative paths or absolute paths
* @return array<string> List of incremented files
*/
public function getIncrementFiles(bool $relative = false): array
{
$after = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
$diff = array_diff($after, $this->before);
if ($relative) {
return $diff;
}
return array_map(fn ($f) => $this->dir . DIRECTORY_SEPARATOR . $f, $diff);
}
/**
* Get the list of changed files (including new files).
*
* @param bool $relative Return relative paths or absolute paths
* @param bool $include_new_files Include new files as changed files
* @return array<string> List of changed files
*/
public function getChangedFiles(bool $relative = false, bool $include_new_files = true): array
{
$after = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
$changed = [];
foreach ($after as $file) {
if (isset($this->before_file_hashes[$file])) {
$after_hash = md5_file($this->dir . DIRECTORY_SEPARATOR . $file);
if ($after_hash !== $this->before_file_hashes[$file]) {
$changed[] = $file;
}
} elseif ($include_new_files) {
// New file, consider as changed
$changed[] = $file;
}
}
if ($relative) {
return $changed;
}
return array_map(fn ($f) => $this->dir . DIRECTORY_SEPARATOR . $f, $changed);
}
/**
* Get the list of removed files.
*
* @param bool $relative Return relative paths or absolute paths
* @return array<string> List of removed files
*/
public function getRemovedFiles(bool $relative = false): array
{
$after = FileSystem::scanDirFiles($this->dir, relative: true) ?: [];
$removed = array_diff($this->before, $after);
if ($relative) {
return $removed;
}
return array_map(fn ($f) => $this->dir . DIRECTORY_SEPARATOR . $f, $removed);
}
}

View File

@@ -16,6 +16,42 @@ use SPC\util\SPCTarget;
*/ */
class Downloader class Downloader
{ {
/**
* Get latest version from PIE config (Packagist)
*
* @param string $name Source name
* @param array $source Source meta info: [repo]
* @return array<int, string> [url, filename]
*/
public static function getPIEInfo(string $name, array $source): array
{
$packagist_url = "https://repo.packagist.org/p2/{$source['repo']}.json";
logger()->debug("Fetching {$name} source from packagist index: {$packagist_url}");
$data = json_decode(self::curlExec(
url: $packagist_url,
retries: self::getRetryAttempts()
), true);
if (!isset($data['packages'][$source['repo']]) || !is_array($data['packages'][$source['repo']])) {
throw new DownloaderException("failed to find {$name} repo info from packagist");
}
// get the first version
$first = $data['packages'][$source['repo']][0] ?? [];
// check 'type' => 'php-ext' or contains 'php-ext' key
if (!isset($first['php-ext'])) {
throw new DownloaderException("failed to find {$name} php-ext info from packagist, maybe not a php extension package");
}
// get download link from dist
$dist_url = $first['dist']['url'] ?? null;
$dist_type = $first['dist']['type'] ?? null;
if (!$dist_url || !$dist_type) {
throw new DownloaderException("failed to find {$name} dist info from packagist");
}
$name = str_replace('/', '_', $source['repo']);
$version = $first['version'] ?? 'unknown';
// file name use: $name-$version.$dist_type
return [$dist_url, "{$name}-{$version}.{$dist_type}"];
}
/** /**
* Get latest version from BitBucket tag * Get latest version from BitBucket tag
* *
@@ -317,84 +353,7 @@ class Downloader
if (self::isAlreadyDownloaded($name, $force, SPC_DOWNLOAD_PACKAGE)) { if (self::isAlreadyDownloaded($name, $force, SPC_DOWNLOAD_PACKAGE)) {
return; return;
} }
self::downloadByType($pkg['type'], $name, $pkg, $force, SPC_DOWNLOAD_PACKAGE);
try {
switch ($pkg['type']) {
case 'bitbuckettag': // BitBucket Tag
[$url, $filename] = self::getLatestBitbucketTag($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
break;
case 'ghtar': // GitHub Release (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'ghtagtar': // GitHub Tag (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $pkg, 'tags');
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'ghrel': // GitHub Release (uploaded)
[$url, $filename] = self::getLatestGithubRelease($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
break;
case 'filelist': // Basic File List (regex based crawler)
[$url, $filename] = self::getFromFileList($name, $pkg);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
break;
case 'url': // Direct download URL
$url = $pkg['url'];
$filename = $pkg['filename'] ?? basename($pkg['url']);
self::downloadFile($name, $url, $filename, $pkg['extract'] ?? null, SPC_DOWNLOAD_PACKAGE);
break;
case 'git': // Git repo
self::downloadGit(
$name,
$pkg['url'],
$pkg['rev'],
$pkg['submodules'] ?? null,
$pkg['extract'] ?? null,
self::getRetryAttempts(),
SPC_DOWNLOAD_PRE_BUILT
);
break;
case 'local':
// Local directory, do nothing, just lock it
logger()->debug("Locking local source {$name}");
LockFile::lockSource($name, [
'source_type' => SPC_SOURCE_LOCAL,
'dirname' => $pkg['dirname'],
'move_path' => $pkg['extract'] ?? null,
'lock_as' => SPC_DOWNLOAD_PACKAGE,
]);
break;
case 'custom': // Custom download method, like API-based download or other
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
if (isset($pkg['func']) && is_callable($pkg['func'])) {
$pkg['name'] = $name;
$pkg['func']($force, $pkg, SPC_DOWNLOAD_PACKAGE);
break;
}
foreach ($classes as $class) {
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
$cls = new $class();
if (in_array($name, $cls->getSupportName())) {
(new $class())->fetch($name, $force, $pkg);
break;
}
}
}
break;
default:
throw new DownloaderException('unknown source type: ' . $pkg['type']);
}
} catch (\Throwable $e) {
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
logger()->warning('Deleting download file: ' . $filename);
unlink(DOWNLOAD_PATH . '/' . $filename);
}
throw new DownloaderException('Download failed! ' . $e->getMessage());
}
} }
/** /**
@@ -439,80 +398,7 @@ class Downloader
return; return;
} }
try { self::downloadByType($source['type'], $name, $source, $force, $download_as);
switch ($source['type']) {
case 'bitbuckettag': // BitBucket Tag
[$url, $filename] = self::getLatestBitbucketTag($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
break;
case 'ghtar': // GitHub Release (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'ghtagtar': // GitHub Tag (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags');
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'ghrel': // GitHub Release (uploaded)
[$url, $filename] = self::getLatestGithubRelease($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
break;
case 'filelist': // Basic File List (regex based crawler)
[$url, $filename] = self::getFromFileList($name, $source);
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
break;
case 'url': // Direct download URL
$url = $source['url'];
$filename = $source['filename'] ?? basename($source['url']);
self::downloadFile($name, $url, $filename, $source['path'] ?? null, $download_as);
break;
case 'git': // Git repo
self::downloadGit(
$name,
$source['url'],
$source['rev'],
$source['submodules'] ?? null,
$source['path'] ?? null,
self::getRetryAttempts(),
$download_as
);
break;
case 'local':
// Local directory, do nothing, just lock it
logger()->debug("Locking local source {$name}");
LockFile::lockSource($name, [
'source_type' => SPC_SOURCE_LOCAL,
'dirname' => $source['dirname'],
'move_path' => $source['extract'] ?? null,
'lock_as' => $download_as,
]);
break;
case 'custom': // Custom download method, like API-based download or other
if (isset($source['func']) && is_callable($source['func'])) {
$source['name'] = $name;
$source['func']($force, $source, $download_as);
break;
}
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
foreach ($classes as $class) {
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
(new $class())->fetch($force, $source, $download_as);
break;
}
}
break;
default:
throw new DownloaderException('unknown source type: ' . $source['type']);
}
} catch (\Throwable $e) {
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
logger()->warning('Deleting download file: ' . $filename);
unlink(DOWNLOAD_PATH . '/' . $filename);
}
throw new DownloaderException('Download failed! ' . $e->getMessage());
}
} }
/** /**
@@ -713,4 +599,109 @@ class Downloader
} }
return false; return false;
} }
/**
* Download by type.
*
* @param string $type Types
* @param string $name Download item name
* @param array{
* url?: string,
* repo?: string,
* rev?: string,
* path?: string,
* filename?: string,
* dirname?: string,
* match?: string,
* prefer-stable?: bool,
* extract?: string,
* submodules?: array<string>,
* provide-pre-built?: bool,
* func?: ?callable,
* license?: array
* } $conf Download item config
* @param bool $force Force download
* @param int $download_as Lock source type
*/
private static function downloadByType(string $type, string $name, array $conf, bool $force, int $download_as): void
{
try {
switch ($type) {
case 'pie': // Packagist
[$url, $filename] = self::getPIEInfo($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'bitbuckettag': // BitBucket Tag
[$url, $filename] = self::getLatestBitbucketTag($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
break;
case 'ghtar': // GitHub Release (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'ghtagtar': // GitHub Tag (tar)
[$url, $filename] = self::getLatestGithubTarball($name, $conf, 'tags');
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);
break;
case 'ghrel': // GitHub Release (uploaded)
[$url, $filename] = self::getLatestGithubRelease($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, ['Accept: application/octet-stream'], [[CurlHook::class, 'setupGithubToken']]);
break;
case 'filelist': // Basic File List (regex based crawler)
[$url, $filename] = self::getFromFileList($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
break;
case 'url': // Direct download URL
$url = $conf['url'];
$filename = $conf['filename'] ?? basename($conf['url']);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
break;
case 'git': // Git repo
self::downloadGit($name, $conf['url'], $conf['rev'], $conf['submodules'] ?? null, $conf['path'] ?? $conf['extract'] ?? null, self::getRetryAttempts(), $download_as);
break;
case 'local': // Local directory, do nothing, just lock it
LockFile::lockSource($name, [
'source_type' => SPC_SOURCE_LOCAL,
'dirname' => $conf['dirname'],
'move_path' => $conf['path'] ?? $conf['extract'] ?? null,
'lock_as' => $download_as,
]);
break;
case 'custom': // Custom download method, like API-based download or other
if (isset($conf['func'])) {
$conf['name'] = $name;
$conf['func']($force, $conf, $download_as);
break;
}
$classes = [
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source'),
...FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'),
];
foreach ($classes as $class) {
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
(new $class())->fetch($force, $conf, $download_as);
break;
}
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
$cls = new $class();
if (in_array($name, $cls->getSupportName())) {
(new $class())->fetch($name, $force, $conf);
break;
}
}
}
break;
default:
throw new DownloaderException("Unknown download type: {$type}");
}
} catch (\Throwable $e) {
// Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
logger()->warning("Deleting download file: {$filename}");
unlink(DOWNLOAD_PATH . '/' . $filename);
}
throw new DownloaderException("Download failed: {$e->getMessage()}");
}
}
} }

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