From 5156a9db00b8b9e2a396c313de5b92ea0f750270 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 12 Jul 2023 14:08:40 +0200 Subject: [PATCH 1/5] Update CommandLineUtils, switch to OnExecuteAsync (although async is not yet used) --- ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj | 6 +----- ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs | 15 ++++++++------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj index 27a0ad480e..cfbb05b506 100644 --- a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj +++ b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj @@ -49,11 +49,7 @@ - - - - - + diff --git a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs index 1e46531cfc..6fe688dead 100644 --- a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs +++ b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs @@ -8,6 +8,7 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Threading; +using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; @@ -48,7 +49,7 @@ into nicely nested directories. MemberName = nameof(DecompilerVersion))] class ILSpyCmdProgram { - public static int Main(string[] args) => CommandLineApplication.Execute(args); + public static Task Main(string[] args) => CommandLineApplication.ExecuteAsync(args); [FilesExist] [Required] @@ -106,7 +107,7 @@ class ILSpyCmdProgram [Option("--nested-directories", "Use nested directories for namespaces.", CommandOptionType.NoValue)] public bool NestedDirectories { get; } - private int OnExecute(CommandLineApplication app) + private Task OnExecuteAsync(CommandLineApplication app) { TextWriter output = System.Console.Out; string outputDirectory = ResolveOutputDirectory(OutputDirectory); @@ -124,7 +125,7 @@ private int OnExecute(CommandLineApplication app) { string projectFileName = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(InputAssemblyNames[0]) + ".csproj"); DecompileAsProject(InputAssemblyNames[0], projectFileName); - return 0; + return Task.FromResult(0); } var projects = new List(); foreach (var file in InputAssemblyNames) @@ -135,7 +136,7 @@ private int OnExecute(CommandLineApplication app) projects.Add(new ProjectItem(projectFileName, projectId.PlatformName, projectId.Guid, projectId.TypeGuid)); } SolutionCreator.WriteSolutionFile(Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(outputDirectory) + ".sln"), projects); - return 0; + return Task.FromResult(0); } else { @@ -143,15 +144,15 @@ private int OnExecute(CommandLineApplication app) { int result = PerformPerFileAction(file); if (result != 0) - return result; + return Task.FromResult(result); } - return 0; + return Task.FromResult(0); } } catch (Exception ex) { app.Error.WriteLine(ex.ToString()); - return ProgramExitCodes.EX_SOFTWARE; + return Task.FromResult(ProgramExitCodes.EX_SOFTWARE); } finally { From 8e3c1af9745f9e42782d3b1dde4dfa96086a0e6a Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 12 Jul 2023 14:14:29 +0200 Subject: [PATCH 2/5] Start using HostBuilder with CommandLineUtils --- ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj | 3 ++- ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj index cfbb05b506..bf430983ae 100644 --- a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj +++ b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj @@ -49,7 +49,8 @@ - + + diff --git a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs index 6fe688dead..50770a98f6 100644 --- a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs +++ b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs @@ -22,6 +22,8 @@ using McMaster.Extensions.CommandLineUtils; +using Microsoft.Extensions.Hosting; + namespace ICSharpCode.ILSpyCmd { [Command(Name = "ilspycmd", Description = "dotnet tool for decompiling .NET assemblies and generating portable PDBs", @@ -49,7 +51,7 @@ into nicely nested directories. MemberName = nameof(DecompilerVersion))] class ILSpyCmdProgram { - public static Task Main(string[] args) => CommandLineApplication.ExecuteAsync(args); + public static Task Main(string[] args) => new HostBuilder().RunCommandLineApplicationAsync(args); [FilesExist] [Required] @@ -107,6 +109,12 @@ class ILSpyCmdProgram [Option("--nested-directories", "Use nested directories for namespaces.", CommandOptionType.NoValue)] public bool NestedDirectories { get; } + private readonly IHostEnvironment _env; + public ILSpyCmdProgram(IHostEnvironment env) + { + _env = env; + } + private Task OnExecuteAsync(CommandLineApplication app) { TextWriter output = System.Console.Out; From bf91f463f9f96db6e4a1ceec77a00e78caf7f147 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 12 Jul 2023 14:56:44 +0200 Subject: [PATCH 3/5] Latest version check added (disabled). (Temp) Fixed DirectoryExists for ReferencePaths (see https://github.com/natemcmaster/CommandLineUtils/issues/536) --- .../DotNetToolUpdateChecker.cs | 51 +++++++++++++++++++ .../ICSharpCode.ILSpyCmd.csproj | 1 + ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs | 12 +++-- 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs diff --git a/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs b/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs new file mode 100644 index 0000000000..2f467e8fc9 --- /dev/null +++ b/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +using NuGet.Common; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; + +namespace ICSharpCode.ILSpyCmd +{ + // Idea from https://github.com/ErikEJ/EFCorePowerTools/blob/master/src/GUI/efcpt/Services/PackageService.cs + internal static class DotNetToolUpdateChecker + { + static NuGetVersion CurrentPackageVersion() + { + return new NuGetVersion(Assembly.GetEntryAssembly()!.GetCustomAttribute()! + .InformationalVersion); + } + + public static async Task CheckForPackageUpdateAsync(string packageId) + { + try + { + using var cache = new SourceCacheContext(); + var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); + var resource = await repository.GetResourceAsync().ConfigureAwait(false); + + var versions = await resource.GetAllVersionsAsync( + packageId, + cache, + new NullLogger(), + CancellationToken.None).ConfigureAwait(false); + + var latestVersion = versions.Where(v => v.Release == "").MaxBy(v => v); + if (latestVersion > CurrentPackageVersion()) + { + Console.WriteLine("You are not using the latest version of the tool, please update."); + Console.WriteLine($"Latest version is '{latestVersion}'"); + } + } +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. + catch (Exception) + { + } +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. + } + } +} diff --git a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj index bf430983ae..c97d0e6505 100644 --- a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj +++ b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj @@ -51,6 +51,7 @@ + diff --git a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs index 50770a98f6..17ff152b25 100644 --- a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs +++ b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs @@ -51,6 +51,8 @@ into nicely nested directories. MemberName = nameof(DecompilerVersion))] class ILSpyCmdProgram { + // https://natemcmaster.github.io/CommandLineUtils/docs/advanced/generic-host.html + // https://github.com/natemcmaster/CommandLineUtils/blob/main/docs/samples/dependency-injection/generic-host/Program.cs public static Task Main(string[] args) => new HostBuilder().RunCommandLineApplicationAsync(args); [FilesExist] @@ -95,7 +97,7 @@ class ILSpyCmdProgram [DirectoryExists] [Option("-r|--referencepath ", "Path to a directory containing dependencies of the assembly that is being decompiled.", CommandOptionType.MultipleValue)] - public string[] ReferencePaths { get; } = new string[0]; + public string[] ReferencePaths { get; } [Option("--no-dead-code", "Remove dead code.", CommandOptionType.NoValue)] public bool RemoveDeadCode { get; } @@ -103,7 +105,7 @@ class ILSpyCmdProgram [Option("--no-dead-stores", "Remove dead stores.", CommandOptionType.NoValue)] public bool RemoveDeadStores { get; } - [Option("-d|--dump-package", "Dump package assembiles into a folder. This requires the output directory option.", CommandOptionType.NoValue)] + [Option("-d|--dump-package", "Dump package assemblies into a folder. This requires the output directory option.", CommandOptionType.NoValue)] public bool DumpPackageFlag { get; } [Option("--nested-directories", "Use nested directories for namespaces.", CommandOptionType.NoValue)] @@ -117,6 +119,8 @@ public ILSpyCmdProgram(IHostEnvironment env) private Task OnExecuteAsync(CommandLineApplication app) { + // await DotNetToolUpdateChecker.CheckForPackageUpdateAsync("ilspycmd"); + TextWriter output = System.Console.Out; string outputDirectory = ResolveOutputDirectory(OutputDirectory); @@ -249,7 +253,7 @@ CSharpDecompiler GetDecompiler(string assemblyFileName) { var module = new PEFile(assemblyFileName); var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId()); - foreach (var path in ReferencePaths) + foreach (var path in (ReferencePaths ?? Array.Empty())) { resolver.AddSearchDirectory(path); } @@ -287,7 +291,7 @@ ProjectId DecompileAsProject(string assemblyFileName, string projectFileName) { var module = new PEFile(assemblyFileName); var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId()); - foreach (var path in ReferencePaths) + foreach (var path in (ReferencePaths ?? Array.Empty())) { resolver.AddSearchDirectory(path); } From 35a3d97066756d4e58b3f8f0b7d706f7dc1f4b90 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 12 Jul 2023 15:29:19 +0200 Subject: [PATCH 4/5] Activate update check in finally of OnExecuteAsync --- .../DotNetToolUpdateChecker.cs | 7 ++++--- ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs b/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs index 2f467e8fc9..449cceb827 100644 --- a/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs +++ b/ICSharpCode.ILSpyCmd/DotNetToolUpdateChecker.cs @@ -20,7 +20,7 @@ static NuGetVersion CurrentPackageVersion() .InformationalVersion); } - public static async Task CheckForPackageUpdateAsync(string packageId) + public static async Task CheckForPackageUpdateAsync(string packageId) { try { @@ -37,8 +37,7 @@ public static async Task CheckForPackageUpdateAsync(string packageId) var latestVersion = versions.Where(v => v.Release == "").MaxBy(v => v); if (latestVersion > CurrentPackageVersion()) { - Console.WriteLine("You are not using the latest version of the tool, please update."); - Console.WriteLine($"Latest version is '{latestVersion}'"); + return latestVersion; } } #pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. @@ -46,6 +45,8 @@ public static async Task CheckForPackageUpdateAsync(string packageId) { } #pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. + + return null; } } } diff --git a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs index 17ff152b25..be2510e088 100644 --- a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs +++ b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs @@ -117,9 +117,9 @@ public ILSpyCmdProgram(IHostEnvironment env) _env = env; } - private Task OnExecuteAsync(CommandLineApplication app) + private async Task OnExecuteAsync(CommandLineApplication app) { - // await DotNetToolUpdateChecker.CheckForPackageUpdateAsync("ilspycmd"); + var updateCheckTask = DotNetToolUpdateChecker.CheckForPackageUpdateAsync("ilspycmd"); TextWriter output = System.Console.Out; string outputDirectory = ResolveOutputDirectory(OutputDirectory); @@ -137,7 +137,7 @@ private Task OnExecuteAsync(CommandLineApplication app) { string projectFileName = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(InputAssemblyNames[0]) + ".csproj"); DecompileAsProject(InputAssemblyNames[0], projectFileName); - return Task.FromResult(0); + return 0; } var projects = new List(); foreach (var file in InputAssemblyNames) @@ -148,7 +148,7 @@ private Task OnExecuteAsync(CommandLineApplication app) projects.Add(new ProjectItem(projectFileName, projectId.PlatformName, projectId.Guid, projectId.TypeGuid)); } SolutionCreator.WriteSolutionFile(Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(outputDirectory) + ".sln"), projects); - return Task.FromResult(0); + return 0; } else { @@ -156,19 +156,26 @@ private Task OnExecuteAsync(CommandLineApplication app) { int result = PerformPerFileAction(file); if (result != 0) - return Task.FromResult(result); + return result; } - return Task.FromResult(0); + return 0; } } catch (Exception ex) { app.Error.WriteLine(ex.ToString()); - return Task.FromResult(ProgramExitCodes.EX_SOFTWARE); + return ProgramExitCodes.EX_SOFTWARE; } finally { output.Close(); + + var latestVersion = await updateCheckTask; + if (null != latestVersion) + { + Console.WriteLine("You are not using the latest version of the tool, please update."); + Console.WriteLine($"Latest version is '{latestVersion}'"); + } } int PerformPerFileAction(string fileName) From ca60ee84e42e0968c7f83759f22d00936f729762 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Thu, 13 Jul 2023 08:06:19 +0200 Subject: [PATCH 5/5] Add option to disable the update check (might not be desired in automation scenarios) --- ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs index be2510e088..8413fdfc4d 100644 --- a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs +++ b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs @@ -24,6 +24,8 @@ using Microsoft.Extensions.Hosting; +using NuGet.Versioning; + namespace ICSharpCode.ILSpyCmd { [Command(Name = "ilspycmd", Description = "dotnet tool for decompiling .NET assemblies and generating portable PDBs", @@ -111,6 +113,9 @@ class ILSpyCmdProgram [Option("--nested-directories", "Use nested directories for namespaces.", CommandOptionType.NoValue)] public bool NestedDirectories { get; } + [Option("--disable-updatecheck", "If using ilspycmd in a tight loop or fully automated scenario, you might want to disable the automatic update check.", CommandOptionType.NoValue)] + public bool DisableUpdateCheck { get; } + private readonly IHostEnvironment _env; public ILSpyCmdProgram(IHostEnvironment env) { @@ -119,7 +124,11 @@ public ILSpyCmdProgram(IHostEnvironment env) private async Task OnExecuteAsync(CommandLineApplication app) { - var updateCheckTask = DotNetToolUpdateChecker.CheckForPackageUpdateAsync("ilspycmd"); + Task updateCheckTask = null; + if (!DisableUpdateCheck) + { + updateCheckTask = DotNetToolUpdateChecker.CheckForPackageUpdateAsync("ilspycmd"); + } TextWriter output = System.Console.Out; string outputDirectory = ResolveOutputDirectory(OutputDirectory); @@ -170,11 +179,14 @@ private async Task OnExecuteAsync(CommandLineApplication app) { output.Close(); - var latestVersion = await updateCheckTask; - if (null != latestVersion) + if (null != updateCheckTask) { - Console.WriteLine("You are not using the latest version of the tool, please update."); - Console.WriteLine($"Latest version is '{latestVersion}'"); + var latestVersion = await updateCheckTask; + if (null != latestVersion) + { + Console.WriteLine("You are not using the latest version of the tool, please update."); + Console.WriteLine($"Latest version is '{latestVersion}'"); + } } }