Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
1468bb9
Add commit tests
crazywhalecc Jun 13, 2025
45ec0ce
Add checkout
crazywhalecc Jun 13, 2025
3a0d21e
Support multi-line
crazywhalecc Jun 13, 2025
fe455bf
Fix shared-extensions as optional
crazywhalecc Jun 13, 2025
6253b7a
Next pr, I won't run
crazywhalecc Jun 13, 2025
e5cd3ad
Next pr, I won't run
crazywhalecc Jun 13, 2025
0e88cdb
Add shared extension parser
crazywhalecc Jun 14, 2025
3a64fee
Change test strategy for commit tests
crazywhalecc Jun 18, 2025
68548cf
Wrap it to test test test
crazywhalecc Jun 18, 2025
2bfc8e9
Test test
crazywhalecc Jun 18, 2025
cb0a90d
Add source hash comparator & refactor download lock
crazywhalecc Jun 14, 2025
57b2278
Define env in phpunit.xml
crazywhalecc Jun 16, 2025
5cb107b
Test test
crazywhalecc Jun 18, 2025
7057a13
Trigger extension test
crazywhalecc Jun 18, 2025
7178308
Use new trigger test message
crazywhalecc Jun 18, 2025
c1870af
add frankenphp sapi
henderkes Jun 18, 2025
f64eb0d
build for bsd and macos too
henderkes Jun 18, 2025
c1e6832
cs fix
henderkes Jun 18, 2025
92338d4
don't bake the rpath in, otherwise we might run into issues when load…
henderkes Jun 18, 2025
c46f851
watcher...
henderkes Jun 18, 2025
abf3bfb
suggest watcher
henderkes Jun 18, 2025
dca43d6
nicer escaping
henderkes Jun 18, 2025
d635b10
specify system gcc to build xcaddy in spc-gnu-docker
henderkes Jun 18, 2025
d094824
--with github.com/dunglas/caddy-cbrotli requires brotli
henderkes Jun 18, 2025
e71f762
support building static frankenphp
henderkes Jun 18, 2025
f37c863
only needed on linux
henderkes Jun 18, 2025
d58534b
add support for frankenphp directory from file system, instead of pul…
henderkes Jun 18, 2025
82ee6f0
allow specifying if we want to build embed shared or static
henderkes Jun 18, 2025
a1e76d9
remove watcher suggestion
henderkes Jun 18, 2025
8c6a708
ah, the infamous arm64 bug with -fpic vs -fPIC
henderkes Jun 18, 2025
04cefda
Merge pull request #774 from crazywhalecc/fix/arm64-musl
henderkes Jun 18, 2025
b4168d0
Add test extensions as trigger
crazywhalecc Jun 18, 2025
f7a3f80
Add test extensions as trigger
crazywhalecc Jun 18, 2025
24e19de
Merge pull request #768 from crazywhalecc/ci/commit-tests
crazywhalecc Jun 18, 2025
f10ba86
add extension test for frankenphp
henderkes Jun 18, 2025
65b828c
embed version information
henderkes Jun 18, 2025
eee2ff6
don't embed minor version when loading libphp.so
henderkes Jun 18, 2025
ae56931
Remove go download from doctor
crazywhalecc Jun 18, 2025
8e2dffc
Add frankenphp sapi embed build at build command, not constant
crazywhalecc Jun 18, 2025
f709f3b
Add custom package downloader and extractor
crazywhalecc Jun 18, 2025
92284e9
Refactor go and frankenphp downloads and builds
crazywhalecc Jun 18, 2025
d6858e1
phpstan fix
crazywhalecc Jun 18, 2025
74b1dda
Fix test-extensions.php
crazywhalecc Jun 18, 2025
4ecaffd
Fix test-extensions.php
crazywhalecc Jun 18, 2025
becee5b
Use version instead of -v
crazywhalecc Jun 18, 2025
a76f49f
Remove libxml2 requirement for linux
crazywhalecc Jun 18, 2025
1a164fa
Merge pull request #775 from crazywhalecc/sapi/frankenphp-prerequisites
henderkes Jun 19, 2025
15979d4
fix double path
henderkes Jun 19, 2025
cb010d8
there's no documented functionality to download without building - xc…
henderkes Jun 19, 2025
b42409e
LD_LIBRARY_PATH for frankenphp sanity check
henderkes Jun 19, 2025
7dc3b7c
Merge remote-tracking branch 'origin/main' into sapi/frankenphp
crazywhalecc Jun 19, 2025
804468f
refactor common exec code out
henderkes Jun 19, 2025
16fccf8
Merge remote-tracking branch 'origin/sapi/frankenphp' into sapi/frank…
henderkes Jun 19, 2025
6e70f16
Merge remote-tracking branch 'origin/docs' into sapi/frankenphp
henderkes Jun 19, 2025
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
3 changes: 3 additions & 0 deletions config/env.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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/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
Expand Down
5 changes: 5 additions & 0 deletions src/SPC/builder/freebsd/BSDBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
$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(
Expand Down Expand Up @@ -143,6 +144,10 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
}
$this->buildEmbed();
}
if ($enableFrankenphp) {
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
}

public function testPHP(int $build_target = BUILD_TARGET_NONE)
Expand Down
33 changes: 19 additions & 14 deletions src/SPC/builder/linux/LinuxBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
$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
Expand All @@ -125,7 +126,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
]);

// 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();
Expand All @@ -137,10 +138,10 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
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 .
Expand All @@ -156,25 +157,29 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void

$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)
Expand Down
5 changes: 5 additions & 0 deletions src/SPC/builder/macos/MacOSBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
$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 ' : '';
Expand Down Expand Up @@ -180,6 +181,10 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
}
$this->buildEmbed();
}
if ($enableFrankenphp) {
logger()->info('building frankenphp');
$this->buildFrankenphp();
}
}

public function testPHP(int $build_target = BUILD_TARGET_NONE)
Expand Down
83 changes: 83 additions & 0 deletions src/SPC/builder/traits/UnixGoCheckTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace SPC\builder\traits;

use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException;
use SPC\store\Downloader;
use SPC\store\FileSystem;

trait UnixGoCheckTrait
{
private function checkGoAndXcaddy(): ?CheckResult
{
$paths = explode(PATH_SEPARATOR, getenv('PATH'));
$goroot = getenv('GOROOT') ?: '/usr/local/go';
$goBin = "{$goroot}/bin";
$paths[] = $goBin;
if ($this->findCommand('go', $paths) === null) {
$this->installGo();
}

$gobin = getenv('GOBIN') ?: (getenv('HOME') . '/go/bin');
putenv("GOBIN={$gobin}");

$paths[] = $gobin;

if ($this->findCommand('xcaddy', $paths) === null) {
shell(true)->exec('go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
}

return CheckResult::ok();
}

private function installGo(): bool
{
$prefix = '';
if (get_current_user() !== 'root') {
$prefix = 'sudo ';
logger()->warning('Current user is not root, using sudo for running command');
}

$arch = php_uname('m');
$go_arch = match ($arch) {
'x86_64' => 'amd64',
'aarch64' => 'arm64',
default => $arch
};
$os = strtolower(PHP_OS_FAMILY);

$go_version = '1.24.4';
$go_filename = "go{$go_version}.{$os}-{$go_arch}.tar.gz";
$go_url = "https://go.dev/dl/{$go_filename}";

logger()->info("Downloading Go {$go_version} for {$go_arch}");

try {
// Download Go binary
Downloader::downloadFile('go', $go_url, $go_filename);

// Extract the tarball
FileSystem::extractSource('go', SPC_SOURCE_ARCHIVE, DOWNLOAD_PATH . "/{$go_filename}");

// Move to /usr/local/go
logger()->info('Installing Go to /usr/local/go');
shell()->exec("{$prefix}rm -rf /usr/local/go");
shell()->exec("{$prefix}mv " . SOURCE_PATH . '/go /usr/local/');

if (!str_contains(getenv('PATH'), '/usr/local/go/bin')) {
logger()->info('Adding Go to PATH');
shell()->exec("{$prefix}echo 'export PATH=\$PATH:/usr/local/go/bin' >> /etc/profile");
putenv('PATH=' . getenv('PATH') . ':/usr/local/go/bin');
}

logger()->info('Go has been installed successfully');
return true;
} catch (RuntimeException $e) {
logger()->error('Failed to install Go: ' . $e->getMessage());
return false;
}
}
}
32 changes: 32 additions & 0 deletions src/SPC/builder/unix/UnixBuilderBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,36 @@ protected function patchPhpScripts(): void
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
}
}

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}");

$brotliLibs = $this->getLib('brotli') !== null ? '-lbrotlienc -lbrotlidec -lbrotlicommon' : '';
$watcherLibs = $this->getLib('brotli') !== null ? '-lwatcher-c' : '';
$nobrotli = $this->getLib('brotli') === null ? ',nobrotli' : '';
$nowatcher = $this->getLib('watcher') === null ? ',nowatcher' : '';

$env = [
'CGO_ENABLED' => '1',
'CGO_CFLAGS' => '$(php-config --includes) -I$(php-config --include-dir)/..',
'CGO_LDFLAGS' => "$(php-config --ldflags) $(php-config --libs) {$brotliLibs} {$watcherLibs} -lphp",
'XCADDY_GO_BUILD_FLAGS' => "-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" . $nobrotli . $nowatcher,
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
];
shell()->cd(BUILD_BIN_PATH)
->setEnv($env)
->exec(
'xcaddy build ' .
'--output frankenphp ' .
'--with github.com/dunglas/frankenphp/caddy ' .
getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES')
);
}
}
5 changes: 4 additions & 1 deletion src/SPC/command/BuildPHPCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public function configure(): void
$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-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');
Expand Down Expand Up @@ -83,7 +84,8 @@ public function handle(): int
$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) {
Expand Down Expand Up @@ -304,6 +306,7 @@ private function parseRules(array $shared_extensions = []): int
$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);
$rule |= ($this->getOption('build-frankenphp') ? BUILD_TARGET_FRANKENPHP : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
return $rule;
}
Expand Down
6 changes: 6 additions & 0 deletions src/SPC/doctor/item/BSDToolCheckList.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ 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: 8 additions & 0 deletions src/SPC/doctor/item/LinuxToolCheckList.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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 @@ -14,6 +15,7 @@
class LinuxToolCheckList
{
use UnixSystemUtilTrait;
use UnixGoCheckTrait;

public const TOOLS_ALPINE = [
'make', 'bison', 'flex',
Expand Down Expand Up @@ -87,6 +89,12 @@ 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: 8 additions & 0 deletions src/SPC/doctor/item/MacOSToolCheckList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace SPC\doctor\item;

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

/** @var string[] MacOS 环境下编译依赖的命令 */
public const REQUIRED_COMMANDS = [
Expand All @@ -34,6 +36,12 @@ 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
3 changes: 2 additions & 1 deletion src/globals/defines.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
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 = BUILD_TARGET_EMBED | 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
Expand Down