From c1f2fd49a6fdf92cbf680b15d5b3faa8977f6660 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 18 Mar 2026 13:30:29 +0800 Subject: [PATCH] Add ext-imap --- config/pkg/ext/ext-imap.yml | 15 +++++++ src/Package/Extension/imap.php | 55 +++++++++++++++++++++++ src/Package/Library/imap.php | 65 +++++++++++++++------------- src/StaticPHP/Util/SPCConfigUtil.php | 4 +- 4 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 config/pkg/ext/ext-imap.yml create mode 100644 src/Package/Extension/imap.php diff --git a/config/pkg/ext/ext-imap.yml b/config/pkg/ext/ext-imap.yml new file mode 100644 index 00000000..a6c18dac --- /dev/null +++ b/config/pkg/ext/ext-imap.yml @@ -0,0 +1,15 @@ +ext-imap: + type: php-extension + artifact: + source: + type: pecl + name: imap + metadata: + license-files: [LICENSE] + license: PHP-3.01 + depends: + - imap + suggests: + - ext-openssl + php-extension: + arg-type: custom diff --git a/src/Package/Extension/imap.php b/src/Package/Extension/imap.php new file mode 100644 index 00000000..e9879b48 --- /dev/null +++ b/src/Package/Extension/imap.php @@ -0,0 +1,55 @@ +getOption('enable-zts')) { + throw new WrongUsageException('ext-imap is not thread safe, do not build it with ZTS builds'); + } + } + + #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-imap')] + public function patchBeforeBuildconf(PackageInstaller $installer): void + { + if ($installer->getLibraryPackage('openssl')) { + // sometimes imap with openssl does not contain zlib (required by openssl) + // we need to add it manually + FileSystem::replaceFileStr("{$this->getSourceDir()}/config.m4", 'TST_LIBS="$DLIBS $IMAP_SHARED_LIBADD"', 'TST_LIBS="$DLIBS $IMAP_SHARED_LIBADD -lz"'); + } + // c-client is built with PASSWDTYPE=nul so libcrypt is not referenced. + FileSystem::replaceFileStr( + "{$this->getSourceDir()}/config.m4", + " PHP_CHECK_LIBRARY(crypt, crypt,\n [\n PHP_ADD_LIBRARY(crypt,, IMAP_SHARED_LIBADD)\n AC_DEFINE(HAVE_LIBCRYPT,1,[ ])\n ])", + ' dnl Skipped: crypt check not needed (c-client built with PASSWDTYPE=nul)' + ); + } + + #[CustomPhpConfigureArg('Darwin')] + #[CustomPhpConfigureArg('Linux')] + public function getUnixConfigureArg(PackageInstaller $installer, PackageBuilder $builder): string + { + $arg = "--with-imap={$builder->getBuildRootPath()}"; + if (($ssl = $installer->getLibraryPackage('openssl')) !== null) { + $arg .= " --with-imap-ssl={$ssl->getBuildRootPath()}"; + } + return $arg; + } +} diff --git a/src/Package/Library/imap.php b/src/Package/Library/imap.php index 607d78ee..3c9f261c 100644 --- a/src/Package/Library/imap.php +++ b/src/Package/Library/imap.php @@ -4,8 +4,6 @@ declare(strict_types=1); namespace Package\Library; -use Package\Target\php; -use StaticPHP\Attribute\Package\AfterStage; use StaticPHP\Attribute\Package\BuildFor; use StaticPHP\Attribute\Package\Library; use StaticPHP\Attribute\Package\PatchBeforeBuild; @@ -19,15 +17,6 @@ use StaticPHP\Util\SourcePatcher; #[Library('imap')] class imap { - #[AfterStage('php', [php::class, 'patchUnixEmbedScripts'], 'imap')] - #[PatchDescription('Fix missing -lcrypt in php-config libs on glibc systems')] - public function afterPatchScripts(): void - { - if (SystemTarget::getLibc() === 'glibc') { - FileSystem::replaceFileRegex(BUILD_BIN_PATH . '/php-config', '/^libs="(.*)"$/m', 'libs="$1 -lcrypt"'); - } - } - #[PatchBeforeBuild] #[PatchDescription('Patch imap build system for Linux and macOS compatibility')] public function patchBeforeBuild(LibraryPackage $lib): void @@ -66,14 +55,24 @@ class imap } $libcVer = SystemTarget::getLibcVersion(); $extraLibs = $libcVer && version_compare($libcVer, '2.17', '<=') ? 'EXTRALDFLAGS="-ldl -lrt -lpthread"' : ''; - shell()->cd($lib->getSourceDir()) - ->exec('make clean') - ->exec('touch ip6') - ->exec('chmod +x tools/an') - ->exec('chmod +x tools/ua') - ->exec('chmod +x src/osdep/unix/drivers') - ->exec('chmod +x src/osdep/unix/mkauths') - ->exec("yes | make slx {$ssl_options} EXTRACFLAGS='-fPIC -Wno-implicit-function-declaration -Wno-incompatible-function-pointer-types' {$extraLibs}"); + try { + shell()->cd($lib->getSourceDir()) + ->exec('make clean') + ->exec('touch ip6') + ->exec('chmod +x tools/an') + ->exec('chmod +x tools/ua') + ->exec('chmod +x src/osdep/unix/drivers') + ->exec('chmod +x src/osdep/unix/mkauths') + // PASSWDTYPE=nul avoids any crypt() symbol reference in c-client.a; + // zig-cc 0.15+ uses paths_first strategy and cannot find libcrypt outside of buildroot. + ->exec("yes | make slx {$ssl_options} PASSWDTYPE=nul EXTRACFLAGS='-fPIC -Wno-implicit-function-declaration -Wno-incompatible-function-pointer-types' {$extraLibs}"); + } catch (\Throwable $e) { + // slx target also builds bundled tools (mtest, etc.) which may fail to link -lcrypt dynamically + // (e.g. with zig-cc). We only need c-client.a, so tolerate the failure if it was built. + if (!file_exists("{$lib->getSourceDir()}/c-client/c-client.a")) { + throw $e; + } + } try { shell() ->exec("cp -rf {$lib->getSourceDir()}/c-client/c-client.a {$lib->getLibDir()}/libc-client.a") @@ -94,16 +93,24 @@ class imap $ssl_options = 'SSLTYPE=none'; } $out = shell()->execWithResult('echo "-include $(xcrun --show-sdk-path)/usr/include/poll.h -include $(xcrun --show-sdk-path)/usr/include/time.h -include $(xcrun --show-sdk-path)/usr/include/utime.h"')[1][0]; - shell()->cd($lib->getSourceDir()) - ->exec('make clean') - ->exec('touch ip6') - ->exec('chmod +x tools/an') - ->exec('chmod +x tools/ua') - ->exec('chmod +x src/osdep/unix/drivers') - ->exec('chmod +x src/osdep/unix/mkauths') - ->exec( - "echo y | make osx {$ssl_options} EXTRACFLAGS='-Wno-implicit-function-declaration -Wno-incompatible-function-pointer-types {$out}'" - ); + try { + shell()->cd($lib->getSourceDir()) + ->exec('make clean') + ->exec('touch ip6') + ->exec('chmod +x tools/an') + ->exec('chmod +x tools/ua') + ->exec('chmod +x src/osdep/unix/drivers') + ->exec('chmod +x src/osdep/unix/mkauths') + ->exec( + "echo y | make osx {$ssl_options} EXTRACFLAGS='-Wno-implicit-function-declaration -Wno-incompatible-function-pointer-types {$out}'" + ); + } catch (\Throwable $e) { + // osx target also builds bundled tools (mtest, etc.) which may fail to link. + // We only need c-client.a, so tolerate the failure if it was built. + if (!file_exists("{$lib->getSourceDir()}/c-client/c-client.a")) { + throw $e; + } + } try { shell() ->exec("cp -rf {$lib->getSourceDir()}/c-client/c-client.a {$lib->getLibDir()}/libc-client.a") diff --git a/src/StaticPHP/Util/SPCConfigUtil.php b/src/StaticPHP/Util/SPCConfigUtil.php index 32ef3bc6..8b6fe6b3 100644 --- a/src/StaticPHP/Util/SPCConfigUtil.php +++ b/src/StaticPHP/Util/SPCConfigUtil.php @@ -389,7 +389,9 @@ class SPCConfigUtil } if (in_array('imap', $packages) && SystemTarget::getTargetOS() === 'Linux' && SystemTarget::getLibc() === 'glibc') { - $lib_names[] = '-lcrypt'; + if (file_exists(BUILD_LIB_PATH . '/libcrypt.a')) { + $lib_names[] = '-lcrypt'; + } } if (!$use_short_libs) { $lib_names = array_map(fn ($l) => $this->getFullLibName($l), $lib_names);