Browse Source

- Introduce ProjectItemInfo instead of tuples in WholeProjectDecompiler project item generation.

- Set 'LogicalName' attribute for all decompiled resources. This makes it possible to correctly recompile projects with resource names that are not valid filenames.
- Set Generator and SubType properties for XAML files.
pull/2857/head
Siegfried Pammer 3 years ago
parent
commit
ff0e929866
  1. 5
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectFileWriter.cs
  2. 14
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs
  3. 31
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs
  4. 53
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  5. 2
      ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs
  6. 4
      ILSpy/Languages/CSharpLanguage.cs
  7. 3
      ILSpy/Languages/IResourceFileHandler.cs

5
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectFileWriter.cs

@ -34,9 +34,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -34,9 +34,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary>
/// <param name="target">The target to write to.</param>
/// <param name="project">The information about the project being created.</param>
/// <param name="files">A collection of source files to be included into the project, each item is a pair
/// of the project entry type and the file path.</param>
/// <param name="files">A collection of source files to be included into the project.</param>
/// <param name="module">The module being decompiled.</param>
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<(string itemType, string fileName)> files, PEFile module);
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module);
}
}

14
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs

@ -25,6 +25,7 @@ using System.Xml; @@ -25,6 +25,7 @@ using System.Xml;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
@ -43,7 +44,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -43,7 +44,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public void Write(
TextWriter target,
IProjectInfoProvider project,
IEnumerable<(string itemType, string fileName)> files,
IEnumerable<ProjectItemInfo> files,
PEFile module)
{
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
@ -162,13 +163,18 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -162,13 +163,18 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
}
w.WriteEndElement(); // </ItemGroup> (References)
foreach (IGrouping<string, string> gr in from f in files group f.fileName by f.itemType into g orderby g.Key select g)
foreach (IGrouping<string, ProjectItemInfo> gr in files.GroupBy(f => f.ItemType).OrderBy(g => g.Key))
{
w.WriteStartElement("ItemGroup");
foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase))
foreach (var item in gr.OrderBy(f => f.FileName, StringComparer.OrdinalIgnoreCase))
{
w.WriteStartElement(gr.Key);
w.WriteAttributeString("Include", file);
w.WriteAttributeString("Include", item.FileName);
if (item.AdditionalProperties != null)
{
foreach (var (key, value) in item.AdditionalProperties)
w.WriteAttributeString(key, value);
}
w.WriteEndElement();
}
w.WriteEndElement();

31
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs

@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public void Write(
TextWriter target,
IProjectInfoProvider project,
IEnumerable<(string itemType, string fileName)> files,
IEnumerable<ProjectItemInfo> files,
PEFile module)
{
using (XmlTextWriter xmlWriter = new XmlTextWriter(target))
@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
}
}
static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<(string itemType, string fileName)> files, PEFile module)
static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module)
{
xml.WriteStartElement("Project");
@ -188,27 +188,27 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -188,27 +188,27 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
}
}
static void WriteMiscellaneousPropertyGroup(XmlTextWriter xml, IEnumerable<(string itemType, string fileName)> files)
static void WriteMiscellaneousPropertyGroup(XmlTextWriter xml, IEnumerable<ProjectItemInfo> files)
{
var (itemType, fileName) = files.FirstOrDefault(t => t.itemType == "ApplicationIcon");
var (itemType, fileName) = files.FirstOrDefault(t => t.ItemType == "ApplicationIcon");
if (fileName != null)
xml.WriteElementString("ApplicationIcon", fileName);
(itemType, fileName) = files.FirstOrDefault(t => t.itemType == "ApplicationManifest");
(itemType, fileName) = files.FirstOrDefault(t => t.ItemType == "ApplicationManifest");
if (fileName != null)
xml.WriteElementString("ApplicationManifest", fileName);
if (files.Any(t => t.itemType == "EmbeddedResource"))
if (files.Any(t => t.ItemType == "EmbeddedResource"))
xml.WriteElementString("RootNamespace", string.Empty);
// TODO: We should add CustomToolNamespace for resources, otherwise we should add empty RootNamespace
}
static void WriteResources(XmlTextWriter xml, IEnumerable<(string itemType, string fileName)> files)
static void WriteResources(XmlTextWriter xml, IEnumerable<ProjectItemInfo> files)
{
// remove phase
foreach (var (itemType, fileName) in files.Where(t => t.itemType == "EmbeddedResource"))
foreach (var item in files.Where(t => t.ItemType == "EmbeddedResource"))
{
string buildAction = Path.GetExtension(fileName).ToUpperInvariant() switch {
string buildAction = Path.GetExtension(item.FileName).ToUpperInvariant() switch {
".CS" => "Compile",
".RESX" => "EmbeddedResource",
_ => "None"
@ -217,18 +217,23 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -217,18 +217,23 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
continue;
xml.WriteStartElement(buildAction);
xml.WriteAttributeString("Remove", fileName);
xml.WriteAttributeString("Remove", item.FileName);
xml.WriteEndElement();
}
// include phase
foreach (var (itemType, fileName) in files.Where(t => t.itemType == "EmbeddedResource"))
foreach (var item in files.Where(t => t.ItemType == "EmbeddedResource"))
{
if (Path.GetExtension(fileName) == ".resx")
if (Path.GetExtension(item.FileName) == ".resx")
continue;
xml.WriteStartElement("EmbeddedResource");
xml.WriteAttributeString("Include", fileName);
xml.WriteAttributeString("Include", item.FileName);
if (item.AdditionalProperties != null)
{
foreach (var (key, value) in item.AdditionalProperties)
xml.WriteAttributeString(key, value);
}
xml.WriteEndElement();
}
}

53
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
TargetDirectory = targetDirectory;
directories.Clear();
var resources = WriteResourceFilesInProject(moduleDefinition).ToList();
var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.partialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList();
files.AddRange(resources.Select(r => (r.itemType, r.fileName)));
var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.PartialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList();
files.AddRange(resources);
files.AddRange(WriteMiscellaneousFilesInProject(moduleDefinition));
if (StrongNameKeyFile != null)
{
@ -190,7 +190,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -190,7 +190,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
return decompiler;
}
IEnumerable<(string itemType, string fileName)> WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken)
IEnumerable<ProjectItemInfo> WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken)
{
var decompiler = CreateDecompiler(ts);
decompiler.CancellationToken = cancellationToken;
@ -205,10 +205,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -205,10 +205,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
}
return new[] { ("Compile", assemblyInfo) };
return new[] { new ProjectItemInfo("Compile", assemblyInfo) };
}
IEnumerable<(string itemType, string fileName)> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken)
IEnumerable<ProjectItemInfo> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken)
{
var metadata = module.Metadata;
var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td))
@ -229,7 +229,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -229,7 +229,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
progress.TotalUnits = files.Count;
}
return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken));
return files.Select(f => new ProjectItemInfo("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken));
string GetFileFileNameForHandle(TypeDefinitionHandle h)
{
@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
#endregion
#region WriteResourceFilesInProject
protected virtual IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceFilesInProject(Metadata.PEFile module)
protected virtual IEnumerable<ProjectItemInfo> WriteResourceFilesInProject(Metadata.PEFile module)
{
foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded))
{
@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
{
bool decodedIntoIndividualFiles;
var individualResources = new List<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)>();
var individualResources = new List<ProjectItemInfo>();
try
{
var resourcesFile = new ResourcesFile(stream);
@ -378,12 +378,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -378,12 +378,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
stream.Position = 0;
stream.CopyTo(fs);
}
yield return ("EmbeddedResource", fileName, null);
yield return new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", r.Name);
}
}
}
protected virtual IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
protected virtual IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
{
if (fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
{
@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
writer.AddResource(entry.Key, entry.Value);
}
}
return new[] { ("EmbeddedResource", resx, (List<PartialTypeInfo>)null) };
return new[] { new ProjectItemInfo("EmbeddedResource", resx).With("LogicalName", resourceName) };
}
catch (BadImageFormatException)
{
@ -413,7 +413,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -413,7 +413,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
entryStream.CopyTo(fs);
}
return new[] { ("EmbeddedResource", fileName, (List<PartialTypeInfo>)null) };
return new[] { new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", resourceName) };
}
string GetFileNameForResource(string fullName)
@ -444,7 +444,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -444,7 +444,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
#endregion
#region WriteMiscellaneousFilesInProject
protected virtual IEnumerable<(string itemType, string fileName)> WriteMiscellaneousFilesInProject(PEFile module)
protected virtual IEnumerable<ProjectItemInfo> WriteMiscellaneousFilesInProject(PEFile module)
{
var resources = module.Reader.ReadWin32Resources();
if (resources == null)
@ -454,21 +454,21 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -454,21 +454,21 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (appIcon != null)
{
File.WriteAllBytes(Path.Combine(TargetDirectory, "app.ico"), appIcon);
yield return ("ApplicationIcon", "app.ico");
yield return new ProjectItemInfo("ApplicationIcon", "app.ico");
}
byte[] appManifest = CreateApplicationManifest(resources);
if (appManifest != null && !IsDefaultApplicationManifest(appManifest))
{
File.WriteAllBytes(Path.Combine(TargetDirectory, "app.manifest"), appManifest);
yield return ("ApplicationManifest", "app.manifest");
yield return new ProjectItemInfo("ApplicationManifest", "app.manifest");
}
var appConfig = module.FileName + ".config";
if (File.Exists(appConfig))
{
File.Copy(appConfig, Path.Combine(TargetDirectory, "app.config"), overwrite: true);
yield return ("ApplicationConfig", Path.GetFileName(appConfig));
yield return new ProjectItemInfo("ApplicationConfig", Path.GetFileName(appConfig));
}
}
@ -753,4 +753,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -753,4 +753,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
return TargetServices.DetectTargetFramework(module).Moniker != null;
}
}
public record struct ProjectItemInfo(string ItemType, string FileName)
{
public List<PartialTypeInfo> PartialTypes { get; set; } = null;
public Dictionary<string, string> AdditionalProperties { get; set; } = null;
public ProjectItemInfo With(string name, string value)
{
AdditionalProperties ??= new Dictionary<string, string>();
AdditionalProperties.Add(name, value);
return this;
}
public ProjectItemInfo With(IEnumerable<KeyValuePair<string, string>> pairs)
{
AdditionalProperties ??= new Dictionary<string, string>();
AdditionalProperties.AddRange(pairs);
return this;
}
}
}

2
ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs

@ -72,6 +72,8 @@ namespace ILSpy.BamlDecompiler @@ -72,6 +72,8 @@ namespace ILSpy.BamlDecompiler
{
fileName = Path.ChangeExtension(fileName, ".xaml");
}
context.AdditionalProperties.Add("Generator", "MSBuild:Compile");
context.AdditionalProperties.Add("SubType", "Designer");
string saveFileName = Path.Combine(context.DecompilationOptions.SaveAsProjectDirectory, fileName);
Directory.CreateDirectory(Path.GetDirectoryName(saveFileName));
result.Xaml.Save(saveFileName);

4
ILSpy/Languages/CSharpLanguage.cs

@ -517,7 +517,7 @@ namespace ICSharpCode.ILSpy @@ -517,7 +517,7 @@ namespace ICSharpCode.ILSpy
this.options = options;
}
protected override IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
protected override IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
{
var context = new ResourceFileHandlerContext(options);
foreach (var handler in App.ExportProvider.GetExportedValues<IResourceFileHandler>())
@ -527,7 +527,7 @@ namespace ICSharpCode.ILSpy @@ -527,7 +527,7 @@ namespace ICSharpCode.ILSpy
entryStream.Position = 0;
fileName = handler.WriteResourceToFile(assembly, fileName, entryStream, context);
return new[] { (handler.EntryType, fileName, context.PartialTypes) };
return new[] { new ProjectItemInfo(handler.EntryType, fileName) { PartialTypes = context.PartialTypes }.With(context.AdditionalProperties) };
}
}
return base.WriteResourceToFile(fileName, resourceName, entryStream);

3
ILSpy/Languages/IResourceFileHandler.cs

@ -36,6 +36,9 @@ namespace ICSharpCode.ILSpy @@ -36,6 +36,9 @@ namespace ICSharpCode.ILSpy
readonly List<PartialTypeInfo> partialTypes = new();
internal List<PartialTypeInfo> PartialTypes => partialTypes;
readonly Dictionary<string, string> additionalProperties = new();
public Dictionary<string, string> AdditionalProperties => additionalProperties;
public DecompilationOptions DecompilationOptions { get; }
public ResourceFileHandlerContext(DecompilationOptions options)

Loading…
Cancel
Save