From 334594c65900a3ed209d4617502d4d132cdee00e Mon Sep 17 00:00:00 2001 From: rango Date: Thu, 30 Apr 2026 18:13:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E5=AE=9E=E7=8E=B0=20C++=20?= =?UTF-8?q?=E6=A0=87=E5=87=86=E7=8B=AC=E7=AB=8B=E9=85=8D=E7=BD=AE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 C++ 标准从 cxxflags 中分离,支持 --cxx-std 参数配置 - 添加 project.yml 中的 cxx_std 配置项,支持独立设置标准版本 - 实现 Windows 和 Unix 平台的差异化默认标准(Windows 使用 c++17,Unix 使用 c++14) - 添加 /NODEFAULTLIB 选项解决 Windows CRT 库冲突问题 - 增加 --debug-info 参数支持,自动禁用优化并生成调试信息 - 更新配置优先级规则文档,明确命令行参数、YAML 配置和默认值的覆盖关系 - 添加 C++ 标准配置指南和调试模式使用说明 - 修复 cxxflags 中标准标志被忽略的问题 --- examples/win32-hello/CONFIG_PRIORITY_RULES.md | 443 +++++++++++++++ examples/win32-hello/CXX_STD_CONFIG_GUIDE.md | 360 ++++++++++++ examples/win32-hello/DEBUG_GUIDE.md | 356 ++++++++++++ examples/win32-hello/DEBUG_MODE_GUIDE.md | 455 +++++++++++++++ .../win32-hello/MSVC_WARNINGS_SUPPRESSION.md | 370 ++++++++++++ examples/win32-hello/SANITIZER_GUIDE.md | 385 +++++++++++++ .../YAML_CONFIG_NAMING_CONVENTION.md | 528 ++++++++++++++++++ project.yml | 9 +- src/Php/CompilerBase.php | 5 +- src/Php/Constants.php | 9 +- src/Php/Translator.php | 121 +++- 11 files changed, 3012 insertions(+), 29 deletions(-) create mode 100644 examples/win32-hello/CONFIG_PRIORITY_RULES.md create mode 100644 examples/win32-hello/CXX_STD_CONFIG_GUIDE.md create mode 100644 examples/win32-hello/DEBUG_GUIDE.md create mode 100644 examples/win32-hello/DEBUG_MODE_GUIDE.md create mode 100644 examples/win32-hello/MSVC_WARNINGS_SUPPRESSION.md create mode 100644 examples/win32-hello/SANITIZER_GUIDE.md create mode 100644 examples/win32-hello/YAML_CONFIG_NAMING_CONVENTION.md diff --git a/examples/win32-hello/CONFIG_PRIORITY_RULES.md b/examples/win32-hello/CONFIG_PRIORITY_RULES.md new file mode 100644 index 00000000..4eb02413 --- /dev/null +++ b/examples/win32-hello/CONFIG_PRIORITY_RULES.md @@ -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 ` | 0-3 | +| 调试信息 | `--debug-info` | 启用调试 | +| 性能分析 | `--profile` | 启用 profiling | +| Sanitizer | `--sanitize` | 内存检测 | +| 并行任务 | `-j ` | 并行编译数 | +| 隐藏控制台 | `--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 配置 ─────────→ 中等优先级,提供默认值 + ↓ +平台默认值 ────────→ 最低优先级,保证可用性 +``` + +遵循这些规则,您可以灵活地控制编译行为! diff --git a/examples/win32-hello/CXX_STD_CONFIG_GUIDE.md b/examples/win32-hello/CXX_STD_CONFIG_GUIDE.md new file mode 100644 index 00000000..106818e2 --- /dev/null +++ b/examples/win32-hello/CXX_STD_CONFIG_GUIDE.md @@ -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++ 标准配置! diff --git a/examples/win32-hello/DEBUG_GUIDE.md b/examples/win32-hello/DEBUG_GUIDE.md new file mode 100644 index 00000000..a2d94c38 --- /dev/null +++ b/examples/win32-hello/DEBUG_GUIDE.md @@ -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 +``` + +#### 常用命令 + +``` +g # 继续执行 (Go) +k # 显示调用堆栈 (Stack trace) +dv # 显示局部变量 +!analyze -v # 详细分析崩溃原因 +bp <地址> # 设置断点 +``` + +--- + +### 方案 4:添加日志输出 + +由于 GUI 程序没有控制台,可以将调试信息写入日志文件: + +```php +getMessage()); + debug_log("堆栈跟踪: " . $e->getTraceAsString()); + } +} +``` + +**查看日志:** +```powershell +Get-Content .\debug.log -Wait +``` + +--- + +### 方案 5:使用消息框调试 + +对于简单的调试,可以使用消息框显示变量值: + +```php + 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()` 调用 + - 查看日志文件定位问题 + +希望这些方法能帮助您成功调试程序! diff --git a/examples/win32-hello/DEBUG_MODE_GUIDE.md b/examples/win32-hello/DEBUG_MODE_GUIDE.md new file mode 100644 index 00000000..7ca1250d --- /dev/null +++ b/examples/win32-hello/DEBUG_MODE_GUIDE.md @@ -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 + +希望这个指南能帮助您有效使用调试模式! diff --git a/examples/win32-hello/MSVC_WARNINGS_SUPPRESSION.md b/examples/win32-hello/MSVC_WARNINGS_SUPPRESSION.md new file mode 100644 index 00000000..f0e16ccd --- /dev/null +++ b/examples/win32-hello/MSVC_WARNINGS_SUPPRESSION.md @@ -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 键为警告编号,值为说明 + */ +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 | 新标准警告 | 提示 | ❌ 否 | + +**所有这些警告都可以安全地忽略。** + +--- + +希望这个文档能帮助您理解为什么需要屏蔽这些警告,以及它们为什么是安全的! diff --git a/examples/win32-hello/SANITIZER_GUIDE.md b/examples/win32-hello/SANITIZER_GUIDE.md new file mode 100644 index 00000000..00562a8a --- /dev/null +++ b/examples/win32-hello/SANITIZER_GUIDE.md @@ -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 +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 +``` + +遵循这些规范,您的配置文件将更加清晰、易读和易于维护! diff --git a/project.yml b/project.yml index 59ebd5c0..aecb0dbd 100644 --- a/project.yml +++ b/project.yml @@ -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 diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 5836f987..7ac98584 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -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; } diff --git a/src/Php/Constants.php b/src/Php/Constants.php index f90f2f89..6837db0e 100644 --- a/src/Php/Constants.php +++ b/src/Php/Constants.php @@ -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' => '使用未定义的函数', ]; } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index e918d66e..0e34baec 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -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 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 C++ standard version (c++14, c++17, c++20, etc.)'); $climate->tab()->out('-o, --output 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;