Compare commits

..

219 Commits

Author SHA1 Message Date
Jerry Ma
fafe7d5778 Merge pull request #835 from crazywhalecc/chore/test-and-validate
Chore: PHPUnit test & docs & PHPDoc for vendor mode
2025-07-29 11:08:53 +08:00
crazywhalecc
6c3ff7da32 Change LDFLAGS_PROGRAM to hard-coded 2025-07-25 12:58:19 +08:00
crazywhalecc
a375ab9780 Fix custom pkg download callable not working bug 2025-07-25 12:58:19 +08:00
crazywhalecc
f47c3a2d20 Fix macOS missing lib when linking 2025-07-25 12:58:19 +08:00
Jerry Ma
f500945298 Merge pull request #833 from crazywhalecc/feat/install-pkg-options
Add --no-alt and --skip-extract option to install-pkg command
2025-07-24 16:35:15 +08:00
crazywhalecc
52a623f31e Suggestions 2025-07-24 12:00:17 +08:00
crazywhalecc
abd7d3cfb4 Add --no-alt and --skip-extract option to install-pkg command 2025-07-24 00:42:30 +08:00
Jerry Ma
f543b55f19 Merge pull request #826 from crazywhalecc/grpc
fix grpc support (use cmake, openssl)
2025-07-23 22:03:55 +08:00
crazywhalecc
f2a3ae409f Use latest minimal micro.sfx for self-packing 2025-07-23 21:37:38 +08:00
DubbleClick
4e4cf4bbc7 fix weird macos thing?! 2025-07-23 17:33:49 +07:00
DubbleClick
d532b57810 use mbstring extension everywhere 2025-07-23 17:21:32 +07:00
DubbleClick
aa337c0c84 space ._. 2025-07-23 16:51:35 +07:00
DubbleClick
cc1f899948 re2.pc didn't have prefix listed 2025-07-23 16:50:26 +07:00
DubbleClick
848c30547c Merge branch 'main' of https://github.com/crazywhalecc/static-php-cli into grpc 2025-07-23 16:25:12 +07:00
Marc
c8cc0131da Merge pull request #831 from crazywhalecc/fix/varname-placeholder
Change variable name for placeholder
2025-07-23 16:24:56 +07:00
crazywhalecc
b0c0ecb59c Rename to suggestions 2025-07-23 15:33:26 +08:00
crazywhalecc
45f257ef14 Change variable name for placeholder 2025-07-23 15:13:38 +08:00
DubbleClick
fa38fa1410 easier debugging for breakpoints 2025-07-23 14:13:22 +07:00
DubbleClick
ee492dcde1 patch pc files 2025-07-23 14:10:28 +07:00
DubbleClick
22bec873a8 more merge stuff 2025-07-23 13:55:09 +07:00
DubbleClick
958fd0cdcb Merge branch 'main' of https://github.com/crazywhalecc/static-php-cli into grpc 2025-07-23 13:51:33 +07:00
Marc
0d0c789d8b Merge pull request #828 from crazywhalecc/feat/pkg-config-integration
Add real pkg-config integration
2025-07-23 13:49:36 +07:00
crazywhalecc
3d19299d30 Patch both 2025-07-23 14:35:34 +08:00
DubbleClick
4c56c96326 lib-suggests: ldap was missing for curl 2025-07-23 13:16:23 +07:00
crazywhalecc
42e2f6a89d Update composer deps, trigger extension test 2025-07-23 12:49:13 +08:00
Jerry Ma
c715f20fe3 Merge pull request #830 from crazywhalecc/fix/pack-location-independent
Patch pkg-config and la files with placeholder when packing pre-built content
2025-07-23 12:38:29 +08:00
crazywhalecc
e02ce4c151 Fix gnu docker openssl build 2025-07-23 12:15:18 +08:00
crazywhalecc
9f7a7a5703 Add packing placeholder 2025-07-23 11:52:25 +08:00
crazywhalecc
147fd396cf Fix openssl pc file 2025-07-23 11:52:05 +08:00
crazywhalecc
293a95da6d Use absolute path for pc files 2025-07-23 11:51:28 +08:00
DubbleClick
952ecb2eec test all affected operating systems! 2025-07-23 10:50:29 +07:00
DubbleClick
31e7cd31c1 make sure that mimalloc.o is *always* first, no matter if $_ENV['LIBS'] is set 2025-07-23 10:49:23 +07:00
DubbleClick
51ec8199e4 bring back static lib logic 2025-07-23 10:46:30 +07:00
DubbleClick
f75c58b7d2 revert to double array_reverse, because static libs with dependencies are backwards 2025-07-23 10:21:36 +07:00
DubbleClick
f8970a21be fix cs 2025-07-23 09:53:31 +07:00
DubbleClick
b6be20727d fix a few accidental array_reverse calls, pkg-config --static instead of pkg-config --libs 2025-07-23 09:47:35 +07:00
crazywhalecc
9c4a6b46b6 Fix pkg-config missing option 2025-07-23 09:57:09 +08:00
crazywhalecc
779c7cec30 test pgsql 2025-07-23 01:32:42 +08:00
crazywhalecc
154a21fbaf phpstan, extension test 2025-07-23 00:48:37 +08:00
crazywhalecc
a2e0efaed3 extension test 2025-07-23 00:45:06 +08:00
crazywhalecc
0d408e0df1 Allow empty target 2025-07-23 00:38:36 +08:00
crazywhalecc
b5ceda5955 Fix libxslt build again 2025-07-23 00:38:25 +08:00
crazywhalecc
ffa5219d23 trigger extension test 2025-07-22 23:18:19 +08:00
crazywhalecc
099425abc8 Fix c++, check pkg-config exists before calling PkgConfigUtil 2025-07-22 22:46:13 +08:00
crazywhalecc
dea3c4afeb Looks like ngtcp2 does not happy with pkg-config 2025-07-22 22:25:46 +08:00
crazywhalecc
4cdafe2497 Fix unique and reverse order 2025-07-22 22:25:21 +08:00
crazywhalecc
a88f9933d2 Fix unique and reverse order 2025-07-22 22:25:16 +08:00
crazywhalecc
a095d489f3 Fix libwebp static libs order 2025-07-22 22:09:45 +08:00
crazywhalecc
2c81ca2bf8 Replace example.com 2025-07-22 22:00:39 +08:00
crazywhalecc
f2ccabf062 Fix library base, fix brotli and curl libs, trigger extension test 2025-07-22 21:54:27 +08:00
crazywhalecc
6dd4406782 Disable extension test's UPX 2025-07-22 21:19:11 +08:00
crazywhalecc
062cc84d51 Fix phpunit 2025-07-22 21:15:22 +08:00
crazywhalecc
dfbc9a7fff Trigger common extension test 2025-07-22 21:14:25 +08:00
crazywhalecc
1888b735d1 Fix all extension builds 2025-07-22 21:13:35 +08:00
Marc
a44dc0a5f6 cd later 2025-07-22 19:45:58 +07:00
crazywhalecc
6634ade6b4 Fix library build failure not interrupt bug 2025-07-22 20:41:35 +08:00
DubbleClick
6962d24b52 unify CMakeExecutor and AutoConfExecutor 2025-07-22 19:12:17 +07:00
DubbleClick
eb56690684 use escapeshellarg 2025-07-22 19:03:16 +07:00
DubbleClick
4b7020fbbb also escape if it contains a space 2025-07-22 19:01:46 +07:00
DubbleClick
f9a8231820 simpler check 2025-07-22 19:01:07 +07:00
crazywhalecc
7e977414a7 Use spc-config instead of SPC_EXTRA_LIBS, trigger extension test 2025-07-22 19:59:44 +08:00
DubbleClick
c96d503dae fix cs 2025-07-22 18:45:28 +07:00
DubbleClick
8c603d59c6 simplify path 2025-07-22 18:42:34 +07:00
DubbleClick
bdb705e89a only escape path if required 2025-07-22 18:38:17 +07:00
DubbleClick
9172c39a26 change --with-prefix to --with-path, add --enable-path (yes, grpc uses that...) 2025-07-22 18:29:58 +07:00
DubbleClick
3ca7c15208 simplify back to --enable, it actually supports paths :O 2025-07-22 18:21:33 +07:00
DubbleClick
cfda1e93a0 add macos ldflags 2025-07-22 17:17:11 +07:00
DubbleClick
5aa9255909 cs fix 2025-07-22 16:27:05 +07:00
DubbleClick
107fb08e34 simplify patch logic for grpc (--enable-grpc=PATH does not actually pass a path, needs to be --with-grpc=PATH) 2025-07-22 16:26:37 +07:00
crazywhalecc
95a94da66f Add --no-php option to spc-config 2025-07-22 17:26:34 +08:00
crazywhalecc
c8eb62e8f0 Add real pkg-config integration 2025-07-22 17:23:13 +08:00
DubbleClick
4010a84d88 set up tmate 2025-07-22 15:00:22 +07:00
crazywhalecc
c0db3a69d7 Update composer deps 2025-07-22 14:46:51 +08:00
crazywhalecc
5dbf401615 Fix macOS libphp.dylib compatibility 2025-07-22 14:46:41 +08:00
crazywhalecc
c5811ae947 Reduce grpc building things 2025-07-22 14:46:28 +08:00
crazywhalecc
e1595e1091 Remove old workaround code 2025-07-22 14:46:01 +08:00
crazywhalecc
20ab4afb4b Merge branch 'main' into grpc
# Conflicts:
#	src/globals/test-extensions.php
2025-07-22 13:57:31 +08:00
Marc
a98f72cc32 Merge pull request #824 from crazywhalecc/tiff
add Lerc, JBIG and JXL libraries for use in imagick
2025-07-22 12:46:45 +07:00
DubbleClick
9c9ffb615f cs fix 2025-07-22 12:07:03 +07:00
DubbleClick
62a58d410f describe centos 7 behaviour better 2025-07-22 11:49:13 +07:00
DubbleClick
9e7e2e30a4 goddamn centos 7 2025-07-22 11:48:12 +07:00
DubbleClick
22552262b3 add different build dir in again 2025-07-22 11:43:19 +07:00
DubbleClick
307d3dda20 join array to string 2025-07-22 11:30:00 +07:00
DubbleClick
6594f6b653 fix remaining errors 2025-07-22 11:23:42 +07:00
DubbleClick
0942637d12 Merge remote-tracking branch 'origin/grpc' into grpc
# Conflicts:
#	src/SPC/builder/extension/grpc.php
#	src/SPC/builder/unix/library/grpc.php
2025-07-22 10:55:48 +07:00
DubbleClick
bb96db4395 rework static lib detection 2025-07-22 10:54:44 +07:00
DubbleClick
f31f0d6168 should be fixed properly 2025-07-22 10:48:14 +07:00
DubbleClick
1f4a3e4b8f oh, it will likely be this instead 2025-07-22 10:48:14 +07:00
DubbleClick
b30f2bc193 for some reason alpine docker fails in CI, but not locally, after I added this? 2025-07-22 10:48:14 +07:00
DubbleClick
b43d0746f4 cs fix 2025-07-22 10:48:14 +07:00
DubbleClick
227210209c -static-libstdc++ and -static-libgcc for static builds (musl toolchain) 2025-07-22 10:48:14 +07:00
DubbleClick
3dd3cf2814 use pkgconfig to determine libs fix cs 2025-07-22 10:48:14 +07:00
DubbleClick
e98e221d93 use pkgconfig to determine libs 2025-07-22 10:48:14 +07:00
DubbleClick
81a59be0c4 use =package 2025-07-22 10:48:14 +07:00
DubbleClick
8eb7723114 enable tests 2025-07-22 10:48:13 +07:00
DubbleClick
45d96734dd grpc can be built shared too 2025-07-22 10:48:13 +07:00
DubbleClick
915ffd84c8 remove grpc_php_ext_src 2025-07-22 10:48:13 +07:00
DubbleClick
161a92aff7 needed --libs --static 2025-07-22 10:48:13 +07:00
DubbleClick
9b207807eb doesn't even work yet, there has to be a better way than to add 2913912309 libraries 2025-07-22 10:48:13 +07:00
DubbleClick
0bdafed92f fix cs 2025-07-22 10:47:35 +07:00
DubbleClick
2ebd230a08 use git instead of patching deps.sh 2025-07-22 10:43:51 +07:00
DubbleClick
6d60000952 Merge branch 'tiff' of https://github.com/crazywhalecc/static-php-cli into tiff 2025-07-22 10:26:25 +07:00
DubbleClick
1b6422eed3 suggestion 2 2025-07-22 10:22:53 +07:00
DubbleClick
a643c44ac6 suggestion 1 2025-07-22 10:22:53 +07:00
DubbleClick
02732036de test shared imagick.so build 2025-07-22 10:22:53 +07:00
DubbleClick
c0483b4b34 don't download libpng, zlib, brotli, as we already build them 2025-07-22 10:22:53 +07:00
DubbleClick
eff6c0666b disable jni instead of sjpeg 2025-07-22 10:22:53 +07:00
DubbleClick
ed97669d9e force usage of already built brotli lib 2025-07-22 10:22:52 +07:00
DubbleClick
ddb14e2db7 install libatomic-devel for devtoolset-10 2025-07-22 10:22:52 +07:00
DubbleClick
c452252a7d add jxl to test 2025-07-22 10:22:52 +07:00
DubbleClick
6fcd301cf5 fix 2025-07-22 10:22:52 +07:00
DubbleClick
52862a3d63 fix libtiff libs being defined after configure 2025-07-22 10:22:52 +07:00
DubbleClick
ff092b4e9d add libjxl #755 2025-07-22 10:22:52 +07:00
DubbleClick
0d6453751d fixes 2025-07-22 10:22:52 +07:00
Marc
082b9a907b Update src/SPC/builder/unix/library/jbig.php
Co-authored-by: Jerry Ma <jesse2061@outlook.com>
2025-07-22 10:22:52 +07:00
DubbleClick
5675a1e2ad Revert "only uncomment that line temporarily"
This reverts commit d07fadde76.
2025-07-22 10:22:52 +07:00
DubbleClick
8601c26f2d only uncomment that line temporarily 2025-07-22 10:22:52 +07:00
DubbleClick
ece7a04437 oops 2025-07-22 10:22:52 +07:00
DubbleClick
9d2d3d2d0a test
# Conflicts:
#	src/globals/test-extensions.php
2025-07-22 10:22:48 +07:00
DubbleClick
cec445eab5 sort 2025-07-22 10:21:09 +07:00
DubbleClick
8116e8898e fix lerc build (needs -lstdc++) 2025-07-22 10:21:09 +07:00
DubbleClick
be0dbcd9eb add jbig 2025-07-22 10:21:09 +07:00
DubbleClick
5c44703f53 add lerc 2025-07-22 10:21:09 +07:00
Marc
c364970e05 Merge pull request #827 from crazywhalecc/feat/git-submodules
Allow specifying submodules for git source
2025-07-22 10:19:30 +07:00
Jerry Ma
b2385cf80c Update src/SPC/store/Downloader.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-21 23:33:20 +08:00
Jerry Ma
2b1d03d9d4 Update src/SPC/store/Downloader.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-21 23:29:38 +08:00
crazywhalecc
5b6f33a2a1 Allow specifying submodules for git source 2025-07-21 23:21:00 +08:00
Marc
352955d608 absolute dir? 2025-07-21 21:36:33 +07:00
Marc
e07b281577 fix build dir conflict 2025-07-21 21:25:33 +07:00
Marc
71f4e45256 remove workaround 2025-07-21 21:22:41 +07:00
DubbleClick
4a0a7e0df3 suggestion 2 2025-07-21 19:34:31 +07:00
DubbleClick
3c8b588312 suggestion 1 2025-07-21 19:32:44 +07:00
DubbleClick
d66d7b3a73 should be fixed properly 2025-07-21 16:56:39 +07:00
DubbleClick
024542d361 oh, it will likely be this instead 2025-07-21 16:03:28 +07:00
DubbleClick
7d85aacace for some reason alpine docker fails in CI, but not locally, after I added this? 2025-07-21 16:00:28 +07:00
DubbleClick
31b2e6779d cs fix 2025-07-21 14:29:27 +07:00
DubbleClick
2b39a6d535 -static-libstdc++ and -static-libgcc for static builds (musl toolchain) 2025-07-21 14:29:01 +07:00
DubbleClick
e490a1763a use pkgconfig to determine libs fix cs 2025-07-21 12:12:18 +07:00
DubbleClick
bf4b35aa0a use pkgconfig to determine libs 2025-07-21 12:10:14 +07:00
DubbleClick
1f7d3ec91d use =package 2025-07-21 10:55:21 +07:00
DubbleClick
23412d6bfa enable tests 2025-07-21 09:46:06 +07:00
DubbleClick
bd0042f079 grpc can be built shared too 2025-07-21 09:43:01 +07:00
DubbleClick
0ccf1a1c5d remove grpc_php_ext_src 2025-07-21 09:41:36 +07:00
DubbleClick
76d7002646 needed --libs --static 2025-07-21 09:33:29 +07:00
DubbleClick
38cc4cfe7e doesn't even work yet, there has to be a better way than to add 2913912309 libraries 2025-07-21 00:32:43 +07:00
DubbleClick
d91b476f33 test shared imagick.so build 2025-07-20 10:37:37 +07:00
DubbleClick
f7080c61d5 don't download libpng, zlib, brotli, as we already build them 2025-07-20 02:49:30 +07:00
DubbleClick
26842fba27 disable jni instead of sjpeg 2025-07-20 02:17:00 +07:00
DubbleClick
dce1065af5 force usage of already built brotli lib 2025-07-20 02:06:43 +07:00
DubbleClick
f0af17abe4 install libatomic-devel for devtoolset-10 2025-07-20 01:44:09 +07:00
DubbleClick
fbd56da7ad add jxl to test 2025-07-20 01:21:33 +07:00
DubbleClick
0ef8b8c175 fix 2025-07-20 01:17:58 +07:00
DubbleClick
230879a0db fix libtiff libs being defined after configure 2025-07-20 01:13:51 +07:00
DubbleClick
2ef64e4597 add libjxl #755 2025-07-20 00:11:29 +07:00
DubbleClick
6c18862fd1 fixes 2025-07-19 23:54:38 +07:00
Marc
6c109c52f6 Update src/SPC/builder/unix/library/jbig.php
Co-authored-by: Jerry Ma <jesse2061@outlook.com>
2025-07-19 17:56:54 +07:00
Jerry Ma
f3400b8756 Merge pull request #822 from yoramdelangen/fix/workflow-reuse-upload-license-conflicts
fix: conflict upload names when reusing workflow more then once
2025-07-19 18:37:08 +08:00
DubbleClick
c7762a5b65 Revert "only uncomment that line temporarily"
This reverts commit d07fadde76.
2025-07-18 20:44:04 +07:00
DubbleClick
d07fadde76 only uncomment that line temporarily 2025-07-18 20:30:51 +07:00
DubbleClick
ac7a09edd1 oops 2025-07-18 14:52:39 +07:00
Marc
f30f095f00 Merge branch 'main' into tiff 2025-07-18 14:47:59 +07:00
DubbleClick
d91a66ee8d test 2025-07-18 14:47:05 +07:00
DubbleClick
7db7ed97ee sort 2025-07-18 14:41:10 +07:00
DubbleClick
6b9d49eb71 fix lerc build (needs -lstdc++) 2025-07-18 14:39:17 +07:00
DubbleClick
7c9e8e3e40 add jbig 2025-07-18 14:04:55 +07:00
DubbleClick
5586f627d3 add lerc 2025-07-18 13:51:33 +07:00
Marc
e4921087d9 Merge pull request #823 from crazywhalecc/fix/curl-patch
Fix curl build for non-windows
2025-07-18 12:37:38 +07:00
crazywhalecc
005b78413c Fix curl build for non-windows 2025-07-18 13:23:46 +08:00
Yoram
0a1677af95 add build-meta to use php-version and os for artifacts upload name 2025-07-17 10:19:54 +02:00
Yoram
7ffbb10587 fix: conflict upload names when reusing workflow more then once 2025-07-17 09:48:22 +02:00
Jerry Ma
3d45890a2d Merge pull request #821 from crazywhalecc/fix/windows-curl-sspi
Fix/windows curl sspi
2025-07-17 13:58:31 +08:00
Jerry Ma
bb5eb64b28 Merge pull request #802 from crazywhalecc/feat/safe-pack
Check lib-suggest before packing libs
2025-07-17 13:34:15 +08:00
crazywhalecc
e6a050de82 Use SPC_EXTRA_LIBS instead of SPC_WINDOWS_EXEC_LIBS 2025-07-17 13:14:18 +08:00
crazywhalecc
7e7eb844a7 cs-fix 2025-07-17 12:26:30 +08:00
crazywhalecc
e56c628bec Add windows tests 2025-07-17 12:23:03 +08:00
crazywhalecc
c604f88685 Disallow alt for php-src 2025-07-17 12:21:50 +08:00
crazywhalecc
83db186972 Fix curl static build for Windows 2025-07-17 12:21:13 +08:00
Jerry Ma
9af3b745a2 Merge pull request #792 from crazywhalecc/fix/ext-imap-extraction
Skip re-extract ext-imap source
2025-07-10 21:39:34 +08:00
crazywhalecc
a5cd5fcb31 Fix test and bump docker image version 2025-07-10 20:20:29 +08:00
crazywhalecc
ec153fa6ff Workaround for swoole with alpine 3.21 2025-07-10 20:10:54 +08:00
crazywhalecc
dd72b32559 Merge 2025-07-10 12:59:27 +08:00
Marc
a5d26d6517 Merge pull request #813 from crazywhalecc/fix-native
fix g++ not found error
2025-07-01 22:12:09 +07:00
DubbleClick
e5ea32e9c0 fix in case it's not set 2025-07-01 17:57:36 +07:00
DubbleClick
5bc4504e37 fix g++ not found error 2025-07-01 17:53:35 +07:00
Jerry Ma
4cb4c9c6a5 Merge pull request #812 from crazywhalecc/sponsor-badge
Add special sponsors
2025-06-30 22:09:36 +08:00
Jerry Ma
9190335a70 Merge pull request #810 from crazywhalecc/refactor/spc-target
refactor: replace SPC_LIBC with SPC_TARGET and update related logic
2025-06-30 21:49:01 +08:00
crazywhalecc
4e244789a2 Add frankenphp SAPI 2025-06-30 21:45:45 +08:00
crazywhalecc
6de7b8e63b Add special sponsors 2025-06-30 21:41:48 +08:00
Marc
68e500eea0 Merge branch 'main' into refactor/spc-target 2025-06-30 20:16:59 +07:00
Marc
840e09a684 Merge pull request #811 from crazywhalecc/fix/micro-patches
Fix/micro patches
2025-06-30 20:16:19 +07:00
crazywhalecc
d00a5223d3 Add exception for checking patch file exist 2025-06-30 19:45:26 +08:00
crazywhalecc
3965a899c7 Add missing directory separator 2025-06-30 19:41:32 +08:00
DubbleClick
956667bf08 fix alpine doctor saying gettext-dev isn't installed 2025-06-30 13:40:51 +07:00
DubbleClick
51e23d21cd remove w32 patches (other branch isn't merged yet) 2025-06-30 12:56:12 +07:00
crazywhalecc
44c6d6288e Refactor LinuxMuslCheck to use MuslToolchain class for toolchain check 2025-06-30 13:04:49 +08:00
Jerry Ma
e5848086c3 Update bin/spc-gnu-docker
Co-authored-by: Marc <m@pyc.ac>
2025-06-30 12:19:17 +08:00
Jerry Ma
138e5588e9 Update bin/spc-gnu-docker
Co-authored-by: Marc <m@pyc.ac>
2025-06-30 12:18:49 +08:00
crazywhalecc
ab5828a560 Remove redundant path in gnu docker 2025-06-30 09:05:56 +08:00
crazywhalecc
e1e48922de Use isStatic instead of isStaticTarget 2025-06-29 22:49:48 +08:00
crazywhalecc
956688d9f4 Fix CI again 2025-06-29 19:53:22 +08:00
crazywhalecc
7f45f4aeec Fix phpunit, add SPC_SKIP_TOOLCHAIN_CHECK 2025-06-29 19:48:09 +08:00
crazywhalecc
fcaa7c5f42 Add no-env-check for builder 2025-06-29 16:21:22 +08:00
crazywhalecc
e36800b951 phpunit fix 2025-06-29 16:03:38 +08:00
crazywhalecc
977fbaa8ef Suggestions 2025-06-29 16:00:17 +08:00
crazywhalecc
0598eff9c5 Add suffix support for SPC_TARGET 2025-06-28 23:13:40 +08:00
crazywhalecc
a6364389ba Add suffix support for SPC_TARGET 2025-06-28 23:11:26 +08:00
crazywhalecc
c23c5ae614 Disable openmp for imagemagick 2025-06-28 23:08:17 +08:00
crazywhalecc
454b5a77ad Add SPC_LIBC mapping to SPC_TARGET 2025-06-28 22:59:02 +08:00
crazywhalecc
8145a7536b Fix spc-gnu-docker path 2025-06-28 19:05:52 +08:00
crazywhalecc
e41d7899c7 Add comments, re-trigger tests 2025-06-28 18:14:10 +08:00
Jerry Ma
88d99a7dea Update src/SPC/store/SourcePatcher.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-28 18:04:45 +08:00
crazywhalecc
90823e3b17 Use explict glibc toolchain target 2025-06-28 17:28:02 +08:00
crazywhalecc
3357f286ab Remove libgomp deps 2025-06-28 17:13:22 +08:00
crazywhalecc
ad080da026 Refactor micro patching logic in SourcePatcher 2025-06-28 17:06:51 +08:00
crazywhalecc
25c2def710 Remove unnecessary SPC_MICRO_PATCHES 2025-06-28 16:47:19 +08:00
crazywhalecc
604131b31d Use own exception 2025-06-28 16:45:20 +08:00
crazywhalecc
924da6ae69 extension test 2025-06-28 16:39:02 +08:00
crazywhalecc
12aadf18cc refactor: replace SPC_LIBC with SPC_TARGET and update related logic 2025-06-28 16:36:05 +08:00
crazywhalecc
1409d1b47b Check lib-suggest before packing libs 2025-06-22 13:52:53 +08:00
131 changed files with 3889 additions and 1073 deletions

View File

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

View File

@@ -136,11 +136,4 @@ jobs:
- name: "Build library: ${{ matrix.library }}" - name: "Build library: ${{ matrix.library }}"
run: | run: |
SPC_USE_SUDO=yes ./bin/spc doctor --auto-fix SPC_USE_SUDO=yes ./bin/spc doctor --auto-fix
if [ "${{ env.OS }}" = "linux-x86_64" ]; then ./bin/spc build --build-cli --build-micro --build-fpm ${{ matrix.extension }} --debug --with-suggested-libs --with-suggested-exts
./bin/spc install-pkg upx
UPX=--with-upx-pack
elif [ "${{ env.OS }}" = "linux-aarch64" ]; then
./bin/spc install-pkg upx
UPX=--with-upx-pack
fi
./bin/spc build --build-cli --build-micro --build-fpm ${{ matrix.extension }} --debug $UPX --with-suggested-libs --with-suggested-exts

View File

@@ -10,7 +10,7 @@ on:
env: env:
PHP_VERSION: 8.4 PHP_VERSION: 8.4
MICRO_VERSION: 8.4.4 MICRO_VERSION: 8.4.10
jobs: jobs:
build-release-artifacts: build-release-artifacts:
@@ -42,12 +42,13 @@ jobs:
run: echo "SPC_BUILD_DEBUG=--debug" >> $GITHUB_ENV run: echo "SPC_BUILD_DEBUG=--debug" >> $GITHUB_ENV
- name: "Install PHP for official runners" - name: "Install PHP for official runners"
uses: "shivammathur/setup-php@v2" uses: shivammathur/setup-php@v2
with: with:
coverage: none coverage: none
tools: composer:v2 tools: composer:v2
php-version: "${{ env.PHP_VERSION }}" php-version: "${{ env.PHP_VERSION }}"
ini-values: memory_limit=-1 ini-values: memory_limit=-1
extensions: curl, openssl, mbstring
- name: "Get Composer Cache Directory" - name: "Get Composer Cache Directory"
id: composer-cache id: composer-cache

View File

@@ -121,6 +121,7 @@ jobs:
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: 8.4 php-version: 8.4
extensions: curl, openssl, mbstring
- name: Define - name: Define
id: gendef id: gendef
@@ -198,3 +199,7 @@ jobs:
- name: "Run Build Tests (build - embed for non-windows)" - name: "Run Build Tests (build - embed for non-windows)"
if: ${{ !startsWith(matrix.os, 'windows-') }} if: ${{ !startsWith(matrix.os, 'windows-') }}
run: php src/globals/test-extensions.php build_embed_cmd ${{ matrix.os }} ${{ matrix.php }} run: php src/globals/test-extensions.php build_embed_cmd ${{ matrix.os }} ${{ matrix.php }}
# - name: Setup tmate session
# if: ${{ failure() }}
# uses: mxschmitt/action-tmate@v3

View File

@@ -32,12 +32,13 @@ jobs:
cp -r config/* docs/.vitepress/config/ cp -r config/* docs/.vitepress/config/
- name: "Install PHP for official runners" - name: "Install PHP for official runners"
uses: "shivammathur/setup-php@v2" uses: shivammathur/setup-php@v2
with: with:
coverage: none coverage: none
tools: composer:v2 tools: composer:v2
php-version: 8.4 php-version: 8.4
ini-values: memory_limit=-1 ini-values: memory_limit=-1
extensions: curl, openssl, mbstring
- name: "Get Composer Cache Directory" - name: "Get Composer Cache Directory"
id: composer-cache id: composer-cache

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
return (new PhpCsFixer\Config()) return (new PhpCsFixer\Config())
->setRiskyAllowed(true) ->setRiskyAllowed(true)
->setUnsupportedPhpVersionAllowed(true)
->setRules([ ->setRules([
'@PSR12' => true, '@PSR12' => true,
'@Symfony' => true, '@Symfony' => true,

View File

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

View File

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

View File

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

View File

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

365
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -34,7 +34,6 @@
; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`) ; SPC_LINUX_DEFAULT_CXX: the default c++ compiler for linux. (For alpine linux: `g++`, default: `$GNU_ARCH-linux-musl-g++`)
; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`) ; SPC_LINUX_DEFAULT_AR: the default archiver for linux. (For alpine linux: `ar`, default: `$GNU_ARCH-linux-musl-ar`)
[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.
SPC_CONCURRENCY=${CPU_COUNT} SPC_CONCURRENCY=${CPU_COUNT}
@@ -55,14 +54,29 @@ SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/cadd
; EXTENSION_DIR= ; EXTENSION_DIR=
[windows] [windows]
; build target: win7-static
SPC_TARGET=native-windows
; php-sdk-binary-tools path ; php-sdk-binary-tools path
PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools" PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path ; upx executable path
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe" UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static
; Windows static linking system libs
SPC_EXTRA_LIBS=""
[linux] [linux]
; Linux can use different build toolchain, but the toolchain can not be changed in this file:
; - musl (default): used for general linux distros, can build `musl-static` target only.
; - zig (WIP): used for general linux distros, can build `musl` and `glibc` targets.
; - musl-native: used for alpine linux, can build `musl-static` and `musl`(WIP) target.
; - gnu-native (assume): used for general linux distros, can build `glibc` target only and have portability issues.
; build target:
; - musl-static (default): pure static linking, using musl-libc, can run on any linux distro.
; - musl: static linking with dynamic linking to musl-libc, can run on musl-based linux distro.
; - glibc: static linking with dynamic linking to glibc, can run on glibc-based linux distro.
; include PATH for musl libc. ; include PATH for musl libc.
SPC_LIBC=musl SPC_LIBC=musl
; compiler environments ; compiler environments
@@ -74,11 +88,11 @@ LD=${SPC_LINUX_DEFAULT_LD}
SPC_DEFAULT_C_FLAGS="-fPIC -Os" SPC_DEFAULT_C_FLAGS="-fPIC -Os"
SPC_DEFAULT_CXX_FLAGS="-fPIC -Os" SPC_DEFAULT_CXX_FLAGS="-fPIC -Os"
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
SPC_EXTRA_LIBS= SPC_EXTRA_LIBS=""
; upx executable path ; upx executable path
UPX_EXEC=${PKG_ROOT_PATH}/bin/upx UPX_EXEC=${PKG_ROOT_PATH}/bin/upx
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream SPC_MICRO_PATCHES=cli_checks,disable_huge_page
; *** default build command for building php *** ; *** default build command for building php ***
; buildconf command ; buildconf command
@@ -86,11 +100,11 @@ 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-cgi --disable-phpdbg --with-pic" SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-cgi --disable-phpdbg --with-pic"
; make command ; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}" SPC_CMD_PREFIX_PHP_MAKE="make -j${SPC_CONCURRENCY}"
; embed type for php, static (libphp.a) or shared (libphp.so)
SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; *** default build vars for building php *** ; *** default build vars for building php ***
; embed type for php, static (libphp.a) or shared (libphp.so)
SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; CFLAGS for configuring php ; CFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE" SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE"
; CPPFLAGS for configuring php ; CPPFLAGS for configuring php
@@ -100,15 +114,14 @@ SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; LIBS for configuring php ; LIBS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm" SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
; EXTRA_CFLAGS for `make` php ; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_DEFAULT_C_FLAGS} -g -fstack-protector-strong -fno-ident -fPIE"
; EXTRA_LIBS for `make` php ; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm" SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm"
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
; EXTRA_LDFLAGS_PROGRAM for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-all-static -Wl,-O1 -pie"
[macos] [macos]
; build target: macho or macho (possibly we could support macho-universal in the future)
; Currently we do not support universal and cross-compilation for macOS.
SPC_TARGET=native-macos
; compiler environments ; compiler environments
CC=clang CC=clang
CXX=clang++ CXX=clang++
@@ -116,9 +129,9 @@ CXX=clang++
SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os"
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated) ; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
SPC_EXTRA_LIBS= SPC_EXTRA_LIBS=""
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,macos_iconv SPC_MICRO_PATCHES=cli_checks,macos_iconv
; *** default build command for building php *** ; *** default build command for building php ***
; buildconf command ; buildconf command
@@ -126,9 +139,11 @@ 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-cgi --disable-phpdbg" SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg"
; make command ; make command
SPC_CMD_PREFIX_PHP_MAKE="make -j${CPU_COUNT}" 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)
SPC_CMD_VAR_PHP_EMBED_TYPE="static"
; CFLAGS for configuring php ; CFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option" SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -Werror=unknown-warning-option"
; CPPFLAGS for configuring php ; CPPFLAGS for configuring php
@@ -136,11 +151,9 @@ SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
; LDFLAGS for configuring php ; LDFLAGS for configuring php
SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}" SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
; EXTRA_CFLAGS for `make` php ; EXTRA_CFLAGS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie ${SPC_DEFAULT_C_FLAGS}" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="${SPC_DEFAULT_C_FLAGS} -g -fstack-protector-strong -fpic -fpie"
; EXTRA_LIBS for `make` php ; EXTRA_LIBS for `make` php
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv" SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-lresolv"
; embed type for php, static (libphp.a) or shared (libphp.dylib)
SPC_CMD_VAR_PHP_EMBED_TYPE="static"
[freebsd] [freebsd]
; compiler environments ; compiler environments

View File

@@ -34,7 +34,7 @@
}, },
"bz2": { "bz2": {
"type": "builtin", "type": "builtin",
"arg-type-unix": "with-prefix", "arg-type-unix": "with-path",
"arg-type-windows": "with", "arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"bzip2" "bzip2"
@@ -185,7 +185,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"gettext" "gettext"
] ]
@@ -211,7 +211,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"gmp" "gmp"
] ]
@@ -233,7 +233,7 @@
}, },
"type": "external", "type": "external",
"source": "grpc", "source": "grpc",
"arg-type-unix": "custom", "arg-type-unix": "enable-path",
"cpp-extension": true, "cpp-extension": true,
"lib-depends": [ "lib-depends": [
"grpc" "grpc"
@@ -244,7 +244,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"arg-type-windows": "with", "arg-type-windows": "with",
"lib-depends-unix": [ "lib-depends-unix": [
"libiconv" "libiconv"
@@ -320,7 +320,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"ldap" "ldap"
], ],
@@ -529,7 +529,7 @@
}, },
"notes": true, "notes": true,
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"libargon2" "libargon2"
] ]
@@ -571,7 +571,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"arg-type-windows": "custom", "arg-type-windows": "custom",
"ext-depends": [ "ext-depends": [
"pdo", "pdo",
@@ -674,7 +674,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"readline" "readline"
], ],
@@ -785,7 +785,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"arg-type-windows": "with", "arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"sqlite" "sqlite"
@@ -811,7 +811,7 @@
}, },
"type": "external", "type": "external",
"source": "ext-ssh2", "source": "ext-ssh2",
"arg-type": "with-prefix", "arg-type": "with-path",
"arg-type-windows": "with", "arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"libssh2" "libssh2"
@@ -937,7 +937,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"tidy" "tidy"
] ]
@@ -953,7 +953,7 @@
}, },
"type": "external", "type": "external",
"source": "ext-uuid", "source": "ext-uuid",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"libuuid" "libuuid"
] ]
@@ -965,7 +965,7 @@
}, },
"type": "external", "type": "external",
"source": "ext-uv", "source": "ext-uv",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"libuv" "libuv"
], ],
@@ -1067,7 +1067,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"lib-depends": [ "lib-depends": [
"libxslt" "libxslt"
], ],
@@ -1104,7 +1104,7 @@
}, },
"type": "external", "type": "external",
"source": "yaml", "source": "yaml",
"arg-type-unix": "with-prefix", "arg-type-unix": "with-path",
"arg-type-windows": "with", "arg-type-windows": "with",
"lib-depends": [ "lib-depends": [
"libyaml" "libyaml"
@@ -1115,7 +1115,7 @@
"BSD": "wip" "BSD": "wip"
}, },
"type": "builtin", "type": "builtin",
"arg-type": "with-prefix", "arg-type": "with-path",
"arg-type-windows": "enable", "arg-type-windows": "enable",
"lib-depends-unix": [ "lib-depends-unix": [
"libzip" "libzip"

View File

@@ -35,10 +35,10 @@
}, },
"brotli": { "brotli": {
"source": "brotli", "source": "brotli",
"static-libs-unix": [ "pkg-configs": [
"libbrotlidec.a", "libbrotlicommon",
"libbrotlienc.a", "libbrotlidec",
"libbrotlicommon.a" "libbrotlienc"
], ],
"static-libs-windows": [ "static-libs-windows": [
"brotlicommon.lib", "brotlicommon.lib",
@@ -89,7 +89,8 @@
"nghttp3", "nghttp3",
"ngtcp2", "ngtcp2",
"zstd", "zstd",
"libcares" "libcares",
"ldap"
], ],
"lib-suggests-windows": [ "lib-suggests-windows": [
"brotli", "brotli",
@@ -185,14 +186,15 @@
}, },
"grpc": { "grpc": {
"source": "grpc", "source": "grpc",
"static-libs-unix": [ "pkg-configs": [
"libgrpc.a", "grpc"
"libcares.a"
], ],
"lib-depends": [ "lib-depends": [
"zlib", "zlib",
"openssl" "openssl",
"libcares"
], ],
"provide-pre-built": true,
"frameworks": [ "frameworks": [
"CoreFoundation" "CoreFoundation"
] ]
@@ -200,11 +202,10 @@
"icu": { "icu": {
"source": "icu", "source": "icu",
"cpp-library": true, "cpp-library": true,
"static-libs-unix": [ "pkg-configs": [
"libicui18n.a", "icu-uc",
"libicuio.a", "icu-i18n",
"libicuuc.a", "icu-io"
"libicudata.a"
] ]
}, },
"icu-static-win": { "icu-static-win": {
@@ -221,15 +222,16 @@
}, },
"imagemagick": { "imagemagick": {
"source": "imagemagick", "source": "imagemagick",
"static-libs-unix": [ "pkg-configs": [
"libMagick++-7.Q16HDRI.a", "Magick++-7.Q16HDRI",
"libMagickWand-7.Q16HDRI.a", "MagickCore-7.Q16HDRI",
"libMagickCore-7.Q16HDRI.a" "MagickWand-7.Q16HDRI"
], ],
"lib-depends": [ "lib-depends": [
"zlib", "zlib",
"libpng",
"libjpeg", "libjpeg",
"libjxl",
"libpng",
"libwebp", "libwebp",
"freetype", "freetype",
"libtiff", "libtiff",
@@ -252,11 +254,23 @@
"openssl" "openssl"
] ]
}, },
"jbig": {
"source": "jbig",
"static-libs-unix": [
"libjbig.a",
"libjbig85.a"
],
"headers": [
"jbig.h",
"jbig85.h",
"jbig_ar.h"
]
},
"ldap": { "ldap": {
"source": "ldap", "source": "ldap",
"static-libs-unix": [ "pkg-configs": [
"libldap.a", "ldap",
"liblber.a" "lber"
], ],
"lib-depends": [ "lib-depends": [
"openssl", "openssl",
@@ -265,6 +279,13 @@
"libsodium" "libsodium"
] ]
}, },
"lerc": {
"source": "lerc",
"static-libs-unix": [
"libLerc.a"
],
"cpp-library": true
},
"libacl": { "libacl": {
"source": "libacl", "source": "libacl",
"static-libs-unix": [ "static-libs-unix": [
@@ -400,6 +421,21 @@
"zlib" "zlib"
] ]
}, },
"libjxl": {
"source": "libjxl",
"pkg-configs": [
"libjxl",
"libjxl_cms",
"libjxl_threads",
"libhwy"
],
"lib-depends": [
"brotli",
"libjpeg",
"libpng",
"libwebp"
]
},
"liblz4": { "liblz4": {
"source": "liblz4", "source": "liblz4",
"static-libs-unix": [ "static-libs-unix": [
@@ -485,9 +521,6 @@
], ],
"lib-depends": [ "lib-depends": [
"openssl" "openssl"
],
"lib-suggests": [
"zlib"
] ]
}, },
"libtiff": { "libtiff": {
@@ -498,6 +531,13 @@
"lib-depends": [ "lib-depends": [
"zlib", "zlib",
"libjpeg" "libjpeg"
],
"lib-suggests-unix": [
"lerc",
"libwebp",
"jbig",
"xz",
"zstd"
] ]
}, },
"libuuid": { "libuuid": {
@@ -517,12 +557,12 @@
}, },
"libwebp": { "libwebp": {
"source": "libwebp", "source": "libwebp",
"static-libs-unix": [ "pkg-configs": [
"libwebp.a", "libwebp",
"libwebpdecoder.a", "libwebpdecoder",
"libwebpdemux.a", "libwebpdemux",
"libwebpmux.a", "libwebpmux",
"libsharpyuv.a" "libsharpyuv"
], ],
"static-libs-windows": [ "static-libs-windows": [
"libwebp.lib", "libwebp.lib",
@@ -698,8 +738,8 @@
"openssl": { "openssl": {
"source": "openssl", "source": "openssl",
"static-libs-unix": [ "static-libs-unix": [
"libssl.a", "libcrypto.a",
"libcrypto.a" "libssl.a"
], ],
"static-libs-windows": [ "static-libs-windows": [
"libssl.lib", "libssl.lib",
@@ -714,10 +754,8 @@
}, },
"postgresql": { "postgresql": {
"source": "postgresql", "source": "postgresql",
"static-libs-unix": [ "pkg-configs": [
"libpq.a", "libpq"
"libpgport.a",
"libpgcommon.a"
], ],
"lib-depends": [ "lib-depends": [
"libiconv", "libiconv",
@@ -813,6 +851,15 @@
"libiconv" "libiconv"
] ]
}, },
"watcher": {
"source": "watcher",
"static-libs-unix": [
"libwatcher-c.a"
],
"headers": [
"wtr/watcher-c.h"
]
},
"xz": { "xz": {
"source": "xz", "source": "xz",
"static-libs-unix": [ "static-libs-unix": [
@@ -866,14 +913,5 @@
"zstd.h", "zstd.h",
"zstd_errors.h" "zstd_errors.h"
] ]
},
"watcher": {
"source": "watcher",
"static-libs-unix": [
"libwatcher-c.a"
],
"headers": [
"wtr/watcher-c.h"
]
} }
} }

View File

@@ -4,7 +4,8 @@
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} },
"alt": false
}, },
"amqp": { "amqp": {
"type": "url", "type": "url",
@@ -387,6 +388,19 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"jbig": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/jbig/jbigkit-2.1.tar.gz",
"provide-pre-built": true,
"alt": {
"type": "url",
"url": "https://www.cl.cam.ac.uk/~mgk25/jbigkit/download/jbigkit-2.1.tar.gz"
},
"license": {
"type": "file",
"path": "COPYING"
}
},
"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/",
@@ -396,6 +410,16 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"lerc": {
"type": "ghtar",
"repo": "Esri/lerc",
"prefer-stable": true,
"provide-pre-built": true,
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libacl": { "libacl": {
"alt": { "alt": {
"type": "url", "type": "url",
@@ -533,6 +557,21 @@
"path": "LICENSE.md" "path": "LICENSE.md"
} }
}, },
"libjxl": {
"type": "git",
"url": "https://github.com/libjxl/libjxl",
"rev": "main",
"submodules": [
"third_party/highway",
"third_party/libjpeg-turbo",
"third_party/sjpeg",
"third_party/skcms"
],
"license": {
"type": "file",
"path": "LICENSE"
}
},
"liblz4": { "liblz4": {
"type": "ghrel", "type": "ghrel",
"repo": "lz4/lz4", "repo": "lz4/lz4",
@@ -596,6 +635,7 @@
"repo": "libssh2/libssh2", "repo": "libssh2/libssh2",
"match": "libssh2.+\\.tar\\.gz", "match": "libssh2.+\\.tar\\.gz",
"prefer-stable": true, "prefer-stable": true,
"provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "COPYING"
@@ -605,7 +645,6 @@
"type": "filelist", "type": "filelist",
"url": "https://download.osgeo.org/libtiff/", "url": "https://download.osgeo.org/libtiff/",
"regex": "/href=\"(?<file>tiff-(?<version>[^\"]+)\\.tar\\.xz)\"/", "regex": "/href=\"(?<file>tiff-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"provide-pre-built": true,
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE.md" "path": "LICENSE.md"
@@ -998,6 +1037,15 @@
"path": "COPYING" "path": "COPYING"
} }
}, },
"watcher": {
"type": "ghtar",
"repo": "e-dant/watcher",
"prefer-stable": true,
"license": {
"type": "file",
"path": "license"
}
},
"xdebug": { "xdebug": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/xdebug", "url": "https://pecl.php.net/get/xdebug",
@@ -1078,14 +1126,5 @@
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
},
"watcher": {
"type": "ghtar",
"repo": "e-dant/watcher",
"prefer-stable": true,
"license": {
"type": "file",
"path": "license"
}
} }
} }

View File

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

View File

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

View File

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

View File

@@ -82,7 +82,7 @@ and this extension cannot be compiled into php by static linking, so it cannot b
## xdebug ## xdebug
1. Xdebug is only buildable as a shared extension. On Linux, you need to use static-php-cli with SPC_LIBC=glibc. 1. Xdebug is only buildable as a shared extension. You need to use a build target other than `musl-static` for SPC_TARGET.
2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug". 2. When using Linux/glibc or macOS, you can compile Xdebug as a shared extension using --build-shared="xdebug".
The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`. The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

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

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -4,45 +4,48 @@
## 贡献方法 ## 贡献方法
如果你有代码或文档要贡献,需要先了解以下内容。 如果你有代码或文档要贡献,以下是你需要先了解内容。
1. 你要贡献什么类型的代码?(新扩展、修复 Bug、安全问题、项目框架优化、文档 1. 你要贡献什么类型的代码?(新扩展、修复 Bug、安全问题、项目框架优化、文档
2. 如果你贡献了新文件或新片段,你的代码是否经过 `php-cs-fixer``phpstan` 的检查? 2. 如果你贡献了新文件或新片段,你的代码是否经过 `php-cs-fixer``phpstan` 的检查?
3. 在贡献代码前是否充分阅读了 [开发指南](../develop/) 3. 在贡献代码前是否充分阅读了 [开发指南](../develop/)
如果你可以回答以上问题,并已经对代码做出了修改,可以及时在项目 GitHub 仓库发起 Pull Request。待代码审查完毕后,可根据建议修改代码,或直接合并到主分支。 如果你能回答上述问题并对代码进行了修改,可以及时在项目 GitHub 仓库发起 Pull Request。
代码审查完成后,可以根据建议修改代码,或直接合并到主分支。
## 贡献类型 ## 贡献类型
本项目主要用途是编译静态链接的 PHP 二进制基于 `symfony/console` 编写了命令行处理功能。在开发之前,如果你对它不够熟悉, 本项目主要目的是编译静态链接的 PHP 二进制文件,命令行处理功能基于 `symfony/console` 编写
可以先查看 [symfony/console 文档](https://symfony.com/doc/current/components/console.html)。 在开发之前,如果你对它不够熟悉,请先查看 [symfony/console 文档](https://symfony.com/doc/current/components/console.html)。
### 安全问题 ### 安全更新
因为本项目基本上是属于本地运行的 PHP 项目,一般来说不会存在远程攻击行为。但如果你发现了此类问题,请**不要**在 GitHub 仓库提交 PR 或 Issue 因为本项目基本上是一个本地运行的 PHP 项目,一般来说不会远程攻击
你需要通过 [邮件](mailto:admin@zhamao.me) 的方式联系项目维护者crazywhalecc 但如果你发现此类问题,请**不要**在 GitHub 仓库提交 PR 或 Issue
你需要通过 [邮件](mailto:admin@zhamao.me) 联系项目维护者crazywhalecc
### 修复 Bug ### 修复 Bug
修复 Bug 一般不涉及项目结构和框架的修改,所以如果你可以定位到错误代码并直接修复它,请直接提交 PR。 修复 Bug 一般不涉及项目结构和框架的修改,所以如果你定位到错误代码并直接修复它,请直接提交 PR。
### 新扩展 ### 新扩展
对于添加一个新扩展来说,你需要了解一些本项目的基本结构以及如何根据现有逻辑添加新扩展。在本页的下一章节将会详细介绍。 对于添加新扩展,你需要了解项目的一些基本结构以及如何根据现有逻辑添加新扩展。
这将在本页的下一节中详细介绍。
总的来说,你需要: 总的来说,你需要:
1. 评估扩展是否可以内联编译到 PHP 中。 1. 评估扩展是否可以内联编译到 PHP 中。
2. 评估扩展的依赖库(如果有)是否可以静态编译。 2. 评估扩展的依赖库(如果有)是否可以静态编译。
3.出扩展的依赖库在不同平台编译命令。 3. 写不同平台的库编译命令。
4. 验证扩展及其依赖库能否与现有扩展和依赖兼容。 4. 验证扩展及其依赖与现有扩展和依赖兼容。
5. 验证扩展在 `cli``micro``fpm``embed` 几种 SAPI 中正常工作。 5. 验证扩展在 `cli``micro``fpm``embed` SAPIs 中正常工作。
6. 编写文档,加入你的扩展。 6. 编写文档并添加你的扩展。
### 项目框架优化 ### 项目框架优化
如果你已经熟悉 `symfony/console` 的工作原理,并同时要对项目的框架进行一些修改或优化,请先了解以下事情: 如果你已经熟悉 `symfony/console` 的工作原理,并同时要对项目的框架进行一些修改或优化,请先了解以下事情:
1.扩展不属于项目框架优化,但如果你在加入新的扩展时发现不得不优化框架,则需先对框架本身进行修改,然后再加扩展。 1. 加扩展不属于项目框架优化,但如果你在添加新扩展时发现必须优化框架,则需要先修改框架本身,然后再加扩展。
2. 对于一些大规模逻辑修改(例如涉及 LibraryBase、Extension 对象等的修改),建议先提交 Issue 或 Draft PR 进行讨论方案 2. 对于一些大规模逻辑修改(例如涉及 LibraryBase、Extension 对象等的修改),建议先提交 Issue 或 Draft PR 进行讨论。
3. 项目早期为纯中文开发项目,代码中存在一部分中文注释。国际化项目后你可以提交 PR 将这些注释翻译为英语。 3. 项目早期,它是一个纯私有开发项目,代码中有一些中文注释。项目国际化后你可以提交 PR 将这些注释翻译为英语。
4. 请不要在代码中提交包含较多无用的代码片段,例如大量未使用的变量、方法、类、重复写了很多次的代码。 4. 请不要在代码中提交多无用的代码片段,例如大量未使用的变量、方法、类以及多次重写的代码。

View File

@@ -2,7 +2,7 @@
开发本项目需要安装部署 PHP 环境,以及一些 PHP 项目常用的扩展和 Composer。 开发本项目需要安装部署 PHP 环境,以及一些 PHP 项目常用的扩展和 Composer。
项目的开发环境和运行环境几乎完全一致你可以参照 **指南-本地构建** 部分安装系统 PHP 或使用本项目预构建的静态 PHP 作为环境这里不再赘述。 项目的开发环境和运行环境几乎完全一致你可以参照 **手动构建** 部分安装系统 PHP 或使用本项目预构建的静态 PHP 作为环境这里不再赘述。
抛开用途,本项目本身其实就是一个 `php-cli` 程序,你可以将它当作一个正常的 PHP 项目进行编辑和开发,同时你需要了解不同系统的 Shell 命令行。 抛开用途,本项目本身其实就是一个 `php-cli` 程序,你可以将它当作一个正常的 PHP 项目进行编辑和开发,同时你需要了解不同系统的 Shell 命令行。
@@ -18,10 +18,10 @@
curl,dom,filter,mbstring,openssl,pcntl,phar,posix,sodium,tokenizer,xml,xmlwriter curl,dom,filter,mbstring,openssl,pcntl,phar,posix,sodium,tokenizer,xml,xmlwriter
``` ```
static-php-cli 项目本身不需要这么多扩展,但在开发过程中,你会用到 ComposerPHPUnit 等工具,它们需要这些扩展。 static-php-cli 项目本身不需要这么多扩展,但在开发过程中,你会用到 ComposerPHPUnit 等工具,它们需要这些扩展。
> 对于 static-php-cli 自身构建的 micro 自执行二进制,仅需要 `pcntl,posix,mbstring,tokenizer,phar`。 > 对于 static-php-cli 自身构建的 micro 自执行二进制,仅需要 `pcntl,posix,mbstring,tokenizer,phar`。
## 开始开发 ## 开始开发
继续向下查看项目结构文档,你可以从中了解 `static-php-cli` 是如何作的。 继续向下查看项目结构文档,你可以学习 `static-php-cli` 是如何作的。

View File

@@ -4,28 +4,28 @@
## php.ini 的路径是什么? ## php.ini 的路径是什么?
在 Linux、macOS 和 FreeBSD 上,`php.ini`默认路径是 `/usr/local/etc/php/php.ini` 在 Linux、macOS 和 FreeBSD 上,`php.ini` 的路径是 `/usr/local/etc/php/php.ini`
在 Windows 中,路径是 `C:\windows\php.ini``php.exe` 所在的当前目录。 在 Windows 中,路径是 `C:\windows\php.ini``php.exe` 所在的当前目录。
可以在 *nix 系统中使用手动构建选项 `--with-config-file-path` 来更改查找 `php.ini` 的目录。 可以在 *nix 系统中使用手动构建选项 `--with-config-file-path` 来更改查找 `php.ini` 的目录。
此外,在 Linux、macOS 和 FreeBSD 上,`/usr/local/etc/php/conf.d` 目录中的 `*.ini` 文件也会被加载。 此外,在 Linux、macOS 和 FreeBSD 上,`/usr/local/etc/php/conf.d` 目录中的 `.ini` 文件也会被加载。
在 Windows 中,该路径默认为空。 在 Windows 中,该路径默认为空。
可以使用手动构建选项 `--with-config-file-scan-dir` 更改该目录。 可以使用手动构建选项 `--with-config-file-scan-dir` 更改该目录。
PHP 默认也会从 [其他标准位置](https://www.php.net/manual/zh/configuration.file.php) 中搜索 `php.ini` PHP 默认也会从 [其他标准位置](https://www.php.net/manual/zh/configuration.file.php) 中搜索 `php.ini`
## 静态编译的 PHP 可以安装扩展吗 ## 静态编译的 PHP 可以安装扩展吗
因为传统架构下的 PHP 安装扩展的原理是使用 `.so` 类型的动态链接的库方式安装新扩展,而使用本项目编译的静态链接的 PHP。但是静态链接在不同操作系统有不同的定义。 因为传统架构下的 PHP 安装扩展的原理是使用 `.so` 类型的动态链接的库方式安装新扩展,而使用本项目编译的静态链接的 PHP。但是静态链接在不同操作系统有不同的定义。
首先对于 Linux 系统来说,静态链接的二进制文件不会链接系统的动态链接库的,纯静态链接的二进制无法加载动态库,所以无法添加新扩展。 首先对于 Linux 系统,静态链接的二进制文件不会链接系统的动态链接库纯静态链接的二进制文件(`-all-static`无法加载动态库,因此无法添加新扩展。
同时,在纯静态模式下你也不能使用 `ffi` 等扩展加载外部 `.so` 模块。 同时,在纯静态模式下你也不能使用 `ffi` 等扩展加载外部 `.so` 模块。
你可以通过命令 `ldd buildroot/bin/php` 来查你在 Linux 下构建的二进制是否为纯静态链接 你可以使用命令 `ldd buildroot/bin/php`查你在 Linux 下构建的二进制文件是否为纯静态链接。
如果你 [构建 GNU libc 兼容的 PHP](../guide/build-with-glibc),你可以使用 `ffi` 扩展加载外部 `.so` 模块,并加载具有相同 ABI 的 `.so` 扩展。 如果你 [构建基于 GNU libc 的 PHP](../guide/build-with-glibc),你可以使用 `ffi` 扩展加载外部 `.so` 模块,并加载具有相同 ABI 的 `.so` 扩展。
例如,你可以使用以下命令构建一个 glibc 动态链接的静态 PHP 二进制,同时支持 FFI 扩展加载相同 PHP 版本和相同 TS 类型的 `xdebug.so` 扩展: 例如,你可以使用以下命令构建一个 glibc 动态链接的静态 PHP 二进制文件,支持 FFI 扩展加载相同 PHP 版本和相同 TS 类型的 `xdebug.so` 扩展:
```bash ```bash
bin/spc-gnu-docker download --for-extensions=ffi,xml --with-php=8.4 bin/spc-gnu-docker download --for-extensions=ffi,xml --with-php=8.4
@@ -34,14 +34,14 @@ bin/spc-gnu-docker build ffi,xml --build-cli --debug
buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" --ri xdebug buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" --ri xdebug
``` ```
对于 macOS 平台来说macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 系统库:`/usr/lib/libresolv.9.dylib``/usr/lib/libSystem.B.dylib` 对于 macOS 平台macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 系统库:`/usr/lib/libresolv.9.dylib``/usr/lib/libSystem.B.dylib`
因此,在 macOS 上,可以直接构建出使用静态编译 PHP 二进制文件和动态链接的扩展 因此,在 macOS 上,可以**直接**使用 SPC 构建具有动态链接扩展的静态编译 PHP 二进制文件:
1. 使用 `--build-shared=XXX` 选项构建共享扩展 `xxx.so`。例如:`bin/spc build bcmath,zlib --build-shared=xdebug --build-cli` 1. 使用 `--build-shared=XXX` 选项构建共享扩展 `xxx.so`。例如:`bin/spc build bcmath,zlib --build-shared=xdebug --build-cli`
2. 将获得 `buildroot/modules/xdebug.so``buildroot/bin/php` 2. 将获得 `buildroot/modules/xdebug.so``buildroot/bin/php`
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。 3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
## 可以支持 Oracle 数据库扩展吗 ## 可以支持 Oracle 数据库扩展吗
部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so` 部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so`
这些扩展无法使用源码的形式编译到 static-php-cli 中,所以本项目可能永远也不会支持这些扩展。不过,理论上你可以根据上面的问题在 macOS 和 Linux 下接入和使用这类扩展。 这些扩展无法使用源码的形式编译到 static-php-cli 中,所以本项目可能永远也不会支持这些扩展。不过,理论上你可以根据上面的问题在 macOS 和 Linux 下接入和使用这类扩展。
@@ -49,41 +49,43 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" -
如果你对此类扩展有需求,或者大部分人都对这些闭源扩展使用有需求, 如果你对此类扩展有需求,或者大部分人都对这些闭源扩展使用有需求,
可以看看有关 [standalone-php-cli](https://github.com/crazywhalecc/static-php-cli/discussions/58) 的讨论。欢迎留言。 可以看看有关 [standalone-php-cli](https://github.com/crazywhalecc/static-php-cli/discussions/58) 的讨论。欢迎留言。
## 支持 Windows 吗 ## 支持 Windows 吗
该项目目前支持 Windows但支持的扩展数量较少Windows 支持并不完美主要有以下几个问题: 该项目目前支持 Windows但支持的扩展数量较少Windows 支持并不完美主要有以下问题:
1. Windows 的编译程与 *nix 不同,使用的工具链也不同编译个扩展依赖库使用的编译工具也几乎完全不同。 1. Windows 的编译程与 *nix 不同,使用的工具链也不同。用于编译个扩展依赖库的编译工具也几乎完全不同。
2. Windows 版本的需求也会根据所有使用本项目的人的需求推进如果很多人需要,我会尽快支持相关扩展。 2. Windows 版本的需求也会根据所有使用本项目的人的需求推进如果很多人需要,我会尽快支持相关扩展。
## 使用 micro 可以保护我的源码吗 ## 我可以使用 micro 保护我的源码吗
不可以。micro.sfx 本质上是将 php 和 php 代码合为一个文件,没有 PHP 代码编译或加密的过程。 不可以。micro.sfx 本质上是将 php 和 php 代码合为一个文件,没有编译或加密 PHP 代码的过程。
首先 php-src 是 PHP 代码的官方解释器,而且现在市面上还没有一个能兼容主流分支的 PHP 编译器。
之前我在网上看到有一个项目是 BPCBinary PHP Compiler可以把 PHP 编译为二进制,但是限制也是很多很多。
加密保护代码的方向和编译也不是一回事,编译过后也可以通过逆向工程等方式拿到代码,真正保护还是通过加壳、加密代码等手段进行 首先php-src 是 PHP 代码的官方解释器,市场上没有与主流分支兼容的 PHP 编译器
我在网上看到一个名为 BPCBinary PHP Compiler的项目可以将 PHP 编译为二进制,但有很多限制。
所以本项目static-php-cli、相关项目lwmbs、swoole-cli都是提供一个对 php-src 源码的便捷编译工具, 加密和保护代码的方向与编译不同。编译后,也可以通过逆向工程等方法获得代码。真正的保护仍然通过打包和加密代码等手段进行。
本项目和相关项目引用的 phpmicro 也仅仅是 PHP 的 sapi 接口封装,而不是 PHP 代码的编译工具。
PHP 代码的编译器是完全不同的项目,因此不会考虑额外的情况。如果你对加密感兴趣,可以考虑使用现有的加密技术,如 Swoole Compiler、Source Guardian 等 因此本项目static-php-cli和相关项目lwmbs、swoole-cli都提供了 php-src 源代码的便捷编译工具
本项目和相关项目引用的 phpmicro 只是 PHP 的 sapi 接口封装,而不是 PHP 代码的编译工具。
PHP 代码的编译器是一个完全不同的项目,因此不考虑额外的情况。
如果你对加密感兴趣,可以考虑使用现有的加密技术,如 Swoole Compiler、Source Guardian 等。
## 无法使用 ssl ## 无法使用 ssl
**更新:该问题已在最新版本的 static-php-cli 中修复,现在默认读取系统的证书文件。如果你仍然遇到问题,尝试下的解决方案。** **更新:该问题已在最新版本的 static-php-cli 中修复,现在默认读取系统的证书文件。如果你仍然遇到问题,尝试下的解决方案。**
使用 curl、pgsql 等 请求 HTTPS 网站或建立 SSL 连接时,可能存在 `error:80000002:system library::No such file or directory` 错误 使用 curl、pgsql 等请求 HTTPS 网站或建立 SSL 连接时,可能会出现 `error:80000002:system library::No such file or directory` 错误
这个错误是由于静态编译的 PHP 未通过 `php.ini` 指定 `openssl.cafile` 导致的。 错误是由于静态编译的 PHP 未通过 `php.ini` 指定 `openssl.cafile` 导致的。
你可以在使用 PHP 前指定 `php.ini`并在 INI 添加 `openssl.cafile=/path/to/your-cert.pem` 来解决这个问题。 你可以通过在使用 PHP 前指定 `php.ini` 并在 INI 添加 `openssl.cafile=/path/to/your-cert.pem` 来解决问题。
对于 Linux 系统,你可以从 curl 官方网站下载 [cacert.pem](https://curl.se/docs/caextract.html) 文件,也可以使用系统自带的证书文件。 对于 Linux 系统,你可以从 curl 官方网站下载 [cacert.pem](https://curl.se/docs/caextract.html) 文件,也可以使用系统自带的证书文件。
有关不同发行版的证书位置,参考 [Go 标准库](https://go.dev/src/crypto/x509/root_linux.go)。 有关不同发行版的证书位置,参考 [Golang 文档](https://go.dev/src/crypto/x509/root_linux.go)。
> INI 配置 `openssl.cafile` 不可以使用 `ini_set()` 函数动态设置,因为 `openssl.cafile` 是一个 `PHP_INI_SYSTEM` 类型的配置,只能在 `php.ini` 文件中设置。 > INI 配置 `openssl.cafile` 不使用 `ini_set()` 函数动态设置,因为 `openssl.cafile` 是 `PHP_INI_SYSTEM` 类型的配置,只能在 `php.ini` 文件中设置。
## 为什么不支持旧版本 PHP ## 为什么不支持旧版本 PHP
因为旧版本的 PHP 有很多问题,如安全问题、性能问题功能问题。此外,旧版本的 PHP 很多都无法与最新的依赖库兼容,这也是不支持旧版本 PHP 的原因之一。 因为旧版本的 PHP 有很多问题,如安全问题、性能问题功能问题。此外,许多旧版本的 PHP 与最新的依赖库兼容,这也是不支持旧版本 PHP 的原因之一。
你可以使用 static-php-cli 早期编译的旧版本,如 PHP 8.0,但不会明确支持早期版本。 你可以使用 static-php-cli 早期编译的旧版本,如 PHP 8.0,但不会明确支持早期版本。

View File

@@ -5,6 +5,7 @@ Action 构建指的是直接使用 GitHub Action 进行编译。
如果你不想自行编译,可以从本项目现有的 Action 下载 Artifact也可以从自托管的服务器下载[进入](https://dl.static-php.dev/static-php-cli/common/) 如果你不想自行编译,可以从本项目现有的 Action 下载 Artifact也可以从自托管的服务器下载[进入](https://dl.static-php.dev/static-php-cli/common/)
> 自托管的二进制也是由 Action 构建而来,[项目仓库地址](https://github.com/static-php/static-php-cli-hosted)。 > 自托管的二进制也是由 Action 构建而来,[项目仓库地址](https://github.com/static-php/static-php-cli-hosted)。
> 包含的扩展有bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip
## 构建方法 ## 构建方法

View File

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

View File

@@ -4,8 +4,8 @@ static-php-cli 是一个用于构建静态编译的 PHP 二进制的工具,目
在指南章节中,你将了解到如何使用 static-php-cli 构建独立的 php 程序。 在指南章节中,你将了解到如何使用 static-php-cli 构建独立的 php 程序。
- [Action 构建](./action-build)
- [本地构建](./manual-build) - [本地构建](./manual-build)
- [Action 构建](./action-build)
- [扩展列表](./extensions) - [扩展列表](./extensions)
## 编译环境 ## 编译环境

View File

@@ -10,20 +10,22 @@
当下载资源时,你可能最终会看到类似 `curl: (56) The requested URL returned error: 403` 的错误,这通常是由于 GitHub 限制导致的。 当下载资源时,你可能最终会看到类似 `curl: (56) The requested URL returned error: 403` 的错误,这通常是由于 GitHub 限制导致的。
你可以通过在命令中添加 `--debug` 来验证,会看到类似 `[DEBU] Running command (no output) : curl -sfSL "https://api.github.com/repos/openssl/openssl/releases"` 的输出。 你可以通过在命令中添加 `--debug` 来验证,会看到类似 `[DEBU] Running command (no output) : curl -sfSL "https://api.github.com/repos/openssl/openssl/releases"` 的输出。
要解决这个问题,可以在 GitHub 上 [创建](https://github.com/settings/token) 一个个人访问令牌,并将其设置为环境变量 `GITHUB_TOKEN=<XXX>` 要解决这个问题,可以在 GitHub 上 [创建](https://github.com/settings/tokens) 一个个人访问令牌,并将其设置为环境变量 `GITHUB_TOKEN=<XXX>`
如果确认地址确实无法正常访问,可以提交 Issue 或 PR 更新地址。 如果确认地址确实无法正常访问,可以提交 Issue 或 PR 更新地址或下载类型
## doctor 无法修复 ## Doctor 无法修复某些问题
在绝大部分情况下doctor 模块都可以对缺失的系统环境进行自动修复和安装,但也存在特殊的环境无法正常使用自动修复功能。 在绝大部分情况下doctor 模块都可以对缺失的系统环境进行自动修复和安装,但也存在特殊的环境无法正常使用自动修复功能。
部分项目由于系统局限(如 Windows 下无法自动安装 Visual Studio 等软件),无法使用自动修复功能。 由于系统限制(例如,Windows 下无法自动安装 Visual Studio 等软件),自动修复功能无法用于某些项目
在遇到无法自动修复功能时,如果遇到 `Some check items can not be fixed` 字样,则表明无法自动修复,请根据终端显示的方法提交 Issue 或自行修复环境 在遇到无法自动修复功能时,如果遇到 `Some check items can not be fixed` 字样,则表明无法自动修复。
请根据终端显示的方法提交 Issue 或自行修复环境。
## 编译错误 ## 编译错误
遇到编译错误时,如果没有开启 `--debug` 日志,请先开启调试日志,然后确定报错的命令。 遇到编译错误时,如果没有开启 `--debug` 日志,请先开启调试日志,然后确定报错的命令。
报错的终端输出对于修复编译错误非常重要,请在提交 Issue 时一并将终端日志的最后报错片段(或整个终端日志输出)上传,并且包含使用的 `spc` 命令和参数 报错的终端输出对于修复编译错误非常重要。
在提交 Issue 时,请上传终端日志的最后报错片段(或整个终端日志输出),并且包含使用的 `spc` 命令和参数。
如果你是重复构建,请参考 [本地构建 - 多次构建](./manual-build#多次构建) 章节,清理构建缓存后再次构建 如果你是重复构建,请参考 [本地构建 - 多次构建](./manual-build#多次构建) 章节。

View File

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

View File

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

View File

@@ -14,6 +14,7 @@ use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\store\SourceManager; use SPC\store\SourceManager;
use SPC\store\SourcePatcher;
use SPC\util\CustomExt; use SPC\util\CustomExt;
abstract class BuilderBase abstract class BuilderBase
@@ -65,13 +66,17 @@ abstract class BuilderBase
// build all libs // build all libs
foreach ($this->libs as $lib) { foreach ($this->libs as $lib) {
$starttime = microtime(true); $starttime = microtime(true);
match ($lib->setup($this->getOption('rebuild', false))) { $status = $lib->setup($this->getOption('rebuild', false));
match ($status) {
LIB_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] setup success, took ' . round(microtime(true) - $starttime, 2) . ' s'), LIB_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] setup success, took ' . round(microtime(true) - $starttime, 2) . ' s'),
LIB_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'), LIB_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'),
LIB_STATUS_BUILD_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'), LIB_STATUS_BUILD_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'),
LIB_STATUS_INSTALL_FAILED => logger()->error('lib [' . $lib::NAME . '] install failed'), LIB_STATUS_INSTALL_FAILED => logger()->error('lib [' . $lib::NAME . '] install failed'),
default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'), default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'),
}; };
if (in_array($status, [LIB_STATUS_BUILD_FAILED, LIB_STATUS_INSTALL_FAILED])) {
throw new RuntimeException('Library [' . $lib::NAME . '] setup failed.');
}
} }
} }
@@ -203,6 +208,8 @@ abstract class BuilderBase
$this->emitPatchPoint('before-exts-extract'); $this->emitPatchPoint('before-exts-extract');
SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]); SourceManager::initSource(exts: [...$static_extensions, ...$shared_extensions]);
$this->emitPatchPoint('after-exts-extract'); $this->emitPatchPoint('after-exts-extract');
// patch micro
SourcePatcher::patchMicro();
} }
foreach ([...$static_extensions, ...$shared_extensions] as $extension) { foreach ([...$static_extensions, ...$shared_extensions] as $extension) {

View File

@@ -83,13 +83,15 @@ class Extension
*/ */
public function getEnableArg(bool $shared = false): string public function getEnableArg(bool $shared = false): string
{ {
$escapedPath = str_replace("'", '', escapeshellarg(BUILD_ROOT_PATH)) !== BUILD_ROOT_PATH || str_contains(BUILD_ROOT_PATH, ' ') ? escapeshellarg(BUILD_ROOT_PATH) : BUILD_ROOT_PATH;
$_name = str_replace('_', '-', $this->name); $_name = str_replace('_', '-', $this->name);
return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) { return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) {
'enable' => '--enable-' . $_name . ($shared ? '=shared' : '') . ' ', 'enable' => '--enable-' . $_name . ($shared ? '=shared' : '') . ' ',
'enable-path' => '--enable-' . $_name . '=' . ($shared ? 'shared,' : '') . $escapedPath . ' ',
'with' => '--with-' . $_name . ($shared ? '=shared' : '') . ' ', 'with' => '--with-' . $_name . ($shared ? '=shared' : '') . ' ',
'with-prefix' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . '"' . BUILD_ROOT_PATH . '" ', 'with-path' => '--with-' . $_name . '=' . ($shared ? 'shared,' : '') . $escapedPath . ' ',
'none', 'custom' => '', 'none', 'custom' => '',
default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-prefix] ."), default => throw new WrongUsageException("argType does not accept {$arg_type}, use [enable/with/with-path] ."),
}; };
} }
@@ -515,8 +517,7 @@ class Extension
$sharedLibString = ''; $sharedLibString = '';
$staticLibString = ''; $staticLibString = '';
$staticLibs = $this->getLibFilesString(); $staticLibs = $this->getLibFilesString();
$staticLibs = str_replace(BUILD_LIB_PATH . '/lib', '-l', $staticLibs); $staticLibs = str_replace([BUILD_LIB_PATH . '/lib', '.a'], ['-l', ''], $staticLibs);
$staticLibs = str_replace('.a', '', $staticLibs);
$staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']); $staticLibs = explode('-l', $staticLibs . ' ' . $config['libs']);
foreach ($staticLibs as $lib) { foreach ($staticLibs as $lib) {
$lib = trim($lib); $lib = trim($lib);
@@ -532,6 +533,11 @@ class Extension
$sharedLibString .= '-l' . $lib . ' '; $sharedLibString .= '-l' . $lib . ' ';
} }
} }
// move -lstdc++ to static libraries because centos 7 the shared libstdc++ is incomplete
if (str_contains((string) getenv('PATH'), 'rh/devtoolset-10')) {
$staticLibString .= ' -lstdc++';
$sharedLibString = str_replace('-lstdc++', '', $sharedLibString);
}
return [trim($staticLibString), trim($sharedLibString)]; return [trim($staticLibString), trim($sharedLibString)];
} }

View File

@@ -182,22 +182,8 @@ abstract class LibraryBase
return LIB_STATUS_INSTALL_FAILED; return LIB_STATUS_INSTALL_FAILED;
} }
} }
foreach ($this->getStaticLibs() as $name) { if (!$this->isLibraryInstalled()) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { return $this->tryInstall($lock, true);
$this->tryInstall($lock, true);
return LIB_STATUS_OK;
}
}
foreach ($this->getHeaders() as $name) {
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
$this->tryInstall($lock, true);
return LIB_STATUS_OK;
}
}
// pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists
if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) {
$this->tryInstall($lock, true);
return LIB_STATUS_OK;
} }
return LIB_STATUS_ALREADY; return LIB_STATUS_ALREADY;
} }
@@ -240,28 +226,8 @@ abstract class LibraryBase
return LIB_STATUS_OK; return LIB_STATUS_OK;
} }
// check if these libraries exist, if not, invoke compilation and return the result status if (!$this->isLibraryInstalled()) {
foreach ($this->getStaticLibs() as $name) { return $this->tryBuild(true);
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
$this->tryBuild(true);
return LIB_STATUS_OK;
}
}
// header files the same
foreach ($this->getHeaders() as $name) {
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
$this->tryBuild(true);
return LIB_STATUS_OK;
}
}
// current library is package and binary file is not exists
if (Config::getLib(static::NAME, 'type', 'lib') === 'package') {
foreach ($this->getBinaryFiles() as $name) {
if (!file_exists(BUILD_BIN_PATH . "/{$name}")) {
$this->tryBuild(true);
return LIB_STATUS_OK;
}
}
} }
// if all the files exist at this point, skip the compilation process // if all the files exist at this point, skip the compilation process
return LIB_STATUS_ALREADY; return LIB_STATUS_ALREADY;
@@ -332,6 +298,15 @@ abstract class LibraryBase
return false; return false;
} }
/**
* Patch php-config after embed was built
* Example: imap requires -lcrypt
*/
public function patchPhpConfig(): bool
{
return false;
}
/** /**
* Build this library. * Build this library.
* *
@@ -341,7 +316,27 @@ abstract class LibraryBase
protected function install(): void protected function install(): void
{ {
// do something after extracting pre-built files, default do nothing. overwrite this method to do something // replace placeholders if BUILD_ROOT_PATH/.spc-extract-placeholder.json exists
$replace_item_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json';
if (!file_exists($replace_item_file)) {
return;
}
$replace_items = json_decode(file_get_contents($replace_item_file), true);
if (!is_array($replace_items)) {
throw new RuntimeException('Invalid placeholder file: ' . $replace_item_file);
}
$placeholders = get_pack_replace();
// replace placeholders in BUILD_ROOT_PATH
foreach ($replace_items as $item) {
$filepath = BUILD_ROOT_PATH . "/{$item}";
FileSystem::replaceFileStr(
$filepath,
array_values($placeholders),
array_keys($placeholders),
);
}
// remove placeholder file
unlink($replace_item_file);
} }
/** /**
@@ -388,4 +383,29 @@ abstract class LibraryBase
} }
} }
} }
protected function isLibraryInstalled(): bool
{
foreach (Config::getLib(static::NAME, 'static-libs', []) as $name) {
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
return false;
}
}
foreach (Config::getLib(static::NAME, 'headers', []) as $name) {
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
return false;
}
}
foreach (Config::getLib(static::NAME, 'pkg-configs', []) as $name) {
if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$name}.pc")) {
return false;
}
}
foreach (Config::getLib(static::NAME, 'bin', []) as $name) {
if (!file_exists(BUILD_BIN_PATH . "/{$name}")) {
return false;
}
}
return true;
}
} }

View File

@@ -4,7 +4,18 @@ declare(strict_types=1);
namespace SPC\builder; namespace SPC\builder;
/**
* Interface for library implementations
*
* This interface defines the basic contract that all library classes must implement.
* It provides a common way to identify and work with different library types.
*/
interface LibraryInterface interface LibraryInterface
{ {
/**
* Get the name of the library
*
* @return string The library name
*/
public function getName(): string; public function getName(): string;
} }

View File

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

View File

@@ -5,36 +5,47 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\builder\macos\MacOSBuilder;
use SPC\builder\windows\WindowsBuilder; use SPC\builder\windows\WindowsBuilder;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
#[CustomExt('grpc')] #[CustomExt('grpc')]
class grpc extends Extension class grpc extends Extension
{ {
public function patchBeforeBuildconf(): bool public function patchBeforeBuildconf(): bool
{ {
// soft link to the grpc source code
if ($this->builder instanceof WindowsBuilder) { if ($this->builder instanceof WindowsBuilder) {
// not support windows yet
throw new \RuntimeException('grpc extension does not support windows yet'); throw new \RuntimeException('grpc extension does not support windows yet');
} }
if (!is_link(SOURCE_PATH . '/php-src/ext/grpc')) { if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) {
if (is_dir($this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc')) { return false;
shell()->exec('ln -s ' . $this->builder->getLib('grpc')->getSourceDir() . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
} elseif (is_dir(BUILD_ROOT_PATH . '/grpc_php_ext_src')) {
shell()->exec('ln -s ' . BUILD_ROOT_PATH . '/grpc_php_ext_src ' . SOURCE_PATH . '/php-src/ext/grpc');
} else {
throw new \RuntimeException('Cannot find grpc source code');
}
$macos = $this->builder instanceof MacOSBuilder ? "\n" . ' LDFLAGS="$LDFLAGS -framework CoreFoundation"' : '';
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/GRPC_LIBDIR=.*$/m', 'GRPC_LIBDIR=' . BUILD_LIB_PATH . $macos);
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/ext/grpc/config.m4', '/SEARCH_PATH=.*$/m', 'SEARCH_PATH="' . BUILD_ROOT_PATH . '"');
return true;
} }
return false; // soft link to the grpc source code
if (is_dir($this->source_dir . '/src/php/ext/grpc')) {
shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
} else {
throw new \RuntimeException('Cannot find grpc source code');
}
if (SPCTarget::getTargetOS() === 'Darwin') {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/ext/grpc/config.m4',
'/GRPC_LIBDIR=.*$/m',
'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"'
);
}
return true;
}
public function patchBeforeConfigure(): bool
{
$util = new SPCConfigUtil($this->builder, ['libs_only_deps' => true]);
$config = $util->config(['grpc']);
$libs = $config['libs'];
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/configure', '-lgrpc', $libs);
return true;
} }
public function patchBeforeMake(): bool public function patchBeforeMake(): bool
@@ -43,9 +54,4 @@ class grpc extends Extension
GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes'); GlobalEnvManager::putenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS=' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . ' -Wno-strict-prototypes');
return true; return true;
} }
public function getUnixConfigureArg(bool $shared = false): string
{
return '--enable-grpc=' . BUILD_ROOT_PATH . '/grpc GRPC_LIB_SUBDIR=' . BUILD_LIB_PATH;
}
} }

View File

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

View File

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

View File

@@ -63,18 +63,6 @@ class BSDBuilder extends UnixBuilderBase
*/ */
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{ {
// ---------- Update extra-libs ----------
$extra_libs = $this->getOption('extra-libs', '');
// add libc++, some extensions or libraries need it (C++ cannot be linked statically)
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : '');
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
} else {
logger()->info('bloat linking');
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles())));
}
$this->setOption('extra-libs', $extra_libs);
$this->emitPatchPoint('before-php-buildconf'); $this->emitPatchPoint('before-php-buildconf');
SourcePatcher::patchBeforeBuildconf($this); SourcePatcher::patchBeforeBuildconf($this);

View File

@@ -9,13 +9,4 @@ class curl extends BSDLibraryBase
use \SPC\builder\unix\library\curl; use \SPC\builder\unix\library\curl;
public const NAME = 'curl'; public const NAME = 'curl';
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string
{
$libs = parent::getStaticLibFiles($style, $recursive, $include_self);
if ($this->builder->getLib('openssl')) {
$this->builder->setOption('extra-libs', $this->builder->getOption('extra-libs') . ' /usr/lib/libpthread.a /usr/lib/libdl.a');
}
return $libs;
}
} }

View File

@@ -11,6 +11,8 @@ use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
class LinuxBuilder extends UnixBuilderBase class LinuxBuilder extends UnixBuilderBase
{ {
@@ -25,22 +27,8 @@ class LinuxBuilder extends UnixBuilderBase
{ {
$this->options = $options; $this->options = $options;
// check musl-cross make installed if we use musl-cross-make
$arch = arch2gnu(php_uname('m'));
GlobalEnvManager::init(); GlobalEnvManager::init();
GlobalEnvManager::afterInit();
if (getenv('SPC_LIBC') === 'musl' && !SystemUtil::isMuslDist()) {
$this->setOptionIfNotExist('library_path', "LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
$this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\"");
$configure = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE');
$configure = "LD_LIBRARY_PATH=\"/usr/local/musl/{$arch}-linux-musl/lib\" " . $configure;
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE={$configure}");
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {
throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
}
}
// concurrency // concurrency
$this->concurrency = intval(getenv('SPC_CONCURRENCY')); $this->concurrency = intval(getenv('SPC_CONCURRENCY'));
@@ -70,17 +58,6 @@ class LinuxBuilder extends UnixBuilderBase
*/ */
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{ {
// ---------- Update extra-libs ----------
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// bloat means force-load all static libraries, even if they are not used
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
} else {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", array_filter($this->getAllStaticLibFiles())));
}
// add libstdc++, some extensions or libraries need it
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lstdc++ ' : '');
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
$cflags = $this->arch_c_flags; $cflags = $this->arch_c_flags;
f_putenv('CFLAGS=' . $cflags); f_putenv('CFLAGS=' . $cflags);
@@ -120,7 +97,7 @@ class LinuxBuilder extends UnixBuilderBase
$envs_build_php = SystemUtil::makeEnvVarString([ $envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'), 'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'), 'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'), 'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
]); ]);
@@ -288,7 +265,7 @@ class LinuxBuilder extends UnixBuilderBase
->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(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'); $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) { if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
$release = $matches[1]; $release = $matches[1];
$realLibName = 'libphp-' . $release . '.so'; $realLibName = 'libphp-' . $release . '.so';
@@ -322,11 +299,13 @@ class LinuxBuilder extends UnixBuilderBase
private function getMakeExtraVars(): array 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 [ return [
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LIBS' => getenv('SPC_EXTRA_LIBS') . ' ' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'), 'EXTRA_LIBS' => $config['libs'],
'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), 'EXTRA_LDFLAGS_PROGRAM' => "-L{$lib} {$static} -pie",
'EXTRA_LDFLAGS_PROGRAM' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM'),
]; ];
} }

View File

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

View File

@@ -9,13 +9,4 @@ class curl extends LinuxLibraryBase
use \SPC\builder\unix\library\curl; use \SPC\builder\unix\library\curl;
public const NAME = 'curl'; public const NAME = 'curl';
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string
{
$libs = parent::getStaticLibFiles($style, $recursive, $include_self);
if ($this->builder->getLib('openssl')) {
$libs .= ' -ldl -lpthread';
}
return $libs;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -67,8 +67,8 @@ class openssl extends LinuxLibraryBase
shell()->cd($this->source_dir)->initializeEnv($this) shell()->cd($this->source_dir)->initializeEnv($this)
->exec( ->exec(
"{$env} ./Configure no-shared {$extra} " . "{$env} ./Configure no-shared {$extra} " .
'--prefix=/ ' . '--prefix=' . BUILD_ROOT_PATH . ' ' .
'--libdir=lib ' . '--libdir=' . BUILD_LIB_PATH . ' ' .
'--openssldir=/etc/ssl ' . '--openssldir=/etc/ssl ' .
"{$zlib_extra}" . "{$zlib_extra}" .
'no-legacy ' . 'no-legacy ' .
@@ -76,28 +76,19 @@ class openssl extends LinuxLibraryBase
) )
->exec('make clean') ->exec('make clean')
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")
->exec("make install_sw DESTDIR={$destdir}"); ->exec('make install_sw');
$this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']); $this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']);
// patch for openssl 3.3.0+ // patch for openssl 3.3.0+
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
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=${pcfiledir}/../..' . "\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', 'Libs.private: ${libdir}/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")'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
} }
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string
{
$libFiles = parent::getStaticLibFiles($style, $recursive, $include_self);
if (!str_contains('-ldl -lpthread', $libFiles)) {
$libFiles .= ' -ldl -lpthread';
}
return $libFiles;
}
} }

View File

@@ -12,6 +12,7 @@ use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
use SPC\util\SPCConfigUtil;
class MacOSBuilder extends UnixBuilderBase class MacOSBuilder extends UnixBuilderBase
{ {
@@ -29,6 +30,7 @@ class MacOSBuilder extends UnixBuilderBase
// apply global environment variables // apply global environment variables
GlobalEnvManager::init(); GlobalEnvManager::init();
GlobalEnvManager::afterInit();
// ---------- set necessary compile vars ---------- // ---------- set necessary compile vars ----------
// concurrency // concurrency
@@ -87,21 +89,6 @@ class MacOSBuilder extends UnixBuilderBase
*/ */
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{ {
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// ---------- Update extra-libs ----------
// add macOS frameworks
$extra_libs .= (empty($extra_libs) ? '' : ' ') . $this->getFrameworks(true);
// add libc++, some extensions or libraries need it (C++ cannot be linked statically)
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : '');
// bloat means force-load all static libraries, even if they are not used
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
} else {
logger()->info('bloat linking');
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles())));
}
f_putenv('SPC_EXTRA_LIBS=' . $extra_libs);
$this->emitPatchPoint('before-php-buildconf'); $this->emitPatchPoint('before-php-buildconf');
SourcePatcher::patchBeforeBuildconf($this); SourcePatcher::patchBeforeBuildconf($this);
@@ -129,7 +116,7 @@ class MacOSBuilder extends UnixBuilderBase
$envs_build_php = SystemUtil::makeEnvVarString([ $envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'), 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'), 'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'), 'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'), 'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
]); ]);
@@ -290,9 +277,11 @@ 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'));
return [ return [
'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'),
'EXTRA_LIBS' => getenv('SPC_EXTRA_LIBS') . ' ' . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'), 'EXTRA_LDFLAGS_PROGRAM' => '-L' . BUILD_LIB_PATH,
'EXTRA_LIBS' => $config['libs'],
]; ];
} }
} }

View File

@@ -21,7 +21,7 @@ class icu extends MacOSLibraryBase
->exec("make -j{$this->builder->concurrency}") ->exec("make -j{$this->builder->concurrency}")
->exec('make install'); ->exec('make install');
$this->patchPkgconfPrefix(['icu-i18n.pc', 'icu-io.pc', 'icu-uc.pc'], PKGCONF_PATCH_PREFIX); $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX);
FileSystem::removeDir(BUILD_LIB_PATH . '/icu'); FileSystem::removeDir(BUILD_LIB_PATH . '/icu');
} }
} }

View File

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

View File

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

View File

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

View File

@@ -37,8 +37,6 @@ class openssl extends MacOSLibraryBase
*/ */
protected function build(): void protected function build(): void
{ {
[$lib,,$destdir] = SEPARATED_PATH;
// lib:zlib // lib:zlib
$extra = ''; $extra = '';
$ex_lib = ''; $ex_lib = '';
@@ -52,24 +50,24 @@ class openssl extends MacOSLibraryBase
shell()->cd($this->source_dir)->initializeEnv($this) shell()->cd($this->source_dir)->initializeEnv($this)
->exec( ->exec(
"./Configure no-shared {$extra} " . "./Configure no-shared {$extra} " .
'--prefix=/ ' . // use prefix=/ '--prefix=' . BUILD_ROOT_PATH . ' ' . // use prefix=/
"--libdir={$lib} " . '--libdir=lib ' .
'--openssldir=/etc/ssl ' . '--openssldir=/etc/ssl ' .
"darwin64-{$arch}-cc" "darwin64-{$arch}-cc"
) )
->exec('make clean') ->exec('make clean')
->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")
->exec("make install_sw DESTDIR={$destdir}"); ->exec('make install_sw');
$this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']); $this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']);
// patch for openssl 3.3.0+ // patch for openssl 3.3.0+
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=${pcfiledir}/../..' . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/openssl.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
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=${pcfiledir}/../..' . "\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', 'Libs.private: ${libdir}/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")'); 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,11 +4,12 @@ declare(strict_types=1);
namespace SPC\builder\traits; namespace SPC\builder\traits;
use SPC\builder\LibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCConfigUtil;
trait UnixLibraryTrait trait UnixLibraryTrait
{ {
@@ -17,44 +18,13 @@ trait UnixLibraryTrait
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true, bool $include_self = true): string public function getStaticLibFiles(bool $include_self = true): string
{ {
$libs = $include_self ? [$this] : []; $libs = $include_self ? [$this] : [];
if ($recursive) { array_unshift($libs, ...array_values($this->getDependencies(recursive: true)));
array_unshift($libs, ...array_values($this->getDependencies(recursive: true))); $config = new SPCConfigUtil($this->builder, options: ['libs_only_deps' => true, 'absolute_libs' => true]);
} $res = $config->config(libraries: array_map(fn ($x) => $x->getName(), $libs));
return $res['libs'];
$sep = match ($style) {
'autoconf' => ' ',
'cmake' => ';',
default => throw new RuntimeException('style only support autoconf and cmake'),
};
$ret = [];
/** @var LibraryBase $lib */
foreach ($libs as $lib) {
$libFiles = [];
foreach ($lib->getStaticLibs() as $name) {
$name = str_replace(' ', '\ ', FileSystem::convertPath(BUILD_LIB_PATH . "/{$name}"));
$name = str_replace('"', '\"', $name);
$libFiles[] = $name;
}
array_unshift($ret, implode($sep, $libFiles));
}
return implode($sep, $ret);
}
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function makeAutoconfEnv(?string $prefix = null): string
{
if ($prefix === null) {
$prefix = str_replace('-', '_', strtoupper(static::NAME));
}
return $prefix . '_CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ' .
$prefix . '_LIBS="' . $this->getStaticLibFiles() . '"';
} }
/** /**
@@ -64,9 +34,12 @@ trait UnixLibraryTrait
* @throws FileSystemException * @throws FileSystemException
* @throws RuntimeException * @throws RuntimeException
*/ */
public function patchPkgconfPrefix(array $files, int $patch_option = PKGCONF_PATCH_ALL, ?array $custom_replace = null): void public function patchPkgconfPrefix(array $files = [], int $patch_option = PKGCONF_PATCH_ALL, ?array $custom_replace = null): void
{ {
logger()->info('Patching library [' . static::NAME . '] pkgconfig'); logger()->info('Patching library [' . static::NAME . '] pkgconfig');
if ($files === [] && ($conf_pc = Config::getLib(static::NAME, 'pkg-configs', [])) !== []) {
$files = array_map(fn ($x) => "{$x}.pc", $conf_pc);
}
foreach ($files as $name) { foreach ($files as $name) {
$realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name); $realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name);
if ($realpath === false) { if ($realpath === false) {
@@ -75,7 +48,7 @@ trait UnixLibraryTrait
logger()->debug('Patching ' . $realpath); logger()->debug('Patching ' . $realpath);
// replace prefix // replace prefix
$file = FileSystem::readFile($realpath); $file = FileSystem::readFile($realpath);
$file = ($patch_option & PKGCONF_PATCH_PREFIX) === PKGCONF_PATCH_PREFIX ? preg_replace('/^prefix\s*=.*$/m', 'prefix=${pcfiledir}/../..', $file) : $file; $file = ($patch_option & PKGCONF_PATCH_PREFIX) === PKGCONF_PATCH_PREFIX ? preg_replace('/^prefix\s*=.*$/m', 'prefix=' . BUILD_ROOT_PATH, $file) : $file;
$file = ($patch_option & PKGCONF_PATCH_EXEC_PREFIX) === PKGCONF_PATCH_EXEC_PREFIX ? preg_replace('/^exec_prefix\s*=.*$/m', 'exec_prefix=${prefix}', $file) : $file; $file = ($patch_option & PKGCONF_PATCH_EXEC_PREFIX) === PKGCONF_PATCH_EXEC_PREFIX ? preg_replace('/^exec_prefix\s*=.*$/m', 'exec_prefix=${prefix}', $file) : $file;
$file = ($patch_option & PKGCONF_PATCH_LIBDIR) === PKGCONF_PATCH_LIBDIR ? preg_replace('/^libdir\s*=.*$/m', 'libdir=${prefix}/lib', $file) : $file; $file = ($patch_option & PKGCONF_PATCH_LIBDIR) === PKGCONF_PATCH_LIBDIR ? preg_replace('/^libdir\s*=.*$/m', 'libdir=${prefix}/lib', $file) : $file;
$file = ($patch_option & PKGCONF_PATCH_INCLUDEDIR) === PKGCONF_PATCH_INCLUDEDIR ? preg_replace('/^includedir\s*=.*$/m', 'includedir=${prefix}/include', $file) : $file; $file = ($patch_option & PKGCONF_PATCH_INCLUDEDIR) === PKGCONF_PATCH_INCLUDEDIR ? preg_replace('/^includedir\s*=.*$/m', 'includedir=${prefix}/include', $file) : $file;

View File

@@ -5,9 +5,6 @@ declare(strict_types=1);
namespace SPC\builder\unix; namespace SPC\builder\unix;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\builder\freebsd\library\BSDLibraryBase;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
@@ -17,6 +14,7 @@ use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
use SPC\util\SPCTarget;
abstract class UnixBuilderBase extends BuilderBase abstract class UnixBuilderBase extends BuilderBase
{ {
@@ -26,85 +24,6 @@ abstract class UnixBuilderBase extends BuilderBase
/** @var string C++ flags */ /** @var string C++ flags */
public string $arch_cxx_flags; public string $arch_cxx_flags;
/**
* @throws WrongUsageException
* @throws FileSystemException
*/
public function getAllStaticLibFiles(): array
{
$libs = [];
// reorder libs
foreach ($this->libs as $lib) {
foreach ($lib->getDependencies() as $dep) {
$libs[] = $dep;
}
$libs[] = $lib;
}
$libFiles = [];
$libNames = [];
// merge libs
foreach ($libs as $lib) {
if (!in_array($lib::NAME, $libNames, true)) {
$libNames[] = $lib::NAME;
array_unshift($libFiles, ...$lib->getStaticLibs());
}
}
return array_map(fn ($x) => realpath(BUILD_LIB_PATH . "/{$x}"), $libFiles);
}
/**
* Generate configure flags
*/
public function makeAutoconfFlags(int $flag = AUTOCONF_ALL): string
{
$extra = '';
// TODO: add auto pkg-config support
if (($flag & AUTOCONF_LIBS) === AUTOCONF_LIBS) {
$extra .= 'LIBS="' . BUILD_LIB_PATH . '" ';
}
if (($flag & AUTOCONF_CFLAGS) === AUTOCONF_CFLAGS) {
$extra .= 'CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ';
}
if (($flag & AUTOCONF_CPPFLAGS) === AUTOCONF_CPPFLAGS) {
$extra .= 'CPPFLAGS="-I' . BUILD_INCLUDE_PATH . '" ';
}
if (($flag & AUTOCONF_LDFLAGS) === AUTOCONF_LDFLAGS) {
$extra .= 'LDFLAGS="-L' . BUILD_LIB_PATH . '" ';
}
return $extra;
}
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
public function makeAutoconfArgs(string $name, array $libSpecs): string
{
$ret = '';
foreach ($libSpecs as $libName => $arr) {
$lib = $this->getLib($libName);
if ($lib === null && str_starts_with($libName, 'lib')) {
$lib = $this->getLib(substr($libName, 3));
}
$arr = $arr ?? [];
$disableArgs = $arr[0] ?? null;
$prefix = $arr[1] ?? null;
if ($lib instanceof LinuxLibraryBase || $lib instanceof MacOSLibraryBase || $lib instanceof BSDLibraryBase) {
logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support");
$ret .= "--with-{$libName}=yes " . $lib->makeAutoconfEnv($prefix) . ' ';
} else {
logger()->info("{$name} \033[31;1mwithout\033[0;1m {$libName} support");
$ret .= ($disableArgs ?? "--with-{$libName}=no") . ' ';
}
}
return rtrim($ret);
}
public function proveLibs(array $sorted_libraries): void public function proveLibs(array $sorted_libraries): void
{ {
// search all supported libs // search all supported libs
@@ -200,7 +119,7 @@ abstract class UnixBuilderBase extends BuilderBase
$util = new SPCConfigUtil($this); $util = new SPCConfigUtil($this);
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs')); $config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}"; $lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { if (SPCTarget::isStatic()) {
$lens .= ' -static'; $lens .= ' -static';
} }
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
@@ -209,11 +128,22 @@ abstract class UnixBuilderBase extends BuilderBase
} }
// if someone changed to --enable-embed=shared, we need to add LD_LIBRARY_PATH // if someone changed to --enable-embed=shared, we need to add LD_LIBRARY_PATH
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$ext_path = 'LD_LIBRARY_PATH=' . BUILD_ROOT_PATH . '/lib:$LD_LIBRARY_PATH '; if (PHP_OS_FAMILY === 'Darwin') {
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '/lib/libphp.a'); $ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH ';
} else {
$ext_path = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$LD_LIBRARY_PATH ';
}
FileSystem::removeFileIfExists(BUILD_LIB_PATH . '/libphp.a');
} else { } else {
$ext_path = ''; $ext_path = '';
FileSystem::removeFileIfExists(BUILD_ROOT_PATH . '/lib/libphp.so'); $suffix = PHP_OS_FAMILY === 'Darwin' ? 'dylib' : 'so';
foreach (glob(BUILD_LIB_PATH . "/libphp*.{$suffix}") as $file) {
unlink($file);
}
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
} }
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed'); [$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
@@ -228,8 +158,9 @@ abstract class UnixBuilderBase extends BuilderBase
if (!file_exists($frankenphp)) { if (!file_exists($frankenphp)) {
throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp); throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp);
} }
$prefix = PHP_OS_FAMILY === 'Darwin' ? 'DYLD_' : 'LD_';
[$ret, $output] = shell() [$ret, $output] = shell()
->setEnv(['LD_LIBRARY_PATH' => BUILD_LIB_PATH]) ->setEnv(["{$prefix}LIBRARY_PATH" => BUILD_LIB_PATH])
->execWithResult("{$frankenphp} version"); ->execWithResult("{$frankenphp} version");
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) { if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']'); throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']');
@@ -293,6 +224,11 @@ abstract class UnixBuilderBase extends BuilderBase
$php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str); $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
} }
foreach ($this->getLibs() as $lib) {
if ($lib->patchPhpConfig()) {
logger()->debug("Library {$lib->getName()} patched php-config");
}
}
} }
/** /**
@@ -334,7 +270,7 @@ abstract class UnixBuilderBase extends BuilderBase
$debugFlags = $this->getOption('no-strip') ? "'-w -s' " : ''; $debugFlags = $this->getOption('no-strip') ? "'-w -s' " : '';
$extLdFlags = "-extldflags '-pie'"; $extLdFlags = "-extldflags '-pie'";
$muslTags = ''; $muslTags = '';
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') { if (SPCTarget::isStatic()) {
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'"; $extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000'";
$muslTags = 'static_build,'; $muslTags = 'static_build,';
} }

View File

@@ -21,10 +21,10 @@ trait curl
UnixCMakeExecutor::create($this) UnixCMakeExecutor::create($this)
->optionalLib('openssl', '-DCURL_USE_OPENSSL=ON -DCURL_CA_BUNDLE=OFF -DCURL_CA_PATH=OFF -DCURL_CA_FALLBACK=ON', '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF') ->optionalLib('openssl', '-DCURL_USE_OPENSSL=ON -DCURL_CA_BUNDLE=OFF -DCURL_CA_PATH=OFF -DCURL_CA_FALLBACK=ON', '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF')
->optionalLib('brotli', ...cmake_boolean_args('CURL_BROTLI')) ->optionalLib('brotli', ...cmake_boolean_args('CURL_BROTLI'))
->optionalLib('libssh2', fn ($lib) => "-DLIBSSH2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DLIBSSH2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DCURL_USE_LIBSSH2=OFF') ->optionalLib('libssh2', ...cmake_boolean_args('CURL_USE_LIBSSH2'))
->optionalLib('nghttp2', fn ($lib) => "-DUSE_NGHTTP2=ON -DNGHTTP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP2=OFF') ->optionalLib('nghttp2', ...cmake_boolean_args('USE_NGHTTP2'))
->optionalLib('nghttp3', fn ($lib) => "-DUSE_NGHTTP3=ON -DNGHTTP3_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGHTTP3_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGHTTP3=OFF') ->optionalLib('nghttp3', ...cmake_boolean_args('USE_NGHTTP3'))
->optionalLib('ngtcp2', fn ($lib) => "-DUSE_NGTCP2=ON -DNGNGTCP2_LIBRARY=\"{$lib->getStaticLibFiles(style: 'cmake')}\" -DNGNGTCP2_INCLUDE_DIR={$lib->getIncludeDir()}", '-DUSE_NGTCP2=OFF') ->optionalLib('ngtcp2', ...cmake_boolean_args('USE_NGTCP2'))
->optionalLib('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true)) ->optionalLib('ldap', ...cmake_boolean_args('CURL_DISABLE_LDAP', true))
->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'))

View File

@@ -4,22 +4,55 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\SystemUtil;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget;
trait grpc trait grpc
{ {
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileStr(
$this->source_dir . '/third_party/re2/util/pcre.h',
["#define UTIL_PCRE_H_\n#include <stdint.h>", '#define UTIL_PCRE_H_'],
['#define UTIL_PCRE_H_', "#define UTIL_PCRE_H_\n#include <stdint.h>"],
);
return true;
}
protected function build(): void protected function build(): void
{ {
shell()->cd($this->source_dir) $cmake = UnixCMakeExecutor::create($this)
->exec('EXTRA_DEFINES=GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK EMBED_OPENSSL=false CXXFLAGS="-L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '" make static -j' . $this->builder->concurrency); ->setBuildDir("{$this->source_dir}/avoid_BUILD_file_conflict")
copy($this->source_dir . '/libs/opt/libgrpc.a', BUILD_LIB_PATH . '/libgrpc.a'); ->addConfigureArgs(
copy($this->source_dir . '/libs/opt/libboringssl.a', BUILD_LIB_PATH . '/libboringssl.a'); '-DgRPC_INSTALL_BINDIR=' . BUILD_BIN_PATH,
if (!file_exists(BUILD_LIB_PATH . '/libcares.a')) { '-DgRPC_INSTALL_LIBDIR=' . BUILD_LIB_PATH,
copy($this->source_dir . '/libs/opt/libcares.a', BUILD_LIB_PATH . '/libcares.a'); '-DgRPC_INSTALL_SHAREDIR=' . BUILD_ROOT_PATH . '/share/grpc',
'-DCMAKE_C_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK -L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '"',
'-DCMAKE_CXX_FLAGS="-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK -L' . BUILD_LIB_PATH . ' -I' . BUILD_INCLUDE_PATH . '"',
'-DgRPC_BUILD_CODEGEN=OFF',
'-DgRPC_DOWNLOAD_ARCHIVES=OFF',
'-DgRPC_BUILD_TESTS=OFF',
// providers
'-DgRPC_ZLIB_PROVIDER=package',
'-DgRPC_CARES_PROVIDER=package',
'-DgRPC_SSL_PROVIDER=package',
);
if (PHP_OS_FAMILY === 'Linux' && SPCTarget::isStatic() && !SystemUtil::isMuslDist()) {
$cmake->addConfigureArgs(
'-DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++"',
'-DCMAKE_SHARED_LINKER_FLAGS="-static-libgcc -static-libstdc++"',
'-DCMAKE_CXX_STANDARD_LIBRARIES="-static-libgcc -static-libstdc++"',
);
} }
FileSystem::copyDir($this->source_dir . '/include/grpc', BUILD_INCLUDE_PATH . '/grpc');
FileSystem::copyDir($this->source_dir . '/include/grpc++', BUILD_INCLUDE_PATH . '/grpc++'); $cmake->build();
FileSystem::copyDir($this->source_dir . '/include/grpcpp', BUILD_INCLUDE_PATH . '/grpcpp');
FileSystem::copyDir($this->source_dir . '/src/php/ext/grpc', BUILD_ROOT_PATH . '/grpc_php_ext_src'); $re2Content = file_get_contents($this->source_dir . '/third_party/re2/re2.pc');
$re2Content = 'prefix=' . BUILD_ROOT_PATH . "\nexec_prefix=\${prefix}\n" . $re2Content;
file_put_contents(BUILD_LIB_PATH . '/pkgconfig/re2.pc', $re2Content);
$this->patchPkgconfPrefix(['grpc++.pc', 'grpc.pc', 'grpc++_unsecure.pc', 'grpc_unsecure.pc', 're2.pc']);
} }
} }

View File

@@ -17,6 +17,7 @@ trait icu
protected function install(): void protected function install(): void
{ {
parent::install();
$icu_config = BUILD_ROOT_PATH . '/bin/icu-config'; $icu_config = BUILD_ROOT_PATH . '/bin/icu-config';
FileSystem::replaceFileStr($icu_config, '{BUILD_ROOT_PATH}', BUILD_ROOT_PATH); FileSystem::replaceFileStr($icu_config, '{BUILD_ROOT_PATH}', BUILD_ROOT_PATH);
} }

View File

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

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
trait jbig
{
/**
* @throws FileSystemException
*/
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileStr($this->source_dir . '/Makefile', 'CFLAGS = -O2 -W -Wno-unused-result', 'CFLAGS = -O2 -W -Wno-unused-result -fPIC');
return true;
}
/**
* @throws RuntimeException
*/
protected function build(): void
{
shell()->cd($this->source_dir)->initializeEnv($this)
->exec("make -j{$this->builder->concurrency} {$this->builder->getEnvString()} lib")
->exec('cp libjbig/libjbig.a ' . BUILD_LIB_PATH)
->exec('cp libjbig/libjbig85.a ' . BUILD_LIB_PATH)
->exec('cp libjbig/jbig.h ' . BUILD_INCLUDE_PATH)
->exec('cp libjbig/jbig85.h ' . BUILD_INCLUDE_PATH)
->exec('cp libjbig/jbig_ar.h ' . BUILD_INCLUDE_PATH);
}
}

View File

@@ -6,12 +6,13 @@ namespace SPC\builder\unix\library;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait ldap trait ldap
{ {
public function patchBeforeBuild(): bool public function patchBeforeBuild(): bool
{ {
$extra = getenv('SPC_LIBC') === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : ''; $extra = SPCTarget::getLibc() === 'glibc' ? '-ldl -lpthread -lm -lresolv -lutil' : '';
FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra); FileSystem::replaceFileStr($this->source_dir . '/configure', '"-lssl -lcrypto', '"-lssl -lcrypto -lz ' . $extra);
return true; return true;
} }

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\util\executor\UnixCMakeExecutor;
trait lerc
{
/**
* @throws FileSystemException
* @throws RuntimeException
*/
protected function build(): void
{
UnixCMakeExecutor::create($this)
->build();
}
}

View File

@@ -68,6 +68,7 @@ trait libevent
protected function install(): void protected function install(): void
{ {
parent::install();
FileSystem::replaceFileStr( FileSystem::replaceFileStr(
BUILD_LIB_PATH . '/cmake/libevent/LibeventTargets-static.cmake', BUILD_LIB_PATH . '/cmake/libevent/LibeventTargets-static.cmake',
'{BUILD_ROOT_PATH}', '{BUILD_ROOT_PATH}',

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\util\executor\UnixCMakeExecutor;
use SPC\util\SPCTarget;
trait libjxl
{
protected function build(): void
{
UnixCMakeExecutor::create($this)
->addConfigureArgs('-DJPEGXL_ENABLE_TOOLS=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_EXAMPLES=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_MANPAGES=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_BENCHMARK=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_PLUGINS=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_SJPOEG=ON')
->addConfigureArgs('-DJPEGXL_ENABLE_JNI=OFF')
->addConfigureArgs('-DJPEGXL_ENABLE_TRANSCODE_JPEG=ON')
->addConfigureArgs('-DJPEGXL_STATIC=' . (SPCTarget::isStatic() ? 'ON' : 'OFF'))
->addConfigureArgs('-DJPEGXL_FORCE_SYSTEM_BROTLI=ON')
->addConfigureArgs('-DBUILD_TESTING=OFF')
->build();
}
}

View File

@@ -6,7 +6,9 @@ namespace SPC\builder\unix\library;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\executor\UnixAutoconfExecutor;
use SPC\util\SPCTarget;
trait libtiff trait libtiff
{ {
@@ -16,7 +18,15 @@ trait libtiff
*/ */
protected function build(): void protected function build(): void
{ {
$libcpp = SPCTarget::getTargetOS() === 'Linux' ? '-lstdc++' : '-lc++';
FileSystem::replaceFileStr($this->source_dir . '/configure', '-lwebp', '-lwebp -lsharpyuv');
FileSystem::replaceFileStr($this->source_dir . '/configure', '-l"$lerc_lib_name"', '-l"$lerc_lib_name" ' . $libcpp);
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->optionalLib('lerc', '--enable-lerc', '--disable-lerc')
->optionalLib('zstd', '--enable-zstd', '--disable-zstd')
->optionalLib('libwebp', '--enable-webp', '--disable-webp')
->optionalLib('xz', '--enable-lzma', '--disable-lzma')
->optionalLib('jbig', '--enable-jbig', '--disable-jbig')
->configure( ->configure(
// zlib deps // zlib deps
'--enable-zlib', '--enable-zlib',
@@ -24,16 +34,15 @@ trait libtiff
"--with-zlib-lib-dir={$this->getLibDir()}", "--with-zlib-lib-dir={$this->getLibDir()}",
// libjpeg deps // libjpeg deps
'--enable-jpeg', '--enable-jpeg',
'--disable-old-jpeg',
'--disable-jpeg12',
"--with-jpeg-include-dir={$this->getIncludeDir()}", "--with-jpeg-include-dir={$this->getIncludeDir()}",
"--with-jpeg-lib-dir={$this->getLibDir()}", "--with-jpeg-lib-dir={$this->getLibDir()}",
// We disabled lzma, zstd, webp, libdeflate by default to reduce the size of the binary '--disable-old-jpeg',
'--disable-lzma', '--disable-jpeg12',
'--disable-zstd',
'--disable-webp',
'--disable-libdeflate', '--disable-libdeflate',
'--disable-tools',
'--disable-contrib',
'--disable-cxx', '--disable-cxx',
'--without-x',
) )
->make(); ->make();
$this->patchPkgconfPrefix(['libtiff-4.pc']); $this->patchPkgconfPrefix(['libtiff-4.pc']);

View File

@@ -22,8 +22,7 @@ trait libwebp
->addConfigureArgs('-DWEBP_BUILD_EXTRAS=ON') ->addConfigureArgs('-DWEBP_BUILD_EXTRAS=ON')
->build(); ->build();
// patch pkgconfig // patch pkgconfig
$this->patchPkgconfPrefix(['libsharpyuv.pc', 'libwebp.pc', 'libwebpdecoder.pc', 'libwebpdemux.pc', 'libwebpmux.pc'], PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR); $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR);
$this->patchPkgconfPrefix(['libsharpyuv.pc'], PKGCONF_PATCH_CUSTOM, ['/^includedir=.*$/m', 'includedir=${prefix}/include/webp']); $this->patchPkgconfPrefix(['libsharpyuv.pc'], PKGCONF_PATCH_CUSTOM, ['/^includedir=.*$/m', 'includedir=${prefix}/include/webp']);
$this->patchPkgconfPrefix(['libwebp.pc'], PKGCONF_PATCH_CUSTOM, ['/-lwebp$/m', '-lwebp -lsharpyuv']);
} }
} }

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase; use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
@@ -20,11 +21,12 @@ trait libxslt
protected function build(): void protected function build(): void
{ {
$static_libs = $this instanceof LinuxLibraryBase ? $this->getStaticLibFiles(include_self: false) : ''; $static_libs = $this instanceof LinuxLibraryBase ? $this->getStaticLibFiles(include_self: false) : '';
$cpp = $this instanceof MacOSLibraryBase ? '-lc++' : '-lstdc++';
$ac = UnixAutoconfExecutor::create($this) $ac = UnixAutoconfExecutor::create($this)
->appendEnv([ ->appendEnv([
'CFLAGS' => "-I{$this->getIncludeDir()}", 'CFLAGS' => "-I{$this->getIncludeDir()}",
'LDFLAGS' => "-L{$this->getLibDir()}", 'LDFLAGS' => "-L{$this->getLibDir()}",
'LIBS' => "{$static_libs} -lstdc++", 'LIBS' => "{$static_libs} {$cpp}",
]) ])
->addConfigureArgs( ->addConfigureArgs(
'--without-python', '--without-python',
@@ -33,9 +35,15 @@ trait libxslt
'--without-debugger', '--without-debugger',
"--with-libxml-prefix={$this->getBuildRootPath()}", "--with-libxml-prefix={$this->getBuildRootPath()}",
); );
$ac->exec("{$this->builder->getOption('library_path')} {$this->builder->getOption('ld_library_path')} ./configure {$ac->getConfigureArgsString()}")->make(); if (getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH') && getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH')) {
$ac->appendEnv([
'LD_LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LD_LIBRARY_PATH'),
'LIBRARY_PATH' => getenv('SPC_LINUX_DEFAULT_LIBRARY_PATH'),
]);
}
$ac->configure()->make();
$this->patchPkgconfPrefix(['libexslt.pc']); $this->patchPkgconfPrefix(['libexslt.pc', 'libxslt.pc']);
$this->patchLaDependencyPrefix(); $this->patchLaDependencyPrefix();
shell()->cd(BUILD_LIB_PATH) shell()->cd(BUILD_LIB_PATH)
->exec("ar -t libxslt.a | grep '\\.a$' | xargs -n1 ar d libxslt.a") ->exec("ar -t libxslt.a | grep '\\.a$' | xargs -n1 ar d libxslt.a")

View File

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

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
@@ -19,7 +21,7 @@ trait ngtcp2
protected function build(): void protected function build(): void
{ {
UnixAutoconfExecutor::create($this) UnixAutoconfExecutor::create($this)
->optionalLib('openssl', fn ($lib) => implode(' ', [ ->optionalLib('openssl', fn (LinuxLibraryBase|MacOSLibraryBase $lib) => implode(' ', [
'--with-openssl=yes', '--with-openssl=yes',
"OPENSSL_LIBS=\"{$lib->getStaticLibFiles()}\"", "OPENSSL_LIBS=\"{$lib->getStaticLibFiles()}\"",
"OPENSSL_CFLAGS=\"-I{$lib->getIncludeDir()}\"", "OPENSSL_CFLAGS=\"-I{$lib->getIncludeDir()}\"",
@@ -29,7 +31,7 @@ trait ngtcp2
->optionalLib('jemalloc', ...ac_with_args('jemalloc', true)) ->optionalLib('jemalloc', ...ac_with_args('jemalloc', true))
->optionalLib( ->optionalLib(
'brotli', 'brotli',
fn ($lib) => implode(' ', [ fn (LinuxLibraryBase|MacOSLibraryBase $lib) => implode(' ', [
'--with-brotlidec=yes', '--with-brotlidec=yes',
"LIBBROTLIDEC_CFLAGS=\"-I{$lib->getIncludeDir()}\"", "LIBBROTLIDEC_CFLAGS=\"-I{$lib->getIncludeDir()}\"",
"LIBBROTLIDEC_LIBS=\"{$lib->getStaticLibFiles()}\"", "LIBBROTLIDEC_LIBS=\"{$lib->getStaticLibFiles()}\"",

View File

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

View File

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

View File

@@ -21,6 +21,7 @@ trait xz
'--disable-scripts', '--disable-scripts',
'--disable-doc', '--disable-doc',
'--with-libiconv', '--with-libiconv',
'--bindir=/tmp/xz', // xz binary will corrupt `tar` command, that's really strange.
) )
->make(); ->make();
$this->patchPkgconfPrefix(['liblzma.pc']); $this->patchPkgconfPrefix(['liblzma.pc']);

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,6 @@ abstract class BuildCommand extends BaseCommand
} }
$this->addOption('with-clean', null, null, 'fresh build, remove `source` and `buildroot` dir before build'); $this->addOption('with-clean', null, null, 'fresh build, remove `source` and `buildroot` dir before build');
$this->addOption('bloat', null, null, 'add all libraries into binary');
$this->addOption('rebuild', 'r', null, 'Delete old build and rebuild'); $this->addOption('rebuild', 'r', null, 'Delete old build and rebuild');
$this->addOption('enable-zts', null, null, 'enable ZTS support'); $this->addOption('enable-zts', null, null, 'enable ZTS support');
} }

View File

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

View File

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

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace SPC\command; namespace SPC\command;
use SPC\builder\linux\SystemUtil;
use SPC\builder\traits\UnixSystemUtilTrait; use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
@@ -14,6 +13,7 @@ use SPC\store\Config;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@@ -223,8 +223,8 @@ class DownloadCommand extends BaseCommand
'{name}' => $source, '{name}' => $source,
'{arch}' => arch2gnu(php_uname('m')), '{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY), '{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default', '{libc}' => SPCTarget::getLibc() ?? 'default',
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', '{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
]; ];
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern')); $find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
// find filename in asset list // find filename in asset list
@@ -245,11 +245,14 @@ class DownloadCommand extends BaseCommand
} }
// if download failed, we will try to download alternative sources // if download failed, we will try to download alternative sources
logger()->warning("Download failed: {$e->getMessage()}"); logger()->warning("Download failed: {$e->getMessage()}");
logger()->notice("Trying to download alternative sources for {$source}");
$alt_sources = Config::getSource($source)['alt'] ?? null; $alt_sources = Config::getSource($source)['alt'] ?? null;
if ($alt_sources === null) { if ($alt_sources === null) {
$alt_config = array_merge($config, $this->getDefaultAlternativeSource($source)); logger()->warning("No alternative sources found for {$source}, using default alternative source");
$alt_config = array_merge($config, Downloader::getDefaultAlternativeSource($source));
} elseif ($alt_sources === false) {
throw new DownloaderException("No alternative sources found for {$source}, skipping alternative download");
} else { } else {
logger()->notice("Trying to download alternative sources for {$source}");
$alt_config = array_merge($config, $alt_sources); $alt_config = array_merge($config, $alt_sources);
} }
Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list)); Downloader::downloadSource($source, $alt_config, $force_all || in_array($source, $force_list));
@@ -395,27 +398,4 @@ class DownloadCommand extends BaseCommand
} }
return static::FAILURE; return static::FAILURE;
} }
private function getDefaultAlternativeSource(string $source_name): array
{
return [
'type' => 'custom',
'func' => function (bool $force, array $source, int $download_as) use ($source_name) {
logger()->debug("Fetching alternative source for {$source_name}");
// get from dl.static-php.dev
$url = "https://dl.static-php.dev/static-php-cli/deps/spc-download-mirror/{$source_name}/?format=json";
$json = json_decode(Downloader::curlExec(url: $url, retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)), true);
if (!is_array($json)) {
throw new RuntimeException('failed http fetch');
}
$item = $json[0] ?? null;
if ($item === null) {
throw new RuntimeException('failed to parse json');
}
$full_url = 'https://dl.static-php.dev' . $item['full_path'];
$filename = basename($item['full_path']);
Downloader::downloadFile($source_name, $full_url, $filename, $source['path'] ?? null, $download_as);
},
];
}
} }

View File

@@ -24,6 +24,8 @@ class InstallPkgCommand extends BaseCommand
$this->addArgument('packages', InputArgument::REQUIRED, 'The packages will be installed, comma separated'); $this->addArgument('packages', InputArgument::REQUIRED, 'The packages will be installed, comma separated');
$this->addOption('shallow-clone', null, null, 'Clone shallow'); $this->addOption('shallow-clone', null, null, 'Clone shallow');
$this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"'); $this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"');
$this->addOption('no-alt', null, null, 'Do not download alternative packages');
$this->addOption('skip-extract', null, null, 'Skip package extraction, just download the package archive');
} }
/** /**
@@ -66,10 +68,20 @@ class InstallPkgCommand extends BaseCommand
$new_config['filename'] = $config['filename']; $new_config['filename'] = $config['filename'];
} }
logger()->info("Installing source {$pkg} from custom url [{$ni}/{$cnt}]"); logger()->info("Installing source {$pkg} from custom url [{$ni}/{$cnt}]");
PackageManager::installPackage($pkg, $new_config); PackageManager::installPackage(
$pkg,
$new_config,
allow_alt: false,
extract: !$this->getOption('skip-extract')
);
} else { } else {
logger()->info("Fetching package {$pkg} [{$ni}/{$cnt}]"); logger()->info("Fetching package {$pkg} [{$ni}/{$cnt}]");
PackageManager::installPackage($pkg, Config::getPkg($pkg)); PackageManager::installPackage(
$pkg,
Config::getPkg($pkg),
allow_alt: !$this->getOption('no-alt'),
extract: !$this->getOption('skip-extract')
);
} }
} }
$time = round(microtime(true) - START_TIME, 3); $time = round(microtime(true) - START_TIME, 3);

View File

@@ -11,7 +11,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
#[AsCommand('spc-config', 'Build dependencies')] #[AsCommand('spc-config', 'Build dependencies')]
class SPCConfigCommand extends BuildCommand class SPCConfigCommand extends BaseCommand
{ {
protected bool $no_motd = true; protected bool $no_motd = true;
@@ -23,6 +23,9 @@ class SPCConfigCommand extends BuildCommand
$this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts'); $this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts');
$this->addOption('includes', null, null, 'Add additional include path'); $this->addOption('includes', null, null, 'Add additional include path');
$this->addOption('libs', null, null, 'Add additional libs path'); $this->addOption('libs', null, null, 'Add additional libs path');
$this->addOption('libs-only-deps', null, null, 'Output dependent libraries with -l prefix');
$this->addOption('absolute-libs', null, null, 'Output absolute paths for libraries');
$this->addOption('no-php', null, null, 'Do not link to PHP library');
} }
/** /**
@@ -37,16 +40,19 @@ class SPCConfigCommand extends BuildCommand
$include_suggest_ext = $this->getOption('with-suggested-exts'); $include_suggest_ext = $this->getOption('with-suggested-exts');
$include_suggest_lib = $this->getOption('with-suggested-libs'); $include_suggest_lib = $this->getOption('with-suggested-libs');
$util = new SPCConfigUtil(); $util = new SPCConfigUtil(options: [
'no_php' => $this->getOption('no-php'),
'libs_only_deps' => $this->getOption('libs-only-deps'),
'absolute_libs' => $this->getOption('absolute-libs'),
]);
$config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib); $config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);
if ($this->getOption('includes')) { $this->output->writeln(match (true) {
$this->output->writeln($config['cflags']); $this->getOption('includes') => $config['cflags'],
} elseif ($this->getOption('libs')) { $this->getOption('libs-only-deps') => $config['libs'],
$this->output->writeln("{$config['ldflags']} {$config['libs']}"); $this->getOption('libs') => "{$config['ldflags']} {$config['libs']}",
} else { default => "{$config['cflags']} {$config['ldflags']} {$config['libs']}",
$this->output->writeln("{$config['cflags']} {$config['ldflags']} {$config['libs']}"); });
}
return 0; return 0;
} }

View File

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

View File

@@ -6,7 +6,6 @@ namespace SPC\command\dev;
use SPC\builder\BuilderProvider; use SPC\builder\BuilderProvider;
use SPC\builder\LibraryBase; use SPC\builder\LibraryBase;
use SPC\builder\linux\SystemUtil;
use SPC\command\BuildCommand; use SPC\command\BuildCommand;
use SPC\exception\ExceptionHandler; use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
@@ -16,6 +15,7 @@ use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\util\DependencyUtil; use SPC\util\DependencyUtil;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@@ -42,6 +42,19 @@ class PackLibCommand extends BuildCommand
$builder->proveLibs($libraries); $builder->proveLibs($libraries);
$builder->validateLibsAndExts(); $builder->validateLibsAndExts();
// before pack, check if the dependency tree contains lib-suggests
foreach ($libraries as $lib) {
if (Config::getLib($lib, 'lib-suggests', []) !== []) {
logger()->critical("The library {$lib} has lib-suggests, packing [{$lib_name}] is not safe, abort !");
return static::FAILURE;
}
}
$origin_files = [];
// get pack placehoder defines
$placehoder = get_pack_replace();
foreach ($builder->getLibs() as $lib) { foreach ($builder->getLibs() as $lib) {
if ($lib->getName() !== $lib_name) { if ($lib->getName() !== $lib_name) {
// other dependencies: install or build, both ok // other dependencies: install or build, both ok
@@ -64,6 +77,27 @@ class PackLibCommand extends BuildCommand
// After build: load buildroot/ directory, and calculate increase files // After build: load buildroot/ directory, and calculate increase files
$after_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true); $after_buildroot = FileSystem::scanDirFiles(BUILD_ROOT_PATH, relative: true);
$increase_files = array_diff($after_buildroot, $before_buildroot); $increase_files = array_diff($after_buildroot, $before_buildroot);
// patch pkg-config and la files with absolute path
foreach ($increase_files as $file) {
if (str_ends_with($file, '.pc') || str_ends_with($file, '.la')) {
$content = FileSystem::readFile(BUILD_ROOT_PATH . '/' . $file);
$origin_files[$file] = $content;
// replace relative paths with absolute paths
$content = str_replace(
array_keys($placehoder),
array_values($placehoder),
$content
);
FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content);
}
}
// add .spc-extract-placeholder.json in BUILD_ROOT_PATH
$placeholder_file = BUILD_ROOT_PATH . '/.spc-extract-placeholder.json';
file_put_contents($placeholder_file, json_encode(array_keys($origin_files), JSON_PRETTY_PRINT));
$increase_files[] = '.spc-extract-placeholder.json';
// every file mapped with BUILD_ROOT_PATH // every file mapped with BUILD_ROOT_PATH
// get BUILD_ROOT_PATH last dir part // get BUILD_ROOT_PATH last dir part
$buildroot_part = basename(BUILD_ROOT_PATH); $buildroot_part = basename(BUILD_ROOT_PATH);
@@ -76,8 +110,8 @@ class PackLibCommand extends BuildCommand
'{name}' => $lib->getName(), '{name}' => $lib->getName(),
'{arch}' => arch2gnu(php_uname('m')), '{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY), '{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default', '{libc}' => SPCTarget::getLibc() ?? 'default',
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default', '{libcver}' => SPCTarget::getLibcVersion() ?? 'default',
]; ];
// detect suffix, for proper tar option // detect suffix, for proper tar option
$tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern')); $tar_option = $this->getTarOptionFromSuffix(Config::getPreBuilt('match-pattern'));
@@ -85,6 +119,16 @@ class PackLibCommand extends BuildCommand
$filename = WORKING_DIR . '/dist/' . $filename; $filename = WORKING_DIR . '/dist/' . $filename;
f_passthru("tar {$tar_option} {$filename} -T " . WORKING_DIR . '/packlib_files.txt'); f_passthru("tar {$tar_option} {$filename} -T " . WORKING_DIR . '/packlib_files.txt');
logger()->info('Pack library ' . $lib->getName() . ' to ' . $filename . ' complete.'); logger()->info('Pack library ' . $lib->getName() . ' to ' . $filename . ' complete.');
// remove temp files
unlink($placeholder_file);
}
}
foreach ($origin_files as $file => $content) {
// restore original files
if (file_exists(BUILD_ROOT_PATH . '/' . $file)) {
FileSystem::writeFile(BUILD_ROOT_PATH . '/' . $file, $content);
} }
} }

View File

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

View File

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

View File

@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace SPC\doctor\item; namespace SPC\doctor\item;
use SPC\builder\linux\SystemUtil;
use SPC\doctor\AsCheckItem; use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\doctor\OptionalCheck;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
@@ -16,20 +16,20 @@ use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\PackageManager; use SPC\store\PackageManager;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\toolchain\MuslToolchain;
#[OptionalCheck([self::class, 'optionalCheck'])]
class LinuxMuslCheck class LinuxMuslCheck
{ {
public static function optionalCheck(): bool
{
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class;
}
/** @noinspection PhpUnused */ /** @noinspection PhpUnused */
#[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)] #[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)]
public function checkMusl(): CheckResult public function checkMusl(): CheckResult
{ {
if (SystemUtil::isMuslDist()) {
return CheckResult::ok('musl-based distro, skipped');
}
if (getenv('SPC_LIBC') === 'glibc') {
return CheckResult::ok('Building with glibc, skipped');
}
$musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m'));
if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) { if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) {
return CheckResult::ok(); return CheckResult::ok();
@@ -40,13 +40,6 @@ class LinuxMuslCheck
#[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)] #[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)]
public function checkMuslCrossMake(): CheckResult public function checkMuslCrossMake(): CheckResult
{ {
if (SystemUtil::isMuslDist()) {
return CheckResult::ok('musl-based distro, skipped');
}
if (getenv('SPC_LIBC') === 'glibc') {
return CheckResult::ok('Building with glibc, skipped');
}
$arch = arch2gnu(php_uname('m')); $arch = arch2gnu(php_uname('m'));
$cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a";
$cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc"; $cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc";

View File

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

View File

@@ -7,9 +7,6 @@ namespace SPC\store;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
/**
* 一个读取 config 配置的操作类
*/
class Config class Config
{ {
public static ?array $pkg = null; public static ?array $pkg = null;
@@ -23,6 +20,10 @@ class Config
public static ?array $pre_built = null; public static ?array $pre_built = null;
/** /**
* Get pre-built configuration by name
*
* @param string $name The name of the pre-built configuration
* @return mixed The pre-built configuration or null if not found
* @throws WrongUsageException * @throws WrongUsageException
* @throws FileSystemException * @throws FileSystemException
*/ */
@@ -50,8 +51,10 @@ class Config
} }
/** /**
* 从配置文件读取一个资源(source)的元信息 * Get source configuration by name
* *
* @param string $name The name of the source
* @return null|array The source configuration or null if not found
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function getSource(string $name): ?array public static function getSource(string $name): ?array
@@ -63,8 +66,10 @@ class Config
} }
/** /**
* Read pkg from pkg.json * Get package configuration by name
* *
* @param string $name The name of the package
* @return null|array The package configuration or null if not found
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function getPkg(string $name): ?array public static function getPkg(string $name): ?array
@@ -76,11 +81,13 @@ class Config
} }
/** /**
* 根据不同的操作系统分别选择不同的 lib 库依赖项 * Get library configuration by name and optional key
* 如果 key 为 null那么直接返回整个 meta。 * Supports platform-specific configurations for different operating systems
* 如果 key 不为 null则可以使用的 key 有 static-libs、headers、lib-depends、lib-suggests。
* 对于 macOS 平台,支持 frameworks。
* *
* @param string $name The name of the library
* @param null|string $key The configuration key (static-libs, headers, lib-depends, lib-suggests, frameworks, bin)
* @param mixed $default Default value if key not found
* @return mixed The library configuration or default value
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
@@ -115,6 +122,9 @@ class Config
} }
/** /**
* Get all library configurations
*
* @return array All library configurations
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function getLibs(): array public static function getLibs(): array
@@ -126,6 +136,10 @@ class Config
} }
/** /**
* Get extension target configuration by name
*
* @param string $name The name of the extension
* @return null|array The extension target configuration or default ['static', 'shared']
* @throws WrongUsageException * @throws WrongUsageException
* @throws FileSystemException * @throws FileSystemException
*/ */
@@ -141,6 +155,13 @@ class Config
} }
/** /**
* Get extension configuration by name and optional key
* Supports platform-specific configurations for different operating systems
*
* @param string $name The name of the extension
* @param null|string $key The configuration key (lib-depends, lib-suggests, ext-depends, ext-suggests, arg-type)
* @param mixed $default Default value if key not found
* @return mixed The extension configuration or default value
* @throws FileSystemException * @throws FileSystemException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
@@ -175,6 +196,9 @@ class Config
} }
/** /**
* Get all extension configurations
*
* @return array All extension configurations
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function getExts(): array public static function getExts(): array
@@ -186,6 +210,9 @@ class Config
} }
/** /**
* Get all source configurations
*
* @return array All source configurations
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function getSources(): array public static function getSources(): array

View File

@@ -4,13 +4,13 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\builder\linux\SystemUtil;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage; use SPC\store\pkg\CustomPackage;
use SPC\store\source\CustomSourceBase; use SPC\store\source\CustomSourceBase;
use SPC\util\SPCTarget;
/** /**
* Source Downloader. * Source Downloader.
@@ -18,12 +18,13 @@ use SPC\store\source\CustomSourceBase;
class Downloader class Downloader
{ {
/** /**
* Get latest version from BitBucket tag (type = bitbuckettag) * Get latest version from BitBucket tag
* *
* @param string $name source name * @param string $name Source name
* @param array $source source meta info: [repo] * @param array $source Source meta info: [repo]
* @return array<int, string> [url, filename] * @return array<int, string> [url, filename]
* @throws DownloaderException * @throws DownloaderException
* @throws RuntimeException
*/ */
public static function getLatestBitbucketTag(string $name, array $source): array public static function getLatestBitbucketTag(string $name, array $source): array
{ {
@@ -53,13 +54,12 @@ class Downloader
} }
/** /**
* Get latest version from GitHub tarball (type = ghtar / ghtagtar) * Get latest version from GitHub tarball
*
* @param string $name source name
* @param array $source source meta info: [repo]
* @param string $type type of tarball, default is 'releases'
* @return array<int, string> [url, filename]
* *
* @param string $name Source name
* @param array $source Source meta info: [repo]
* @param string $type Type of tarball, default is 'releases'
* @return array<int, string> [url, filename]
* @throws DownloaderException * @throws DownloaderException
*/ */
public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array
@@ -107,8 +107,8 @@ class Downloader
/** /**
* Get latest version from GitHub release (uploaded archive) * Get latest version from GitHub release (uploaded archive)
* *
* @param string $name source name * @param string $name Source name
* @param array $source source meta info: [repo, match] * @param array $source Source meta info: [repo, match]
* @param bool $match_result Whether to return matched result by `match` param (default: true) * @param bool $match_result Whether to return matched result by `match` param (default: true)
* @return array<int, string> When $match_result = true, and we matched, [url, filename]. Otherwise, [{asset object}. ...] * @return array<int, string> When $match_result = true, and we matched, [url, filename]. Otherwise, [{asset object}. ...]
* @throws DownloaderException * @throws DownloaderException
@@ -150,8 +150,8 @@ class Downloader
/** /**
* Get latest version from file list (regex based crawler) * Get latest version from file list (regex based crawler)
* *
* @param string $name source name * @param string $name Source name
* @param array $source source meta info: [url, regex] * @param array $source Source meta info: [filelist]
* @return array<int, string> [url, filename] * @return array<int, string> [url, filename]
* @throws DownloaderException * @throws DownloaderException
*/ */
@@ -187,11 +187,17 @@ class Downloader
} }
/** /**
* Just download file using system curl command, and lock it * Download file from URL
* *
* @throws FileSystemException * @param string $name Download name
* @param string $url Download URL
* @param string $filename Target filename
* @param null|string $move_path Optional move path after download
* @param int $download_as Download type constant
* @param array $headers Optional HTTP headers
* @param array $hooks Optional curl hooks
* @throws DownloaderException
* @throws RuntimeException * @throws RuntimeException
* @throws WrongUsageException
*/ */
public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null, int $download_as = SPC_DOWNLOAD_SOURCE, array $headers = [], array $hooks = []): void public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null, int $download_as = SPC_DOWNLOAD_SOURCE, array $headers = [], array $hooks = []): void
{ {
@@ -213,40 +219,53 @@ class Downloader
} }
/** /**
* Download git source, and lock it. * Download Git repository
* *
* @throws FileSystemException * @param string $name Repository name
* @param string $url Git repository URL
* @param string $branch Branch to checkout
* @param null|array $submodules Optional submodules to initialize
* @param null|string $move_path Optional move path after download
* @param int $retries Number of retry attempts
* @param int $lock_as Lock type constant
* @throws DownloaderException
* @throws RuntimeException * @throws RuntimeException
* @throws WrongUsageException
*/ */
public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null, int $retries = 0, int $lock_as = SPC_DOWNLOAD_SOURCE): void public static function downloadGit(string $name, string $url, string $branch, ?array $submodules = null, ?string $move_path = null, int $retries = 0, int $lock_as = SPC_DOWNLOAD_SOURCE): void
{ {
$download_path = FileSystem::convertPath(DOWNLOAD_PATH . "/{$name}"); $download_path = FileSystem::convertPath(DOWNLOAD_PATH . "/{$name}");
if (file_exists($download_path)) { if (file_exists($download_path)) {
FileSystem::removeDir($download_path); FileSystem::removeDir($download_path);
} }
logger()->debug("cloning {$name} source"); logger()->debug("cloning {$name} source");
$check = !defined('DEBUG_MODE') ? ' -q' : '';
$cancel_func = function () use ($download_path) { $quiet = !defined('DEBUG_MODE') ? '-q --quiet' : '';
$git = SPC_GIT_EXEC;
$shallow = defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '';
$recursive = ($submodules === null) ? '--recursive' : '';
try {
self::registerCancelEvent(function () use ($download_path) {
if (is_dir($download_path)) {
logger()->warning('Removing path ' . $download_path);
FileSystem::removeDir($download_path);
}
});
f_passthru("{$git} clone {$quiet} --config core.autocrlf=false --branch \"{$branch}\" {$shallow} {$recursive} \"{$url}\" \"{$download_path}\"");
if ($submodules !== null) {
foreach ($submodules as $submodule) {
f_passthru("cd \"{$download_path}\" && {$git} submodule update --init " . escapeshellarg($submodule));
}
}
} catch (RuntimeException $e) {
if (is_dir($download_path)) { if (is_dir($download_path)) {
logger()->warning('Removing path ' . $download_path);
FileSystem::removeDir($download_path); FileSystem::removeDir($download_path);
} }
};
try {
self::registerCancelEvent($cancel_func);
f_passthru(
SPC_GIT_EXEC . ' clone' . $check .
(defined('DEBUG_MODE') ? '' : ' --quiet') .
' --config core.autocrlf=false ' .
"--branch \"{$branch}\" " . (defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '') . " --recursive \"{$url}\" \"{$download_path}\""
);
} catch (RuntimeException $e) {
if ($e->getCode() === 2 || $e->getCode() === -1073741510) { if ($e->getCode() === 2 || $e->getCode() === -1073741510) {
throw new WrongUsageException('Keyboard interrupted, download failed !'); throw new WrongUsageException('Keyboard interrupted, download failed !');
} }
if ($retries > 0) { if ($retries > 0) {
self::downloadGit($name, $url, $branch, $move_path, $retries - 1); self::downloadGit($name, $url, $branch, $submodules, $move_path, $retries - 1, $lock_as);
return; return;
} }
throw $e; throw $e;
@@ -297,7 +316,6 @@ class Downloader
if ($pkg === null) { if ($pkg === null) {
$pkg = Config::getPkg($name); $pkg = Config::getPkg($name);
} }
if ($pkg === null) { if ($pkg === null) {
logger()->warning('Package {name} unknown. Skipping.', ['name' => $name]); logger()->warning('Package {name} unknown. Skipping.', ['name' => $name]);
return; return;
@@ -343,6 +361,7 @@ class Downloader
$name, $name,
$pkg['url'], $pkg['url'],
$pkg['rev'], $pkg['rev'],
$pkg['submodules'] ?? null,
$pkg['extract'] ?? null, $pkg['extract'] ?? null,
self::getRetryAttempts(), self::getRetryAttempts(),
SPC_DOWNLOAD_PRE_BUILT SPC_DOWNLOAD_PRE_BUILT
@@ -360,6 +379,11 @@ class Downloader
break; break;
case 'custom': // Custom download method, like API-based download or other case 'custom': // Custom download method, like API-based download or other
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'); $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) { foreach ($classes as $class) {
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) { if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
$cls = new $class(); $cls = new $class();
@@ -385,7 +409,7 @@ class Downloader
} }
/** /**
* Download source by name and meta. * Download source
* *
* @param string $name source name * @param string $name source name
* @param null|array{ * @param null|array{
@@ -415,7 +439,6 @@ class Downloader
if ($source === null) { if ($source === null) {
$source = Config::getSource($name); $source = Config::getSource($name);
} }
if ($source === null) { if ($source === null) {
logger()->warning('Source {name} unknown. Skipping.', ['name' => $name]); logger()->warning('Source {name} unknown. Skipping.', ['name' => $name]);
return; return;
@@ -462,6 +485,7 @@ class Downloader
$name, $name,
$source['url'], $source['url'],
$source['rev'], $source['rev'],
$source['submodules'] ?? null,
$source['path'] ?? null, $source['path'] ?? null,
self::getRetryAttempts(), self::getRetryAttempts(),
$download_as $download_as
@@ -508,7 +532,14 @@ class Downloader
/** /**
* Use curl command to get http response * Use curl command to get http response
* *
* @param string $url Target URL
* @param string $method HTTP method (GET, POST, etc.)
* @param array $headers HTTP headers
* @param array $hooks Curl hooks
* @param int $retries Number of retry attempts
* @return string Response body
* @throws DownloaderException * @throws DownloaderException
* @throws RuntimeException
*/ */
public static function curlExec(string $url, string $method = 'GET', array $headers = [], array $hooks = [], int $retries = 0): string public static function curlExec(string $url, string $method = 'GET', array $headers = [], array $hooks = [], int $retries = 0): string
{ {
@@ -560,6 +591,13 @@ class Downloader
/** /**
* Use curl to download sources from url * Use curl to download sources from url
* *
* @param string $url Download URL
* @param string $path Target file path
* @param string $method HTTP method
* @param array $headers HTTP headers
* @param array $hooks Curl hooks
* @param int $retries Number of retry attempts
* @throws DownloaderException
* @throws RuntimeException * @throws RuntimeException
* @throws WrongUsageException * @throws WrongUsageException
*/ */
@@ -589,9 +627,49 @@ class Downloader
} }
} }
/**
* Get pre-built lock name from source
*
* @param string $source Source name
* @return string Lock name
*/
public static function getPreBuiltLockName(string $source): string public static function getPreBuiltLockName(string $source): string
{ {
return "{$source}-" . PHP_OS_FAMILY . '-' . getenv('GNU_ARCH') . '-' . (getenv('SPC_LIBC') ?: 'default') . '-' . (SystemUtil::getLibcVersionIfExists() ?? 'default'); $os_family = PHP_OS_FAMILY;
$gnu_arch = getenv('GNU_ARCH') ?: 'unknown';
$libc = SPCTarget::getLibc();
$libc_version = SPCTarget::getLibcVersion() ?? 'default';
return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}";
}
/**
* Get default alternative source
*
* @param string $source_name Source name
* @return array Alternative source configuration
*/
public static function getDefaultAlternativeSource(string $source_name): array
{
return [
'type' => 'custom',
'func' => function (bool $force, array $source, int $download_as) use ($source_name) {
logger()->debug("Fetching alternative source for {$source_name}");
// get from dl.static-php.dev
$url = "https://dl.static-php.dev/static-php-cli/deps/spc-download-mirror/{$source_name}/?format=json";
$json = json_decode(Downloader::curlExec(url: $url, retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)), true);
if (!is_array($json)) {
throw new RuntimeException('failed http fetch');
}
$item = $json[0] ?? null;
if ($item === null) {
throw new RuntimeException('failed to parse json');
}
$full_url = 'https://dl.static-php.dev' . $item['full_path'];
$filename = basename($item['full_path']);
Downloader::downloadFile($source_name, $full_url, $filename, $source['path'] ?? null, $download_as);
},
];
} }
/** /**

View File

@@ -12,6 +12,11 @@ class FileSystem
private static array $_extract_hook = []; private static array $_extract_hook = [];
/** /**
* Load configuration array from JSON file
*
* @param string $config The configuration name (ext, lib, source, pkg, pre-built)
* @param null|string $config_dir Optional custom config directory
* @return array The loaded configuration array
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function loadConfigArray(string $config, ?string $config_dir = null): array public static function loadConfigArray(string $config, ?string $config_dir = null): array
@@ -37,9 +42,10 @@ class FileSystem
} }
/** /**
* 读取文件,读不出来直接抛出异常 * Read file contents and throw exception on failure
* *
* @param string $filename 文件路径 * @param string $filename The file path to read
* @return string The file contents
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function readFile(string $filename): string public static function readFile(string $filename): string
@@ -53,6 +59,12 @@ class FileSystem
} }
/** /**
* Replace string content in file
*
* @param string $filename The file path
* @param mixed $search The search string
* @param mixed $replace The replacement string
* @return false|int Number of replacements or false on failure
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function replaceFileStr(string $filename, mixed $search = null, mixed $replace = null): false|int public static function replaceFileStr(string $filename, mixed $search = null, mixed $replace = null): false|int
@@ -61,6 +73,12 @@ class FileSystem
} }
/** /**
* Replace content in file using regex
*
* @param string $filename The file path
* @param mixed $search The regex pattern
* @param mixed $replace The replacement string
* @return false|int Number of replacements or false on failure
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function replaceFileRegex(string $filename, mixed $search = null, mixed $replace = null): false|int public static function replaceFileRegex(string $filename, mixed $search = null, mixed $replace = null): false|int
@@ -69,6 +87,11 @@ class FileSystem
} }
/** /**
* Replace content in file using custom callback
*
* @param string $filename The file path
* @param mixed $callback The callback function
* @return false|int Number of replacements or false on failure
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function replaceFileUser(string $filename, mixed $callback = null): false|int public static function replaceFileUser(string $filename, mixed $callback = null): false|int
@@ -77,9 +100,10 @@ class FileSystem
} }
/** /**
* 获取文件后缀 * Get file extension from filename
* *
* @param string $fn 文件名 * @param string $fn The filename
* @return string The file extension (without dot)
*/ */
public static function extname(string $fn): string public static function extname(string $fn): string
{ {
@@ -91,10 +115,11 @@ class FileSystem
} }
/** /**
* 寻找命令的真实路径,效果类似 which * Find command path in system PATH (similar to which command)
* *
* @param string $name 命令名称 * @param string $name The command name
* @param array $paths 路径列表,如果为空则默认从 PATH 系统变量搜索 * @param array $paths Optional array of paths to search
* @return null|string The full path to the command or null if not found
*/ */
public static function findCommandPath(string $name, array $paths = []): ?string public static function findCommandPath(string $name, array $paths = []): ?string
{ {
@@ -120,6 +145,10 @@ class FileSystem
} }
/** /**
* Copy directory recursively
*
* @param string $from Source directory path
* @param string $to Destination directory path
* @throws RuntimeException * @throws RuntimeException
*/ */
public static function copyDir(string $from, string $to): void public static function copyDir(string $from, string $to): void
@@ -139,6 +168,12 @@ class FileSystem
} }
/** /**
* Extract package archive to specified directory
*
* @param string $name Package name
* @param string $source_type Archive type (tar.gz, zip, etc.)
* @param string $filename Archive filename
* @param null|string $extract_path Optional extraction path
* @throws RuntimeException * @throws RuntimeException
* @throws FileSystemException * @throws FileSystemException
*/ */
@@ -171,10 +206,12 @@ class FileSystem
} }
/** /**
* 解压缩下载的资源包到 source 目录 * Extract source archive to source directory
* *
* @param string $name 资源名 * @param string $name Source name
* @param string $filename 文件名 * @param string $source_type Archive type (tar.gz, zip, etc.)
* @param string $filename Archive filename
* @param null|string $move_path Optional move path
* @throws FileSystemException * @throws FileSystemException
* @throws RuntimeException * @throws RuntimeException
*/ */
@@ -207,9 +244,10 @@ class FileSystem
} }
/** /**
* 根据系统环境的不同,自动转换路径的分隔符 * Convert path to system-specific format
* *
* @param string $path 路径 * @param string $path The path to convert
* @return string The converted path
*/ */
public static function convertPath(string $path): string public static function convertPath(string $path): string
{ {
@@ -219,6 +257,12 @@ class FileSystem
return str_replace('/', DIRECTORY_SEPARATOR, $path); return str_replace('/', DIRECTORY_SEPARATOR, $path);
} }
/**
* Convert Windows path to MinGW format
*
* @param string $path The Windows path
* @return string The MinGW format path
*/
public static function convertWinPathToMinGW(string $path): string public static function convertWinPathToMinGW(string $path): string
{ {
if (preg_match('/^[A-Za-z]:/', $path)) { if (preg_match('/^[A-Za-z]:/', $path)) {
@@ -228,28 +272,21 @@ class FileSystem
} }
/** /**
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表 * Scan directory files recursively
* *
* @param string $dir 目录 * @param string $dir Directory to scan
* @param bool $recursive 是否递归扫描子目录 * @param bool $recursive Whether to scan recursively
* @param bool|string $relative 是否返回相对目录如果为true则返回相对目录如果为false则返回绝对目录 * @param bool|string $relative Whether to return relative paths
* @param bool $include_dir 非递归模式下,是否包含目录 * @param bool $include_dir Whether to include directories in result
* @since 2.5 * @return array|false Array of files or false on failure
*/ */
public static function scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false): array|false public static function scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false): array|false
{ {
$dir = self::convertPath($dir); $dir = self::convertPath($dir);
// 不是目录不扫,直接 false 处理
if (!file_exists($dir)) {
logger()->debug('Scan dir failed, no such file or directory.');
return false;
}
if (!is_dir($dir)) { if (!is_dir($dir)) {
logger()->warning('Scan dir failed, not directory.');
return false; return false;
} }
logger()->debug('scanning directory ' . $dir); logger()->debug('scanning directory ' . $dir);
// 套上 zm_dir
$scan_list = scandir($dir); $scan_list = scandir($dir);
if ($scan_list === false) { if ($scan_list === false) {
logger()->warning('Scan dir failed, cannot scan directory: ' . $dir); logger()->warning('Scan dir failed, cannot scan directory: ' . $dir);
@@ -283,18 +320,17 @@ class FileSystem
} }
/** /**
* 获取该路径下的所有类名,根据 psr-4 方式 * Get PSR-4 classes from directory
* *
* @param string $dir 目录 * @param string $dir Directory to scan
* @param string $base_namespace 基类命名空间 * @param string $base_namespace Base namespace
* @param null|mixed $rule 规则回调 * @param mixed $rule Optional filtering rule
* @param bool|string $return_path_value 是否返回路径对应的数组,默认只返回类名列表 * @param bool|string $return_path_value Whether to return path as value
* @throws FileSystemException * @return array Array of class names or class=>path pairs
*/ */
public static function getClassesPsr4(string $dir, string $base_namespace, mixed $rule = null, bool|string $return_path_value = false): array public static function getClassesPsr4(string $dir, string $base_namespace, mixed $rule = null, bool|string $return_path_value = false): array
{ {
$classes = []; $classes = [];
// 扫描目录使用递归模式相对路径模式因为下面此路径要用作转换成namespace
$files = FileSystem::scanDirFiles($dir, true, true); $files = FileSystem::scanDirFiles($dir, true, true);
if ($files === false) { if ($files === false) {
throw new FileSystemException('Cannot scan dir files during get classes psr-4 from dir: ' . $dir); throw new FileSystemException('Cannot scan dir files during get classes psr-4 from dir: ' . $dir);
@@ -325,15 +361,15 @@ class FileSystem
} }
/** /**
* 删除目录及目录下的所有文件(危险操作) * Remove directory recursively
* *
* @throws FileSystemException * @param string $dir Directory to remove
* @return bool Success status
*/ */
public static function removeDir(string $dir): bool public static function removeDir(string $dir): bool
{ {
$dir = FileSystem::convertPath($dir); $dir = FileSystem::convertPath($dir);
logger()->debug('Removing path recursively: "' . $dir . '"'); logger()->debug('Removing path recursively: "' . $dir . '"');
// 不是目录不扫,直接 false 处理
if (!file_exists($dir)) { if (!file_exists($dir)) {
logger()->debug('Scan dir failed, no such file or directory.'); logger()->debug('Scan dir failed, no such file or directory.');
return false; return false;
@@ -374,6 +410,9 @@ class FileSystem
} }
/** /**
* Create directory recursively
*
* @param string $path Directory path to create
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function createDir(string $path): void public static function createDir(string $path): void
@@ -384,7 +423,12 @@ class FileSystem
} }
/** /**
* @param mixed ...$args Arguments passed to file_put_contents * Write content to file
*
* @param string $path File path
* @param mixed $content Content to write
* @param mixed ...$args Additional arguments passed to file_put_contents
* @return bool|int|string Result of file writing operation
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function writeFile(string $path, mixed $content, ...$args): bool|int|string public static function writeFile(string $path, mixed $content, ...$args): bool|int|string
@@ -397,27 +441,36 @@ class FileSystem
} }
/** /**
* Reset (remove recursively and create again) dir * Reset directory by removing and recreating it
* *
* @param string $dir_name Directory name
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function resetDir(string $dir_name): void public static function resetDir(string $dir_name): void
{ {
$dir_name = self::convertPath($dir_name);
if (is_dir($dir_name)) { if (is_dir($dir_name)) {
self::removeDir($dir_name); self::removeDir($dir_name);
} }
self::createDir($dir_name); self::createDir($dir_name);
} }
/**
* Add source extraction hook
*
* @param string $name Source name
* @param callable $callback Callback function
*/
public static function addSourceExtractHook(string $name, callable $callback): void public static function addSourceExtractHook(string $name, callable $callback): void
{ {
self::$_extract_hook[$name][] = $callback; self::$_extract_hook[$name][] = $callback;
} }
/** /**
* Check whether the path is a relative path (judging according to whether the first character is "/") * Check if path is relative
* *
* @param string $path Path * @param string $path Path to check
* @return bool True if path is relative
*/ */
public static function isRelativePath(string $path): bool public static function isRelativePath(string $path): bool
{ {
@@ -427,6 +480,12 @@ class FileSystem
return strlen($path) > 0 && $path[0] !== '/'; return strlen($path) > 0 && $path[0] !== '/';
} }
/**
* Replace path variables with actual values
*
* @param string $path Path with variables
* @return string Path with replaced variables
*/
public static function replacePathVariable(string $path): string public static function replacePathVariable(string $path): string
{ {
$replacement = [ $replacement = [
@@ -439,12 +498,23 @@ class FileSystem
return str_replace(array_keys($replacement), array_values($replacement), $path); return str_replace(array_keys($replacement), array_values($replacement), $path);
} }
/**
* Create backup of file
*
* @param string $path File path
* @return string Backup file path
*/
public static function backupFile(string $path): string public static function backupFile(string $path): string
{ {
copy($path, $path . '.bak'); copy($path, $path . '.bak');
return $path . '.bak'; return $path . '.bak';
} }
/**
* Restore file from backup
*
* @param string $path Original file path
*/
public static function restoreBackupFile(string $path): void public static function restoreBackupFile(string $path): void
{ {
if (!file_exists($path . '.bak')) { if (!file_exists($path . '.bak')) {
@@ -454,14 +524,26 @@ class FileSystem
unlink($path . '.bak'); unlink($path . '.bak');
} }
/**
* Remove file if it exists
*
* @param string $string File path
*/
public static function removeFileIfExists(string $string): void public static function removeFileIfExists(string $string): void
{ {
$string = self::convertPath($string);
if (file_exists($string)) { if (file_exists($string)) {
unlink($string); unlink($string);
} }
} }
/** /**
* Replace line in file that contains specific string
*
* @param string $file File path
* @param string $find String to find in line
* @param string $line New line content
* @return false|int Number of replacements or false on failure
* @throws FileSystemException * @throws FileSystemException
*/ */
public static function replaceFileLineContainsString(string $file, string $find, string $line): false|int public static function replaceFileLineContainsString(string $file, string $find, string $line): false|int

View File

@@ -4,13 +4,14 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage; use SPC\store\pkg\CustomPackage;
class PackageManager class PackageManager
{ {
public static function installPackage(string $pkg_name, ?array $config = null, bool $force = false): void public static function installPackage(string $pkg_name, ?array $config = null, bool $force = false, bool $allow_alt = true, bool $extract = true): void
{ {
if ($config === null) { if ($config === null) {
$config = Config::getPkg($pkg_name); $config = Config::getPkg($pkg_name);
@@ -32,7 +33,31 @@ class PackageManager
} }
// Download package // Download package
Downloader::downloadPackage($pkg_name, $config, $force); try {
Downloader::downloadPackage($pkg_name, $config, $force);
} catch (\Throwable $e) {
if (!$allow_alt) {
throw new DownloaderException("Download package {$pkg_name} failed: " . $e->getMessage());
}
// if download failed, we will try to download alternative packages
logger()->warning("Download package {$pkg_name} failed: " . $e->getMessage());
$alt = $config['alt'] ?? null;
if ($alt === null) {
logger()->warning("No alternative package found for {$pkg_name}, using default mirror.");
$alt_config = array_merge($config, Downloader::getDefaultAlternativeSource($pkg_name));
} elseif ($alt === false) {
logger()->error("No alternative package found for {$pkg_name}.");
throw $e;
} else {
logger()->notice("Trying alternative package for {$pkg_name}.");
$alt_config = array_merge($config, $alt);
}
Downloader::downloadPackage($pkg_name, $alt_config, $force);
}
if (!$extract) {
logger()->info("Package [{$pkg_name}] downloaded, but extraction is skipped.");
return;
}
if (Config::getPkg($pkg_name)['type'] === 'custom') { if (Config::getPkg($pkg_name)['type'] === 'custom') {
// Custom extract function // Custom extract function
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg'); $classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');

View File

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

View File

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

View File

@@ -4,12 +4,36 @@ declare(strict_types=1);
namespace SPC\store\pkg; namespace SPC\store\pkg;
/**
* Abstract base class for custom package implementations
*
* This class provides a framework for implementing custom package download
* and extraction logic. Extend this class to create custom package handlers.
*/
abstract class CustomPackage abstract class CustomPackage
{ {
/**
* Get the list of package names supported by this implementation
*
* @return array Array of supported package names
*/
abstract public function getSupportName(): array; abstract public function getSupportName(): array;
/**
* Fetch the package from its source
*
* @param string $name Package name
* @param bool $force Force download even if already exists
* @param null|array $config Optional configuration array
*/
abstract public function fetch(string $name, bool $force = false, ?array $config = null): void; abstract public function fetch(string $name, bool $force = false, ?array $config = null): void;
/**
* Extract the downloaded package
*
* @param string $name Package name
* @throws \RuntimeException If extraction is not implemented
*/
public function extract(string $name): void public function extract(string $name): void
{ {
throw new \RuntimeException("Extract method not implemented for package: {$name}"); throw new \RuntimeException("Extract method not implemented for package: {$name}");

View File

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

View File

@@ -4,9 +4,25 @@ declare(strict_types=1);
namespace SPC\store\source; namespace SPC\store\source;
/**
* Abstract base class for custom source implementations
*
* This class provides a framework for implementing custom source download
* logic. Extend this class to create custom source handlers.
*/
abstract class CustomSourceBase abstract class CustomSourceBase
{ {
/**
* The name of this source implementation
*/
public const NAME = 'unknown'; public const NAME = 'unknown';
/**
* Fetch the source from its repository
*
* @param bool $force Force download even if already exists
* @param null|array $config Optional configuration array
* @param int $lock_as Lock type constant
*/
abstract public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void; abstract public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void;
} }

View File

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

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