diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 4adb1257..f67337e9 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -170,15 +170,11 @@ abstract class BuilderBase /** * 开始构建 PHP - * 构建 micro 的规则: - * - BUILD_MICRO_NONE(默认):只编译 cli - * - BUILD_MICRO_ONLY:只编译 micro - * - BUILD_MICRO_BOTH:同时编译 micro 和 cli * - * @param int $build_micro_rule 规则 - * @param bool $bloat 保留 + * @param int $build_target 规则 + * @param bool $bloat 保留 */ - abstract public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $bloat = false); + abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false); /** * 生成依赖的扩展编译启用参数 @@ -215,6 +211,21 @@ abstract class BuilderBase return intval($match[1]); } + public function getBuildTypeName(int $type): string + { + $ls = []; + if (($type & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { + $ls[] = 'cli'; + } + if (($type & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { + $ls[] = 'micro'; + } + if (($type & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { + $ls[] = 'fpm'; + } + return implode(', ', $ls); + } + /** * 检查是否存在 lib 库对应的源码,如果不存在,则抛出异常 * diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index c3e27341..dfd6b859 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -133,7 +133,7 @@ abstract class LibraryBase { // 传入 true,表明直接编译 if ($force_build) { - logger()->info('Building required library ' . static::NAME); + logger()->info('Building required library [' . static::NAME . ']'); $this->build(); return BUILD_STATUS_OK; } diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index bc5d7ff7..a70e324d 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -121,7 +121,7 @@ class LinuxBuilder extends BuilderBase * @throws RuntimeException * @throws FileSystemException */ - public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false) + public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $with_clean = false, bool $bloat = false) { if (!$bloat) { $extra_libs = implode(' ', $this->getAllStaticLibFiles()); @@ -176,6 +176,7 @@ class LinuxBuilder extends BuilderBase '--disable-cgi ' . '--disable-phpdbg ' . '--enable-cli ' . + '--enable-fpm ' . '--enable-micro=all-static ' . ($this->zts ? '--enable-zts' : '') . ' ' . $this->makeExtensionArgs() . ' ' . @@ -194,31 +195,26 @@ class LinuxBuilder extends BuilderBase $extra_libs = "-Wl,--whole-archive {$extra_libs} -Wl,--no-whole-archive"; } - switch ($build_micro_rule) { - case BUILD_MICRO_NONE: - logger()->info('building cli'); - $this->buildCli($extra_libs, $use_lld); - break; - case BUILD_MICRO_ONLY: - logger()->info('building micro'); - $this->buildMicro($extra_libs, $use_lld, $cflags); - break; - case BUILD_MICRO_BOTH: - logger()->info('building cli and micro'); - $this->buildCli($extra_libs, $use_lld); - $this->buildMicro($extra_libs, $use_lld, $cflags); - break; + if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { + logger()->info('building cli'); + $this->buildCli($extra_libs, $use_lld); + } + if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { + logger()->info('building fpm'); + $this->buildFpm($extra_libs, $use_lld); + } + if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { + logger()->info('building micro'); + $this->buildMicro($extra_libs, $use_lld, $cflags); } if (php_uname('m') === $this->arch) { - $this->sanityCheck($build_micro_rule); + $this->sanityCheck($build_target); } if ($this->phar_patched) { shell()->cd(SOURCE_PATH . '/php-src')->exec('patch -p1 -R < sapi/micro/patches/phar.patch'); } - - file_put_contents(SOURCE_PATH . '/php-src/.extensions.json', json_encode($this->plain_extensions, JSON_PRETTY_PRINT)); } /** @@ -241,7 +237,7 @@ class LinuxBuilder extends BuilderBase ->exec('elfedit --output-osabi linux php') ->exec("{$this->cross_compile_prefix}strip --strip-all php") ->exec("{$this->cross_compile_prefix}objcopy --update-section .comment=/tmp/comment --add-gnu-debuglink=php.debug --remove-section=.note php"); - $this->deployBinary(BUILD_TYPE_CLI); + $this->deployBinary(BUILD_TARGET_CLI); } /** @@ -274,7 +270,30 @@ class LinuxBuilder extends BuilderBase shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec("{$this->cross_compile_prefix}strip --strip-all micro.sfx"); - $this->deployBinary(BUILD_TYPE_MICRO); + $this->deployBinary(BUILD_TARGET_MICRO); + } + + /** + * @throws RuntimeException + */ + public function buildFpm(string $extra_libs, string $use_lld): void + { + shell()->cd(SOURCE_PATH . '/php-src') + ->exec('sed -i "s|//lib|/lib|g" Makefile') + ->exec( + 'make -j' . $this->concurrency . + ' EXTRA_CFLAGS="-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)) . '" ' . + "EXTRA_LIBS=\"{$extra_libs}\" " . + "EXTRA_LDFLAGS_PROGRAM='{$use_lld} -all-static' " . + 'fpm' + ); + + shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm') + ->exec("{$this->cross_compile_prefix}objcopy --only-keep-debug php-fpm php-fpm.debug") + ->exec('elfedit --output-osabi linux php-fpm') + ->exec("{$this->cross_compile_prefix}strip --strip-all php-fpm") + ->exec("{$this->cross_compile_prefix}objcopy --update-section .comment=/tmp/comment --add-gnu-debuglink=php-fpm.debug --remove-section=.note php-fpm"); + $this->deployBinary(BUILD_TARGET_FPM); } /** diff --git a/src/SPC/builder/linux/library/LinuxLibraryBase.php b/src/SPC/builder/linux/library/LinuxLibraryBase.php index 6eb5191b..1105578f 100644 --- a/src/SPC/builder/linux/library/LinuxLibraryBase.php +++ b/src/SPC/builder/linux/library/LinuxLibraryBase.php @@ -42,7 +42,7 @@ abstract class LinuxLibraryBase extends LibraryBase { // 传入 true,表明直接编译 if ($force_build) { - logger()->info('Building required library ' . static::NAME); + logger()->info('Building required library [' . static::NAME . ']'); $this->build(); return BUILD_STATUS_OK; } diff --git a/src/SPC/builder/macos/MacOSBuilder.php b/src/SPC/builder/macos/MacOSBuilder.php index 9a6c1cd2..3c29f00d 100644 --- a/src/SPC/builder/macos/MacOSBuilder.php +++ b/src/SPC/builder/macos/MacOSBuilder.php @@ -119,7 +119,7 @@ class MacOSBuilder extends BuilderBase * @throws RuntimeException * @throws FileSystemException */ - public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $bloat = false): void + public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false): void { $extra_libs = $this->getFrameworks(true) . ' ' . ($this->getExt('swoole') ? '-lc++ ' : ''); if (!$bloat) { @@ -159,6 +159,7 @@ class MacOSBuilder extends BuilderBase '--disable-cgi ' . '--disable-phpdbg ' . '--enable-cli ' . + '--enable-fpm ' . '--enable-micro ' . ($this->zts ? '--enable-zts' : '') . ' ' . $this->makeExtensionArgs() . ' ' . @@ -167,24 +168,21 @@ class MacOSBuilder extends BuilderBase $this->cleanMake(); - switch ($build_micro_rule) { - case BUILD_MICRO_NONE: - logger()->info('building cli'); - $this->buildCli($extra_libs); - break; - case BUILD_MICRO_ONLY: - logger()->info('building micro'); - $this->buildMicro($extra_libs); - break; - case BUILD_MICRO_BOTH: - logger()->info('building cli and micro'); - $this->buildCli($extra_libs); - $this->buildMicro($extra_libs); - break; + if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { + logger()->info('building cli'); + $this->buildCli($extra_libs); + } + if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { + logger()->info('building fpm'); + $this->buildFpm($extra_libs); + } + if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { + logger()->info('building micro'); + $this->buildMicro($extra_libs); } if (php_uname('m') === $this->arch) { - $this->sanityCheck($build_micro_rule); + $this->sanityCheck($build_target); } if ($this->phar_patched) { @@ -193,9 +191,24 @@ class MacOSBuilder extends BuilderBase } /** - * 构建 phpmicro + * 构建 cli * * @throws RuntimeException + * @throws FileSystemException + */ + public function buildCli(string $extra_libs): void + { + shell()->cd(SOURCE_PATH . '/php-src') + ->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli") + ->exec('dsymutil -f sapi/cli/php') + ->exec('strip sapi/cli/php'); + $this->deployBinary(BUILD_TARGET_CLI); + } + + /** + * 构建 phpmicro + * + * @throws FileSystemException|RuntimeException */ public function buildMicro(string $extra_libs): void { @@ -215,22 +228,21 @@ class MacOSBuilder extends BuilderBase shell()->cd(SOURCE_PATH . '/php-src') ->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" STRIP=\"dsymutil -f \" micro"); - $this->deployBinary(BUILD_TYPE_MICRO); + $this->deployBinary(BUILD_TARGET_MICRO); } /** - * 构建 cli + * 构建 fpm * * @throws RuntimeException * @throws FileSystemException */ - public function buildCli(string $extra_libs): void + public function buildFpm(string $extra_libs): void { shell()->cd(SOURCE_PATH . '/php-src') - // 生成调试信息、优化编译后的尺寸、禁用标识符(如变量、函数名)缩短 - ->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli") - ->exec('dsymutil -f sapi/cli/php') - ->exec('strip sapi/cli/php'); - $this->deployBinary(BUILD_TYPE_CLI); + ->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" fpm") + ->exec('dsymutil -f sapi/fpm/php-fpm') + ->exec('strip sapi/fpm/php-fpm'); + $this->deployBinary(BUILD_TARGET_FPM); } } diff --git a/src/SPC/builder/traits/UnixBuilderTrait.php b/src/SPC/builder/traits/UnixBuilderTrait.php index c53d9183..81f0371e 100644 --- a/src/SPC/builder/traits/UnixBuilderTrait.php +++ b/src/SPC/builder/traits/UnixBuilderTrait.php @@ -56,12 +56,15 @@ trait UnixBuilderTrait } /** + * Sanity check after build complete + * * @throws RuntimeException */ - public function sanityCheck(int $build_micro_rule): void + public function sanityCheck(int $build_target): void { - logger()->info('running sanity check'); - if ($build_micro_rule !== BUILD_MICRO_ONLY) { + // sanity check for php-cli + if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { + logger()->info('running cli sanity check'); [$ret, $output] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -r "echo \"hello\";"'); if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { throw new RuntimeException('cli failed sanity check'); @@ -80,7 +83,9 @@ trait UnixBuilderTrait } } } - if ($build_micro_rule !== BUILD_MICRO_NONE) { + + // sanity check for phpmicro + if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { if (file_exists(SOURCE_PATH . '/hello.exe')) { @unlink(SOURCE_PATH . '/hello.exe'); } @@ -107,11 +112,12 @@ trait UnixBuilderTrait public function deployBinary(int $type): bool { $src = match ($type) { - BUILD_TYPE_CLI => SOURCE_PATH . '/php-src/sapi/cli/php', - BUILD_TYPE_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx', + BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php', + BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx', + BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm', default => throw new RuntimeException('Deployment does not accept type ' . $type), }; - logger()->info('Deploying ' . ($type === BUILD_TYPE_CLI ? 'cli' : 'micro') . ' file'); + logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); FileSystem::createDir(BUILD_ROOT_PATH . '/bin'); shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '/bin/')); return true; diff --git a/src/SPC/command/BuildCliCommand.php b/src/SPC/command/BuildCliCommand.php index 34b0ea44..77219d15 100644 --- a/src/SPC/command/BuildCliCommand.php +++ b/src/SPC/command/BuildCliCommand.php @@ -12,6 +12,7 @@ use SPC\util\LicenseDumper; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; +use ZM\Logger\ConsoleColor; #[AsCommand('build', 'build CLI binary')] class BuildCliCommand extends BuildCommand @@ -20,8 +21,10 @@ class BuildCliCommand extends BuildCommand { $this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated'); $this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', ''); - $this->addOption('build-micro', null, null, 'build micro only'); - $this->addOption('build-all', null, null, 'build both cli and micro'); + $this->addOption('build-micro', null, null, 'build micro'); + $this->addOption('build-cli', null, null, 'build cli'); + $this->addOption('build-fpm', null, null, 'build fpm'); + $this->addOption('build-all', null, null, 'build cli, micro, fpm'); } public function handle(): int @@ -33,24 +36,30 @@ class BuildCliCommand extends BuildCommand define('BUILD_ALL_STATIC', true); - if ($this->getOption('build-all')) { - $rule = BUILD_MICRO_BOTH; - logger()->info('Builder will build php-cli and phpmicro SAPI'); - } elseif ($this->getOption('build-micro')) { - $rule = BUILD_MICRO_ONLY; - logger()->info('Builder will build phpmicro SAPI'); - } else { - $rule = BUILD_MICRO_NONE; - logger()->info('Builder will build php-cli SAPI'); + $rule = BUILD_TARGET_NONE; + $rule = $rule | ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE); + $rule = $rule | ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE); + $rule = $rule | ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE); + $rule = $rule | ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE); + if ($rule === BUILD_TARGET_NONE) { + $this->output->writeln('Please add at least one build target!'); + $this->output->writeln("\t--build-cli\tBuild php-cli SAPI"); + $this->output->writeln("\t--build-micro\tBuild phpmicro SAPI"); + $this->output->writeln("\t--build-fpm\tBuild php-fpm SAPI"); + $this->output->writeln("\t--build-all\tBuild all SAPI: cli, micro, fpm"); + return 1; } try { // 构建对象 $builder = BuilderProvider::makeBuilderByInput($this->input); // 根据提供的扩展列表获取依赖库列表并编译 [$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries); - - logger()->info('Enabled extensions: ' . implode(', ', $extensions)); - logger()->info('Required libraries: ' . implode(', ', $libraries)); + /* @phpstan-ignore-next-line */ + logger()->info('Build target: ' . ConsoleColor::yellow($builder->getBuildTypeName($rule))); + /* @phpstan-ignore-next-line */ + logger()->info('Enabled extensions: ' . ConsoleColor::yellow(implode(', ', $extensions))); + /* @phpstan-ignore-next-line */ + logger()->info('Required libraries: ' . ConsoleColor::yellow(implode(', ', $libraries))); if (!empty($not_included)) { logger()->warning('some extensions will be enabled due to dependencies: ' . implode(',', $not_included)); } @@ -64,12 +73,15 @@ class BuildCliCommand extends BuildCommand // 统计时间 $time = round(microtime(true) - START_TIME, 3); logger()->info('Build complete, used ' . $time . ' s !'); - if ($rule !== BUILD_MICRO_ONLY) { + if (($rule & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { logger()->info('Static php binary path: ' . BUILD_ROOT_PATH . '/bin/php'); } - if ($rule !== BUILD_MICRO_NONE) { + if (($rule & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { logger()->info('phpmicro binary path: ' . BUILD_ROOT_PATH . '/bin/micro.sfx'); } + if (($rule & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { + logger()->info('Static php-fpm binary path: ' . BUILD_ROOT_PATH . '/bin/php-fpm'); + } // 导出相关元数据 file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); diff --git a/src/globals/defines.php b/src/globals/defines.php index c692e3b2..2ea31c34 100644 --- a/src/globals/defines.php +++ b/src/globals/defines.php @@ -33,20 +33,17 @@ const REPLACE_FILE_STR = 1; const REPLACE_FILE_PREG = 2; const REPLACE_FILE_USER = 3; -// build sapi type -const BUILD_MICRO_NONE = 0; -const BUILD_MICRO_ONLY = 1; -const BUILD_MICRO_BOTH = 2; - // library build status const BUILD_STATUS_OK = 0; const BUILD_STATUS_ALREADY = 1; const BUILD_STATUS_FAILED = 2; // build target type -const BUILD_TYPE_CLI = 1; -const BUILD_TYPE_MICRO = 2; -const BUILD_TYPE_FPM = 3; +const BUILD_TARGET_NONE = 0; +const BUILD_TARGET_CLI = 1; +const BUILD_TARGET_MICRO = 2; +const BUILD_TARGET_FPM = 4; +const BUILD_TARGET_ALL = 7; // doctor error fix policy const FIX_POLICY_DIE = 1; // die directly