From 5c1194ea920399b7abea8eb561f63fefa9b57a7e Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 9 Sep 2025 12:10:06 +0700 Subject: [PATCH 1/7] fix custompackage handling --- src/SPC/builder/BuilderBase.php | 4 ++-- src/SPC/builder/unix/UnixBuilderBase.php | 11 +++-------- src/SPC/store/FileSystem.php | 13 +++++++++---- src/SPC/store/pkg/CustomPackage.php | 6 +++++- src/SPC/store/pkg/GoXcaddy.php | 17 ++++++----------- src/SPC/store/pkg/Zig.php | 17 ++--------------- src/SPC/toolchain/ZigToolchain.php | 4 ++-- 7 files changed, 29 insertions(+), 43 deletions(-) diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index b410aab3..e6e27cda 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\LockFile; +use SPC\store\pkg\GoXcaddy; use SPC\store\SourceManager; use SPC\store\SourcePatcher; use SPC\util\AttributeMapper; @@ -507,8 +508,7 @@ abstract class BuilderBase throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!'); } // frankenphp needs package go-xcaddy installed - $pkg_dir = PKG_ROOT_PATH . '/go-xcaddy-' . arch2gnu(php_uname('m')) . '-' . osfamily2shortname(); - if (!file_exists("{$pkg_dir}/bin/go") || !file_exists("{$pkg_dir}/bin/xcaddy")) { + if (!GoXcaddy::isInstalled()) { global $argv; throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy"); } diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index 5309636f..89c7e937 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -266,7 +266,7 @@ abstract class UnixBuilderBase extends BuilderBase protected function buildFrankenphp(): void { - GlobalEnvManager::addPathIfNotExists(GoXcaddy::getEnvironment()['PATH']); + GlobalEnvManager::addPathIfNotExists(GoXcaddy::getPath()); $nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : ''; $nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : ''; $xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES'); @@ -310,7 +310,7 @@ abstract class UnixBuilderBase extends BuilderBase $cflags .= ' -Wno-error=missing-profile'; $libs .= ' -lgcov'; } - $env = [ + $env = [...[ 'CGO_ENABLED' => '1', 'CGO_CFLAGS' => clean_spaces($cflags), 'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}", @@ -320,12 +320,7 @@ abstract class UnixBuilderBase extends BuilderBase "{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, - ]; - foreach (GoXcaddy::getEnvironment() as $key => $value) { - if ($key !== 'PATH') { - $env[$key] = $value; - } - } + ], ...GoXcaddy::getEnvironment()]; shell()->cd(BUILD_BIN_PATH) ->setEnv($env) ->exec("xcaddy build --output frankenphp {$xcaddyModules}"); diff --git a/src/SPC/store/FileSystem.php b/src/SPC/store/FileSystem.php index a40f5f01..7a574925 100644 --- a/src/SPC/store/FileSystem.php +++ b/src/SPC/store/FileSystem.php @@ -274,7 +274,7 @@ class FileSystem public static function convertWinPathToMinGW(string $path): string { if (preg_match('/^[A-Za-z]:/', $path)) { - $path = '/' . strtolower(substr($path, 0, 1)) . '/' . str_replace('\\', '/', substr($path, 2)); + $path = '/' . strtolower($path[0]) . '/' . str_replace('\\', '/', substr($path, 2)); } return $path; } @@ -314,8 +314,13 @@ class FileSystem $sub_file = self::convertPath($dir . '/' . $v); if (is_dir($sub_file) && $recursive) { # 如果是 目录 且 递推 , 则递推添加下级文件 - $list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative)); - } elseif (is_file($sub_file) || is_dir($sub_file) && !$recursive && $include_dir) { + $sub_list = self::scanDirFiles($sub_file, $recursive, $relative); + if (is_array($sub_list)) { + foreach ($sub_list as $item) { + $list[] = $item; + } + } + } elseif (is_file($sub_file) || (is_dir($sub_file) && !$recursive && $include_dir)) { # 如果是 文件 或 (是 目录 且 不递推 且 包含目录) if (is_string($relative) && mb_strpos($sub_file, $relative) === 0) { $list[] = ltrim(mb_substr($sub_file, mb_strlen($relative)), '/\\'); @@ -440,7 +445,7 @@ class FileSystem public static function writeFile(string $path, mixed $content, ...$args): bool|int|string { $dir = pathinfo(self::convertPath($path), PATHINFO_DIRNAME); - if (!is_dir($dir) && !mkdir($dir, 0755, true)) { + if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) { throw new FileSystemException('Write file failed, cannot create parent directory: ' . $dir); } return file_put_contents($path, $content, ...$args); diff --git a/src/SPC/store/pkg/CustomPackage.php b/src/SPC/store/pkg/CustomPackage.php index 093f65d0..eb972d74 100644 --- a/src/SPC/store/pkg/CustomPackage.php +++ b/src/SPC/store/pkg/CustomPackage.php @@ -30,10 +30,14 @@ abstract class CustomPackage /** * Get the environment variables this package needs to be usable. - * PATH needs to be appended, rather than replaced. */ abstract public static function getEnvironment(): array; + /** + * Get the PATH required to use this package. + */ + abstract public static function getPath(): ?string; + abstract public static function isInstalled(): bool; /** diff --git a/src/SPC/store/pkg/GoXcaddy.php b/src/SPC/store/pkg/GoXcaddy.php index daac9dc9..0c1c6f8c 100644 --- a/src/SPC/store/pkg/GoXcaddy.php +++ b/src/SPC/store/pkg/GoXcaddy.php @@ -90,22 +90,17 @@ class GoXcaddy extends CustomPackage public static function getEnvironment(): array { - $arch = arch2gnu(php_uname('m')); - $os = match (PHP_OS_FAMILY) { - 'Windows' => 'win', - 'Darwin' => 'macos', - 'BSD' => 'freebsd', - default => 'linux', - }; - - $packageName = "go-xcaddy-{$arch}-{$os}"; + $packageName = 'go-xcaddy'; $pkgroot = PKG_ROOT_PATH; - return [ - 'PATH' => "{$pkgroot}/{$packageName}/bin", 'GOROOT' => "{$pkgroot}/{$packageName}", 'GOBIN' => "{$pkgroot}/{$packageName}/bin", 'GOPATH' => "{$pkgroot}/go", ]; } + + public static function getPath(): ?string + { + return PKG_ROOT_PATH . '/go-xcaddy/bin'; + } } diff --git a/src/SPC/store/pkg/Zig.php b/src/SPC/store/pkg/Zig.php index 7c206f7c..e9865dbe 100644 --- a/src/SPC/store/pkg/Zig.php +++ b/src/SPC/store/pkg/Zig.php @@ -129,23 +129,10 @@ class Zig extends CustomPackage public static function getEnvironment(): array { - $arch = arch2gnu(php_uname('m')); - $os = match (PHP_OS_FAMILY) { - 'Windows' => 'win', - 'Darwin' => 'macos', - 'BSD' => 'freebsd', - default => 'linux', - }; - - $packageName = "zig-{$arch}-{$os}"; - $path = PKG_ROOT_PATH . "/{$packageName}"; - - return [ - 'PATH' => $path, - ]; + return []; } - private static function getPath(): string + public static function getPath(): ?string { return PKG_ROOT_PATH . '/zig'; } diff --git a/src/SPC/toolchain/ZigToolchain.php b/src/SPC/toolchain/ZigToolchain.php index 3a929f2b..8f2e261e 100644 --- a/src/SPC/toolchain/ZigToolchain.php +++ b/src/SPC/toolchain/ZigToolchain.php @@ -42,10 +42,10 @@ class ZigToolchain implements ToolchainInterface public function afterInit(): void { - if (!is_dir(Zig::getEnvironment()['PATH'])) { + if (!Zig::isInstalled()) { throw new EnvironmentException('You are building with zig, but zig is not installed, please install zig first. (You can use `doctor` command to install it)'); } - GlobalEnvManager::addPathIfNotExists(Zig::getEnvironment()['PATH']); + GlobalEnvManager::addPathIfNotExists(Zig::getPath()); f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: ''; $cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: ''; From 893ce31b173a997fb3f22a0d7ff0d079c15a7cdc Mon Sep 17 00:00:00 2001 From: henderkes Date: Wed, 10 Sep 2025 23:28:20 +0700 Subject: [PATCH 2/7] only install pkgs for crafting when necessary --- src/SPC/command/CraftCommand.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SPC/command/CraftCommand.php b/src/SPC/command/CraftCommand.php index 27b3c38f..6c40133d 100644 --- a/src/SPC/command/CraftCommand.php +++ b/src/SPC/command/CraftCommand.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace SPC\command; use SPC\exception\ValidationException; +use SPC\store\pkg\GoXcaddy; +use SPC\store\pkg\Zig; use SPC\toolchain\ToolchainManager; use SPC\toolchain\ZigToolchain; use SPC\util\ConfigValidator; @@ -63,7 +65,7 @@ class CraftCommand extends BuildCommand } } // install go and xcaddy for frankenphp - if (in_array('frankenphp', $craft['sapi'])) { + if (in_array('frankenphp', $craft['sapi']) && !GoXcaddy::isInstalled()) { $retcode = $this->runCommand('install-pkg', 'go-xcaddy'); if ($retcode !== 0) { $this->output->writeln('craft go-xcaddy failed'); @@ -71,7 +73,7 @@ class CraftCommand extends BuildCommand } } // install zig if requested - if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { + if (ToolchainManager::getToolchainClass() === ZigToolchain::class && !Zig::isInstalled()) { $retcode = $this->runCommand('install-pkg', 'zig'); if ($retcode !== 0) { $this->output->writeln('craft zig failed'); From 0b0ae270da0aa88eb0817a37ab20f977f5295857 Mon Sep 17 00:00:00 2001 From: henderkes Date: Wed, 10 Sep 2025 23:35:17 +0700 Subject: [PATCH 3/7] we lost a v somehow --- src/SPC/builder/unix/UnixBuilderBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index 89c7e937..6017479d 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -317,7 +317,7 @@ abstract class UnixBuilderBase extends BuilderBase 'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' . '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags . '-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' . - "{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . + "v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, ], ...GoXcaddy::getEnvironment()]; From 8680e83af33205aa7b9494e6c9a267bf94ad2f42 Mon Sep 17 00:00:00 2001 From: Marc Henderkes Date: Tue, 16 Sep 2025 14:43:49 +0200 Subject: [PATCH 4/7] fix dba building shared --- src/SPC/builder/extension/dba.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SPC/builder/extension/dba.php b/src/SPC/builder/extension/dba.php index abda5651..d1f8cda3 100644 --- a/src/SPC/builder/extension/dba.php +++ b/src/SPC/builder/extension/dba.php @@ -12,7 +12,7 @@ class dba extends Extension { public function getUnixConfigureArg(bool $shared = false): string { - $qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . ($shared ? 'shared,' : '') . BUILD_ROOT_PATH) : ''; + $qdbm = $this->builder->getLib('qdbm') ? (' --with-qdbm=' . BUILD_ROOT_PATH) : ''; return '--enable-dba' . ($shared ? '=shared' : '') . $qdbm; } From 808d224b0859a18b03c677b91ea3c5b99f62de7e Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Sep 2025 09:07:18 +0200 Subject: [PATCH 5/7] build frankenphp before shared extensions again --- src/SPC/builder/linux/LinuxBuilder.php | 11 +++++------ src/SPC/builder/macos/MacOSBuilder.php | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index 3525ffb7..b1b1ffc5 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -147,16 +147,15 @@ class LinuxBuilder extends UnixBuilderBase } $this->buildEmbed(); } - // build dynamic extensions if needed, must happen before building FrankenPHP to make sure we export all necessary, undefined symbols + if ($enableFrankenphp) { + logger()->info('building frankenphp'); + $this->buildFrankenphp(); + } $shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared')))); if (!empty($shared_extensions)) { logger()->info('Building shared extensions ...'); $this->buildSharedExts(); } - if ($enableFrankenphp) { - logger()->info('building frankenphp'); - $this->buildFrankenphp(); - } } public function testPHP(int $build_target = BUILD_TARGET_NONE) @@ -326,7 +325,7 @@ class LinuxBuilder extends UnixBuilderBase $target = "{$libDir}/{$realLibName}"; if (file_exists($target)) { [, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target)); - $output = join("\n", $output); + $output = implode("\n", $output); if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) { $currentSoname = $sonameMatch[1]; if ($currentSoname !== basename($target)) { diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 20a2ed13..ef7b303b 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -161,15 +161,15 @@ class MacOSBuilder extends UnixBuilderBase } $this->buildEmbed(); } + if ($enableFrankenphp) { + logger()->info('building frankenphp'); + $this->buildFrankenphp(); + } $shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared')))); if (!empty($shared_extensions)) { logger()->info('Building shared extensions ...'); $this->buildSharedExts(); } - if ($enableFrankenphp) { - logger()->info('building frankenphp'); - $this->buildFrankenphp(); - } } public function testPHP(int $build_target = BUILD_TARGET_NONE) From 311ee40ae47074aa8eb25f8439fbd719d679efbe Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Sep 2025 11:58:58 +0200 Subject: [PATCH 6/7] update LinuxToolCheckList.php to install perl-Time-Piece to make openssl work --- src/SPC/doctor/item/LinuxToolCheckList.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index c3611dcc..41202f64 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -54,6 +54,7 @@ class LinuxToolCheckList 'gettext-devel' => 'gettextize', 'gettext-dev' => 'gettextize', 'perl-IPC-Cmd' => '/usr/share/doc/perl-IPC-Cmd', + 'perl-Time-Piece' => '/usr/share/doc/perl-Time-Piece', ]; /** @noinspection PhpUnused */ @@ -65,7 +66,7 @@ class LinuxToolCheckList $required = match ($distro['dist']) { 'alpine' => self::TOOLS_ALPINE, 'redhat' => self::TOOLS_RHEL, - 'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd']), + 'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd', 'perl-Time-Piece']), 'arch' => self::TOOLS_ARCH, default => self::TOOLS_DEBIAN, }; From 191d34525093410249cb11c56083580fdfe70117 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Sep 2025 12:41:36 +0200 Subject: [PATCH 7/7] update paths to only install once --- src/SPC/doctor/item/LinuxToolCheckList.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php index 41202f64..43a88067 100644 --- a/src/SPC/doctor/item/LinuxToolCheckList.php +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -53,8 +53,8 @@ class LinuxToolCheckList 'base-devel' => 'automake', 'gettext-devel' => 'gettextize', 'gettext-dev' => 'gettextize', - 'perl-IPC-Cmd' => '/usr/share/doc/perl-IPC-Cmd', - 'perl-Time-Piece' => '/usr/share/doc/perl-Time-Piece', + 'perl-IPC-Cmd' => '/usr/share/perl5/vendor_perl/IPC/Cmd.pm', + 'perl-Time-Piece' => '/usr/lib64/perl5/Time/Piece.pm', ]; /** @noinspection PhpUnused */