mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-18 17:19:46 +01:00
Compare commits
3 Commits
async-cpp-
...
yuleng/aot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a2d8d7851 | ||
|
|
f882bc3370 | ||
|
|
d872e844e7 |
1
deps/cxxopts
vendored
Submodule
1
deps/cxxopts
vendored
Submodule
Submodule deps/cxxopts added at 12e496da3d
51
tools/README-CmdPal-AOT-Analysis.md
Normal file
51
tools/README-CmdPal-AOT-Analysis.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# PowerToys CmdPal AOT 分析工具
|
||||||
|
|
||||||
|
这个工具包用于分析 PowerToys CmdPal 在启用 AOT (Ahead-of-Time) 编译时被移除的类型。
|
||||||
|
|
||||||
|
## 核心文件
|
||||||
|
|
||||||
|
### 分析工具 (`tools/TrimmingAnalyzer/`)
|
||||||
|
- `TrimmingAnalyzer.csproj` - 项目文件
|
||||||
|
- `Program.cs` - 主程序入口
|
||||||
|
- `TypeAnalyzer.cs` - 程序集类型分析引擎
|
||||||
|
- `ReportGenerator.cs` - 报告生成器 (Markdown/XML/JSON)
|
||||||
|
|
||||||
|
### 脚本文件 (`tools/build/`)
|
||||||
|
- `Generate-CmdPalTrimmingReport.ps1` - 主要分析脚本(包含完整说明和自动化流程)
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 前提条件
|
||||||
|
- Visual Studio 2022 with C++ workload
|
||||||
|
- Windows SDK
|
||||||
|
- 使用 Developer Command Prompt for VS 2022
|
||||||
|
|
||||||
|
### 运行分析
|
||||||
|
```powershell
|
||||||
|
cd C:\Users\yuleng\PowerToys
|
||||||
|
.\tools\build\Generate-CmdPalTrimmingReport.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 输出报告
|
||||||
|
- `TrimmedTypes.md` - 人类可读的 Markdown 报告
|
||||||
|
- `TrimmedTypes.rd.xml` - 运行时指令以保留类型
|
||||||
|
- JSON 格式的分析数据
|
||||||
|
|
||||||
|
## 分析原理
|
||||||
|
|
||||||
|
1. **Debug 构建** - 不启用 AOT,保留所有类型
|
||||||
|
2. **Release 构建** - 启用 AOT,移除未使用的类型
|
||||||
|
3. **程序集比较** - 识别被 AOT 优化移除的类型
|
||||||
|
4. **报告生成** - 生成详细的优化效果报告
|
||||||
|
|
||||||
|
## 价值
|
||||||
|
|
||||||
|
- 显示 AOT 优化的有效性
|
||||||
|
- 识别被消除的未使用代码
|
||||||
|
- 帮助理解二进制大小减少
|
||||||
|
- 协助排查运行时反射问题
|
||||||
|
- 为性能优化决策提供数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*工具状态: 已完成,等待 Visual Studio C++ 构建环境配置后即可使用*
|
||||||
20
tools/TrimmingAnalyzer/Models/TypeInfo.cs
Normal file
20
tools/TrimmingAnalyzer/Models/TypeInfo.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TrimmingAnalyzer.Models
|
||||||
|
{
|
||||||
|
public class TypeInfo
|
||||||
|
{
|
||||||
|
public string FullName { get; set; } = string.Empty;
|
||||||
|
public string Namespace { get; set; } = string.Empty;
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public bool IsPublic { get; set; }
|
||||||
|
public bool IsSealed { get; set; }
|
||||||
|
public bool IsAbstract { get; set; }
|
||||||
|
public bool IsInterface { get; set; }
|
||||||
|
public bool IsEnum { get; set; }
|
||||||
|
public bool IsDelegate { get; set; }
|
||||||
|
public string? BaseType { get; set; }
|
||||||
|
public List<string> Interfaces { get; set; } = new();
|
||||||
|
public int MemberCount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
59
tools/TrimmingAnalyzer/Program.cs
Normal file
59
tools/TrimmingAnalyzer/Program.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace TrimmingAnalyzer
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 3)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Usage: TrimmingAnalyzer <untrimmed.dll> <trimmed.dll> <output-dir> [formats]");
|
||||||
|
Console.WriteLine("Formats: rdxml,markdown (default: rdxml,markdown)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var untrimmedPath = Path.GetFullPath(args[0]);
|
||||||
|
var trimmedPath = Path.GetFullPath(args[1]);
|
||||||
|
var outputDir = Path.GetFullPath(args[2]);
|
||||||
|
var formats = args.Length > 3 ? args[3].Split(',') : new[] { "rdxml", "markdown" };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine("Analyzing assemblies...");
|
||||||
|
var analyzer = new TypeAnalyzer();
|
||||||
|
var removedTypes = analyzer.GetRemovedTypes(untrimmedPath, trimmedPath);
|
||||||
|
|
||||||
|
Console.WriteLine($"Found {removedTypes.Count} trimmed types");
|
||||||
|
|
||||||
|
var generator = new ReportGenerator();
|
||||||
|
|
||||||
|
foreach (var format in formats)
|
||||||
|
{
|
||||||
|
switch (format.Trim().ToLower())
|
||||||
|
{
|
||||||
|
case "rdxml":
|
||||||
|
var rdxmlPath = Path.Combine(outputDir, "TrimmedTypes.rd.xml");
|
||||||
|
generator.GenerateRdXml(removedTypes, rdxmlPath);
|
||||||
|
Console.WriteLine($"Generated: {rdxmlPath}");
|
||||||
|
break;
|
||||||
|
case "markdown":
|
||||||
|
var markdownPath = Path.Combine(outputDir, "TrimmedTypes.md");
|
||||||
|
generator.GenerateMarkdown(removedTypes, markdownPath);
|
||||||
|
Console.WriteLine($"Generated: {markdownPath}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine($"Unknown format: {format}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
167
tools/TrimmingAnalyzer/ReportGenerator.cs
Normal file
167
tools/TrimmingAnalyzer/ReportGenerator.cs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using TrimmingAnalyzer.Models;
|
||||||
|
|
||||||
|
namespace TrimmingAnalyzer
|
||||||
|
{
|
||||||
|
public class ReportGenerator
|
||||||
|
{
|
||||||
|
public void GenerateRdXml(List<TypeInfo> removedTypes, string outputPath)
|
||||||
|
{
|
||||||
|
var typesByNamespace = removedTypes.GroupBy(t => t.Namespace);
|
||||||
|
|
||||||
|
// Define the namespace
|
||||||
|
XNamespace ns = "http://schemas.microsoft.com/netfx/2013/01/metadata";
|
||||||
|
|
||||||
|
var doc = new XDocument(
|
||||||
|
new XElement(ns + "Directives",
|
||||||
|
new XElement(ns + "Application",
|
||||||
|
new XComment($"CmdPal Trimming Report - Generated on {DateTime.Now:yyyy-MM-dd HH:mm:ss}"),
|
||||||
|
new XComment($"Total types trimmed: {removedTypes.Count}"),
|
||||||
|
new XComment("TrimMode: partial (as configured in Microsoft.CmdPal.UI.csproj)"),
|
||||||
|
new XElement(ns + "Assembly",
|
||||||
|
new XAttribute("Name", "Microsoft.CmdPal.UI"),
|
||||||
|
new XAttribute("Dynamic", "Required All"),
|
||||||
|
typesByNamespace.Select(g =>
|
||||||
|
new XElement(ns + "Namespace",
|
||||||
|
new XAttribute("Name", g.Key),
|
||||||
|
new XAttribute("Preserve", "All"),
|
||||||
|
new XAttribute("Dynamic", "Required All"),
|
||||||
|
g.Select(type =>
|
||||||
|
new XElement(ns + "Type",
|
||||||
|
new XAttribute("Name", type.Name),
|
||||||
|
new XAttribute("Dynamic", "Required All"),
|
||||||
|
new XAttribute("Serialize", "All"),
|
||||||
|
new XAttribute("DataContractSerializer", "All"),
|
||||||
|
new XAttribute("DataContractJsonSerializer", "All"),
|
||||||
|
new XAttribute("XmlSerializer", "All"),
|
||||||
|
new XAttribute("MarshalObject", "All"),
|
||||||
|
new XAttribute("MarshalDelegate", "All")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? ".");
|
||||||
|
doc.Save(outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateMarkdown(List<TypeInfo> removedTypes, string outputPath)
|
||||||
|
{
|
||||||
|
GenerateMarkdown(removedTypes, outputPath, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateMarkdown(List<TypeInfo> removedTypes, string outputPath, List<string>? assemblyNames)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("# CmdPal Debug vs AOT Release Comparison Report");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine($"**Generated:** {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine($"**Comparison:** Debug Build (no AOT) vs Release Build (with AOT)");
|
||||||
|
sb.AppendLine($"**Purpose:** Show types removed when enabling AOT compilation in Release mode");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
if (assemblyNames != null && assemblyNames.Count > 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"**Analyzed assemblies:** {string.Join(", ", assemblyNames.Distinct().OrderBy(x => x))}");
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine($"**Total types removed by AOT:** {removedTypes.Count}");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Summary by namespace
|
||||||
|
sb.AppendLine("## Summary by Namespace");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("| Namespace | Types Trimmed |");
|
||||||
|
sb.AppendLine("|-----------|---------------|");
|
||||||
|
|
||||||
|
foreach (var group in removedTypes.GroupBy(t => t.Namespace).OrderBy(g => g.Key))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"| `{group.Key}` | {group.Count()} |");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("## Detailed Type List");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
foreach (var group in removedTypes.GroupBy(t => t.Namespace).OrderBy(g => g.Key))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"### {group.Key}");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("| Type | Kind | Visibility | Base Type | Interfaces | Members |");
|
||||||
|
sb.AppendLine("|------|------|------------|-----------|------------|---------|");
|
||||||
|
|
||||||
|
foreach (var type in group.OrderBy(t => t.Name))
|
||||||
|
{
|
||||||
|
var kind = GetTypeKind(type);
|
||||||
|
var visibility = type.IsPublic ? "Public" : "Internal";
|
||||||
|
var baseType = string.IsNullOrEmpty(type.BaseType) ? "-" : $"`{type.BaseType.Split('.').Last()}`";
|
||||||
|
var interfaces = type.Interfaces.Any()
|
||||||
|
? string.Join(", ", type.Interfaces.Take(3).Select(i => $"`{i.Split('.').Last()}`")) +
|
||||||
|
(type.Interfaces.Count > 3 ? "..." : "")
|
||||||
|
: "-";
|
||||||
|
|
||||||
|
sb.AppendLine($"| `{type.Name}` | {kind} | {visibility} | {baseType} | {interfaces} | {type.MemberCount} |");
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add usage instructions
|
||||||
|
sb.AppendLine("## How to Use This Report");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("If you need to preserve any of these types from trimming:");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("1. Copy the relevant entries from `TrimmedTypes.rd.xml` to your project's `rd.xml` file");
|
||||||
|
sb.AppendLine("2. Or use `[DynamicallyAccessedMembers]` attributes in your code");
|
||||||
|
sb.AppendLine("3. Or use `[DynamicDependency]` attributes to preserve specific members");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Note: This report shows types that are present in Debug builds but removed in AOT Release builds.");
|
||||||
|
sb.AppendLine("AOT compilation removes unused types and members to reduce binary size and improve startup performance.");
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? ".");
|
||||||
|
File.WriteAllText(outputPath, sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateJson(List<TypeInfo> removedTypes, string outputPath, string assemblyName)
|
||||||
|
{
|
||||||
|
var analysisResult = new
|
||||||
|
{
|
||||||
|
AssemblyName = assemblyName,
|
||||||
|
GeneratedAt = DateTime.Now,
|
||||||
|
TotalTypes = removedTypes.Count,
|
||||||
|
RemovedTypes = removedTypes.OrderBy(t => t.FullName).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(analysisResult, options);
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? ".");
|
||||||
|
File.WriteAllText(outputPath, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTypeKind(TypeInfo type)
|
||||||
|
{
|
||||||
|
if (type.IsInterface) return "Interface";
|
||||||
|
if (type.IsEnum) return "Enum";
|
||||||
|
if (type.IsDelegate) return "Delegate";
|
||||||
|
if (type.IsAbstract) return "Abstract Class";
|
||||||
|
if (type.IsSealed) return "Sealed Class";
|
||||||
|
return "Class";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
tools/TrimmingAnalyzer/TrimmingAnalyzer.csproj
Normal file
26
tools/TrimmingAnalyzer/TrimmingAnalyzer.csproj
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<EnableDefaultItems>false</EnableDefaultItems>
|
||||||
|
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||||
|
<WarningsAsErrors />
|
||||||
|
<WarningsNotAsErrors />
|
||||||
|
<NoWarn>SA1633;SA1400;SA1518;SA1516;SA1503;SA1111;SA1116;SA1122;SA1028;SA1413;SA1513;CA1311;CA1304;CA1305;CA1860;CA1852</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="TypeAnalyzer.cs" />
|
||||||
|
<Compile Include="ReportGenerator.cs" />
|
||||||
|
<Compile Include="Models\TypeInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Text.Json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
79
tools/TrimmingAnalyzer/TypeAnalyzer.cs
Normal file
79
tools/TrimmingAnalyzer/TypeAnalyzer.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using TrimmingAnalyzer.Models;
|
||||||
|
|
||||||
|
namespace TrimmingAnalyzer
|
||||||
|
{
|
||||||
|
public class TypeAnalyzer
|
||||||
|
{
|
||||||
|
public List<Models.TypeInfo> GetRemovedTypes(string untrimmedPath, string trimmedPath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(untrimmedPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException($"Untrimmed assembly not found: {untrimmedPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(trimmedPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException($"Trimmed assembly not found: {trimmedPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var removedTypes = new List<Models.TypeInfo>();
|
||||||
|
|
||||||
|
var untrimmedContext = new AssemblyLoadContext("Untrimmed", true);
|
||||||
|
var trimmedContext = new AssemblyLoadContext("Trimmed", true);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var untrimmedAssembly = untrimmedContext.LoadFromAssemblyPath(untrimmedPath);
|
||||||
|
var trimmedAssembly = trimmedContext.LoadFromAssemblyPath(trimmedPath);
|
||||||
|
|
||||||
|
var untrimmedTypes = untrimmedAssembly.GetTypes().Where(t => t.FullName != null).ToDictionary(t => t.FullName!);
|
||||||
|
var trimmedTypeNames = trimmedAssembly.GetTypes().Where(t => t.FullName != null).Select(t => t.FullName!).ToHashSet();
|
||||||
|
|
||||||
|
foreach (var kvp in untrimmedTypes)
|
||||||
|
{
|
||||||
|
if (!trimmedTypeNames.Contains(kvp.Key))
|
||||||
|
{
|
||||||
|
var type = kvp.Value;
|
||||||
|
var typeInfo = new Models.TypeInfo
|
||||||
|
{
|
||||||
|
FullName = type.FullName ?? string.Empty,
|
||||||
|
Namespace = type.Namespace ?? "Global",
|
||||||
|
Name = type.Name,
|
||||||
|
IsPublic = type.IsPublic,
|
||||||
|
IsSealed = type.IsSealed,
|
||||||
|
IsAbstract = type.IsAbstract,
|
||||||
|
IsInterface = type.IsInterface,
|
||||||
|
IsEnum = type.IsEnum,
|
||||||
|
IsDelegate = type.IsSubclassOf(typeof(Delegate)),
|
||||||
|
BaseType = type.BaseType?.FullName,
|
||||||
|
Interfaces = type.GetInterfaces().Select(i => i.FullName ?? string.Empty).ToList(),
|
||||||
|
MemberCount = type.GetMembers(
|
||||||
|
BindingFlags.Public | BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Instance | BindingFlags.Static |
|
||||||
|
BindingFlags.DeclaredOnly).Length,
|
||||||
|
};
|
||||||
|
|
||||||
|
removedTypes.Add(typeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Error analyzing assemblies: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
untrimmedContext.Unload();
|
||||||
|
trimmedContext.Unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
return removedTypes.OrderBy(t => t.Namespace).ThenBy(t => t.Name).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
168
tools/build/Generate-CmdPalTrimmingReport.ps1
Normal file
168
tools/build/Generate-CmdPalTrimmingReport.ps1
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
PowerToys CmdPal AOT Trimming Analysis - Generates assembly comparison reports
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This script builds CmdPal UI with and without AOT optimization, then uses TrimmingAnalyzer
|
||||||
|
to analyze the differences and generate reports showing which types are removed by AOT.
|
||||||
|
|
||||||
|
ANALYSIS PROCESS:
|
||||||
|
1. Build Debug version (no AOT optimization)
|
||||||
|
2. Build Release version (with AOT optimization)
|
||||||
|
3. Compare assemblies to identify removed types
|
||||||
|
4. Generate reports: TrimmedTypes.md, TrimmedTypes.rd.xml
|
||||||
|
|
||||||
|
REQUIREMENTS:
|
||||||
|
• Visual Studio 2022 with C++ workload
|
||||||
|
• Windows SDK
|
||||||
|
• Use Developer Command Prompt for VS 2022
|
||||||
|
|
||||||
|
OUTPUT REPORTS:
|
||||||
|
• TrimmedTypes.md - Human-readable Markdown report
|
||||||
|
• TrimmedTypes.rd.xml - Runtime directives to preserve types
|
||||||
|
• Analysis JSON data for further processing
|
||||||
|
|
||||||
|
.PARAMETER Configuration
|
||||||
|
Build configuration (Debug/Release). Defaults to Release
|
||||||
|
|
||||||
|
.PARAMETER EnableAOT
|
||||||
|
Whether to enable AOT compilation. Defaults to true
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Generate-CmdPalTrimmingReport.ps1
|
||||||
|
|
||||||
|
Runs the complete AOT trimming analysis with default settings
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Author: PowerToys CmdPal AOT Analysis Tool
|
||||||
|
Purpose: Show types removed when enabling AOT compilation
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Configuration = "Release",
|
||||||
|
[bool]$EnableAOT = $true
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Write-Host "======================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "PowerToys CmdPal AOT Trimming Analysis" -ForegroundColor Cyan
|
||||||
|
Write-Host "======================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "PURPOSE: Generate reports showing types removed by AOT optimization" -ForegroundColor Yellow
|
||||||
|
Write-Host "OUTPUT: TrimmedTypes.md, TrimmedTypes.rd.xml, analysis data" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Get paths
|
||||||
|
$rootDir = Resolve-Path (Join-Path $PSScriptRoot "..\..")
|
||||||
|
$cmdPalProject = Join-Path $rootDir "src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj"
|
||||||
|
$cmdPalDir = Split-Path -Parent $cmdPalProject
|
||||||
|
$analyzerProject = Join-Path $rootDir "tools\TrimmingAnalyzer\TrimmingAnalyzer.csproj"
|
||||||
|
|
||||||
|
# Build paths
|
||||||
|
$tempDir = Join-Path $env:TEMP "CmdPalTrimAnalysis_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||||||
|
$untrimmedDir = Join-Path $tempDir "untrimmed"
|
||||||
|
$trimmedDir = Join-Path $tempDir "trimmed"
|
||||||
|
|
||||||
|
# Ensure all NuGet packages are restored
|
||||||
|
Write-Host "Restoring NuGet packages..." -ForegroundColor Yellow
|
||||||
|
& dotnet restore $cmdPalProject
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Warning "Package restore had some issues, but continuing..."
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Create directories
|
||||||
|
New-Item -ItemType Directory -Path $untrimmedDir -Force | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path $trimmedDir -Force | Out-Null
|
||||||
|
|
||||||
|
# Build TrimmingAnalyzer
|
||||||
|
Write-Host "Building TrimmingAnalyzer tool..." -ForegroundColor Yellow
|
||||||
|
& dotnet build $analyzerProject -c Release
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Failed to build TrimmingAnalyzer"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build Debug mode without AOT (baseline for comparison)
|
||||||
|
Write-Host "Building CmdPal in Debug mode without AOT (baseline)..." -ForegroundColor Yellow
|
||||||
|
& dotnet publish $cmdPalProject `
|
||||||
|
--configuration Debug `
|
||||||
|
--runtime win-x64 `
|
||||||
|
--self-contained true `
|
||||||
|
--property:PublishTrimmed=false `
|
||||||
|
--property:EnableCmdPalAOT=false `
|
||||||
|
--property:PublishAot=false `
|
||||||
|
--verbosity minimal
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Failed to build Debug baseline version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy baseline (Debug without AOT) output to analysis directory
|
||||||
|
$baselineOutput = Join-Path $cmdPalDir "bin\Debug\net9.0-windows10.0.26100.0\win-x64\publish"
|
||||||
|
Copy-Item "$baselineOutput\Microsoft.CmdPal.UI.dll" $untrimmedDir -Force
|
||||||
|
# Copy all dependencies to help with assembly resolution
|
||||||
|
Get-ChildItem "$baselineOutput\*.dll" | ForEach-Object {
|
||||||
|
if ($_.Name -ne "Microsoft.CmdPal.UI.dll") {
|
||||||
|
Copy-Item $_.FullName $untrimmedDir -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "Copied Debug baseline DLLs from: $baselineOutput"
|
||||||
|
|
||||||
|
# Build Release mode with AOT enabled
|
||||||
|
Write-Host "Building CmdPal in Release mode with AOT enabled..." -ForegroundColor Yellow
|
||||||
|
& dotnet publish $cmdPalProject `
|
||||||
|
--configuration Release `
|
||||||
|
--runtime win-x64 `
|
||||||
|
--self-contained true `
|
||||||
|
--property:PublishTrimmed=false `
|
||||||
|
--property:EnableCmdPalAOT=true `
|
||||||
|
--property:PublishAot=true `
|
||||||
|
--verbosity minimal
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Failed to build AOT+trimmed version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy AOT+trimmed output to analysis directory
|
||||||
|
$trimmedOutput = Join-Path $cmdPalDir "bin\$Configuration\net9.0-windows10.0.26100.0\win-x64\publish"
|
||||||
|
Copy-Item "$trimmedOutput\Microsoft.CmdPal.UI.dll" $trimmedDir -Force
|
||||||
|
# Copy all dependencies to help with assembly resolution
|
||||||
|
Get-ChildItem "$trimmedOutput\*.dll" | ForEach-Object {
|
||||||
|
if ($_.Name -ne "Microsoft.CmdPal.UI.dll") {
|
||||||
|
Copy-Item $_.FullName $trimmedDir -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "Copied Release AOT DLLs from: $trimmedOutput"
|
||||||
|
|
||||||
|
# Use new directory comparison method to compare all types
|
||||||
|
Write-Host "Analyzing differences (Debug baseline vs Release AOT)..." -ForegroundColor Yellow
|
||||||
|
Write-Host "Using advanced directory comparison to detect AOT-optimized types..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Use the new directory comparison feature
|
||||||
|
& $analyzerPath --compare-directories "$untrimmedDir" "$trimmedDir" "$cmdPalDir" "rdxml,markdown,json" "TrimmedTypes"
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "Analysis completed successfully!" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
throw "Analysis failed with exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "`n===== Analysis Complete =====" -ForegroundColor Cyan
|
||||||
|
Write-Host "Reports generated in: $cmdPalDir" -ForegroundColor Green
|
||||||
|
|
||||||
|
# List the main combined reports
|
||||||
|
$mainReports = @("TrimmedTypes.md", "TrimmedTypes.rd.xml")
|
||||||
|
foreach ($report in $mainReports) {
|
||||||
|
$reportPath = Join-Path $cmdPalDir $report
|
||||||
|
if (Test-Path $reportPath) {
|
||||||
|
Write-Host " - $report" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
# Cleanup
|
||||||
|
if (Test-Path $tempDir) {
|
||||||
|
Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user