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.
 
 

191 lines
5.8 KiB

<?php
namespace TypePhp\Backend;
use TypePhp\Platform\PlatformBase;
use TypePhp\Platform\Windows;
use TypePhp\Platform\Linux;
use TypePhp\Platform\Macos;
/**
* 编译器工厂类
* 根据平台自动创建合适的编译器后端
*/
class CompilerFactory
{
/**
* 创建默认编译器后端
*/
public static function create(PlatformBase $platform): CompilerBackend
{
if ($platform instanceof Windows) {
// Windows 默认使用 MSVC
return new Msvc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Linux) {
// Linux 默认使用 GCC
return new Gcc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Macos) {
// macOS 默认使用 Clang
return new Clang($platform, $platform->getDefaultCompiler());
} else {
throw new \RuntimeException("Unsupported platform: " . $platform->getName());
}
}
/**
* 根据配置、环境变量和平台默认值解析编译器命令
*/
public static function detectCompilerName(PlatformBase $platform, string $configuredCompiler = ''): string
{
if ($configuredCompiler !== '') {
return $configuredCompiler;
}
$compilerEnv = getenv('PHPX_CC');
if ($compilerEnv) {
return $compilerEnv;
}
if (!$platform instanceof Windows) {
$cxxEnv = getenv('CXX');
if ($cxxEnv) {
return $cxxEnv;
}
}
return $platform->getDefaultCompiler();
}
/**
* 创建指定类型的编译器后端
*/
public static function createByName(string $compilerName, PlatformBase $platform): CompilerBackend
{
$normalized = self::normalizeCompilerName($compilerName);
$lowerCommand = strtolower($compilerName);
if (str_contains($normalized, 'clang') || str_contains($lowerCommand, 'clang')) {
$linker = $platform instanceof Windows ? Clang::detectWindowsLinker() : null;
return new Clang($platform, $compilerName, $linker);
}
if (
$normalized === 'gcc' ||
$normalized === 'g++' ||
$normalized === 'c++' ||
str_ends_with($normalized, '-gcc') ||
str_ends_with($normalized, '-g++') ||
str_contains($lowerCommand, 'g++') ||
str_contains($lowerCommand, 'c++') ||
str_contains($lowerCommand, 'gcc')
) {
return new Gcc($platform, $compilerName);
}
if ($normalized === 'msvc' || $normalized === 'cl') {
if (!$platform instanceof Windows) {
throw new \RuntimeException("MSVC compiler is only supported on Windows");
}
return new Msvc($platform, $compilerName);
}
throw new \RuntimeException("Unsupported compiler: {$compilerName}");
}
/**
* 自动检测并创建编译器和平台
*/
public static function autoDetect(string $compilerName = '', ?PlatformBase $platform = null): array
{
// 创建平台
$platform ??= \TypePhp\Platform\PlatformFactory::create();
// 创建编译器
$compilerName = self::detectCompilerName($platform, $compilerName);
$compiler = self::createByName($compilerName, $platform);
return [
'platform' => $platform,
'compiler' => $compiler,
];
}
public static function isCommandExecutable(string $command): bool
{
$program = self::getCommandProgram($command);
if ($program === '') {
return false;
}
if (self::isPathLikeCommand($program)) {
return is_file($program) && is_executable($program);
}
$path = getenv('PATH');
if ($path === false || $path === '') {
return false;
}
$extensions = [''];
if (DIRECTORY_SEPARATOR === '\\') {
$pathext = getenv('PATHEXT') ?: '.COM;.EXE;.BAT;.CMD';
$extensions = array_filter(array_map('strtolower', explode(';', $pathext)));
if (preg_match('/\.[A-Za-z0-9]+$/', $program)) {
array_unshift($extensions, '');
}
}
foreach (explode(PATH_SEPARATOR, $path) as $dir) {
if ($dir === '') {
continue;
}
foreach ($extensions as $extension) {
$candidate = rtrim($dir, DIRECTORY_SEPARATOR . '/\\') . DIRECTORY_SEPARATOR . $program . $extension;
if (is_file($candidate) && is_executable($candidate)) {
return true;
}
}
}
return false;
}
public static function getCommandProgram(string $command): string
{
$command = trim($command);
if ($command === '') {
return '';
}
if ($command[0] === '"' || $command[0] === "'") {
$quote = $command[0];
$end = strpos($command, $quote, 1);
if ($end !== false) {
return substr($command, 1, $end - 1);
}
}
$firstToken = strtok($command, " \t\r\n");
return $firstToken === false ? '' : $firstToken;
}
private static function normalizeCompilerName(string $compilerName): string
{
$firstToken = self::getCommandProgram($compilerName);
if ($firstToken === '') {
return '';
}
$name = basename(str_replace('\\', '/', $firstToken));
$name = strtolower($name);
return preg_replace('/\.exe$/', '', $name);
}
private static function isPathLikeCommand(string $program): bool
{
return str_contains($program, '/')
|| str_contains($program, '\\')
|| preg_match('/^[A-Za-z]:[\/\\\\]/', $program) === 1;
}
}