diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 2640ffbad..dc6eaa36b 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -108,8 +108,7 @@ RUN apk update; \ wget \ xz \ gettext-dev \ - binutils-gold \ - patchelf + binutils-gold RUN curl -#fSL https://dl.static-php.dev/static-php-cli/bulk/php-8.4.4-cli-linux-\$(uname -m).tar.gz | tar -xz -C /usr/local/bin && \ chmod +x /usr/local/bin/php diff --git a/bin/spc-gnu-docker b/bin/spc-gnu-docker index 68f85109f..286ef9859 100755 --- a/bin/spc-gnu-docker +++ b/bin/spc-gnu-docker @@ -92,11 +92,6 @@ RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc RUN source /etc/bashrc RUN yum install -y which -RUN curl -fsSL -o patchelf.tgz https://github.com/NixOS/patchelf/releases/download/0.18.0/patchelf-0.18.0-$SPC_USE_ARCH.tar.gz && \ - mkdir -p /patchelf && \ - tar -xzf patchelf.tgz -C /patchelf --strip-components=1 && \ - cp /patchelf/bin/patchelf /usr/bin/ - RUN curl -o cmake.tgz -#fSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$SPC_USE_ARCH.tar.gz && \ mkdir /cmake && \ tar -xzf cmake.tgz -C /cmake --strip-components 1 diff --git a/config/artifact.json b/config/artifact.json index c8fd66215..ad8507b90 100644 --- a/config/artifact.json +++ b/config/artifact.json @@ -1,135 +1,4 @@ { - "vswhere": { - "binary": { - "windows-x86_64": { - "type": "url", - "url": "https://github.com/microsoft/vswhere/releases/download/3.1.7/vswhere.exe", - "extract": "{pkg_root_path}/bin/vswhere.exe" - } - } - }, - "musl-wrapper": { - "source": "https://musl.libc.org/releases/musl-1.2.5.tar.gz" - }, - "php-src": { - "source": { - "type": "php-release" - } - }, - "php-sdk-binary-tools": { - "binary": { - "windows-x86_64": { - "type": "git", - "rev": "master", - "url": "https://github.com/php/php-sdk-binary-tools.git", - "extract": "{php_sdk_path}" - } - } - }, - "go-xcaddy": { - "binary": "custom" - }, - "musl-toolchain": { - "binary": { - "linux-x86_64": { - "type": "url", - "url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz", - "extract": "{pkg_root_path}/musl-toolchain" - }, - "linux-aarch64": { - "type": "url", - "url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz", - "extract": "{pkg_root_path}/musl-toolchain" - } - } - }, - "pkg-config": { - "source": "https://dl.static-php.dev/static-php-cli/deps/pkg-config/pkg-config-0.29.2.tar.gz", - "binary": { - "linux-x86_64": { - "type": "ghrel", - "repo": "static-php/static-php-cli-hosted", - "match": "pkg-config-aarch64-linux-musl-1.2.5.txz", - "extract": { - "bin/pkg-config": "{pkg_root_path}/bin/pkg-config" - } - }, - "linux-aarch64": { - "type": "ghrel", - "repo": "static-php/static-php-cli-hosted", - "match": "pkg-config-x86_64-linux-musl-1.2.5.txz", - "extract": { - "bin/pkg-config": "{pkg_root_path}/bin/pkg-config" - } - }, - "macos-x86_64": { - "type": "ghrel", - "repo": "static-php/static-php-cli-hosted", - "match": "pkg-config-x86_64-darwin.txz", - "extract": { - "bin/pkg-config": "{pkg_root_path}/bin/pkg-config" - } - }, - "macos-aarch64": { - "type": "ghrel", - "repo": "static-php/static-php-cli-hosted", - "match": "pkg-config-aarch64-darwin.txz", - "extract": "{pkg_root_path}" - } - } - }, - "strawberry-perl": { - "binary": { - "windows-x86_64": { - "type": "url", - "url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip", - "extract": "{pkg_root_path}/strawberry-perl" - } - } - }, - "upx": { - "binary": { - "linux-x86_64": { - "type": "ghrel", - "repo": "upx/upx", - "match": "upx.+-amd64_linux\\.tar\\.xz", - "extract": { - "upx": "{pkg_root_path}/bin/upx" - } - }, - "linux-aarch64": { - "type": "ghrel", - "repo": "upx/upx", - "match": "upx.+-arm64_linux\\.tar\\.xz", - "extract": { - "upx": "{pkg_root_path}/bin/upx" - } - }, - "windows-x86_64": { - "type": "ghrel", - "repo": "upx/upx", - "match": "upx.+-win64\\.zip", - "extract": { - "upx.exe": "{pkg_root_path}/bin/upx.exe" - } - } - } - }, - "zig": { - "binary": "custom" - }, - "nasm": { - "binary": { - "windows-x86_64": { - "type": "url", - "url": "https://dl.static-php.dev/static-php-cli/deps/nasm/nasm-2.16.01-win64.zip", - "extract": { - "nasm.exe": "{php_sdk_path}/bin/nasm.exe", - "ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe" - } - } - } - }, "amqp": { "source": { "type": "url", @@ -395,6 +264,9 @@ "repo": "guanzhi/GmSSL" } }, + "go-xcaddy": { + "binary": "custom" + }, "grpc": { "binary": "hosted", "source": { @@ -733,13 +605,6 @@ "extract": "php-src/ext/memcached" } }, - "mimalloc": { - "source": { - "type": "ghtagtar", - "repo": "microsoft/mimalloc", - "match": "v2\\.\\d\\.[^3].*" - } - }, "micro": { "source": { "type": "git", @@ -748,6 +613,13 @@ "url": "https://github.com/static-php/phpmicro" } }, + "mimalloc": { + "source": { + "type": "ghtagtar", + "repo": "microsoft/mimalloc", + "match": "v2\\.\\d\\.[^3].*" + } + }, "mongodb": { "source": { "type": "ghrel", @@ -765,6 +637,35 @@ "extract": "php-src/ext/msgpack" } }, + "musl-toolchain": { + "binary": { + "linux-x86_64": { + "type": "url", + "url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz", + "extract": "{pkg_root_path}/musl-toolchain" + }, + "linux-aarch64": { + "type": "url", + "url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz", + "extract": "{pkg_root_path}/musl-toolchain" + } + } + }, + "musl-wrapper": { + "source": "https://musl.libc.org/releases/musl-1.2.5.tar.gz" + }, + "nasm": { + "binary": { + "windows-x86_64": { + "type": "url", + "url": "https://dl.static-php.dev/static-php-cli/deps/nasm/nasm-2.16.01-win64.zip", + "extract": { + "nasm.exe": "{php_sdk_path}/bin/nasm.exe", + "ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe" + } + } + } + }, "ncurses": { "binary": "hosted", "source": { @@ -850,6 +751,56 @@ "extract": "php-src/ext/pdo_sqlsrv" } }, + "php-sdk-binary-tools": { + "binary": { + "windows-x86_64": { + "type": "git", + "rev": "master", + "url": "https://github.com/php/php-sdk-binary-tools.git", + "extract": "{php_sdk_path}" + } + } + }, + "php-src": { + "source": { + "type": "php-release" + } + }, + "pkg-config": { + "binary": { + "linux-x86_64": { + "type": "ghrel", + "repo": "static-php/static-php-cli-hosted", + "match": "pkg-config-aarch64-linux-musl-1.2.5.txz", + "extract": { + "bin/pkg-config": "{pkg_root_path}/bin/pkg-config" + } + }, + "linux-aarch64": { + "type": "ghrel", + "repo": "static-php/static-php-cli-hosted", + "match": "pkg-config-x86_64-linux-musl-1.2.5.txz", + "extract": { + "bin/pkg-config": "{pkg_root_path}/bin/pkg-config" + } + }, + "macos-x86_64": { + "type": "ghrel", + "repo": "static-php/static-php-cli-hosted", + "match": "pkg-config-x86_64-darwin.txz", + "extract": { + "bin/pkg-config": "{pkg_root_path}/bin/pkg-config" + } + }, + "macos-aarch64": { + "type": "ghrel", + "repo": "static-php/static-php-cli-hosted", + "match": "pkg-config-aarch64-darwin.txz", + "extract": "{pkg_root_path}" + } + }, + "source": "https://dl.static-php.dev/static-php-cli/deps/pkg-config/pkg-config-0.29.2.tar.gz" + }, "postgresql": { "source": { "type": "ghtagtar", @@ -950,6 +901,15 @@ "extract": "php-src/ext/sqlsrv" } }, + "strawberry-perl": { + "binary": { + "windows-x86_64": { + "type": "url", + "url": "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_5380_5361/strawberry-perl-5.38.0.1-64bit-portable.zip", + "extract": "{pkg_root_path}/strawberry-perl" + } + } + }, "swoole": { "source": { "type": "ghtar", @@ -982,6 +942,43 @@ "version": "2.3.12" } }, + "upx": { + "binary": { + "linux-x86_64": { + "type": "ghrel", + "repo": "upx/upx", + "match": "upx.+-amd64_linux\\.tar\\.xz", + "extract": { + "upx": "{pkg_root_path}/bin/upx" + } + }, + "linux-aarch64": { + "type": "ghrel", + "repo": "upx/upx", + "match": "upx.+-arm64_linux\\.tar\\.xz", + "extract": { + "upx": "{pkg_root_path}/bin/upx" + } + }, + "windows-x86_64": { + "type": "ghrel", + "repo": "upx/upx", + "match": "upx.+-win64\\.zip", + "extract": { + "upx.exe": "{pkg_root_path}/bin/upx.exe" + } + } + } + }, + "vswhere": { + "binary": { + "windows-x86_64": { + "type": "url", + "url": "https://github.com/microsoft/vswhere/releases/download/3.1.7/vswhere.exe", + "extract": "{pkg_root_path}/bin/vswhere.exe" + } + } + }, "watcher": { "source": { "type": "ghtar", @@ -1041,6 +1038,9 @@ "extract": "php-src/ext/yaml" } }, + "zig": { + "binary": "custom" + }, "zlib": { "binary": "hosted", "source": { diff --git a/config/env.ini b/config/env.ini index 8e25aa6e7..bd2f3d4f6 100644 --- a/config/env.ini +++ b/config/env.ini @@ -122,17 +122,20 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS="" +; optional, path to openssl conf. This affects where openssl will look for the default CA. +; default on Debian/Alpine: /etc/ssl, default on RHEL: /etc/pki/tls +OPENSSLDIR="" + [macos] ; 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 -CC=${SPC_LINUX_DEFAULT_CC} -CXX=${SPC_LINUX_DEFAULT_CXX} -AR=${SPC_LINUX_DEFAULT_AR} -LD=${SPC_LINUX_DEFAULT_LD} +CC=clang +CXX=clang++ +AR=ar +LD=ld ; default compiler flags, used in CMake toolchain file, openssl and pkg-config build -; this will be added to all CFLAGS and CXXFLAGS for the library builds SPC_DEFAULT_C_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_CXX_FLAGS="--target=${MAC_ARCH}-apple-darwin -Os" SPC_DEFAULT_LD_FLAGS="" @@ -150,3 +153,5 @@ SPC_CMD_PREFIX_PHP_CONFIGURE="./configure --prefix= --with-valgrind=no --enable- SPC_CMD_VAR_PHP_EMBED_TYPE="static" ; EXTRA_CFLAGS for `configure` and `make` php SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -Werror=unknown-warning-option ${SPC_DEFAULT_C_FLAGS}" +; minimum compatible macOS version (LLVM vars, availability not guaranteed) +MACOSX_DEPLOYMENT_TARGET=12.0 diff --git a/config/ext.json b/config/ext.json index d3fd2aa27..566974b92 100644 --- a/config/ext.json +++ b/config/ext.json @@ -127,6 +127,14 @@ "sockets" ] }, + "excimer": { + "support": { + "Windows": "wip", + "BSD": "wip" + }, + "type": "external", + "source": "ext-excimer" + }, "exif": { "type": "builtin" }, @@ -232,11 +240,13 @@ "BSD": "wip" }, "type": "external", - "source": "grpc", + "source": "ext-grpc", "arg-type-unix": "enable-path", "cpp-extension": true, "lib-depends": [ - "grpc" + "zlib", + "openssl", + "libcares" ] }, "iconv": { @@ -408,8 +418,7 @@ "ext-depends": [ "zlib", "session" - ], - "build-with-php": true + ] }, "memcached": { "support": { @@ -487,6 +496,40 @@ "zlib" ] }, + "mysqlnd_ed25519": { + "type": "external", + "source": "mysqlnd_ed25519", + "arg-type": "enable", + "target": [ + "shared" + ], + "ext-depends": [ + "mysqlnd" + ], + "lib-depends": [ + "libsodium" + ], + "lib-suggests": [ + "openssl" + ] + }, + "mysqlnd_parsec": { + "type": "external", + "source": "mysqlnd_parsec", + "arg-type": "enable", + "target": [ + "shared" + ], + "ext-depends": [ + "mysqlnd" + ], + "lib-depends": [ + "libsodium" + ], + "lib-suggests": [ + "openssl" + ] + }, "oci8": { "type": "wip", "support": { diff --git a/config/lib.json b/config/lib.json index 47f3c7b89..3be972484 100644 --- a/config/lib.json +++ b/config/lib.json @@ -361,6 +361,9 @@ "source": "libargon2", "static-libs-unix": [ "libargon2.a" + ], + "lib-suggests": [ + "libsodium" ] }, "libavif": { diff --git a/config/pkg.ext.json b/config/pkg.ext.json index 70fe34e63..b1e819eb3 100644 --- a/config/pkg.ext.json +++ b/config/pkg.ext.json @@ -1,66 +1,66 @@ { "ext-amqp": { - "type": "php-extension", - "php-extension": { - "support": { - "BSD": "wip" - }, - "arg-type": "custom" - }, + "artifact": "amqp", "depends": [ "librabbitmq" ], "depends@windows": [ "ext-openssl" ], - "artifact": "amqp", "license": { "type": "file", "path": "LICENSE" - } + }, + "php-extension": { + "support": { + "BSD": "wip" + }, + "arg-type": "custom" + }, + "type": "php-extension" }, "ext-apcu": { - "type": "php-extension", "artifact": "apcu", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "php-extension" }, "ext-ast": { - "type": "php-extension", "artifact": "ast", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "php-extension" }, "ext-bcmath": { "type": "php-extension" }, "ext-brotli": { - "type": "php-extension", - "php-extension": { - "arg-type": "enable" - }, + "artifact": "ext-brotli", "depends": [ "brotli" ], - "artifact": "ext-brotli", "license": { "type": "file", "path": "LICENSE" - } + }, + "php-extension": { + "arg-type": "enable" + }, + "type": "php-extension" }, "ext-bz2": { - "type": "php-extension", + "depends": [ + "bzip2" + ], "php-extension": { "arg-type@windows": "with", "arg-type": "with-path" }, - "depends": [ - "bzip2" - ] + "type": "php-extension" }, "ext-calendar": { "type": "php-extension" @@ -69,68 +69,67 @@ "type": "php-extension" }, "ext-curl": { - "type": "php-extension", - "php-extension": { - "arg-type": "with", - "notes": true - }, "depends": [ "curl" ], "depends@windows": [ "ext-zlib", "ext-openssl" - ] + ], + "php-extension": { + "arg-type": "with", + "notes": true + }, + "type": "php-extension" }, "ext-dba": { - "type": "php-extension", "php-extension": { "arg-type": "custom" }, "suggests": [ "qdbm" - ] + ], + "type": "php-extension" }, "ext-dio": { - "type": "php-extension", - "php-extension": { - "support": { - "BSD": "wip" - } - }, "artifact": "dio", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-dom": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" - }, - "arg-type": "custom", - "arg-type@windows": "with" + } }, + "type": "php-extension" + }, + "ext-dom": { "depends": [ "libxml2", "zlib" ], "depends@windows": [ "ext-xml" - ] + ], + "php-extension": { + "support": { + "BSD": "wip" + }, + "arg-type": "custom", + "arg-type@windows": "with" + }, + "type": "php-extension" }, "ext-ds": { - "type": "php-extension", "artifact": "ext-ds", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "php-extension" }, "ext-enchant": { - "type": "php-extension", "php-extension": { "support": { "Windows": "wip", @@ -138,24 +137,33 @@ "Darwin": "wip", "Linux": "wip" } - } + }, + "type": "php-extension" }, "ext-ev": { - "type": "php-extension", - "php-extension": { - "arg-type@windows": "with" - }, + "artifact": "ev", "depends": [ "ext-sockets" ], - "artifact": "ev", "license": { "type": "file", "path": "LICENSE" - } + }, + "php-extension": { + "arg-type@windows": "with" + }, + "type": "php-extension" }, "ext-event": { - "type": "php-extension", + "artifact": "ext-event", + "depends": [ + "libevent", + "ext-openssl" + ], + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", @@ -164,24 +172,21 @@ "arg-type": "custom", "notes": true }, - "depends": [ - "libevent", - "ext-openssl" - ], "suggests": [ "ext-sockets" ], - "artifact": "ext-event", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-exif": { "type": "php-extension" }, "ext-ffi": { - "type": "php-extension", + "depends": [ + "libffi" + ], + "depends@windows": [ + "libffi-win" + ], "php-extension": { "support": { "Linux": "partial", @@ -190,12 +195,7 @@ "arg-type": "custom", "notes": true }, - "depends@windows": [ - "libffi-win" - ], - "depends": [ - "libffi" - ] + "type": "php-extension" }, "ext-fileinfo": { "type": "php-extension" @@ -204,13 +204,17 @@ "type": "php-extension" }, "ext-ftp": { - "type": "php-extension", "suggests": [ "openssl" - ] + ], + "type": "php-extension" }, "ext-gd": { - "type": "php-extension", + "depends": [ + "zlib", + "libpng", + "ext-zlib" + ], "php-extension": { "support": { "BSD": "wip" @@ -219,20 +223,18 @@ "arg-type@windows": "with", "notes": true }, - "depends": [ - "zlib", - "libpng", - "ext-zlib" - ], "suggests": [ "libavif", "libwebp", "libjpeg", "freetype" - ] + ], + "type": "php-extension" }, "ext-gettext": { - "type": "php-extension", + "depends": [ + "gettext" + ], "php-extension": { "support": { "Windows": "wip", @@ -240,12 +242,18 @@ }, "arg-type": "with-path" }, - "depends": [ - "gettext" - ] + "type": "php-extension" }, "ext-glfw": { - "type": "php-extension", + "artifact": "ext-glfw", + "depends": [ + "glfw" + ], + "depends@windows": [], + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", @@ -255,18 +263,12 @@ "arg-type": "custom", "notes": true }, - "depends": [ - "glfw" - ], - "depends@windows": [], - "artifact": "ext-glfw", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-gmp": { - "type": "php-extension", + "depends": [ + "gmp" + ], "php-extension": { "support": { "Windows": "wip", @@ -274,47 +276,50 @@ }, "arg-type": "with-path" }, - "depends": [ - "gmp" - ] + "type": "php-extension" }, "ext-gmssl": { - "type": "php-extension", - "php-extension": { - "support": { - "BSD": "wip" - } - }, + "artifact": "ext-gmssl", "depends": [ "gmssl" ], - "artifact": "ext-gmssl", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-grpc": { - "type": "php-extension", + }, "php-extension": { "support": { - "Windows": "wip", "BSD": "wip" - }, - "arg-type": "enable-path" + } }, + "type": "php-extension" + }, + "ext-grpc": { + "artifact": "grpc", "depends": [ "grpc" ], "lang": "cpp", - "artifact": "grpc", "license": { "type": "file", "path": "LICENSE" - } + }, + "php-extension": { + "support": { + "Windows": "wip", + "BSD": "wip" + }, + "arg-type": "enable-path" + }, + "type": "php-extension" }, "ext-iconv": { - "type": "php-extension", + "depends": [ + "libiconv" + ], + "depends@windows": [ + "libiconv-win" + ], "php-extension": { "support": { "BSD": "wip" @@ -322,15 +327,14 @@ "arg-type": "with-path", "arg-type@windows": "with" }, - "depends@windows": [ - "libiconv-win" - ], - "depends": [ - "libiconv" - ] + "type": "php-extension" }, "ext-igbinary": { - "type": "php-extension", + "artifact": "igbinary", + "license": { + "type": "file", + "path": "COPYING" + }, "php-extension": { "support": { "BSD": "wip" @@ -340,14 +344,17 @@ "ext-session", "ext-apcu" ], - "artifact": "igbinary", - "license": { - "type": "file", - "path": "COPYING" - } + "type": "php-extension" }, "ext-imagick": { - "type": "php-extension", + "artifact": "ext-imagick", + "depends": [ + "imagemagick" + ], + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", @@ -356,17 +363,19 @@ "arg-type": "custom", "notes": true }, + "type": "php-extension" + }, + "ext-imap": { + "artifact": "ext-imap", "depends": [ - "imagemagick" + "imap" ], - "artifact": "ext-imagick", "license": { "type": "file", - "path": "LICENSE" - } - }, - "ext-imap": { - "type": "php-extension", + "path": [ + "LICENSE" + ] + }, "php-extension": { "support": { "Windows": "wip", @@ -375,22 +384,17 @@ "arg-type": "custom", "notes": true }, - "depends": [ - "imap" - ], "suggests": [ "ext-openssl" ], - "artifact": "ext-imap", - "license": { - "type": "file", - "path": [ - "LICENSE" - ] - } + "type": "php-extension" }, "ext-inotify": { - "type": "php-extension", + "artifact": "inotify", + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "no", @@ -398,28 +402,26 @@ "Darwin": "no" } }, - "artifact": "inotify", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-intl": { - "type": "php-extension", - "php-extension": { - "support": { - "BSD": "wip" - } - }, + "depends": [ + "icu" + ], "depends@windows": [ "icu-static-win" ], - "depends": [ - "icu" - ] + "php-extension": { + "support": { + "BSD": "wip" + } + }, + "type": "php-extension" }, "ext-ldap": { - "type": "php-extension", + "depends": [ + "ldap" + ], "php-extension": { "support": { "Windows": "wip", @@ -427,17 +429,17 @@ }, "arg-type": "with-path" }, - "depends": [ - "ldap" - ], "suggests": [ "gmp", "libsodium", "ext-openssl" - ] + ], + "type": "php-extension" }, "ext-libxml": { - "type": "php-extension", + "depends": [ + "ext-xml" + ], "php-extension": { "support": { "BSD": "wip" @@ -447,50 +449,47 @@ "build-static": true, "build-with-php": true }, - "depends": [ - "ext-xml" - ] + "type": "php-extension" }, "ext-lz4": { - "type": "php-extension", - "php-extension": { - "support": { - "Windows": "wip", - "BSD": "wip" - }, - "arg-type": "custom" - }, + "artifact": "ext-lz4", "depends": [ "liblz4" ], - "artifact": "ext-lz4", "license": { "type": "file", "path": [ "LICENSE" ] - } + }, + "php-extension": { + "support": { + "Windows": "wip", + "BSD": "wip" + }, + "arg-type": "custom" + }, + "type": "php-extension" }, "ext-mbregex": { - "type": "php-extension", + "depends": [ + "onig", + "ext-mbstring" + ], "php-extension": { "arg-type": "custom", "build-shared": false, "build-static": true }, - "depends": [ - "onig", - "ext-mbstring" - ] + "type": "php-extension" }, "ext-mbstring": { - "type": "php-extension", "php-extension": { "arg-type": "custom" - } + }, + "type": "php-extension" }, "ext-mcrypt": { - "type": "php-extension", "php-extension": { "support": { "Windows": "no", @@ -499,10 +498,19 @@ "Linux": "no" }, "notes": true - } + }, + "type": "php-extension" }, "ext-memcache": { - "type": "php-extension", + "artifact": "ext-memcache", + "depends": [ + "ext-zlib", + "ext-session" + ], + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", @@ -511,18 +519,21 @@ "arg-type": "custom", "build-with-php": true }, + "type": "php-extension" + }, + "ext-memcached": { + "artifact": "memcached", "depends": [ - "ext-zlib", - "ext-session" + "libmemcached", + "fastlz", + "ext-session", + "ext-zlib" ], - "artifact": "ext-memcache", + "lang": "cpp", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-memcached": { - "type": "php-extension", + }, "php-extension": { "support": { "Windows": "wip", @@ -530,27 +541,24 @@ }, "arg-type": "custom" }, - "depends": [ - "libmemcached", - "fastlz", - "ext-session", - "ext-zlib" - ], "suggests": [ "zstd", "ext-igbinary", "ext-msgpack", "ext-session" ], - "lang": "cpp", - "artifact": "memcached", + "type": "php-extension" + }, + "ext-mongodb": { + "artifact": "mongodb", + "frameworks": [ + "CoreFoundation", + "Security" + ], "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-mongodb": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip", @@ -564,18 +572,17 @@ "zstd", "zlib" ], - "frameworks": [ - "CoreFoundation", - "Security" + "type": "php-extension" + }, + "ext-msgpack": { + "artifact": "msgpack", + "depends": [ + "ext-session" ], - "artifact": "mongodb", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-msgpack": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" @@ -583,37 +590,29 @@ "arg-type@windows": "enable", "arg-type": "with" }, - "depends": [ - "ext-session" - ], - "artifact": "msgpack", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-mysqli": { - "type": "php-extension", + "depends": [ + "ext-mysqlnd" + ], "php-extension": { "arg-type": "with", "build-with-php": true }, - "depends": [ - "ext-mysqlnd" - ] + "type": "php-extension" }, "ext-mysqlnd": { - "type": "php-extension", + "depends": [ + "zlib" + ], "php-extension": { "arg-type@windows": "with", "build-with-php": true }, - "depends": [ - "zlib" - ] + "type": "php-extension" }, "ext-oci8": { - "type": "php-extension", "php-extension": { "support": { "Windows": "wip", @@ -622,10 +621,13 @@ "Linux": "no" }, "notes": true - } + }, + "type": "php-extension" }, "ext-odbc": { - "type": "php-extension", + "depends": [ + "unixodbc" + ], "php-extension": { "support": { "BSD": "wip", @@ -633,47 +635,52 @@ }, "arg-type": "custom" }, - "depends": [ - "unixodbc" - ] + "type": "php-extension" }, "ext-opcache": { - "type": "php-extension", "php-extension": { "arg-type@windows": "enable", "arg-type": "custom", "zend-extension": true - } + }, + "type": "php-extension" }, "ext-openssl": { - "type": "php-extension", + "depends": [ + "openssl", + "zlib", + "ext-zlib" + ], "php-extension": { "arg-type": "custom", "arg-type@windows": "with", "build-with-php": true, "notes": true }, - "depends": [ - "openssl", - "zlib", - "ext-zlib" - ] + "type": "php-extension" }, "ext-opentelemetry": { - "type": "php-extension", + "artifact": "opentelemetry", + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "BSD": "wip" } }, - "artifact": "opentelemetry", + "type": "php-extension" + }, + "ext-parallel": { + "artifact": "parallel", + "depends@windows": [ + "pthreads4w" + ], "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-parallel": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" @@ -681,17 +688,13 @@ "arg-type@windows": "with", "notes": true }, - "depends@windows": [ - "pthreads4w" - ], - "artifact": "parallel", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-password-argon2": { - "type": "php-extension", + "depends": [ + "libargon2", + "openssl" + ], "php-extension": { "support": { "Windows": "wip", @@ -700,98 +703,100 @@ "arg-type": "custom", "notes": true }, - "depends": [ - "libargon2", - "openssl" - ] + "type": "php-extension" }, "ext-pcntl": { - "type": "php-extension", "php-extension": { "support": { "Windows": "no" } - } + }, + "type": "php-extension" }, "ext-pdo": { "type": "php-extension" }, "ext-pdo_mysql": { - "type": "php-extension", - "php-extension": { - "arg-type": "with" - }, "depends": [ "ext-pdo", "ext-mysqlnd" - ] - }, - "ext-pdo_odbc": { - "type": "php-extension", + ], "php-extension": { - "support": { - "BSD": "wip" - }, - "arg-type": "custom" + "arg-type": "with" }, + "type": "php-extension" + }, + "ext-pdo_odbc": { "depends": [ "unixodbc", "ext-pdo", "ext-odbc" - ] - }, - "ext-pdo_pgsql": { - "type": "php-extension", + ], "php-extension": { "support": { "BSD": "wip" }, - "arg-type": "with-path", - "arg-type@windows": "custom" + "arg-type": "custom" }, - "depends@windows": [ - "postgresql-win" - ], + "type": "php-extension" + }, + "ext-pdo_pgsql": { "depends": [ "postgresql", "ext-pdo", "ext-pgsql" - ] - }, - "ext-pdo_sqlite": { - "type": "php-extension", + ], + "depends@windows": [ + "postgresql-win" + ], "php-extension": { "support": { "BSD": "wip" }, - "arg-type": "with" + "arg-type": "with-path", + "arg-type@windows": "custom" }, + "type": "php-extension" + }, + "ext-pdo_sqlite": { "depends": [ "sqlite", "ext-pdo", "ext-sqlite3" - ] - }, - "ext-pdo_sqlsrv": { - "type": "php-extension", + ], "php-extension": { "support": { "BSD": "wip" }, "arg-type": "with" }, + "type": "php-extension" + }, + "ext-pdo_sqlsrv": { + "artifact": "pdo_sqlsrv", "depends": [ "ext-pdo", "ext-sqlsrv" ], - "artifact": "pdo_sqlsrv", "license": { "type": "file", "path": "LICENSE" - } + }, + "php-extension": { + "support": { + "BSD": "wip" + }, + "arg-type": "with" + }, + "type": "php-extension" }, "ext-pgsql": { - "type": "php-extension", + "depends": [ + "postgresql" + ], + "depends@windows": [ + "postgresql-win" + ], "php-extension": { "support": { "BSD": "wip" @@ -799,43 +804,43 @@ "arg-type": "custom", "notes": true }, - "depends@windows": [ - "postgresql-win" - ], - "depends": [ - "postgresql" - ] + "type": "php-extension" }, "ext-phar": { - "type": "php-extension", "depends": [ "ext-zlib" - ] + ], + "type": "php-extension" }, "ext-posix": { - "type": "php-extension", "php-extension": { "support": { "Windows": "no" } - } + }, + "type": "php-extension" }, "ext-protobuf": { - "type": "php-extension", + "artifact": "protobuf", + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", "BSD": "wip" } }, - "artifact": "protobuf", + "type": "php-extension" + }, + "ext-rar": { + "artifact": "rar", + "lang": "cpp", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-rar": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip", @@ -843,15 +848,18 @@ }, "notes": true }, + "type": "php-extension" + }, + "ext-rdkafka": { + "artifact": "ext-rdkafka", + "depends": [ + "librdkafka" + ], "lang": "cpp", - "artifact": "rar", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-rdkafka": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip", @@ -859,18 +867,12 @@ }, "arg-type": "custom" }, - "depends": [ - "librdkafka" - ], - "lang": "cpp", - "artifact": "ext-rdkafka", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-readline": { - "type": "php-extension", + "depends": [ + "libedit" + ], "php-extension": { "support": { "Windows": "wip", @@ -880,12 +882,17 @@ "build-shared": false, "build-static": true }, - "depends": [ - "libedit" - ] + "type": "php-extension" }, "ext-redis": { - "type": "php-extension", + "artifact": "redis", + "license": { + "type": "file", + "path": [ + "LICENSE", + "COPYING" + ] + }, "php-extension": { "support": { "BSD": "wip" @@ -899,38 +906,36 @@ "ext-igbinary", "ext-msgpack" ], - "artifact": "redis", - "license": { - "type": "file", - "path": [ - "LICENSE", - "COPYING" - ] - } + "type": "php-extension" }, "ext-session": { - "type": "php-extension", "php-extension": { "build-with-php": true - } + }, + "type": "php-extension" }, "ext-shmop": { - "type": "php-extension", "php-extension": { "build-with-php": true - } + }, + "type": "php-extension" }, "ext-simdjson": { - "type": "php-extension", - "lang": "cpp", "artifact": "ext-simdjson", + "lang": "cpp", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "php-extension" }, "ext-simplexml": { - "type": "php-extension", + "depends": [ + "libxml2" + ], + "depends@windows": [ + "ext-xml" + ], "php-extension": { "support": { "BSD": "wip" @@ -938,15 +943,18 @@ "arg-type": "custom", "build-with-php": true }, - "depends": [ - "libxml2" - ], - "depends@windows": [ - "ext-xml" - ] + "type": "php-extension" }, "ext-snappy": { - "type": "php-extension", + "artifact": "ext-snappy", + "depends": [ + "snappy" + ], + "lang": "cpp", + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", @@ -954,21 +962,15 @@ }, "arg-type": "custom" }, - "depends": [ - "snappy" - ], "suggests": [ "ext-apcu" ], - "lang": "cpp", - "artifact": "ext-snappy", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-snmp": { - "type": "php-extension", + "depends": [ + "net-snmp" + ], "php-extension": { "support": { "Windows": "wip", @@ -977,40 +979,45 @@ "arg-type@windows": "with", "arg-type": "with" }, - "depends": [ - "net-snmp" - ] + "type": "php-extension" }, "ext-soap": { - "type": "php-extension", + "depends": [ + "ext-libxml", + "ext-session" + ], "php-extension": { "support": { "BSD": "wip" }, "arg-type": "custom" }, - "depends": [ - "ext-libxml", - "ext-session" - ] + "type": "php-extension" }, "ext-sockets": { "type": "php-extension" }, "ext-sodium": { - "type": "php-extension", + "depends": [ + "libsodium" + ], "php-extension": { "support": { "BSD": "wip" }, "arg-type": "with" }, - "depends": [ - "libsodium" - ] + "type": "php-extension" }, "ext-spx": { - "type": "php-extension", + "artifact": "spx", + "depends": [ + "zlib" + ], + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "BSD": "wip", @@ -1019,17 +1026,12 @@ "arg-type": "custom", "notes": true }, - "depends": [ - "zlib" - ], - "artifact": "spx", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-sqlite3": { - "type": "php-extension", + "depends": [ + "sqlite" + ], "php-extension": { "support": { "BSD": "wip" @@ -1038,17 +1040,10 @@ "arg-type@windows": "with", "build-with-php": true }, - "depends": [ - "sqlite" - ] + "type": "php-extension" }, "ext-sqlsrv": { - "type": "php-extension", - "php-extension": { - "support": { - "BSD": "wip" - } - }, + "artifact": "sqlsrv", "depends": [ "unixodbc" ], @@ -1056,42 +1051,39 @@ "ext-pcntl" ], "lang": "cpp", - "artifact": "sqlsrv", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-ssh2": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" - }, - "arg-type": "with-path", - "arg-type@windows": "with" + } }, + "type": "php-extension" + }, + "ext-ssh2": { + "artifact": "ext-ssh2", "depends": [ "libssh2", "ext-openssl", "ext-zlib" ], - "artifact": "ext-ssh2", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-swoole": { - "type": "php-extension", + }, "php-extension": { "support": { - "Windows": "no", "BSD": "wip" }, - "arg-type": "custom", - "notes": true + "arg-type": "with-path", + "arg-type@windows": "with" }, + "type": "php-extension" + }, + "ext-swoole": { + "artifact": "swoole", "depends": [ "libcares", "brotli", @@ -1100,6 +1092,19 @@ "ext-openssl", "ext-curl" ], + "lang": "cpp", + "license": { + "type": "file", + "path": "LICENSE" + }, + "php-extension": { + "support": { + "Windows": "no", + "BSD": "wip" + }, + "arg-type": "custom", + "notes": true + }, "suggests": [ "zstd", "ext-sockets", @@ -1109,18 +1114,18 @@ "ext-swoole-hook-odbc" ], "suggests@linux": [ - "zstd", - "liburing" - ], - "lang": "cpp", - "artifact": "swoole", - "license": { - "type": "file", - "path": "LICENSE" - } + "zstd", + "liburing" + ], + "type": "php-extension" }, "ext-swoole-hook-mysql": { - "type": "php-extension", + "depends": [ + "ext-mysqlnd", + "ext-pdo", + "ext-pdo_mysql", + "ext-swoole" + ], "php-extension": { "support": { "Windows": "no", @@ -1129,18 +1134,17 @@ "arg-type": "none", "notes": true }, - "depends": [ - "ext-mysqlnd", - "ext-pdo", - "ext-pdo_mysql", - "ext-swoole" - ], "suggests": [ "ext-mysqli" - ] + ], + "type": "php-extension" }, "ext-swoole-hook-odbc": { - "type": "php-extension", + "depends": [ + "unixodbc", + "ext-pdo", + "ext-swoole" + ], "php-extension": { "support": { "Windows": "no", @@ -1149,14 +1153,14 @@ "arg-type": "none", "notes": true }, + "type": "php-extension" + }, + "ext-swoole-hook-pgsql": { "depends": [ - "unixodbc", + "ext-pgsql", "ext-pdo", "ext-swoole" - ] - }, - "ext-swoole-hook-pgsql": { - "type": "php-extension", + ], "php-extension": { "support": { "Windows": "no", @@ -1166,14 +1170,14 @@ "arg-type": "none", "notes": true }, + "type": "php-extension" + }, + "ext-swoole-hook-sqlite": { "depends": [ - "ext-pgsql", + "ext-sqlite3", "ext-pdo", "ext-swoole" - ] - }, - "ext-swoole-hook-sqlite": { - "type": "php-extension", + ], "php-extension": { "support": { "Windows": "no", @@ -1182,14 +1186,14 @@ "arg-type": "none", "notes": true }, - "depends": [ - "ext-sqlite3", - "ext-pdo", - "ext-swoole" - ] + "type": "php-extension" }, "ext-swow": { - "type": "php-extension", + "artifact": "swow", + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "BSD": "wip" @@ -1203,40 +1207,38 @@ "ext-openssl", "ext-curl" ], - "artifact": "swow", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-sysvmsg": { - "type": "php-extension", "php-extension": { "support": { "Windows": "no", "BSD": "wip" } - } + }, + "type": "php-extension" }, "ext-sysvsem": { - "type": "php-extension", "php-extension": { "support": { "Windows": "no", "BSD": "wip" } - } + }, + "type": "php-extension" }, "ext-sysvshm": { - "type": "php-extension", "php-extension": { "support": { "BSD": "wip" } - } + }, + "type": "php-extension" }, "ext-tidy": { - "type": "php-extension", + "depends": [ + "tidy" + ], "php-extension": { "support": { "Windows": "wip", @@ -1244,32 +1246,37 @@ }, "arg-type": "with-path" }, - "depends": [ - "tidy" - ] + "type": "php-extension" }, "ext-tokenizer": { - "type": "php-extension", "php-extension": { "build-with-php": true - } + }, + "type": "php-extension" }, "ext-trader": { - "type": "php-extension", + "artifact": "ext-trader", + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "BSD": "wip", "Windows": "wip" } }, - "artifact": "ext-trader", + "type": "php-extension" + }, + "ext-uuid": { + "artifact": "ext-uuid", + "depends": [ + "libuuid" + ], "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-uuid": { - "type": "php-extension", + }, "php-extension": { "support": { "Windows": "wip", @@ -1277,17 +1284,18 @@ }, "arg-type": "with-path" }, + "type": "php-extension" + }, + "ext-uv": { + "artifact": "ext-uv", "depends": [ - "libuuid" + "libuv", + "ext-sockets" ], - "artifact": "ext-uuid", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-uv": { - "type": "php-extension", + }, "php-extension": { "support": { "Windows": "wip", @@ -1295,18 +1303,14 @@ }, "arg-type": "with-path" }, - "depends": [ - "libuv", - "ext-sockets" - ], - "artifact": "ext-uv", + "type": "php-extension" + }, + "ext-xdebug": { + "artifact": "xdebug", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-xdebug": { - "type": "php-extension", + }, "php-extension": { "support": { "Windows": "wip", @@ -1319,14 +1323,17 @@ "notes": true, "zend-extension": true }, - "artifact": "xdebug", + "type": "php-extension" + }, + "ext-xhprof": { + "artifact": "xhprof", + "depends": [ + "ext-ctype" + ], "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-xhprof": { - "type": "php-extension", + }, "php-extension": { "support": { "Windows": "wip", @@ -1335,17 +1342,18 @@ "build-with-php": true, "notes": true }, + "type": "php-extension" + }, + "ext-xlswriter": { + "artifact": "xlswriter", "depends": [ - "ext-ctype" + "ext-zlib", + "ext-zip" ], - "artifact": "xhprof", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-xlswriter": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" @@ -1355,18 +1363,15 @@ "suggests": [ "openssl" ], - "depends": [ - "ext-zlib", - "ext-zip" - ], - "artifact": "xlswriter", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" }, "ext-xml": { - "type": "php-extension", + "depends": [ + "libxml2" + ], + "depends@windows": [ + "ext-iconv" + ], "php-extension": { "support": { "BSD": "wip" @@ -1376,15 +1381,16 @@ "build-with-php": true, "notes": true }, + "type": "php-extension" + }, + "ext-xmlreader": { "depends": [ "libxml2" ], "depends@windows": [ - "ext-iconv" - ] - }, - "ext-xmlreader": { - "type": "php-extension", + "ext-xml", + "ext-dom" + ], "php-extension": { "support": { "BSD": "wip" @@ -1392,16 +1398,15 @@ "arg-type": "custom", "build-with-php": true }, + "type": "php-extension" + }, + "ext-xmlwriter": { "depends": [ "libxml2" ], "depends@windows": [ - "ext-xml", - "ext-dom" - ] - }, - "ext-xmlwriter": { - "type": "php-extension", + "ext-xml" + ], "php-extension": { "support": { "BSD": "wip" @@ -1409,15 +1414,14 @@ "arg-type": "custom", "build-with-php": true }, - "depends": [ - "libxml2" - ], - "depends@windows": [ - "ext-xml" - ] + "type": "php-extension" }, "ext-xsl": { - "type": "php-extension", + "depends": [ + "libxslt", + "ext-xml", + "ext-dom" + ], "php-extension": { "support": { "Windows": "wip", @@ -1425,71 +1429,63 @@ }, "arg-type": "with-path" }, - "depends": [ - "libxslt", - "ext-xml", - "ext-dom" - ] + "type": "php-extension" }, "ext-xz": { - "type": "php-extension", - "php-extension": { - "arg-type": "with" - }, + "artifact": "ext-xz", "depends": [ "xz" ], - "artifact": "ext-xz", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-yac": { - "type": "php-extension", + }, "php-extension": { - "support": { - "BSD": "wip" - }, - "arg-type": "custom" + "arg-type": "with" }, + "type": "php-extension" + }, + "ext-yac": { + "artifact": "yac", "depends": [ "fastlz", "ext-igbinary" ], - "artifact": "yac", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-yaml": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" }, - "arg-type@windows": "with", - "arg-type": "with-path" + "arg-type": "custom" }, + "type": "php-extension" + }, + "ext-yaml": { + "artifact": "yaml", "depends": [ "libyaml" ], - "artifact": "yaml", "license": { "type": "file", "path": "LICENSE" - } - }, - "ext-zip": { - "type": "php-extension", + }, "php-extension": { "support": { "BSD": "wip" }, - "arg-type": "custom", - "arg-type@windows": "enable" + "arg-type@windows": "with", + "arg-type": "with-path" }, + "type": "php-extension" + }, + "ext-zip": { + "artifact": "ext-zip", + "depends": [ + "libzip" + ], "depends@windows": [ "libzip", "zlib", @@ -1498,17 +1494,23 @@ "ext-zlib", "ext-bz2" ], - "depends": [ - "libzip" - ], - "artifact": "ext-zip", "license": { "type": "file", "path": "LICENSE" - } + }, + "php-extension": { + "support": { + "BSD": "wip" + }, + "arg-type": "custom", + "arg-type@windows": "enable" + }, + "type": "php-extension" }, "ext-zlib": { - "type": "php-extension", + "depends": [ + "zlib" + ], "php-extension": { "arg-type": "custom", "arg-type@windows": "enable", @@ -1516,12 +1518,17 @@ "build-static": true, "build-with-php": true }, - "depends": [ - "zlib" - ] + "type": "php-extension" }, "ext-zstd": { - "type": "php-extension", + "artifact": "ext-zstd", + "depends": [ + "zstd" + ], + "license": { + "type": "file", + "path": "LICENSE" + }, "php-extension": { "support": { "Windows": "wip", @@ -1529,13 +1536,6 @@ }, "arg-type": "custom" }, - "depends": [ - "zstd" - ], - "artifact": "ext-zstd", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "php-extension" } } diff --git a/config/pkg.lib.json b/config/pkg.lib.json index 79e1a8534..6cade9b7f 100644 --- a/config/pkg.lib.json +++ b/config/pkg.lib.json @@ -1,54 +1,62 @@ { "attr": { - "type": "library", "artifact": "attr", "license": { "type": "file", "path": "doc/COPYING.LGPL" - } + }, + "type": "library" }, "brotli": { - "type": "library", + "artifact": "brotli", "headers": [ "brotli" ], + "license": { + "type": "file", + "path": "LICENSE" + }, "pkg-configs": [ "libbrotlicommon", "libbrotlidec", "libbrotlienc" ], - "artifact": "brotli", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "bzip2": { - "type": "library", + "artifact": "bzip2", "headers": [ "bzlib.h" ], - "artifact": "bzip2", "license": { "type": "text", "text": "This program, \"bzip2\", the associated library \"libbzip2\", and all documentation, are copyright (C) 1996-2010 Julian R Seward. All rights reserved. \n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nJulian Seward, jseward@bzip.org bzip2/libbzip2 version 1.0.6 of 6 September 2010\n\nPATENTS: To the best of my knowledge, bzip2 and libbzip2 do not use any patented algorithms. However, I do not have the resources to carry out a patent search. Therefore I cannot give any guarantee of the above statement." - } + }, + "type": "library" }, "curl": { - "type": "library", + "artifact": "curl", + "depends": [ + "openssl", + "zlib" + ], "depends@windows": [ "zlib", "libssh2", "nghttp2" ], - "depends": [ - "openssl", - "zlib" + "frameworks": [ + "CoreFoundation", + "CoreServices", + "SystemConfiguration" ], - "suggests@windows": [ - "brotli", - "zstd" + "headers": [ + "curl" ], + "license": { + "type": "file", + "path": "COPYING" + }, "suggests": [ "libssh2", "brotli", @@ -59,150 +67,142 @@ "libcares", "ldap" ], - "headers": [ - "curl" - ], - "frameworks": [ - "CoreFoundation", - "CoreServices", - "SystemConfiguration" + "suggests@windows": [ + "brotli", + "zstd" ], - "artifact": "curl", - "license": { - "type": "file", - "path": "COPYING" - } + "type": "library" }, "fastlz": { - "type": "library", + "artifact": "fastlz", "headers": [ "fastlz/fastlz.h" ], - "artifact": "fastlz", "license": { "type": "file", "path": "LICENSE.MIT" - } + }, + "type": "library" }, "freetype": { - "type": "library", + "artifact": "freetype", "depends": [ "zlib" ], - "suggests": [ - "libpng", - "bzip2", - "brotli" - ], "headers": [ "freetype2/freetype/freetype.h", "freetype2/ft2build.h" ], - "artifact": "freetype", "license": { "type": "file", "path": "LICENSE.TXT" - } + }, + "suggests": [ + "libpng", + "bzip2", + "brotli" + ], + "type": "library" }, "gettext": { - "type": "library", + "artifact": "gettext", "depends": [ "libiconv" ], - "suggests": [ - "ncurses", - "libxml2" - ], "frameworks": [ "CoreFoundation" ], - "artifact": "gettext", "license": { "type": "file", "path": "gettext-runtime/intl/COPYING.LIB" - } + }, + "suggests": [ + "ncurses", + "libxml2" + ], + "type": "library" }, "glfw": { - "type": "library", + "artifact": "ext-glfw", "frameworks": [ "CoreVideo", "OpenGL", "Cocoa", "IOKit" ], - "artifact": "ext-glfw", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "gmp": { - "type": "library", + "artifact": "gmp", "headers": [ "gmp.h" ], - "artifact": "gmp", "license": { "type": "text", "text": "Since version 6, GMP is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2. These licenses make the library free to use, share, and improve, and allow you to pass on the result. The GNU licenses give freedoms, but also set firm restrictions on the use with non-free programs." - } + }, + "type": "library" }, "gmssl": { - "type": "library", + "artifact": "gmssl", "frameworks": [ "Security" ], - "artifact": "gmssl", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "grpc": { - "type": "library", + "artifact": "grpc", "depends": [ "zlib", "openssl", "libcares" ], - "pkg-configs": [ - "grpc" - ], "frameworks": [ "CoreFoundation" ], - "artifact": "grpc", "license": { "type": "file", "path": "LICENSE" - } + }, + "pkg-configs": [ + "grpc" + ], + "type": "library" }, "icu": { - "type": "library", + "artifact": "icu", + "license": { + "type": "file", + "path": "LICENSE" + }, "pkg-configs": [ "icu-uc", "icu-i18n", "icu-io" ], - "artifact": "icu", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "icu-static-win": { - "type": "library", + "artifact": "icu-static-win", "headers@windows": [ "unicode" ], - "artifact": "icu-static-win", "license": { "type": "text", "text": "none" - } + }, + "type": "library" }, "imagemagick": { - "type": "library", + "artifact": "imagemagick", "depends": [ "zlib", "libjpeg", @@ -214,183 +214,183 @@ "libheif", "bzip2" ], + "license": { + "type": "file", + "path": "LICENSE" + }, + "pkg-configs": [ + "Magick++-7.Q16HDRI", + "MagickCore-7.Q16HDRI", + "MagickWand-7.Q16HDRI" + ], "suggests": [ "zstd", "xz", "libzip", "libxml2" ], - "pkg-configs": [ - "Magick++-7.Q16HDRI", - "MagickCore-7.Q16HDRI", - "MagickWand-7.Q16HDRI" - ], - "artifact": "imagemagick", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "imap": { - "type": "library", - "suggests": [ - "openssl" - ], "artifact": "imap", "license": { "type": "file", "path": "LICENSE" - } + }, + "suggests": [ + "openssl" + ], + "type": "library" }, "jbig": { - "type": "library", + "artifact": "jbig", "headers": [ "jbig.h", "jbig85.h", "jbig_ar.h" ], - "artifact": "jbig", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "ldap": { - "type": "library", + "artifact": "ldap", "depends": [ "openssl", "zlib", "gmp", "libsodium" ], + "license": { + "type": "file", + "path": "LICENSE" + }, "pkg-configs": [ "ldap", "lber" ], - "artifact": "ldap", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "lerc": { - "type": "library", "artifact": "lerc", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libacl": { - "type": "library", + "artifact": "libacl", "depends": [ "attr" ], - "artifact": "libacl", "license": { "type": "file", "path": "doc/COPYING.LGPL" - } + }, + "type": "library" }, "libaom": { - "type": "library", "artifact": "libaom", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libargon2": { - "type": "library", "artifact": "libargon2", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libavif": { - "type": "library", "artifact": "libavif", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libcares": { - "type": "library", + "artifact": "libcares", "headers": [ "ares.h", "ares_dns.h", "ares_nameser.h" ], - "artifact": "libcares", "license": { "type": "file", "path": "LICENSE.md" - } + }, + "type": "library" }, "libde265": { - "type": "library", "artifact": "libde265", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "libedit": { - "type": "library", + "artifact": "libedit", "depends": [ "ncurses" ], - "artifact": "libedit", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "libevent": { - "type": "library", + "artifact": "libevent", "depends": [ "openssl" ], - "artifact": "libevent", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libffi": { - "type": "library", - "headers@windows": [ + "artifact": "libffi", + "headers": [ "ffi.h", - "fficonfig.h", "ffitarget.h" ], - "headers": [ + "headers@windows": [ "ffi.h", + "fficonfig.h", "ffitarget.h" ], - "artifact": "libffi", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libffi-win": { - "type": "library", + "artifact": "libffi-win", "headers@windows": [ "ffi.h", "ffitarget.h", "fficonfig.h" ], - "artifact": "libffi-win", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libheif": { - "type": "library", + "artifact": "libheif", "depends": [ "libde265", "libwebp", @@ -398,113 +398,121 @@ "zlib", "brotli" ], - "artifact": "libheif", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "libiconv": { - "type": "library", + "artifact": "libiconv", "headers": [ "iconv.h", "libcharset.h", "localcharset.h" ], - "artifact": "libiconv", "license": { "type": "file", "path": "COPYING.LIB" - } + }, + "type": "library" }, "libiconv-win": { - "type": "library", "artifact": "libiconv-win", "license": { "type": "file", "path": "source/COPYING" - } + }, + "type": "library" }, "libjpeg": { - "type": "library", - "suggests@windows": [ - "zlib" - ], "artifact": "libjpeg", "license": { "type": "file", "path": "LICENSE.md" - } + }, + "suggests@windows": [ + "zlib" + ], + "type": "library" }, "libjxl": { - "type": "library", + "artifact": "libjxl", "depends": [ "brotli", "libjpeg", "libpng", "libwebp" ], + "license": { + "type": "file", + "path": "LICENSE" + }, "pkg-configs": [ "libjxl", "libjxl_cms", "libjxl_threads", "libhwy" ], - "artifact": "libjxl", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "liblz4": { - "type": "library", "artifact": "liblz4", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libmemcached": { - "type": "library", "artifact": "libmemcached", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libpng": { - "type": "library", + "artifact": "libpng", "depends": [ "zlib" ], - "headers@windows": [ - "png.h", - "pngconf.h" - ], "headers": [ "png.h", "pngconf.h", "pnglibconf.h" ], - "artifact": "libpng", + "headers@windows": [ + "png.h", + "pngconf.h" + ], "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "librabbitmq": { - "type": "library", + "artifact": "librabbitmq", "depends": [ "openssl" ], - "artifact": "librabbitmq", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "librdkafka": { - "type": "library", + "artifact": "librdkafka", + "license": { + "type": "file", + "path": "LICENSE" + }, + "pkg-configs": [ + "rdkafka++-static", + "rdkafka-static" + ], "suggests": [ "curl", "liblz4", @@ -512,26 +520,18 @@ "zlib", "zstd" ], - "pkg-configs": [ - "rdkafka++-static", - "rdkafka-static" - ], - "artifact": "librdkafka", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "libsodium": { - "type": "library", "artifact": "libsodium", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "libssh2": { - "type": "library", + "artifact": "libssh2", "depends": [ "openssl" ], @@ -540,18 +540,22 @@ "libssh2_publickey.h", "libssh2_sftp.h" ], - "artifact": "libssh2", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "libtiff": { - "type": "library", + "artifact": "libtiff", "depends": [ "zlib", "libjpeg" ], + "license": { + "type": "file", + "path": "LICENSE.md" + }, "suggests": [ "lerc", "libwebp", @@ -559,41 +563,36 @@ "xz", "zstd" ], - "artifact": "libtiff", - "license": { - "type": "file", - "path": "LICENSE.md" - } + "type": "library" }, "liburing": { - "type": "library", + "artifact": "liburing", "headers@linux": [ "liburing/", "liburing.h" ], + "license": { + "type": "file", + "path": "COPYING" + }, "pkg-configs": [ "liburing", "liburing-ffi" ], - "artifact": "liburing", - "license": { - "type": "file", - "path": "COPYING" - } + "type": "library" }, "libuuid": { - "type": "library", + "artifact": "libuuid", "headers": [ "uuid/uuid.h" ], - "artifact": "libuuid", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "libuv": { - "type": "library", "artifact": "libuv", "license": [ { @@ -604,10 +603,15 @@ "type": "file", "path": "LICENSE-extra" } - ] + ], + "type": "library" }, "libwebp": { - "type": "library", + "artifact": "libwebp", + "license": { + "type": "file", + "path": "COPYING" + }, "pkg-configs": [ "libwebp", "libwebpdecoder", @@ -615,206 +619,202 @@ "libwebpmux", "libsharpyuv" ], - "artifact": "libwebp", - "license": { - "type": "file", - "path": "COPYING" - } + "type": "library" }, "libxml2": { - "type": "library", - "depends@windows": [ - "libiconv-win" - ], + "artifact": "libxml2", "depends": [ "libiconv" ], - "suggests@windows": [ - "zlib" - ], - "suggests": [ - "xz", - "zlib" + "depends@windows": [ + "libiconv-win" ], "headers": [ "libxml2" ], - "pkg-configs": [ - "libxml-2.0" - ], - "artifact": "libxml2", "license": { "type": "file", "path": "Copyright" - } + }, + "pkg-configs": [ + "libxml-2.0" + ], + "suggests": [ + "xz", + "zlib" + ], + "suggests@windows": [ + "zlib" + ], + "type": "library" }, "libxslt": { - "type": "library", + "artifact": "libxslt", "depends": [ "libxml2" ], - "artifact": "libxslt", "license": { "type": "file", "path": "Copyright" - } + }, + "type": "library" }, "libyaml": { - "type": "library", + "artifact": "libyaml", "headers": [ "yaml.h" ], - "artifact": "libyaml", "license": { "type": "file", "path": "License" - } + }, + "type": "library" }, "libzip": { - "type": "library", + "artifact": "libzip", + "depends": [ + "zlib" + ], "depends@windows": [ "zlib", "bzip2", "xz" ], - "depends": [ - "zlib" - ], - "suggests@windows": [ - "zstd", - "openssl" + "headers": [ + "zip.h", + "zipconf.h" ], + "license": { + "type": "file", + "path": "LICENSE" + }, "suggests": [ "bzip2", "xz", "zstd", "openssl" ], - "headers": [ - "zip.h", - "zipconf.h" + "suggests@windows": [ + "zstd", + "openssl" ], - "artifact": "libzip", - "license": { - "type": "file", - "path": "LICENSE" - } + "type": "library" }, "mimalloc": { - "type": "library", "artifact": "mimalloc", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "ncurses": { - "type": "library", "artifact": "ncurses", - "static-libs@unix": [ - "libncurses.a" - ], "license": { "type": "file", "path": "COPYING" - } + }, + "static-libs@unix": [ + "libncurses.a" + ], + "type": "library" }, "net-snmp": { - "type": "library", + "artifact": "net-snmp", "depends": [ "openssl", "zlib" ], + "license": { + "type": "file", + "path": "COPYING" + }, "pkg-configs": [ "netsnmp", "netsnmp-agent" ], - "artifact": "net-snmp", - "license": { - "type": "file", - "path": "COPYING" - } + "type": "library" }, "nghttp2": { - "type": "library", + "artifact": "nghttp2", "depends": [ "zlib", "openssl" ], - "suggests": [ - "libxml2", - "nghttp3", - "ngtcp2" - ], "headers": [ "nghttp2" ], - "artifact": "nghttp2", "license": { "type": "file", "path": "COPYING" - } + }, + "suggests": [ + "libxml2", + "nghttp3", + "ngtcp2" + ], + "type": "library" }, "nghttp3": { - "type": "library", + "artifact": "nghttp3", "depends": [ "openssl" ], "headers": [ "nghttp3" ], - "artifact": "nghttp3", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "ngtcp2": { - "type": "library", + "artifact": "ngtcp2", "depends": [ "openssl" ], - "suggests": [ - "nghttp3", - "brotli" - ], "headers": [ "ngtcp2" ], - "artifact": "ngtcp2", "license": { "type": "file", "path": "COPYING" - } + }, + "suggests": [ + "nghttp3", + "brotli" + ], + "type": "library" }, "onig": { - "type": "library", + "artifact": "onig", "headers": [ "oniggnu.h", "oniguruma.h" ], - "artifact": "onig", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "openssl": { - "type": "library", + "artifact": "openssl", "depends": [ "zlib" ], "headers": [ "openssl" ], - "artifact": "openssl", "license": { "type": "file", "path": "LICENSE.txt" - } + }, + "type": "library" }, "postgresql": { - "type": "library", + "artifact": "postgresql", "depends": [ "libiconv", "libxml2", @@ -822,69 +822,69 @@ "zlib", "libedit" ], + "license": { + "type": "file", + "path": "COPYRIGHT" + }, + "pkg-configs": [ + "libpq" + ], "suggests": [ "icu", "libxslt", "ldap", "zstd" ], - "pkg-configs": [ - "libpq" - ], - "artifact": "postgresql", - "license": { - "type": "file", - "path": "COPYRIGHT" - } + "type": "library" }, "postgresql-win": { - "type": "library", "artifact": "postgresql-win", "license": { "type": "text", "text": "PostgreSQL Database Management System\n(also known as Postgres, formerly as Postgres95)\n\nPortions Copyright (c) 1996-2025, The PostgreSQL Global Development Group\n\nPortions Copyright (c) 1994, The Regents of the University of California\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose, without fee, and without a written\nagreement is hereby granted, provided that the above copyright notice\nand this paragraph and the following two paragraphs appear in all\ncopies.\n\nIN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY\nFOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,\nINCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS\nDOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n\nTHE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,\nINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS\nON AN \"AS IS\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS\nTO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS." - } + }, + "type": "library" }, "pthreads4w": { - "type": "library", "artifact": "pthreads4w", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "qdbm": { - "type": "library", + "artifact": "qdbm", "headers@windows": [ "depot.h" ], - "artifact": "qdbm", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "re2c": { - "type": "library", "artifact": "re2c", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" }, "readline": { - "type": "library", + "artifact": "readline", "depends": [ "ncurses" ], - "artifact": "readline", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "snappy": { - "type": "library", + "artifact": "snappy", "depends": [ "zlib" ], @@ -894,99 +894,99 @@ "snappy-sinksource.h", "snappy-stubs-public.h" ], - "artifact": "snappy", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "sqlite": { - "type": "library", + "artifact": "sqlite", "headers": [ "sqlite3.h", "sqlite3ext.h" ], - "artifact": "sqlite", "license": { "type": "text", "text": "The author disclaims copyright to this source code. In place of\na legal notice, here is a blessing:\n\n * May you do good and not evil.\n * May you find forgiveness for yourself and forgive others.\n * May you share freely, never taking more than you give." - } + }, + "type": "library" }, "tidy": { - "type": "library", "artifact": "tidy", "license": { "type": "file", "path": "README/LICENSE.md" - } + }, + "type": "library" }, "unixodbc": { - "type": "library", + "artifact": "unixodbc", "depends": [ "libiconv" ], - "artifact": "unixodbc", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "watcher": { - "type": "library", + "artifact": "watcher", "headers": [ "wtr/watcher-c.h" ], - "artifact": "watcher", "license": { "type": "file", "path": "license" - } + }, + "type": "library" }, "xz": { - "type": "library", + "artifact": "xz", "depends": [ "libiconv" ], + "headers": [ + "lzma" + ], "headers@windows": [ "lzma", "lzma.h" ], - "headers": [ - "lzma" - ], - "artifact": "xz", "license": { "type": "file", "path": "COPYING" - } + }, + "type": "library" }, "zlib": { - "type": "library", + "artifact": "zlib", "headers": [ "zlib.h", "zconf.h" ], - "artifact": "zlib", "license": { "type": "text", "text": "(C) 1995-2022 Jean-loup Gailly and Mark Adler\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n claim that you wrote the original software. If you use this software\n in a product, an acknowledgment in the product documentation would be\n appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\nJean-loup Gailly Mark Adler\njloup@gzip.org madler@alumni.caltech.edu" - } + }, + "type": "library" }, "zstd": { - "type": "library", - "headers@windows": [ + "artifact": "zstd", + "headers": [ + "zdict.h", "zstd.h", "zstd_errors.h" ], - "headers": [ - "zdict.h", + "headers@windows": [ "zstd.h", "zstd_errors.h" ], - "artifact": "zstd", "license": { "type": "file", "path": "LICENSE" - } + }, + "type": "library" } } diff --git a/config/pkg.target.json b/config/pkg.target.json index 2ae49f400..8e04df905 100644 --- a/config/pkg.target.json +++ b/config/pkg.target.json @@ -1,98 +1,98 @@ { - "vswhere": { - "type": "target", - "artifact": "vswhere", - "static-bins@windows": [ - "vswhere.exe" - ] + "frankenphp": { + "artifact": "frankenphp", + "depends": [ + "php-embed", + "go-xcaddy" + ], + "depends@macos": [ + "php-embed", + "go-xcaddy", + "libxml2" + ], + "type": "virtual-target" }, - "pkg-config": { - "type": "target", + "go-xcaddy": { + "artifact": "go-xcaddy", "static-bins": [ - "pkg-config" + "xcaddy" ], - "artifact": "pkg-config" + "type": "target" + }, + "musl-toolchain": { + "artifact": "musl-toolchain", + "type": "target" + }, + "nasm": { + "artifact": "nasm", + "type": "target" }, "php": { - "type": "target", "artifact": "php-src", "depends@macos": [ "libxml2" - ] + ], + "type": "target" }, - "php-cli": { - "type": "virtual-target", + "php-cgi": { "depends": [ "php" - ] + ], + "type": "virtual-target" }, - "php-micro": { - "type": "virtual-target", - "artifact": "micro", + "php-cli": { "depends": [ "php" - ] + ], + "type": "virtual-target" }, - "php-cgi": { - "type": "virtual-target", + "php-embed": { "depends": [ "php" - ] + ], + "type": "virtual-target" }, "php-fpm": { - "type": "virtual-target", "depends": [ "php" - ] + ], + "type": "virtual-target" }, - "php-embed": { - "type": "virtual-target", + "php-micro": { + "artifact": "micro", "depends": [ "php" - ] - }, - "frankenphp": { - "type": "virtual-target", - "artifact": "frankenphp", - "depends": [ - "php-embed", - "go-xcaddy" ], - "depends@macos": [ - "php-embed", - "go-xcaddy", - "libxml2" - ] + "type": "virtual-target" }, - "go-xcaddy": { - "type": "target", - "artifact": "go-xcaddy", - "static-bins": [ - "xcaddy" - ] + "php-sdk-binary-tools": { + "artifact": "php-sdk-binary-tools", + "type": "target" }, - "musl-toolchain": { - "type": "target", - "artifact": "musl-toolchain" + "pkg-config": { + "artifact": "pkg-config", + "static-bins": [ + "pkg-config" + ], + "type": "target" }, "strawberry-perl": { - "type": "target", - "artifact": "strawberry-perl" + "artifact": "strawberry-perl", + "type": "target" }, "upx": { - "type": "target", - "artifact": "upx" - }, - "zig": { - "type": "target", - "artifact": "zig" + "artifact": "upx", + "type": "target" }, - "nasm": { - "type": "target", - "artifact": "nasm" + "vswhere": { + "artifact": "vswhere", + "static-bins@windows": [ + "vswhere.exe" + ], + "type": "target" }, - "php-sdk-binary-tools": { - "type": "target", - "artifact": "php-sdk-binary-tools" + "zig": { + "artifact": "zig", + "type": "target" } } diff --git a/config/source.json b/config/source.json index 9a80cd059..036260155 100644 --- a/config/source.json +++ b/config/source.json @@ -126,13 +126,23 @@ }, "ext-event": { "type": "url", - "url": "https://bitbucket.org/osmanov/pecl-event/get/3.0.8.tar.gz", + "url": "https://bitbucket.org/osmanov/pecl-event/get/3.1.4.tar.gz", "path": "php-src/ext/event", "license": { "type": "file", "path": "LICENSE" } }, + "ext-excimer": { + "type": "url", + "url": "https://pecl.php.net/get/excimer", + "path": "php-src/ext/excimer", + "filename": "excimer.tgz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-glfw": { "type": "git", "url": "https://github.com/mario-deluna/php-glfw", @@ -151,6 +161,18 @@ "path": "LICENSE" } }, + "ext-grpc": { + "type": "url", + "url": "https://pecl.php.net/get/grpc", + "path": "php-src/ext/grpc", + "filename": "grpc.tgz", + "license": { + "type": "file", + "path": [ + "LICENSE" + ] + } + }, "ext-imagick": { "type": "url", "url": "https://pecl.php.net/get/imagick", @@ -670,9 +692,10 @@ } }, "libpng": { - "type": "git", - "url": "https://github.com/glennrp/libpng.git", - "rev": "libpng16", + "type": "ghtagtar", + "repo": "pnggroup/libpng", + "match": "v1\\.6\\.\\d+", + "query": "?per_page=150", "provide-pre-built": true, "license": { "type": "file", @@ -680,9 +703,9 @@ } }, "librabbitmq": { - "type": "git", - "url": "https://github.com/alanxz/rabbitmq-c.git", - "rev": "master", + "type": "ghtar", + "repo": "alanxz/rabbitmq-c", + "prefer-stable": true, "license": { "type": "file", "path": "LICENSE" @@ -699,7 +722,7 @@ "libsodium": { "type": "ghrel", "repo": "jedisct1/libsodium", - "match": "libsodium-\\d+(\\.\\d+)*\\.tar\\.gz", + "match": "libsodium-(?!1\\.0\\.21)\\d+(\\.\\d+)*\\.tar\\.gz", "prefer-stable": true, "provide-pre-built": true, "license": { @@ -771,8 +794,9 @@ ] }, "libwebp": { - "type": "url", - "url": "https://github.com/webmproject/libwebp/archive/refs/tags/v1.3.2.tar.gz", + "type": "ghtagtar", + "repo": "webmproject/libwebp", + "match": "v1\\.\\d+\\.\\d+$", "provide-pre-built": true, "license": { "type": "file", @@ -780,8 +804,10 @@ } }, "libxml2": { - "type": "url", - "url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.12.5.tar.gz", + "type": "ghtagtar", + "repo": "GNOME/libxml2", + "match": "v2\\.\\d+\\.\\d+$", + "provide-pre-built": false, "license": { "type": "file", "path": "Copyright" @@ -868,6 +894,24 @@ "path": "LICENSE" } }, + "mysqlnd_ed25519": { + "type": "pie", + "repo": "mariadb/mysqlnd_ed25519", + "path": "php-src/ext/mysqlnd_ed25519", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "mysqlnd_parsec": { + "type": "pie", + "repo": "mariadb/mysqlnd_parsec", + "path": "php-src/ext/mysqlnd_parsec", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ncurses": { "type": "filelist", "url": "https://ftp.gnu.org/pub/gnu/ncurses/", @@ -1169,9 +1213,8 @@ } }, "xdebug": { - "type": "url", - "url": "https://pecl.php.net/get/xdebug", - "filename": "xdebug.tgz", + "type": "pie", + "repo": "xdebug/xdebug", "license": { "type": "file", "path": "LICENSE" diff --git a/docs/en/guide/manual-build.md b/docs/en/guide/manual-build.md index 28f0ec87e..319022ca3 100644 --- a/docs/en/guide/manual-build.md +++ b/docs/en/guide/manual-build.md @@ -549,22 +549,24 @@ otherwise it will be executed repeatedly in other events. The following are the supported `patch_point` event names and corresponding locations: -| Event name | Event description | -|------------------------------|----------------------------------------------------------------------------------------------------| -| before-libs-extract | Triggered before the dependent libraries extracted | -| after-libs-extract | Triggered after the compiled dependent libraries extracted | -| before-php-extract | Triggered before PHP source code extracted | -| after-php-extract | Triggered after PHP source code extracted | -| before-micro-extract | Triggered before phpmicro extract | -| after-micro-extract | Triggered after phpmicro extracted | -| before-exts-extract | Triggered before the extension (to be compiled) extracted to the PHP source directory | -| after-exts-extract | Triggered after the extension extracted to the PHP source directory | -| before-library[*name*]-build | Triggered before the library named `name` is compiled (such as `before-library[postgresql]-build`) | -| after-library[*name*]-build | Triggered after the library named `name` is compiled | -| before-php-buildconf | Triggered before compiling PHP command `./buildconf` | -| before-php-configure | Triggered before compiling PHP command `./configure` | -| before-php-make | Triggered before compiling PHP command `make` | -| before-sanity-check | Triggered after compiling PHP but before running extended checks | +| Event name | Event description | +|---------------------------------|----------------------------------------------------------------------------------------------------| +| before-libs-extract | Triggered before the dependent libraries extracted | +| after-libs-extract | Triggered after the compiled dependent libraries extracted | +| before-php-extract | Triggered before PHP source code extracted | +| after-php-extract | Triggered after PHP source code extracted | +| before-micro-extract | Triggered before phpmicro extract | +| after-micro-extract | Triggered after phpmicro extracted | +| before-exts-extract | Triggered before the extension (to be compiled) extracted to the PHP source directory | +| after-exts-extract | Triggered after the extension extracted to the PHP source directory | +| before-library[*name*]-build | Triggered before the library named `name` is compiled (such as `before-library[postgresql]-build`) | +| after-library[*name*]-build | Triggered after the library named `name` is compiled | +| after-shared-ext[*name*]-build | Triggered after the shared extension named `name` is compiled | +| before-shared-ext[*name*]-build | Triggered before the shared extension named `name` is compiled | +| before-php-buildconf | Triggered before compiling PHP command `./buildconf` | +| before-php-configure | Triggered before compiling PHP command `./configure` | +| before-php-make | Triggered before compiling PHP command `make` | +| before-sanity-check | Triggered after compiling PHP but before running extended checks | The following is a simple example of temporarily modifying the PHP source code. Enable the CLI function to search for the `php.ini` configuration in the current working directory: diff --git a/docs/zh/guide/manual-build.md b/docs/zh/guide/manual-build.md index 4c24cab8f..d7745a02b 100644 --- a/docs/zh/guide/manual-build.md +++ b/docs/zh/guide/manual-build.md @@ -500,6 +500,8 @@ bin/spc dev:sort-config ext | after-exts-extract | 在要编译的扩展解压到 PHP 源码目录后触发 | | before-library[*name*]-build | 在名称为 `name` 的库编译前触发(如 `before-library[postgresql]-build`) | | after-library[*name*]-build | 在名称为 `name` 的库编译后触发 | +| after-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译后触发(如 `after-shared-ext[redis]-build`) | +| before-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译前触发 | | before-php-buildconf | 在编译 PHP 命令 `./buildconf` 前触发 | | before-php-configure | 在编译 PHP 命令 `./configure` 前触发 | | before-php-make | 在编译 PHP 命令 `make` 前触发 | diff --git a/phpstan.neon b/phpstan.neon index cf6e49742..45e512ba0 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,6 +1,7 @@ parameters: reportUnmatchedIgnoredErrors: false level: 4 + phpVersion: 80400 paths: - ./src/ ignoreErrors: diff --git a/skeleton-test.php b/skeleton-test.php new file mode 100644 index 000000000..59fbbb7e1 --- /dev/null +++ b/skeleton-test.php @@ -0,0 +1,28 @@ +addDependency('bar') + ->addStaticLib('libfoo.a', 'unix') + ->addStaticLib('libfoo.a', 'unix') + ->addArtifact($artifact_generator = new ArtifactGenerator('foo')->setSource(['type' => 'url', 'url' => 'https://example.com/foo.tar.gz'])) + ->enableBuild(['Darwin', 'Linux'], 'build') + ->addFunctionExecutorBinding('build', new ExecutorGenerator(UnixCMakeExecutor::class)); + +$pkg_config = $package_generator->generateConfigArray(); +$artifact_config = $artifact_generator->generateConfigArray(); + +echo '===== pkg.json =====' . PHP_EOL; +echo json_encode($pkg_config, 64 | 128 | 256) . PHP_EOL; +echo '===== artifact.json =====' . PHP_EOL; +echo json_encode($artifact_config, 64 | 128 | 256) . PHP_EOL; +echo '===== php code for package =====' . PHP_EOL; +echo $package_generator->generatePackageClassFile('Package\Library'); diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index 414c3bde8..19fdd41dc 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -34,7 +34,7 @@ */ final class ConsoleApplication extends Application { - public const string VERSION = '3.0.0-dev'; + public const string VERSION = '2.8.0'; public function __construct() { diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 08b403e61..925a4c8ef 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -385,6 +385,9 @@ public function buildShared(array $visited = []): void logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)'); return; } + if ((string) Config::getExt($this->getName(), 'type') === 'addon') { + return; + } logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)'); foreach ($this->dependencies as $dependency) { if (!$dependency instanceof Extension) { @@ -395,13 +398,12 @@ public function buildShared(array $visited = []): void $dependency->buildShared([...$visited, $this->getName()]); } } - if (Config::getExt($this->getName(), 'type') === 'addon') { - return; - } + $this->builder->emitPatchPoint('before-shared-ext[' . $this->getName() . ']-build'); match (PHP_OS_FAMILY) { 'Darwin', 'Linux' => $this->buildUnixShared(), default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'), }; + $this->builder->emitPatchPoint('after-shared-ext[' . $this->getName() . ']-build'); } catch (SPCException $e) { $e->bindExtensionInfo(['extension_name' => $this->getName()]); throw $e; @@ -452,12 +454,17 @@ public function buildUnixShared(): void // process *.so file $soFile = BUILD_MODULES_PATH . '/' . $this->getName() . '.so'; + $soDest = $soFile; + preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches); + if (!empty($matches[1])) { + $soDest = str_replace('.so', '-' . $matches[1] . '.so', $soFile); + } if (!file_exists($soFile)) { throw new ValidationException("extension {$this->getName()} build failed: {$soFile} not found", validation_module: "Extension {$this->getName()} build"); } /** @var UnixBuilderBase $builder */ $builder = $this->builder; - $builder->deployBinary($soFile, $soFile, false); + $builder->deployBinary($soFile, $soDest, false); } /** @@ -543,6 +550,7 @@ protected function getSharedExtensionEnv(): array 'CFLAGS' => $config['cflags'], 'CXXFLAGS' => $config['cflags'], 'LDFLAGS' => $config['ldflags'], + 'EXTRA_LDFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, ]; diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index c88e8d96c..383faa41a 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -184,18 +184,18 @@ public function tryBuild(bool $force_build = false): int // extract first if not exists if (!is_dir($this->source_dir)) { - $this->getBuilder()->emitPatchPoint('before-library[ ' . static::NAME . ']-extract'); + $this->getBuilder()->emitPatchPoint('before-library[' . static::NAME . ']-extract'); SourceManager::initSource(libs: [static::NAME], source_only: true); - $this->getBuilder()->emitPatchPoint('after-library[ ' . static::NAME . ']-extract'); + $this->getBuilder()->emitPatchPoint('after-library[' . static::NAME . ']-extract'); } if (!$this->patched && $this->patchBeforeBuild()) { file_put_contents($this->source_dir . '/.spc.patched', 'PATCHED!!!'); } - $this->getBuilder()->emitPatchPoint('before-library[ ' . static::NAME . ']-build'); + $this->getBuilder()->emitPatchPoint('before-library[' . static::NAME . ']-build'); $this->build(); $this->installLicense(); - $this->getBuilder()->emitPatchPoint('after-library[ ' . static::NAME . ']-build'); + $this->getBuilder()->emitPatchPoint('after-library[' . static::NAME . ']-build'); return LIB_STATUS_OK; } @@ -346,19 +346,19 @@ protected function getSnakeCaseName(): string */ protected function installLicense(): void { - FileSystem::createDir(BUILD_ROOT_PATH . '/source-licenses/' . $this->getName()); $source = Config::getLib($this->getName(), 'source'); + FileSystem::createDir(BUILD_ROOT_PATH . "/source-licenses/{$source}"); $license_files = Config::getSource($source)['license'] ?? []; if (is_assoc_array($license_files)) { $license_files = [$license_files]; } foreach ($license_files as $index => $license) { if ($license['type'] === 'text') { - FileSystem::writeFile(BUILD_ROOT_PATH . '/source-licenses/' . $this->getName() . "/{$index}.txt", $license['text']); + FileSystem::writeFile(BUILD_ROOT_PATH . "/source-licenses/{$source}/{$index}.txt", $license['text']); continue; } if ($license['type'] === 'file') { - copy($this->source_dir . '/' . $license['path'], BUILD_ROOT_PATH . '/source-licenses/' . $this->getName() . "/{$index}.txt"); + copy($this->source_dir . '/' . $license['path'], BUILD_ROOT_PATH . "/source-licenses/{$source}/{$index}.txt"); } } } @@ -375,8 +375,17 @@ protected function isLibraryInstalled(): bool return false; } } + $pkg_config_path = getenv('PKG_CONFIG_PATH') ?: ''; + $search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)); foreach (Config::getLib(static::NAME, 'pkg-configs', []) as $name) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$name}.pc")) { + $found = false; + foreach ($search_paths as $path) { + if (file_exists($path . "/{$name}.pc")) { + $found = true; + break; + } + } + if (!$found) { return false; } } diff --git a/src/SPC/builder/extension/excimer.php b/src/SPC/builder/extension/excimer.php new file mode 100644 index 000000000..03dd8f228 --- /dev/null +++ b/src/SPC/builder/extension/excimer.php @@ -0,0 +1,19 @@ +builder instanceof WindowsBuilder) { throw new ValidationException('grpc extension does not support windows yet'); } - if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) { - 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 ValidationException('Cannot find grpc source code in ' . $this->source_dir . '/src/php/ext/grpc'); - } + FileSystem::replaceFileStr( + $this->source_dir . '/src/php/ext/grpc/call.c', + 'zend_exception_get_default(TSRMLS_C),', + 'zend_ce_exception,', + ); if (SPCTarget::getTargetOS() === 'Darwin') { FileSystem::replaceFileRegex( - SOURCE_PATH . '/php-src/ext/grpc/config.m4', + $this->source_dir . '/config.m4', '/GRPC_LIBDIR=.*$/m', 'GRPC_LIBDIR=' . BUILD_LIB_PATH . "\n" . 'LDFLAGS="$LDFLAGS -framework CoreFoundation"' ); diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index bef772ee6..a548a8a3d 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -5,6 +5,8 @@ namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\toolchain\ToolchainManager; +use SPC\toolchain\ZigToolchain; use SPC\util\CustomExt; #[CustomExt('imagick')] @@ -19,7 +21,9 @@ public function getUnixConfigureArg(bool $shared = false): string protected function splitLibsIntoStaticAndShared(string $allLibs): array { [$static, $shared] = parent::splitLibsIntoStaticAndShared($allLibs); - if (str_contains(getenv('PATH'), 'rh/devtoolset') || str_contains(getenv('PATH'), 'rh/gcc-toolset')) { + if (ToolchainManager::getToolchainClass() !== ZigToolchain::class && + (str_contains(getenv('PATH'), 'rh/devtoolset') || str_contains(getenv('PATH'), 'rh/gcc-toolset')) + ) { $static .= ' -l:libstdc++.a'; $shared = str_replace('-lstdc++', '', $shared); } diff --git a/src/SPC/builder/extension/memcache.php b/src/SPC/builder/extension/memcache.php index b63fa47a4..32cb301c6 100644 --- a/src/SPC/builder/extension/memcache.php +++ b/src/SPC/builder/extension/memcache.php @@ -18,6 +18,9 @@ public function getUnixConfigureArg(bool $shared = false): string public function patchBeforeBuildconf(): bool { + if (!$this->isBuildStatic()) { + return false; + } FileSystem::replaceFileStr( SOURCE_PATH . '/php-src/ext/memcache/config9.m4', 'if test -d $abs_srcdir/src ; then', @@ -43,4 +46,27 @@ public function patchBeforeBuildconf(): bool ); return true; } + + public function patchBeforeSharedConfigure(): bool + { + if (!$this->isBuildShared()) { + return false; + } + FileSystem::replaceFileStr( + SOURCE_PATH . '/php-src/ext/memcache/config9.m4', + 'if test -d $abs_srcdir/main ; then', + 'if test -d $abs_srcdir/src ; then', + ); + FileSystem::replaceFileStr( + SOURCE_PATH . '/php-src/ext/memcache/config9.m4', + 'export CPPFLAGS="$CPPFLAGS $INCLUDES -I$abs_srcdir/main"', + 'export CPPFLAGS="$CPPFLAGS $INCLUDES"', + ); + return true; + } + + protected function getExtraEnv(): array + { + return ['CFLAGS' => '-std=c17']; + } } diff --git a/src/SPC/builder/extension/mongodb.php b/src/SPC/builder/extension/mongodb.php index 745417bb1..08861e4ef 100644 --- a/src/SPC/builder/extension/mongodb.php +++ b/src/SPC/builder/extension/mongodb.php @@ -24,4 +24,9 @@ public function getUnixConfigureArg(bool $shared = false): string $arg .= $this->builder->getLib('zlib') ? ' --with-mongodb-zlib=yes ' : ' --with-mongodb-zlib=bundled '; return clean_spaces($arg); } + + public function getExtraEnv(): array + { + return ['CFLAGS' => '-std=c17']; + } } diff --git a/src/SPC/builder/extension/mysqlnd_ed25519.php b/src/SPC/builder/extension/mysqlnd_ed25519.php new file mode 100644 index 000000000..7b2b4abcd --- /dev/null +++ b/src/SPC/builder/extension/mysqlnd_ed25519.php @@ -0,0 +1,22 @@ +getConfigureArg(); + } +} diff --git a/src/SPC/builder/extension/mysqlnd_parsec.php b/src/SPC/builder/extension/mysqlnd_parsec.php new file mode 100644 index 000000000..d044b1c52 --- /dev/null +++ b/src/SPC/builder/extension/mysqlnd_parsec.php @@ -0,0 +1,22 @@ +getConfigureArg(); + } +} diff --git a/src/SPC/builder/extension/password_argon2.php b/src/SPC/builder/extension/password_argon2.php index 30e6fd2c5..d42fe4e37 100644 --- a/src/SPC/builder/extension/password_argon2.php +++ b/src/SPC/builder/extension/password_argon2.php @@ -24,25 +24,6 @@ public function runCliCheckUnix(): void } } - public function patchBeforeMake(): bool - { - $patched = parent::patchBeforeMake(); - if ($this->builder->getLib('libsodium') !== null) { - $extraLibs = getenv('SPC_EXTRA_LIBS'); - if ($extraLibs !== false) { - $extraLibs = str_replace( - [BUILD_LIB_PATH . '/libargon2.a', BUILD_LIB_PATH . '/libsodium.a'], - ['', BUILD_LIB_PATH . '/libargon2.a ' . BUILD_LIB_PATH . '/libsodium.a'], - $extraLibs, - ); - $extraLibs = trim(preg_replace('/\s+/', ' ', $extraLibs)); // normalize spacing - f_putenv('SPC_EXTRA_LIBS=' . $extraLibs); - return true; - } - } - return $patched; - } - public function getConfigureArg(bool $shared = false): string { if ($this->builder->getLib('openssl') !== null) { diff --git a/src/SPC/builder/extension/pgsql.php b/src/SPC/builder/extension/pgsql.php index f22f7ba68..06efa8b13 100644 --- a/src/SPC/builder/extension/pgsql.php +++ b/src/SPC/builder/extension/pgsql.php @@ -45,7 +45,7 @@ public function getWindowsConfigureArg(bool $shared = false): string protected function getExtraEnv(): array { return [ - 'CFLAGS' => '-Wno-int-conversion', + 'CFLAGS' => '-std=c17 -Wno-int-conversion', ]; } } diff --git a/src/SPC/builder/extension/swoole.php b/src/SPC/builder/extension/swoole.php index f6ff5931d..4e292a362 100644 --- a/src/SPC/builder/extension/swoole.php +++ b/src/SPC/builder/extension/swoole.php @@ -17,6 +17,7 @@ class swoole extends Extension public function patchBeforeMake(): bool { $patched = parent::patchBeforeMake(); + FileSystem::replaceFileStr($this->source_dir . '/ext-src/php_swoole_private.h', 'PHP_VERSION_ID > 80500', 'PHP_VERSION_ID >= 80600'); if ($this->builder instanceof MacOSBuilder) { // Fix swoole with event extension conflict bug $util_path = shell()->execWithResult('xcrun --show-sdk-path', false)[1][0] . '/usr/include/util.h'; diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 0d6f77fba..004c37def 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -283,11 +283,14 @@ protected function buildEmbed(): void // process libphp.so for shared embed $libphpSo = BUILD_LIB_PATH . '/libphp.so'; + $libphpSoDest = BUILD_LIB_PATH . '/libphp.so'; if (file_exists($libphpSo)) { - // post actions: rename libphp.so to libphp-.so if -release is set in LDFLAGS - $this->processLibphpSoFile($libphpSo); // deploy libphp.so - $this->deployBinary($libphpSo, $libphpSo, false); + preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches); + if (!empty($matches[1])) { + $libphpSoDest = str_replace('.so', '-' . $matches[1] . '.so', $libphpSo); + } + $this->deployBinary($libphpSo, $libphpSoDest, false); } // process shared extensions build-with-php @@ -324,74 +327,6 @@ private function getMakeExtraVars(): array ]); } - private function processLibphpSoFile(string $libphpSo): void - { - $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: ''; - $libDir = BUILD_LIB_PATH; - $modulesDir = BUILD_MODULES_PATH; - $realLibName = 'libphp.so'; - $cwd = getcwd(); - - if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) { - $release = $matches[1]; - $realLibName = "libphp-{$release}.so"; - $libphpRelease = "{$libDir}/{$realLibName}"; - if (!file_exists($libphpRelease) && file_exists($libphpSo)) { - rename($libphpSo, $libphpRelease); - } - if (file_exists($libphpRelease)) { - chdir($libDir); - if (file_exists($libphpSo)) { - unlink($libphpSo); - } - symlink($realLibName, 'libphp.so'); - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg($realLibName), - escapeshellarg($libphpRelease) - )); - } - if (is_dir($modulesDir)) { - chdir($modulesDir); - foreach ($this->getExts() as $ext) { - if (!$ext->isBuildShared()) { - continue; - } - $name = $ext->getName(); - $versioned = "{$name}-{$release}.so"; - $unversioned = "{$name}.so"; - $src = "{$modulesDir}/{$versioned}"; - $dst = "{$modulesDir}/{$unversioned}"; - if (is_file($src)) { - rename($src, $dst); - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg($unversioned), - escapeshellarg($dst) - )); - } - } - } - chdir($cwd); - } - - $target = "{$libDir}/{$realLibName}"; - if (file_exists($target)) { - [, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target)); - $output = implode("\n", $output); - if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) { - $currentSoname = $sonameMatch[1]; - if ($currentSoname !== basename($target)) { - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg(basename($target)), - escapeshellarg($target) - )); - } - } - } - } - /** * Patch micro.sfx after UPX compression. * micro needs special section handling in LinuxBuilder. diff --git a/src/SPC/builder/linux/library/liburing.php b/src/SPC/builder/linux/library/liburing.php index 82249cf80..9a67f50c4 100644 --- a/src/SPC/builder/linux/library/liburing.php +++ b/src/SPC/builder/linux/library/liburing.php @@ -6,6 +6,8 @@ use SPC\builder\linux\SystemUtil; use SPC\store\FileSystem; +use SPC\toolchain\GccNativeToolchain; +use SPC\toolchain\ToolchainManager; use SPC\util\executor\UnixAutoconfExecutor; use SPC\util\SPCTarget; @@ -15,26 +17,19 @@ class liburing extends LinuxLibraryBase public function patchBeforeBuild(): bool { - if (!SystemUtil::isMuslDist()) { - return false; + if (SystemUtil::isMuslDist()) { + FileSystem::replaceFileStr($this->source_dir . '/configure', 'realpath -s', 'realpath'); + return true; } - FileSystem::replaceFileStr($this->source_dir . '/configure', 'realpath -s', 'realpath'); - return true; + return false; } protected function build(): void { - $use_libc = SPCTarget::getLibc() !== 'glibc' || version_compare(SPCTarget::getLibcVersion(), '2.30', '>='); + $use_libc = ToolchainManager::getToolchainClass() !== GccNativeToolchain::class || version_compare(SPCTarget::getLibcVersion(), '2.30', '>='); $make = UnixAutoconfExecutor::create($this); - if (!$use_libc) { - $make->appendEnv([ - 'CC' => 'gcc', // libc-less version fails to compile with clang or zig - 'CXX' => 'g++', - 'AR' => 'ar', - 'LD' => 'ld', - ]); - } else { + if ($use_libc) { $make->appendEnv([ 'CFLAGS' => '-D_GNU_SOURCE', ]); @@ -51,7 +46,7 @@ protected function build(): void $use_libc ? '--use-libc' : '', ) ->configure() - ->make('library', 'install ENABLE_SHARED=0', with_clean: false); + ->make('library ENABLE_SHARED=0', 'install ENABLE_SHARED=0', with_clean: false); $this->patchPkgconfPrefix(); } diff --git a/src/SPC/builder/linux/library/openssl.php b/src/SPC/builder/linux/library/openssl.php index d9e04b323..bfc3936ba 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -21,6 +21,7 @@ namespace SPC\builder\linux\library; +use SPC\builder\linux\SystemUtil; use SPC\store\FileSystem; class openssl extends LinuxLibraryBase @@ -51,6 +52,9 @@ public function build(): void $zlib_extra = ''; } + $openssl_dir = getenv('OPENSSLDIR') ?: null; + // TODO: in v3 use the following: $openssl_dir ??= SystemUtil::getOSRelease()['dist'] === 'redhat' ? '/etc/pki/tls' : '/etc/ssl'; + $openssl_dir ??= '/etc/ssl'; $ex_lib = trim($ex_lib); shell()->cd($this->source_dir)->initializeEnv($this) @@ -58,10 +62,11 @@ public function build(): void "{$env} ./Configure no-shared {$extra} " . '--prefix=' . BUILD_ROOT_PATH . ' ' . '--libdir=' . BUILD_LIB_PATH . ' ' . - '--openssldir=/etc/ssl ' . + "--openssldir={$openssl_dir} " . "{$zlib_extra}" . 'enable-pie ' . 'no-legacy ' . + 'no-tests ' . "linux-{$arch}" ) ->exec('make clean') diff --git a/src/SPC/builder/traits/UnixLibraryTrait.php b/src/SPC/builder/traits/UnixLibraryTrait.php index 868cf6f7b..ec1e7c2be 100644 --- a/src/SPC/builder/traits/UnixLibraryTrait.php +++ b/src/SPC/builder/traits/UnixLibraryTrait.php @@ -34,7 +34,7 @@ public function patchPkgconfPrefix(array $files = [], int $patch_option = PKGCON $files = array_map(fn ($x) => "{$x}.pc", $conf_pc); } foreach ($files as $name) { - $realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name); + $realpath = realpath(BUILD_LIB_PATH . '/pkgconfig/' . $name); if ($realpath === false) { throw new PatchException('pkg-config prefix patcher', 'Cannot find library [' . static::NAME . '] pkgconfig file [' . $name . '] in ' . BUILD_LIB_PATH . '/pkgconfig/ !'); } diff --git a/src/SPC/builder/traits/UnixSystemUtilTrait.php b/src/SPC/builder/traits/UnixSystemUtilTrait.php index b1ef9db45..ff75bf7c2 100644 --- a/src/SPC/builder/traits/UnixSystemUtilTrait.php +++ b/src/SPC/builder/traits/UnixSystemUtilTrait.php @@ -72,12 +72,8 @@ public static function getDynamicExportedSymbols(string $lib_file): ?string if (!is_file($symbol_file)) { throw new SPCInternalException("The symbol file {$symbol_file} does not exist, please check if nm command is available."); } - // https://github.com/ziglang/zig/issues/24662 - if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { - return '-Wl,--export-dynamic'; - } - // macOS - if (SPCTarget::getTargetOS() !== 'Linux') { + // macOS/zig + if (SPCTarget::getTargetOS() !== 'Linux' || ToolchainManager::getToolchainClass() === ZigToolchain::class) { return "-Wl,-exported_symbols_list,{$symbol_file}"; } return "-Wl,--dynamic-list={$symbol_file}"; diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index d98059cb0..464c9b2fc 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -145,11 +145,10 @@ public function deployBinary(string $src, string $dst, bool $executable = true): throw new SPCInternalException("Deploy failed. Cannot find file after copy: {$dst}"); } - // extract debug info - $this->extractDebugInfo($dst); - - // strip if (!$this->getOption('no-strip')) { + // extract debug info + $this->extractDebugInfo($dst); + // extra strip $this->stripBinary($dst); } @@ -236,8 +235,10 @@ protected function sanityCheck(int $build_target): void $lens .= ' -static'; } $dynamic_exports = ''; + $embedType = 'static'; // if someone changed to EMBED_TYPE=shared, we need to add LD_LIBRARY_PATH if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { + $embedType = 'shared'; if (PHP_OS_FAMILY === 'Darwin') { $ext_path = 'DYLD_LIBRARY_PATH=' . BUILD_LIB_PATH . ':$DYLD_LIBRARY_PATH '; } else { @@ -256,18 +257,19 @@ protected function sanityCheck(int $build_target): void } } $cc = getenv('CC'); + [$ret, $out] = shell()->cd($sample_file_path)->execWithResult("{$cc} -o embed embed.c {$lens} {$dynamic_exports}"); if ($ret !== 0) { throw new ValidationException( - 'embed failed sanity check: build failed. Error message: ' . implode("\n", $out), - validation_module: 'static libphp.a sanity check' + 'embed failed to build. Error message: ' . implode("\n", $out), + validation_module: $embedType . ' libphp embed build sanity check' ); } [$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed'); if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { throw new ValidationException( - 'embed failed sanity check: run failed. Error message: ' . implode("\n", $output), - validation_module: 'static libphp.a sanity check' + 'embed failed to run. Error message: ' . implode("\n", $output), + validation_module: $embedType . ' libphp embed run sanity check' ); } } diff --git a/src/SPC/builder/unix/library/gmp.php b/src/SPC/builder/unix/library/gmp.php index f09976d8c..97a88ba1c 100644 --- a/src/SPC/builder/unix/library/gmp.php +++ b/src/SPC/builder/unix/library/gmp.php @@ -10,7 +10,14 @@ trait gmp { protected function build(): void { - UnixAutoconfExecutor::create($this)->configure()->make(); + UnixAutoconfExecutor::create($this) + ->appendEnv([ + 'CFLAGS' => '-std=c17', + ]) + ->configure( + '--enable-fat' + ) + ->make(); $this->patchPkgconfPrefix(['gmp.pc']); } } diff --git a/src/SPC/builder/unix/library/libjxl.php b/src/SPC/builder/unix/library/libjxl.php index 13f8481b1..4c922d9df 100644 --- a/src/SPC/builder/unix/library/libjxl.php +++ b/src/SPC/builder/unix/library/libjxl.php @@ -29,13 +29,17 @@ protected function build(): void ); if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { - $cmake->addConfigureArgs( - '-DCXX_MAVX512F_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512DQ_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512CD_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512BW_SUPPORTED:BOOL=FALSE', - '-DCXX_MAVX512VL_SUPPORTED:BOOL=FALSE' - ); + $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: ''; + $has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4'); + if (!$has_avx512) { + $cmake->addConfigureArgs( + '-DCXX_MAVX512F_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512DQ_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512CD_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512BW_SUPPORTED:BOOL=FALSE', + '-DCXX_MAVX512VL_SUPPORTED:BOOL=FALSE' + ); + } } $cmake->build(); diff --git a/src/SPC/builder/unix/library/libwebp.php b/src/SPC/builder/unix/library/libwebp.php index 46a88af49..015fa73ba 100644 --- a/src/SPC/builder/unix/library/libwebp.php +++ b/src/SPC/builder/unix/library/libwebp.php @@ -10,8 +10,26 @@ trait libwebp { protected function build(): void { + $code = '#include +int main() { return _mm256_cvtsi256_si32(_mm256_setzero_si256()); }'; + $cc = getenv('CC') ?: 'gcc'; + [$ret] = shell()->execWithResult("printf '%s' '{$code}' | {$cc} -x c -mavx2 -o /dev/null - 2>&1"); + $disableAvx2 = $ret !== 0 && GNU_ARCH === 'x86_64' && PHP_OS_FAMILY === 'Linux'; + UnixCMakeExecutor::create($this) - ->addConfigureArgs('-DWEBP_BUILD_EXTRAS=ON') + ->addConfigureArgs( + '-DWEBP_BUILD_EXTRAS=OFF', + '-DWEBP_BUILD_ANIM_UTILS=OFF', + '-DWEBP_BUILD_CWEBP=OFF', + '-DWEBP_BUILD_DWEBP=OFF', + '-DWEBP_BUILD_GIF2WEBP=OFF', + '-DWEBP_BUILD_IMG2WEBP=OFF', + '-DWEBP_BUILD_VWEBP=OFF', + '-DWEBP_BUILD_WEBPINFO=OFF', + '-DWEBP_BUILD_WEBPMUX=OFF', + '-DWEBP_BUILD_FUZZTEST=OFF', + $disableAvx2 ? '-DWEBP_ENABLE_SIMD=OFF' : '' + ) ->build(); // patch pkgconfig $this->patchPkgconfPrefix(patch_option: PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR); diff --git a/src/SPC/builder/unix/library/ncurses.php b/src/SPC/builder/unix/library/ncurses.php index 27725c3d5..cb8c10df0 100644 --- a/src/SPC/builder/unix/library/ncurses.php +++ b/src/SPC/builder/unix/library/ncurses.php @@ -16,6 +16,7 @@ protected function build(): void UnixAutoconfExecutor::create($this) ->appendEnv([ + 'CFLAGS' => '-std=c17', 'LDFLAGS' => SPCTarget::isStatic() ? '-static' : '', ]) ->configure( @@ -29,7 +30,7 @@ protected function build(): void '--without-tests', '--without-dlsym', '--without-debug', - '-enable-symlinks', + '--enable-symlinks', "--bindir={$this->getBinDir()}", "--includedir={$this->getIncludeDir()}", "--libdir={$this->getLibDir()}", diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php index 6e0cb606e..a72f3a1a6 100644 --- a/src/SPC/builder/unix/library/postgresql.php +++ b/src/SPC/builder/unix/library/postgresql.php @@ -50,7 +50,7 @@ protected function build(): void $config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs', false)); $env_vars = [ - 'CFLAGS' => $config['cflags'], + 'CFLAGS' => $config['cflags'] . ' -std=c17', 'CPPFLAGS' => '-DPIC', 'LDFLAGS' => $config['ldflags'], 'LIBS' => $config['libs'], diff --git a/src/SPC/builder/windows/library/curl.php b/src/SPC/builder/windows/library/curl.php index 1229dbd69..bba130e1a 100644 --- a/src/SPC/builder/windows/library/curl.php +++ b/src/SPC/builder/windows/library/curl.php @@ -30,7 +30,6 @@ protected function build(): void '-DCMAKE_BUILD_TYPE=Release ' . '-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_STATIC_LIBS=ON ' . - '-DCURL_STATICLIB=ON ' . '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' . '-DBUILD_CURL_EXE=OFF ' . // disable curl.exe '-DBUILD_TESTING=OFF ' . // disable tests @@ -42,9 +41,9 @@ protected function build(): void '-DCURL_USE_OPENSSL=OFF ' . // disable openssl due to certificate issue '-DCURL_ENABLE_SSL=ON ' . '-DUSE_NGHTTP2=ON ' . // enable nghttp2 + '-DSHARE_LIB_OBJECT=OFF ' . // disable shared lib object '-DCURL_USE_LIBSSH2=ON ' . // enable libssh2 '-DENABLE_IPV6=ON ' . // enable ipv6 - '-DNGHTTP2_CFLAGS="/DNGHTTP2_STATICLIB" ' . $alt ) ->execWithWrapper( @@ -53,5 +52,7 @@ protected function build(): void ); // move libcurl.lib to libcurl_a.lib rename(BUILD_LIB_PATH . '\libcurl.lib', BUILD_LIB_PATH . '\libcurl_a.lib'); + + FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\curl\curl.h', '#ifdef CURL_STATICLIB', '#if 1'); } } diff --git a/src/SPC/builder/windows/library/nghttp2.php b/src/SPC/builder/windows/library/nghttp2.php index 7e6e999dc..5a1c6bf15 100644 --- a/src/SPC/builder/windows/library/nghttp2.php +++ b/src/SPC/builder/windows/library/nghttp2.php @@ -29,11 +29,16 @@ protected function build(): void '-DBUILD_SHARED_LIBS=OFF ' . '-DENABLE_STATIC_CRT=ON ' . '-DENABLE_LIB_ONLY=ON ' . - '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' + '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' . + '-DENABLE_STATIC_CRT=ON ' . + '-DENABLE_DOC=OFF ' . + '-DBUILD_TESTING=OFF ' ) ->execWithWrapper( $this->builder->makeSimpleWrapper('cmake'), "--build build --config Release --target install -j{$this->builder->concurrency}" ); + + FileSystem::replaceFileStr(BUILD_INCLUDE_PATH . '\nghttp2\nghttp2.h', '#ifdef NGHTTP2_STATICLIB', '#if 1'); } } diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index 08a2b4dc9..6e4c5034f 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -22,7 +22,6 @@ class LinuxToolCheckList 'bzip2', 'cmake', 'gcc', 'g++', 'patch', 'binutils-gold', 'libtoolize', 'which', - 'patchelf', ]; public const TOOLS_DEBIAN = [ @@ -31,7 +30,6 @@ class LinuxToolCheckList 'tar', 'unzip', 'gzip', 'gcc', 'g++', 'bzip2', 'cmake', 'patch', 'xz', 'libtoolize', 'which', - 'patchelf', ]; public const TOOLS_RHEL = [ @@ -39,8 +37,7 @@ class LinuxToolCheckList 'git', 'autoconf', 'automake', 'tar', 'unzip', 'gzip', 'gcc', 'g++', 'bzip2', 'cmake', 'patch', 'which', - 'xz', 'libtool', 'gettext-devel', - 'patchelf', 'file', + 'xz', 'libtool', 'gettext-devel', 'file', ]; public const TOOLS_ARCH = [ diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index 63bec8075..ccf61dd8d 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -97,8 +97,9 @@ public static function getLatestBitbucketTag(string $name, array $source): array public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array { logger()->debug("finding {$name} source from github {$type} tarball"); + $source['query'] ??= ''; $data = json_decode(self::curlExec( - url: "https://api.github.com/repos/{$source['repo']}/{$type}", + url: "https://api.github.com/repos/{$source['repo']}/{$type}{$source['query']}", hooks: [[CurlHook::class, 'setupGithubToken']], retries: self::getRetryAttempts() ), true, 512, JSON_THROW_ON_ERROR); @@ -108,6 +109,9 @@ public static function getLatestGithubTarball(string $name, array $source, strin if (($rel['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) { continue; } + if (($rel['draft'] ?? false) === true && (($source['prefer-stable'] ?? false) || !$rel['tarball_url'])) { + continue; + } if (!($source['match'] ?? null)) { $url = $rel['tarball_url'] ?? null; break; diff --git a/src/SPC/store/FileSystem.php b/src/SPC/store/FileSystem.php index 3b88a2bce..1d0815ce2 100644 --- a/src/SPC/store/FileSystem.php +++ b/src/SPC/store/FileSystem.php @@ -408,13 +408,13 @@ public static function removeDir(string $dir): bool continue; } $sub_file = self::convertPath($dir . '/' . $v); - if (is_dir($sub_file)) { - # 如果是 目录 且 递推 , 则递推添加下级文件 - if (!self::removeDir($sub_file)) { + if (is_link($sub_file) || is_file($sub_file)) { + if (!unlink($sub_file)) { return false; } - } elseif (is_link($sub_file) || is_file($sub_file)) { - if (!unlink($sub_file)) { + } elseif (is_dir($sub_file)) { + # 如果是 目录 且 递推 , 则递推添加下级文件 + if (!self::removeDir($sub_file)) { return false; } } @@ -572,6 +572,44 @@ public static function replaceFileLineContainsString(string $file, string $find, return file_put_contents($file, implode('', $lines)); } + /** + * Move file or directory, handling cross-device scenarios + * Uses rename() if possible, falls back to copy+delete for cross-device moves + * + * @param string $source Source path + * @param string $dest Destination path + */ + public static function moveFileOrDir(string $source, string $dest): void + { + $source = self::convertPath($source); + $dest = self::convertPath($dest); + + // Check if source and dest are on the same device to avoid cross-device rename errors + $source_stat = @stat($source); + $dest_parent = dirname($dest); + $dest_stat = @stat($dest_parent); + + // Only use rename if on same device + if ($source_stat !== false && $dest_stat !== false && $source_stat['dev'] === $dest_stat['dev']) { + if (@rename($source, $dest)) { + return; + } + } + + // Fall back to copy + delete for cross-device moves or if rename failed + if (is_dir($source)) { + self::copyDir($source, $dest); + self::removeDir($source); + } else { + if (!copy($source, $dest)) { + throw new FileSystemException("Failed to copy file from {$source} to {$dest}"); + } + if (!unlink($source)) { + throw new FileSystemException("Failed to remove source file: {$source}"); + } + } + } + private static function extractArchive(string $filename, string $target): void { // Create base dir @@ -648,44 +686,6 @@ private static function extractWithType(string $source_type, string $filename, s }; } - /** - * Move file or directory, handling cross-device scenarios - * Uses rename() if possible, falls back to copy+delete for cross-device moves - * - * @param string $source Source path - * @param string $dest Destination path - */ - private static function moveFileOrDir(string $source, string $dest): void - { - $source = self::convertPath($source); - $dest = self::convertPath($dest); - - // Check if source and dest are on the same device to avoid cross-device rename errors - $source_stat = @stat($source); - $dest_parent = dirname($dest); - $dest_stat = @stat($dest_parent); - - // Only use rename if on same device - if ($source_stat !== false && $dest_stat !== false && $source_stat['dev'] === $dest_stat['dev']) { - if (@rename($source, $dest)) { - return; - } - } - - // Fall back to copy + delete for cross-device moves or if rename failed - if (is_dir($source)) { - self::copyDir($source, $dest); - self::removeDir($source); - } else { - if (!copy($source, $dest)) { - throw new FileSystemException("Failed to copy file from {$source} to {$dest}"); - } - if (!unlink($source)) { - throw new FileSystemException("Failed to remove source file: {$source}"); - } - } - } - /** * Unzip file with stripping top-level directory */ diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index f628544fe..0068f53e3 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -25,6 +25,7 @@ public static function init(): void FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']); FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']); FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']); + FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVPhp85']); FileSystem::addSourceExtractHook('yaml', [__CLASS__, 'patchYamlWin32']); FileSystem::addSourceExtractHook('libyaml', [__CLASS__, 'patchLibYaml']); FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']); @@ -432,6 +433,23 @@ public static function patchSQLSRVWin32(string $source_name): bool return false; } + /** + * Fix the compilation issue of pdo_sqlsrv with php 8.5 + */ + public static function patchSQLSRVPhp85(): bool + { + $source_dir = SOURCE_PATH . '/php-src/ext/pdo_sqlsrv'; + if (!file_exists($source_dir . '/config.m4') && is_dir($source_dir . '/source/pdo_sqlsrv')) { + FileSystem::moveFileOrDir($source_dir . '/LICENSE', $source_dir . '/source/pdo_sqlsrv/LICENSE'); + FileSystem::moveFileOrDir($source_dir . '/source/shared', $source_dir . '/source/pdo_sqlsrv/shared'); + FileSystem::moveFileOrDir($source_dir . '/source/pdo_sqlsrv', SOURCE_PATH . '/pdo_sqlsrv'); + FileSystem::removeDir($source_dir); + FileSystem::moveFileOrDir(SOURCE_PATH . '/pdo_sqlsrv', $source_dir); + return true; + } + return false; + } + public static function patchYamlWin32(): bool { FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/yaml/config.w32', "lib.substr(lib.length - 6, 6) == '_a.lib'", "lib.substr(lib.length - 6, 6) == '_a.lib' || 'yes' == 'yes'"); diff --git a/src/SPC/store/pkg/GoXcaddy.php b/src/SPC/store/pkg/GoXcaddy.php index 0c1c6f8c6..93821aaa9 100644 --- a/src/SPC/store/pkg/GoXcaddy.php +++ b/src/SPC/store/pkg/GoXcaddy.php @@ -48,10 +48,10 @@ public function fetch(string $name, bool $force = false, ?array $config = null): 'macos' => 'darwin', default => throw new \InvalidArgumentException('Unsupported OS: ' . $name), }; - $go_version = '1.25.0'; + [$go_version] = explode("\n", Downloader::curlExec('https://go.dev/VERSION?m=text')); $config = [ 'type' => 'url', - 'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz", + 'url' => "https://go.dev/dl/{$go_version}.{$os}-{$arch}.tar.gz", ]; Downloader::downloadPackage($name, $config, $force); } diff --git a/src/SPC/store/pkg/Zig.php b/src/SPC/store/pkg/Zig.php index e9865dbe3..c2a81c0da 100644 --- a/src/SPC/store/pkg/Zig.php +++ b/src/SPC/store/pkg/Zig.php @@ -72,8 +72,11 @@ public function fetch(string $name, bool $force = false, ?array $config = null): $latest_version = null; foreach ($index_json as $version => $data) { - $latest_version = $version; - break; + // Skip the master branch, get the latest stable release + if ($version !== 'master') { + $latest_version = $version; + break; + } } if (!$latest_version) { diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php index bb423db20..1b7cc70dc 100644 --- a/src/SPC/toolchain/ZigToolchain.php +++ b/src/SPC/toolchain/ZigToolchain.php @@ -67,7 +67,8 @@ public function afterInit(): void $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: getenv('CFLAGS') ?: ''; $has_avx512 = str_contains($cflags, '-mavx512') || str_contains($cflags, '-march=x86-64-v4'); if (!$has_avx512) { - GlobalEnvManager::putenv('SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no'); + $extra_vars = getenv('SPC_EXTRA_PHP_VARS') ?: ''; + GlobalEnvManager::putenv("SPC_EXTRA_PHP_VARS=php_cv_have_avx512=no php_cv_have_avx512vbmi=no {$extra_vars}"); } } diff --git a/src/SPC/util/SPCConfigUtil.php b/src/SPC/util/SPCConfigUtil.php index a74d6a24c..8c4fa9267 100644 --- a/src/SPC/util/SPCConfigUtil.php +++ b/src/SPC/util/SPCConfigUtil.php @@ -80,7 +80,6 @@ public function config(array $extensions = [], array $libraries = [], bool $incl $libs = $this->getLibsString($libraries, !$this->absolute_libs); // additional OS-specific libraries (e.g. macOS -lresolv) - // embed if ($extra_libs = SPCTarget::getRuntimeLibs()) { $libs .= " {$extra_libs}"; } @@ -226,9 +225,17 @@ private function getIncludesString(array $libraries): string // parse pkg-configs foreach ($libraries as $library) { $pc = Config::getLib($library, 'pkg-configs', []); + $pkg_config_path = getenv('PKG_CONFIG_PATH') ?: ''; + $search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)); foreach ($pc as $file) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$file}.pc")) { - throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); + $found = false; + foreach ($search_paths as $path) { + if (file_exists($path . "/{$file}.pc")) { + $found = true; + } + } + if (!$found) { + throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$library}] does not exist. Please build it first."); } } $pc_cflags = implode(' ', $pc); @@ -257,9 +264,17 @@ private function getLibsString(array $libraries, bool $use_short_libs = true): s foreach ($libraries as $library) { // add pkg-configs libs $pkg_configs = Config::getLib($library, 'pkg-configs', []); - foreach ($pkg_configs as $pkg_config) { - if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$pkg_config}.pc")) { - throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); + $pkg_config_path = getenv('PKG_CONFIG_PATH') ?: ''; + $search_paths = array_filter(explode(is_unix() ? ':' : ';', $pkg_config_path)); + foreach ($pkg_configs as $file) { + $found = false; + foreach ($search_paths as $path) { + if (file_exists($path . "/{$file}.pc")) { + $found = true; + } + } + if (!$found) { + throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$library}] does not exist. Please build it first."); } } $pkg_configs = implode(' ', $pkg_configs); diff --git a/src/StaticPHP/Artifact/ArtifactDownloader.php b/src/StaticPHP/Artifact/ArtifactDownloader.php index 315cfb11d..b53ddd8a2 100644 --- a/src/StaticPHP/Artifact/ArtifactDownloader.php +++ b/src/StaticPHP/Artifact/ArtifactDownloader.php @@ -329,8 +329,7 @@ public function download(bool $interactive = true): void } if ($interactive) { $skip_msg = !empty($skipped) ? ' (Skipped ' . count($skipped) . ' artifacts for being already downloaded)' : ''; - InteractiveTerm::success("Downloaded all {$count} artifacts.{$skip_msg}", true); - echo PHP_EOL; + InteractiveTerm::success("Downloaded all {$count} artifacts.{$skip_msg}\n", true); } } } catch (SPCException $e) { diff --git a/src/StaticPHP/Command/BaseCommand.php b/src/StaticPHP/Command/BaseCommand.php index e416be26d..02f84ffb5 100644 --- a/src/StaticPHP/Command/BaseCommand.php +++ b/src/StaticPHP/Command/BaseCommand.php @@ -23,7 +23,6 @@ abstract class BaseCommand extends Command \___ \| __/ _` | __| |/ __| |_) | |_| | |_) | ___) | || (_| | |_| | (__| __/| _ | __/ |____/ \__\__,_|\__|_|\___|_| |_| |_|_| {version} - '; protected bool $no_motd = false; @@ -71,7 +70,7 @@ public function initialize(InputInterface $input, OutputInterface $output): void $version = $this->getVersionWithCommit(); if (!$this->no_motd) { $str = str_replace('{version}', '' . ConsoleColor::none("v{$version}"), '' . ConsoleColor::magenta(self::$motd)); - echo $this->input->getOption('no-ansi') ? strip_ansi_colors($str) : $str; + $this->output->writeln($this->input->getOption('no-ansi') ? strip_ansi_colors($str) : $str); } } diff --git a/src/StaticPHP/Command/Dev/SkeletonCommand.php b/src/StaticPHP/Command/Dev/SkeletonCommand.php new file mode 100644 index 000000000..b19eb4a11 --- /dev/null +++ b/src/StaticPHP/Command/Dev/SkeletonCommand.php @@ -0,0 +1,402 @@ +output->writeln('The dev:skel command is not available in phar mode.'); + return 1; + } + if (SystemTarget::getTargetOS() === 'Windows') { + $this->output->writeln('The dev:skel command is not available on Windows systems.'); + return 1; + } + + $this->runMainMenu(); + + return 0; + } + + public function validatePackageName(string $name): ?string + { + if (!preg_match('/^[a-zA-Z0-9_-]+$/', $name)) { + return 'Library name can only contain letters, numbers, underscores, and hyphens.'; + } + // must start with a letter + if (!preg_match('/^[a-zA-Z]/', $name)) { + return 'Library name must start with a letter.'; + } + return null; + } + + private function runMainMenu(): void + { + $main = select('Please select the skeleton option', [ + 'library' => 'Create a new library package', + 'target' => 'Create a new target package', + 'php-extension' => 'Create a new PHP extension', + 'q' => 'Exit', + ]); + $generator = match ($main) { + 'library' => $this->runCreateLib(), + 'target' => $this->runCreateTarget(), + 'php-extension' => $this->runCreateExt(), + 'q' => exit(0), + default => null, + }; + $write = $generator->writeAll(); + $this->output->writeln("Package config in: {$write['package_config']}"); + $this->output->writeln("Artifact config in: {$write['artifact_config']}"); + $this->output->writeln('Package class:'); + $this->output->writeln($write['package_class_content']); + } + + private function runCreateLib(): PackageGenerator + { + // init empty + $static_libs = ''; + $headers = ''; + $static_bins = ''; + $pkg_configs = ''; + + // ask name + $package_name = text('Please enter your library name', placeholder: 'e.g. pcre2', validate: [$this, 'validatePackageName']); + + // ask OS + $os = select("[{$package_name}] On which OS family do you want to build this library?", [ + 'unix' => 'Both Linux and Darwin (unix-like OS)', + 'linux' => 'Linux only', + 'macos' => 'Darwin(macOS) only', + 'windows' => 'Windows only', + 'all' => 'All supported OS (' . implode(', ', SUPPORTED_OS_FAMILY) . ')', + ]); + + $produce = select("[{$package_name}] What does this library produce?", [ + 'static_libs' => 'Static Libraries (.a/.lib)', + 'headers' => 'Header Files (.h)', + 'static_bins' => 'Static Binaries (executables)', + 'pkg_configs' => 'Pkg-Config files (.pc)', + 'all' => 'All of the above', + ]); + + if ($produce === 'all' || $produce === 'static_libs') { + $static_libs = text( + 'Please enter the names of the static libraries produced', + placeholder: 'e.g. libpcre2.a, libbar.a', + default: str_starts_with($package_name, 'lib') ? "{$package_name}.a" : "lib{$package_name}.a", + validate: function ($value) { + $names = array_map('trim', explode(',', $value)); + if (array_any($names, fn ($name) => !preg_match('/^[a-zA-Z0-9_.-]+$/', $name))) { + return 'Library names can only contain letters, numbers, underscores, hyphens, and dots.'; + } + return null; + }, + hint: 'Separate multiple names with commas' + ); + } + if ($produce === 'all' || $produce === 'headers') { + $headers = text( + 'Please enter the names of the header files produced', + placeholder: 'e.g. foo.h, bar.h', + default: str_starts_with($package_name, 'lib') ? str_replace('lib', '', $package_name) . '.h' : $package_name . '.h', + validate: function ($value) { + $names = array_map('trim', explode(',', $value)); + if (array_any($names, fn ($name) => !preg_match('/^[a-zA-Z0-9_.-]+$/', $name))) { + return 'Header file names can only contain letters, numbers, underscores, hyphens, and dots.'; + } + return null; + }, + hint: 'Separate multiple names with commas, directories are allowed (e.g. openssl directory)' + ); + } + if ($produce === 'all' || $produce === 'static_bins') { + $static_bins = text( + 'Please enter the names of the static binaries produced', + placeholder: 'e.g. foo, bar', + default: $package_name, + validate: function ($value) { + $names = array_map('trim', explode(',', $value)); + if (array_any($names, fn ($name) => !preg_match('/^[a-zA-Z0-9_.-]+$/', $name))) { + return 'Binary names can only contain letters, numbers, underscores, hyphens, and dots.'; + } + return null; + }, + hint: 'Separate multiple names with commas' + ); + } + if ($produce === 'all' || $produce === 'pkg_configs') { + $pkg_configs = text( + 'Please enter the names of the pkg-config files produced', + placeholder: 'e.g. foo.pc, bar.pc', + default: str_starts_with($package_name, 'lib') ? str_replace('lib', '', $package_name) . '.pc' : $package_name . '.pc', + validate: function ($value) { + if (!str_ends_with($value, '.pc')) { + return 'Pkg-config file names must end with .pc extension.'; + } + return null; + }, + hint: 'Separate multiple names with commas' + ); + } + + if ($headers === '' && $static_bins === '' && $static_libs === '' && $pkg_configs === '') { + $this->output->writeln('You must specify at least one of static libraries, header files, or static binaries produced.'); + exit(1); + } + + // ask source + $artifact_generator = $this->runCreateArtifact($package_name, true, false, null); + $package_generator = new PackageGenerator($package_name, 'library'); + // set artifact + $package_generator = $package_generator->addArtifact($artifact_generator); + // set os + $package_generator = match ($os) { + 'unix' => $package_generator->enableBuild(['Darwin', 'Linux'], 'build'), + 'linux' => $package_generator->enableBuild(['Linux'], 'build'), + 'macos' => $package_generator->enableBuild(['Darwin'], 'build'), + 'windows' => $package_generator->enableBuild(['Windows'], 'build'), + 'all' => $package_generator->enableBuild(SUPPORTED_OS_FAMILY, 'build'), + default => $package_generator, + }; + // set produce + if ($static_libs !== '') { + $lib_names = array_map('trim', explode(',', $static_libs)); + foreach ($lib_names as $lib_name) { + $package_generator = $package_generator->addStaticLib($lib_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os)); + } + } + if ($headers !== '') { + $header_names = array_map('trim', explode(',', $headers)); + foreach ($header_names as $header_name) { + $package_generator = $package_generator->addHeaderFile($header_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os)); + } + } + if ($static_bins !== '') { + $bin_names = array_map('trim', explode(',', $static_bins)); + foreach ($bin_names as $bin_name) { + $package_generator = $package_generator->addStaticBin($bin_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os)); + } + } + if ($pkg_configs !== '') { + $pc_names = array_map('trim', explode(',', $pkg_configs)); + foreach ($pc_names as $pc_name) { + $package_generator = $package_generator->addPkgConfigFile($pc_name, $os === 'all' ? 'all' : ($os === 'unix' ? 'unix' : $os)); + } + } + // ask for package config writing selection, same as artifact + $package_configs = Registry::getLoadedPackageConfigs(); + $package_config_file = select("[{$package_name}] Please select the package config file to write the package config to", $package_configs); + return $package_generator->setConfigFile($package_config_file); + } + + private function runCreateArtifact( + string $package_name, + ?bool $create_source, + ?bool $create_binary, + string|true|null $default_extract_dir = true + ): ArtifactGenerator { + $artifact = new ArtifactGenerator($package_name); + + if ($create_source === null) { + $create_source = confirm("[{$package_name}] Do you want to create a source artifact?"); + } + + if (!$create_source) { + goto binary; + } + + $source_type = select("[{$package_name}] Where is the source code located?", SPC_DOWNLOAD_TYPE_DISPLAY_NAME); + + $source_config = $this->askDownloadTypeConfig($package_name, $source_type, $default_extract_dir, 'source'); + $artifact = $artifact->setSource($source_config); + + binary: + if ($create_binary === null) { + $create_binary = confirm("[{$package_name}] Do you want to create a binary artifact?"); + } + + if (!$create_binary) { + goto end; + } + + $binary_fix = [ + 'macos-x86_64' => null, + 'macos-aarch64' => null, + 'linux-x86_64' => null, + 'linux-aarch64' => null, + 'windows-x86_64' => null, + ]; + while (($os = select("[{$package_name}] Please configure the binary downloading options for OS", [ + 'macos-x86_64' => 'macos-x86_64' . ($binary_fix['macos-x86_64'] ? ' (done)' : ''), + 'macos-aarch64' => 'macos-aarch64' . ($binary_fix['macos-aarch64'] ? ' (done)' : ''), + 'linux-x86_64' => 'linux-x86_64' . ($binary_fix['linux-x86_64'] ? ' (done)' : ''), + 'linux-aarch64' => 'linux-aarch64' . ($binary_fix['linux-aarch64'] ? ' (done)' : ''), + 'windows-x86_64' => 'windows-x86_64' . ($binary_fix['windows-x86_64'] ? ' (done)' : ''), + 'copy' => 'Duplicate from another OS', + 'finish' => 'Submit', + ])) !== 'finish') { + $source_type = select("[{$package_name}] Where is the binary for {$os} located?", SPC_DOWNLOAD_TYPE_DISPLAY_NAME); + $source_config = $this->askDownloadTypeConfig($package_name, $source_type, $default_extract_dir, 'binary'); + // set to artifact + $artifact = $artifact->setBinary($os, $source_config); + $binary_fix[$os] = true; + } + + end: + + // generate config files, select existing package config file to write + $artifact_configs = Registry::getLoadedArtifactConfigs(); + $artifact_config_file = select("[{$package_name}] Please select the artifact config file to write the artifact config to", $artifact_configs); + return $artifact->setConfigFile($artifact_config_file); + } + + private function runCreateTarget(): PackageGenerator + { + throw new WrongUsageException('Not implemented'); + } + + private function runCreateExt(): PackageGenerator + { + throw new WrongUsageException('Not implemented'); + } + + private function askDownloadTypeConfig(string $package_name, int|string $source_type, bool|string|null $default_extract_dir, string $config_type): array + { + $source_config = ['type' => $source_type]; + switch ($source_type) { + case 'bitbuckettag': + $source_config['repo'] = text("[{$package_name}] Please enter the BitBucket repository (e.g. user/repo)"); + break; + case 'filelist': + $source_config['url'] = text( + "[{$package_name}] Please enter the file index website URL", + placeholder: 'e.g. https://ftp.gnu.org/pub/gnu/gettext/', + hint: 'Make sure the target url is a directory listing page like ftp.gnu.org.' + ); + $source_config['regex'] = text( + "[{$package_name}] Please enter the regex pattern to match the archive file", + placeholder: 'e.g. /gettext-(\d+\.\d+(\.\d+)?)\.tar\.gz/', + default: "/href=\"(?{$package_name}-(?[^\"]+)\\.tar\\.gz)\"/", + hint: 'Make sure the regex contains a capturing group for the version number.' + ); + break; + case 'git': + $source_config['url'] = text( + "[{$package_name}] Please enter the Git repository URL", + validate: function ($value) { + if (!filter_var($value, FILTER_VALIDATE_URL) && !preg_match('/^(git|ssh|http|https|git@[-\w.]+):(\/\/)?(.*?)(\.git)(\/?|#[-\d\w._]+?)$/', $value)) { + return 'Please enter a valid Git repository URL.'; + } + return null; + }, + hint: 'e.g. https://github.com/user/repo.git' + ); + $source_config['rev'] = text( + "[{$package_name}] Please enter the Git revision (branch, tag, or commit hash)", + default: 'main', + hint: 'e.g. main, master, v1.0.0, or a commit hash' + ); + break; + case 'ghrel': + $source_config['repo'] = text("[{$package_name}] Please enter the GitHub repository (e.g. user/repo)"); + $source_config['match'] = text( + "[{$package_name}] Please enter the regex pattern to match the source archive file", + placeholder: 'e.g. /foo-(\d+\.\d+(\.\d+)?)\.tar\.gz/', + default: "{$package_name}-.+\\.tar\\.gz", + ); + break; + case 'ghtar': + case 'ghtagtar': + $source_config['repo'] = text("[{$package_name}] Please enter the GitHub repository (e.g. user/repo)"); + $source_config['prefer-stable'] = confirm("[{$package_name}] Do you want to prefer stable releases?"); + if ($source_type === 'ghtagtar' && confirm('Do you want to match tags with a specific pattern?', default: false)) { + $source_config['match'] = text( + "[{$package_name}] Please enter the regex pattern to match tags", + placeholder: 'e.g. v(\d+\.\d+(\.\d+)?)', + ); + } + break; + case 'local': + $source_config['dirname'] = text( + "[{$package_name}] Please enter the local directory path", + validate: function ($value) { + if (trim($value) === '') { + return 'Local source directory cannot be empty.'; + } + if (!is_dir($value)) { + return 'The specified local source directory does not exist.'; + } + return null; + }, + ); + break; + case 'pie': + $source_config['repo'] = text( + "[{$package_name}] Please enter the PIE repository name", + placeholder: 'e.g. user/repo', + ); + break; + case 'url': + $source_config['url'] = text( + "[{$package_name}] Please enter the file download URL", + validate: function ($value) { + if (!filter_var($value, FILTER_VALIDATE_URL)) { + return 'Please enter a valid URL.'; + } + return null; + }, + ); + break; + case 'custom': + break; + } + // ask extract dir if is true + if ($default_extract_dir === true) { + if (confirm('Do you want to specify a custom extract directory?')) { + $extract_hint = match ($config_type) { + 'source' => 'the source will be from the `source/` dir by default', + 'binary' => 'the binary will be from the `pkgroot/{arch}-{os}/` dir by default', + default => '', + }; + $default_extract_dir = text( + "[{$package_name}] Please enter the source extract directory", + validate: function ($value) { + if (trim($value) === '') { + return 'Extract directory cannot be empty.'; + } + return null; + }, + hint: 'You can use relative path, ' . $extract_hint . '.' + ); + } else { + $default_extract_dir = null; + } + } + if ($default_extract_dir !== null) { + $source_config['extract'] = $default_extract_dir; + } + + // return config + return $source_config; + } +} diff --git a/src/StaticPHP/Command/Dev/SortConfigCommand.php b/src/StaticPHP/Command/Dev/SortConfigCommand.php new file mode 100644 index 000000000..aa3a9ecdb --- /dev/null +++ b/src/StaticPHP/Command/Dev/SortConfigCommand.php @@ -0,0 +1,49 @@ +sortConfigFile($file); + } + $loaded_pkg_configs = Registry::getLoadedPackageConfigs(); + foreach ($loaded_pkg_configs as $file) { + $this->sortConfigFile($file); + } + return static::SUCCESS; + } + + private function sortConfigFile(mixed $file): void + { + $content = file_get_contents($file); + if ($content === false) { + $this->output->writeln("Failed to read artifact config file: {$file}"); + return; + } + $data = json_decode($content, true); + if (!is_array($data)) { + $this->output->writeln("Invalid JSON format in artifact config file: {$file}"); + return; + } + ksort($data); + foreach ($data as $artifact_name => &$config) { + ksort($config); + } + unset($config); + $new_content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; + file_put_contents($file, $new_content); + $this->output->writeln("Sorted artifact config file: {$file}"); + } +} diff --git a/src/StaticPHP/Config/ArtifactConfig.php b/src/StaticPHP/Config/ArtifactConfig.php index d25c6dd1a..49abae926 100644 --- a/src/StaticPHP/Config/ArtifactConfig.php +++ b/src/StaticPHP/Config/ArtifactConfig.php @@ -5,31 +5,36 @@ namespace StaticPHP\Config; use StaticPHP\Exception\WrongUsageException; +use StaticPHP\Registry\Registry; class ArtifactConfig { private static array $artifact_configs = []; - public static function loadFromDir(string $dir): void + public static function loadFromDir(string $dir, string $registry_name): array { if (!is_dir($dir)) { throw new WrongUsageException("Directory {$dir} does not exist, cannot load artifact config."); } + $loaded = []; $files = glob("{$dir}/artifact.*.json"); if (is_array($files)) { foreach ($files as $file) { - self::loadFromFile($file); + self::loadFromFile($file, $registry_name); + $loaded[] = $file; } } if (file_exists("{$dir}/artifact.json")) { - self::loadFromFile("{$dir}/artifact.json"); + self::loadFromFile("{$dir}/artifact.json", $registry_name); + $loaded[] = "{$dir}/artifact.json"; } + return $loaded; } /** * Load artifact configurations from a specified JSON file. */ - public static function loadFromFile(string $file): void + public static function loadFromFile(string $file, string $registry_name): string { $content = file_get_contents($file); if ($content === false) { @@ -42,7 +47,9 @@ public static function loadFromFile(string $file): void ConfigValidator::validateAndLintArtifacts(basename($file), $data); foreach ($data as $artifact_name => $config) { self::$artifact_configs[$artifact_name] = $config; + Registry::_bindArtifactConfigFile($artifact_name, $registry_name, $file); } + return $file; } /** diff --git a/src/StaticPHP/Config/PackageConfig.php b/src/StaticPHP/Config/PackageConfig.php index dc0b3d546..56ef7ab1c 100644 --- a/src/StaticPHP/Config/PackageConfig.php +++ b/src/StaticPHP/Config/PackageConfig.php @@ -5,6 +5,7 @@ namespace StaticPHP\Config; use StaticPHP\Exception\WrongUsageException; +use StaticPHP\Registry\Registry; use StaticPHP\Runtime\SystemTarget; class PackageConfig @@ -15,20 +16,24 @@ class PackageConfig * Load package configurations from a specified directory. * It will look for files matching the pattern 'pkg.*.json' and 'pkg.json'. */ - public static function loadFromDir(string $dir): void + public static function loadFromDir(string $dir, string $registry_name): array { if (!is_dir($dir)) { throw new WrongUsageException("Directory {$dir} does not exist, cannot load pkg.json config."); } + $loaded = []; $files = glob("{$dir}/pkg.*.json"); if (is_array($files)) { foreach ($files as $file) { - self::loadFromFile($file); + self::loadFromFile($file, $registry_name); + $loaded[] = $file; } } if (file_exists("{$dir}/pkg.json")) { - self::loadFromFile("{$dir}/pkg.json"); + self::loadFromFile("{$dir}/pkg.json", $registry_name); + $loaded[] = "{$dir}/pkg.json"; } + return $loaded; } /** @@ -36,7 +41,7 @@ public static function loadFromDir(string $dir): void * * @param string $file the path to the json package configuration file */ - public static function loadFromFile(string $file): void + public static function loadFromFile(string $file, string $registry_name): string { $content = file_get_contents($file); if ($content === false) { @@ -49,7 +54,9 @@ public static function loadFromFile(string $file): void ConfigValidator::validateAndLintPackages(basename($file), $data); foreach ($data as $pkg_name => $config) { self::$package_configs[$pkg_name] = $config; + Registry::_bindPackageConfigFile($pkg_name, $registry_name, $file); } + return $file; } /** diff --git a/src/StaticPHP/ConsoleApplication.php b/src/StaticPHP/ConsoleApplication.php index 0e5371ac4..fd7650aa8 100644 --- a/src/StaticPHP/ConsoleApplication.php +++ b/src/StaticPHP/ConsoleApplication.php @@ -9,6 +9,8 @@ use StaticPHP\Command\Dev\EnvCommand; use StaticPHP\Command\Dev\IsInstalledCommand; use StaticPHP\Command\Dev\ShellCommand; +use StaticPHP\Command\Dev\SkeletonCommand; +use StaticPHP\Command\Dev\SortConfigCommand; use StaticPHP\Command\DoctorCommand; use StaticPHP\Command\DownloadCommand; use StaticPHP\Command\ExtractCommand; @@ -27,12 +29,12 @@ class ConsoleApplication extends Application public function __construct() { - parent::__construct('static-php-cli', self::VERSION); + parent::__construct('StaticPHP', self::VERSION); require_once ROOT_DIR . '/src/bootstrap.php'; - // check registry - Registry::checkLoadedRegistries(); + // resolve registry + Registry::resolve(); /** * @var string $name @@ -59,6 +61,8 @@ public function __construct() new ShellCommand(), new IsInstalledCommand(), new EnvCommand(), + new SkeletonCommand(), + new SortConfigCommand(), ]); // add additional commands from registries diff --git a/src/StaticPHP/Package/PackageInstaller.php b/src/StaticPHP/Package/PackageInstaller.php index 4c44ce920..ae3b7346a 100644 --- a/src/StaticPHP/Package/PackageInstaller.php +++ b/src/StaticPHP/Package/PackageInstaller.php @@ -133,9 +133,8 @@ public function run(bool $interactive = true, bool $disable_delay_msg = false): // show install or build options in terminal with beautiful output $this->printInstallerInfo(); - InteractiveTerm::notice('Build process will start after 2s ...'); + InteractiveTerm::notice('Build process will start after 2s ...' . PHP_EOL); sleep(2); - echo PHP_EOL; } // Early validation: check if packages can be built or installed before downloading diff --git a/src/StaticPHP/Registry/Registry.php b/src/StaticPHP/Registry/Registry.php index 4ae5df4f4..e464ed471 100644 --- a/src/StaticPHP/Registry/Registry.php +++ b/src/StaticPHP/Registry/Registry.php @@ -13,9 +13,39 @@ class Registry { - /** @var string[] List of loaded registry names */ + /** @var string[] List of loaded registries */ private static array $loaded_registries = []; + /** @var array Loaded registry configs */ + private static array $registry_configs = []; + + private static array $loaded_package_configs = []; + + private static array $loaded_artifact_configs = []; + + /** @var array Maps of package and artifact names to their registry config file paths (for reverse lookup) */ + private static array $package_reversed_registry_files = []; + + private static array $artifact_reversed_registry_files = []; + + /** + * Get the current registry configuration. + * "Current" depends on SPC load mode + */ + public static function getRegistryConfig(?string $registry_name = null): array + { + if ($registry_name === null && spc_mode(SPC_MODE_SOURCE)) { + return self::$registry_configs['internal']; + } + if ($registry_name !== null && isset(self::$registry_configs[$registry_name])) { + return self::$registry_configs[$registry_name]; + } + if ($registry_name === null) { + throw new RegistryException('No registry name specified.'); + } + throw new RegistryException("Registry '{$registry_name}' is not loaded."); + } + /** * Load a registry from file path. * This method handles external registries that may not be in composer autoload. @@ -48,12 +78,14 @@ public static function loadRegistry(string $registry_file, bool $auto_require = return; } self::$loaded_registries[] = $registry_name; + self::$registry_configs[$registry_name] = $data; + self::$registry_configs[$registry_name]['_file'] = $registry_file; logger()->debug("Loading registry '{$registry_name}' from file: {$registry_file}"); // Load composer autoload if specified (for external registries with their own dependencies) if (isset($data['autoload']) && is_string($data['autoload'])) { - $autoload_path = self::fullpath($data['autoload'], dirname($registry_file)); + $autoload_path = FileSystem::fullpath($data['autoload'], dirname($registry_file)); if (file_exists($autoload_path)) { logger()->debug("Loading external autoload from: {$autoload_path}"); require_once $autoload_path; @@ -65,7 +97,7 @@ public static function loadRegistry(string $registry_file, bool $auto_require = // load doctor items from PSR-4 directories if (isset($data['doctor']['psr-4']) && is_assoc_array($data['doctor']['psr-4'])) { foreach ($data['doctor']['psr-4'] as $namespace => $path) { - $path = self::fullpath($path, dirname($registry_file)); + $path = FileSystem::fullpath($path, dirname($registry_file)); DoctorLoader::loadFromPsr4Dir($path, $namespace, $auto_require); } } @@ -83,11 +115,11 @@ public static function loadRegistry(string $registry_file, bool $auto_require = // load package configs if (isset($data['package']['config']) && is_array($data['package']['config'])) { foreach ($data['package']['config'] as $path) { - $path = self::fullpath($path, dirname($registry_file)); + $path = FileSystem::fullpath($path, dirname($registry_file)); if (is_file($path)) { - PackageConfig::loadFromFile($path); + self::$loaded_package_configs[] = PackageConfig::loadFromFile($path, $registry_name); } elseif (is_dir($path)) { - PackageConfig::loadFromDir($path); + self::$loaded_package_configs = array_merge(self::$loaded_package_configs, PackageConfig::loadFromDir($path, $registry_name)); } } } @@ -95,11 +127,11 @@ public static function loadRegistry(string $registry_file, bool $auto_require = // load artifact configs if (isset($data['artifact']['config']) && is_array($data['artifact']['config'])) { foreach ($data['artifact']['config'] as $path) { - $path = self::fullpath($path, dirname($registry_file)); + $path = FileSystem::fullpath($path, dirname($registry_file)); if (is_file($path)) { - ArtifactConfig::loadFromFile($path); + self::$loaded_artifact_configs[] = ArtifactConfig::loadFromFile($path, $registry_name); } elseif (is_dir($path)) { - ArtifactConfig::loadFromDir($path); + self::$loaded_package_configs = array_merge(self::$loaded_package_configs, ArtifactConfig::loadFromDir($path, $registry_name)); } } } @@ -107,7 +139,7 @@ public static function loadRegistry(string $registry_file, bool $auto_require = // load packages from PSR-4 directories if (isset($data['package']['psr-4']) && is_assoc_array($data['package']['psr-4'])) { foreach ($data['package']['psr-4'] as $namespace => $path) { - $path = self::fullpath($path, dirname($registry_file)); + $path = FileSystem::fullpath($path, dirname($registry_file)); PackageLoader::loadFromPsr4Dir($path, $namespace, $auto_require); } } @@ -125,7 +157,7 @@ public static function loadRegistry(string $registry_file, bool $auto_require = // load artifacts from PSR-4 directories if (isset($data['artifact']['psr-4']) && is_assoc_array($data['artifact']['psr-4'])) { foreach ($data['artifact']['psr-4'] as $namespace => $path) { - $path = self::fullpath($path, dirname($registry_file)); + $path = FileSystem::fullpath($path, dirname($registry_file)); ArtifactLoader::loadFromPsr4Dir($path, $namespace, $auto_require); } } @@ -143,7 +175,7 @@ public static function loadRegistry(string $registry_file, bool $auto_require = // load additional commands from PSR-4 directories if (isset($data['command']['psr-4']) && is_assoc_array($data['command']['psr-4'])) { foreach ($data['command']['psr-4'] as $namespace => $path) { - $path = self::fullpath($path, dirname($registry_file)); + $path = FileSystem::fullpath($path, dirname($registry_file)); $classes = FileSystem::getClassesPsr4($path, $namespace, auto_require: $auto_require); $instances = array_map(fn ($x) => new $x(), $classes); ConsoleApplication::_addAdditionalCommands($instances); @@ -187,7 +219,12 @@ public static function loadFromEnvOrOption(?string $registries = null): void } } - public static function checkLoadedRegistries(): void + /** + * Resolve loaded registries. + * This method finalizes the loading process by registering default stages + * and validating stage events. + */ + public static function resolve(): void { // Register default stages for all PhpExtensionPackage instances // This must be done after all registries are loaded to ensure custom stages take precedence @@ -217,6 +254,52 @@ public static function reset(): void self::$loaded_registries = []; } + /** + * Bind a package name to its registry config file for reverse lookup. + * + * @internal + */ + public static function _bindPackageConfigFile(string $package_name, string $registry_name, string $config_file): void + { + self::$package_reversed_registry_files[$package_name] = [ + 'registry' => $registry_name, + 'config' => $config_file, + ]; + } + + /** + * Bind an artifact name to its registry config file for reverse lookup. + * + * @internal + */ + public static function _bindArtifactConfigFile(string $artifact_name, string $registry_name, string $config_file): void + { + self::$artifact_reversed_registry_files[$artifact_name] = [ + 'registry' => $registry_name, + 'config' => $config_file, + ]; + } + + public static function getPackageConfigInfo(string $package_name): ?array + { + return self::$package_reversed_registry_files[$package_name] ?? null; + } + + public static function getArtifactConfigInfo(string $artifact_name): ?array + { + return self::$artifact_reversed_registry_files[$artifact_name] ?? null; + } + + public static function getLoadedPackageConfigs(): array + { + return self::$loaded_package_configs; + } + + public static function getLoadedArtifactConfigs(): array + { + return self::$loaded_artifact_configs; + } + /** * Parse a class entry from the classes array. * Supports two formats: @@ -253,7 +336,7 @@ private static function requireClassFile(string $class, ?string $file_path, stri // If file path is provided, require it if ($file_path !== null) { - $full_path = self::fullpath($file_path, $base_path); + $full_path = FileSystem::fullpath($file_path, $base_path); require_once $full_path; return; } @@ -266,21 +349,4 @@ private static function requireClassFile(string $class, ?string $file_path, stri " 3. Provide file path in classes map: \"{$class}\": \"path/to/file.php\"" ); } - - /** - * Return full path, resolving relative paths against a base path. - * - * @param string $path Input path (relative or absolute) - * @param string $relative_path_base Base path for relative paths - */ - private static function fullpath(string $path, string $relative_path_base): string - { - if (FileSystem::isRelativePath($path)) { - $path = $relative_path_base . DIRECTORY_SEPARATOR . $path; - } - if (!file_exists($path)) { - throw new RegistryException("Path does not exist: {$path}"); - } - return FileSystem::convertPath($path); - } } diff --git a/src/StaticPHP/Skeleton/ArtifactGenerator.php b/src/StaticPHP/Skeleton/ArtifactGenerator.php new file mode 100644 index 000000000..e05f94d14 --- /dev/null +++ b/src/StaticPHP/Skeleton/ArtifactGenerator.php @@ -0,0 +1,115 @@ +name; + } + + public function setSource(array $source): static + { + $clone = clone $this; + $clone->source = $source; + return $clone; + } + + public function setCustomSource(): static + { + $clone = clone $this; + $clone->source = ['type' => 'custom']; + $clone->generate_class = true; + $clone->generate_custom_source_func = true; + return $clone; + } + + public function getSource(): ?array + { + return $this->source; + } + + public function setBinary(string $os, array $config): static + { + $clone = clone $this; + if ($clone->binary === null) { + $clone->binary = [$os => $config]; + } else { + $clone->binary[$os] = $config; + } + return $clone; + } + + public function generateConfigArray(): array + { + $config = []; + + if ($this->source) { + $config['source'] = $this->source; + } + if ($this->binary) { + $config['binary'] = $this->binary; + } + return $config; + } + + public function setConfigFile(string $file): static + { + $clone = clone $this; + $clone->config_file = $file; + return $clone; + } + + /** + * Write the artifact configuration to the config file. + */ + public function writeConfigFile(): string + { + if ($this->config_file === null) { + throw new ValidationException('Config file path is not set.'); + } + $config_array = $this->generateConfigArray(); + $config_file_json = json_decode(FileSystem::readFile($this->config_file), true); + if (!is_array($config_file_json)) { + throw new ValidationException('Existing config file is not a valid JSON array.'); + } + + $config_file_json[$this->name] = $config_array; + // sort keys + ksort($config_file_json); + $json_content = json_encode($config_file_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + if ($json_content === false) { + throw new ValidationException('Failed to encode config array to JSON.'); + } + if (file_put_contents($this->config_file, $json_content) === false) { + throw new FileSystemException("Failed to write config file: {$this->config_file}"); + } + return $this->config_file; + } +} diff --git a/src/StaticPHP/Skeleton/ExecutorGenerator.php b/src/StaticPHP/Skeleton/ExecutorGenerator.php new file mode 100644 index 000000000..69d7a9f89 --- /dev/null +++ b/src/StaticPHP/Skeleton/ExecutorGenerator.php @@ -0,0 +1,36 @@ +class) { + UnixCMakeExecutor::class => [UnixCMakeExecutor::class, 'UnixCMakeExecutor::create($package)->build();'], + UnixAutoconfExecutor::class => [UnixAutoconfExecutor::class, 'UnixAutoconfExecutor::create($package)->build();'], + WindowsCMakeExecutor::class => [WindowsCMakeExecutor::class, 'WindowsCMakeExecutor::create($package)->build();'], + default => throw new ValidationException("Unsupported executor class: {$this->class}"), + }; + } +} diff --git a/src/StaticPHP/Skeleton/PackageGenerator.php b/src/StaticPHP/Skeleton/PackageGenerator.php new file mode 100644 index 000000000..89802899a --- /dev/null +++ b/src/StaticPHP/Skeleton/PackageGenerator.php @@ -0,0 +1,412 @@ + $depends An array of dependencies required by the package, categorized by operating system. */ + protected array $depends = []; + + /** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $suggests An array of suggested packages for the package, categorized by operating system. */ + protected array $suggests = []; + + /** @var array $frameworks An array of macOS frameworks for the package */ + protected array $frameworks = []; + + /** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $static_libs An array of static libraries required by the package, categorized by operating system. */ + protected array $static_libs = []; + + /** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $headers An array of header files required by the package, categorized by operating system. */ + protected array $headers = []; + + /** @var array<''|'linux'|'macos'|'unix'|'windows', string[]> $static_bins An array of static binaries required by the package, categorized by operating system. */ + protected array $static_bins = []; + + protected ?string $config_file = null; + + /** @var null|ArtifactGenerator $artifact Artifact */ + protected ?ArtifactGenerator $artifact = null; + + /** @var array $licenses Licenses */ + protected array $licenses = []; + + /** @var array<'Darwin'|'Linux'|'Windows', null|string> $build_for_enables Enable build function generating */ + protected array $build_for_enables = [ + 'Darwin' => null, + 'Linux' => null, + 'Windows' => null, + ]; + + /** @var array */ + protected array $func_executor_binding = []; + + /** + * @param string $package_name Package name + * @param 'library'|'php-extension'|'target'|'virtual-target' $type Package type ('library', 'target', 'virtual-target', etc.) + */ + public function __construct(protected string $package_name, protected string $type) {} + + /** + * Add package dependency. + * + * @param string $package Package name + * @param string $os_category Operating system ('' for all OSes, 'unix', 'windows', 'macos') + */ + public function addDependency(string $package, string $os_category = ''): static + { + if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) { + throw new ValidationException("Invalid OS suffix: {$os_category}"); + } + $clone = clone $this; + if (!isset($clone->depends[$os_category])) { + $clone->depends[$os_category] = []; + } + if (!in_array($package, $clone->depends[$os_category], true)) { + $clone->depends[$os_category][] = $package; + } + return $clone; + } + + /** + * Add package suggestion. + * + * @param string $package Package name + * @param string $os_category Operating system ('' for all OSes) + */ + public function addSuggestion(string $package, string $os_category = ''): static + { + if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) { + throw new ValidationException("Invalid OS suffix: {$os_category}"); + } + $clone = clone $this; + if (!isset($clone->suggests[$os_category])) { + $clone->suggests[$os_category] = []; + } + if (!in_array($package, $clone->suggests[$os_category], true)) { + $clone->suggests[$os_category][] = $package; + } + return $clone; + } + + public function addStaticLib(string $lib_a, string $os_category = ''): static + { + if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) { + throw new ValidationException("Invalid OS suffix: {$os_category}"); + } + if (!str_ends_with($lib_a, '.lib') && !str_ends_with($lib_a, '.a')) { + throw new ValidationException("Static library must end with .lib or .a, got: {$lib_a}"); + } + if (str_ends_with($lib_a, '.lib') && in_array($os_category, ['unix', 'linux', 'macos'], true)) { + throw new ValidationException("Static library with .lib extension cannot be added for non-Windows OS: {$lib_a}"); + } + if (str_ends_with($lib_a, '.a') && $os_category === 'windows') { + throw new ValidationException("Static library with .a extension cannot be added for Windows OS: {$lib_a}"); + } + if (isset($this->static_libs[$os_category]) && in_array($lib_a, $this->static_libs[$os_category], true)) { + // already exists + return $this; + } + $clone = clone $this; + if (!isset($clone->static_libs[$os_category])) { + $clone->static_libs[$os_category] = []; + } + if (!in_array($lib_a, $clone->static_libs[$os_category], true)) { + $clone->static_libs[$os_category][] = $lib_a; + } + return $clone; + } + + public function addHeader(string $header_file, string $os_category = ''): static + { + if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) { + throw new ValidationException("Invalid OS suffix: {$os_category}"); + } + $clone = clone $this; + if (!isset($clone->headers[$os_category])) { + $clone->headers[$os_category] = []; + } + if (!in_array($header_file, $clone->headers[$os_category], true)) { + $clone->headers[$os_category][] = $header_file; + } + return $clone; + } + + public function addStaticBin(string $bin_file, string $os_category = ''): static + { + if (!in_array($os_category, ['', ...SUPPORTED_OS_CATEGORY], true)) { + throw new ValidationException("Invalid OS suffix: {$os_category}"); + } + $clone = clone $this; + if (!isset($clone->static_bins[$os_category])) { + $clone->static_bins[$os_category] = []; + } + if (!in_array($bin_file, $clone->static_bins[$os_category], true)) { + $clone->static_bins[$os_category][] = $bin_file; + } + return $clone; + } + + /** + * Add package artifact. + * + * @param ArtifactGenerator $artifactGenerator Artifact generator + */ + public function addArtifact(ArtifactGenerator $artifactGenerator): static + { + $clone = clone $this; + $clone->artifact = $artifactGenerator; + return $clone; + } + + /** + * Add license from string. + * + * @param string $text License content + */ + public function addLicenseFromString(string $text): static + { + $clone = clone $this; + $clone->licenses[] = [ + 'type' => 'text', + 'text' => $text, + ]; + return $clone; + } + + /** + * Add license from file. + * + * @param string $file_path License file path + */ + public function addLicenseFromFile(string $file_path): static + { + $clone = clone $this; + $clone->licenses[] = [ + 'type' => 'file', + 'path' => $file_path, + ]; + return $clone; + } + + /** + * Enable build for specific OS. + * + * @param 'Darwin'|'Linux'|'Windows'|array<'Darwin'|'Linux'|'Windows'> $build_for Build for OS + */ + public function enableBuild(array|string $build_for, ?string $build_function_name = null): static + { + $clone = clone $this; + if (is_array($build_for)) { + foreach ($build_for as $bf) { + $clone = $clone->enableBuild($bf, $build_function_name ?? 'build'); + } + return $clone; + } + if (!in_array($build_for, SUPPORTED_OS_FAMILY, true)) { + throw new ValidationException("Unsupported build_for value: {$build_for}"); + } + $clone->build_for_enables[$build_for] = $build_function_name ?? "buildFor{$build_for}"; + return $clone; + } + + /** + * Bind function executor. + * + * @param string $func_name Function name + * @param ExecutorGenerator $executor Executor generator + */ + public function addFunctionExecutorBinding(string $func_name, ExecutorGenerator $executor): static + { + $clone = clone $this; + $clone->func_executor_binding[$func_name] = $executor; + return $clone; + } + + public function generatePackageClassFile(string $namespace, bool $uppercase = false): string + { + $printer = new class extends Printer { + public string $indentation = ' '; + }; + $file = new PhpFile(); + $namespace = $file->setStrictTypes()->addNamespace($namespace); + + $uses = []; + + // class name and package attribute + $class_name = str_replace('-', '_', $uppercase ? ucwords($this->package_name, '-') : $this->package_name); + $class_attribute = match ($this->type) { + 'library' => Library::class, + 'php-extension' => Extension::class, + 'target', 'virtual-target' => Target::class, + }; + $package_class = match ($this->type) { + 'library' => LibraryPackage::class, + 'php-extension' => PhpExtensionPackage::class, + 'target', 'virtual-target' => TargetPackage::class, + }; + $uses[] = $class_attribute; + $uses[] = $package_class; + $uses[] = BuildFor::class; + $uses[] = PackageInstaller::class; + + foreach ($uses as $use) { + $namespace->addUse($use); + } + + // add class attribute + $class = $namespace->addClass($class_name); + $class->addAttribute($class_attribute, [$this->package_name]); + + // add build functions if enabled + $funcs = []; + foreach ($this->build_for_enables as $os_family => $func_name) { + if ($func_name !== null) { + $funcs[$func_name][] = $os_family; + } + } + foreach ($funcs as $name => $oss) { + $method = $class->addMethod(name: $name ?: 'build') + ->setPublic() + ->setReturnType('void'); + // check if function executor is bound + if (isset($this->func_executor_binding[$name])) { + $executor = $this->func_executor_binding[$name]; + [$executor_use, $code] = $executor->generateCode(); + $namespace->addUse($executor_use); + $method->setBody($code); + } + $method->addParameter('package')->setType($package_class); + $method->addParameter('installer')->setType(PackageInstaller::class); + foreach ($oss as $os) { + $method->addAttribute(BuildFor::class, [$os]); + } + } + + return $printer->printFile($file); + } + + /** + * Generate package config + */ + public function generateConfigArray(): array + { + $config = ['type' => $this->type]; + + // Add dependencies + foreach ($this->depends as $suffix => $depends) { + $k = $suffix !== '' ? "depends@{$suffix}" : 'depends'; + $config[$k] = $depends; + } + + // add suggests + foreach ($this->suggests as $suffix => $suggests) { + $k = $suffix !== '' ? "suggests@{$suffix}" : 'suggests'; + $config[$k] = $suggests; + } + + // Add frameworks + if (!empty($this->frameworks)) { + $config['frameworks'] = $this->frameworks; + } + + // Add static libs + foreach ($this->static_libs as $suffix => $libs) { + $k = $suffix !== '' ? "static-libs@{$suffix}" : 'static-libs'; + $config[$k] = $libs; + } + + // Add headers + foreach ($this->headers as $suffix => $headers) { + $k = $suffix !== '' ? "headers@{$suffix}" : 'headers'; + $config[$k] = $headers; + } + + // Add static bins + foreach ($this->static_bins as $suffix => $bins) { + $k = $suffix !== '' ? "static-bins@{$suffix}" : 'static-bins'; + $config[$k] = $bins; + } + + // Add artifact + if ($this->artifact !== null) { + $config['artifact'] = $this->artifact->getName(); + } + + // Add licenses + if (!empty($this->licenses)) { + if (count($this->licenses) === 1) { + $config['license'] = $this->licenses[0]; + } else { + $config['license'] = $this->licenses; + } + } + + return $config; + } + + public function setConfigFile(string $config_file): static + { + $clone = clone $this; + $clone->config_file = $config_file; + return $clone; + } + + public function writeConfigFile(): string + { + if ($this->config_file === null) { + throw new ValidationException('Config file path is not set.'); + } + $config_array = $this->generateConfigArray(); + $config_file_json = json_decode(FileSystem::readFile($this->config_file), true); + if (!is_array($config_file_json)) { + throw new ValidationException('Existing config file is not a valid JSON array.'); + } + $config_file_json[$this->package_name] = $config_array; + ksort($config_file_json); + $json_content = json_encode($config_file_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + if ($json_content === false) { + throw new ValidationException('Failed to encode package config to JSON.'); + } + if (file_put_contents($this->config_file, $json_content) === false) { + throw new FileSystemException("Failed to write config file: {$this->config_file}"); + } + return $this->config_file; + } + + public function writeAll(): array + { + // write config + $package_config_file = $this->writeConfigFile(); + $artifact_config_file = $this->artifact->writeConfigFile(); + + // write class file + $package_class_file_content = $this->generatePackageClassFile('StaticPHP\Packages'); + $package_class_file_path = str_replace('-', '_', $this->package_name) . '.php'; + // file_put_contents($package_class_file_path, $package_class_file_content); // Uncomment this line to actually write the file + return [ + 'package_config' => $package_config_file, + 'artifact_config' => $artifact_config_file, + 'package_class_file' => $package_class_file_path, + 'package_class_content' => $package_class_file_content, + ]; + } +} diff --git a/src/StaticPHP/Util/FileSystem.php b/src/StaticPHP/Util/FileSystem.php index 2f540d70d..1c21a92b5 100644 --- a/src/StaticPHP/Util/FileSystem.php +++ b/src/StaticPHP/Util/FileSystem.php @@ -472,6 +472,23 @@ public static function replaceFileLineContainsString(string $file, string $find, return file_put_contents($file, implode('', $lines)); } + /** + * Return full path, resolving relative paths against a base path. + * + * @param string $path Input path (relative or absolute) + * @param string $relative_path_base Base path for relative paths + */ + public static function fullpath(string $path, string $relative_path_base): string + { + if (FileSystem::isRelativePath($path)) { + $path = $relative_path_base . DIRECTORY_SEPARATOR . $path; + } + if (!file_exists($path)) { + throw new FileSystemException("Path does not exist: {$path}"); + } + return FileSystem::convertPath($path); + } + private static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): false|int { logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename); diff --git a/src/StaticPHP/Util/InteractiveTerm.php b/src/StaticPHP/Util/InteractiveTerm.php index 1682ed1f6..0570f31c6 100644 --- a/src/StaticPHP/Util/InteractiveTerm.php +++ b/src/StaticPHP/Util/InteractiveTerm.php @@ -52,6 +52,7 @@ public static function plain(string $message, string $level = 'info'): void default => logger()->info(strip_ansi_colors($message)), }; } else { + $output = $level === 'error' && $output instanceof ConsoleOutput ? $output->getErrorOutput() : $output; $output->writeln(($no_ansi ? 'strip_ansi_colors' : 'strval')($message)); } } diff --git a/src/globals/defines.php b/src/globals/defines.php index 3e6d23605..dbcb63f22 100644 --- a/src/globals/defines.php +++ b/src/globals/defines.php @@ -96,13 +96,32 @@ const SPC_DOWNLOAD_TYPE_DISPLAY_NAME = [ 'bitbuckettag' => 'BitBucket', - 'filelist' => 'website', + 'filelist' => 'File index website', 'git' => 'git', 'ghrel' => 'GitHub release', - 'ghtar', 'ghtagtar' => 'GitHub tarball', + 'ghtar' => 'GitHub release tarball', + 'ghtagtar' => 'GitHub tag tarball', 'local' => 'local dir', - 'pie' => 'PHP Installer for Extensions', + 'pie' => 'PHP Installer for Extensions (PIE)', 'url' => 'url', 'php-release' => 'php.net', 'custom' => 'custom downloader', ]; + +const SUPPORTED_OS_CATEGORY = [ + 'unix', + 'windows', + 'linux', + 'macos', +]; + +const SUPPORTED_OS_FAMILY = [ + 'Linux', + 'Darwin', + 'Windows', +]; + +const SPC_MODE_SOURCE = 1; +const SPC_MODE_VENDOR = 2; +const SPC_MODE_PHAR = 4; +const SPC_MODE_VENDOR_PHAR = SPC_MODE_VENDOR | SPC_MODE_PHAR; diff --git a/src/globals/functions.php b/src/globals/functions.php index 93cd1ae09..bb22f3a71 100644 --- a/src/globals/functions.php +++ b/src/globals/functions.php @@ -10,6 +10,31 @@ use StaticPHP\Runtime\Shell\WindowsCmd; use ZM\Logger\ConsoleLogger; +/** + * Get the current SPC loading mode. If passed a mode to check, will return whether current mode matches the given mode. + */ +function spc_mode(?int $check_mode = null): bool|int +{ + $mode = SPC_MODE_SOURCE; + // if current file is in phar, then it's phar mode + if (str_starts_with(__FILE__, 'phar://') && Phar::running()) { + // judge whether it's vendor mode (inside vendor/) or source mode (inside src/) + if (basename(dirname(__FILE__, 3)) === 'static-php-cli' && basename(dirname(__FILE__, 5)) === 'vendor') { + $mode = SPC_MODE_VENDOR_PHAR; + } else { + $mode = SPC_MODE_PHAR; + } + } elseif (basename(dirname(__FILE__, 3)) === 'static-php-cli' && basename(dirname(__FILE__, 5)) === 'vendor') { + $mode = SPC_MODE_VENDOR; + } + + if ($check_mode === null) { + return $mode; + } + // use bitwise AND to check mode + return ($mode & $check_mode) !== 0; +} + /** * Judge if an array is an associative array */ diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index f44914ece..e2186c8e4 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -14,8 +14,8 @@ // test php version (8.1 ~ 8.4 available, multiple for matrix) $test_php_version = [ // '8.1', - // '8.2', - // '8.3', + '8.2', + '8.3', '8.4', '8.5', // 'git', @@ -25,11 +25,11 @@ $test_os = [ 'macos-15-intel', // bin/spc for x86_64 'macos-15', // bin/spc for arm64 - // 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 + 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 - // 'ubuntu-24.04', // bin/spc for x86_64 + 'ubuntu-24.04', // bin/spc for x86_64 // 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64 - 'ubuntu-24.04-arm', // bin/spc for arm64 + // 'ubuntu-24.04-arm', // bin/spc for arm64 // 'windows-2022', // .\bin\spc.ps1 // 'windows-2025', ]; @@ -50,14 +50,14 @@ // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). $extensions = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'bcmath', + 'Linux', 'Darwin' => 'mysqli,gmp', 'Windows' => 'bcmath', }; // If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`). $shared_extensions = match (PHP_OS_FAMILY) { - 'Linux' => 'pcov', - 'Darwin' => 'pcov', + 'Linux' => 'grpc,mysqlnd_parsec,mysqlnd_ed25519', + 'Darwin' => '', 'Windows' => '', }; @@ -66,7 +66,7 @@ // If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true. $with_libs = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => '', + 'Linux', 'Darwin' => 'libwebp', 'Windows' => '', }; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ba9173323..14397d038 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -4,6 +4,6 @@ use Psr\Log\LogLevel; require_once __DIR__ . '/../src/bootstrap.php'; -\StaticPHP\Registry\Registry::checkLoadedRegistries(); +\StaticPHP\Registry\Registry::resolve(); logger()->setLevel(LogLevel::ERROR);