static-php-cli/src/SPC/builder/linux/LinuxBuilder.php

358 lines
14 KiB
PHP
Raw Normal View History

2023-03-21 00:25:46 +08:00
<?php
declare(strict_types=1);
namespace SPC\builder\linux;
use SPC\builder\linux\library\LinuxLibraryBase;
2024-01-10 21:08:25 +08:00
use SPC\builder\unix\UnixBuilderBase;
2023-03-21 00:25:46 +08:00
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
2023-03-29 21:39:36 +08:00
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
2023-04-30 12:42:19 +08:00
use SPC\store\SourcePatcher;
2023-03-21 00:25:46 +08:00
2024-01-10 21:08:25 +08:00
class LinuxBuilder extends UnixBuilderBase
2023-03-21 00:25:46 +08:00
{
/** @var array Tune cflags */
2023-03-21 00:25:46 +08:00
public array $tune_c_flags;
/** @var bool Micro patch phar flag */
2023-03-21 00:25:46 +08:00
private bool $phar_patched = false;
/**
* @throws FileSystemException
2023-03-21 00:25:46 +08:00
* @throws RuntimeException
2023-04-15 18:45:34 +08:00
* @throws WrongUsageException
2023-03-21 00:25:46 +08:00
*/
public function __construct(array $options = [])
2023-03-21 00:25:46 +08:00
{
$this->options = $options;
// ---------- set necessary options ----------
2023-10-16 14:09:49 +02:00
// set C/C++ compilers (default: alpine: gcc, others: musl-cross-make)
if (SystemUtil::isMuslDist()) {
f_putenv("CC={$this->getOption('cc', 'gcc')}");
f_putenv("CXX={$this->getOption('cxx', 'g++')}");
f_putenv("AR={$this->getOption('ar', 'ar')}");
f_putenv("LD={$this->getOption('ld', 'ld.gold')}");
2023-10-16 14:09:49 +02:00
} else {
$arch = arch2gnu(php_uname('m'));
f_putenv("CC={$this->getOption('cc', "{$arch}-linux-musl-gcc")}");
f_putenv("CXX={$this->getOption('cxx', "{$arch}-linux-musl-g++")}");
f_putenv("AR={$this->getOption('ar', "{$arch}-linux-musl-ar")}");
f_putenv("LD={$this->getOption('ld', 'ld.gold')}");
f_putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . BUILD_ROOT_PATH . '/bin:' . getenv('PATH'));
// set library path, some libraries need it. (We cannot use `putenv` here, because cmake will be confused)
$this->setOptionIfNotExist('library_path', "LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib");
$this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib");
// check musl-cross make installed if we use musl-cross-make
if (str_ends_with(getenv('CC'), 'linux-musl-gcc') && !file_exists("/usr/local/musl/bin/{$arch}-linux-musl-gcc")) {
throw new WrongUsageException('musl-cross-make not installed, please install it first. (You can use `doctor` command to install it)');
}
2023-10-16 14:09:49 +02:00
}
// set PKG_CONFIG
f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config');
// set PKG_CONFIG_PATH
f_putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig');
// set arch (default: current)
$this->setOptionIfNotExist('arch', php_uname('m'));
$this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch')));
// concurrency
2023-03-21 00:25:46 +08:00
$this->concurrency = SystemUtil::getCpuCount();
// cflags
$this->arch_c_flags = SystemUtil::getArchCFlags(getenv('CC'), $this->getOption('arch'));
$this->arch_cxx_flags = SystemUtil::getArchCFlags(getenv('CXX'), $this->getOption('arch'));
$this->tune_c_flags = SystemUtil::checkCCFlags(SystemUtil::getTuneCFlags($this->getOption('arch')), getenv('CC'));
// cmake toolchain
2023-03-21 00:25:46 +08:00
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile(
'Linux',
$this->getOption('arch'),
$this->arch_c_flags,
getenv('CC'),
getenv('CXX'),
2023-03-21 00:25:46 +08:00
);
2023-10-16 14:09:49 +02:00
// cross-compiling is not supported yet
/*if (php_uname('m') !== $this->arch) {
2023-03-21 00:25:46 +08:00
$this->cross_compile_prefix = SystemUtil::getCrossCompilePrefix($this->cc, $this->arch);
logger()->info('using cross compile prefix: ' . $this->cross_compile_prefix);
$this->configure_env .= " CROSS_COMPILE='{$this->cross_compile_prefix}'";
}*/
2023-03-21 00:25:46 +08:00
// create pkgconfig and include dir (some libs cannot create them automatically)
2023-03-21 00:25:46 +08:00
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
f_mkdir(BUILD_INCLUDE_PATH, recursive: true);
}
/**
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
*/
2023-03-21 00:25:46 +08:00
public function makeAutoconfArgs(string $name, array $libSpecs): string
{
$ret = '';
foreach ($libSpecs as $libName => $arr) {
$lib = $this->getLib($libName);
$arr = $arr ?? [];
$disableArgs = $arr[0] ?? null;
$prefix = $arr[1] ?? null;
if ($lib instanceof LinuxLibraryBase) {
logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support");
$ret .= $lib->makeAutoconfEnv($prefix) . ' ';
} else {
logger()->info("{$name} \033[31;1mwithout\033[0;1m {$libName} support");
$ret .= ($disableArgs ?? "--with-{$libName}=no") . ' ';
}
}
return rtrim($ret);
}
/**
* @throws RuntimeException
* @throws FileSystemException
* @throws WrongUsageException
2023-03-21 00:25:46 +08:00
*/
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
2023-03-21 00:25:46 +08:00
{
// ---------- Update extra-libs ----------
$extra_libs = $this->getOption('extra-libs', '');
// non-bloat linking
if (!$this->getOption('bloat', false)) {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles());
2023-03-21 00:25:46 +08:00
} else {
$extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", array_filter($this->getAllStaticLibFiles())));
2023-03-21 00:25:46 +08:00
}
2023-10-16 14:09:49 +02:00
// add libstdc++, some extensions or libraries need it
$extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lstdc++ ' : '');
$this->setOption('extra-libs', $extra_libs);
2023-03-21 00:25:46 +08:00
$cflags = $this->arch_c_flags;
// prepare build php envs
$envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => $cflags,
2023-12-21 14:02:32 +08:00
'CPPFLAGS' => '-I' . BUILD_INCLUDE_PATH,
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
'LDFLAGS' => '-L' . BUILD_LIB_PATH,
'LIBS' => '-ldl -lpthread',
]);
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-php-buildconf');
SourcePatcher::patchBeforeBuildconf($this);
2023-03-21 00:25:46 +08:00
shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force');
2023-03-21 00:25:46 +08:00
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-php-configure');
SourcePatcher::patchBeforeConfigure($this);
2023-08-21 12:54:36 +02:00
$phpVersionID = $this->getPHPVersionID();
$json_74 = $phpVersionID < 80000 ? '--enable-json ' : '';
2023-08-21 18:37:00 +02:00
if ($this->getOption('enable-zts', false)) {
$maxExecutionTimers = $phpVersionID >= 80100 ? '--enable-zend-max-execution-timers ' : '';
$zts = '--enable-zts --disable-zend-signals ';
} else {
$maxExecutionTimers = '';
$zts = '';
}
$disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : '';
2023-03-21 00:25:46 +08:00
$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;
2024-02-19 12:17:03 +08:00
// upx pack and strip for micro
if ($this->getOption('with-upx-pack', false)) {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH) && ' . $this->getOption('upx-exec') . ' --best \$(SAPI_MICRO_PATH)',
);
} elseif (!$this->getOption('no-strip', false)) {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=true',
);
2024-02-19 12:27:15 +08:00
} else {
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/sapi/micro/Makefile.frag',
'/POST_MICRO_BUILD_COMMANDS=.*/',
'POST_MICRO_BUILD_COMMANDS=\$(STRIP) \$(MICRO_STRIP_FLAGS) \$(SAPI_MICRO_PATH)',
);
2024-02-19 12:17:03 +08:00
}
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
"{$this->getOption('ld_library_path')} " .
'./configure ' .
'--prefix= ' .
'--with-valgrind=no ' .
'--enable-shared=no ' .
'--enable-static=yes ' .
'--disable-all ' .
'--disable-cgi ' .
'--disable-phpdbg ' .
2023-09-02 21:25:33 +02:00
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static ' : '--disable-embed ') .
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
$disable_jit .
2023-05-04 11:29:14 +08:00
$json_74 .
$zts .
2023-08-21 12:54:36 +02:00
$maxExecutionTimers .
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
$this->makeExtensionArgs() .
' ' . $envs_build_php . ' '
);
2023-03-21 00:25:46 +08:00
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-php-make');
SourcePatcher::patchBeforeMake($this);
2023-04-30 12:42:19 +08:00
$this->cleanMake();
2023-03-21 00:25:46 +08:00
if ($enableCli) {
2023-04-23 20:31:58 +08:00
logger()->info('building cli');
2023-10-30 22:14:47 +01:00
$this->buildCli();
2023-04-23 20:31:58 +08:00
}
if ($enableFpm) {
2023-04-23 20:31:58 +08:00
logger()->info('building fpm');
2023-10-30 22:14:47 +01:00
$this->buildFpm();
2023-04-23 20:31:58 +08:00
}
if ($enableMicro) {
2023-04-23 20:31:58 +08:00
logger()->info('building micro');
2023-10-30 22:14:47 +01:00
$this->buildMicro();
2023-03-21 00:25:46 +08:00
}
if ($enableEmbed) {
logger()->info('building embed');
if ($enableMicro) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
}
2023-10-30 22:14:47 +01:00
$this->buildEmbed();
}
2023-03-21 00:25:46 +08:00
if (php_uname('m') === $this->getOption('arch')) {
2024-01-03 15:57:05 +08:00
$this->emitPatchPoint('before-sanity-check');
2023-04-23 20:31:58 +08:00
$this->sanityCheck($build_target);
2023-03-21 00:25:46 +08:00
}
}
/**
* Build cli sapi
*
2023-03-21 00:25:46 +08:00
* @throws RuntimeException
* @throws FileSystemException
2023-03-21 00:25:46 +08:00
*/
protected function buildCli(): void
2023-03-21 00:25:46 +08:00
{
2023-10-30 22:14:47 +01:00
$vars = SystemUtil::makeEnvVarString($this->getBuildVars());
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("make -j{$this->concurrency} {$vars} cli");
2024-02-19 15:29:43 +08:00
if ($this->getOption('with-upx-pack')) {
2024-02-19 12:17:03 +08:00
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')
->exec('strip --strip-all php')
->exec($this->getOption('upx-exec') . ' --best php');
2024-02-19 15:29:43 +08:00
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php');
}
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_CLI);
2023-03-21 00:25:46 +08:00
}
/**
* Build phpmicro sapi
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
2023-03-21 00:25:46 +08:00
*/
protected function buildMicro(): void
2023-03-21 00:25:46 +08:00
{
2023-04-03 20:47:24 +08:00
if ($this->getPHPVersionID() < 80000) {
throw new WrongUsageException('phpmicro only support PHP >= 8.0!');
2023-04-03 20:47:24 +08:00
}
2023-03-21 00:25:46 +08:00
if ($this->getExt('phar')) {
$this->phar_patched = true;
2023-05-01 12:50:01 +08:00
SourcePatcher::patchMicro(['phar']);
2023-03-21 00:25:46 +08:00
}
2023-10-30 22:14:47 +01:00
$vars = SystemUtil::makeEnvVarString($this->getBuildVars([
'EXTRA_CFLAGS' => $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '',
]));
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("make -j{$this->concurrency} {$vars} micro");
2023-03-21 00:25:46 +08:00
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_MICRO);
if ($this->phar_patched) {
SourcePatcher::patchMicro(['phar'], true);
}
2023-04-23 20:31:58 +08:00
}
/**
* Build fpm sapi
*
* @throws FileSystemException
* @throws RuntimeException
2023-04-23 20:31:58 +08:00
*/
protected function buildFpm(): void
2023-04-23 20:31:58 +08:00
{
2023-10-30 22:14:47 +01:00
$vars = SystemUtil::makeEnvVarString($this->getBuildVars());
2023-04-23 20:31:58 +08:00
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec("make -j{$this->concurrency} {$vars} fpm");
2024-02-19 15:29:43 +08:00
if ($this->getOption('with-upx-pack')) {
2024-02-19 12:17:03 +08:00
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')
->exec('strip --strip-all php-fpm')
->exec($this->getOption('upx-exec') . ' --best php-fpm');
2024-02-19 15:29:43 +08:00
} elseif (!$this->getOption('no-strip', false)) {
shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm');
}
2023-04-23 20:31:58 +08:00
$this->deployBinary(BUILD_TARGET_FPM);
2023-03-21 00:25:46 +08:00
}
/**
* Build embed sapi
*
* @throws RuntimeException
*/
protected function buildEmbed(): void
{
2023-10-30 22:14:47 +01:00
$vars = SystemUtil::makeEnvVarString($this->getBuildVars());
shell()
->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec('make INSTALL_ROOT=' . BUILD_ROOT_PATH . " -j{$this->concurrency} {$vars} install");
}
2023-10-30 22:14:47 +01:00
private function getBuildVars($input = []): array
{
$use_lld = '';
if (str_ends_with(getenv('CC'), 'clang') && SystemUtil::findCommand('lld')) {
$use_lld = '-Xcompiler -fuse-ld=lld';
}
2023-10-31 12:17:53 +08:00
$optimization = $this->getOption('no-strip', false) ? '-g -O0' : '-g0 -Os';
2023-10-30 22:14:47 +01:00
$cflags = isset($input['EXTRA_CFLAGS']) && $input['EXTRA_CFLAGS'] ? " {$input['EXTRA_CFLAGS']}" : '';
$libs = isset($input['EXTRA_LIBS']) && $input['EXTRA_LIBS'] ? " {$input['EXTRA_LIBS']}" : '';
$ldflags = isset($input['EXTRA_LDFLAGS_PROGRAM']) && $input['EXTRA_LDFLAGS_PROGRAM'] ? " {$input['EXTRA_LDFLAGS_PROGRAM']}" : '';
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
$tune_c_flags = implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags));
2023-10-30 22:14:47 +01:00
return [
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
'EXTRA_CFLAGS' => "{$optimization} -fno-ident -fPIE {$tune_c_flags}{$cflags}",
'EXTRA_LIBS' => "{$this->getOption('extra-libs', '')} {$libs}",
'EXTRA_LDFLAGS_PROGRAM' => "{$use_lld} -all-static{$ldflags}",
2023-10-30 22:14:47 +01:00
];
}
2023-03-21 00:25:46 +08:00
}