feat(compiler): 添加外部库集成和编译优化功能

- 支持通过 -I/--include-path 添加自定义头文件搜索路径
- 支持通过 -D/--define 定义预处理器宏用于条件编译
- 实现 LTO 链接时优化功能 (--lto) 提升性能和减小体积
- 添加 clang-format 代码格式化支持 (--format)
- 支持通过 -l/-L 参数链接外部库和指定库搜索路径
- 添加配置文件方式定义链接库和搜索路径
- 更新文档包含新功能使用说明和示例
pull/3/head
韩天峰 2 months ago
parent b8247d4cc7
commit 02a91bba95
  1. 202
      docs/COMPILER_CLI.md
  2. 19
      src/Php/Backend/Clang.php
  3. 19
      src/Php/Backend/Gcc.php
  4. 21
      src/Php/Backend/Msvc.php
  5. 32
      src/Php/CompilerBase.php
  6. 40
      src/Php/Constants.php
  7. 121
      src/Php/Translator.php

@ -319,6 +319,14 @@ OPTIONS:
-m, --mode <mode> Compilation mode, -m bin(binary) or -m ext(extension), default: bin
-j, --job <num> Number of parallel compilation jobs (default: 4)
--no-literal-strings Disable literal strings optimization
-I, --include-path Add an additional C++ include directory (repeatable)
-D, --define <macro> Define a preprocessor macro (repeatable, e.g. -D FOO=bar)
--dry Dry run: only generate C++ code, skip compilation and linking
--lto Enable Link Time Optimization (-flto)
--format Enable clang-format code formatting (disabled by default)
-l, --link-lib <lib> Link against a library (repeatable, e.g. -lcurl)
-L, --link-path <dir> Add a library search path (repeatable, e.g. -L/usr/local/lib)
--build-dir <dir> Specify build directory for generated C++ code
EXAMPLES:
./bin/compiler.php examples/hello.php
@ -367,6 +375,194 @@ EXAMPLES:
./bin/compiler.php project.yml -O2 -j 16 -v
```
### 场景六:使用外部 C++ 库
```bash
# 添加自定义头文件路径和预处理器宏
./bin/compiler.php app.php -I /opt/mylib/include -I ../shared/include -D MY_DEBUG=1 -O2
```
---
### 12. `-I <dir>` / `--include-path <dir>` - 添加头文件搜索路径
**别名**: `--include-path`
**类型**: 可重复参数
添加额外的 C++ 头文件(`.h` / `.hpp`)搜索目录。编译器的 include 路径由两部分组成:
1. 系统默认路径(PHP 头文件、PHPX 头文件等)
2. 用户通过 `-I` 指定的自定义路径
**适用场景**:
- ✅ 引用外部 C/C++ 库的头文件
- ✅ 项目有自定义的 C++ 扩展代码
- ✅ 多个项目共享的头文件目录
**可重复使用**: 可以多次指定 `-I` 添加多个目录:
```bash
./bin/compiler.php app.php \
-I /opt/openssl/include \
-I /opt/mylib/include \
-I ../shared/headers
```
**与 C++ 编译器等价**: `-I <dir>` 直接传递给 GCC/Clang/MSVC 的 `-I` 选项。
---
### 13. `-D <macro>` / `--define <macro>` - 定义预处理器宏
**别名**: `--define`
**类型**: 可重复参数
定义 C++ 预处理器宏,等价于在 C++ 代码中使用 `#define`。使用 `name=value` 格式。
**适用场景**:
- ✅ 条件编译(`#ifdef MY_FEATURE` / `#ifndef MY_FEATURE`
- ✅ 功能开关(`-D ENABLE_LOGGING=1`)
- ✅ 调试标志(`-D DEBUG_LEVEL=3`)
- ✅ 版本号定义(`-D APP_VERSION=\"2.0\"`)
**格式说明**:
| 格式 | 等价 C++ 代码 | 说明 |
|------|--------------|------|
| `-D FOO` | `#define FOO` | 无值宏(值为空) |
| `-D FOO=1` | `#define FOO 1` | 整数值宏 |
| `-D FOO=bar` | `#define FOO bar` | 字符串值宏 |
| `-D FOO=\"bar\"` | `#define FOO "bar"` | 引号字符串宏 |
**可重复使用**: 可以多次指定 `-D` 定义多个宏:
```bash
./bin/compiler.php app.php \
-D MY_DEBUG=1 \
-D LOG_LEVEL=3 \
-D APP_NAME=\\"MyApp\"
```
**与 C++ 编译器等价**:
| 编译器 | 产生的编译标志 |
|--------|-------------|
| GCC / Clang | `-D<macro>` |
| MSVC | `/D<macro>` |
**示例: 条件编译控制功能开关**:
```bash
# 启用调试日志
./bin/compiler.php app.php -D ENABLE_LOGGING=1 -O2
# 生产环境(关闭调试)
./bin/compiler.php app.php -O2
```
---
### 14. `--lto` - 链接时优化
**类型**: 开关(无参数)
启用链接时优化(Link Time Optimization, LTO),允许编译器在链接阶段跨编译单元进行优化,可显著提升运行时性能和减小二进制体积。
**编译器适配**:
| 编译器 | 编译阶段标志 | 链接阶段标志 |
|--------|-----------|-----------|
| GCC | `-flto` | `-flto` |
| Clang | `-flto` | `-flto` |
| MSVC | `/GL` | `/LTCG` |
**适用场景**:
- ✅ 生产环境部署(配合 `-O2``-O3`
- ✅ 对性能有极致要求的应用
- ✅ 需要减小二进制体积的场景
- ⚠ 会增加链接时间
**示例**:
```bash
# 启用 LTO 的生产环境编译
./bin/compiler.php app.php -O2 --lto
# 与自定义 include 和 define 组合使用
./bin/compiler.php app.php -O3 --lto -I /opt/lib/include -D NDEBUG=1
```
---
### 15. `--format` - 代码格式化
**类型**: 开关(无参数)
**默认**: 关闭
启用 `clang-format` 对生成的 C++ 代码进行自动格式化。由于格式化会增加编译时间,默认关闭。需要系统中安装了 `clang-format` 才能生效。
**适用场景**:
- ✅ 需要审查生成的 C++ 代码
- ✅ 团队开发需要统一的代码风格
- ⚠ 会增加编译时间
**示例**:
```bash
# 编译时启用代码格式化
./bin/compiler.php app.php --format
# 结合优化使用
./bin/compiler.php app.php -O2 --format
```
如果系统未安装 `clang-format`,使用 `--format` 时会显示警告并跳过格式化。
---
### 16. `-l <lib>` / `--link-lib <lib>` - 链接库
**别名**: `--link-lib`
**类型**: 可重复参数
指定要链接的库,等价于 GCC/Clang 的 `-l<lib>` 选项。实际产生的标志为 `-l<lib>`
**适用场景**:
- ✅ 链接第三方 C/C++ 库(如 `-lcurl`、`-lssl`)
- ✅ 链接自定义编译的静态库/动态库
- ✅ 多库依赖的项目
**可重复使用**: 可以多次指定 `-l` 链接多个库:
```bash
./bin/compiler.php app.php \
-lcurl \
-lssl \
-lcrypto
# 等价的长格式
./bin/compiler.php app.php --link-lib curl --link-lib ssl --link-lib crypto
```
**与 GCC/Clang 等价**: `-l<lib>` 直接传递给链接器的 `-l` 选项,链接 `lib<lib>.so``lib<lib>.a`
---
### 17. `-L <dir>` / `--link-path <dir>` - 库搜索路径
**别名**: `--link-path`
**类型**: 可重复参数
添加库文件搜索路径,等价于 GCC/Clang 的 `-L<dir>` 选项。实际产生的标志为 `-L<dir>`
**适用场景**:
- ✅ 链接非标准路径下的库文件
- ✅ 使用自定义编译的本地库
- ✅ 链接项目内部的私有库
**可重复使用**: 可以多次指定 `-L` 添加多个搜索路径:
```bash
./bin/compiler.php app.php \
-L/usr/local/lib \
-L/opt/custom/lib \
-lmycustom
# 等价的长格式
./bin/compiler.php app.php --link-path /usr/local/lib --link-path /opt/custom/lib --link-lib mycustom
```
**与 GCC/Clang 等价**: `-L<dir>` 直接传递给链接器的 `-L` 选项。
---
## 🔧 编译器选择
@ -509,13 +705,14 @@ convert: /path/to/file.php
- ✅ 生成对应的 `.cpp` 文件
- ✅ 处理类型映射
### 阶段四:格式化 (Format)
### 阶段四:格式化 (Format)(需 `--format` 开启)
```
format: /path/to/build/file.cpp
cd /path && clang-format -i /path/to/build/file.cpp
```
- 🔘 需 `--format` 参数显式开启
- ✅ 使用 clang-format 格式化 C++ 代码
- ✅ 确保代码风格一致
@ -650,6 +847,9 @@ skip: /path/to/file.php
# 高性能构建
./bin/compiler.php app.php -O3 -j 16 -p
# 外部库集成构建
./bin/compiler.php app.php -I /opt/mylib/include -D ENABLE_FEATURE=1 -O2
# 调试构建
./bin/compiler.php app.php -O0 -v --debug

@ -420,6 +420,18 @@ class Clang extends CompilerBackend
$cmd .= ' ' . $config['cxxflags'];
}
// 用户自定义预处理器宏
if (!empty($config['user_defines'])) {
foreach ($config['user_defines'] as $define) {
$cmd .= ' -D' . $define;
}
}
// LTO(链接时优化)
if (!empty($config['lto'])) {
$cmd .= ' -flto';
}
return $cmd;
}
@ -472,7 +484,12 @@ class Clang extends CompilerBackend
if (!empty($config['sanitize'])) {
$cmd .= ' -fsanitize=' . $config['sanitize'];
}
// LTO(链接时优化)
if (!empty($config['lto'])) {
$cmd .= ' -flto';
}
return $cmd;
}
}

@ -302,6 +302,18 @@ class Gcc extends CompilerBackend
$cmd .= ' ' . $config['cxxflags'];
}
// 用户自定义预处理器宏
if (!empty($config['user_defines'])) {
foreach ($config['user_defines'] as $define) {
$cmd .= ' -D' . $define;
}
}
// LTO(链接时优化)
if (!empty($config['lto'])) {
$cmd .= ' -flto';
}
return $cmd;
}
@ -336,7 +348,12 @@ class Gcc extends CompilerBackend
$cmd .= ' -fsanitize=undefined';
}
}
// LTO(链接时优化)
if (!empty($config['lto'])) {
$cmd .= ' -flto';
}
return $cmd;
}
}

@ -389,16 +389,28 @@ class Msvc extends CompilerBackend
$cmd .= ' ' . $config['cxxflags'];
}
// 用户自定义预处理器宏
if (!empty($config['user_defines'])) {
foreach ($config['user_defines'] as $define) {
$cmd .= ' /D' . $define;
}
}
// LTO(全程序优化 /GL + 链接时代码生成 /LTCG)
if (!empty($config['lto'])) {
$cmd .= ' /GL';
}
return $cmd;
}
/**
* 构建链接选项(实现抽象方法)
*/
public function buildLinkOptions(array $config = []): string
{
$cmd = '';
// 调试
if (!empty($config['debug'])) {
$cmd .= ' /DEBUG';
@ -420,6 +432,11 @@ class Msvc extends CompilerBackend
// nologo
$cmd .= ' /nologo';
// LTO(链接时代码生成)
if (!empty($config['lto'])) {
$cmd .= ' /LTCG';
}
return $cmd;
}

@ -242,14 +242,19 @@ class CompilerBase extends \PhpAot\Core\Translator
protected string $cxxFlags = '';
protected string $cxxStd = 'c++17';
protected string $ldflags = '';
protected array $linkLibs = []; // --link-lib / -l: user-specified libraries to link
protected array $linkPaths = []; // --link-path / -L: user-specified library search paths
protected int $floatPrecision = 17;
protected bool $debug = false;
protected bool $formatCode = true;
protected bool $formatCode = false; // --format: enable clang-format (disabled by default)
protected bool $printBacktraceOnError = true;
protected bool $noLiteralStrings = false;
protected bool $noConsole = false; // Windows: hide console window
protected string $sanitize = ''; // Sanitizer type (address, undefined, etc.)
protected bool $dryRun = false; // Dry run: only generate C++ code, skip compile & link
protected array $userIncludePaths = []; // --include-path / -I: user-provided C++ include dirs
protected array $userDefines = []; // --define / -D: user-provided preprocessor macros
protected bool $enableLto = false; // --lto: enable Link Time Optimization (-flto)
protected string $file;
protected string $dir;
@ -743,6 +748,31 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->buildDir;
}
public function getUserIncludePaths(): array
{
return $this->userIncludePaths;
}
public function getUserDefines(): array
{
return $this->userDefines;
}
public function isLtoEnabled(): bool
{
return $this->enableLto;
}
public function getLinkLibs(): array
{
return $this->linkLibs;
}
public function getLinkPaths(): array
{
return $this->linkPaths;
}
public function getRelativePath($path, $cwd = ''): string
{
$cwd = $cwd ?: getcwd();

@ -175,6 +175,46 @@ class Constants
'required' => false,
'noValue' => true,
],
'include-path' => [
'prefix' => 'I',
'longPrefix' => 'include-path',
'description' => 'Add an additional C++ include directory (repeatable)',
'required' => false,
'multiple' => true,
],
'define' => [
'prefix' => 'D',
'longPrefix' => 'define',
'description' => 'Define a preprocessor macro (repeatable, e.g. -D FOO=bar)',
'required' => false,
'multiple' => true,
],
'lto' => [
'longPrefix' => 'lto',
'description' => 'Enable Link Time Optimization (-flto)',
'required' => false,
'noValue' => true,
],
'format' => [
'longPrefix' => 'format',
'description' => 'Enable clang-format code formatting (disabled by default)',
'required' => false,
'noValue' => true,
],
'link-lib' => [
'prefix' => 'l',
'longPrefix' => 'link-lib',
'description' => 'Link against a library (repeatable, e.g. -lcurl)',
'required' => false,
'multiple' => true,
],
'link-path' => [
'prefix' => 'L',
'longPrefix' => 'link-path',
'description' => 'Add a library search path (repeatable, e.g. -L/usr/local/lib)',
'required' => false,
'multiple' => true,
],
];
/**

@ -223,6 +223,12 @@ class Translator extends Preprocessor
$climate->tab()->out('--sanitize <type> Enable sanitizers (address, undefined, etc.)');
$climate->tab()->out('--build-dir <dir> Specify build directory for generated C++ code (default: <root>/build)');
$climate->tab()->out('--dry Dry run: only generate C++ code, skip compilation and linking');
$climate->tab()->out('-I, --include-path <dir> Add an additional C++ include directory (repeatable)');
$climate->tab()->out('-D, --define <macro> Define a preprocessor macro (repeatable, e.g. -D FOO=bar)');
$climate->tab()->out('--lto Enable Link Time Optimization (-flto)');
$climate->tab()->out('--format Enable clang-format code formatting (disabled by default)');
$climate->tab()->out('-l, --link-lib <lib> Link against a library (repeatable, e.g. -lcurl)');
$climate->tab()->out('-L, --link-path <dir> Add a library search path (repeatable, e.g. -L/usr/local/lib)');
$climate->br();
$climate->bold('EXAMPLES:');
@ -238,6 +244,7 @@ class Translator extends Preprocessor
$climate->tab()->out($cmd . ' app.php -r -O2 -- --flag1 value1');
$climate->tab()->out($cmd . ' hello.php --dry (only generate C++ code, skip compilation)');
$climate->tab()->out($cmd . ' app.php --build-dir /tmp/mybuild -O2 (specify build directory)');
$climate->tab()->out($cmd . ' app.php -I /opt/mylib/include -D MY_DEBUG=1 -O2 (custom includes and defines)');
$climate->br();
}
@ -312,6 +319,78 @@ class Translator extends Preprocessor
if ($this->climate->arguments->defined('dry')) {
$this->dryRun = true;
}
// 用户自定义 C++ include 路径(直接从 argv 解析以支持多值)
$this->userIncludePaths = $this->parseRepeatableArgv(['-I', '--include-path']);
// 用户自定义预处理器宏(直接从 argv 解析以支持多值)
$this->userDefines = $this->parseRepeatableArgv(['-D', '--define']);
// 链接时优化
if ($this->climate->arguments->defined('lto')) {
$this->enableLto = true;
}
// clang-format 代码格式化(默认关闭,需显式 --format 开启)
if ($this->climate->arguments->defined('format')) {
$clangFormatVersion = shell_exec('clang-format --version');
if (!empty($clangFormatVersion)) {
$this->formatCode = true;
} else {
$this->climate->warning('--format requested but clang-format not found, skipping formatting');
}
}
// 用户自定义链接库(直接从 argv 解析以支持多值)
$this->linkLibs = $this->parseRepeatableArgv(['-l', '--link-lib']);
// 用户自定义库搜索路径(直接从 argv 解析以支持多值)
$this->linkPaths = $this->parseRepeatableArgv(['-L', '--link-path']);
}
/**
* 从原始 $argv 中解析可重复参数,支持 -X val 和 --long val 两种形式。
* CLImate 的 multiple 选项只能保留最后一个值,因此需要手动解析。
*
* @param string[] $flags 要匹配的标志列表,如 ['-I', '--include-path']
* @return string[] 收集到的所有值
*/
protected function parseRepeatableArgv(array $flags): array
{
global $argv;
$values = [];
for ($i = 1; $i < count($argv); $i++) {
// 精确匹配标志(如 -I, --include-path)
if (in_array($argv[$i], $flags, true) && isset($argv[$i + 1]) && $argv[$i + 1] !== '' && $argv[$i + 1][0] !== '-') {
$values[] = $argv[$i + 1];
$i++; // 跳过值
}
// 合并形式:-I/path 或 --include-path=/path
elseif (!$this->isLongFlagWithEquals($argv[$i], $flags, $values)) {
// 检查短标志合并:-I/path
foreach ($flags as $flag) {
if (strlen($flag) === 2 && $flag[0] === '-') {
$short = substr($flag, 1);
if (preg_match('/^-' . preg_quote($short, '/') . '(.+)$/', $argv[$i], $m)) {
$values[] = $m[1];
}
}
}
}
}
return $values;
}
/**
* 处理 --flag=value 格式的长标志
*/
private function isLongFlagWithEquals(string $arg, array $flags, array &$values): bool
{
foreach ($flags as $flag) {
if (str_starts_with($flag, '--') && preg_match('/^' . preg_quote($flag, '/') . '=(.+)$/', $arg, $m)) {
$values[] = $m[1];
return true;
}
}
return false;
}
private function showVersion(): void
@ -489,11 +568,6 @@ class Translator extends Preprocessor
}
}
$clangFormatVersion = shell_exec('clang-format --version');
if (empty($clangFormatVersion)) {
$this->formatCode = false;
}
$files = $this->getFiles($path);
// 应用 ignorePaths 过滤
if (!empty($this->ignorePaths)) {
@ -1312,8 +1386,14 @@ CODE;
protected function getCompileCommandOptions(): array
{
// 包含路径:系统路径 + 用户自定义路径
$includePaths = $this->getIncludePaths();
if (!empty($this->userIncludePaths)) {
$includePaths = array_merge($includePaths, $this->userIncludePaths);
}
return [
'include_paths' => $this->getIncludePaths(),
'include_paths' => $includePaths,
'optimize' => $this->optimizeLevel,
'debug' => $this->debug,
'sanitize' => $this->sanitize,
@ -1324,6 +1404,8 @@ CODE;
'prof_output' => $this->targetName . '.prof',
'suppressed_warnings' => Constants::MSVC_SUPPRESSED_WARNINGS ?? [],
'cxxflags' => $this->cxxFlags,
'user_defines' => $this->userDefines,
'lto' => $this->enableLto,
];
}
@ -1365,6 +1447,16 @@ CODE;
$ldflags .= ' -lprofiler';
}
// 用户通过 --link-lib / -l 指定的链接库
foreach ($this->linkLibs as $lib) {
$ldflags .= ' -l' . $lib;
}
// 用户通过 --link-path / -L 指定的库搜索路径
foreach ($this->linkPaths as $path) {
$ldflags .= ' -L' . escapeshellarg($path);
}
$options = [
'library_paths' => $this->getLibraryPaths(),
'libraries' => $this->getLibraries(),
@ -1373,6 +1465,7 @@ CODE;
'no_console' => $this->noConsole,
'build_mode' => $this->buildMode,
'sanitize' => $this->sanitize,
'lto' => $this->enableLto,
];
$rpaths = $this->getPlatform()->getDefaultRpaths(
@ -1798,6 +1891,22 @@ CODE;
}
}
// 读取 link-libs(支持中横线和下划线)
$linkLibs = $cfg['link-libs'] ?? $cfg['link_libs'] ?? null;
if (!empty($linkLibs) && is_array($linkLibs)) {
foreach ($linkLibs as $lib) {
$this->linkLibs[] = (string)$lib;
}
}
// 读取 link-paths(支持中横线和下划线)
$linkPaths = $cfg['link-paths'] ?? $cfg['link_paths'] ?? null;
if (!empty($linkPaths) && is_array($linkPaths)) {
foreach ($linkPaths as $path) {
$this->linkPaths[] = (string)$path;
}
}
// 读取 name
if (!empty($cfg['name'])) {
$this->setTargetName($cfg['name']);

Loading…
Cancel
Save