From ed659cc72a291d0113a5ba42bd70890b4a37dbee Mon Sep 17 00:00:00 2001 From: David Stone Date: Fri, 10 Apr 2026 12:50:27 -0600 Subject: [PATCH 1/9] feat: add gmagick extension and GraphicsMagick library support Add support for the gmagick PHP extension (PECL) backed by the GraphicsMagick library. This mirrors the existing imagick/ImageMagick pattern: - config/ext.json: register gmagick as external extension - config/lib.json: register graphicsmagick library with pkg-config, dependencies (zlib, libpng, libjpeg, bzip2) and optional deps (libwebp, libtiff, freetype, zstd, xz) - config/source.json: add ext-gmagick from PECL and graphicsmagick library from SourceForge - Extension builder: custom configure arg (--with-gmagick=BUILD_ROOT) - Library builder: autoconf-based build with optional image format support, OpenMP disabled, no X11, static-only --- config/ext.json | 12 +++++ config/lib.json | 20 ++++++++ config/source.json | 18 +++++++ src/SPC/builder/extension/gmagick.php | 17 +++++++ .../builder/linux/library/graphicsmagick.php | 12 +++++ .../builder/macos/library/graphicsmagick.php | 12 +++++ .../builder/unix/library/graphicsmagick.php | 47 +++++++++++++++++++ 7 files changed, 138 insertions(+) create mode 100644 src/SPC/builder/extension/gmagick.php create mode 100644 src/SPC/builder/linux/library/graphicsmagick.php create mode 100644 src/SPC/builder/macos/library/graphicsmagick.php create mode 100644 src/SPC/builder/unix/library/graphicsmagick.php diff --git a/config/ext.json b/config/ext.json index 16a71c212..848770b57 100644 --- a/config/ext.json +++ b/config/ext.json @@ -222,6 +222,18 @@ ], "lib-depends-windows": [] }, + "gmagick": { + "support": { + "Windows": "wip", + "BSD": "wip" + }, + "type": "external", + "source": "ext-gmagick", + "arg-type": "custom", + "lib-depends": [ + "graphicsmagick" + ] + }, "gmp": { "support": { "Windows": "wip", diff --git a/config/lib.json b/config/lib.json index 4792a9329..0434df14e 100644 --- a/config/lib.json +++ b/config/lib.json @@ -197,6 +197,26 @@ "Security" ] }, + "graphicsmagick": { + "source": "graphicsmagick", + "pkg-configs": [ + "GraphicsMagick", + "GraphicsMagickWand" + ], + "lib-depends": [ + "zlib", + "libpng", + "libjpeg", + "bzip2" + ], + "lib-suggests": [ + "libwebp", + "libtiff", + "freetype", + "zstd", + "xz" + ] + }, "grpc": { "source": "grpc", "pkg-configs": [ diff --git a/config/source.json b/config/source.json index 18bca217b..b17ad159e 100644 --- a/config/source.json +++ b/config/source.json @@ -152,6 +152,16 @@ "path": "LICENSE" } }, + "ext-gmagick": { + "type": "url", + "url": "https://pecl.php.net/get/gmagick", + "path": "php-src/ext/gmagick", + "filename": "gmagick.tgz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-gmssl": { "type": "ghtar", "repo": "gmssl/GmSSL-PHP", @@ -382,6 +392,14 @@ "path": "LICENSE" } }, + "graphicsmagick": { + "type": "url", + "url": "https://downloads.sourceforge.net/project/graphicsmagick/graphicsmagick/1.3.45/GraphicsMagick-1.3.45.tar.xz", + "license": { + "type": "file", + "path": "Copyright.txt" + } + }, "grpc": { "type": "git", "rev": "v1.75.x", diff --git a/src/SPC/builder/extension/gmagick.php b/src/SPC/builder/extension/gmagick.php new file mode 100644 index 000000000..6f8667fa7 --- /dev/null +++ b/src/SPC/builder/extension/gmagick.php @@ -0,0 +1,17 @@ +optionalLib('zlib', ...ac_with_args('zlib')) + ->optionalLib('libpng', ...ac_with_args('png')) + ->optionalLib('libjpeg', ...ac_with_args('jpeg')) + ->optionalLib('libwebp', ...ac_with_args('webp')) + ->optionalLib('libtiff', ...ac_with_args('tiff')) + ->optionalLib('freetype', ...ac_with_args('ttf')) + ->optionalLib('bzip2', ...ac_with_args('bzlib')) + ->addConfigureArgs( + '--disable-openmp', + '--without-x', + '--without-perl', + '--enable-shared=no', + '--enable-static=yes', + ); + + // special: linux-static target needs `-static` + $ldflags = SPCTarget::isStatic() ? '-static -ldl' : '-ldl'; + + // special: macOS needs -liconv + $libs = SPCTarget::getTargetOS() === 'Darwin' ? '-liconv' : ''; + + $ac->appendEnv([ + 'LDFLAGS' => $ldflags, + 'LIBS' => $libs, + 'PKG_CONFIG' => '$PKG_CONFIG --static', + ]); + + $ac->configure()->make(); + + $this->patchPkgconfPrefix(['GraphicsMagick.pc', 'GraphicsMagick++.pc', 'GraphicsMagickWand.pc']); + $this->patchLaDependencyPrefix(); + } +} From b92699c4bbf1afe48b36201dc8fce740d9b99f86 Mon Sep 17 00:00:00 2001 From: David Stone Date: Fri, 10 Apr 2026 13:15:38 -0600 Subject: [PATCH 2/9] fix: patch out OpenMP check in gmagick for static builds The gmagick PECL extension's config.m4 has a compile-time check for omp_pause_resource_all that succeeds when omp.h is available but then fails at link time because libgomp is not available in static builds. Patch the config.m4 before buildconf to remove the OMP check entirely. The gmagick source already has a fallback path (usleep loop) when HAVE_OMP_PAUSE_RESOURCE_ALL is not defined. --- src/SPC/builder/extension/gmagick.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/SPC/builder/extension/gmagick.php b/src/SPC/builder/extension/gmagick.php index 6f8667fa7..b433a0828 100644 --- a/src/SPC/builder/extension/gmagick.php +++ b/src/SPC/builder/extension/gmagick.php @@ -5,6 +5,7 @@ namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\store\FileSystem; use SPC\util\CustomExt; #[CustomExt('gmagick')] @@ -14,4 +15,17 @@ public function getUnixConfigureArg(bool $shared = false): string { return '--with-gmagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH; } + + public function patchBeforeBuildconf(): bool + { + // Remove the OpenMP check from config.m4 to avoid linking against libgomp + // in static builds. The gmagick source code already has a fallback path + // (usleep loop) when HAVE_OMP_PAUSE_RESOURCE_ALL is not defined. + FileSystem::replaceFileRegex( + SOURCE_PATH . '/php-src/ext/gmagick/config.m4', + '/AC_MSG_CHECKING\(omp_pause_resource_all usability\).*?AC_MSG_RESULT\(no\)\n\t\t\]\)/s', + 'dnl OMP check removed for static build' + ); + return true; + } } From cd9f8981928900e2e39946c2239de7662180eaf7 Mon Sep 17 00:00:00 2001 From: David Stone Date: Fri, 10 Apr 2026 14:03:35 -0600 Subject: [PATCH 3/9] test: add gmagick extension sanity test Verify the Gmagick class exists and that JPEG and PNG formats are available, matching the pattern used by other ext-tests. --- src/globals/ext-tests/gmagick.php | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/globals/ext-tests/gmagick.php diff --git a/src/globals/ext-tests/gmagick.php b/src/globals/ext-tests/gmagick.php new file mode 100644 index 000000000..aa98ff510 --- /dev/null +++ b/src/globals/ext-tests/gmagick.php @@ -0,0 +1,7 @@ +queryformats())); +assert(in_array('PNG', (new Gmagick())->queryformats())); From e837397c5ea26917f0dc2f5d88f024d36fe34e82 Mon Sep 17 00:00:00 2001 From: David Stone Date: Fri, 10 Apr 2026 17:23:06 -0600 Subject: [PATCH 4/9] refactor: use ac_cv_func var instead of patching config.m4 for OMP Match the imagick pattern: pass ac_cv_func_omp_pause_resource_all=no to configure instead of patching config.m4. This is simpler and consistent with how the project handles the same issue in imagick. --- src/SPC/builder/extension/gmagick.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/SPC/builder/extension/gmagick.php b/src/SPC/builder/extension/gmagick.php index b433a0828..7a0b0c1a2 100644 --- a/src/SPC/builder/extension/gmagick.php +++ b/src/SPC/builder/extension/gmagick.php @@ -5,7 +5,6 @@ namespace SPC\builder\extension; use SPC\builder\Extension; -use SPC\store\FileSystem; use SPC\util\CustomExt; #[CustomExt('gmagick')] @@ -13,19 +12,7 @@ class gmagick extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - return '--with-gmagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH; - } - - public function patchBeforeBuildconf(): bool - { - // Remove the OpenMP check from config.m4 to avoid linking against libgomp - // in static builds. The gmagick source code already has a fallback path - // (usleep loop) when HAVE_OMP_PAUSE_RESOURCE_ALL is not defined. - FileSystem::replaceFileRegex( - SOURCE_PATH . '/php-src/ext/gmagick/config.m4', - '/AC_MSG_CHECKING\(omp_pause_resource_all usability\).*?AC_MSG_RESULT\(no\)\n\t\t\]\)/s', - 'dnl OMP check removed for static build' - ); - return true; + $disable_omp = ' ac_cv_func_omp_pause_resource_all=no'; + return '--with-gmagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; } } From 0b63d99b0a971e6777e6360187567a4b8ee6c6ab Mon Sep 17 00:00:00 2001 From: Marc Date: Sat, 11 Apr 2026 10:59:12 +0700 Subject: [PATCH 5/9] Update config/source.json --- config/source.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/source.json b/config/source.json index b17ad159e..f51dbf5c5 100644 --- a/config/source.json +++ b/config/source.json @@ -394,7 +394,7 @@ }, "graphicsmagick": { "type": "url", - "url": "https://downloads.sourceforge.net/project/graphicsmagick/graphicsmagick/1.3.45/GraphicsMagick-1.3.45.tar.xz", + "url": "https://downloads.sourceforge.net/project/graphicsmagick/graphicsmagick/1.3.46/GraphicsMagick-1.3.46.tar.xz", "license": { "type": "file", "path": "Copyright.txt" From a628054aa252419b96bcaa2946b574620cc5081b Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 11 Apr 2026 14:01:13 +0800 Subject: [PATCH 6/9] Add libxml2 dep for graphicsmagick, add tests and patches --- config/lib.json | 3 ++- src/SPC/builder/extension/gmagick.php | 7 +++++++ src/globals/test-extensions.php | 14 +++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/config/lib.json b/config/lib.json index 0434df14e..12ba224de 100644 --- a/config/lib.json +++ b/config/lib.json @@ -207,7 +207,8 @@ "zlib", "libpng", "libjpeg", - "bzip2" + "bzip2", + "libxml2" ], "lib-suggests": [ "libwebp", diff --git a/src/SPC/builder/extension/gmagick.php b/src/SPC/builder/extension/gmagick.php index 7a0b0c1a2..12eaeefc6 100644 --- a/src/SPC/builder/extension/gmagick.php +++ b/src/SPC/builder/extension/gmagick.php @@ -5,11 +5,18 @@ namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\store\FileSystem; use SPC\util\CustomExt; #[CustomExt('gmagick')] class gmagick extends Extension { + public function patchBeforeBuildconf(): bool + { + FileSystem::replaceFileStr($this->source_dir . '/gmagick.c', 'zend_exception_get_default()', 'zend_ce_exception'); + return true; + } + public function getUnixConfigureArg(bool $shared = false): string { $disable_omp = ' ac_cv_func_omp_pause_resource_all=no'; diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index 8281f88b7..a02c2e54a 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -14,9 +14,9 @@ // test php version (8.1 ~ 8.4 available, multiple for matrix) $test_php_version = [ // '8.1', - // '8.2', + '8.2', '8.3', - // '8.4', + '8.4', '8.5', // 'git', ]; @@ -27,7 +27,7 @@ 'macos-15', // bin/spc for arm64 // 'ubuntu-latest', // bin/spc-alpine-docker for x86_64 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64 - // 'ubuntu-24.04', // bin/spc for x86_64 + 'ubuntu-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 // 'windows-2022', // .\bin\spc.ps1 @@ -35,7 +35,7 @@ ]; // whether enable thread safe -$zts = true; +$zts = false; $no_strip = false; @@ -43,14 +43,14 @@ $upx = false; // whether to test frankenphp build, only available for macOS and linux -$frankenphp = false; +$frankenphp = true; // prefer downloading pre-built packages to speed up the build process $prefer_pre_built = false; // If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`). $extensions = match (PHP_OS_FAMILY) { - 'Linux', 'Darwin' => 'curl,swoole', + 'Linux', 'Darwin' => 'gmagick', 'Windows' => 'intl', }; @@ -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' => 'krb5', + 'Linux', 'Darwin' => '', 'Windows' => '', }; From f7b69d9fef9e30136844d780103e570ab3b91401 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sat, 11 Apr 2026 14:11:16 +0800 Subject: [PATCH 7/9] zts --- src/globals/test-extensions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/globals/test-extensions.php b/src/globals/test-extensions.php index a02c2e54a..c2340fff1 100644 --- a/src/globals/test-extensions.php +++ b/src/globals/test-extensions.php @@ -35,7 +35,7 @@ ]; // whether enable thread safe -$zts = false; +$zts = true; $no_strip = false; From d023c25a4e7607d8424683a24f52c46ecb565096 Mon Sep 17 00:00:00 2001 From: David Stone Date: Sat, 11 Apr 2026 13:28:04 -0600 Subject: [PATCH 8/9] fix: patch config.m4 directly to remove OMP check for static builds The ac_cv_func_omp_pause_resource_all=no approach does not work for gmagick because its config.m4 uses PHP_CHECK_FUNC which does not honour ac_cv cache variables. Revert to patching config.m4 directly to remove the entire OpenMP check block, preventing the linker error on ubuntu-24.04 (undefined reference to omp_pause_resource_all). --- src/SPC/builder/extension/gmagick.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/SPC/builder/extension/gmagick.php b/src/SPC/builder/extension/gmagick.php index 12eaeefc6..9be1f158d 100644 --- a/src/SPC/builder/extension/gmagick.php +++ b/src/SPC/builder/extension/gmagick.php @@ -13,13 +13,22 @@ class gmagick extends Extension { public function patchBeforeBuildconf(): bool { + // PHP 8.5 removed zend_exception_get_default(), use zend_ce_exception instead FileSystem::replaceFileStr($this->source_dir . '/gmagick.c', 'zend_exception_get_default()', 'zend_ce_exception'); + + // Remove the entire OpenMP check block from config.m4 to avoid linking + // against libgomp in static builds. gmagick's config.m4 uses PHP_CHECK_FUNC + // which does not honour ac_cv cache variables, so we must patch the source. + FileSystem::replaceFileRegex( + SOURCE_PATH . '/php-src/ext/gmagick/config.m4', + '/AC_MSG_CHECKING\(omp_pause_resource_all usability\).*?AC_MSG_RESULT\(no\)\n\t\t\]\)/s', + 'dnl OMP check removed for static build' + ); return true; } public function getUnixConfigureArg(bool $shared = false): string { - $disable_omp = ' ac_cv_func_omp_pause_resource_all=no'; - return '--with-gmagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH . $disable_omp; + return '--with-gmagick=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH; } } From d26a08189a2151e587338f0f71fc183586a438be Mon Sep 17 00:00:00 2001 From: David Stone Date: Wed, 15 Apr 2026 18:22:38 -0600 Subject: [PATCH 9/9] fix: exclude HEIF/JXL from GraphicsMagick static build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GraphicsMagick auto-detects libheif/libjxl headers in the buildroot but the API versions are often incompatible (e.g. GM 1.3.46 uses heif_colorspace_nonvisual which was added in libheif 2.4+, while static-php-cli builds an older version). Disable both to prevent compile errors. These formats are not needed for typical web image processing — JPEG, PNG, WebP, and GIF cover the common use cases. --- src/SPC/builder/unix/library/graphicsmagick.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/SPC/builder/unix/library/graphicsmagick.php b/src/SPC/builder/unix/library/graphicsmagick.php index 29a436883..869fcd400 100644 --- a/src/SPC/builder/unix/library/graphicsmagick.php +++ b/src/SPC/builder/unix/library/graphicsmagick.php @@ -23,6 +23,13 @@ protected function build(): void '--disable-openmp', '--without-x', '--without-perl', + // HEIF/JXL: GraphicsMagick auto-detects libheif/libjxl from the + // buildroot but the API versions are often incompatible (e.g. + // GM 1.3.46 expects libheif symbols added in 2.4+). Exclude them + // to avoid compile errors. Neither format is needed for typical + // WordPress/web image processing (JPEG/PNG/WebP/GIF suffice). + '--without-heif', + '--without-jxl', '--enable-shared=no', '--enable-static=yes', );