feat(compiler): 添加编译器选择机制和平台优化

- 实现基于环境变量的编译器选择(PHPX_CC > CXX > 平台默认)
- 支持通过 YAML 配置文件指定 cpp-compiler 选项
- 为 macOS、Linux、Windows 自动选择合适的默认编译器
- 优化 PHP 路径检测逻辑,移除硬编码路径依赖
- 为 extension 和 bin 模式统一添加 PHP 库链接
- 在 macOS 上实现 RPATH 支持以解决动态库加载问题
- 添加对多种构建模式名称的映射支持(extension/ext/binary/bin/cli)
pull/1/head
韩天峰 4 months ago
parent 054560c0f9
commit 028a83dcb2
  1. 73
      docs/COMPILER_CLI.md
  2. 65
      src/Php/CompilerBase.php
  3. 18
      src/Php/Platform/Linux.php
  4. 72
      src/Php/Platform/Macos.php
  5. 13
      src/Php/Platform/PlatformBase.php
  6. 11
      src/Php/Translator.php

@ -369,6 +369,79 @@ EXAMPLES:
---
## 🔧 编译器选择
### 默认编译器
编译器会根据操作系统自动选择合适的 C++ 编译器:
| 平台 | 默认编译器 | 说明 |
|------|----------|------|
| **macOS** | `clang++` | 系统自带,性能优秀 |
| **Linux** | `g++` | GNU 编译器集合 |
| **Windows** | `cl` (MSVC) | Microsoft Visual C++ |
### 通过环境变量切换编译器
你可以通过设置环境变量来覆盖默认的编译器选择。
#### 方法一:PHPX_CC(推荐)
```bash
# macOS 使用 GCC(如果已安装)
export PHPX_CC=g++
php bin/compiler.php examples/hello.php
# Linux 使用 Clang
export PHPX_CC=clang++
php bin/compiler.php examples/hello.php
# Windows 使用 Clang
set PHPX_CC=clang++
php bin\compiler.php examples\hello.php
```
#### 方法二:CXX(标准环境变量)
```bash
# 使用标准的 CXX 环境变量
export CXX=clang++
php bin/compiler.php examples/hello.php
```
**优先级**:`PHPX_CC` > `CXX` > 平台默认
### 通过配置文件指定编译器
在项目 YAML 配置文件中,可以使用 `cpp-compiler` 选项指定编译器:
```yaml
name: myapp
type: bin
cpp-compiler: clang++ # 或 g++, cl
sources:
- src/*.php
```
支持的编译器名称:
- `clang++` / `clang` - LLVM Clang 编译器
- `g++` / `gcc` - GNU GCC 编译器
- `cl` / `msvc` - Microsoft Visual C++(仅 Windows)
### 检查当前使用的编译器
编译时会显示使用的编译器信息:
```bash
$ php bin/compiler.php examples/hello.php
Initialized new architecture: macOS + Clang
prepare: examples/hello.php
prepare completed: 1 source files in total
...
```
---
## 📁 支持的输入类型
### 1. 单个 PHP 文件

@ -335,8 +335,26 @@ class CompilerBase extends \PhpAot\Core\Translator
// 初始化新的 Platform 和 Backend 抽象层
$this->initializeNewArchitecture();
} else {
// Unix/Linux/macOS 默认使用 g++
$this->cppCompiler = 'g++';
// Unix/Linux/macOS 下检测编译器
// 优先级:环境变量 PHPX_CC > CXX > 平台默认
$compilerEnv = getenv('PHPX_CC') ?: getenv('CXX');
if ($compilerEnv) {
// 用户通过环境变量指定编译器
$this->cppCompiler = $compilerEnv;
$this->climate->info("Using compiler from environment: {$this->cppCompiler}");
} else {
// 根据平台选择默认编译器
if ($this->isMacos()) {
// macOS 默认使用 Clang(系统自带)
$this->cppCompiler = 'clang++';
$this->climate->info('Using Clang compiler (clang++)');
} else {
// Linux 默认使用 GCC
$this->cppCompiler = 'g++';
$this->climate->info('Using GCC compiler (g++)');
}
}
// 初始化新的 Platform 和 Backend 抽象层
$this->initializeNewArchitecture();
@ -582,7 +600,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'C:\php';
} else {
// Unix/Linux/macOS 下获取 PHP 路径
// 优先级:环境变量 PHP_HOME > /opt/php-8.4 > php-config > which php
// 优先级:环境变量 PHP_HOME > php-config > which php
// 1. 尝试环境变量 PHP_HOME
$phpDir = getenv('PHP_HOME');
@ -590,25 +608,13 @@ class CompilerBase extends \PhpAot\Core\Translator
return rtrim($phpDir, '\/');
}
// 2. 尝试常见的 PHP 8.4 安装路径
$commonPaths = [
'/opt/php-8.4',
'/usr/local/php-8.4',
'/usr/local/php84',
];
foreach ($commonPaths as $path) {
if (is_dir($path) && is_executable($path . '/bin/php')) {
return $path;
}
}
// 3. 使用 php-config 获取 PHP 路径
// 2. 使用 php-config 获取 PHP 路径(优先从 PATH 中查找)
$phpDir = shell_exec('php-config --prefix 2>/dev/null');
if (!empty($phpDir)) {
return trim($phpDir);
}
// 4. 如果 php-config 不可用,尝试从 which php 推断
// 3. 如果 php-config 不可用,尝试从 which php 推断
$phpExe = trim(shell_exec('which php 2>/dev/null'));
if ($phpExe && file_exists($phpExe)) {
$phpDir = dirname(dirname($phpExe));
@ -2674,20 +2680,20 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
// bin 模式需要链接 PHP 库
if ($this->buildMode === 'bin') {
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
// Windows 使用已检测的库文件
// extension 和 bin 模式需要链接 PHP 库
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
// Windows bin 模式使用已检测的库文件
if ($this->buildMode === 'bin') {
if (!empty($this->windowsPhpEmbedLib)) {
$libraries[] = '"' . $this->windowsPhpEmbedLib . '"';
}
if (!empty($this->windowsPhpCoreLib)) {
$libraries[] = '"' . $this->windowsPhpCoreLib . '"';
}
} else {
// Linux/macOS: 添加 php 库
$libraries[] = 'php';
}
} else {
// Linux/macOS: extension 和 bin 模式都需要添加 php 库
$libraries[] = 'php';
}
return $this->platform->getLibraryFlags($libraries);
@ -2820,6 +2826,17 @@ class CompilerBase extends \PhpAot\Core\Translator
'sanitize' => $this->sanitize,
];
// 添加 RPATH(通过 Platform 层获取,仅 macOS 需要)
if ($this->platform !== null) {
$rpaths = $this->platform->getDefaultRpaths(
$this->getPhpxDir(),
$this->getPhpDir()
);
if (!empty($rpaths)) {
$config['rpath'] = $rpaths;
}
}
$cmd .= $this->compilerBackend->buildLinkOptions($config);
// 最后添加库文件(平台相关,必须在链接选项之后)

@ -156,16 +156,16 @@ class Linux extends PlatformBase
*/
private function findPhpConfig(string $phpDir): ?string
{
$candidates = [
$phpDir . '/bin/php-config',
'/usr/bin/php-config',
'/usr/local/bin/php-config',
];
// 优先使用 PHP_DIR 指定的路径
$candidate = $phpDir . '/bin/php-config';
if (is_executable($candidate)) {
return $candidate;
}
foreach ($candidates as $path) {
if (is_executable($path)) {
return $path;
}
// 回退到 PATH 中查找(通过 which 命令)
$whichResult = trim(shell_exec('which php-config 2>/dev/null'));
if ($whichResult && is_executable($whichResult)) {
return $whichResult;
}
return null;

@ -128,20 +128,56 @@ class Macos extends PlatformBase
}
/**
* 构建 PHP 包含路径
* 构建 PHP 包含路径(使用 php-config 动态获取)
*/
public function buildPhpIncludePaths(string $phpDir): array
{
// 优先使用 php-config 获取包含路径
$phpConfigPath = $this->findPhpConfig($phpDir);
if ($phpConfigPath) {
$includes = shell_exec("{$phpConfigPath} --includes 2>/dev/null");
if ($includes) {
// 解析 -I/path 格式的路径
preg_match_all('/-I([^\s]+)/', $includes, $matches);
if (!empty($matches[1])) {
// 过滤不存在的路径并返回
return array_filter($matches[1], 'is_dir');
}
}
}
// 回退到硬编码路径(兼容旧版本)
$paths = [
$phpDir . '/include',
$phpDir . '/include/main',
$phpDir . '/include/TSRM',
$phpDir . '/include/Zend',
$phpDir . '/include/php',
$phpDir . '/include/php/main',
$phpDir . '/include/php/TSRM',
$phpDir . '/include/php/Zend',
$phpDir . '/include/php/ext',
];
// 过滤不存在的路径
return array_filter($paths, 'is_dir');
}
/**
* 查找 php-config 可执行文件
*/
private function findPhpConfig(string $phpDir): ?string
{
// 优先使用 PHP_DIR 指定的路径
$candidate = $phpDir . '/bin/php-config';
if (is_executable($candidate)) {
return $candidate;
}
// 回退到 PATH 中查找(通过 which 命令)
$whichResult = trim(shell_exec('which php-config 2>/dev/null'));
if ($whichResult && is_executable($whichResult)) {
return $whichResult;
}
return null;
}
/**
* 构建 PHP 库路径
@ -185,4 +221,30 @@ class Macos extends PlatformBase
'is_shared' => $hasEmbed,
];
}
/**
* 获取默认的 RPATH 路径列表(macOS 需要)
*/
public function getDefaultRpaths(?string $phpxDir = null, ?string $phpDir = null): array
{
$rpaths = [];
// 添加 phpx 库路径
if ($phpxDir !== null) {
$phpxLibDir = $phpxDir . '/lib';
if (is_dir($phpxLibDir)) {
$rpaths[] = $phpxLibDir;
}
}
// 添加 PHP 库路径
if ($phpDir !== null) {
$phpLibDir = $phpDir . '/lib';
if (is_dir($phpLibDir)) {
$rpaths[] = $phpLibDir;
}
}
return $rpaths;
}
}

@ -68,4 +68,17 @@ abstract class PlatformBase
{
return implode($this->getPathSeparator(), $parts);
}
/**
* 获取默认的 RPATH 路径列表(仅 macOS 需要)
*
* @param string|null $phpxDir phpx 目录路径
* @param string|null $phpDir PHP 目录路径
* @return array RPATH 路径数组
*/
public function getDefaultRpaths(?string $phpxDir = null, ?string $phpDir = null): array
{
// 默认返回空数组,由子类重写
return [];
}
}

@ -1301,7 +1301,16 @@ CODE;
// 读取 type/build-mode(支持中横线和下划线)
$buildMode = $cfg['build-mode'] ?? $cfg['type'] ?? null;
if (!empty($buildMode)) {
$this->setBuildMode($buildMode);
// 映射常见的类型名称到内部 buildMode
$modeMap = [
'extension' => 'ext',
'ext' => 'ext',
'binary' => 'bin',
'bin' => 'bin',
'cli' => 'bin',
];
$mappedMode = $modeMap[strtolower($buildMode)] ?? $buildMode;
$this->setBuildMode($mappedMode);
}
// 读取 ignore(支持中横线和下划线)

Loading…
Cancel
Save