diff --git a/docs/COMPILER_CLI.md b/docs/COMPILER_CLI.md index de71c9b6..f5c4a589 100644 --- a/docs/COMPILER_CLI.md +++ b/docs/COMPILER_CLI.md @@ -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 文件 diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 13ff542f..9bb049ba 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.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); // 最后添加库文件(平台相关,必须在链接选项之后) diff --git a/src/Php/Platform/Linux.php b/src/Php/Platform/Linux.php index 84e920bd..cfc32d08 100644 --- a/src/Php/Platform/Linux.php +++ b/src/Php/Platform/Linux.php @@ -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; diff --git a/src/Php/Platform/Macos.php b/src/Php/Platform/Macos.php index ba559f01..160d0218 100644 --- a/src/Php/Platform/Macos.php +++ b/src/Php/Platform/Macos.php @@ -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; + } } diff --git a/src/Php/Platform/PlatformBase.php b/src/Php/Platform/PlatformBase.php index 67335c49..639d2cb3 100644 --- a/src/Php/Platform/PlatformBase.php +++ b/src/Php/Platform/PlatformBase.php @@ -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 []; + } } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 57755615..fb4ce92e 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -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(支持中横线和下划线)