feat(compiler): 实现 C++ 标准独立配置功能

- 将 C++ 标准从 cxxflags 中分离,支持 --cxx-std 参数配置
- 添加 project.yml 中的 cxx_std 配置项,支持独立设置标准版本
- 实现 Windows 和 Unix 平台的差异化默认标准(Windows 使用 c++17,Unix 使用 c++14)
- 添加 /NODEFAULTLIB 选项解决 Windows CRT 库冲突问题
- 增加 --debug-info 参数支持,自动禁用优化并生成调试信息
- 更新配置优先级规则文档,明确命令行参数、YAML 配置和默认值的覆盖关系
- 添加 C++ 标准配置指南和调试模式使用说明
- 修复 cxxflags 中标准标志被忽略的问题
pull/1/head
韩天峰 4 months ago
parent 20436c5e41
commit 334594c659
  1. 443
      examples/win32-hello/CONFIG_PRIORITY_RULES.md
  2. 360
      examples/win32-hello/CXX_STD_CONFIG_GUIDE.md
  3. 356
      examples/win32-hello/DEBUG_GUIDE.md
  4. 455
      examples/win32-hello/DEBUG_MODE_GUIDE.md
  5. 370
      examples/win32-hello/MSVC_WARNINGS_SUPPRESSION.md
  6. 385
      examples/win32-hello/SANITIZER_GUIDE.md
  7. 528
      examples/win32-hello/YAML_CONFIG_NAMING_CONVENTION.md
  8. 9
      project.yml
  9. 5
      src/Php/CompilerBase.php
  10. 9
      src/Php/Constants.php
  11. 121
      src/Php/Translator.php

@ -0,0 +1,443 @@
# 配置优先级规则说明
## 📋 概述
PHPX 编译器的配置遵循明确的优先级规则,确保用户可以灵活地控制编译行为。
---
## 🎯 优先级顺序(从高到低)
```
1. 命令行参数(最高优先级)
2. YAML 配置文件
3. 平台默认值(最低优先级)
```
---
## 💡 工作原理
### 执行流程
```mermaid
graph TD
A[启动编译器] --> B[解析命令行参数]
B --> C{输入类型?}
C -->|YAML 文件| D[解析 YAML 配置]
C -->|单 PHP 文件| E[跳过 YAML]
C -->|目录| F[跳过 YAML]
D --> G[应用命令行参数覆盖]
E --> G
F --> G
G --> H[开始编译]
```
### 详细说明
1. **构造函数阶段**
- 解析命令行参数
- **不立即应用**到属性
- 仅处理 `--help``--version`
2. **YAML 解析阶段**(仅当输入是 `.yml` 文件时)
- 读取 `project.yml` 配置
- 应用到编译器属性
- 设置默认值
3. **命令行参数应用阶段**
- 检查哪些命令行参数被定义
- **覆盖** YAML 配置或默认值
- 确保命令行参数优先级最高
---
## 📊 配置项示例
### 示例 1:C++ 标准版本
#### YAML 配置
```yaml
# project.yml
cxx-std: c++14
```
#### 命令行覆盖
```bash
php bin/compiler.php project.yml --cxx-std=c++17
```
#### 结果
✅ 使用 **c++17**(命令行优先级更高)
---
### 示例 2:构建模式
#### YAML 配置
```yaml
# project.yml
build-mode: bin
```
#### 命令行覆盖
```bash
php bin/compiler.php project.yml --mode=ext
```
#### 结果
✅ 使用 **ext**(命令行优先级更高)
---
### 示例 3:编译选项
#### YAML 配置
```yaml
# project.yml
cxx-flags:
- -Wall
- -O2
```
#### 命令行覆盖
```bash
php bin/compiler.php project.yml -O3
```
#### 结果
✅ 优化级别为 **3**(命令行优先级更高)
✅ cxx-flags 仍为 `-Wall -O2`(YAML 配置)
---
## 🔍 不同输入类型的处理
### 类型 1:YAML 配置文件
```bash
php bin/compiler.php project.yml --cxx-std=c++17
```
**执行流程:**
1. ✅ 解析 `project.yml`
2. ✅ 应用 YAML 中的配置
3. ✅ 用 `--cxx-std=c++17` 覆盖
**适用场景:**
- 大型项目
- 需要复杂配置
- 团队协作
---
### 类型 2:单个 PHP 文件
```bash
php bin/compiler.php hello.php --cxx-std=c++17 -O2
```
**执行流程:**
1. ❌ 跳过 YAML 解析
2. ✅ 直接应用命令行参数
3. ✅ 使用平台默认值作为基础
**适用场景:**
- 快速测试
- 简单脚本
- 临时编译
---
### 类型 3:目录
```bash
php bin/compiler.php src/ --mode=ext --cxx-std=c++17
```
**执行流程:**
1. ❌ 跳过 YAML 解析
2. ✅ 扫描目录中的所有 PHP 文件
3. ✅ 应用命令行参数
4. ✅ 使用平台默认值作为基础
**适用场景:**
- 批量编译
- 扩展模块
- 多文件项目
---
## 📝 完整示例
### 项目配置
```yaml
# project.yml
name: my-app
build-mode: bin
version: 1.0.0
cxx-std: c++14
cxx-flags:
- -Wall
- -Wextra
ld-flags:
- -lm
sources:
- src/main.php
- src/utils.php
```
### 场景 1:使用默认配置
```bash
php bin/compiler.php project.yml
```
**结果:**
- cxx-std: **c++14**(来自 YAML)
- build-mode: **bin**(来自 YAML)
- cxx-flags: **-Wall -Wextra**(来自 YAML)
---
### 场景 2:部分覆盖
```bash
php bin/compiler.php project.yml --cxx-std=c++17
```
**结果:**
- cxx-std: **c++17**(命令行覆盖)
- build-mode: **bin**(来自 YAML)
- cxx-flags: **-Wall -Wextra**(来自 YAML)
---
### 场景 3:完全覆盖
```bash
php bin/compiler.php project.yml --cxx-std=c++20 --mode=ext -O3
```
**结果:**
- cxx-std: **c++20**(命令行覆盖)
- build-mode: **ext**(命令行覆盖)
- optimize-level: **3**(命令行覆盖)
- cxx-flags: **-Wall -Wextra**(来自 YAML,未被覆盖)
---
### 场景 4:单文件编译
```bash
php bin/compiler.php test.php --cxx-std=c++17 -O2
```
**结果:**
- cxx-std: **c++17**(命令行)
- build-mode: **bin**(默认值)
- optimize-level: **2**(命令行)
- 无 YAML 配置
---
## ⚙ 支持的配置项
### 可从 YAML 读取的配置
| 配置项 | YAML 键 | 命令行参数 | 说明 |
|--------|---------|-----------|------|
| 项目名称 | `name` | `--output` | 输出文件名 |
| 构建模式 | `build-mode` / `type` | `--mode` | bin 或 ext |
| C++ 标准 | `cxx-std` | `--cxx-std` | c++14/17/20 |
| 编译选项 | `cxx-flags` | - | C++ 编译标志 |
| 链接选项 | `ld-flags` | - | 链接器标志 |
| 源文件 | `sources` | - | 源文件列表 |
| 忽略列表 | `ignore` | - | 忽略的文件 |
### 只能从命令行设置的配置
| 配置项 | 命令行参数 | 说明 |
|--------|-----------|------|
| 优化级别 | `-O <level>` | 0-3 |
| 调试信息 | `--debug-info` | 启用调试 |
| 性能分析 | `--profile` | 启用 profiling |
| Sanitizer | `--sanitize` | 内存检测 |
| 并行任务 | `-j <num>` | 并行编译数 |
| 隐藏控制台 | `--no-console` | Windows GUI |
---
## 🎨 最佳实践
### 1. YAML 中设置默认值
```yaml
# project.yml - 团队共享的默认配置
name: my-app
build-mode: bin
cxx-std: c++17
cxx-flags:
- -Wall
- -Wextra
```
---
### 2. 命令行用于临时覆盖
```bash
# 开发时使用调试模式
php bin/compiler.php project.yml --debug-info
# 发布时使用优化
php bin/compiler.php project.yml -O3
# 测试不同的 C++ 标准
php bin/compiler.php project.yml --cxx-std=c++20
```
---
### 3. 单文件快速测试
```bash
# 不需要 YAML,直接编译
php bin/compiler.php test.php -O2 --cxx-std=c++17
```
---
## 🐛 常见问题
### Q1: 为什么命令行参数没有生效?
A: 确保使用了正确的参数名称:
```bash
# ✅ 正确
php bin/compiler.php project.yml --cxx-std=c++17
# ❌ 错误(参数名不对)
php bin/compiler.php project.yml --cxx_std=c++17
```
---
### Q2: YAML 和命令行都设置了同一个值,哪个生效?
A: **命令行参数始终优先**
```yaml
# project.yml
cxx-std: c++14
```
```bash
php bin/compiler.php project.yml --cxx-std=c++17
# 结果:使用 c++17
```
---
### Q3: 可以在 YAML 中设置优化级别吗?
A: 目前不支持。优化级别只能通过命令行设置:
```bash
php bin/compiler.php project.yml -O2
```
---
### Q4: 如何查看当前使用的配置?
A: 编译时会显示相关信息:
```
prepare: project.yml
...
C++ standard: c++17
Build mode: bin
Optimization: O2
...
```
---
## 📚 技术实现
### 代码位置
**Translator.php:**
```php
// 1. 构造函数:解析但不应用
public function __construct(string $rootPath)
{
$this->climate->arguments->parse();
// 不立即应用参数
}
// 2. YAML 解析:应用配置
protected function parseProjectYaml(string $path): array
{
// 读取 YAML 并应用到属性
$this->cxxStd = $cfg['cxx-std'] ?? 'c++17';
$this->buildMode = $cfg['build-mode'] ?? 'bin';
// ...
}
// 3. 应用命令行:覆盖配置
protected function applyCommandLineArguments(): void
{
if ($this->climate->arguments->defined('cxx-std')) {
$this->cxxStd = $this->climate->arguments->get('cxx-std');
}
// ...
}
// 4. getFiles:控制流程
public function getFiles(string $path): array
{
if (is_yml($path)) {
$this->parseProjectYaml($path); // 先 YAML
$this->applyCommandLineArguments(); // 后命令行
} else {
$this->applyCommandLineArguments(); // 直接命令行
}
}
```
---
## 🎉 总结
### 核心原则
1. ✅ **命令行参数优先级最高** - 用户可以随时覆盖
2. ✅ **YAML 提供默认值** - 简化日常使用
3. ✅ **平台默认值兜底** - 确保总能运行
4. ✅ **清晰的执行顺序** - 易于理解和调试
### 优先级图示
```
用户意图
命令行参数 ────────→ 最高优先级,立即生效
YAML 配置 ─────────→ 中等优先级,提供默认值
平台默认值 ────────→ 最低优先级,保证可用性
```
遵循这些规则,您可以灵活地控制编译行为!

@ -0,0 +1,360 @@
# C++ 标准配置指南
## 📋 概述
PHPX 编译器现在支持独立配置 C++ 标准版本,不再需要从 `cxxflags` 中提取。这使得配置更加清晰和易于管理。
---
## 🚀 使用方法
### 1. 命令行方式
使用 `--cxx-std` 参数指定 C++ 标准版本:
```bash
# 使用 C++14
php bin/compiler.php app.php --cxx-std=c++14
# 使用 C++17(默认)
php bin/compiler.php app.php --cxx-std=c++17
# 使用 C++20
php bin/compiler.php app.php --cxx-std=c++20
# Windows MSVC
php bin/compiler.php app.php --cxx-std=c++17
# Linux/macOS GCC/Clang
php bin/compiler.php app.php --cxx-std=c++17
```
---
### 2. project.yml 配置文件方式
`project.yml` 中添加 `cxx_std` 配置项:
```yaml
name: my-app
type: bin
sources:
- src/main.php
- src/utils.php
# C++ 标准版本(独立配置)
cxx_std: c++17
# 其他编译选项(不包含 C++ 标准)
cxxflags:
- -Wall
- -Wextra
ldflags:
- -lm
```
**注意:**
- ✅ `cxx_std` 专门用于指定 C++ 标准
- ✅ `cxxflags` 用于其他编译选项(如警告、优化等)
- ❌ 不要在 `cxxflags` 中包含 `-std=c++XX``/std:c++XX`
---
## 📊 支持的 C++ 标准
| 标准 | MSVC 标志 | GCC/Clang 标志 | 说明 |
|------|----------|----------------|------|
| c++14 | `/std:c++14` | `-std=c++14` | 默认(Unix) |
| c++17 | `/std:c++17` | `-std=c++17` | **推荐**(Windows 默认) |
| c++20 | `/std:c++20` | `-std=c++20` | 最新标准 |
| c++23 | `/std:c++23` | `-std=c++23` | 实验性支持 |
---
## 💡 平台默认值
### Windows (MSVC)
```php
// CompilerBase.php
$this->cxxStd = 'c++17'; // MSVC 更好的支持 C++17
```
**原因:** MSVC 对 C++17 的支持更成熟,C++14 的部分特性在 MSVC 中实现不完整。
---
### Linux/macOS (GCC/Clang)
```php
// CompilerBase.php
$this->cxxStd = 'c++14';
```
**原因:** C++14 是最广泛支持的标准,兼容性最好。
---
## 🔧 优先级规则
C++ 标准的设置遵循以下优先级(从高到低):
1. **命令行参数** `--cxx-std=XXX`(最高优先级)
2. **project.yml 配置** `cxx_std: XXX`
3. **平台默认值**(Windows: c++17, Unix: c++14)
**示例:**
```yaml
# project.yml
cxx_std: c++14
```
```bash
# 命令行覆盖配置文件
php bin/compiler.php project.yml --cxx-std=c++17
# 最终使用 c++17
```
---
## ⚠ 注意事项
### 1. 不要在 cxxflags 中重复指定
❌ **错误做法:**
```yaml
cxx_std: c++17
cxxflags:
- -std=c++17 # 重复指定!
```
✅ **正确做法:**
```yaml
cxx_std: c++17
cxxflags:
- -Wall
- -Wextra
```
---
### 2. cxxflags 中的标准会被忽略
如果 `cxxflags` 中包含了 `-std=``/std:`,编译器会使用 `$this->cxxStd` 的值,而不是 `cxxflags` 中的值。
**代码逻辑:**
```php
// Windows
if (!str_contains($this->cxxflags, '/std:')) {
$cmd .= ' /std:' . $this->cxxStd; // 使用 cxxStd
}
// Unix
if (!str_contains($this->cxxflags, ' -std=')) {
$cmd .= ' -std=' . $this->cxxStd; // 使用 cxxStd
}
```
---
### 3. 选择合适的 C++ 标准
| 场景 | 推荐标准 | 原因 |
|------|---------|------|
| 最大兼容性 | c++14 | 所有编译器都支持 |
| 现代特性 | c++17 | 结构化绑定、if constexpr 等 |
| 最新特性 | c++20 | concepts、coroutines、modules |
| Windows 项目 | c++17 | MSVC 支持更好 |
| Linux 项目 | c++14 或 c++17 | 根据需求选择 |
---
## 📝 完整示例
### 示例 1:基本项目
```yaml
# project.yml
name: hello-world
type: bin
sources:
- src/main.php
cxx_std: c++17
```
编译命令:
```bash
php bin/compiler.php project.yml
```
生成的编译命令:
```bash
# Windows
cl /std:c++17 /O0 ...
# Linux
g++ -std=c++17 -O0 ...
```
---
### 示例 2:带额外编译选项
```yaml
# project.yml
name: my-app
type: bin
sources:
- src/*.php
cxx_std: c++17
cxxflags:
- -Wall
- -Wextra
- -Wpedantic
ldflags:
- -lm
- -lpthread
```
---
### 示例 3:命令行覆盖
```yaml
# project.yml
name: my-app
cxx_std: c++14 # 配置文件中的默认值
```
```bash
# 使用 C++17 覆盖配置文件
php bin/compiler.php project.yml --cxx-std=c++17
```
---
## 🎯 迁移指南
如果您之前的 `project.yml` 中有这样的配置:
### ❌ 旧配置(不推荐)
```yaml
cxxflags:
- -std=c++17
- -Wall
- -O2
```
### ✅ 新配置(推荐)
```yaml
cxx_std: c++17
cxxflags:
- -Wall
- -O2
```
**优势:**
- ✅ 配置更清晰
- ✅ 更容易维护
- ✅ 跨平台兼容更好
- ✅ 避免重复和冲突
---
## 🔍 验证方法
编译时查看输出,确认 C++ 标准是否正确设置:
```bash
php bin/compiler.php project.yml --verbose
```
应该看到类似这样的输出:
```
Compiling main.cc...
g++ -std=c++17 -O0 -Wall ...
```
或者 Windows:
```
cl /std:c++17 /O0 /Wall ...
```
---
## 🐛 常见问题
### Q1: 为什么我的 C++17 特性不起作用?
A: 检查是否正确设置了 `cxx_std`
```yaml
cxx_std: c++17 # 确保是 c++17 而不是 c++14
```
或者使用命令行:
```bash
php bin/compiler.php app.php --cxx-std=c++17
```
---
### Q2: cxxflags 中的 -std= 会被忽略吗?
A: 是的,编译器会优先使用 `$this->cxxStd` 的值。建议在 `cxxflags` 中不要包含 `-std=``/std:`
---
### Q3: 可以在不同文件中使用不同的 C++ 标准吗?
A: 不可以。C++ 标准是整个项目的统一配置,所有文件使用相同的标准。
---
### Q4: C++20 支持如何?
A:
- **MSVC**: 需要 Visual Studio 2019 16.11+ 或 VS 2022
- **GCC**: 需要 GCC 10+
- **Clang**: 需要 Clang 10+
如果您的编译器不支持 C++20,请使用 C++17。
---
## 📚 相关资源
- [C++14 特性](https://en.cppreference.com/w/cpp/14)
- [C++17 特性](https://en.cppreference.com/w/cpp/17)
- [C++20 特性](https://en.cppreference.com/w/cpp/20)
- [MSVC 编译器选项](https://docs.microsoft.com/cpp/build/reference/std-specify-language-standard-version)
- [GCC C++ 标准](https://gcc.gnu.org/projects/cxx-status.html)
---
## 🎉 总结
通过将 C++ 标准从 `cxxflags` 中独立出来,我们实现了:
1. ✅ **配置更清晰** - `cxx_std` 专门用于标准版本
2. ✅ **更易维护** - 不需要在 `cxxflags` 中查找标准选项
3. ✅ **跨平台兼容** - 自动适配 MSVC 和 GCC/Clang 的标志
4. ✅ **灵活覆盖** - 命令行可以覆盖配置文件
5. ✅ **避免冲突** - 不会重复指定标准版本
希望这个指南能帮助您更好地使用 C++ 标准配置!

@ -0,0 +1,356 @@
# Windows 程序调试指南
## 常见问题:Debug Assertion Failed
### 问题描述
运行编译后的程序时出现 "Debug Assertion Failed" 错误对话框。
### 原因分析
1. **CRT 库冲突**:混合使用了调试版和发布版的 C 运行时库
2. **内存访问错误**:访问了无效内存或空指针
3. **初始化失败**:PHP 或 PHPX 未正确初始化
---
## 🔧 解决方案
### 方案 1:添加 /NODEFAULTLIB 选项(已实现)
编译器现在会自动添加以下链接选项来排除冲突的 CRT 库:
```cpp
/NODEFAULTLIB:LIBCMT // 排除静态多线程 CRT
/NODEFAULTLIB:LIBCMTD // 排除静态多线程调试 CRT
/NODEFAULTLIB:MSVCRTD // 排除动态多线程调试 CRT
```
这样可以确保只使用动态多线程 CRT(`/MD`),避免库冲突。
**重新编译:**
```powershell
php bin/compiler.php examples/win32-hello/project.yml --no-console
```
---
### 方案 2:使用 Visual Studio 调试器
#### 步骤 1:以调试模式编译
```powershell
# 启用调试信息
php bin/compiler.php examples/win32-hello/project.yml --debug-info --no-console
```
这会在编译时添加 `/Zi` 选项,生成完整的调试符号。
#### 步骤 2:在 Visual Studio 中打开可执行文件
1. 打开 Visual Studio 2022
2. 菜单:**文件** → **打开** → **项目/解决方案**
3. 选择 `win32_hello.exe`
4. 或者直接将 exe 文件拖入 Visual Studio
#### 步骤 3:设置断点
1. 在代码视图中找到您想调试的位置
2. 点击行号左侧的灰色区域,设置红色断点
3. 或者按 `F9` 在当前行设置断点
#### 步骤 4:启动调试
1. 按 `F5` 或点击 **调试** → **开始调试**
2. 程序会在断点处暂停
3. 可以查看变量值、调用堆栈等信息
#### 步骤 5:附加到正在运行的进程
如果程序已经启动但出现问题:
1. 在 Visual Studio 中:**调试** → **附加到进程**
2. 找到 `win32_hello.exe` 进程
3. 点击 **附加**
4. 程序会在下一个断点或异常处暂停
---
### 方案 3:使用 WinDbg 调试
WinDbg 是 Windows SDK 中的强大调试工具。
#### 安装 WinDbg
```powershell
# 通过 Microsoft Store 安装
winget install Microsoft.WinDbg
```
#### 使用方法
```powershell
# 启动 WinDbg 并加载程序
windbg .\win32_hello.exe
# 或者附加到正在运行的进程
windbg -p <PID>
```
#### 常用命令
```
g # 继续执行 (Go)
k # 显示调用堆栈 (Stack trace)
dv # 显示局部变量
!analyze -v # 详细分析崩溃原因
bp <地址> # 设置断点
```
---
### 方案 4:添加日志输出
由于 GUI 程序没有控制台,可以将调试信息写入日志文件:
```php
<?php
function debug_log(string $message): void
{
$logFile = __DIR__ . '/debug.log';
$timestamp = date('Y-m-d H:i:s');
file_put_contents(
$logFile,
"[$timestamp] $message\n",
FILE_APPEND | LOCK_EX
);
}
function main()
{
debug_log("程序启动");
try {
date_default_timezone_set('Asia/Shanghai');
debug_log("时区设置成功");
debug_log("准备显示第一个消息框");
messagebox(0, "测试消息", "调试", 0);
debug_log("第一个消息框已关闭");
debug_log("准备显示第二个消息框");
messagebox(0, "程序即将退出", "再见", 0);
debug_log("程序正常退出");
} catch (\Exception $e) {
debug_log("错误: " . $e->getMessage());
debug_log("堆栈跟踪: " . $e->getTraceAsString());
}
}
```
**查看日志:**
```powershell
Get-Content .\debug.log -Wait
```
---
### 方案 5:使用消息框调试
对于简单的调试,可以使用消息框显示变量值:
```php
<?php
function main()
{
date_default_timezone_set('Asia/Shanghai');
// 调试:显示当前时间
$currentTime = date('Y-m-d H:i:s');
messagebox(0, "当前时间: $currentTime", "调试信息", 0);
// 调试:检查函数是否存在
$funcExists = function_exists('messagebox') ? '存在' : '不存在';
messagebox(0, "messagebox 函数: $funcExists", "调试信息", 0);
// 主逻辑
messagebox(0, "欢迎!", "Hello", 0);
}
```
---
## 🛡 预防措施
### 1. 确保正确的编译选项
```php
// 在 project.yml 或编译命令中
- 使用 /MD(动态 CRT)而不是 /MT(静态 CRT)
- 使用 /O2 优化(发布模式)或 /Od(调试模式)
- 添加 /EHsc 启用 C++ 异常处理
```
### 2. 检查依赖库
确保所有依赖的 DLL 都存在且版本匹配:
```powershell
# 检查程序依赖的 DLL
dumpbin /dependents win32_hello.exe
# 或使用 Dependency Walker
depends.exe win32_hello.exe
```
### 3. 验证 PHP 环境
```powershell
# 检查 PHP 版本
php -v
# 检查 PHP 扩展
php -m
# 确认使用的是正确的 PHP(8.4+)
where php
```
---
## 📊 调试检查清单
当遇到 "Debug Assertion Failed" 时,按以下步骤检查:
- [ ] 是否使用了正确的 PHP 版本(8.4+)?
- [ ] Visual Studio 环境是否正确设置?
- [ ] 编译时是否使用了 `/MD` 选项?
- [ ] 链接时是否添加了 `/NODEFAULTLIB` 选项?
- [ ] 所有依赖的 DLL 是否都存在?
- [ ] 程序是否有足够的权限运行?
- [ ] 是否尝试过以管理员身份运行?
- [ ] 是否查看了 Windows 事件查看器中的错误日志?
---
## 🔍 高级调试技巧
### 1. 使用 ProcMon 监控系统调用
[Process Monitor](https://docs.microsoft.com/en-us/sysinternals/downloads/procmon) 可以监控:
- 文件访问
- 注册表操作
- 进程/线程活动
- DLL 加载
**使用方法:**
1. 下载并运行 ProcMon
2. 设置过滤器:`Process Name is win32_hello.exe`
3. 运行程序
4. 观察是否有 `ACCESS DENIED``NAME NOT FOUND` 错误
### 2. 使用 Application Verifier
Windows 自带的 Application Verifier 可以检测:
- 内存泄漏
- 句柄泄漏
- 堆损坏
**启用方法:**
```powershell
# 以管理员身份运行
appverif -enable Heaps -for win32_hello.exe
```
### 3. 查看 Windows 事件日志
```powershell
# 查看应用程序事件日志
Get-EventLog -LogName Application -Source "Application Error" -Newest 10
# 或打开事件查看器
eventvwr.msc
```
---
## 💡 常见问题解答
### Q: 为什么只在调试模式下出错,发布模式正常?
A: 调试模式启用了额外的检查和断言,会捕获潜在问题。发布模式 optimizations 可能掩盖了这些问题。
### Q: 如何禁用 Debug Assertion 对话框?
A:
```cpp
// 在 C++ 代码开头添加
#define _CRT_DISABLE_PERFCRIT_LOCKS
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
```
或在 PHP 中设置环境变量:
```php
putenv('_NO_DEBUG_HEAP=1');
```
### Q: 程序闪退,看不到错误信息怎么办?
A:
1. 使用日志记录(见方案 4)
2. 在 cmd 中运行:`win32_hello.exe > output.txt 2>&1`
3. 使用 ProcMon 监控
4. 检查 Windows 事件查看器
### Q: 如何在没有 Visual Studio 的情况下调试?
A:
1. 使用 WinDbg(免费)
2. 添加详细的日志输出
3. 使用消息框显示调试信息
4. 查看 Windows 事件日志
---
## 📚 相关资源
- [Visual Studio 调试教程](https://docs.microsoft.com/visualstudio/debugger/)
- [WinDbg 文档](https://docs.microsoft.com/windows-hardware/drivers/debugger/)
- [CRT 库冲突解决方案](https://docs.microsoft.com/cpp/build/reference/nodefaultlib-ignore-library)
- [Windows 调试技术](https://docs.microsoft.com/windows/win32/debug/)
---
## 🎯 快速修复步骤
如果遇到 "Debug Assertion Failed",按以下顺序尝试:
1. **重新编译**(使用最新的修复)
```powershell
php bin/compiler.php examples/win32-hello/project.yml --no-console
```
2. **清理构建目录**
```powershell
Remove-Item -Recurse -Force build/
php bin/compiler.php examples/win32-hello/project.yml --no-console
```
3. **添加调试信息重新编译**
```powershell
php bin/compiler.php examples/win32-hello/project.yml --debug-info --no-console
```
4. **使用 Visual Studio 调试**
- 打开 exe 文件
- 按 F5 启动调试
- 查看输出窗口的错误信息
5. **添加日志输出**
- 在关键位置添加 `debug_log()` 调用
- 查看日志文件定位问题
希望这些方法能帮助您成功调试程序!

@ -0,0 +1,455 @@
# 调试模式使用指南
## 📋 概述
`--debug-info` 参数用于启用调试模式,它会自动:
1. **禁用优化**(`-O0` 或 `/Od`
2. **添加调试信息**(`-g` 或 `/Zi`
3. **生成符号文件**(`.pdb` 文件,Windows)
这使得您可以使用调试器(如 GDB、LLDB、Visual Studio)来调试编译后的程序。
---
## 🚀 快速开始
### Windows (MSVC)
```powershell
# 启用调试模式
php bin/compiler.php your-app.php --debug-info
# 结合其他选项
php bin/compiler.php your-app.php --debug-info --no-console
# 运行程序
.\your-app.exe
# 使用 Visual Studio 调试
# 1. 打开 your-app.exe
# 2. 按 F5 启动调试
# 3. 设置断点,查看变量
```
### Linux/macOS (GCC/Clang)
```bash
# 启用调试模式
php bin/compiler.php your-app.php --debug-info
# 结合其他选项
php bin/compiler.php your-app.php --debug-info --sanitize=address
# 运行程序
./your-app
# 使用 GDB 调试
gdb ./your-app
(gdb) break main
(gdb) run
(gdb) next
(gdb) print variable_name
```
---
## 🔍 调试模式 vs 发布模式
| 特性 | 调试模式 (`--debug-info`) | 发布模式 (默认) |
|------|--------------------------|----------------|
| 优化级别 | `-O0` / `/Od` (禁用) | `-O2` / `/O2` (最大速度) |
| 调试信息 | ✅ 生成 (`-g` / `/Zi`) | ❌ 不生成 |
| 符号文件 | ✅ 生成 (`.pdb`) | ❌ 不生成 |
| 执行速度 | 较慢 | 快 |
| 文件大小 | 较大 | 较小 |
| 适用场景 | 开发、调试 | 生产环境 |
---
## 💡 使用示例
### 1. 基本调试
```powershell
# 编译带调试信息的版本
php bin/compiler.php debug-test.php --debug-info --no-console
# 在 Visual Studio 中调试
# - 打开 debug-test.exe
# - 设置断点
# - 按 F5 运行
# - 查看变量值、调用堆栈
```
### 2. 结合 AddressSanitizer
```powershell
# 同时启用调试信息和 AddressSanitizer
php bin/compiler.php asan-test.php --debug-info --sanitize=address --no-console
# 这样可以:
# - 看到源代码行号
# - 检测内存错误
# - 获得详细的错误报告
```
### 3. GDB 调试 (Linux)
```bash
# 编译
php bin/compiler.php app.php --debug-info
# 启动 GDB
gdb ./app
# GDB 常用命令
(gdb) break main # 在 main 函数设置断点
(gdb) break filename:10 # 在第 10 行设置断点
(gdb) run # 运行程序
(gdb) next # 执行下一行
(gdb) step # 进入函数
(gdb) print var # 打印变量值
(gdb) backtrace # 显示调用堆栈
(gdb) continue # 继续执行
(gdb) quit # 退出
```
### 4. LLDB 调试 (macOS)
```bash
# 编译
php bin/compiler.php app.php --debug-info
# 启动 LLDB
lldb ./app
# LLDB 常用命令
(lldb) breakpoint set --name main
(lldb) run
(lldb) next
(lldb) step
(lldb) frame variable
(lldb) thread backtrace
(lldb) continue
(lldb) quit
```
---
## 🛠 高级技巧
### 1. 条件断点
```gdb
# GDB
(gdb) break main if x > 10
# LLDB
(lldb) breakpoint set --name main --condition 'x > 10'
```
### 2. 观察点(Watchpoint)
```gdb
# 当变量改变时中断
(gdb) watch my_variable
(gdb) continue
```
### 3. 检查内存
```gdb
# GDB
(gdb) x/10x &array # 查看数组的前 10 个元素
(gdb) p *ptr@10 # 查看指针指向的 10 个元素
# LLDB
(lldb) memory read --format x --count 10 &array
```
### 4. 多线程调试
```gdb
# GDB
(gdb) info threads # 查看所有线程
(gdb) thread 2 # 切换到线程 2
(gdb) thread apply all bt # 所有线程的堆栈
# LLDB
(lldb) thread list
(lldb) thread select 2
(lldb) thread backtrace all
```
---
## 📊 性能对比
### 编译时间
| 模式 | 相对时间 |
|------|---------|
| 发布模式 (-O2) | 100% |
| 调试模式 (-O0 -g) | 80% (更快) |
### 运行时性能
| 模式 | 相对速度 | 内存使用 |
|------|---------|---------|
| 发布模式 (-O2) | 100% | 100% |
| 调试模式 (-O0 -g) | 30-50% | 120-150% |
### 文件大小
| 模式 | 可执行文件 | 符号文件 |
|------|-----------|---------|
| 发布模式 | 小 | 无 |
| 调试模式 | 大 | .pdb (Windows) / 嵌入 (Unix) |
---
## 🔧 平台特定说明
### Windows (MSVC)
**生成的文件:**
- `app.exe` - 可执行文件
- `app.pdb` - 程序数据库文件(包含调试信息)
**调试工具:**
- Visual Studio 2022(推荐)
- WinDbg
- Visual Studio Code + C++ 扩展
**注意事项:**
- PDB 文件必须与 EXE 在同一目录
- 不要删除 PDB 文件,否则无法调试
- 可以使用 `/DEBUG:FASTLINK` 加快链接速度
### Linux (GCC)
**调试信息:**
- 默认嵌入到可执行文件中
- 也可以使用 `-ggdb` 生成 GDB 专用信息
**调试工具:**
- GDB
- DDD (GDB 图形界面)
- Visual Studio Code + C++ 扩展
**优化选项:**
```bash
# 基本调试
-g
# GDB 专用
-ggdb
# 更多详细信息
-g3
# 仅调试宏
-ggdb3
```
### macOS (Clang)
**调试信息:**
- 默认使用 DWARF 格式
- 嵌入到可执行文件中
**调试工具:**
- LLDB(默认)
- Xcode
- Visual Studio Code + C++ 扩展
**特殊选项:**
```bash
# 生成 dSYM 文件(分离调试信息)
-g -Wl,-S
# 保留所有符号
-g -fno-eliminate-unused-debug-types
```
---
## 🐛 常见问题
### Q: 为什么调试模式下程序运行很慢?
A: 因为禁用了所有优化(`-O0`)。这是正常的,调试模式的目标是便于调试,而不是性能。
**解决方案:**
- 只在调试时使用 `--debug-info`
- 发布时使用 `-O2``-O3`
### Q: 调试器看不到某些变量?
A: 可能的原因:
1. 变量被优化掉了(即使使用 `-O0`
2. 变量超出了作用域
3. 调试信息不完整
**解决方案:**
```bash
# 使用更详细的调试信息
php bin/compiler.php app.php --debug-info
# 或者在 GCC/Clang 上
# 手动添加 -g3
```
### Q: 如何调试 Release 版本?
A: 不推荐,但可以:
```bash
# 保留调试信息但启用优化
php bin/compiler.php app.php -O2 --debug-info
```
注意:优化可能会使调试变得困难,因为代码可能被重排或内联。
### Q: PDB 文件太大怎么办?
A:
```powershell
# 使用增量链接
/link /INCREMENTAL
# 或使用 FASTLINK
/link /DEBUG:FASTLINK
```
### Q: 如何在没有调试器的情况下调试?
A:
1. 添加日志输出
2. 使用消息框显示变量值
3. 使用 AddressSanitizer 检测错误
4. 查看核心转储(core dump)
---
## 🎯 最佳实践
### 1. 开发工作流
```bash
# 日常开发
php bin/compiler.php app.php --debug-info
# 运行测试
./app
# 调试问题
gdb ./app
# 准备发布
php bin/compiler.php app.php -O2
```
### 2. 持续集成
```yaml
# .github/workflows/test.yml
- name: Debug Build
run: php bin/compiler.php tests/*.php --debug-info
- name: Run Tests with GDB
run: |
gdb -batch -ex "run" -ex "bt" ./test_app
- name: Release Build
run: php bin/compiler.php src/*.php -O2
```
### 3. 调试检查清单
遇到问题时:
- [ ] 是否使用 `--debug-info` 编译?
- [ ] 是否设置了断点?
- [ ] 是否查看了调用堆栈?
- [ ] 是否检查了变量值?
- [ ] 是否使用了 AddressSanitizer?
- [ ] 是否查看了日志文件?
### 4. 符号文件管理
**Windows:**
```powershell
# 保留 PDB 文件
Copy-Item app.pdb symbols/
# 发布时剥离符号
# PDB 文件不需要分发给用户
```
**Linux:**
```bash
# 分离调试信息
objcopy --only-keep-debug app app.debug
strip app
# 使用时
gdb -s app.debug ./app
```
---
## 📚 相关资源
- [GDB 用户手册](https://sourceware.org/gdb/current/onlinedocs/gdb/)
- [LLDB 教程](https://lldb.llvm.org/use/tutorial.html)
- [Visual Studio 调试](https://docs.microsoft.com/visualstudio/debugger/)
- [MSVC 调试选项](https://docs.microsoft.com/cpp/build/reference/z7-zi-ld-debug-information-format)
- [GCC 调试选项](https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html)
---
## 🔗 编译器命令参考
```powershell
# Windows - 基本调试
php bin/compiler.php app.php --debug-info
# Windows - 调试 + GUI
php bin/compiler.php app.php --debug-info --no-console
# Windows - 调试 + ASan
php bin/compiler.php app.php --debug-info --sanitize=address
# Linux/macOS - 基本调试
php bin/compiler.php app.php --debug-info
# Linux/macOS - 调试 + 多个 sanitizer
php bin/compiler.php app.php --debug-info --sanitize=address,undefined
# 自定义优化级别(不使用调试模式)
php bin/compiler.php app.php -O2
# 完全禁用优化(不生成调试信息)
php bin/compiler.php app.php -O0
```
---
## 💡 提示
1. **始终在开发时使用 `--debug-info`**
- 更容易找到 bug
- 更好的错误报告
- 支持调试器
2. **发布前移除 `--debug-info`**
- 更好的性能
- 更小的文件
- 更安全(不暴露符号)
3. **结合使用多种调试工具**
- 调试器(GDB/LLDB/VS)
- Sanitizer(AddressSanitizer 等)
- 日志记录
- Profiler
希望这个指南能帮助您有效使用调试模式!

@ -0,0 +1,370 @@
# MSVC 编译警告屏蔽说明
## 📋 概述
在 Windows 平台使用 MSVC 编译器编译 PHPX 项目时,会收到大量来自 Windows SDK 和 PHP SDK 头文件的警告。这些警告都是**编译器噪音**,不影响程序的正确性和功能。
本文档列出了所有被屏蔽的警告及其原因。
---
## 🔇 已屏蔽的警告列表
### C4244 - 类型转换可能丢失数据
```
warning C4244: 'argument': conversion from '__int64' to 'int', possible loss of data
```
**原因:** PHP 内部代码经常在 `int``size_t`/`__int64` 之间转换。
**安全性:** ✅ 安全 - 数值在小范围内,不会溢出。
**示例:**
```cpp
int len = strlen(str); // size_t -> int
```
---
### C4242 - 类型转换可能丢失数据(类似 C4244)
```
warning C4242: 'return': conversion from 'unsigned __int64' to 'unsigned int', possible loss of data
```
**原因:** 与 C4244 类似,但针对不同的类型组合。
**安全性:** ✅ 安全 - 已知范围内的转换。
---
### C4146 - 一元负运算符应用于无符号类型
```
warning C4146: unary minus operator applied to unsigned type, result still unsigned
```
**原因:** PHP 源码中使用了 `-UINT_MAX` 这样的表达式。
**安全性:** ✅ 安全 - 这是预期的行为,用于生成特定的位模式。
**示例:**
```cpp
unsigned int x = -1; // 实际上是 UINT_MAX
```
---
### C4820 - 结构体成员后有填充字节
```
warning C4820: 'struct_name': 'N' bytes padding added after data member 'member_name'
```
**原因:** MSVC 为了实现内存对齐,自动在结构体成员之间添加填充字节。
**安全性:** ✅ 完全正常 - 这是编译器的标准行为,所有编译器都会这样做。
**示例:**
```cpp
struct Example {
char a; // 1 byte
// 3 bytes padding here (for alignment)
int b; // 4 bytes
};
```
---
### C4464 - 相对包含路径含 ".."
```
warning C4464: relative include path contains '..'
```
**原因:** PHP SDK 头文件使用了 `#include "../xxx.h"` 的写法。
**安全性:** ✅ 安全 - 这只是包含路径的写法,不影响功能。
**示例:**
```cpp
#include "../main/php.h"
```
---
### C4365 - 有符号/无符号转换
```
warning C4365: 'argument': conversion from 'int' to 'unsigned int', signed/unsigned mismatch
```
**原因:** PHP 内部代码混合使用有符号和无符号整数。
**安全性:** ✅ 安全 - 数值在正数范围内,转换是安全的。
---
### C4127 - 条件表达式是常量
```
warning C4127: conditional expression is constant
```
**原因:** 常见于宏展开,如 `while(1)``if (sizeof(T) > 0)`
**安全性:** ✅ 完全正常 - 这是有意为之的代码模式。
**示例:**
```cpp
while (1) { // 无限循环
// ...
}
```
---
### C4668 - 未定义的宏当 0 处理
```
warning C4668: '__GNUC__' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
```
**原因:** PHP 源码中使用 `#ifdef __GNUC__` 来检测 GCC 编译器,在 MSVC 下这个宏未定义。
**安全性:** ✅ 预期行为 - `#ifdef` 会正确地检测到宏未定义。
**示例:**
```cpp
#ifdef __GNUC__
// GCC 特定代码
#else
// 其他编译器(包括 MSVC)
#endif
```
---
### C4626 / C5027 - 赋值运算符被隐式删除
```
warning C4626: 'class_name': assignment operator was implicitly defined as deleted
warning C5027: 'class_name': move assignment operator was implicitly defined as deleted
```
**原因:** PHP 结构体包含 `const` 成员或引用成员,导致编译器无法生成默认的赋值运算符。
**安全性:** ✅ 设计如此 - 这些结构体本来就不应该被赋值。
**示例:**
```cpp
struct Immutable {
const int value; // const 成员使赋值运算符被删除
};
```
---
### C5219 - 隐式转换警告
```
warning C5219: implicit conversion from 'type1' to 'type2', possible loss of data
```
**原因:** C++17 引入的新警告,检测潜在的精度丢失。
**安全性:** ✅ 提示信息 - 在已知范围内是安全的。
---
### C5220 - volatile 成员警告
```
warning C5220: 'member': a non-static data member with a volatile qualified type no longer corresponds to the C++ standard
```
**原因:** C++20 对 `volatile` 成员的规则有所改变。
**安全性:** ✅ 提示信息 - 不影响正确性。
---
## 🛠 实现方式
这些警告在 [Constants.php](file:///D:/workspace/compiler/src/Php/Constants.php#L156-L174) 中配置,并在 [CompilerBase.php](file:///D:/workspace/compiler/src/Php/CompilerBase.php#L2439-L2445) 中动态应用:
### 配置位置(Constants.php)
```php
/**
* MSVC 编译器警告屏蔽列表
* 这些警告来自 Windows SDK 和 PHP SDK 头文件,都是编译器噪音,不影响功能
*
* @var array<string, string> 键为警告编号,值为说明
*/
public const array MSVC_SUPPRESSED_WARNINGS = [
'4244' => '类型转换可能丢失数据 (int -> smaller type)',
'4242' => '类型转换可能丢失数据 (similar to C4244)',
'4146' => '一元负运算符应用于无符号类型',
'4820' => '结构体成员后有填充字节(内存对齐)',
'4464' => '相对包含路径含 ".."',
'4365' => '有符号/无符号转换',
'4127' => '条件表达式是常量(如 while(1))',
'4668' => '未定义的宏当 0 处理(#ifdef __GNUC__)',
'4626' => '赋值运算符被隐式删除(const 成员)',
'5027' => '移动赋值运算符被隐式删除',
'5219' => '隐式转换警告',
'5220' => 'volatile 成员警告',
];
```
### 应用位置(CompilerBase.php)
```php
// 禁用 PHP SDK 和 Windows SDK 头文件中的常见警告
// 这些警告都是编译器噪音,不影响功能(从 Constants 配置中读取)
foreach (Constants::MSVC_SUPPRESSED_WARNINGS as $code => $description) {
$cmd .= " /wd{$code}"; // C{$code}: {$description}
}
```
**优势:**
- ✅ 集中管理,易于维护
- ✅ 不是硬编码,可以动态修改
- ✅ 带有详细注释,说明每个警告的原因
- ✅ 可以轻松添加或删除警告
---
## 💡 为什么需要屏蔽这些警告?
### 1. **来源不可控**
这些警告来自:
- Windows SDK 头文件(微软提供)
- PHP SDK 头文件(PHP 官方提供)
- PHX 库头文件
我们无法修改这些第三方库的代码。
### 2. **数量巨大**
如果不屏蔽,编译时会输出数百甚至数千条警告信息,淹没真正重要的警告和错误。
### 3. **都是误报**
这些警告在实际运行中不会导致任何问题:
- 类型转换都在安全范围内
- 结构体填充是正常的内存对齐
- 宏检测按预期工作
### 4. **行业标准做法**
大型项目(如 Chromium、Firefox、Qt)都会屏蔽这些第三方库的警告。
---
## ⚠ 注意事项
### 不要屏蔽的警告
以下警告**不应该**被屏蔽,因为它们可能指示真正的问题:
- **C4700** - 使用了未初始化的变量
- **C4703** - 使用了可能未初始化的指针
- **C4996** - 使用了废弃的函数(如 `strcpy`
- **C6XXX** - Code Analysis 警告(潜在的安全问题)
### 如何添加新的警告屏蔽
如果您发现新的无害警告,可以在 [Constants.php](file:///D:/workspace/compiler/src/Php/Constants.php#L156-L174) 中添加:
```php
public const array MSVC_SUPPRESSED_WARNINGS = [
// ... 现有警告 ...
'XXXX' => '警告描述', // 添加新警告
];
```
**步骤:**
1. 打开 `src/Php/Constants.php`
2. 在 `MSVC_SUPPRESSED_WARNINGS` 数组中添加新条目
3. 格式:`'警告编号' => '说明文字'`
4. 保存文件,重新编译即可生效
**原则:**
1. 确认警告来自第三方库(Windows SDK、PHP SDK)
2. 确认警告不会影响程序正确性
3. 添加清晰的注释说明原因
4. 在本文档中记录
---
## 📊 效果对比
### 屏蔽前
```
Compiling hello-win.cc...
hello-win.cc
D:\workspace\php-8.4.20\SDK\include\Zend\zend_types.h(125): warning C4820: '_zval_struct': '4' bytes padding added after data member 'u1'
D:\workspace\php-8.4.20\SDK\include\Zend\zend_portability.h(345): warning C4464: relative include path contains '..'
D:\workspace\php-8.4.20\SDK\include\main\php.h(512): warning C4244: 'return': conversion from 'zend_long' to 'int', possible loss of data
... (数百条类似警告)
Successfully compiled 1 files
```
### 屏蔽后
```
Compiling hello-win.cc...
hello-win.cc
Successfully compiled 1 files
```
**清爽多了!** ✨
---
## 🔍 如何验证屏蔽是否有效
编译时观察输出:
1. ✅ 没有看到上述警告编号
2. ✅ 只看到真正的错误或您自己代码的警告
3. ✅ 编译成功且程序运行正常
如果仍然看到某些警告,检查:
- 警告编号是否在屏蔽列表中
- 是否有拼写错误(如 `/wd4244` 写成 `/wd424`
- 是否在正确的编译阶段添加(编译时,不是链接时)
---
## 📚 相关资源
- [MSVC 编译器警告文档](https://docs.microsoft.com/cpp/build/reference/compiler-warnings)
- [/wd (Disable Specific Warnings)](https://docs.microsoft.com/cpp/build/reference/wd-disable-specific-compiler-warnings)
- [PHP Windows 编译指南](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2)
---
## 🎯 总结
| 警告编号 | 类型 | 严重程度 | 是否需要关注 |
|---------|------|---------|------------|
| C4244/C4242 | 类型转换 | 低 | ❌ 否 |
| C4146 | 一元运算符 | 低 | ❌ 否 |
| C4820 | 结构体填充 | 信息 | ❌ 否 |
| C4464 | 包含路径 | 信息 | ❌ 否 |
| C4365 | 符号转换 | 低 | ❌ 否 |
| C4127 | 常量条件 | 信息 | ❌ 否 |
| C4668 | 宏未定义 | 信息 | ❌ 否 |
| C4626/C5027 | 运算符删除 | 设计 | ❌ 否 |
| C5219/C5220 | 新标准警告 | 提示 | ❌ 否 |
**所有这些警告都可以安全地忽略。**
---
希望这个文档能帮助您理解为什么需要屏蔽这些警告,以及它们为什么是安全的!

@ -0,0 +1,385 @@
# AddressSanitizer 使用指南
## 📋 概述
AddressSanitizer (ASan) 是一个快速的内存错误检测工具,可以检测:
- 堆缓冲区溢出/下溢
- 栈缓冲区溢出/下溢
- 全局缓冲区溢出/下溢
- 释放后使用(Use-after-free)
- 返回后使用(Use-after-return)
- 重复释放(Double-free)
- 内存泄漏
---
## 🚀 快速开始
### Windows (MSVC)
```powershell
# 启用 AddressSanitizer
php bin/compiler.php your-app.php --sanitize=address
# 或简写
php bin/compiler.php your-app.php --sanitize=addr
```
**要求:**
- Visual Studio 2019 16.9+ 或 Visual Studio 2022
- MSVC 编译器版本 19.29+
### Linux/macOS (GCC/Clang)
```bash
# 单个 sanitizer
php bin/compiler.php your-app.php --sanitize=address
# 多个 sanitizer(用逗号分隔)
php bin/compiler.php your-app.php --sanitize=address,undefined
# 可用的 sanitizer 类型:
# - address: 地址错误检测
# - undefined: 未定义行为检测
# - thread: 线程竞争检测
# - memory: 未初始化内存读取检测
# - leak: 内存泄漏检测
```
---
## 🔍 示例输出
当检测到内存错误时,AddressSanitizer 会输出详细的错误信息:
```
=================================================================
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000010
READ of size 4 at 0x602000000010 thread T0
#0 0x7ff6abc12345 in main D:\workspace\compiler\examples\test.php:10
#1 0x7ff6abc67890 in __scrt_common_main_seh
0x602000000010 is located 0 bytes to the right of 16-byte region [0x602000000000,0x602000000010)
allocated by thread T0 here:
#0 0x7ff6abc98765 in operator new[]
#1 0x7ff6abc12340 in main D:\workspace\compiler\examples\test.php:8
SUMMARY: AddressSanitizer: heap-buffer-overflow
=================================================================
```
---
## 💡 使用建议
### 1. 开发阶段启用
在开发和测试阶段启用 AddressSanitizer,可以帮助您尽早发现内存错误:
```powershell
# 编译带 AddressSanitizer 的版本
php bin/compiler.php debug-test.php --sanitize=address --no-console
# 运行测试
.\debug-test.exe
```
### 2. 不要与优化同时使用
AddressSanitizer 会降低程序性能(约 2x),建议:
- 开发时:`-O0 --sanitize=address`
- 发布时:`-O2`(不使用 sanitizer)
### 3. 结合调试信息使用
```powershell
# 同时启用调试信息和 AddressSanitizer
php bin/compiler.php app.php --debug-info --sanitize=address
```
这样可以在错误报告中看到源代码行号。
---
## 🛠 常见用例
### 检测数组越界
```php
<?php
function test_buffer_overflow()
{
$arr = [1, 2, 3, 4, 5];
// 这会触发 AddressSanitizer 错误
$value = $arr[10]; // 越界访问
return $value;
}
function main()
{
test_buffer_overflow();
}
```
### 检测释放后使用
```php
<?php
class TestClass
{
public function __destruct()
{
// 对象被销毁
}
}
function test_use_after_free()
{
$obj = new TestClass();
unset($obj); // 对象被销毁
// 如果继续访问 $obj,可能触发错误
// (PHP 的垃圾回收机制通常会防止这种情况)
}
function main()
{
test_use_after_free();
}
```
### 检测内存泄漏
```php
<?php
function test_memory_leak()
{
// 在 C++ 层分配的内存如果没有正确释放
// AddressSanitizer 会检测到
}
function main()
{
test_memory_leak();
}
```
---
## ⚙ 高级配置
### 环境变量
AddressSanitizer 支持通过环境变量进行配置:
#### Windows
```powershell
# 设置 ASan 选项
$env:ASAN_OPTIONS = "detect_leaks=1:print_stats=1"
# 运行程序
.\your-app.exe
```
#### Linux/macOS
```bash
# 设置 ASan 选项
export ASAN_OPTIONS="detect_leaks=1:print_stats=1"
# 运行程序
./your-app
```
### 常用选项
| 选项 | 说明 | 默认值 |
|------|------|--------|
| `detect_leaks` | 检测内存泄漏 | 1 |
| `print_stats` | 打印统计信息 | 0 |
| `abort_on_error` | 错误时中止程序 | 0 |
| `log_path` | 日志文件路径 | stderr |
| `halt_on_error` | 第一个错误后停止 | 1 |
**示例:**
```powershell
# Windows
$env:ASAN_OPTIONS = "detect_leaks=1:print_stats=1:log_path=asan.log"
.\your-app.exe
# Linux/macOS
export ASAN_OPTIONS="detect_leaks=1:print_stats=1:log_path=asan.log"
./your-app
```
---
## 🔧 平台特定说明
### Windows (MSVC)
**限制:**
- 仅支持 `address` sanitizer
- 需要 Visual Studio 2019 16.9+ 或更新版本
- 可能与某些第三方库不兼容
**注意事项:**
- AddressSanitizer 会增加可执行文件大小
- 运行时性能下降约 2x
- 内存使用增加约 2-3x
### Linux (GCC/Clang)
**支持的 sanitizer:**
- `address` - 地址错误
- `undefined` - 未定义行为
- `thread` - 线程竞争
- `memory` - 未初始化内存
- `leak` - 内存泄漏
**组合使用:**
```bash
# AddressSanitizer + UndefinedBehaviorSanitizer
php bin/compiler.php app.php --sanitize=address,undefined
# ThreadSanitizer(不能与其他 sanitizer 同时使用)
php bin/compiler.php app.php --sanitize=thread
```
### macOS (Clang)
与 Linux 类似,但需要注意:
- MemorySanitizer 在 macOS 上可能不可用
- 建议使用 Homebrew 安装最新版本的 Clang
```bash
brew install llvm
export CC=/usr/local/opt/llvm/bin/clang
export CXX=/usr/local/opt/llvm/bin/clang++
php bin/compiler.php app.php --sanitize=address
```
---
## 🐛 故障排除
### Q: 编译时提示不支持 sanitizer?
A: 检查编译器版本:
```powershell
# Windows
cl
# Linux
gcc --version
clang --version
```
确保使用支持 sanitizer 的版本。
### Q: 运行时出现 "Sanitizer CHECK failed"?
A: 这可能是由于:
1. 库冲突 - 确保所有库都用相同的 sanitizer 编译
2. 不兼容的选项 - 尝试移除其他优化选项
3. 系统限制 - 检查是否有足够的内存
### Q: AddressSanitizer 报告误报?
A: 可能的原因:
1. 第三方库的问题 - 考虑抑制特定模块的检查
2. 已知的无害问题 - 使用 `__attribute__((no_sanitize))` 禁用特定函数
```cpp
// 在 C++ 代码中
__attribute__((no_sanitize("address")))
void safe_function() {
// 这个函数不会被 ASan 检查
}
```
### Q: 性能太慢怎么办?
A:
1. 只在调试时使用 sanitizer
2. 发布版本禁用 sanitizer
3. 使用 `-O1` 而不是 `-O0`(仍保持较好的检测能力)
---
## 📊 性能影响
| 配置 | 速度 | 内存 | 适用场景 |
|------|------|------|----------|
| 无 sanitizer, -O2 | 100% | 100% | 生产环境 |
| -fsanitize=address, -O0 | ~50% | ~200% | 开发调试 |
| -fsanitize=address, -O1 | ~60% | ~180% | 测试环境 |
| -fsanitize=undefined, -O0 | ~80% | ~120% | 轻量级检查 |
---
## 🎯 最佳实践
1. **持续集成中启用**
```yaml
# .github/workflows/test.yml
- name: Compile with ASan
run: php bin/compiler.php tests/*.php --sanitize=address
- name: Run tests
run: ./run-tests.sh
```
2. **定期扫描**
- 每周运行一次带 sanitizer 的完整测试套件
- 修复所有报告的问题
3. **结合其他工具**
- Valgrind(Linux)
- Dr. Memory(Windows)
- Static analyzers(静态分析器)
4. **文档化已知问题**
- 记录无法立即修复的 sanitizer 警告
- 说明为什么这些警告可以忽略
---
## 📚 相关资源
- [AddressSanitizer 官方文档](https://github.com/google/sanitizers/wiki/AddressSanitizer)
- [MSVC AddressSanitizer](https://docs.microsoft.com/cpp/sanitizers/asan)
- [GCC Sanitizers](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
- [Clang Sanitizers](https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation)
---
## 🔗 编译器命令参考
```powershell
# Windows - 基础用法
php bin/compiler.php app.php --sanitize=address
# Windows - 结合其他选项
php bin/compiler.php app.php --sanitize=address --debug-info --no-console
# Linux/macOS - 单个 sanitizer
php bin/compiler.php app.php --sanitize=address
# Linux/macOS - 多个 sanitizer
php bin/compiler.php app.php --sanitize=address,undefined
# Linux/macOS - 内存泄漏检测
php bin/compiler.php app.php --sanitize=leak
```
希望这个指南能帮助您有效使用 AddressSanitizer 来检测和修复内存错误!

@ -0,0 +1,528 @@
# YAML 配置命名规范指南
## 📋 概述
PHPX 编译器的 YAML 配置文件统一使用**中横线(kebab-case)**命名约定,这符合现代配置文件的行业标准。
---
## 🎯 命名约定
### ✅ 推荐:中横线(kebab-case)
```yaml
# 所有配置项使用中横线
name: my-app
build-mode: bin
cxx-std: c++17
cxx-flags:
- -Wall
ld-flags:
- -lm
debug-info: true
no-console: true
```
**优势:**
- ✅ 符合 YAML/JSON 生态标准
- ✅ 与命令行参数一致(`--cxx-std`)
- ✅ Docker、Kubernetes、npm 等主流工具都使用
- ✅ URL 友好,可直接用于 API
---
### ⚠ 兼容:下划线(snake_case)
为了向后兼容,解析器也支持下划线命名:
```yaml
# 这些也会被正确解析(但不推荐)
type: bin # 等同于 build-mode
cxx_std: c++17 # 等同于 cxx-std
cxxflags: # 等同于 cxx-flags
- -Wall
```
**注意:** 新项目应该使用中横线,下划线仅用于兼容旧配置。
---
## 📊 配置项对照表
| 配置项(推荐) | 兼容写法(别名) | 说明 | 类型 |
|--------------|----------------|------|------|
| `name` | - | 项目名称 | string |
| `build-mode` | `type` | 构建模式(bin/ext) | string |
| `version` | - | 版本号 | string |
| `cxx-std` | `cxx_std` | C++ 标准版本 | string |
| `cxx-flags` | `cxxflags` | C++ 编译选项 | array/string |
| `ld-flags` | `ldflags` | 链接器选项 | array/string |
| `sources` | - | 源文件列表 | array |
| `ignore` | - | 忽略的文件/目录 | array |
| `debug-info` | - | 启用调试信息 | boolean |
| `no-console` | - | 隐藏控制台窗口 | boolean |
**注意:**
- ✅ **推荐使用中横线格式**(如 `build-mode`, `cxx-flags`
- ⚠ **别名仅用于向后兼容**(如 `type`, `cxxflags`
- 📖 **使用手册中的示例可能使用别名**,两者都有效
---
## 💡 完整示例
### 示例 1:基本项目配置
```yaml
# project.yml - 推荐使用中横线
name: hello-world
build-mode: bin
version: 1.0.0
cxx-std: c++17
cxx-flags:
- -Wall
- -Wextra
- -O2
sources:
- src/main.php
- src/utils.php
```
---
### 示例 2:扩展模块配置
```yaml
# project.yml
name: my-extension
build-mode: ext
version: 0.1.0
cxx-std: c++17
cxx-flags:
- -Wall
- -fPIC
ld-flags:
- -shared
sources:
- src/*.php
```
---
### 示例 3:复杂项目配置
```yaml
# project.yml
name: my-app
build-mode: bin
version: 2.0.0
# C++ 标准
cxx-std: c++20
# 编译选项
cxx-flags:
- -Wall
- -Wextra
- -Wpedantic
- -O3
# 链接选项
ld-flags:
- -lm
- -lpthread
- -lssl
# 源文件
sources:
- src/main.php
- src/controllers
- src/models
- src/views
# 忽略的文件
ignore:
- tests/
- docs/
- "*.md"
- ext-json # 忽略内置扩展
```
---
## 🔧 优先级规则
配置值的读取遵循以下优先级(从高到低):
1. **命令行参数**(最高优先级)
```bash
php bin/compiler.php project.yml --cxx-std=c++20
```
2. **YAML 配置文件**
```yaml
cxx-std: c++17
```
3. **平台默认值**
- Windows: `c++17`
- Unix: `c++14`
---
## 🔄 兼容性说明
### 自动转换逻辑
解析器会自动处理以下情况:
```php
// Translator.php 中的逻辑
// build-mode / type 别名支持
$buildMode = $cfg['build-mode'] ?? $cfg['type'] ?? null;
if (!empty($buildMode)) {
$this->setBuildMode($buildMode);
}
// cxx-flags / cxxflags 别名支持
$cxxflags = $cfg['cxx-flags'] ?? $cfg['cxxflags'] ?? null;
if (!empty($cxxflags)) {
// 处理 cxxflags
}
```
**支持的写法:**
```yaml
# ✅ 推荐(中横线)
build-mode: bin
cxx-flags:
- -Wall
# ⚠ 兼容(别名,用于向后兼容使用手册)
type: bin
cxxflags:
- -Wall
# 两者效果完全相同
```
**优先级:**
1. 如果同时指定了 `build-mode``type`,优先使用 `build-mode`
2. 如果同时指定了 `cxx-flags``cxxflags`,优先使用 `cxx-flags`
3. 建议只使用其中一种,避免混淆
---
### 废弃的写法
以下写法仍然有效,但**不推荐**:
```yaml
# ❌ 不推荐:在 cxx-flags 中包含 -std=
cxx-flags:
- -std=c++17 # 应该使用 cxx-std
- -Wall
# ✅ 推荐:分开配置
cxx-std: c++17
cxx-flags:
- -Wall
```
---
## 📝 迁移指南
### 从旧配置迁移到新配置
#### ⚠ 旧配置(使用手册中的示例,仍然有效)
```yaml
name: my-app
type: bin
version: 1.0.0
cxx_std: c++14
cxxflags: |
-std=c++14
-Wall
-O2
ldflags: -lm -lpthread
```
**说明:**
- ✅ `type``build-mode` 的别名(向后兼容)
- ✅ `cxxflags``cxx-flags` 的别名(向后兼容)
- ✅ 这些配置**仍然完全有效**
- 📖 使用手册中的示例继续使用这些别名
---
#### ✅ 新配置(推荐)
```yaml
name: my-app
build-mode: bin
version: 1.0.0
cxx-std: c++14
cxx-flags:
- -Wall
- -O2
ld-flags:
- -lm
- -lpthread
```
**改进点:**
1. ✅ `type``build-mode`(更清晰的中横线命名)
2. ✅ `cxx_std``cxx-std`(中横线)
3. ✅ `cxxflags``cxx-flags`(中横线)
4. ✅ 移除 `-std=c++14`(使用独立的 `cxx-std`
5. ✅ 数组格式更清晰
---
### 重要提示
**不需要立即迁移!**
- ✅ 旧配置(使用 `type`, `cxxflags`)**完全有效**
- ✅ 新配置(使用 `build-mode`, `cxx-flags`)**推荐使用**
- ✅ 两者可以混合使用(但不建议)
- 📖 使用手册中的示例保持不变
**建议:**
- 新项目 → 使用中横线格式
- 现有项目 → 可以继续使用别名,无需修改
---
## 🎨 最佳实践
### 1. 始终使用中横线
```yaml
# ✅ 好
cxx-std: c++17
build-mode: bin
debug-info: true
# ❌ 避免
cxx_std: c++17
build_mode: bin
debug_info: true
```
---
### 2. 使用数组而非多行字符串
```yaml
# ✅ 推荐:数组格式
cxx-flags:
- -Wall
- -Wextra
- -O2
# ⚠ 可用但不推荐:多行字符串
cxx-flags: |
-Wall
-Wextra
-O2
```
---
### 3. 分离 C++ 标准和编译选项
```yaml
# ✅ 推荐
cxx-std: c++17
cxx-flags:
- -Wall
- -O2
# ❌ 避免
cxx-flags:
- -std=c++17 # 不要在这里指定标准
- -Wall
```
---
### 4. 添加注释说明
```yaml
name: my-app
build-mode: bin
# 使用 C++17 以获得更好的性能
cxx-std: c++17
# 启用所有警告
cxx-flags:
- -Wall
- -Wextra
- -Wpedantic
```
---
## 🐛 常见问题
### Q1: 我可以使用 `type``cxxflags` 吗?
A: **可以!** 这些是官方支持的别名,用于向后兼容使用手册。
```yaml
# ✅ 完全有效(使用手册中的示例)
type: bin
cxxflags:
- -Wall
# ✅ 同样有效(推荐的新格式)
build-mode: bin
cxx-flags:
- -Wall
```
**建议:**
- 新项目 → 使用中横线格式
- 现有项目 → 可以继续使用别名
---
### Q2: `type``build-mode` 有什么区别?
A: **没有区别**,`type` 是 `build-mode` 的别名。推荐使用 `build-mode`
```yaml
# 这两个是等价的
build-mode: bin # ✅ 推荐
type: bin # ⚠ 别名(使用手册中的示例)
```
---
### Q3: 可以在 cxx-flags 中使用 `-std=` 吗?
A: 技术上可以,但**不推荐**。应该使用独立的 `cxx-std` 配置项。
```yaml
# ❌ 不推荐
cxx-flags:
- -std=c++17
- -Wall
# ✅ 推荐
cxx-std: c++17
cxx-flags:
- -Wall
```
---
### Q4: 如何覆盖配置文件中的设置?
A: 使用命令行参数:
```bash
# 覆盖 cxx-std
php bin/compiler.php project.yml --cxx-std=c++20
# 覆盖 build-mode
php bin/compiler.php project.yml --mode=ext
# 启用调试信息
php bin/compiler.php project.yml --debug-info
```
---
## 📚 相关资源
- [YAML 官方规范](https://yaml.org/spec/)
- [Kubernetes 命名约定](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/)
- [Docker Compose 文件参考](https://docs.docker.com/compose/compose-file/)
- [npm package.json 规范](https://docs.npmjs.com/cli/v9/configuring-npm/package-json)
---
## 🎉 总结
### 核心原则
1. ✅ **统一使用中横线**(kebab-case)
2. ✅ **分离关注点**(`cxx-std` vs `cxx-flags`
3. ✅ **使用数组格式**(更易读)
4. ✅ **添加注释**(提高可维护性)
5. ✅ **保持向后兼容**(支持 `type`, `cxxflags` 别名)
### 别名说明
**为了兼容使用手册,以下别名仍然有效:**
| 推荐写法 | 别名(使用手册) | 状态 |
|---------|----------------|------|
| `build-mode` | `type` | ✅ 完全支持 |
| `cxx-flags` | `cxxflags` | ✅ 完全支持 |
**建议:**
- 📖 使用手册中的示例继续使用别名
- ✨ 新项目推荐使用中横线格式
- 🔄 现有项目无需修改,别名完全有效
### 快速参考
```yaml
# 标准模板(推荐)
name: my-project
build-mode: bin
version: 1.0.0
cxx-std: c++17
cxx-flags:
- -Wall
- -Wextra
ld-flags:
- -lm
sources:
- src/*.php
```
```yaml
# 使用手册中的示例(仍然有效)
name: my-project
type: bin
version: 1.0.0
cxx_std: c++17
cxxflags:
- -Wall
- -Wextra
ldflags:
- -lm
sources:
- src/*.php
```
遵循这些规范,您的配置文件将更加清晰、易读和易于维护!

@ -1,9 +1,10 @@
name: swoole-compiler
type: bin
build-mode: bin
version: 0.1.0
cxxflags: |
-std=c++14
-Wall
cxx-std: c++14
cxx-flags:
- -Wall
sources:
- ./src/Php
- ./src/Core

@ -2448,9 +2448,9 @@ class CompilerBase extends \PhpAot\Core\Translator
// 启用 C++ 异常处理(消除 C4530 警告)
$cmd .= ' /EHsc';
// C++ 标准
// C++ 标准(从 cxxStd 属性读取,如果 cxxflags 中没有指定)
if (!str_contains($this->cxxflags, '/std:')) {
$cmd .= ' /std:c++17';
$cmd .= ' /std:' . $this->cxxStd;
}
// 编译时的额外选项
@ -2572,6 +2572,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->cxxflags) {
$cmd .= ' ' . $this->cxxflags;
}
// C++ 标准(从 cxxStd 属性读取,如果 cxxflags 中没有指定)
if (!str_contains($this->cxxflags, ' -std=')) {
$cmd .= ' -std=' . $this->cxxStd;
}

@ -98,7 +98,7 @@ class Constants
'required' => false,
'noValue' => true,
],
'noLiteralStrings' => [
'no-literal-strings' => [
'longPrefix' => 'no-literal-strings',
'description' => 'Disable literal strings optimization',
'required' => false,
@ -149,6 +149,12 @@ class Constants
'required' => false,
'defaultValue' => '',
],
'cxx-std' => [
'longPrefix' => 'cxx-std',
'description' => 'C++ standard version (c++14, c++17, c++20, etc.)',
'required' => false,
'defaultValue' => 'c++17',
],
];
/**
@ -171,5 +177,6 @@ class Constants
'5219' => '隐式转换警告',
'5220' => 'volatile 成员警告',
'4100' => '未使用的参数',
'5039' => '使用未定义的函数',
];
}

@ -55,15 +55,8 @@ class Translator extends Preprocessor
$this->preprocessArgvAdvanced();
$this->climate->arguments->parse();
$this->optimizeLevel = $this->climate->arguments->get('optimize');
$this->buildMode = $this->climate->arguments->get('mode');
$this->debugLine = intval($this->climate->arguments->get('debug-line'));
$this->maxJob = intval($this->climate->arguments->get('job'));
$this->debugInfo = $this->climate->arguments->defined('debug-info');
$this->noLiteralStrings = $this->climate->arguments->get('noLiteralStrings');
$this->enableProfiler = $this->climate->arguments->defined('profile');
$this->noConsole = $this->climate->arguments->defined('no-console');
$this->sanitize = $this->climate->arguments->get('sanitize');
// 只读取命令行参数,不立即应用(等待 YAML 解析后再应用)
// 这样可以确保优先级:命令行 > YAML > 默认值
$this->internalFunctions = array_flip(get_defined_functions()['internal']);
unset($this->internalFunctions['main']);
$this->internalConstants = get_defined_constants();
@ -98,6 +91,7 @@ class Translator extends Preprocessor
$climate->tab()->out('-O <level> Optimization level (0-3, default: 0)');
$climate->tab()->out('-p, --profile Enable performance profiling');
$climate->tab()->out('-d, --debug-info Enable debug info (auto-disable optimizations, add -g/-Zi)');
$climate->tab()->out('--cxx-std <version> C++ standard version (c++14, c++17, c++20, etc.)');
$climate->tab()->out('-o, --output <file> Output binary name (default: input basename)');
$climate->tab()->out('-v, --version Show version');
$climate->tab()->out('-h, --help Show this help message');
@ -117,9 +111,67 @@ class Translator extends Preprocessor
$climate->tab()->out($cmd . ' app.php -O3 -o myapp -v');
$climate->tab()->out($cmd . ' gui-app.php --no-console (Windows GUI app, no console)');
$climate->tab()->out($cmd . ' app.php --sanitize=address (Enable AddressSanitizer)');
$climate->tab()->out($cmd . ' app.php --cxx-std=c++17 (Use C++17 standard)');
$climate->tab()->out($cmd . ' app.php --no-literal-strings (Disable string optimization)');
$climate->br();
}
/**
* 应用命令行参数(在 YAML 解析后调用,确保命令行参数优先级最高)
*/
protected function applyCommandLineArguments(): void
{
// 优化级别
if ($this->climate->arguments->defined('optimize')) {
$this->optimizeLevel = $this->climate->arguments->get('optimize');
}
// 构建模式
if ($this->climate->arguments->defined('mode')) {
$this->buildMode = $this->climate->arguments->get('mode');
}
// 调试行号
if ($this->climate->arguments->defined('debug-line')) {
$this->debugLine = intval($this->climate->arguments->get('debug-line'));
}
// 最大并行任务数
if ($this->climate->arguments->defined('job')) {
$this->maxJob = intval($this->climate->arguments->get('job'));
}
// 调试信息
if ($this->climate->arguments->defined('debug-info')) {
$this->debugInfo = true;
}
// 禁用字面量字符串优化
if ($this->climate->arguments->defined('no-literal-strings')) {
$this->noLiteralStrings = true;
}
// 启用性能分析
if ($this->climate->arguments->defined('profile')) {
$this->enableProfiler = true;
}
// 隐藏控制台窗口
if ($this->climate->arguments->defined('no-console')) {
$this->noConsole = true;
}
// Sanitizer
if ($this->climate->arguments->defined('sanitize')) {
$this->sanitize = $this->climate->arguments->get('sanitize');
}
// C++ 标准版本
if ($this->climate->arguments->defined('cxx-std')) {
$this->cxxStd = $this->climate->arguments->get('cxx-std');
}
}
private function showVersion(): void
{
$this->climate->bold()->out(self::APP_NAME . ' v' . self::VERSION);
@ -196,6 +248,7 @@ class Translator extends Preprocessor
$path = $realpath;
if (is_dir($path)) {
// 目录模式:不解析 YAML
$list = $this->getFilesFromDir($path);
$targetName = basename($path);
$this->setTargetName($targetName);
@ -203,8 +256,10 @@ class Translator extends Preprocessor
} else {
$ext = pathinfo($path, PATHINFO_EXTENSION);
if ($ext === 'yml') {
// YAML 配置模式:先解析 YAML
$list = $this->parseProjectYaml($path);
} elseif ($ext === 'php') {
// 单文件模式:不解析 YAML
$list = [$path];
$targetName = FileScanner::getFileName($path);
$this->setTargetName($targetName);
@ -214,6 +269,9 @@ class Translator extends Preprocessor
}
}
// 在所有配置加载完成后,应用命令行参数(确保优先级最高)
$this->applyCommandLineArguments();
return $list;
}
@ -1181,32 +1239,51 @@ CODE;
} else {
$list = $this->getFilesFromDir($projectDir);
}
if (!empty($cfg['cxxflags'])) {
if (is_array($cfg['cxxflags'])) {
$this->cxxflags = implode(' ', $cfg['cxxflags']);
// 读取 cxxflags(支持中横线和下划线)
$cxxflags = $cfg['cxx-flags'] ?? $cfg['cxxflags'] ?? null;
if (!empty($cxxflags)) {
if (is_array($cxxflags)) {
$this->cxxflags = implode(' ', $cxxflags);
} else {
$this->cxxflags = str_replace("\n", ' ', $cfg['cxxflags']);
$this->cxxflags = str_replace("\n", ' ', $cxxflags);
}
}
if (!empty($cfg['ldflags'])) {
if (is_array($cfg['ldflags'])) {
$this->ldflags = implode(' ', $cfg['ldflags']);
// 读取 C++ 标准版本(支持中横线和下划线)
$cxxStd = $cfg['cxx-std'] ?? $cfg['cxx_std'] ?? null;
if (!empty($cxxStd)) {
$this->cxxStd = $cxxStd;
}
// 读取 ldflags(支持中横线和下划线)
$ldflags = $cfg['ld-flags'] ?? $cfg['ldflags'] ?? null;
if (!empty($ldflags)) {
if (is_array($ldflags)) {
$this->ldflags = implode(' ', $ldflags);
} else {
$this->ldflags = str_replace("\n", ' ', $cfg['ldflags']);
$this->ldflags = str_replace("\n", ' ', $ldflags);
}
}
// 读取 name
if (!empty($cfg['name'])) {
$this->setTargetName($cfg['name']);
}
if (!empty($cfg['type'])) {
$this->setBuildMode($cfg['type']);
// 读取 type/build-mode(支持中横线和下划线)
$buildMode = $cfg['build-mode'] ?? $cfg['type'] ?? null;
if (!empty($buildMode)) {
$this->setBuildMode($buildMode);
}
if (!empty($cfg['ignore'])) {
if (!is_array($cfg['ignore'])) {
// 读取 ignore(支持中横线和下划线)
$ignore = $cfg['ignore'] ?? null;
if (!empty($ignore)) {
if (!is_array($ignore)) {
$this->error('`ignore` must be array');
}
foreach ($cfg['ignore'] as $src) {
foreach ($ignore as $src) {
if (preg_match('/ext-([a-z0-9_]+)/i', $src, $matches)) {
$this->ignoreExtensions[] = $matches[1];
continue;

Loading…
Cancel
Save