mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-31 01:16:59 +01:00
Compare commits
36 Commits
yuleng/aot
...
leilzh/pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfa490cfde | ||
|
|
b99e62af48 | ||
|
|
18b926c41d | ||
|
|
2eb99f96a3 | ||
|
|
fb260bd098 | ||
|
|
f35f94bbaa | ||
|
|
873a7a2bbe | ||
|
|
0a8b83e528 | ||
|
|
7849079de4 | ||
|
|
1a303301bb | ||
|
|
3206c0b073 | ||
|
|
82d031cf2a | ||
|
|
5c7c8c3009 | ||
|
|
64a48fda97 | ||
|
|
75cbd2d577 | ||
|
|
dd47beabd8 | ||
|
|
29a8bbc40a | ||
|
|
6607b60bd5 | ||
|
|
983591e4e0 | ||
|
|
0a2a3fea93 | ||
|
|
30d67ec078 | ||
|
|
4c87a78b1a | ||
|
|
f12d983025 | ||
|
|
369babc0f8 | ||
|
|
6834fa24af | ||
|
|
14f18f4b95 | ||
|
|
4bf2342a09 | ||
|
|
20243c4b99 | ||
|
|
a19127bf49 | ||
|
|
aeaa5297f9 | ||
|
|
cd45bad9ce | ||
|
|
2ae636a860 | ||
|
|
3bf8a63dd8 | ||
|
|
11b6e15869 | ||
|
|
f59063d285 | ||
|
|
83703d2cf2 |
2
.github/actions/spell-check/expect.txt
vendored
2
.github/actions/spell-check/expect.txt
vendored
@@ -1440,7 +1440,6 @@ secpol
|
||||
securestring
|
||||
SEEMASKINVOKEIDLIST
|
||||
SELCHANGE
|
||||
selfhost
|
||||
SENDCHANGE
|
||||
sendvirtualinput
|
||||
serverside
|
||||
@@ -1880,7 +1879,6 @@ winexe
|
||||
winforms
|
||||
winget
|
||||
wingetcreate
|
||||
wingetpkgs
|
||||
Winhook
|
||||
WINL
|
||||
winlogon
|
||||
|
||||
1
deps/cxxopts
vendored
1
deps/cxxopts
vendored
Submodule deps/cxxopts deleted from 12e496da3d
@@ -87,13 +87,6 @@
|
||||
|
||||
### Building PowerToys Locally
|
||||
|
||||
#### One stop script for building installer
|
||||
1. Open developer powershell for vs 2022
|
||||
2. Run tools\build\build-installer.ps1
|
||||
> For the first-time setup, please run the installer as an administrator. This ensures that the Wix tool can move wix.target to the desired location and trust the certificate used to sign the MSIX packages.
|
||||
|
||||
The following manual steps will not install the MSIX apps (such as Command Palette) on your local installer.
|
||||
|
||||
#### Prerequisites for building the MSI installer
|
||||
|
||||
1. Install the [WiX Toolset Visual Studio 2022 Extension](https://marketplace.visualstudio.com/items?itemName=WixToolset.WixToolsetVisualStudio2022Extension).
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
## If for any reason, you'd like to test winget install scenario, you can follow this doc:
|
||||
|
||||
### Powertoys winget manifest definition:
|
||||
[winget repository](https://github.com/microsoft/winget-pkgs/tree/master/manifests/m/Microsoft/PowerToys)
|
||||
|
||||
### How to test a winget installation locally:
|
||||
1. Get artifacts from release CI pipeline Pipelines - Runs for PowerToys Signed YAML Release Build, or you can build one yourself by execute the
|
||||
'tools\build\build-installer.ps1' script
|
||||
|
||||
2. Get the artifact hash, this is required to define winget manifest
|
||||
```powershell
|
||||
cd /path/to/your/directory/contains/installer
|
||||
Get-FileHash -Path ".\<Installer-name>.exe" -Algorithm SHA256
|
||||
```
|
||||
3. Host your installer.exe - Attention: staged github release artifacts or artifacts in release pipeline is not OK in this step
|
||||
You can self-host it or you can upload to a publicly available endpoint
|
||||
**How to selfhost it** (A extremely simple way):
|
||||
```powershell
|
||||
python -m http.server 8000
|
||||
```
|
||||
|
||||
4. Download a version folder from wingetpkgs like: [version 0.92.1](https://github.com/microsoft/winget-pkgs/tree/master/manifests/m/Microsoft/PowerToys/0.92.1)
|
||||
and you get **a folder contains 3 yml files**
|
||||
>note: Do not put any files other than these three in this folder
|
||||
|
||||
5. Modify the yml files based on your version and the self hosted artifact link, and modify the sha256 hash for the installer you'd like to use
|
||||
|
||||
6. Start winget install:
|
||||
```powershell
|
||||
#execute as admin
|
||||
winget settings --enable LocalManifestFiles
|
||||
winget install --manifest "<folder_path_of_manifest_files>" --architecture x64 --scope user
|
||||
```
|
||||
@@ -32,7 +32,6 @@ public partial class MainListPage : DynamicListPage,
|
||||
public MainListPage(IServiceProvider serviceProvider)
|
||||
{
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.scale-200.png");
|
||||
PlaceholderText = Properties.Resources.builtin_main_list_page_searchbar_placeholder;
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
_tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
|
||||
@@ -321,15 +321,6 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search for apps, files and commands....
|
||||
/// </summary>
|
||||
public static string builtin_main_list_page_searchbar_placeholder {
|
||||
get {
|
||||
return ResourceManager.GetString("builtin_main_list_page_searchbar_placeholder", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Creates a project for a new Command Palette extension.
|
||||
/// </summary>
|
||||
|
||||
@@ -227,7 +227,4 @@
|
||||
<data name="builtin_disabled_extension" xml:space="preserve">
|
||||
<value>Disabled</value>
|
||||
</data>
|
||||
<data name="builtin_main_list_page_searchbar_placeholder" xml:space="preserve">
|
||||
<value>Search for apps, files and commands...</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,51 +0,0 @@
|
||||
# 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++ 构建环境配置后即可使用*
|
||||
@@ -1,20 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<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>
|
||||
@@ -1,79 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
<#
|
||||
.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