Browse Source

Fix #2363: CLI support for generating a solution from multiple projects (based on code provided by @marwie in #2364)

pull/2679/head
Siegfried Pammer 3 years ago
parent
commit
05eb2cdf04
  1. 91
      ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs
  2. 67
      ICSharpCode.ILSpyCmd/ValidationAttributes.cs

91
ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs

@ -15,11 +15,11 @@ using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; @@ -15,11 +15,11 @@ using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
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
{
@ -36,12 +36,11 @@ Remarks: @@ -36,12 +36,11 @@ Remarks:
{
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; }
@ -98,74 +97,108 @@ Remarks: @@ -98,74 +97,108 @@ Remarks:
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)
@ -217,7 +250,7 @@ Remarks: @@ -217,7 +250,7 @@ Remarks:
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());
@ -226,8 +259,8 @@ Remarks: @@ -226,8 +259,8 @@ Remarks:
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)

67
ICSharpCode.ILSpyCmd/ValidationAttributes.cs

@ -2,6 +2,9 @@ @@ -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)]
@ -27,14 +30,68 @@ namespace ICSharpCode.ILSpyCmd @@ -27,14 +30,68 @@ namespace ICSharpCode.ILSpyCmd
[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!");
}
}
}
}

Loading…
Cancel
Save