-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Support batch PDB generation. #3619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3ee07e4
ef52899
71cfce3
c5c3505
1f13b80
cae0022
cfc5d2f
e807f1a
5625db2
a973b2e
e027a1a
752e6ed
b631b55
da6d257
4e5727f
217c72d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |||||||||||
| // DEALINGS IN THE SOFTWARE. | ||||||||||||
|
|
||||||||||||
| using System; | ||||||||||||
| using System.Collections.Generic; | ||||||||||||
| using System.Composition; | ||||||||||||
| using System.Diagnostics; | ||||||||||||
| using System.IO; | ||||||||||||
|
|
@@ -47,19 +48,30 @@ class GeneratePdbContextMenuEntry(LanguageService languageService, DockWorkspace | |||||||||||
| { | ||||||||||||
| public void Execute(TextViewContext context) | ||||||||||||
| { | ||||||||||||
| var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly; | ||||||||||||
| if (assembly == null) | ||||||||||||
| var selectedNodes = context.SelectedTreeNodes?.OfType<AssemblyTreeNode>().ToArray(); | ||||||||||||
| if (selectedNodes == null || selectedNodes.Length == 0) | ||||||||||||
| return; | ||||||||||||
| GeneratePdbForAssembly(assembly, languageService, dockWorkspace); | ||||||||||||
|
|
||||||||||||
| if (selectedNodes.Length == 1) | ||||||||||||
| { | ||||||||||||
| var assembly = selectedNodes.First().LoadedAssembly; | ||||||||||||
| if (assembly == null) | ||||||||||||
| return; | ||||||||||||
| GeneratePdbForAssembly(assembly, languageService, dockWorkspace); | ||||||||||||
| } | ||||||||||||
| else | ||||||||||||
| { | ||||||||||||
| GeneratePdbForAssemblies(selectedNodes.Select(n => n.LoadedAssembly), languageService, dockWorkspace); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| public bool IsEnabled(TextViewContext context) => true; | ||||||||||||
|
|
||||||||||||
| public bool IsVisible(TextViewContext context) | ||||||||||||
| { | ||||||||||||
| return context.SelectedTreeNodes?.Length == 1 | ||||||||||||
| && context.SelectedTreeNodes?.FirstOrDefault() is AssemblyTreeNode tn | ||||||||||||
| && tn.LoadedAssembly.IsLoadedAsValidAssembly; | ||||||||||||
| var selectedNodes = context.SelectedTreeNodes; | ||||||||||||
| return selectedNodes?.Any() == true | ||||||||||||
| && selectedNodes.All(n => n is AssemblyTreeNode asm && asm.LoadedAssembly.IsLoadedAsValidAssembly); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| internal static void GeneratePdbForAssembly(LoadedAssembly assembly, LanguageService languageService, DockWorkspace dockWorkspace) | ||||||||||||
|
|
@@ -82,13 +94,13 @@ internal static void GeneratePdbForAssembly(LoadedAssembly assembly, LanguageSer | |||||||||||
| AvalonEditTextOutput output = new AvalonEditTextOutput(); | ||||||||||||
| Stopwatch stopwatch = Stopwatch.StartNew(); | ||||||||||||
| options.CancellationToken = ct; | ||||||||||||
| using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) | ||||||||||||
| using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write)) | ||||||||||||
| { | ||||||||||||
| try | ||||||||||||
| { | ||||||||||||
| var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences), options.DecompilerSettings); | ||||||||||||
| decompiler.CancellationToken = ct; | ||||||||||||
| PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream, progress: options.Progress); | ||||||||||||
| PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream, progress: options.Progress, currentProgressTitle: string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName))); | ||||||||||||
| } | ||||||||||||
| catch (OperationCanceledException) | ||||||||||||
| { | ||||||||||||
|
|
@@ -100,7 +112,130 @@ internal static void GeneratePdbForAssembly(LoadedAssembly assembly, LanguageSer | |||||||||||
| stopwatch.Stop(); | ||||||||||||
| output.WriteLine(Resources.GenerationCompleteInSeconds, stopwatch.Elapsed.TotalSeconds.ToString("F1")); | ||||||||||||
| output.WriteLine(); | ||||||||||||
| output.AddButton(null, Resources.OpenExplorer, delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); }); | ||||||||||||
| output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItem(fileName); }); | ||||||||||||
| output.WriteLine(); | ||||||||||||
| return output; | ||||||||||||
| }, ct)).Then(dockWorkspace.ShowText).HandleExceptions(); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| internal static void GeneratePdbForAssemblies(IEnumerable<LoadedAssembly> assemblies, LanguageService languageService, DockWorkspace dockWorkspace) | ||||||||||||
| { | ||||||||||||
| var assemblyArray = assemblies?.Where(a => a != null).ToArray() ?? []; | ||||||||||||
| if (assemblyArray == null || assemblyArray.Length == 0) | ||||||||||||
| return; | ||||||||||||
|
|
||||||||||||
| // Ensure at least one assembly supports PDB generation | ||||||||||||
| var supported = new Dictionary<LoadedAssembly, PEFile>(); | ||||||||||||
| var unsupported = new List<LoadedAssembly>(); | ||||||||||||
| foreach (var a in assemblyArray) | ||||||||||||
| { | ||||||||||||
| try | ||||||||||||
| { | ||||||||||||
| var file = a.GetMetadataFileOrNull() as PEFile; | ||||||||||||
| if (PortablePdbWriter.HasCodeViewDebugDirectoryEntry(file)) | ||||||||||||
| supported.Add(a, file); | ||||||||||||
| else | ||||||||||||
| unsupported.Add(a); | ||||||||||||
| } | ||||||||||||
| catch | ||||||||||||
| { | ||||||||||||
| unsupported.Add(a); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| if (supported.Count == 0) | ||||||||||||
| { | ||||||||||||
| // none can be generated | ||||||||||||
| string msg = string.Format(Resources.CannotCreatePDBFile, ":" + Environment.NewLine + | ||||||||||||
| string.Join(Environment.NewLine, unsupported.Select(u => Path.GetFileName(u.FileName))) | ||||||||||||
| + Environment.NewLine); | ||||||||||||
|
Comment on lines
+148
to
+150
|
||||||||||||
| string msg = string.Format(Resources.CannotCreatePDBFile, ":" + Environment.NewLine + | |
| string.Join(Environment.NewLine, unsupported.Select(u => Path.GetFileName(u.FileName))) | |
| + Environment.NewLine); | |
| string msg = Resources.CannotCreatePDBFile + Environment.NewLine + | |
| string.Join(Environment.NewLine, unsupported.Select(u => " - " + Path.GetFileName(u.FileName))); |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expression 'A != true' can be simplified to '!A'.
| if (dlg.ShowDialog() != true || string.IsNullOrWhiteSpace(dlg.FolderName)) | |
| if (!dlg.ShowDialog() || string.IsNullOrWhiteSpace(dlg.FolderName)) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The redundant null check can be simplified. Line 123 already filters out null values with
assemblies?.Where(a => a != null).ToArray() ?? [], so the subsequent checkif (assemblyArray == null || assemblyArray.Length == 0)will never have a nullassemblyArray.