feat(generator): 添加 Windows 资源文件生成器

- 实现 ResourceFileGenerator 类用于生成 .rc 资源文件
- 支持图标文件嵌入功能,可指定 icon 配置项
- 实现版本信息生成功能,支持文件版本、产品版本等属性
- 提供配置示例文档,包括公司名称、版权等元数据
- 实现版本号格式化功能,支持多种输入格式转换
- 添加 UTF-8 编码支持,解决中文乱码问题
- 实现资源头文件生成,便于 C++ 代码引用资源 ID
pull/1/head
韩天峰 3 months ago
parent 826e62e7b4
commit 8c8daa47e5
  1. 233
      src/Php/Generator/ResourceFileGenerator.php

@ -0,0 +1,233 @@
<?php
namespace PhpAot\Php\Generator;
/**
* Windows 资源文件 (.rc) 生成器
*
* 用于生成 Windows PE 资源文件,可将图标、版本信息等嵌入到 exe 中
*
* 配置示例(在 project.yml 中):
*
* resource:
* icon: path/to/icon.ico
* version-info:
* file-version: 1.0.0.0
* product-version: 1.0.0.0
* file-flags-mask: 3f
* file-flags: 00
* file-os: 040004
* file-type: 01
* file-subtype: 00
* company-name: "My Company"
* file-description: "My Application"
* internal-name: "myapp"
* legal-copyright: "Copyright (C) 2026 My Company"
* legal-trademarks: "MyApp is a trademark of My Company"
* original-filename: "myapp.exe"
* product-name: "My Product"
* comments: "Built with Swoole Compiler"
*/
class ResourceFileGenerator
{
/**
* 资源配置
*/
private array $config;
/**
* 项目目录(用于解析相对路径)
*/
private string $projectDir;
public function __construct(array $config, string $projectDir)
{
$this->config = $config;
$this->projectDir = $projectDir;
}
/**
* 检查是否有任何资源配置
*/
public function hasResource(): bool
{
return !empty($this->config['icon']) || !empty($this->config['version-info']);
}
/**
* 获取图标文件的绝对路径
*/
public function getIconPath(): ?string
{
$icon = $this->config['icon'] ?? null;
if (empty($icon)) {
return null;
}
// 如果是绝对路径,直接使用
if (preg_match('/^[A-Za-z]:\\\\|^\//', $icon)) {
return $icon;
}
// 相对路径,基于项目目录解析
return $this->projectDir . DIRECTORY_SEPARATOR . $icon;
}
/**
* 生成 .rc 资源文件内容
*/
public function generate(): string
{
$content = '';
$content .= '// Generated by Swoole Compiler - Windows Resource File' . PHP_EOL;
$content .= '// DO NOT EDIT - This file is auto-generated' . PHP_EOL;
$content .= PHP_EOL;
// 告诉 rc.exe 此文件使用 UTF-8 编码,避免中文乱码
$content .= '#pragma code_page(65001)' . PHP_EOL;
$content .= PHP_EOL;
// 包含 Windows 版本信息头文件
$content .= '#include <windows.h>' . PHP_EOL;
$content .= PHP_EOL;
// 图标资源
$iconPath = $this->getIconPath();
if ($iconPath) {
// 使用正斜杠,Windows RC 编译器更兼容
$iconPathRc = str_replace('\\', '/', $iconPath);
$content .= '// Icon Resource' . PHP_EOL;
$content .= 'MAINICON ICON "' . addslashes($iconPathRc) . '"' . PHP_EOL;
$content .= PHP_EOL;
}
// 版本信息
$versionInfo = $this->config['version-info'] ?? [];
if (!empty($versionInfo)) {
$content .= $this->generateVersionInfo($versionInfo);
}
return $content;
}
/**
* 生成版本信息块
*/
private function generateVersionInfo(array $info): string
{
$fileVersion = $info['file-version'] ?? '0.0.0.0';
$productVersion = $info['product-version'] ?? $fileVersion;
$content = '';
$content .= '// Version Information' . PHP_EOL;
$content .= '1 VERSIONINFO' . PHP_EOL;
$content .= 'FILEVERSION ' . $this->formatVersionDots($fileVersion) . PHP_EOL;
$content .= 'PRODUCTVERSION ' . $this->formatVersionDots($productVersion) . PHP_EOL;
$content .= 'FILEFLAGSMASK ' . ($info['file-flags-mask'] ?? '0x3fL') . PHP_EOL;
$content .= 'FILEFLAGS ' . ($info['file-flags'] ?? '0x0L') . PHP_EOL;
$content .= 'FILEOS ' . ($info['file-os'] ?? 'VOS_NT_WINDOWS32') . PHP_EOL;
$content .= 'FILETYPE ' . ($info['file-type'] ?? 'VFT_APP') . PHP_EOL;
$content .= 'FILESUBTYPE ' . ($info['file-subtype'] ?? 'VFT2_UNKNOWN') . PHP_EOL;
$content .= 'BEGIN' . PHP_EOL;
// StringFileInfo 块
$content .= ' BLOCK "StringFileInfo"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL;
// 语言代码页(040904b0 = 英文/UTF-8,配合 #pragma code_page(65001) 正确显示中文)
$langCodepage = $info['lang-codepage'] ?? '040904b0';
$content .= ' BLOCK "' . $langCodepage . '"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL;
// 字符串值
$stringFields = [
'company-name' => 'CompanyName',
'file-description' => 'FileDescription',
'file-version' => 'FileVersion',
'internal-name' => 'InternalName',
'legal-copyright' => 'LegalCopyright',
'legal-trademarks' => 'LegalTrademarks',
'original-filename' => 'OriginalFilename',
'product-name' => 'ProductName',
'product-version' => 'ProductVersion',
'comments' => 'Comments',
];
foreach ($stringFields as $yamlKey => $rcKey) {
if (isset($info[$yamlKey])) {
$value = $info[$yamlKey];
$content .= ' VALUE "' . $rcKey . '", "' . addslashes($value) . '\\0"' . PHP_EOL;
}
}
// 如果没有设置 FileVersion,从 file-version 字段自动填入
if (!isset($info['file-version-str']) && $fileVersion) {
// 已在上面通过 file-version 键处理
}
$content .= ' END' . PHP_EOL;
$content .= ' END' . PHP_EOL;
// VarFileInfo 块
$content .= ' BLOCK "VarFileInfo"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL;
// 0x0409 = English(US),1200 = Unicode(UTF-16)
// 配合 StringFileInfo 中的 040904b0 代码页,确保中文在 UTF-8 源文件中正确编码
$content .= ' VALUE "Translation", 0x0409, 1200' . PHP_EOL;
$content .= ' END' . PHP_EOL;
$content .= 'END' . PHP_EOL;
return $content;
}
/**
* 将版本号格式化为逗号分隔的格式(1,0,0,0)
* 支持以下输入格式:
* - "1.0.0.0" → "1,0,0,0"
* - "1,0,0,0" → "1,0,0,0"
* - "v1052" → "1052,0,0,0" (去掉 v 前缀)
* - "1.0" → "1,0,0,0"
*/
private function formatVersionDots(string $version): string
{
// 去掉 v/V 前缀(如 v1052 → 1052)
$version = ltrim($version, 'vV');
// 如果已经是逗号分隔格式,直接返回
if (str_contains($version, ',')) {
return $version;
}
// 用点号分隔
$parts = explode('.', $version);
// 确保每个部分都是数字(过滤掉非数字字符)
$parts = array_map(function ($p) {
return preg_replace('/[^0-9]/', '', $p) ?: '0';
}, $parts);
// 确保恰好有4个部分
while (count($parts) < 4) {
$parts[] = '0';
}
return implode(',', array_slice($parts, 0, 4));
}
/**
* 生成 resource.h 头文件内容(可选,供 C++ 代码引用资源 ID)
*/
public function generateHeader(): string
{
$content = '';
$content .= '// Generated by Swoole Compiler - Resource Header' . PHP_EOL;
$content .= '// DO NOT EDIT - This file is auto-generated' . PHP_EOL;
$content .= PHP_EOL;
if ($this->getIconPath()) {
$content .= '#define MAINICON 101' . PHP_EOL;
}
return $content;
}
}
Loading…
Cancel
Save