Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions config/pkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,17 @@
"extract-files": {
"upx-*-win64/upx.exe": "{pkg_root_path}/bin/upx.exe"
}
},
"go-mod-frankenphp-x86_64-linux": {
"type": "custom"
},
"go-mod-frankenphp-aarch64-linux": {
"type": "custom"
},
"go-mod-frankenphp-x86_64-macos": {
"type": "custom"
},
"go-mod-frankenphp-aarch64-macos": {
"type": "custom"
}
}
23 changes: 23 additions & 0 deletions src/SPC/builder/BuilderBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,9 @@ public function getBuildTypeName(int $type): string
if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
$ls[] = 'embed';
}
if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
$ls[] = 'frankenphp';
}
return implode(', ', $ls);
}

Expand Down Expand Up @@ -510,6 +513,26 @@ public function emitPatchPoint(string $point_name): void
}
}

public function checkBeforeBuildPHP(int $rule): void
{
if (($rule & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
// frankenphp only support linux and macOS
if (!in_array(PHP_OS_FAMILY, ['Linux', 'Darwin'])) {
throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!');
}
// frankenphp needs package go-mod-frankenphp installed
$pkg_dir = PKG_ROOT_PATH . '/go-mod-frankenphp-' . arch2gnu(php_uname('m')) . '-' . osfamily2shortname();
if (!file_exists("{$pkg_dir}/bin/go") || !file_exists("{$pkg_dir}/bin/xcaddy")) {
global $argv;
throw new WrongUsageException("FrankenPHP SAPI requires go-mod-frankenphp package, please install it first: {$argv[0]} install-pkg go-mod-frankenphp");
}
// frankenphp needs libxml2 libs
if (PHP_OS_FAMILY === 'Darwin' && !$this->getLib('libxml2')) {
throw new WrongUsageException('FrankenPHP SAPI for macOS requires libxml2 library, please include `xml` extension in your build.');
}
}
}

/**
* Generate micro extension test php code.
*/
Expand Down
83 changes: 0 additions & 83 deletions src/SPC/builder/traits/UnixGoCheckTrait.php

This file was deleted.

52 changes: 37 additions & 15 deletions src/SPC/builder/unix/UnixBuilderBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,19 @@ protected function sanityCheck(int $build_target): void
throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output));
}
}

// sanity check for frankenphp
if (($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
logger()->info('running frankenphp sanity check');
$frankenphp = BUILD_BIN_PATH . '/frankenphp';
if (!file_exists($frankenphp)) {
throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp);
}
[$ret, $output] = shell()->execWithResult("{$frankenphp} version");
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']');
}
}
}

/**
Expand Down Expand Up @@ -285,16 +298,19 @@ protected function patchPhpScripts(): void
*/
protected function buildFrankenphp(): void
{
$path = getenv('PATH');
$xcaddyPath = getenv('GOBIN') ?: (getenv('HOME') . '/go/bin');
if (!str_contains($path, $xcaddyPath)) {
$path = $path . ':' . $xcaddyPath;
}
$path = BUILD_BIN_PATH . ':' . $path;
f_putenv("PATH={$path}");
$os = match (PHP_OS_FAMILY) {
'Linux' => 'linux',
'Windows' => 'win',
'Darwin' => 'macos',
'BSD' => 'freebsd',
default => throw new RuntimeException('Unsupported OS: ' . PHP_OS_FAMILY),
};
$arch = arch2gnu(php_uname('m'));

// define executables for go and xcaddy
$go_exec = PKG_ROOT_PATH . "/go-mod-frankenphp-{$arch}-{$os}/bin/go";
$xcaddy_exec = PKG_ROOT_PATH . "/go-mod-frankenphp-{$arch}-{$os}/bin/xcaddy";

$brotliLibs = $this->getLib('brotli') !== null ? '-lbrotlienc -lbrotlidec -lbrotlicommon' : '';
$watcherLibs = $this->getLib('watcher') !== null ? '-lwatcher-c' : '';
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
Expand All @@ -303,7 +319,7 @@ protected function buildFrankenphp(): void
$xcaddyModules = '--with github.com/dunglas/frankenphp ' . $xcaddyModules;
}
if ($this->getLib('brotli') === null && str_contains($xcaddyModules, '--with github.com/dunglas/caddy-cbrotli')) {
logger()->warning('caddy-cbrotli module is enabled, but broli library is not built. Disabling caddy-cbrotli.');
logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.');
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
}
$lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : '';
Expand All @@ -313,21 +329,27 @@ protected function buildFrankenphp(): void
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
$libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion);
}
$debugFlags = $this->getOption('--with-debug') ? "'-w -s' " : '';
$debugFlags = $this->getOption('--with-debug') ? "'-w -s' " : '';

$config = (new SPCConfigUtil($this))->config($this->ext_list, $this->lib_list, with_dependencies: true);

$env = [
'PATH' => PKG_ROOT_PATH . "/go-mod-frankenphp-{$arch}-{$os}/bin:" . getenv('PATH'),
'GOROOT' => PKG_ROOT_PATH . "/go-mod-frankenphp-{$arch}-{$os}",
'GOBIN' => PKG_ROOT_PATH . "/go-mod-frankenphp-{$arch}-{$os}/bin",
'GOPATH' => PKG_ROOT_PATH . '/go',
'CGO_ENABLED' => '1',
'CGO_CFLAGS' => '$(php-config --includes) -I$(php-config --include-dir)/..',
'CGO_LDFLAGS' => '$(php-config --ldflags) -L' . BUILD_LIB_PATH . " $(php-config --libs) {$brotliLibs} {$watcherLibs} -lphp {$lrt}",
'CGO_CFLAGS' => $config['cflags'],
'CGO_LDFLAGS' => "{$config['ldflags']} {$config['libs']} {$lrt}",
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
'-ldflags \\"-linkmode=external -extldflags \'-pie\' '. $debugFlags .
'-ldflags \"-linkmode=external -extldflags \'-pie\' ' . $debugFlags .
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
"{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " .
"-tags=nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}",
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
shell()->cd(BUILD_BIN_PATH)
->setEnv($env)
->exec('xcaddy build --output frankenphp ' . $xcaddyModules);
->exec("{$xcaddy_exec} build --output frankenphp {$xcaddyModules}");
}
}
5 changes: 4 additions & 1 deletion src/SPC/command/BuildPHPCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ public function handle(): int
// validate libs and extensions
$builder->validateLibsAndExts();

// check some things before building all the things
$builder->checkBeforeBuildPHP($rule);

// clean builds and sources
if ($this->input->getOption('with-clean')) {
logger()->info('Cleaning source and previous build dir...');
Expand Down Expand Up @@ -316,7 +319,7 @@ private function parseRules(array $shared_extensions = []): int
$rule |= BUILD_TARGET_EMBED;
f_putenv('SPC_CMD_VAR_PHP_EMBED_TYPE=' . ($embed === 'static' ? 'static' : 'shared'));
}
$rule |= ($this->getOption('build-frankenphp') ? BUILD_TARGET_FRANKENPHP : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-frankenphp') ? (BUILD_TARGET_FRANKENPHP | BUILD_TARGET_EMBED) : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
return $rule;
}
Expand Down
6 changes: 0 additions & 6 deletions src/SPC/doctor/item/BSDToolCheckList.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ public function checkCliTools(): ?CheckResult
return CheckResult::ok();
}

#[AsCheckItem('if xcaddy is installed', limit_os: 'BSD')]
public function checkXcaddy(): ?CheckResult
{
return $this->checkGoAndXcaddy();
}

#[AsFixItem('build-tools-bsd')]
public function fixBuildTools(array $missing): bool
{
Expand Down
8 changes: 0 additions & 8 deletions src/SPC/doctor/item/LinuxToolCheckList.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace SPC\doctor\item;

use SPC\builder\linux\SystemUtil;
use SPC\builder\traits\UnixGoCheckTrait;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
Expand All @@ -15,7 +14,6 @@
class LinuxToolCheckList
{
use UnixSystemUtilTrait;
use UnixGoCheckTrait;

public const TOOLS_ALPINE = [
'make', 'bison', 'flex',
Expand Down Expand Up @@ -89,12 +87,6 @@ public function checkCliTools(): ?CheckResult
return CheckResult::ok();
}

#[AsCheckItem('if xcaddy is installed', limit_os: 'Linux')]
public function checkXcaddy(): ?CheckResult
{
return $this->checkGoAndXcaddy();
}

#[AsCheckItem('if cmake version >= 3.18', limit_os: 'Linux')]
public function checkCMakeVersion(): ?CheckResult
{
Expand Down
8 changes: 0 additions & 8 deletions src/SPC/doctor/item/MacOSToolCheckList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace SPC\doctor\item;

use SPC\builder\traits\UnixGoCheckTrait;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
Expand All @@ -14,7 +13,6 @@
class MacOSToolCheckList
{
use UnixSystemUtilTrait;
use UnixGoCheckTrait;

/** @var string[] MacOS 环境下编译依赖的命令 */
public const REQUIRED_COMMANDS = [
Expand All @@ -36,12 +34,6 @@ class MacOSToolCheckList
'glibtoolize',
];

#[AsCheckItem('if xcaddy is installed', limit_os: 'Darwin')]
public function checkXcaddy(): ?CheckResult
{
return $this->checkGoAndXcaddy();
}

#[AsCheckItem('if homebrew has installed', limit_os: 'Darwin', level: 998)]
public function checkBrew(): ?CheckResult
{
Expand Down
22 changes: 18 additions & 4 deletions src/SPC/store/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage;
use SPC\store\source\CustomSourceBase;

/**
Expand Down Expand Up @@ -385,10 +386,13 @@ public static function downloadPackage(string $name, ?array $pkg = null, bool $f
]);
break;
case 'custom': // Custom download method, like API-based download or other
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\store\source');
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
foreach ($classes as $class) {
if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) {
(new $class())->fetch($force);
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
$cls = new $class();
if (in_array($name, $cls->getSupportName())) {
(new $class())->fetch($name, $force, $pkg);
}
break;
}
}
Expand Down Expand Up @@ -708,7 +712,6 @@ private static function isAlreadyDownloaded(string $name, bool $force, int $down
}
}
// If lock file exists for current arch and glibc target, skip downloading

if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && isset($lock[$lock_name = self::getPreBuiltLockName($name)])) {
// lock name with env
if (
Expand All @@ -719,6 +722,17 @@ private static function isAlreadyDownloaded(string $name, bool $force, int $down
return true;
}
}

// If lock file exists, skip downloading for source mode
if (!$force && $download_as === SPC_DOWNLOAD_PACKAGE && isset($lock[$name])) {
if (
$lock[$name]['source_type'] === SPC_SOURCE_ARCHIVE && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename']) ||
$lock[$name]['source_type'] === SPC_SOURCE_GIT && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname'])
) {
logger()->notice("Package [{$name}] already downloaded: " . ($lock[$name]['filename'] ?? $lock[$name]['dirname']));
return true;
}
}
return false;
}
}
15 changes: 15 additions & 0 deletions src/SPC/store/PackageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage;

class PackageManager
{
Expand All @@ -32,6 +33,20 @@ public static function installPackage(string $pkg_name, ?array $config = null, b

// Download package
Downloader::downloadPackage($pkg_name, $config, $force);
if (Config::getPkg($pkg_name)['type'] === 'custom') {
// Custom extract function
$classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/pkg', 'SPC\store\pkg');
foreach ($classes as $class) {
if (is_a($class, CustomPackage::class, true) && $class !== CustomPackage::class) {
$cls = new $class();
if (in_array($pkg_name, $cls->getSupportName())) {
(new $class())->extract($pkg_name);
break;
}
}
}
return;
}
// After download, read lock file name
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
$source_type = $lock[$pkg_name]['source_type'];
Expand Down
Loading