mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-19 13:24:51 +08:00
Merge pull request #772 from crazywhalecc/sapi/frankenphp
Sapi/frankenphp
This commit is contained in:
commit
3af40a66dd
12
.github/pull_request_template.md
vendored
12
.github/pull_request_template.md
vendored
@ -7,7 +7,11 @@
|
||||
> If your PR involves the changes mentioned below and completed the action, please tick the corresponding option.
|
||||
> If a modification is not involved, please skip it directly.
|
||||
|
||||
- [ ] If you modified `*.php`, run `composer cs-fix` at local machine.
|
||||
- [ ] If it's an extension or dependency update, make sure adding related extensions in `src/global/test-extensions.php`.
|
||||
- [ ] If you changed the behavior of static-php-cli, update docs in `./docs/`.
|
||||
- [ ] If you updated `config/xxx.json` content, run `bin/spc dev:sort-config xxx`.
|
||||
- If you modified `*.php` or `*.json`, run them locally to ensure your changes are valid:
|
||||
- [ ] `PHP_CS_FIXER_IGNORE_ENV=1 composer cs-fix`
|
||||
- [ ] `composer analyse`
|
||||
- [ ] `composer test`
|
||||
- [ ] `bin/spc dev:sort-config`
|
||||
- If it's an extension or dependency update, please ensure the following:
|
||||
- [ ] Add your test combination to `src/globals/test-extensions.php`.
|
||||
- [ ] If adding new or fixing bugs, add commit message containing `extension test` or `test extensions` to trigger full test suite.
|
||||
|
||||
8
.github/workflows/ext-matrix-tests.yml
vendored
8
.github/workflows/ext-matrix-tests.yml
vendored
@ -1,16 +1,14 @@
|
||||
name: "Extension matrix tests"
|
||||
name: "Extension Matrix Tests"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- '.github/workflows/ext-matrix-tests.yml'
|
||||
push:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "${{ matrix.extension }} (PHP ${{ matrix.php-version }} on ${{ matrix.operating-system }})"
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
if: contains(github.event.head_commit.message, 'extension test') || contains(github.event.head_commit.message, 'test extensions')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
@ -94,7 +94,7 @@ ENV PATH="/app/bin:/cmake/bin:$PATH"
|
||||
ENV SPC_LIBC=glibc
|
||||
|
||||
ADD ./config/env.ini /app/config/env.ini
|
||||
RUN bin/spc doctor --auto-fix --debug
|
||||
RUN CC=gcc bin/spc doctor --auto-fix --debug
|
||||
|
||||
RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \
|
||||
tar -zxvf make.tgz && \
|
||||
|
||||
@ -42,6 +42,9 @@ SPC_CONCURRENCY=${CPU_COUNT}
|
||||
SPC_SKIP_PHP_VERSION_CHECK="no"
|
||||
; Ignore some check item for bin/spc doctor command, comma separated (e.g. SPC_SKIP_DOCTOR_CHECK_ITEMS="if homebrew has installed")
|
||||
SPC_SKIP_DOCTOR_CHECK_ITEMS=""
|
||||
; extra modules that xcaddy will include in the FrankenPHP build
|
||||
SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/frankenphp/caddy --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli"
|
||||
|
||||
; EXTENSION_DIR where the built php will look for extension when a .ini instructs to load them
|
||||
; only useful for builds targeting not pure-static linking
|
||||
; default paths
|
||||
@ -68,8 +71,8 @@ CXX=${SPC_LINUX_DEFAULT_CXX}
|
||||
AR=${SPC_LINUX_DEFAULT_AR}
|
||||
LD=ld.gold
|
||||
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
|
||||
SPC_DEFAULT_C_FLAGS="-fpic -Os"
|
||||
SPC_DEFAULT_CXX_FLAGS="-fpic -Os"
|
||||
SPC_DEFAULT_C_FLAGS="-fPIC -Os"
|
||||
SPC_DEFAULT_CXX_FLAGS="-fPIC -Os"
|
||||
; extra libs for building php executable, used in `make` command for building php (this value may changed by extension build process, space separated)
|
||||
SPC_EXTRA_LIBS=
|
||||
; upx executable path
|
||||
@ -89,7 +92,7 @@ SPC_CMD_VAR_PHP_EMBED_TYPE="static"
|
||||
|
||||
; *** default build vars for building php ***
|
||||
; CFLAGS for configuring php
|
||||
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fpie"
|
||||
SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS="${SPC_DEFAULT_C_FLAGS} -fPIE"
|
||||
; CPPFLAGS for configuring php
|
||||
SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS="-I${BUILD_INCLUDE_PATH}"
|
||||
; LDFLAGS for configuring php
|
||||
@ -97,7 +100,7 @@ SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS="-L${BUILD_LIB_PATH}"
|
||||
; LIBS for configuring php
|
||||
SPC_CMD_VAR_PHP_CONFIGURE_LIBS="-ldl -lpthread -lm"
|
||||
; EXTRA_CFLAGS for `make` php
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fpie ${SPC_DEFAULT_C_FLAGS}"
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE ${SPC_DEFAULT_C_FLAGS}"
|
||||
; EXTRA_LIBS for `make` php
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm"
|
||||
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,9 @@ build-options:
|
||||
# Set micro SAPI as win32 mode, without this, micro SAPI will be compiled as a console application (only for Windows, default: false)
|
||||
enable-micro-win32: false
|
||||
|
||||
# Build options for shared extensions (list or comma-separated are both accepted)
|
||||
shared-extensions: [ ]
|
||||
|
||||
# Download options
|
||||
download-options:
|
||||
# Use custom url for specified sources, format: "{source-name}:{url}" (e.g. "php-src:https://example.com/php-8.4.0.tar.gz")
|
||||
|
||||
@ -401,6 +401,9 @@ abstract class BuilderBase
|
||||
if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
|
||||
$ls[] = 'embed';
|
||||
}
|
||||
if (($type & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP) {
|
||||
$ls[] = 'frankenphp';
|
||||
}
|
||||
return implode(', ', $ls);
|
||||
}
|
||||
|
||||
@ -507,6 +510,26 @@ abstract class BuilderBase
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@ -96,6 +96,7 @@ class BSDBuilder extends UnixBuilderBase
|
||||
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(
|
||||
@ -143,6 +144,10 @@ class BSDBuilder extends UnixBuilderBase
|
||||
}
|
||||
$this->buildEmbed();
|
||||
}
|
||||
if ($enableFrankenphp) {
|
||||
logger()->info('building frankenphp');
|
||||
$this->buildFrankenphp();
|
||||
}
|
||||
}
|
||||
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||
|
||||
@ -110,10 +110,11 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
$config_file_scan_dir = $this->getOption('with-config-file-scan-dir', false) ?
|
||||
('--with-config-file-scan-dir=' . $this->getOption('with-config-file-scan-dir') . ' ') : '';
|
||||
|
||||
$enable_cli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
|
||||
$enable_fpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||
$enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enable_embed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
|
||||
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||
|
||||
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
|
||||
// prepare build php envs
|
||||
@ -125,7 +126,7 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
]);
|
||||
|
||||
// process micro upx patch if micro sapi enabled
|
||||
if ($enable_micro) {
|
||||
if ($enableMicro) {
|
||||
if (version_compare($this->getMicroVersion(), '0.2.0') < 0) {
|
||||
// for phpmicro 0.1.x
|
||||
$this->processMicroUPXLegacy();
|
||||
@ -137,10 +138,10 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
shell()->cd(SOURCE_PATH . '/php-src')
|
||||
->exec(
|
||||
getenv('SPC_CMD_PREFIX_PHP_CONFIGURE') . ' ' .
|
||||
($enable_cli ? '--enable-cli ' : '--disable-cli ') .
|
||||
($enable_fpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
|
||||
($enable_embed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
||||
($enable_micro ? '--enable-micro=all-static ' : '--disable-micro ') .
|
||||
($enableCli ? '--enable-cli ' : '--disable-cli ') .
|
||||
($enableFpm ? '--enable-fpm ' . ($this->getLib('libacl') !== null ? '--with-fpm-acl ' : '') : '--disable-fpm ') .
|
||||
($enableEmbed ? "--enable-embed={$embed_type} " : '--disable-embed ') .
|
||||
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
|
||||
$config_file_path .
|
||||
$config_file_scan_dir .
|
||||
$disable_jit .
|
||||
@ -156,25 +157,29 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
|
||||
$this->cleanMake();
|
||||
|
||||
if ($enable_cli) {
|
||||
if ($enableCli) {
|
||||
logger()->info('building cli');
|
||||
$this->buildCli();
|
||||
}
|
||||
if ($enable_fpm) {
|
||||
if ($enableFpm) {
|
||||
logger()->info('building fpm');
|
||||
$this->buildFpm();
|
||||
}
|
||||
if ($enable_micro) {
|
||||
if ($enableMicro) {
|
||||
logger()->info('building micro');
|
||||
$this->buildMicro();
|
||||
}
|
||||
if ($enable_embed) {
|
||||
if ($enableEmbed) {
|
||||
logger()->info('building embed');
|
||||
if ($enable_micro) {
|
||||
if ($enableMicro) {
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
|
||||
}
|
||||
$this->buildEmbed();
|
||||
}
|
||||
if ($enableFrankenphp) {
|
||||
logger()->info('building frankenphp');
|
||||
$this->buildFrankenphp();
|
||||
}
|
||||
}
|
||||
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||
|
||||
@ -122,6 +122,7 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
|
||||
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
|
||||
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
|
||||
$enableFrankenphp = ($build_target & BUILD_TARGET_FRANKENPHP) === BUILD_TARGET_FRANKENPHP;
|
||||
|
||||
// prepare build php envs
|
||||
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
|
||||
@ -180,6 +181,10 @@ class MacOSBuilder extends UnixBuilderBase
|
||||
}
|
||||
$this->buildEmbed();
|
||||
}
|
||||
if ($enableFrankenphp) {
|
||||
logger()->info('building frankenphp');
|
||||
$this->buildFrankenphp();
|
||||
}
|
||||
}
|
||||
|
||||
public function testPHP(int $build_target = BUILD_TARGET_NONE)
|
||||
|
||||
@ -12,6 +12,7 @@ use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\DependencyUtil;
|
||||
use SPC\util\SPCConfigUtil;
|
||||
@ -218,6 +219,21 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
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()
|
||||
->setEnv(['LD_LIBRARY_PATH' => BUILD_LIB_PATH])
|
||||
->execWithResult("{$frankenphp} version");
|
||||
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
|
||||
throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,4 +293,64 @@ abstract class UnixBuilderBase extends BuilderBase
|
||||
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WrongUsageException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function buildFrankenphp(): void
|
||||
{
|
||||
$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
|
||||
$xcaddy_exec = PKG_ROOT_PATH . "/go-mod-frankenphp-{$arch}-{$os}/bin/xcaddy";
|
||||
|
||||
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
|
||||
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';
|
||||
$xcaddyModules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES');
|
||||
// make it possible to build from a different frankenphp directory!
|
||||
if (!str_contains($xcaddyModules, '--with github.com/dunglas/frankenphp')) {
|
||||
$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 brotli library is not built. Disabling caddy-cbrotli.');
|
||||
$xcaddyModules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddyModules);
|
||||
}
|
||||
$lrt = PHP_OS_FAMILY === 'Linux' ? '-lrt' : '';
|
||||
$releaseInfo = json_decode(Downloader::curlExec('https://api.github.com/repos/php/frankenphp/releases/latest'), true);
|
||||
$frankenPhpVersion = $releaseInfo['tag_name'];
|
||||
$libphpVersion = $this->getPHPVersion();
|
||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
|
||||
$libphpVersion = preg_replace('/\.\d$/', '', $libphpVersion);
|
||||
}
|
||||
$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' => $config['cflags'],
|
||||
'CGO_LDFLAGS' => "{$config['ldflags']} {$config['libs']} {$lrt}",
|
||||
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
|
||||
'-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_exec} build --output frankenphp {$xcaddyModules}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,8 @@ class BuildPHPCommand extends BuildCommand
|
||||
$this->addOption('build-micro', null, null, 'Build micro SAPI');
|
||||
$this->addOption('build-cli', null, null, 'Build cli SAPI');
|
||||
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
|
||||
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
|
||||
$this->addOption('build-embed', null, InputOption::VALUE_OPTIONAL, 'Build embed SAPI (not available on Windows)');
|
||||
$this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
|
||||
$this->addOption('build-all', null, null, 'Build all SAPI');
|
||||
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
|
||||
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');
|
||||
@ -83,7 +84,8 @@ class BuildPHPCommand extends BuildCommand
|
||||
$this->output->writeln("<comment>\t--build-micro\tBuild phpmicro SAPI</comment>");
|
||||
$this->output->writeln("<comment>\t--build-fpm\tBuild php-fpm SAPI</comment>");
|
||||
$this->output->writeln("<comment>\t--build-embed\tBuild embed SAPI/libphp</comment>");
|
||||
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed</comment>");
|
||||
$this->output->writeln("<comment>\t--build-frankenphp\tBuild FrankenPHP SAPI/libphp</comment>");
|
||||
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed, frankenphp</comment>");
|
||||
return static::FAILURE;
|
||||
}
|
||||
if ($rule === BUILD_TARGET_ALL) {
|
||||
@ -194,6 +196,9 @@ class BuildPHPCommand extends BuildCommand
|
||||
// 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...');
|
||||
@ -303,7 +308,18 @@ class BuildPHPCommand extends BuildCommand
|
||||
$rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
|
||||
$rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
|
||||
$rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
|
||||
$rule |= ($this->getOption('build-embed') || !empty($shared_extensions) ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE);
|
||||
$embed = $this->getOption('build-embed');
|
||||
if (!$embed && !empty($shared_extensions)) {
|
||||
$embed = true;
|
||||
}
|
||||
if ($embed) {
|
||||
if ($embed === true) {
|
||||
$embed = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
|
||||
}
|
||||
$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_EMBED) : BUILD_TARGET_NONE);
|
||||
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
|
||||
return $rule;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ class CraftCommand extends BaseCommand
|
||||
}
|
||||
|
||||
$static_extensions = implode(',', $craft['extensions']);
|
||||
$shared_extensions = implode(',', $craft['shared-extensions']);
|
||||
$shared_extensions = implode(',', $craft['shared-extensions'] ?? []);
|
||||
$libs = implode(',', $craft['libs']);
|
||||
|
||||
// init log
|
||||
|
||||
@ -9,6 +9,7 @@ use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\pkg\CustomPackage;
|
||||
use SPC\store\source\CustomSourceBase;
|
||||
|
||||
/**
|
||||
@ -358,10 +359,13 @@ class Downloader
|
||||
]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -646,6 +650,17 @@ class Downloader
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ namespace SPC\store;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
use SPC\store\pkg\CustomPackage;
|
||||
|
||||
class PackageManager
|
||||
{
|
||||
@ -32,6 +33,20 @@ class PackageManager
|
||||
|
||||
// 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 = LockFile::get($pkg_name);
|
||||
$source_type = $lock['source_type'];
|
||||
|
||||
17
src/SPC/store/pkg/CustomPackage.php
Normal file
17
src/SPC/store/pkg/CustomPackage.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store\pkg;
|
||||
|
||||
abstract class CustomPackage
|
||||
{
|
||||
abstract public function getSupportName(): array;
|
||||
|
||||
abstract public function fetch(string $name, bool $force = false, ?array $config = null): void;
|
||||
|
||||
public function extract(string $name): void
|
||||
{
|
||||
throw new \RuntimeException("Extract method not implemented for package: {$name}");
|
||||
}
|
||||
}
|
||||
64
src/SPC/store/pkg/GoModFrankenphp.php
Normal file
64
src/SPC/store/pkg/GoModFrankenphp.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store\pkg;
|
||||
|
||||
use SPC\store\Downloader;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class GoModFrankenphp extends CustomPackage
|
||||
{
|
||||
public function getSupportName(): array
|
||||
{
|
||||
return [
|
||||
'go-mod-frankenphp-x86_64-linux',
|
||||
'go-mod-frankenphp-x86_64-macos',
|
||||
'go-mod-frankenphp-aarch64-linux',
|
||||
'go-mod-frankenphp-aarch64-macos',
|
||||
];
|
||||
}
|
||||
|
||||
public function fetch(string $name, bool $force = false, ?array $config = null): void
|
||||
{
|
||||
$arch = match (explode('-', $name)[3]) {
|
||||
'x86_64' => 'amd64',
|
||||
'aarch64' => 'arm64',
|
||||
default => throw new \InvalidArgumentException('Unsupported architecture: ' . $name),
|
||||
};
|
||||
$os = match (explode('-', $name)[4]) {
|
||||
'linux' => 'linux',
|
||||
'macos' => 'darwin',
|
||||
default => throw new \InvalidArgumentException('Unsupported OS: ' . $name),
|
||||
};
|
||||
$go_version = '1.24.4';
|
||||
$config = [
|
||||
'type' => 'url',
|
||||
'url' => "https://go.dev/dl/go{$go_version}.{$os}-{$arch}.tar.gz",
|
||||
];
|
||||
Downloader::downloadPackage($name, $config, $force);
|
||||
}
|
||||
|
||||
public function extract(string $name): void
|
||||
{
|
||||
$pkgroot = PKG_ROOT_PATH;
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true);
|
||||
$source_type = $lock[$name]['source_type'];
|
||||
$filename = DOWNLOAD_PATH . '/' . ($lock[$name]['filename'] ?? $lock[$name]['dirname']);
|
||||
$extract = $lock[$name]['move_path'] === null ? "{$pkgroot}/{$name}" : $lock[$name]['move_path'];
|
||||
|
||||
FileSystem::extractPackage($name, $source_type, $filename, $extract);
|
||||
|
||||
// install xcaddy
|
||||
$go_exec = PKG_ROOT_PATH . "{$pkgroot}/{$name}/bin/go";
|
||||
// $xcaddy_exec = PKG_ROOT_PATH . "$pkgroot/$name/bin/xcaddy";
|
||||
shell()
|
||||
->appendEnv([
|
||||
'PATH' => "{$pkgroot}/{$name}/bin:" . getenv('PATH'),
|
||||
'GOROOT' => "{$pkgroot}/{$name}",
|
||||
'GOBIN' => "{$pkgroot}/{$name}/bin",
|
||||
'GOPATH' => "{$pkgroot}/go",
|
||||
])
|
||||
->exec("{$go_exec} install github.com/caddyserver/xcaddy/cmd/xcaddy@latest");
|
||||
}
|
||||
}
|
||||
@ -164,6 +164,12 @@ class ConfigValidator
|
||||
if (is_string($craft['extensions'])) {
|
||||
$craft['extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['extensions'])));
|
||||
}
|
||||
if (!isset($craft['shared-extensions'])) {
|
||||
$craft['shared-extensions'] = [];
|
||||
}
|
||||
if (is_string($craft['shared-extensions'] ?? [])) {
|
||||
$craft['shared-extensions'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['shared-extensions'])));
|
||||
}
|
||||
// check libs
|
||||
if (isset($craft['libs']) && is_string($craft['libs'])) {
|
||||
$craft['libs'] = array_filter(array_map(fn ($x) => trim($x), explode(',', $craft['libs'])));
|
||||
|
||||
@ -44,14 +44,7 @@ class UnixShell
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
logger()->info(ConsoleColor::yellow('[EXEC] ') . ConsoleColor::green($cmd));
|
||||
logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']);
|
||||
$env_str = $this->getEnvString();
|
||||
if (!empty($env_str)) {
|
||||
$cmd = "{$env_str} {$cmd}";
|
||||
}
|
||||
if ($this->cd !== null) {
|
||||
$cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd;
|
||||
}
|
||||
$cmd = $this->getExecString($cmd);
|
||||
if (!$this->debug) {
|
||||
$cmd .= ' 1>/dev/null 2>&1';
|
||||
}
|
||||
@ -99,10 +92,7 @@ class UnixShell
|
||||
/* @phpstan-ignore-next-line */
|
||||
logger()->debug(ConsoleColor::blue('[EXEC] ') . ConsoleColor::gray($cmd));
|
||||
}
|
||||
logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']);
|
||||
if ($this->cd !== null) {
|
||||
$cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd;
|
||||
}
|
||||
$cmd = $this->getExecString($cmd);
|
||||
exec($cmd, $out, $code);
|
||||
return [$code, $out];
|
||||
}
|
||||
@ -126,4 +116,21 @@ class UnixShell
|
||||
}
|
||||
return trim($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
* @return string
|
||||
*/
|
||||
private function getExecString(string $cmd): string
|
||||
{
|
||||
logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']);
|
||||
$env_str = $this->getEnvString();
|
||||
if (!empty($env_str)) {
|
||||
$cmd = "{$env_str} {$cmd}";
|
||||
}
|
||||
if ($this->cd !== null) {
|
||||
$cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd;
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,8 @@ const BUILD_TARGET_CLI = 1; // build cli
|
||||
const BUILD_TARGET_MICRO = 2; // build micro
|
||||
const BUILD_TARGET_FPM = 4; // build fpm
|
||||
const BUILD_TARGET_EMBED = 8; // build embed
|
||||
const BUILD_TARGET_ALL = 15; // build all
|
||||
const BUILD_TARGET_FRANKENPHP = 16; // build frankenphp
|
||||
const BUILD_TARGET_ALL = BUILD_TARGET_CLI | BUILD_TARGET_MICRO | BUILD_TARGET_FPM | BUILD_TARGET_EMBED | BUILD_TARGET_FRANKENPHP; // build all
|
||||
|
||||
// doctor error fix policy
|
||||
const FIX_POLICY_DIE = 1; // die directly
|
||||
|
||||
@ -102,6 +102,17 @@ function osfamily2dir(): string
|
||||
};
|
||||
}
|
||||
|
||||
function osfamily2shortname(): string
|
||||
{
|
||||
return match (PHP_OS_FAMILY) {
|
||||
'Windows' => 'win',
|
||||
'Darwin' => 'macos',
|
||||
'Linux' => 'linux',
|
||||
'BSD' => 'bsd',
|
||||
default => throw new WrongUsageException('Not support os: ' . PHP_OS_FAMILY),
|
||||
};
|
||||
}
|
||||
|
||||
function shell(?bool $debug = null): UnixShell
|
||||
{
|
||||
/* @noinspection PhpUnhandledExceptionInspection */
|
||||
|
||||
@ -40,6 +40,9 @@ $no_strip = false;
|
||||
// compress with upx
|
||||
$upx = false;
|
||||
|
||||
// whether to test frankenphp build, only available for macos and linux
|
||||
$frankenphp = true;
|
||||
|
||||
// prefer downloading pre-built packages to speed up the build process
|
||||
$prefer_pre_built = false;
|
||||
|
||||
@ -208,7 +211,13 @@ switch ($argv[1] ?? null) {
|
||||
passthru($prefix . $build_cmd . ' --build-cli --build-micro', $retcode);
|
||||
break;
|
||||
case 'build_embed_cmd':
|
||||
passthru($prefix . $build_cmd . (str_starts_with($argv[2], 'windows-') ? ' --build-cli' : ' --build-embed'), $retcode);
|
||||
if ($frankenphp) {
|
||||
passthru("{$prefix}install-pkg go-mod-frankenphp --debug", $retcode);
|
||||
if ($retcode !== 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
passthru($prefix . $build_cmd . (str_starts_with($argv[2], 'windows-') ? ' --build-cli' : (' --build-embed' . ($frankenphp ? ' --build-frankenphp' : ''))), $retcode);
|
||||
break;
|
||||
case 'doctor_cmd':
|
||||
passthru($prefix . $doctor_cmd, $retcode);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user