Compare commits

...

21 Commits
2.7.3 ... 2.7.4

Author SHA1 Message Date
Marc
79ab6490fd fix CustomPackage handling (#897) 2025-09-18 13:37:26 +02:00
henderkes
191d345250 update paths to only install once 2025-09-18 12:41:36 +02:00
henderkes
311ee40ae4 update LinuxToolCheckList.php to install perl-Time-Piece to make openssl work 2025-09-18 11:58:58 +02:00
henderkes
808d224b08 build frankenphp before shared extensions again 2025-09-18 09:07:18 +02:00
Marc
4247883664 Merge branch 'main' into fix-zig 2025-09-18 08:06:23 +02:00
Marc
3198cc40c1 Remove icu backend from libxml2 (#903) 2025-09-17 10:46:05 +02:00
Marc Henderkes
8680e83af3 fix dba building shared 2025-09-16 14:43:49 +02:00
Marc
0156f33a20 Merge branch 'main' into remove-icu-backend 2025-09-16 13:35:46 +02:00
Marc Henderkes
df8b2dbf16 better patching for libstdc++ when extensions forget they need it 2025-09-16 13:10:10 +02:00
Marc Henderkes
30e174ac95 use any available english locale instead of requiring en_US.utf-8 2025-09-16 13:10:09 +02:00
Marc Henderkes
b9f8f02d98 don't use libicu backend for libxml2
(it's discouraged from being used, the first path always goes through iconv, icu is only the fallback)
2025-09-16 13:10:09 +02:00
henderkes
0b0ae270da we lost a v somehow 2025-09-10 23:35:17 +07:00
henderkes
893ce31b17 only install pkgs for crafting when necessary 2025-09-10 23:28:20 +07:00
crazywhalecc
e35d6c2651 Oh, we also support windows here 2025-09-09 19:43:08 +08:00
crazywhalecc
8dd0512335 Fix macOS lib-depends 2025-09-09 19:43:08 +08:00
henderkes
5c1194ea92 fix custompackage handling 2025-09-09 12:10:06 +07:00
Marc
953ed83df5 Merge pull request #896 from crazywhalecc/fix-zig
Fix zig -isystem overriding
2025-09-08 11:00:03 +02:00
henderkes
c330d02e78 update pkg paths to not double the $arch-$os 2025-09-08 14:18:03 +07:00
henderkes
af6a23011c fix zig relative directory 2025-09-08 13:33:44 +07:00
Abi أب
882ae07deb libxml2 arg typo 2025-09-07 22:56:41 +08:00
crazywhalecc
4bc30b0b6f Add php-src/config.log for SPCException 2025-09-07 11:50:51 +08:00
20 changed files with 154 additions and 118 deletions

View File

@@ -414,9 +414,17 @@
"libmemcached", "libmemcached",
"fastlz" "fastlz"
], ],
"lib-suggests": [
"zstd"
],
"ext-depends": [ "ext-depends": [
"session", "session",
"zlib" "zlib"
],
"ext-suggests": [
"igbinary",
"msgpack",
"session"
] ]
}, },
"mongodb": { "mongodb": {

View File

@@ -9,17 +9,19 @@
"lib-base", "lib-base",
"micro" "micro"
], ],
"lib-depends-macos": [
"lib-base",
"micro",
"libxml2"
],
"lib-suggests-linux": [ "lib-suggests-linux": [
"libacl", "libacl",
"brotli", "brotli",
"watcher" "watcher"
], ],
"lib-suggests-unix": [ "lib-suggests-macos": [
"brotli", "brotli",
"watcher" "watcher"
],
"lib-depends-macos": [
"libxml2"
] ]
}, },
"micro": { "micro": {
@@ -450,6 +452,7 @@
}, },
"libmemcached": { "libmemcached": {
"source": "libmemcached", "source": "libmemcached",
"cpp-library": true,
"static-libs-unix": [ "static-libs-unix": [
"libmemcached.a", "libmemcached.a",
"libmemcachedprotocol.a", "libmemcachedprotocol.a",
@@ -594,7 +597,6 @@
], ],
"lib-suggests-unix": [ "lib-suggests-unix": [
"xz", "xz",
"icu",
"zlib" "zlib"
], ],
"lib-depends-windows": [ "lib-depends-windows": [

View File

@@ -34,7 +34,7 @@ use Symfony\Component\Console\Application;
*/ */
final class ConsoleApplication extends Application final class ConsoleApplication extends Application
{ {
public const string VERSION = '2.7.3'; public const string VERSION = '2.7.4';
public function __construct() public function __construct()
{ {

View File

@@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\LockFile; use SPC\store\LockFile;
use SPC\store\pkg\GoXcaddy;
use SPC\store\SourceManager; use SPC\store\SourceManager;
use SPC\store\SourcePatcher; use SPC\store\SourcePatcher;
use SPC\util\AttributeMapper; use SPC\util\AttributeMapper;
@@ -507,8 +508,7 @@ abstract class BuilderBase
throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!'); throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!');
} }
// frankenphp needs package go-xcaddy installed // frankenphp needs package go-xcaddy installed
$pkg_dir = PKG_ROOT_PATH . '/go-xcaddy-' . arch2gnu(php_uname('m')) . '-' . osfamily2shortname(); if (!GoXcaddy::isInstalled()) {
if (!file_exists("{$pkg_dir}/bin/go") || !file_exists("{$pkg_dir}/bin/xcaddy")) {
global $argv; global $argv;
throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy"); throw new WrongUsageException("FrankenPHP SAPI requires the go-xcaddy package, please install it first: {$argv[0]} install-pkg go-xcaddy");
} }

View File

@@ -5,11 +5,14 @@ declare(strict_types=1);
namespace SPC\builder; namespace SPC\builder;
use SPC\exception\EnvironmentException; use SPC\exception\EnvironmentException;
use SPC\exception\FileSystemException;
use SPC\exception\SPCException; use SPC\exception\SPCException;
use SPC\exception\ValidationException; use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\toolchain\ClangNativeToolchain;
use SPC\toolchain\GccNativeToolchain;
use SPC\toolchain\ToolchainManager; use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain; use SPC\toolchain\ZigToolchain;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
@@ -418,8 +421,24 @@ class Extension
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"), 'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
'LD_LIBRARY_PATH' => BUILD_LIB_PATH, 'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
]; ];
if (ToolchainManager::getToolchainClass() === ZigToolchain::class && SPCTarget::getTargetOS() === 'Linux') { if (str_contains($env['LIBS'], '-lstdc++') && SPCTarget::getTargetOS() === 'Linux') {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++'; if (ToolchainManager::getToolchainClass() === ZigToolchain::class) {
$env['SPC_COMPILER_EXTRA'] = '-lstdc++';
} elseif (ToolchainManager::getToolchainClass() === GccNativeToolchain::class || ToolchainManager::getToolchainClass() === ClangNativeToolchain::class) {
try {
$content = FileSystem::readFile($this->source_dir . '/config.m4');
if ($content && !str_contains($content, 'PHP_ADD_LIBRARY(stdc++')) {
$pattern = '/(PHP_NEW_EXTENSION\(' . $this->name . ',.*\))/m';
$replacement = "$1\nPHP_ADD_LIBRARY(stdc++, 1, " . strtoupper($this->name) . '_SHARED_LIBADD)';
FileSystem::replaceFileRegex(
$this->source_dir . '/config.m4',
$pattern,
$replacement
);
}
} catch (FileSystemException) {
}
}
} }
if ($this->patchBeforeSharedPhpize()) { if ($this->patchBeforeSharedPhpize()) {

View File

@@ -12,7 +12,7 @@ class dba extends Extension
{ {
public function getUnixConfigureArg(bool $shared = false): string 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; return '--enable-dba' . ($shared ? '=shared' : '') . $qdbm;
} }

View File

@@ -17,6 +17,10 @@ class memcached extends Extension
'--with-libmemcached-dir=' . BUILD_ROOT_PATH . ' ' . '--with-libmemcached-dir=' . BUILD_ROOT_PATH . ' ' .
'--disable-memcached-sasl ' . '--disable-memcached-sasl ' .
'--enable-memcached-json ' . '--enable-memcached-json ' .
($this->builder->getLib('zstd') ? '--with-zstd ' : '') .
($this->builder->getExt('igbinary') ? '--enable-memcached-igbinary ' : '') .
($this->builder->getExt('session') ? '--enable-memcached-session ' : '') .
($this->builder->getExt('msgpack') ? '--enable-memcached-msgpack ' : '') .
'--with-system-fastlz'; '--with-system-fastlz';
} }
} }

View File

@@ -103,9 +103,8 @@ class LinuxBuilder extends UnixBuilderBase
); );
} }
shell()->cd(SOURCE_PATH . '/php-src') $this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
->exec( $php_configure_env . ' ' .
$php_configure_env . ' ' .
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' . getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') . ($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
@@ -118,7 +117,7 @@ class LinuxBuilder extends UnixBuilderBase
$zts . $zts .
$maxExecutionTimers . $maxExecutionTimers .
$this->makeStaticExtensionArgs() . ' ' $this->makeStaticExtensionArgs() . ' '
); ));
$this->emitPatchPoint('before-php-make'); $this->emitPatchPoint('before-php-make');
SourcePatcher::patchBeforeMake($this); SourcePatcher::patchBeforeMake($this);
@@ -148,16 +147,15 @@ class LinuxBuilder extends UnixBuilderBase
} }
$this->buildEmbed(); $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')))); $shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
if (!empty($shared_extensions)) { if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...'); logger()->info('Building shared extensions ...');
$this->buildSharedExts(); $this->buildSharedExts();
} }
if ($enableFrankenphp) {
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
} }
public function testPHP(int $build_target = BUILD_TARGET_NONE) public function testPHP(int $build_target = BUILD_TARGET_NONE)
@@ -327,7 +325,7 @@ class LinuxBuilder extends UnixBuilderBase
$target = "{$libDir}/{$realLibName}"; $target = "{$libDir}/{$realLibName}";
if (file_exists($target)) { if (file_exists($target)) {
[, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target)); [, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target));
$output = join("\n", $output); $output = implode("\n", $output);
if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) { if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) {
$currentSoname = $sonameMatch[1]; $currentSoname = $sonameMatch[1];
if ($currentSoname !== basename($target)) { if ($currentSoname !== basename($target)) {

View File

@@ -118,9 +118,8 @@ class MacOSBuilder extends UnixBuilderBase
} }
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static'; $embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
shell()->cd(SOURCE_PATH . '/php-src') $this->seekPhpSrcLogFileOnException(fn () => shell()->cd(SOURCE_PATH . '/php-src')->exec(
->exec( getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') . ($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') . ($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
@@ -132,7 +131,7 @@ class MacOSBuilder extends UnixBuilderBase
$zts . $zts .
$this->makeStaticExtensionArgs() . ' ' . $this->makeStaticExtensionArgs() . ' ' .
$envs_build_php $envs_build_php
); ));
$this->emitPatchPoint('before-php-make'); $this->emitPatchPoint('before-php-make');
SourcePatcher::patchBeforeMake($this); SourcePatcher::patchBeforeMake($this);
@@ -162,15 +161,15 @@ class MacOSBuilder extends UnixBuilderBase
} }
$this->buildEmbed(); $this->buildEmbed();
} }
if ($enableFrankenphp) {
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
$shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared')))); $shared_extensions = array_map('trim', array_filter(explode(',', $this->getOption('build-shared'))));
if (!empty($shared_extensions)) { if (!empty($shared_extensions)) {
logger()->info('Building shared extensions ...'); logger()->info('Building shared extensions ...');
$this->buildSharedExts(); $this->buildSharedExts();
} }
if ($enableFrankenphp) {
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
} }
public function testPHP(int $build_target = BUILD_TARGET_NONE) public function testPHP(int $build_target = BUILD_TARGET_NONE)

View File

@@ -6,6 +6,7 @@ namespace SPC\builder\unix;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\exception\SPCException;
use SPC\exception\SPCInternalException; use SPC\exception\SPCInternalException;
use SPC\exception\ValidationException; use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
@@ -266,7 +267,7 @@ abstract class UnixBuilderBase extends BuilderBase
protected function buildFrankenphp(): void protected function buildFrankenphp(): void
{ {
GlobalEnvManager::addPathIfNotExists(GoXcaddy::getEnvironment()['PATH']); GlobalEnvManager::addPathIfNotExists(GoXcaddy::getPath());
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : ''; $nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : ''; $nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES'); $xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
@@ -310,22 +311,17 @@ abstract class UnixBuilderBase extends BuilderBase
$cflags .= ' -Wno-error=missing-profile'; $cflags .= ' -Wno-error=missing-profile';
$libs .= ' -lgcov'; $libs .= ' -lgcov';
} }
$env = [ $env = [...[
'CGO_ENABLED' => '1', 'CGO_ENABLED' => '1',
'CGO_CFLAGS' => clean_spaces($cflags), 'CGO_CFLAGS' => clean_spaces($cflags),
'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}", 'CGO_LDFLAGS' => "{$this->arch_ld_flags} {$staticFlags} {$config['ldflags']} {$libs}",
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' . 'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags . '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' . '-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
"{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . "v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
"-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",
'LD_LIBRARY_PATH' => BUILD_LIB_PATH, 'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
]; ], ...GoXcaddy::getEnvironment()];
foreach (GoXcaddy::getEnvironment() as $key => $value) {
if ($key !== 'PATH') {
$env[$key] = $value;
}
}
shell()->cd(BUILD_BIN_PATH) shell()->cd(BUILD_BIN_PATH)
->setEnv($env) ->setEnv($env)
->exec("xcaddy build --output frankenphp {$xcaddyModules}"); ->exec("xcaddy build --output frankenphp {$xcaddyModules}");
@@ -338,4 +334,20 @@ abstract class UnixBuilderBase extends BuilderBase
} }
} }
} }
/**
* Seek php-src/config.log when building PHP, add it to exception.
*/
protected function seekPhpSrcLogFileOnException(callable $callback): void
{
try {
$callback();
} catch (SPCException $e) {
if (file_exists(SOURCE_PATH . '/php-src/config.log')) {
$e->addExtraLogFile('php-src config.log', 'php-src.config.log');
copy(SOURCE_PATH . '/php-src/config.log', SPC_LOGS_DIR . '/php-src.config.log');
}
throw $e;
}
}
} }

View File

@@ -20,17 +20,17 @@ trait libxml2
"-DZLIB_INCLUDE_DIR={$this->getIncludeDir()}", "-DZLIB_INCLUDE_DIR={$this->getIncludeDir()}",
'-DLIBXML2_WITH_ZLIB=OFF', '-DLIBXML2_WITH_ZLIB=OFF',
) )
->optionalLib('icu', ...cmake_boolean_args('LIBXML2_WITH_ICU'))
->optionalLib('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA')) ->optionalLib('xz', ...cmake_boolean_args('LIBXML2_WITH_LZMA'))
->addConfigureArgs( ->addConfigureArgs(
'-DLIBXML2_WITH_ICONV=ON', '-DLIBXML2_WITH_ICONV=ON',
'-DLIBXML2_WITH_ICU=OFF', // optional, but discouraged: https://gitlab.gnome.org/GNOME/libxml2/-/blob/master/README.md
'-DLIBXML2_WITH_PYTHON=OFF', '-DLIBXML2_WITH_PYTHON=OFF',
'-DLIBXML2_WITH_PROGRAMS=OFF', '-DLIBXML2_WITH_PROGRAMS=OFF',
'-DLIBXML2_WITH_TESTS=OFF', '-DLIBXML2_WITH_TESTS=OFF',
); );
if ($this instanceof LinuxLibraryBase) { if ($this instanceof LinuxLibraryBase) {
$cmake->addConfigureArgs('-DIconv_IS_BUILD_IN=OFF'); $cmake->addConfigureArgs('-DIconv_IS_BUILT_IN=OFF');
} }
$cmake->build(); $cmake->build();

View File

@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace SPC\command; namespace SPC\command;
use SPC\exception\ValidationException; use SPC\exception\ValidationException;
use SPC\store\pkg\GoXcaddy;
use SPC\store\pkg\Zig;
use SPC\toolchain\ToolchainManager; use SPC\toolchain\ToolchainManager;
use SPC\toolchain\ZigToolchain; use SPC\toolchain\ZigToolchain;
use SPC\util\ConfigValidator; use SPC\util\ConfigValidator;
@@ -63,7 +65,7 @@ class CraftCommand extends BuildCommand
} }
} }
// install go and xcaddy for frankenphp // 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'); $retcode = $this->runCommand('install-pkg', 'go-xcaddy');
if ($retcode !== 0) { if ($retcode !== 0) {
$this->output->writeln('<error>craft go-xcaddy failed</error>'); $this->output->writeln('<error>craft go-xcaddy failed</error>');
@@ -71,7 +73,7 @@ class CraftCommand extends BuildCommand
} }
} }
// install zig if requested // install zig if requested
if (ToolchainManager::getToolchainClass() === ZigToolchain::class) { if (ToolchainManager::getToolchainClass() === ZigToolchain::class && !Zig::isInstalled()) {
$retcode = $this->runCommand('install-pkg', 'zig'); $retcode = $this->runCommand('install-pkg', 'zig');
if ($retcode !== 0) { if ($retcode !== 0) {
$this->output->writeln('<error>craft zig failed</error>'); $this->output->writeln('<error>craft zig failed</error>');

View File

@@ -53,7 +53,8 @@ class LinuxToolCheckList
'base-devel' => 'automake', 'base-devel' => 'automake',
'gettext-devel' => 'gettextize', 'gettext-devel' => 'gettextize',
'gettext-dev' => '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 */ /** @noinspection PhpUnused */
@@ -65,7 +66,7 @@ class LinuxToolCheckList
$required = match ($distro['dist']) { $required = match ($distro['dist']) {
'alpine' => self::TOOLS_ALPINE, 'alpine' => self::TOOLS_ALPINE,
'redhat' => self::TOOLS_RHEL, '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, 'arch' => self::TOOLS_ARCH,
default => self::TOOLS_DEBIAN, default => self::TOOLS_DEBIAN,
}; };

View File

@@ -274,7 +274,7 @@ class FileSystem
public static function convertWinPathToMinGW(string $path): string public static function convertWinPathToMinGW(string $path): string
{ {
if (preg_match('/^[A-Za-z]:/', $path)) { 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; return $path;
} }
@@ -314,8 +314,13 @@ class FileSystem
$sub_file = self::convertPath($dir . '/' . $v); $sub_file = self::convertPath($dir . '/' . $v);
if (is_dir($sub_file) && $recursive) { if (is_dir($sub_file) && $recursive) {
# 如果是 目录 且 递推 , 则递推添加下级文件 # 如果是 目录 且 递推 , 则递推添加下级文件
$list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative)); $sub_list = self::scanDirFiles($sub_file, $recursive, $relative);
} elseif (is_file($sub_file) || is_dir($sub_file) && !$recursive && $include_dir) { 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) { if (is_string($relative) && mb_strpos($sub_file, $relative) === 0) {
$list[] = ltrim(mb_substr($sub_file, mb_strlen($relative)), '/\\'); $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 public static function writeFile(string $path, mixed $content, ...$args): bool|int|string
{ {
$dir = pathinfo(self::convertPath($path), PATHINFO_DIRNAME); $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); throw new FileSystemException('Write file failed, cannot create parent directory: ' . $dir);
} }
return file_put_contents($path, $content, ...$args); return file_put_contents($path, $content, ...$args);

View File

@@ -30,10 +30,14 @@ abstract class CustomPackage
/** /**
* Get the environment variables this package needs to be usable. * Get the environment variables this package needs to be usable.
* PATH needs to be appended, rather than replaced.
*/ */
abstract public static function getEnvironment(): array; 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; abstract public static function isInstalled(): bool;
/** /**

View File

@@ -13,18 +13,7 @@ class GoXcaddy extends CustomPackage
{ {
public static function isInstalled(): bool public static function isInstalled(): bool
{ {
$arch = arch2gnu(php_uname('m')); $folder = PKG_ROOT_PATH . '/go-xcaddy';
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "go-xcaddy-{$arch}-{$os}";
$pkgroot = PKG_ROOT_PATH;
$folder = "{$pkgroot}/{$packageName}";
return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy"); return is_dir($folder) && is_file("{$folder}/bin/go") && is_file("{$folder}/bin/xcaddy");
} }
@@ -59,7 +48,7 @@ class GoXcaddy extends CustomPackage
'macos' => 'darwin', 'macos' => 'darwin',
default => throw new \InvalidArgumentException('Unsupported OS: ' . $name), default => throw new \InvalidArgumentException('Unsupported OS: ' . $name),
}; };
$go_version = '1.24.4'; $go_version = '1.25.0';
$config = [ $config = [
'type' => 'url', 'type' => 'url',
'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz", 'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz",
@@ -70,15 +59,15 @@ class GoXcaddy extends CustomPackage
public function extract(string $name): void public function extract(string $name): void
{ {
$pkgroot = PKG_ROOT_PATH; $pkgroot = PKG_ROOT_PATH;
$go_exec = "{$pkgroot}/{$name}/bin/go"; $go_exec = "{$pkgroot}/go-xcaddy/bin/go";
$xcaddy_exec = "{$pkgroot}/{$name}/bin/xcaddy"; $xcaddy_exec = "{$pkgroot}/go-xcaddy/bin/xcaddy";
if (file_exists($go_exec) && file_exists($xcaddy_exec)) { if (file_exists($go_exec) && file_exists($xcaddy_exec)) {
return; return;
} }
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true); $lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type']; $source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']); $filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = $lock[$name]['move_path'] ?? "{$pkgroot}/{$name}"; $extract = $lock[$name]['move_path'] ?? "{$pkgroot}/go-xcaddy";
FileSystem::extractPackage($name, $source_type, $filename, $extract); FileSystem::extractPackage($name, $source_type, $filename, $extract);
@@ -91,9 +80,9 @@ class GoXcaddy extends CustomPackage
// install xcaddy without using musl tools, xcaddy build requires dynamic linking // install xcaddy without using musl tools, xcaddy build requires dynamic linking
shell() shell()
->appendEnv([ ->appendEnv([
'PATH' => "{$pkgroot}/{$name}/bin:" . $sanitizedPath, 'PATH' => "{$pkgroot}/go-xcaddy/bin:" . $sanitizedPath,
'GOROOT' => "{$pkgroot}/{$name}", 'GOROOT' => "{$pkgroot}/go-xcaddy",
'GOBIN' => "{$pkgroot}/{$name}/bin", 'GOBIN' => "{$pkgroot}/go-xcaddy/bin",
'GOPATH' => "{$pkgroot}/go", 'GOPATH' => "{$pkgroot}/go",
]) ])
->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest'); ->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
@@ -101,22 +90,17 @@ class GoXcaddy extends CustomPackage
public static function getEnvironment(): array public static function getEnvironment(): array
{ {
$arch = arch2gnu(php_uname('m')); $packageName = 'go-xcaddy';
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "go-xcaddy-{$arch}-{$os}";
$pkgroot = PKG_ROOT_PATH; $pkgroot = PKG_ROOT_PATH;
return [ return [
'PATH' => "{$pkgroot}/{$packageName}/bin",
'GOROOT' => "{$pkgroot}/{$packageName}", 'GOROOT' => "{$pkgroot}/{$packageName}",
'GOBIN' => "{$pkgroot}/{$packageName}/bin", 'GOBIN' => "{$pkgroot}/{$packageName}/bin",
'GOPATH' => "{$pkgroot}/go", 'GOPATH' => "{$pkgroot}/go",
]; ];
} }
public static function getPath(): ?string
{
return PKG_ROOT_PATH . '/go-xcaddy/bin';
}
} }

View File

@@ -103,7 +103,7 @@ class Zig extends CustomPackage
public function extract(string $name): void public function extract(string $name): void
{ {
$pkgroot = PKG_ROOT_PATH; $pkgroot = PKG_ROOT_PATH;
$zig_bin_dir = "{$pkgroot}/{$name}"; $zig_bin_dir = "{$pkgroot}/zig";
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy']; $files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
$all_exist = true; $all_exist = true;
@@ -120,7 +120,7 @@ class Zig extends CustomPackage
$lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true); $lock = json_decode(FileSystem::readFile(LockFile::LOCK_FILE), true);
$source_type = $lock[$name]['source_type']; $source_type = $lock[$name]['source_type'];
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']); $filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
$extract = "{$pkgroot}/{$name}"; $extract = "{$pkgroot}/zig";
FileSystem::extractPackage($name, $source_type, $filename, $extract); FileSystem::extractPackage($name, $source_type, $filename, $extract);
@@ -129,34 +129,12 @@ class Zig extends CustomPackage
public static function getEnvironment(): array public static function getEnvironment(): array
{ {
$arch = arch2gnu(php_uname('m')); return [];
$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,
];
} }
private static function getPath(): string public static function getPath(): ?string
{ {
$arch = arch2gnu(php_uname('m')); return PKG_ROOT_PATH . '/zig';
$os = match (PHP_OS_FAMILY) {
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => 'linux',
};
$packageName = "zig-{$arch}-{$os}";
return PKG_ROOT_PATH . "/{$packageName}";
} }
private function createZigCcScript(string $bin_dir): void private function createZigCcScript(string $bin_dir): void

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../buildroot/include" 2>/dev/null || true)" BUILDROOT_ABS="$(realpath "$SCRIPT_DIR/../../../buildroot/include" 2>/dev/null || true)"
PARSED_ARGS=() PARSED_ARGS=()
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do

View File

@@ -42,10 +42,10 @@ class ZigToolchain implements ToolchainInterface
public function afterInit(): void 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)'); 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 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') ?: ''; $cflags = getenv('SPC_DEFAULT_C_FLAGS') ?: '';
$cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: ''; $cxxflags = getenv('SPC_DEFAULT_CXX_FLAGS') ?: '';

View File

@@ -4,20 +4,40 @@ declare(strict_types=1);
assert(function_exists('gettext')); assert(function_exists('gettext'));
assert(function_exists('bindtextdomain')); assert(function_exists('bindtextdomain'));
assert(function_exists('bind_textdomain_codeset'));
assert(function_exists('textdomain')); assert(function_exists('textdomain'));
if (!is_dir('locale/en_US/LC_MESSAGES/')) { foreach (['en_US', 'en_GB'] as $lc) {
mkdir('locale/en_US/LC_MESSAGES/', 0755, true); $dir = "locale/{$lc}/LC_MESSAGES";
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSytaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';
$path = "{$dir}/test.mo";
if (!file_exists($path)) {
file_put_contents($path, base64_decode($mo));
}
} }
if (!file_exists('locale/en_US/LC_MESSAGES/test.mo')) {
$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSStaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA=='; // Probe for an available English locale
file_put_contents('locale/en_US/LC_MESSAGES/test.mo', base64_decode($mo)); $candidates = [
} 'en_US.UTF-8', 'en_US.utf8', 'en_US.utf-8', 'en_US',
putenv('LANG=en_US'); 'en_GB.UTF-8', 'en_GB.utf8', 'en_GB.utf-8', 'en_GB',
assert(setlocale(LC_ALL, 'en_US.utf-8') === 'en_US.utf-8'); 'English_United States.65001', 'English_United States.1252',
'English_United Kingdom.65001', 'English_United Kingdom.1252',
];
$locale = setlocale(LC_ALL, $candidates);
assert($locale !== false);
putenv('LC_ALL=' . $locale);
putenv('LANG=' . $locale);
putenv('LANGUAGE=' . (stripos($locale, 'US') !== false ? 'en_US:en_GB' : 'en_GB:en_US'));
$domain = 'test'; $domain = 'test';
bindtextdomain($domain, 'locale/'); bindtextdomain($domain, 'locale/');
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain); textdomain($domain);
assert(gettext(json_decode('"\u793a\u4f8b"', true)) === 'Example'); $src = json_decode('"\u793a\u4f8b"', true);
assert(gettext($src) === 'Example');