mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-17 20:34:51 +08:00
Add frankenphp SAPI build support
This commit is contained in:
parent
82bf317911
commit
18434b68f6
84
composer.lock
generated
84
composer.lock
generated
@ -8,30 +8,30 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
"version": "v0.3.11",
|
||||
"version": "v0.3.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/prompts.git",
|
||||
"reference": "dd2a2ed95acacbcccd32fd98dee4c946ae7a7217"
|
||||
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/prompts/zipball/dd2a2ed95acacbcccd32fd98dee4c946ae7a7217",
|
||||
"reference": "dd2a2ed95acacbcccd32fd98dee4c946ae7a7217",
|
||||
"url": "https://api.github.com/repos/laravel/prompts/zipball/4861ded9003b7f8a158176a0b7666f74ee761be8",
|
||||
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": "^2.2",
|
||||
"ext-mbstring": "*",
|
||||
"php": "^8.1",
|
||||
"symfony/console": "^6.2|^7.0"
|
||||
"symfony/console": "^6.2|^7.0|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"illuminate/console": ">=10.17.0 <10.25.0",
|
||||
"laravel/framework": ">=10.17.0 <10.25.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/collections": "^10.0|^11.0|^12.0",
|
||||
"illuminate/collections": "^10.0|^11.0|^12.0|^13.0",
|
||||
"mockery/mockery": "^1.5",
|
||||
"pestphp/pest": "^2.3|^3.4|^4.0",
|
||||
"phpstan/phpstan": "^1.12.28",
|
||||
@ -61,33 +61,33 @@
|
||||
"description": "Add beautiful and user-friendly forms to your command-line applications.",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/prompts/issues",
|
||||
"source": "https://github.com/laravel/prompts/tree/v0.3.11"
|
||||
"source": "https://github.com/laravel/prompts/tree/v0.3.12"
|
||||
},
|
||||
"time": "2026-01-27T02:55:06+00:00"
|
||||
"time": "2026-02-03T06:57:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.8",
|
||||
"version": "v2.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/serializable-closure.git",
|
||||
"reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b"
|
||||
"reference": "8f631589ab07b7b52fead814965f5a800459cb3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b",
|
||||
"reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/8f631589ab07b7b52fead814965f5a800459cb3e",
|
||||
"reference": "8f631589ab07b7b52fead814965f5a800459cb3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
|
||||
"nesbot/carbon": "^2.67|^3.0",
|
||||
"pestphp/pest": "^2.36|^3.0|^4.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"symfony/var-dumper": "^6.2.0|^7.0.0"
|
||||
"symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -124,20 +124,20 @@
|
||||
"issues": "https://github.com/laravel/serializable-closure/issues",
|
||||
"source": "https://github.com/laravel/serializable-closure"
|
||||
},
|
||||
"time": "2026-01-08T16:22:46+00:00"
|
||||
"time": "2026-02-03T06:55:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/php-generator",
|
||||
"version": "v4.2.0",
|
||||
"version": "v4.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/php-generator.git",
|
||||
"reference": "4707546a1f11badd72f5d82af4f8a6bc64bd56ac"
|
||||
"reference": "52aff4d9b12f20ca9f3e31a559b646d2fd21dd61"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/php-generator/zipball/4707546a1f11badd72f5d82af4f8a6bc64bd56ac",
|
||||
"reference": "4707546a1f11badd72f5d82af4f8a6bc64bd56ac",
|
||||
"url": "https://api.github.com/repos/nette/php-generator/zipball/52aff4d9b12f20ca9f3e31a559b646d2fd21dd61",
|
||||
"reference": "52aff4d9b12f20ca9f3e31a559b646d2fd21dd61",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -146,9 +146,9 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"jetbrains/phpstorm-attributes": "^1.2",
|
||||
"nette/tester": "^2.4",
|
||||
"nette/tester": "^2.6",
|
||||
"nikic/php-parser": "^5.0",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"phpstan/phpstan": "^2.0@stable",
|
||||
"tracy/tracy": "^2.8"
|
||||
},
|
||||
"suggest": {
|
||||
@ -194,22 +194,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/php-generator/issues",
|
||||
"source": "https://github.com/nette/php-generator/tree/v4.2.0"
|
||||
"source": "https://github.com/nette/php-generator/tree/v4.2.1"
|
||||
},
|
||||
"time": "2025-08-06T18:24:31+00:00"
|
||||
"time": "2026-02-09T05:43:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v4.1.1",
|
||||
"version": "v4.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72"
|
||||
"reference": "f76b5dc3d6c6d3043c8d937df2698515b99cbaf5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/f76b5dc3d6c6d3043c8d937df2698515b99cbaf5",
|
||||
"reference": "f76b5dc3d6c6d3043c8d937df2698515b99cbaf5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -222,7 +222,7 @@
|
||||
"require-dev": {
|
||||
"jetbrains/phpstorm-attributes": "^1.2",
|
||||
"nette/tester": "^2.5",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"phpstan/phpstan": "^2.0@stable",
|
||||
"tracy/tracy": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
@ -283,9 +283,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v4.1.1"
|
||||
"source": "https://github.com/nette/utils/tree/v4.1.2"
|
||||
},
|
||||
"time": "2025-12-22T12:14:32+00:00"
|
||||
"time": "2026-02-03T17:21:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-di/invoker",
|
||||
@ -2217,7 +2217,7 @@
|
||||
},
|
||||
{
|
||||
"name": "captainhook/captainhook-phar",
|
||||
"version": "5.27.5",
|
||||
"version": "5.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/captainhook-git/captainhook-phar.git",
|
||||
@ -2271,7 +2271,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/captainhook-git/captainhook/issues",
|
||||
"source": "https://github.com/captainhook-git/captainhook-phar/tree/5.27.5"
|
||||
"source": "https://github.com/captainhook-git/captainhook-phar/tree/5.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2660,29 +2660,29 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/deprecations.git",
|
||||
"reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38"
|
||||
"reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
|
||||
"reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
|
||||
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
|
||||
"reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<=7.5 || >=13"
|
||||
"phpunit/phpunit": "<=7.5 || >=14"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9 || ^12 || ^13",
|
||||
"phpstan/phpstan": "1.4.10 || 2.1.11",
|
||||
"doctrine/coding-standard": "^9 || ^12 || ^14",
|
||||
"phpstan/phpstan": "1.4.10 || 2.1.30",
|
||||
"phpstan/phpstan-phpunit": "^1.0 || ^2",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
},
|
||||
"suggest": {
|
||||
@ -2702,9 +2702,9 @@
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/deprecations/issues",
|
||||
"source": "https://github.com/doctrine/deprecations/tree/1.1.5"
|
||||
"source": "https://github.com/doctrine/deprecations/tree/1.1.6"
|
||||
},
|
||||
"time": "2025-04-07T20:06:18+00:00"
|
||||
"time": "2026-02-07T07:09:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "evenement/evenement",
|
||||
|
||||
4
config/pkg/ext/ext-phar.yml
Normal file
4
config/pkg/ext/ext-phar.yml
Normal file
@ -0,0 +1,4 @@
|
||||
ext-phar:
|
||||
type: php-extension
|
||||
depends:
|
||||
- zlib
|
||||
18
config/pkg/target/frankenphp.yml
Normal file
18
config/pkg/target/frankenphp.yml
Normal file
@ -0,0 +1,18 @@
|
||||
frankenphp:
|
||||
type: target
|
||||
artifact:
|
||||
source:
|
||||
type: ghtar
|
||||
repo: php/frankenphp
|
||||
prefer-stable: true
|
||||
metadata:
|
||||
license-files: [LICENSE]
|
||||
license: MIT
|
||||
depends:
|
||||
- php-embed
|
||||
- go-xcaddy
|
||||
suggests@unix:
|
||||
- brotli
|
||||
- watcher
|
||||
static-bins@unix:
|
||||
- frankenphp
|
||||
@ -1,16 +1,3 @@
|
||||
frankenphp:
|
||||
type: virtual-target
|
||||
artifact:
|
||||
source:
|
||||
type: ghtar
|
||||
repo: php/frankenphp
|
||||
prefer-stable: true
|
||||
metadata:
|
||||
license-files: [LICENSE]
|
||||
license: MIT
|
||||
depends:
|
||||
- php-embed
|
||||
- go-xcaddy
|
||||
php:
|
||||
type: target
|
||||
artifact: php-src
|
||||
@ -32,6 +19,8 @@ php-fpm:
|
||||
type: virtual-target
|
||||
depends:
|
||||
- php
|
||||
suggests@linux:
|
||||
- libacl
|
||||
php-micro:
|
||||
type: virtual-target
|
||||
artifact:
|
||||
|
||||
29
src/Package/Extension/phar.php
Normal file
29
src/Package/Extension/phar.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Package\Extension;
|
||||
|
||||
use Package\Target\php;
|
||||
use StaticPHP\Attribute\Package\AfterStage;
|
||||
use StaticPHP\Attribute\Package\BeforeStage;
|
||||
use StaticPHP\Attribute\Package\Extension;
|
||||
use StaticPHP\Attribute\PatchDescription;
|
||||
use StaticPHP\Util\SourcePatcher;
|
||||
|
||||
#[Extension('phar')]
|
||||
class phar
|
||||
{
|
||||
#[BeforeStage('php', [php::class, 'makeMicroForUnix'], 'ext-phar')]
|
||||
#[PatchDescription('Patch phar extension for micro SAPI to support compressed phar')]
|
||||
public function beforeMicroUnixBuild(): void
|
||||
{
|
||||
SourcePatcher::patchMicroPhar(php::getPHPVersionID());
|
||||
}
|
||||
|
||||
#[AfterStage('php', [php::class, 'makeMicroForUnix'], 'ext-phar')]
|
||||
public function afterMicroUnixBuild(): void
|
||||
{
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Package\Target;
|
||||
|
||||
use StaticPHP\Attribute\Package\BeforeStage;
|
||||
use StaticPHP\Attribute\Package\InitPackage;
|
||||
use StaticPHP\Attribute\Package\Target;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
@ -12,6 +13,7 @@ use StaticPHP\Util\GlobalEnvManager;
|
||||
class go_xcaddy
|
||||
{
|
||||
#[InitPackage]
|
||||
#[BeforeStage('frankenphp', 'build', 'go-xcaddy')]
|
||||
public function init(): void
|
||||
{
|
||||
if (is_dir(PKG_ROOT_PATH . '/go-xcaddy/bin')) {
|
||||
|
||||
@ -19,4 +19,11 @@ class micro
|
||||
{
|
||||
FileSystem::replaceFileStr("{$package->getSourceDir()}/Makefile", 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
|
||||
}
|
||||
|
||||
#[BeforeStage('php', [php::class, 'makeForUnix'], 'php-micro')]
|
||||
#[PatchDescription('Patch Makefile to skip installing micro binary')]
|
||||
public function patchMakefileBeforeUnixMake(TargetPackage $package): void
|
||||
{
|
||||
FileSystem::replaceFileStr("{$package->getSourceDir()}/Makefile", 'install-micro', '');
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +74,34 @@ class php extends TargetPackage
|
||||
throw new WrongUsageException('PHP version file format is malformed, please remove "./source/php-src" dir and download/extract again');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PHP version from php_version.h
|
||||
*
|
||||
* @param null|string $from_custom_source Where to read php_version.h from custom source
|
||||
* @param bool $return_null_if_failed Whether to return null if failed to get version
|
||||
* @return null|string PHP version (e.g., "8.4.0") or null if failed
|
||||
*/
|
||||
public static function getPHPVersion(?string $from_custom_source = null, bool $return_null_if_failed = false): ?string
|
||||
{
|
||||
$source_dir = $from_custom_source ?? ArtifactLoader::getArtifactInstance('php-src')->getSourceDir();
|
||||
if (!file_exists("{$source_dir}/main/php_version.h")) {
|
||||
if ($return_null_if_failed) {
|
||||
return null;
|
||||
}
|
||||
throw new WrongUsageException('PHP source files are not available, you need to download them first');
|
||||
}
|
||||
|
||||
$file = file_get_contents("{$source_dir}/main/php_version.h");
|
||||
if (preg_match('/PHP_VERSION "(.*)"/', $file, $match) !== 0) {
|
||||
return $match[1];
|
||||
}
|
||||
|
||||
if ($return_null_if_failed) {
|
||||
return null;
|
||||
}
|
||||
throw new WrongUsageException('PHP version file format is malformed, please remove "./source/php-src" dir and download/extract again');
|
||||
}
|
||||
|
||||
#[InitPackage]
|
||||
public function init(TargetPackage $package): void
|
||||
{
|
||||
@ -222,6 +250,8 @@ class php extends TargetPackage
|
||||
'Static Extensions (' . count($static_extensions) . ')' => implode(',', array_map(fn ($x) => substr($x->getName(), 4), $static_extensions)),
|
||||
'Shared Extensions (' . count($shared_extensions) . ')' => implode(',', $shared_extensions),
|
||||
'Install Packages (' . count($install_packages) . ')' => implode(',', array_map(fn ($x) => $x->getName(), $install_packages)),
|
||||
'Strip Binaries' => $package->getBuildOption('no-strip') ? 'No' : 'Yes',
|
||||
'Enable ZTS' => $package->getBuildOption('enable-zts') ? 'Yes' : 'No',
|
||||
];
|
||||
}
|
||||
|
||||
@ -236,7 +266,7 @@ class php extends TargetPackage
|
||||
logger()->info("Adding hardcoded INI [{$source_name} = {$ini_value}]");
|
||||
}
|
||||
if (!empty($custom_ini)) {
|
||||
SourcePatcher::patchHardcodedINI($package->getSourceDir(), $custom_ini);
|
||||
ApplicationContext::invoke([SourcePatcher::class, 'patchHardcodedINI'], [$package->getSourceDir(), $custom_ini]);
|
||||
}
|
||||
|
||||
// Patch StaticPHP version
|
||||
|
||||
155
src/Package/Target/php/frankenphp.php
Normal file
155
src/Package/Target/php/frankenphp.php
Normal file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Package\Target\php;
|
||||
|
||||
use Package\Target\php;
|
||||
use StaticPHP\Attribute\Package\Stage;
|
||||
use StaticPHP\Exception\SPCInternalException;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Package\PackageBuilder;
|
||||
use StaticPHP\Package\PackageInstaller;
|
||||
use StaticPHP\Package\TargetPackage;
|
||||
use StaticPHP\Toolchain\GccNativeToolchain;
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
use StaticPHP\Util\FileSystem;
|
||||
use StaticPHP\Util\InteractiveTerm;
|
||||
use StaticPHP\Util\SPCConfigUtil;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
use ZM\Logger\ConsoleColor;
|
||||
|
||||
trait frankenphp
|
||||
{
|
||||
#[Stage]
|
||||
public function buildFrankenphpUnix(TargetPackage $package, PackageInstaller $installer, ToolchainInterface $toolchain, PackageBuilder $builder): void
|
||||
{
|
||||
if (getenv('GOROOT') === false) {
|
||||
throw new SPCInternalException('go-xcaddy is not initialized properly. GOROOT is not set.');
|
||||
}
|
||||
|
||||
// process --with-frankenphp-app option
|
||||
$package->runStage([$this, 'processFrankenphpApp']);
|
||||
|
||||
// modules
|
||||
$no_brotli = $installer->isPackageResolved('brotli') ? '' : ',nobrotli';
|
||||
$no_watcher = $installer->isPackageResolved('watcher') ? '' : ',nowatcher';
|
||||
$xcaddy_modules = getenv('SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES'); // from env.ini
|
||||
$source_dir = $package->getSourceDir();
|
||||
|
||||
$xcaddy_modules = preg_replace('#--with github.com/dunglas/frankenphp\S*#', '', $xcaddy_modules);
|
||||
$xcaddy_modules = "--with github.com/dunglas/frankenphp={$source_dir} " .
|
||||
"--with github.com/dunglas/frankenphp/caddy={$source_dir}/caddy {$xcaddy_modules}";
|
||||
|
||||
// disable caddy-cbrotli if brotli is not built
|
||||
if (!$installer->isPackageResolved('brotli') && str_contains($xcaddy_modules, '--with github.com/dunglas/caddy-cbrotli')) {
|
||||
logger()->warning('caddy-cbrotli module is enabled, but brotli library is not built. Disabling caddy-cbrotli.');
|
||||
$xcaddy_modules = str_replace('--with github.com/dunglas/caddy-cbrotli', '', $xcaddy_modules);
|
||||
}
|
||||
|
||||
$frankenphp_version = $this->getFrankenPHPVersion($package);
|
||||
$libphp_version = php::getPHPVersion();
|
||||
$dynamic_exports = '';
|
||||
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') {
|
||||
$libphp_version = preg_replace('/\.\d+$/', '', $libphp_version);
|
||||
} elseif ($dynamicSymbolsArgument = LinuxUtil::getDynamicExportedSymbols(BUILD_LIB_PATH . '/libphp.a')) {
|
||||
$dynamic_exports = ' ' . $dynamicSymbolsArgument;
|
||||
}
|
||||
|
||||
// full-static build flags
|
||||
if ($toolchain->isStatic()) {
|
||||
$extLdFlags = "-extldflags '-static-pie -Wl,-z,stack-size=0x80000{$dynamic_exports} {$package->getLibExtraLdFlags()}'";
|
||||
$muslTags = 'static_build,';
|
||||
$staticFlags = '-static-pie';
|
||||
} else {
|
||||
$extLdFlags = "-extldflags '-pie{$dynamic_exports} {$package->getLibExtraLdFlags()}'";
|
||||
$muslTags = '';
|
||||
$staticFlags = '';
|
||||
}
|
||||
|
||||
$resolved = array_keys($installer->getResolvedPackages());
|
||||
// remove self from deps
|
||||
$resolved = array_filter($resolved, fn ($pkg_name) => $pkg_name !== $package->getName());
|
||||
$config = new SPCConfigUtil()->config($resolved);
|
||||
$cflags = "{$package->getLibExtraCFlags()} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . " -DFRANKENPHP_VERSION={$frankenphp_version}";
|
||||
$libs = $config['libs'];
|
||||
|
||||
// Go's gcc driver doesn't automatically link against -lgcov or -lrt. Ugly, but necessary fix.
|
||||
if ((str_contains((string) getenv('SPC_DEFAULT_C_FLAGS'), '-fprofile') ||
|
||||
str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), '-fprofile')) &&
|
||||
$toolchain instanceof GccNativeToolchain) {
|
||||
$cflags .= ' -Wno-error=missing-profile';
|
||||
$libs .= ' -lgcov';
|
||||
}
|
||||
|
||||
$env = [
|
||||
'CGO_ENABLED' => '1',
|
||||
'CGO_CFLAGS' => clean_spaces($cflags),
|
||||
'CGO_LDFLAGS' => "{$package->getLibExtraLdFlags()} {$staticFlags} {$config['ldflags']} {$libs}",
|
||||
'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' .
|
||||
'-ldflags \"-linkmode=external ' . $extLdFlags . ' ' .
|
||||
'-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' .
|
||||
"v{$frankenphp_version} PHP {$libphp_version} Caddy'\\\" " .
|
||||
"-tags={$muslTags}nobadger,nomysql,nopgx{$no_brotli}{$no_watcher}",
|
||||
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
||||
];
|
||||
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('building with xcaddy'));
|
||||
shell()->cd(BUILD_LIB_PATH)
|
||||
->setEnv($env)
|
||||
->exec("xcaddy build --output frankenphp {$xcaddy_modules}");
|
||||
|
||||
$builder->deployBinary(BUILD_LIB_PATH . '/frankenphp', BUILD_BIN_PATH . '/frankenphp');
|
||||
$package->setOutput('Binary path for FrankenPHP SAPI', BUILD_BIN_PATH . '/frankenphp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the --with-frankenphp-app option
|
||||
* Creates app.tar and app.checksum in source/frankenphp directory
|
||||
*/
|
||||
#[Stage]
|
||||
public function processFrankenphpApp(TargetPackage $package): void
|
||||
{
|
||||
$frankenphpSourceDir = $package->getSourceDir();
|
||||
|
||||
$frankenphpAppPath = $package->getBuildOption('with-frankenphp-app');
|
||||
|
||||
if ($frankenphpAppPath) {
|
||||
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('processing --with-frankenphp-app option'));
|
||||
if (!is_dir($frankenphpAppPath)) {
|
||||
throw new WrongUsageException("The path provided to --with-frankenphp-app is not a valid directory: {$frankenphpAppPath}");
|
||||
}
|
||||
$appTarPath = "{$frankenphpSourceDir}/app.tar";
|
||||
logger()->info("Creating app.tar from {$frankenphpAppPath}");
|
||||
|
||||
shell()->exec('tar -cf ' . escapeshellarg($appTarPath) . ' -C ' . escapeshellarg($frankenphpAppPath) . ' .');
|
||||
|
||||
$checksum = hash_file('md5', $appTarPath);
|
||||
file_put_contents($frankenphpSourceDir . '/app_checksum.txt', $checksum);
|
||||
} else {
|
||||
FileSystem::removeFileIfExists("{$frankenphpSourceDir}/app.tar");
|
||||
FileSystem::removeFileIfExists("{$frankenphpSourceDir}/app_checksum.txt");
|
||||
file_put_contents("{$frankenphpSourceDir}/app.tar", '');
|
||||
file_put_contents("{$frankenphpSourceDir}/app_checksum.txt", '');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFrankenPHPVersion(TargetPackage $package): string
|
||||
{
|
||||
if ($version = getenv('FRANKENPHP_VERSION')) {
|
||||
return $version;
|
||||
}
|
||||
$frankenphpSourceDir = $package->getSourceDir();
|
||||
$goModPath = $frankenphpSourceDir . '/caddy/go.mod';
|
||||
|
||||
if (!file_exists($goModPath)) {
|
||||
throw new SPCInternalException("FrankenPHP caddy/go.mod file not found at {$goModPath}, why did we not download FrankenPHP?");
|
||||
}
|
||||
|
||||
$content = file_get_contents($goModPath);
|
||||
if (preg_match('/github\.com\/dunglas\/frankenphp\s+v?(\d+\.\d+\.\d+)/', $content, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
throw new SPCInternalException('Could not find FrankenPHP version in caddy/go.mod');
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Package\Target\php;
|
||||
|
||||
use Package\Target\php;
|
||||
use StaticPHP\Attribute\Package\BeforeStage;
|
||||
use StaticPHP\Attribute\Package\BuildFor;
|
||||
use StaticPHP\Attribute\Package\Stage;
|
||||
use StaticPHP\Attribute\PatchDescription;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Exception\PatchException;
|
||||
use StaticPHP\Exception\SPCException;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Package\PackageBuilder;
|
||||
@ -20,7 +22,6 @@ use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
use StaticPHP\Util\DirDiff;
|
||||
use StaticPHP\Util\FileSystem;
|
||||
use StaticPHP\Util\InteractiveTerm;
|
||||
use StaticPHP\Util\SourcePatcher;
|
||||
use StaticPHP\Util\SPCConfigUtil;
|
||||
use StaticPHP\Util\System\UnixUtil;
|
||||
use StaticPHP\Util\V2CompatLayer;
|
||||
@ -28,6 +29,8 @@ use ZM\Logger\ConsoleColor;
|
||||
|
||||
trait unix
|
||||
{
|
||||
use frankenphp;
|
||||
|
||||
#[BeforeStage('php', [self::class, 'buildconfForUnix'], 'php')]
|
||||
#[PatchDescription('Patch configure.ac for musl and musl-toolchain')]
|
||||
#[PatchDescription('Let php m4 tools use static pkg-config')]
|
||||
@ -46,11 +49,47 @@ trait unix
|
||||
FileSystem::replaceFileStr("{$package->getSourceDir()}/build/php.m4", 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
|
||||
}
|
||||
|
||||
#[BeforeStage('php', [php::class, 'makeForUnix'], 'php')]
|
||||
#[PatchDescription('Patch TSRM for musl TLS symbol visibility issue')]
|
||||
#[PatchDescription('Patch ext/standard/info.c for configure command info')]
|
||||
public function patchTSRMBeforeUnixMake(ToolchainInterface $toolchain): void
|
||||
{
|
||||
if (!$toolchain->isStatic() && SystemTarget::getLibc() === 'musl') {
|
||||
// we need to patch the symbol to global visibility, otherwise extensions with `initial-exec` TLS model will fail to load
|
||||
FileSystem::replaceFileStr(
|
||||
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
|
||||
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
|
||||
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
|
||||
);
|
||||
} else {
|
||||
FileSystem::replaceFileStr(
|
||||
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
|
||||
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
|
||||
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
|
||||
);
|
||||
}
|
||||
|
||||
if (str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), '-release')) {
|
||||
FileSystem::replaceFileLineContainsString(
|
||||
SOURCE_PATH . '/php-src/ext/standard/info.c',
|
||||
'#ifdef CONFIGURE_COMMAND',
|
||||
'#ifdef NO_CONFIGURE_COMMAND',
|
||||
);
|
||||
} else {
|
||||
FileSystem::replaceFileLineContainsString(
|
||||
SOURCE_PATH . '/php-src/ext/standard/info.c',
|
||||
'#ifdef NO_CONFIGURE_COMMAND',
|
||||
'#ifdef CONFIGURE_COMMAND',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[Stage]
|
||||
public function buildconfForUnix(TargetPackage $package): void
|
||||
{
|
||||
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./buildconf'));
|
||||
V2CompatLayer::emitPatchPoint('before-php-buildconf');
|
||||
// run ./buildconf
|
||||
shell()->cd($package->getSourceDir())->exec(getenv('SPC_CMD_PREFIX_PHP_BUILDCONF'));
|
||||
}
|
||||
|
||||
@ -112,18 +151,23 @@ trait unix
|
||||
logger()->info('cleaning up php-src build files');
|
||||
shell()->cd($package->getSourceDir())->exec('make clean');
|
||||
|
||||
// cli
|
||||
if ($installer->isPackageResolved('php-cli')) {
|
||||
$package->runStage([self::class, 'makeCliForUnix']);
|
||||
}
|
||||
// cgi
|
||||
if ($installer->isPackageResolved('php-cgi')) {
|
||||
$package->runStage([self::class, 'makeCgiForUnix']);
|
||||
}
|
||||
// fpm
|
||||
if ($installer->isPackageResolved('php-fpm')) {
|
||||
$package->runStage([self::class, 'makeFpmForUnix']);
|
||||
}
|
||||
// micro
|
||||
if ($installer->isPackageResolved('php-micro')) {
|
||||
$package->runStage([self::class, 'makeMicroForUnix']);
|
||||
}
|
||||
// embed
|
||||
if ($installer->isPackageResolved('php-embed')) {
|
||||
$package->runStage([self::class, 'makeEmbedForUnix']);
|
||||
}
|
||||
@ -175,32 +219,44 @@ trait unix
|
||||
}
|
||||
|
||||
#[Stage]
|
||||
#[PatchDescription('Patch phar extension for micro SAPI to support compressed phar')]
|
||||
#[PatchDescription('Patch micro.sfx after UPX compression')]
|
||||
public function makeMicroForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
|
||||
{
|
||||
$phar_patched = false;
|
||||
try {
|
||||
if ($installer->isPackageResolved('ext-phar')) {
|
||||
$phar_patched = true;
|
||||
SourcePatcher::patchMicroPhar(self::getPHPVersionID());
|
||||
}
|
||||
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make micro'));
|
||||
// apply --with-micro-fake-cli option
|
||||
$vars = $this->makeVars($installer);
|
||||
$vars['EXTRA_CFLAGS'] .= $package->getBuildOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
|
||||
$makeArgs = $this->makeVarsToArgs($vars);
|
||||
// build
|
||||
shell()->cd($package->getSourceDir())
|
||||
->setEnv($vars)
|
||||
->exec("make -j{$builder->concurrency} {$makeArgs} micro");
|
||||
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make micro'));
|
||||
// apply --with-micro-fake-cli option
|
||||
$vars = $this->makeVars($installer);
|
||||
$vars['EXTRA_CFLAGS'] .= $package->getBuildOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '';
|
||||
$makeArgs = $this->makeVarsToArgs($vars);
|
||||
// build
|
||||
shell()->cd($package->getSourceDir())
|
||||
->setEnv($vars)
|
||||
->exec("make -j{$builder->concurrency} {$makeArgs} micro");
|
||||
|
||||
$builder->deployBinary($package->getSourceDir() . '/sapi/micro/micro.sfx', BUILD_BIN_PATH . '/micro.sfx');
|
||||
$package->setOutput('Binary path for micro SAPI', BUILD_BIN_PATH . '/micro.sfx');
|
||||
} finally {
|
||||
if ($phar_patched) {
|
||||
SourcePatcher::unpatchMicroPhar();
|
||||
$dst = BUILD_BIN_PATH . '/micro.sfx';
|
||||
$builder->deployBinary("{$package->getSourceDir()}/sapi/micro/micro.sfx", $dst);
|
||||
|
||||
/*
|
||||
* Patch micro.sfx after UPX compression.
|
||||
* micro needs special section handling in LinuxBuilder.
|
||||
* The micro.sfx does not support UPX directly, but we can remove UPX
|
||||
* info segment to adapt.
|
||||
* This will also make micro.sfx with upx-packed more like a malware fore antivirus
|
||||
*/
|
||||
if ($package->getBuildOption('with-upx-pack') && SystemTarget::getTargetOS() === 'Linux') {
|
||||
// strip first
|
||||
// cut binary with readelf
|
||||
[$ret, $out] = shell()->execWithResult("readelf -l {$dst} | awk '/LOAD|GNU_STACK/ {getline; print \$1, \$2, \$3, \$4, \$6, \$7}'");
|
||||
$out[1] = explode(' ', $out[1]);
|
||||
$offset = $out[1][0];
|
||||
if ($ret !== 0 || !str_starts_with($offset, '0x')) {
|
||||
throw new PatchException('phpmicro UPX patcher', 'Cannot find offset in readelf output');
|
||||
}
|
||||
$offset = hexdec($offset);
|
||||
// remove upx extra wastes
|
||||
file_put_contents($dst, substr(file_get_contents($dst), 0, $offset));
|
||||
}
|
||||
|
||||
$package->setOutput('Binary path for micro SAPI', BUILD_BIN_PATH . '/micro.sfx');
|
||||
}
|
||||
|
||||
#[Stage]
|
||||
@ -229,13 +285,18 @@ trait unix
|
||||
// process libphp.so for shared embed
|
||||
$suffix = SystemTarget::getTargetOS() === 'Darwin' ? 'dylib' : 'so';
|
||||
$libphp_so = "{$package->getLibDir()}/libphp.{$suffix}";
|
||||
$libphp_so_dst = $libphp_so;
|
||||
if (file_exists($libphp_so)) {
|
||||
// rename libphp.so if -release is set
|
||||
if (SystemTarget::getTargetOS() === 'Linux') {
|
||||
$this->processLibphpSoFile($libphp_so, $installer);
|
||||
// deploy libphp.so
|
||||
preg_match('/-release\s+(\S*)/', getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), $matches);
|
||||
if (!empty($matches[1])) {
|
||||
$libphp_so_dst = str_replace('.so', '-' . $matches[1] . '.so', $libphp_so);
|
||||
}
|
||||
}
|
||||
// deploy
|
||||
$builder->deployBinary($libphp_so, $libphp_so, false);
|
||||
$builder->deployBinary($libphp_so, $libphp_so_dst, false);
|
||||
$package->setOutput('Library path for embed SAPI', $libphp_so);
|
||||
}
|
||||
|
||||
@ -312,7 +373,11 @@ trait unix
|
||||
public function build(TargetPackage $package): void
|
||||
{
|
||||
// virtual target, do nothing
|
||||
if ($package->getName() !== 'php') {
|
||||
if (in_array($package->getName(), ['php-cli', 'php-fpm', 'php-cgi', 'php-micro', 'php-embed'], true)) {
|
||||
return;
|
||||
}
|
||||
if ($package->getName() === 'frankenphp') {
|
||||
$package->runStage([$this, 'buildFrankenphpUnix']);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -113,11 +113,10 @@ class PackageBuilder
|
||||
throw new SPCInternalException("Deploy failed. Cannot find file after copy: {$dst}");
|
||||
}
|
||||
|
||||
// extract debug info
|
||||
$this->extractDebugInfo($dst);
|
||||
|
||||
// strip
|
||||
if (!$this->getOption('no-strip') && SystemTarget::isUnix()) {
|
||||
// extract debug info
|
||||
$this->extractDebugInfo($dst);
|
||||
// strip binary
|
||||
$this->stripBinary($dst);
|
||||
}
|
||||
|
||||
@ -145,13 +144,12 @@ class PackageBuilder
|
||||
public function extractDebugInfo(string $binary_path): string
|
||||
{
|
||||
$target_dir = BUILD_ROOT_PATH . '/debug';
|
||||
FileSystem::createDir($target_dir);
|
||||
$basename = basename($binary_path);
|
||||
$debug_file = "{$target_dir}/{$basename}" . (SystemTarget::getTargetOS() === 'Darwin' ? '.dwarf' : '.debug');
|
||||
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||
FileSystem::createDir($target_dir);
|
||||
shell()->exec("dsymutil -f {$binary_path} -o {$debug_file}");
|
||||
} elseif (SystemTarget::getTargetOS() === 'Linux') {
|
||||
FileSystem::createDir($target_dir);
|
||||
if ($eu_strip = LinuxUtil::findCommand('eu-strip')) {
|
||||
shell()
|
||||
->exec("{$eu_strip} -f {$debug_file} {$binary_path}")
|
||||
@ -176,6 +174,7 @@ class PackageBuilder
|
||||
shell()->exec(match (SystemTarget::getTargetOS()) {
|
||||
'Darwin' => "strip -S {$binary_path}",
|
||||
'Linux' => "strip --strip-unneeded {$binary_path}",
|
||||
'Windows' => 'echo "Skip strip on Windows"', // Windows strip is not available for now
|
||||
default => throw new SPCInternalException('stripBinary is only supported on Linux and macOS'),
|
||||
});
|
||||
}
|
||||
|
||||
@ -614,8 +614,13 @@ class PackageInstaller
|
||||
}
|
||||
} else {
|
||||
// process specific php sapi targets
|
||||
$this->build_packages['php'] = PackageLoader::getPackage('php');
|
||||
$this->install_packages[$package->getName()] = $package;
|
||||
if ($package->getName() === 'frankenphp') {
|
||||
$this->build_packages['php'] = PackageLoader::getPackage('php');
|
||||
$this->build_packages['frankenphp'] = PackageLoader::getPackage('frankenphp');
|
||||
} else {
|
||||
$this->install_packages[$package->getName()] = $package;
|
||||
$this->build_packages['php'] = PackageLoader::getPackage('php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user