diff --git a/app.manifest b/app.manifest
new file mode 100644
index 00000000..2c598dbb
--- /dev/null
+++ b/app.manifest
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ PerMonitorV2
+
+
+
+
diff --git a/project.yml b/project.yml
index 6f49c330..7c8d1a63 100644
--- a/project.yml
+++ b/project.yml
@@ -23,6 +23,12 @@ resource:
product-name: "Swoole Compiler"
comments: "PHP 原生编译器,可将 PHP 项目编译为 Windows/Linux/macOS 平台原生的可执行文件"
+# Windows 应用程序清单文件(缺省不携带 manifest)
+# 仅在 Windows 平台编译 bin 模式时生效
+# 清单文件为 XML 格式,可配置 UAC 权限级别、DPI 感知、运行时依赖等
+# 示例:如需启用,取消注释下一行并指定 manifest 文件路径
+# manifest: app.manifest
+
sources:
- ./src/Php
- ./src/Core
diff --git a/src/Php/Generator/ResourceFileGenerator.php b/src/Php/Generator/ResourceFileGenerator.php
index 9e44749b..f16e069c 100644
--- a/src/Php/Generator/ResourceFileGenerator.php
+++ b/src/Php/Generator/ResourceFileGenerator.php
@@ -27,6 +27,9 @@ namespace PhpAot\Php\Generator;
* original-filename: "myapp.exe"
* product-name: "My Product"
* comments: "Built with Swoole Compiler"
+ *
+ * # manifest 与 resource 同级(可选,Windows 平台缺省不携带)
+ * manifest: path/to/app.manifest
*/
class ResourceFileGenerator
{
@@ -51,7 +54,9 @@ class ResourceFileGenerator
*/
public function hasResource(): bool
{
- return !empty($this->config['icon']) || !empty($this->config['version-info']);
+ return !empty($this->config['icon'])
+ || !empty($this->config['version-info'])
+ || !empty($this->config['manifest']);
}
/**
@@ -64,13 +69,34 @@ class ResourceFileGenerator
return null;
}
+ return $this->resolvePath($icon);
+ }
+
+ /**
+ * 获取 manifest 文件的绝对路径
+ */
+ public function getManifestPath(): ?string
+ {
+ $manifest = $this->config['manifest'] ?? null;
+ if (empty($manifest)) {
+ return null;
+ }
+
+ return $this->resolvePath($manifest);
+ }
+
+ /**
+ * 解析相对/绝对路径为绝对路径
+ */
+ private function resolvePath(string $path): string
+ {
// 如果是绝对路径,直接使用
- if (preg_match('/^[A-Za-z]:\\\\|^\//', $icon)) {
- return $icon;
+ if (preg_match('/^[A-Za-z]:\\\\|^\//', $path)) {
+ return $path;
}
// 相对路径,基于项目目录解析
- return $this->projectDir . DIRECTORY_SEPARATOR . $icon;
+ return $this->projectDir . DIRECTORY_SEPARATOR . $path;
}
/**
@@ -91,6 +117,15 @@ class ResourceFileGenerator
$content .= '#include ' . PHP_EOL;
$content .= PHP_EOL;
+ // Manifest 资源(Windows 清单文件,如 UAC、DPI 感知等)
+ $manifestPath = $this->getManifestPath();
+ if ($manifestPath) {
+ $manifestPathRc = str_replace('\\', '/', $manifestPath);
+ $content .= '// Manifest Resource' . PHP_EOL;
+ $content .= 'CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "' . addslashes($manifestPathRc) . '"' . PHP_EOL;
+ $content .= PHP_EOL;
+ }
+
// 图标资源
$iconPath = $this->getIconPath();
if ($iconPath) {
diff --git a/src/Php/Translator.php b/src/Php/Translator.php
index 0e5bac32..63270a84 100644
--- a/src/Php/Translator.php
+++ b/src/Php/Translator.php
@@ -2223,7 +2223,7 @@ CODE;
}
}
- // 读取 resource(Windows 资源配置:图标、版本信息等)
+ // 读取 resource(Windows 资源配置:图标、版本信息)
$resource = $cfg['resource'] ?? null;
if (!empty($resource)) {
if (!is_array($resource)) {
@@ -2243,6 +2243,25 @@ CODE;
$this->resourceConfig['_projectDir'] = $projectDir;
}
+ // 读取 manifest(Windows 清单文件,与 resource 同级,缺省不携带)
+ $manifest = $cfg['manifest'] ?? null;
+ if (!empty($manifest)) {
+ if (!is_string($manifest)) {
+ $this->error('`manifest` must be a string (path to manifest file)');
+ }
+ $manifestPath = $manifest;
+ if (!preg_match('/^[A-Za-z]:\\|^\//', $manifestPath)) {
+ $manifestPath = $projectDir . DIRECTORY_SEPARATOR . $manifestPath;
+ }
+ if (!file_exists($manifestPath)) {
+ $this->error('Manifest file not exists: `' . $manifest . '`');
+ }
+ if (empty($this->resourceConfig)) {
+ $this->resourceConfig = ['_projectDir' => $projectDir];
+ }
+ $this->resourceConfig['manifest'] = $manifest;
+ }
+
return $this->filterIgnoredFiles($list);
}