diff --git a/config/env.ini b/config/env.ini index 4b9f294ad..8b5a58d41 100644 --- a/config/env.ini +++ b/config/env.ini @@ -43,7 +43,7 @@ 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" +SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="--with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli" ; The display message for php version output (PHP >= 8.4 available) PHP_BUILD_PROVIDER="static-php-cli ${SPC_VERSION}" diff --git a/config/lib.json b/config/lib.json index bda0baad2..eccda3043 100644 --- a/config/lib.json +++ b/config/lib.json @@ -7,7 +7,8 @@ "source": "php-src", "lib-depends": [ "lib-base", - "micro" + "micro", + "frankenphp" ], "lib-suggests-linux": [ "libacl", @@ -940,5 +941,9 @@ "liburing/", "liburing.h" ] + }, + "frankenphp": { + "source": "frankenphp", + "type": "target" } } diff --git a/config/source.json b/config/source.json index 4a0e0fdb5..f020ae06e 100644 --- a/config/source.json +++ b/config/source.json @@ -343,6 +343,16 @@ "path": "LICENSE" } }, + "frankenphp": { + "type": "ghtar", + "repo": "php/frankenphp", + "prefer-stable": true, + "provide-pre-build": false, + "license": { + "type": "file", + "path": "LICENSE" + } + }, "icu-static-win": { "type": "url", "url": "https://dl.static-php.dev/static-php-cli/deps/icu-static-windows-x64/icu-static-windows-x64.zip", diff --git a/src/SPC/builder/unix/UnixBuilderBase.php b/src/SPC/builder/unix/UnixBuilderBase.php index 5309636fc..341a15bc1 100644 --- a/src/SPC/builder/unix/UnixBuilderBase.php +++ b/src/SPC/builder/unix/UnixBuilderBase.php @@ -12,6 +12,7 @@ use SPC\store\Config; use SPC\store\FileSystem; use SPC\store\pkg\GoXcaddy; +use SPC\store\SourceManager; use SPC\toolchain\GccNativeToolchain; use SPC\toolchain\ToolchainManager; use SPC\util\DependencyUtil; @@ -264,22 +265,70 @@ protected function patchPhpScripts(): void } } + /** + * Process the --with-frankenphp-app option + * Creates app.tar and app.checksum in source/frankenphp directory + */ + protected function processFrankenphpApp(): void + { + $frankenphpSourceDir = SOURCE_PATH . '/frankenphp'; + SourceManager::initSource(['frankenphp'], ['frankenphp']); + $frankenphpAppPath = $this->getOption('with-frankenphp-app'); + + if ($frankenphpAppPath) { + 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(): string + { + $goModPath = SOURCE_PATH . '/frankenphp/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'); + } + protected function buildFrankenphp(): void { GlobalEnvManager::addPathIfNotExists(GoXcaddy::getEnvironment()['PATH']); + $this->processFrankenphpApp(); $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; - } + $frankenphpSourceDir = SOURCE_PATH . '/frankenphp'; + + $xcaddyModules = preg_replace('#--with github.com/dunglas/frankenphp(=\S+)?#', '', $xcaddyModules); + $xcaddyModules = preg_replace('#--with github.com/dunglas/frankenphp/caddy(=\S+)?#', '', $xcaddyModules); + $xcaddyModules = "--with github.com/dunglas/frankenphp={$frankenphpSourceDir} " . + "--with github.com/dunglas/frankenphp/caddy={$frankenphpSourceDir}/caddy {$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); } - [, $out] = shell()->execWithResult('go list -m github.com/dunglas/frankenphp@latest'); - $frankenPhpVersion = str_replace('github.com/dunglas/frankenphp v', '', $out[0]); + + $frankenPhpVersion = $this->getFrankenPHPVersion(); $libphpVersion = $this->getPHPVersion(); $dynamic_exports = ''; if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'shared') { @@ -317,7 +366,7 @@ protected function buildFrankenphp(): void 'XCADDY_GO_BUILD_FLAGS' => '-buildmode=pie ' . '-ldflags \"-linkmode=external ' . $extLdFlags . ' ' . $debugFlags . '-X \'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ' . - "{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . + "v{$frankenPhpVersion} PHP {$libphpVersion} Caddy'\\\" " . "-tags={$muslTags}nobadger,nomysql,nopgx{$nobrotli}{$nowatcher}", 'LD_LIBRARY_PATH' => BUILD_LIB_PATH, ]; diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php index 3ffc54cef..f8ab384b0 100644 --- a/src/SPC/command/BuildPHPCommand.php +++ b/src/SPC/command/BuildPHPCommand.php @@ -48,6 +48,7 @@ public function configure(): void $this->addOption('with-upx-pack', null, null, 'Compress / pack binary using UPX tool (linux/windows only)'); $this->addOption('with-micro-logo', null, InputOption::VALUE_REQUIRED, 'Use custom .ico for micro.sfx (windows only)'); $this->addOption('enable-micro-win32', null, null, 'Enable win32 mode for phpmicro (Windows only)'); + $this->addOption('with-frankenphp-app', null, InputOption::VALUE_REQUIRED, 'Path to a folder to be embedded in FrankenPHP'); } public function handle(): int