Skip to content

Commit

Permalink
Fix #2363: CLI support for generating a solution from multiple projec…
Browse files Browse the repository at this point in the history
…ts (based on code provided by @marwie in #2364)
  • Loading branch information
siegfriedpammer committed Apr 15, 2022
1 parent f72e0a8 commit 05eb2cd
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 34 deletions.
91 changes: 62 additions & 29 deletions ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX.PdbProvider;

using McMaster.Extensions.CommandLineUtils;
// ReSharper disable All

namespace ICSharpCode.ILSpyCmd
{
Expand All @@ -36,12 +36,11 @@ class ILSpyCmdProgram
{
public static int Main(string[] args) => CommandLineApplication.Execute<ILSpyCmdProgram>(args);

[FileExists]
[FilesExist]
[Required]
[Argument(0, "Assembly file name", "The assembly that is being decompiled. This argument is mandatory.")]
public string InputAssemblyName { get; }
[Argument(0, "Assembly file name(s)", "The list of assemblies that is being decompiled. This argument is mandatory.")]
public string[] InputAssemblyNames { get; }

[DirectoryExists]
[Option("-o|--outputdir <directory>", "The output directory, if omitted decompiler output is written to standard out.", CommandOptionType.SingleValue)]
public string OutputDirectory { get; }

Expand Down Expand Up @@ -98,74 +97,108 @@ private int OnExecute(CommandLineApplication app)
TextWriter output = System.Console.Out;
bool outputDirectorySpecified = !string.IsNullOrEmpty(OutputDirectory);

if (outputDirectorySpecified)
{
Directory.CreateDirectory(OutputDirectory);
}

try
{
if (CreateCompilableProjectFlag)
{
return DecompileAsProject(InputAssemblyName, OutputDirectory);
if (InputAssemblyNames.Length == 1)
{
string projectFileName = Path.Combine(Environment.CurrentDirectory, OutputDirectory, Path.GetFileNameWithoutExtension(InputAssemblyNames[0]) + ".csproj");
DecompileAsProject(InputAssemblyNames[0], projectFileName);
return 0;
}
var projects = new List<ProjectItem>();
foreach (var file in InputAssemblyNames)
{
string projectFileName = Path.Combine(Environment.CurrentDirectory, OutputDirectory, Path.GetFileNameWithoutExtension(file), Path.GetFileNameWithoutExtension(file) + ".csproj");
Directory.CreateDirectory(Path.GetDirectoryName(projectFileName));
ProjectId projectId = DecompileAsProject(file, projectFileName);
projects.Add(new ProjectItem(projectFileName, projectId.PlatformName, projectId.Guid, projectId.TypeGuid));
}
SolutionCreator.WriteSolutionFile(Path.Combine(Environment.CurrentDirectory, OutputDirectory, Path.GetFileNameWithoutExtension(OutputDirectory) + ".sln"), projects);
return 0;
}
else if (EntityTypes.Any())
else
{
foreach (var file in InputAssemblyNames)
{
int result = PerformPerFileAction(file);
if (result != 0)
return result;
}
return 0;
}
}
catch (Exception ex)
{
app.Error.WriteLine(ex.ToString());
return ProgramExitCodes.EX_SOFTWARE;
}
finally
{
output.Close();
}

int PerformPerFileAction(string fileName)
{
if (EntityTypes.Any())
{
var values = EntityTypes.SelectMany(v => v.Split(',', ';')).ToArray();
HashSet<TypeKind> kinds = TypesParser.ParseSelection(values);
if (outputDirectorySpecified)
{
string outputName = Path.GetFileNameWithoutExtension(InputAssemblyName);
string outputName = Path.GetFileNameWithoutExtension(fileName);
output = File.CreateText(Path.Combine(OutputDirectory, outputName) + ".list.txt");
}

return ListContent(InputAssemblyName, output, kinds);
return ListContent(fileName, output, kinds);
}
else if (ShowILCodeFlag || ShowILSequencePointsFlag)
{
if (outputDirectorySpecified)
{
string outputName = Path.GetFileNameWithoutExtension(InputAssemblyName);
string outputName = Path.GetFileNameWithoutExtension(fileName);
output = File.CreateText(Path.Combine(OutputDirectory, outputName) + ".il");
}

return ShowIL(InputAssemblyName, output);
return ShowIL(fileName, output);
}
else if (CreateDebugInfoFlag)
{
string pdbFileName = null;
if (outputDirectorySpecified)
{
string outputName = Path.GetFileNameWithoutExtension(InputAssemblyName);
string outputName = Path.GetFileNameWithoutExtension(fileName);
pdbFileName = Path.Combine(OutputDirectory, outputName) + ".pdb";
}
else
{
pdbFileName = Path.ChangeExtension(InputAssemblyName, ".pdb");
pdbFileName = Path.ChangeExtension(fileName, ".pdb");
}

return GeneratePdbForAssembly(InputAssemblyName, pdbFileName, app);
return GeneratePdbForAssembly(fileName, pdbFileName, app);
}
else if (DumpPackageFlag)
{
return DumpPackageAssemblies(InputAssemblyName, OutputDirectory, app);
return DumpPackageAssemblies(fileName, OutputDirectory, app);
}
else
{
if (outputDirectorySpecified)
{
string outputName = Path.GetFileNameWithoutExtension(InputAssemblyName);
string outputName = Path.GetFileNameWithoutExtension(fileName);
output = File.CreateText(Path.Combine(OutputDirectory,
(string.IsNullOrEmpty(TypeName) ? outputName : TypeName) + ".decompiled.cs"));
}

return Decompile(InputAssemblyName, output, TypeName);
return Decompile(fileName, output, TypeName);
}
}
catch (Exception ex)
{
app.Error.WriteLine(ex.ToString());
return ProgramExitCodes.EX_SOFTWARE;
}
finally
{
output.Close();
}
}

DecompilerSettings GetSettings(PEFile module)
Expand Down Expand Up @@ -217,7 +250,7 @@ int ShowIL(string assemblyFileName, TextWriter output)
return 0;
}

int DecompileAsProject(string assemblyFileName, string outputDirectory)
ProjectId DecompileAsProject(string assemblyFileName, string projectFileName)
{
var module = new PEFile(assemblyFileName);
var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId());
Expand All @@ -226,8 +259,8 @@ int DecompileAsProject(string assemblyFileName, string outputDirectory)
resolver.AddSearchDirectory(path);
}
var decompiler = new WholeProjectDecompiler(GetSettings(module), resolver, resolver, TryLoadPDB(module));
decompiler.DecompileProject(module, outputDirectory);
return 0;
using (var projectFileWriter = new StreamWriter(File.OpenWrite(projectFileName)))
return decompiler.DecompileProject(module, Path.GetDirectoryName(projectFileName), projectFileWriter);
}

int Decompile(string assemblyFileName, TextWriter output, string typeName = null)
Expand Down
67 changes: 62 additions & 5 deletions ICSharpCode.ILSpyCmd/ValidationAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using System.ComponentModel.DataAnnotations;
using System.IO;

using McMaster.Extensions.CommandLineUtils.Abstractions;
using McMaster.Extensions.CommandLineUtils.Validation;

namespace ICSharpCode.ILSpyCmd
{
[AttributeUsage(AttributeTargets.Class)]
Expand All @@ -27,14 +30,68 @@ protected override ValidationResult IsValid(object value, ValidationContext cont
[AttributeUsage(AttributeTargets.Property)]
public sealed class FileExistsOrNullAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var s = value as string;
if (string.IsNullOrEmpty(s))
var path = value as string;
if (string.IsNullOrEmpty(path))
{
return ValidationResult.Success;
if (File.Exists(s))
}

if (!Path.IsPathRooted(path) && validationContext.GetService(typeof(CommandLineContext)) is CommandLineContext context)
{
path = Path.Combine(context.WorkingDirectory, path);
}

if (File.Exists(path))
{
return ValidationResult.Success;
return new ValidationResult($"File '{s}' does not exist!");
}

return new ValidationResult($"File '{path}' does not exist!");
}
}

[AttributeUsage(AttributeTargets.Property)]
public sealed class FilesExistAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
switch (value)
{
case string path:
return ValidatePath(path);
case string[] paths:
{
foreach (string path in paths)
{
ValidationResult result = ValidatePath(path);
if (result != ValidationResult.Success)
return result;
}
return ValidationResult.Success;
}
default:
return new ValidationResult($"File '{value}' does not exist!");
}

ValidationResult ValidatePath(string path)
{
if (!string.IsNullOrWhiteSpace(path))
{
if (!Path.IsPathRooted(path) && validationContext.GetService(typeof(CommandLineContext)) is CommandLineContext context)
{
path = Path.Combine(context.WorkingDirectory, path);
}

if (File.Exists(path))
{
return ValidationResult.Success;
}
}

return new ValidationResult($"File '{path}' does not exist!");
}
}
}
}

0 comments on commit 05eb2cd

Please sign in to comment.