TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

379 lines
12 KiB

<?php
namespace TypePhp\Backend;
use TypePhp\Platform\PlatformBase;
use TypePhp\Platform\Windows;
/**
* GCC/Clang 共享后端基类
* 包含 Unix-like 编译器(GCC、Clang)的通用命令行构建逻辑。
* 子类只需覆盖平台差异的钩子方法。
*/
abstract class GccLikeBackend extends CompilerBackend
{
protected string $compilerCommand;
protected ?string $linkerCommand;
public function __construct(PlatformBase $platform, string $compilerCommand, ?string $linkerCommand = null)
{
parent::__construct($platform);
$this->compilerCommand = $compilerCommand;
$this->linkerCommand = $linkerCommand;
}
public function getCompilerCommand(): string
{
return $this->compilerCommand;
}
public function supportsPrecompiledHeaders(): bool
{
return true;
}
public function getPrecompiledHeaderArtifact(string $headerFile): string
{
return $headerFile . '.gch';
}
// ──── 钩子方法(子类覆盖点) ────
/** 编译器特定的前缀标志(如 MSVC 兼容模式) */
protected function getCompilerPrefixFlags(): string
{
return '';
}
/** 链接器输出标志(-o vs /OUT:) */
protected function getLinkerOutputFlag(): string
{
return '-o';
}
/** 格式化 sanitizer 标志 */
protected function formatSanitizerFlag(string $sanitizer): string
{
return match ($sanitizer) {
'address', 'addr' => '-fsanitize=address',
'undefined', 'undef' => '-fsanitize=undefined',
default => '-fsanitize=' . $sanitizer,
};
}
/** 获取 PIC 标志 */
protected function getPICFlag(array $config): string
{
if ((!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) || !empty($config['pic'])) {
return ' -fPIC';
}
return '';
}
/** 构建 GCC/Clang 共享编译选项,C 和 C++ 编译路径都复用这里 */
protected function buildSharedCompileFlags(array $config, bool $includeCppStd = false): string
{
$cmd = '';
if (!empty($config['sanitize'])) {
$cmd .= ' ' . $this->formatSanitizerFlag($config['sanitize']);
}
if (!empty($config['debug'])) {
$cmd .= ' -O0 -g';
} else {
$optimizeLevel = $config['optimize'] ?? 2;
$cmd .= ' -O' . $optimizeLevel;
}
$cmd .= ' -Wall';
if ($includeCppStd && !empty($config['cpp_std'])) {
$cmd .= ' -std=' . $config['cpp_std'];
}
if (!empty($config['march'])) {
$cmd .= ' -march=' . $config['march'];
}
if (!empty($config['target_platform'])) {
$cmd .= ' --target=' . $config['target_platform'];
}
$cmd .= $this->getPICFlag($config);
if (in_array(($config['build_mode'] ?? null), ['ext', 'lib'], true)
&& !($this->platform instanceof Windows)) {
$cmd .= ' -fvisibility=hidden';
}
if (!empty($config['enable_profiler'])) {
$cmd .= ' ' . $this->formatDefineFlag('PPROF_ON=1', '-D');
if (!empty($config['prof_output'])) {
$profOutput = addcslashes($config['prof_output'], "\\\"");
$cmd .= ' ' . $this->formatDefineFlag('PROF_OUTPUT_FILE="' . $profOutput . '"', '-D');
}
}
if ($includeCppStd && !empty($config['cxxflags'])) {
$cmd .= ' ' . $config['cxxflags'];
}
if (!empty($config['user_defines'])) {
foreach ($config['user_defines'] as $define) {
$cmd .= ' ' . $this->formatDefineFlag($define, '-D');
}
}
if (!empty($config['lto'])) {
$cmd .= ' -flto';
}
if ($includeCppStd && !empty($config['precompiled_header'])) {
$cmd .= $this->formatPrecompiledHeaderFlag($config['precompiled_header']);
}
if ($includeCppStd && !empty($config['forced_include'])) {
$cmd .= ' -include ' . escapeshellarg($config['forced_include']);
}
return $cmd;
}
/** @param array{header: string, artifact: string} $precompiledHeader */
protected function formatPrecompiledHeaderFlag(array $precompiledHeader): string
{
return ' -include ' . escapeshellarg($precompiledHeader['header']);
}
/** 获取平台特定的链接选项 */
protected function getPlatformLinkFlags(array $config): string
{
$flags = '';
if ((!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) || !empty($config['shared'])) {
$flags .= ' ' . $this->platform->getSharedLinkFlag();
// Shared libraries must be self-contained. Executables can defer symbols
// to their host, but a lib-mode artifact must be loadable via dlopen().
if (($config['build_mode'] ?? null) === 'lib' && !($this->platform instanceof \TypePhp\Platform\Macos)) {
$flags .= ' -Wl,-z,defs';
}
// A macOS PHP extension intentionally leaves Zend/PHP symbols for
// the host SAPI to resolve. Linking libphp.dylib would create a
// second runtime, so use the platform's standard bundle behavior.
if (($config['build_mode'] ?? null) === 'ext' && $this->platform instanceof \TypePhp\Platform\Macos) {
$flags .= ' -undefined dynamic_lookup';
}
if ($this->platform instanceof \TypePhp\Platform\Macos && !empty($config['install_name'])) {
$flags .= ' ' . $this->platform->getCurrentInstallNameOption($config['install_name']);
}
}
if (!empty($config['rpath'])) {
foreach ($config['rpath'] as $path) {
$flags .= ' -Wl,-rpath,' . escapeshellarg($path);
}
}
return $flags;
}
/** 获取平台特定的完整链接选项 */
protected function getPlatformFullLinkFlags(array $options): string
{
$flags = '';
if (!empty($options['shared'])) {
$flags .= ' ' . $this->platform->getSharedLinkFlag();
}
if (!empty($options['rpath'])) {
$flags .= ' ' . $this->platform->getRpathOptions($options['rpath']);
}
return $flags;
}
// ──── 抽象方法实现 ────
public function compileFile(
string $sourceFile,
string $outputFile,
array $includePaths = [],
array $defines = [],
array $flags = []
): string {
$cmd = $this->getCompilerCommand();
$cmd .= $this->getCompilerPrefixFlags();
$cmd .= ' -c ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
if (!empty($includePaths)) {
$cmd .= ' ' . $this->formatIncludePaths($includePaths);
}
foreach ($defines as $define) {
$cmd .= ' ' . $this->formatDefineFlag($define, '-D');
}
if (!empty($flags)) {
$cmd .= ' ' . implode(' ', $flags);
}
return $cmd;
}
public function linkObjects(
array $objectFiles,
string $outputFile,
array $libraryPaths = [],
array $libraries = [],
array $flags = []
): string {
$cmd = $this->getLinkerCommand();
$cmd .= ' ' . $this->createResponseFile($objectFiles, $outputFile);
$cmd .= ' ' . $this->getLinkerOutputFlag() . ' ' . escapeshellarg($outputFile);
if (!empty($libraryPaths)) {
$cmd .= ' ' . $this->formatLibraryPaths($libraryPaths);
}
if (!empty($libraries)) {
$cmd .= ' ' . $this->formatLibraries($libraries);
}
if (!empty($flags)) {
$cmd .= ' ' . implode(' ', $flags);
}
return $cmd;
}
public function buildCompileCommand(string $sourceFile, string $outputFile, array $options = []): string
{
$cmd = $this->getCompilerCommand();
$cmd .= $this->getCompilerPrefixFlags();
$cmd .= ' -c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
$cmd .= $this->buildCompileOptions($options);
return $cmd;
}
public function buildCCompileCommand(string $sourceFile, string $outputFile, array $options = []): string
{
$cmd = $this->getCompilerCommand();
$cmd .= $this->getCompilerPrefixFlags();
$cmd .= ' -c';
$cmd .= ' -x c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
$cmd .= $this->buildSharedCompileFlags($options, false);
return $cmd;
}
public function buildNativeCompileCommand(string $sourceFile, string $outputFile, array $options = [], string $language = ''): string
{
$cmd = $this->getCompilerCommand();
$cmd .= $this->getCompilerPrefixFlags();
$cmd .= ' -c';
if ($language !== '') {
$cmd .= ' -x ' . $language;
}
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
$cmd .= $this->buildCompileOptions($options);
return $cmd;
}
public function buildLinkCommand(array $objectFiles, string $outputFile, array $options = []): string
{
$cmd = $this->getLinkerCommand();
$cmd .= ' ' . $this->createResponseFile($objectFiles, $outputFile);
$cmd .= ' ' . $this->getLinkerOutputFlag() . ' ' . escapeshellarg($outputFile);
if (!empty($options['library_paths'])) {
$cmd .= ' ' . $this->formatLibraryPaths($options['library_paths']);
}
if (!empty($options['ldflags'])) {
$cmd .= ' ' . $options['ldflags'];
}
$cmd .= $this->buildLinkOptions($options);
if (!empty($options['libraries'])) {
$cmd .= ' ' . $this->formatLibraries($options['libraries']);
}
return $cmd;
}
public function buildCompileOptions(array $config = []): string
{
$cmd = $this->getCompilerPrefixFlags();
$cmd .= $this->buildSharedCompileFlags($config, true);
return $cmd;
}
public function buildLinkOptions(array $config = []): string
{
$cmd = '';
$cmd .= $this->getPlatformLinkFlags($config);
if (!empty($config['sanitize'])) {
$cmd .= ' ' . $this->formatSanitizerFlag($config['sanitize']);
}
if (!empty($config['target_platform'])) {
$cmd .= ' --target=' . $config['target_platform'];
}
if (!empty($config['lto'])) {
$cmd .= ' -flto';
}
return $cmd;
}
public function buildFullCompileOptions(array $options = []): string
{
$cmd = $this->getCompilerPrefixFlags();
$cmd .= $this->buildSharedCompileFlags($options, true);
return $cmd;
}
public function buildFullLinkOptions(array $options = []): string
{
$cmd = '';
$cmd .= $this->getPlatformFullLinkFlags($options);
if (!empty($options['sanitize'])) {
$cmd .= ' -fsanitize=' . $options['sanitize'];
}
return $cmd;
}
}