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/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;
}
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)
diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php
index 88ee20aa..5942f90c 100644
--- a/src/SPC/builder/unix/UnixBuilderBase.php
+++ b/src/SPC/builder/unix/UnixBuilderBase.php
@@ -267,7 +267,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');
@@ -311,22 +311,17 @@ 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}",
'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,
- ];
- 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/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');
diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php
index c3611dcc..43a88067 100644
--- a/src/SPC/doctor/item/LinuxToolCheckList.php
+++ b/src/SPC/doctor/item/LinuxToolCheckList.php
@@ -53,7 +53,8 @@ class LinuxToolCheckList
'base-devel' => 'automake',
'gettext-devel' => 'gettextize',
'gettext-dev' => 'gettextize',
- 'perl-IPC-Cmd' => '/usr/share/doc/perl-IPC-Cmd',
+ 'perl-IPC-Cmd' => '/usr/share/perl5/vendor_perl/IPC/Cmd.pm',
+ 'perl-Time-Piece' => '/usr/lib64/perl5/Time/Piece.pm',
];
/** @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,
};
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') ?: '';