diff --git a/deps/cxxopts b/deps/cxxopts new file mode 160000 index 0000000000..12e496da3d --- /dev/null +++ b/deps/cxxopts @@ -0,0 +1 @@ +Subproject commit 12e496da3d486b87fa9df43edea65232ed852510 diff --git a/tools/TrimmingAnalyzer/ReportGenerator.cs b/tools/TrimmingAnalyzer/ReportGenerator.cs index f0c4ce1c02..ebdf4480c5 100644 --- a/tools/TrimmingAnalyzer/ReportGenerator.cs +++ b/tools/TrimmingAnalyzer/ReportGenerator.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.Json; using System.Xml.Linq; using TrimmingAnalyzer.Models; @@ -14,23 +15,25 @@ namespace TrimmingAnalyzer { 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("Directives", - new XAttribute("xmlns", "http://schemas.microsoft.com/netfx/2013/01/metadata"), - new XElement("Application", + 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("Assembly", + new XElement(ns + "Assembly", new XAttribute("Name", "Microsoft.CmdPal.UI"), new XAttribute("Dynamic", "Required All"), typesByNamespace.Select(g => - new XElement("Namespace", + new XElement(ns + "Namespace", new XAttribute("Name", g.Key), new XAttribute("Preserve", "All"), new XAttribute("Dynamic", "Required All"), g.Select(type => - new XElement("Type", + new XElement(ns + "Type", new XAttribute("Name", type.Name), new XAttribute("Dynamic", "Required All"), new XAttribute("Serialize", "All"), @@ -53,15 +56,28 @@ namespace TrimmingAnalyzer } public void GenerateMarkdown(List removedTypes, string outputPath) + { + GenerateMarkdown(removedTypes, outputPath, null); + } + + public void GenerateMarkdown(List removedTypes, string outputPath, List? assemblyNames) { var sb = new StringBuilder(); - sb.AppendLine("# CmdPal Trimming Report"); + 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($"**Configuration:** TrimMode=partial (as configured in project)"); + 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(); - sb.AppendLine($"**Total types trimmed:** {removedTypes.Count}"); + + 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 @@ -110,12 +126,34 @@ namespace TrimmingAnalyzer 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: CmdPal uses `TrimMode=partial` which only trims assemblies that opt-in to trimming."); + 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 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"; diff --git a/tools/TrimmingAnalyzer/TrimmingAnalyzer.csproj b/tools/TrimmingAnalyzer/TrimmingAnalyzer.csproj index 54e555d6ae..c3bedaa495 100644 --- a/tools/TrimmingAnalyzer/TrimmingAnalyzer.csproj +++ b/tools/TrimmingAnalyzer/TrimmingAnalyzer.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 latest enable false @@ -19,4 +19,8 @@ + + + + \ No newline at end of file diff --git a/tools/build/Generate-CmdPalTrimmingReport.ps1 b/tools/build/Generate-CmdPalTrimmingReport.ps1 index 742603dd7e..f367d54a50 100644 --- a/tools/build/Generate-CmdPalTrimmingReport.ps1 +++ b/tools/build/Generate-CmdPalTrimmingReport.ps1 @@ -2,7 +2,12 @@ .SYNOPSIS Generates a trimming report for CmdPal UI project .DESCRIPTION - This script builds CmdPal UI with and without trimming/AOT, then uses TrimmingAnalyzer + if ($LASTEXITCODE -ne 0) { + throw "Failed to build Release AOT version" + } + + # Copy AOT Release output to analysis directory + $trimmedOutput = Join-Path $cmdPalDir "bin\Release\net9.0-windows10.0.26100.0\win-x64\publish"s script builds CmdPal UI with and without trimming/AOT, then uses TrimmingAnalyzer to analyze the differences and generate reports. .PARAMETER Configuration Build configuration (Debug/Release). Defaults to Release @@ -42,10 +47,10 @@ try { throw "Failed to build TrimmingAnalyzer" } - # Build without trimming using MSBuild - Write-Host "Building CmdPal without trimming..." -ForegroundColor Yellow + # Build Debug mode without AOT (baseline for comparison) + Write-Host "Building CmdPal in Debug mode without AOT (baseline)..." -ForegroundColor Yellow & msbuild $cmdPalProject ` - /p:Configuration=$Configuration ` + /p:Configuration=Debug ` /p:Platform=x64 ` /p:PublishTrimmed=false ` /p:EnableCmdPalAOT=false ` @@ -56,58 +61,72 @@ try { /verbosity:minimal if ($LASTEXITCODE -ne 0) { - throw "Failed to build untrimmed version" + throw "Failed to build Debug baseline version" } - # Copy untrimmed output to analysis directory - $untrimmedOutput = Join-Path $cmdPalDir "x64\$Configuration\WinUI3Apps\CmdPal" - Copy-Item "$untrimmedOutput\Microsoft.CmdPal.UI.dll" $untrimmedDir -Force - Write-Host "Copied untrimmed DLL from: $untrimmedOutput" + # 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 with trimming using MSBuild - Write-Host "Building CmdPal with trimming (AOT=$EnableAOT)..." -ForegroundColor Yellow + # Build Release mode with AOT enabled + Write-Host "Building CmdPal in Release mode with AOT enabled..." -ForegroundColor Yellow & msbuild $cmdPalProject ` - /p:Configuration=$Configuration ` + /p:Configuration=Release ` /p:Platform=x64 ` - /p:PublishTrimmed=true ` - /p:EnableCmdPalAOT=$EnableAOT ` - /p:PublishAot=$EnableAOT ` + /p:PublishTrimmed=false ` + /p:EnableCmdPalAOT=true ` + /p:PublishAot=true ` /p:RuntimeIdentifier=win-x64 ` /p:SelfContained=true ` - /p:TrimMode=partial ` - /p:TreatWarningsAsErrors=false ` - /p:WarningsAsErrors="" ` - /p:WarningsNotAsErrors="" ` - /p:SuppressTrimAnalysisWarnings=true ` - "/p:NoWarn=IL2104,IL2026,IL2070,IL2072,IL2075,IL2077,IL2080,IL2091,IL2092,IL2093,IL2094,IL2095,IL2096,IL2097,IL2098,IL2099,IL2103,CsWinRT1028" ` /t:Publish ` /verbosity:minimal if ($LASTEXITCODE -ne 0) { - throw "Failed to build trimmed version" + throw "Failed to build AOT+trimmed version" } - # Copy trimmed output to analysis directory - $trimmedOutput = Join-Path $cmdPalDir "x64\$Configuration\WinUI3Apps\CmdPal" + # 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 - Write-Host "Copied trimmed DLL from: $trimmedOutput" + # 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" - # Run analyzer - Write-Host "Analyzing trimming differences..." -ForegroundColor Yellow - & dotnet run --project $analyzerProject -- ` - "$untrimmedDir\Microsoft.CmdPal.UI.dll" ` - "$trimmedDir\Microsoft.CmdPal.UI.dll" ` - $cmdPalDir ` - "rdxml,markdown" - - if ($LASTEXITCODE -ne 0) { - throw "Failed to analyze trimming" + # 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 - Write-Host " - TrimmedTypes.rd.xml" -ForegroundColor Green - Write-Host " - TrimmedTypes.md" -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 diff --git a/tools/build/Monitor-Build.ps1 b/tools/build/Monitor-Build.ps1 new file mode 100644 index 0000000000..4c925fbdca --- /dev/null +++ b/tools/build/Monitor-Build.ps1 @@ -0,0 +1,53 @@ +# Monitor build progress and report generation +Write-Host "===== Monitoring CmdPal Build Progress =====" + +$reportPath = "C:\Users\yuleng\PowerToys\src\modules\cmdpal\Microsoft.CmdPal.UI\TrimmedTypes.md" +$lastSize = 0 +$lastModified = Get-Date "1/1/1900" + +if (Test-Path $reportPath) { + $lastModified = (Get-Item $reportPath).LastWriteTime + $lastSize = (Get-Item $reportPath).Length + Write-Host "Current report file: $reportPath" + Write-Host "Last modified: $lastModified" + Write-Host "File size: $lastSize bytes" +} else { + Write-Host "No report file found yet at: $reportPath" +} + +Write-Host "`nChecking for active build processes..." +$buildProcesses = Get-Process | Where-Object { + $_.ProcessName -like "*msbuild*" -or + $_.ProcessName -like "*dotnet*" -or + $_.ProcessName -like "*cl*" -or + $_.ProcessName -like "*link*" +} + +Write-Host "Found $($buildProcesses.Count) build-related processes running" + +if ($buildProcesses.Count -gt 0) { + Write-Host "Build processes still active:" + $buildProcesses | Select-Object ProcessName, Id, CPU | Format-Table -AutoSize +} else { + Write-Host "No build processes detected. Build may be complete." +} + +# Check if directories exist for both builds +$debugPath = "C:\Users\yuleng\PowerToys\src\modules\cmdpal\Microsoft.CmdPal.UI\bin\Debug" +$releasePath = "C:\Users\yuleng\PowerToys\src\modules\cmdpal\Microsoft.CmdPal.UI\bin\Release" + +Write-Host "`nBuild directory status:" +Write-Host "Debug path exists: $(Test-Path $debugPath)" +Write-Host "Release path exists: $(Test-Path $releasePath)" + +if (Test-Path $debugPath) { + $debugDlls = Get-ChildItem -Path $debugPath -Recurse -Filter "Microsoft.CmdPal.UI.dll" -ErrorAction SilentlyContinue + Write-Host "Debug DLLs found: $($debugDlls.Count)" +} + +if (Test-Path $releasePath) { + $releaseDlls = Get-ChildItem -Path $releasePath -Recurse -Filter "Microsoft.CmdPal.UI.dll" -ErrorAction SilentlyContinue + Write-Host "Release DLLs found: $($releaseDlls.Count)" +} + +Write-Host "`nMonitoring complete." diff --git a/tools/build/Test-Analyzer.ps1 b/tools/build/Test-Analyzer.ps1 new file mode 100644 index 0000000000..651ce58833 --- /dev/null +++ b/tools/build/Test-Analyzer.ps1 @@ -0,0 +1,39 @@ +# Simple test to verify TrimmingAnalyzer functionality +Write-Host "===== TrimmingAnalyzer Simple Test =====" + +$analyzerPath = "C:\Users\yuleng\PowerToys\tools\TrimmingAnalyzer\bin\Release\net9.0\TrimmingAnalyzer.exe" + +if (-not (Test-Path $analyzerPath)) { + Write-Error "TrimmingAnalyzer not found at $analyzerPath" + exit 1 +} + +Write-Host "TrimmingAnalyzer found at: $analyzerPath" + +# Test the help command +Write-Host "`nTesting help command..." +try { + $helpOutput = & $analyzerPath --help 2>&1 + Write-Host "Help command executed successfully" + Write-Host "Output length: $($helpOutput.Length) characters" + + # Show first few lines of help + $helpLines = $helpOutput -split "`n" + $helpLines | Select-Object -First 10 | ForEach-Object { Write-Host " $_" } + + if ($helpLines.Length -gt 10) { + Write-Host " ... (truncated)" + } +} catch { + Write-Error "Failed to run help command: $_" +} + +Write-Host "`nTesting version command..." +try { + $versionOutput = & $analyzerPath --version 2>&1 + Write-Host "Version: $versionOutput" +} catch { + Write-Warning "Version command failed: $_" +} + +Write-Host "`nTrimmingAnalyzer test completed." diff --git a/tools/build/Test-NewAnalyzer.ps1 b/tools/build/Test-NewAnalyzer.ps1 new file mode 100644 index 0000000000..8290d4090c --- /dev/null +++ b/tools/build/Test-NewAnalyzer.ps1 @@ -0,0 +1,30 @@ +# Test script to run the updated CmdPal analysis with current working functionality +param( + [string]$Configuration = "Release", + [switch]$EnableAOT = $true +) + +Write-Host "===== CmdPal AOT Analysis Test =====" + +$rootDir = Split-Path -Parent $PSScriptRoot +$rootDir = Split-Path -Parent $rootDir + +$cmdPalProject = Join-Path $rootDir "src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj" +$analyzerProject = Join-Path $rootDir "tools\TrimmingAnalyzer\TrimmingAnalyzer.csproj" + +# Build the current analyzer +Write-Host "Building TrimmingAnalyzer..." +dotnet build $analyzerProject -c Release + +if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to build TrimmingAnalyzer" + exit 1 +} + +$analyzerPath = Join-Path $rootDir "tools\TrimmingAnalyzer\bin\Release\net9.0\TrimmingAnalyzer.exe" + +# Test the analyzer help +Write-Host "`nTesting analyzer functionality..." +& $analyzerPath + +Write-Host "`nAnalyzer test completed." diff --git a/tools/build/Test-TrimmingReport.ps1 b/tools/build/Test-TrimmingReport.ps1 new file mode 100644 index 0000000000..f3edfb8c6b --- /dev/null +++ b/tools/build/Test-TrimmingReport.ps1 @@ -0,0 +1,60 @@ +# Quick test of the trimming analyzer with existing builds +param( + [string]$BaselineDir = "C:\Users\yuleng\PowerToys\src\modules\cmdpal\Microsoft.CmdPal.UI\bin\Release\net9.0-windows10.0.26100.0\win-x64\publish", + [string]$TrimmedDir = "C:\Users\yuleng\PowerToys\src\modules\cmdpal\Microsoft.CmdPal.UI\bin\Release\net9.0-windows10.0.26100.0\win-x64\publish" +) + +Write-Host "===== Quick Trimming Analysis Test =====" + +# Build the analyzer first +Write-Host "Building TrimmingAnalyzer..." +dotnet build "C:\Users\yuleng\PowerToys\tools\TrimmingAnalyzer\TrimmingAnalyzer.csproj" -c Release + +if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to build TrimmingAnalyzer" + exit 1 +} + +$analyzerPath = "C:\Users\yuleng\PowerToys\tools\TrimmingAnalyzer\bin\Release\net9.0\TrimmingAnalyzer.exe" + +if (-not (Test-Path $analyzerPath)) { + Write-Error "TrimmingAnalyzer not found at $analyzerPath" + exit 1 +} + +# Check if any DLLs exist in the specified directories +$baselineDlls = Get-ChildItem -Path $BaselineDir -Filter "*.dll" -ErrorAction SilentlyContinue +$trimmedDlls = Get-ChildItem -Path $TrimmedDir -Filter "*.dll" -ErrorAction SilentlyContinue + +Write-Host "Baseline directory: $BaselineDir" +Write-Host "Found $($baselineDlls.Count) DLLs in baseline" + +Write-Host "Trimmed directory: $TrimmedDir" +Write-Host "Found $($trimmedDlls.Count) DLLs in trimmed" + +if ($baselineDlls.Count -eq 0 -or $trimmedDlls.Count -eq 0) { + Write-Warning "No DLLs found in one or both directories. The script may need actual AOT builds to compare." + Write-Host "Current directories contain:" + Write-Host "Baseline: $($baselineDlls.Name -join ', ')" + Write-Host "Trimmed: $($trimmedDlls.Name -join ', ')" +} + +# Test the analyzer with a simple case +if ($baselineDlls.Count -gt 0 -and $trimmedDlls.Count -gt 0) { + $testDll = $baselineDlls[0].Name + $baselinePath = Join-Path $BaselineDir $testDll + $trimmedPath = Join-Path $TrimmedDir $testDll + + if (Test-Path $trimmedPath) { + Write-Host "Testing analyzer with $testDll..." + & $analyzerPath analyze "$baselinePath" "$trimmedPath" --output "test-report" + + if (Test-Path "test-report.md") { + Write-Host "`nGenerated report preview:" + Get-Content "test-report.md" | Select-Object -First 20 + } + } +} + +Write-Host "`nAnalyzer path: $analyzerPath" +Write-Host "Test completed."