Browse Source

Merge pull request #3449 from ds5678/fix-nested-namespace-directories

Fix nested namespace directories
net10upgrade
Siegfried Pammer 1 month ago committed by GitHub
parent
commit
37c56e7e7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 2
      ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs
  3. 101
      ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs
  4. 41
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  5. 2
      ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -131,6 +131,7 @@
<Compile Include="Helpers\TestsAssemblyOutput.cs" /> <Compile Include="Helpers\TestsAssemblyOutput.cs" />
<Compile Include="Output\InsertParenthesesVisitorTests.cs" /> <Compile Include="Output\InsertParenthesesVisitorTests.cs" />
<Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" /> <Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" />
<Compile Include="ProjectDecompiler\WholeProjectDecompilerTests.cs" />
<Compile Include="TestAssemblyResolver.cs" /> <Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\ILPretty\Issue3421.cs" /> <Compile Include="TestCases\ILPretty\Issue3421.cs" />
<Compile Include="TestCases\ILPretty\Issue3442.cs" /> <Compile Include="TestCases\ILPretty\Issue3442.cs" />

2
ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs

@ -23,7 +23,7 @@ using ICSharpCode.Decompiler.Metadata;
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests namespace ICSharpCode.Decompiler.Tests.ProjectDecompiler
{ {
[TestFixture] [TestFixture]
public sealed class TargetFrameworkTests public sealed class TargetFrameworkTests

101
ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs

@ -0,0 +1,101 @@
// Copyright (c) 2025 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.ProjectDecompiler;
[TestFixture]
public sealed class WholeProjectDecompilerTests
{
[Test]
public void UseNestedDirectoriesForNamespacesTrueWorks()
{
string targetDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetRandomFileName());
TestFriendlyProjectDecompiler decompiler = new(new UniversalAssemblyResolver(null, false, null));
decompiler.Settings.UseNestedDirectoriesForNamespaces = true;
decompiler.DecompileProject(new PEFile("ICSharpCode.Decompiler.dll"), targetDirectory);
AssertDirectoryDoesntExist(targetDirectory);
string projectDecompilerDirectory = Path.Combine(targetDirectory, "ICSharpCode", "Decompiler", "CSharp", "ProjectDecompiler");
string projectDecompilerFile = Path.Combine(projectDecompilerDirectory, $"{nameof(WholeProjectDecompiler)}.cs");
using (Assert.EnterMultipleScope())
{
Assert.That(decompiler.Files.ContainsKey(projectDecompilerFile), Is.True);
Assert.That(decompiler.Directories.Contains(projectDecompilerDirectory), Is.True);
}
}
[Test]
public void UseNestedDirectoriesForNamespacesFalseWorks()
{
string targetDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetRandomFileName());
TestFriendlyProjectDecompiler decompiler = new(new UniversalAssemblyResolver(null, false, null));
decompiler.Settings.UseNestedDirectoriesForNamespaces = false;
decompiler.DecompileProject(new PEFile("ICSharpCode.Decompiler.dll"), targetDirectory);
AssertDirectoryDoesntExist(targetDirectory);
string projectDecompilerDirectory = Path.Combine(targetDirectory, "ICSharpCode.Decompiler.CSharp.ProjectDecompiler");
string projectDecompilerFile = Path.Combine(projectDecompilerDirectory, $"{nameof(WholeProjectDecompiler)}.cs");
using (Assert.EnterMultipleScope())
{
Assert.That(decompiler.Files.ContainsKey(projectDecompilerFile), Is.True);
Assert.That(decompiler.Directories.Contains(projectDecompilerDirectory), Is.True);
}
}
static void AssertDirectoryDoesntExist(string directory)
{
if (Directory.Exists(directory))
{
Directory.Delete(directory, recursive: true);
Assert.Fail("Directory should not have been created.");
}
}
sealed class TestFriendlyProjectDecompiler(IAssemblyResolver assemblyResolver) : WholeProjectDecompiler(assemblyResolver)
{
public Dictionary<string, StringWriter> Files { get; } = [];
public HashSet<string> Directories { get; } = [];
protected override TextWriter CreateFile(string path)
{
StringWriter writer = new();
Files[path] = writer;
return writer;
}
protected override void CreateDirectory(string path)
{
Directories.Add(path);
}
protected override IEnumerable<ProjectItemInfo> WriteMiscellaneousFilesInProject(PEFile module) => [];
protected override IEnumerable<ProjectItemInfo> WriteResourceFilesInProject(MetadataFile module) => [];
}
}

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

@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public void DecompileProject(MetadataFile file, string targetDirectory, CancellationToken cancellationToken = default(CancellationToken)) public void DecompileProject(MetadataFile file, string targetDirectory, CancellationToken cancellationToken = default(CancellationToken))
{ {
string projectFileName = Path.Combine(targetDirectory, CleanUpFileName(file.Name, ".csproj")); string projectFileName = Path.Combine(targetDirectory, CleanUpFileName(file.Name, ".csproj"));
using (var writer = new StreamWriter(projectFileName)) using (var writer = CreateFile(projectFileName))
{ {
DecompileProject(file, targetDirectory, writer, cancellationToken); DecompileProject(file, targetDirectory, writer, cancellationToken);
} }
@ -186,6 +186,24 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
return true; return true;
} }
protected virtual TextWriter CreateFile(string path)
{
return new StreamWriter(path);
}
protected virtual void CreateDirectory(string path)
{
try
{
Directory.CreateDirectory(path);
}
catch (IOException)
{
File.Delete(path);
Directory.CreateDirectory(path);
}
}
CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts)
{ {
var decompiler = new CSharpDecompiler(ts, Settings); var decompiler = new CSharpDecompiler(ts, Settings);
@ -204,9 +222,9 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
const string prop = "Properties"; const string prop = "Properties";
if (directories.Add(prop)) if (directories.Add(prop))
Directory.CreateDirectory(Path.Combine(TargetDirectory, prop)); CreateDirectory(Path.Combine(TargetDirectory, prop));
string assemblyInfo = Path.Combine(prop, "AssemblyInfo.cs"); string assemblyInfo = Path.Combine(prop, "AssemblyInfo.cs");
using (StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, assemblyInfo))) using (var w = CreateFile(Path.Combine(TargetDirectory, assemblyInfo)))
{ {
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
} }
@ -251,15 +269,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (directories.Add(dir)) if (directories.Add(dir))
{ {
var path = Path.Combine(TargetDirectory, dir); var path = Path.Combine(TargetDirectory, dir);
try CreateDirectory(path);
{
Directory.CreateDirectory(path);
}
catch (IOException)
{
File.Delete(path);
Directory.CreateDirectory(path);
}
} }
return Path.Combine(dir, file); return Path.Combine(dir, file);
} }
@ -277,7 +287,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
delegate (IGrouping<string, TypeDefinitionHandle> file) { delegate (IGrouping<string, TypeDefinitionHandle> file) {
try try
{ {
using StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key)); using var w = CreateFile(Path.Combine(TargetDirectory, file.Key));
CSharpDecompiler decompiler = CreateDecompiler(ts); CSharpDecompiler decompiler = CreateDecompiler(ts);
foreach (var partialType in partialTypes) foreach (var partialType in partialTypes)
@ -344,7 +354,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
string dirName = Path.GetDirectoryName(fileName); string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName))
{ {
Directory.CreateDirectory(Path.Combine(TargetDirectory, dirName)); CreateDirectory(Path.Combine(TargetDirectory, dirName));
} }
Stream entryStream = (Stream)value; Stream entryStream = (Stream)value;
entryStream.Position = 0; entryStream.Position = 0;
@ -743,7 +753,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public static string CleanUpPath(string text) public static string CleanUpPath(string text)
{ {
return CleanUpName(text, separateAtDots: true, treatAsFileName: true, treatAsPath: true); return CleanUpName(text, separateAtDots: true, treatAsFileName: false, treatAsPath: true)
.Replace('.', Path.DirectorySeparatorChar);
} }
static bool IsReservedFileSystemName(string name) static bool IsReservedFileSystemName(string name)

2
ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs

@ -62,7 +62,7 @@ namespace ILSpy.BamlDecompiler
var typeDefinition = result.TypeName.HasValue ? typeSystem.MainModule.GetTypeDefinition(result.TypeName.Value.TopLevelTypeName) : null; var typeDefinition = result.TypeName.HasValue ? typeSystem.MainModule.GetTypeDefinition(result.TypeName.Value.TopLevelTypeName) : null;
if (typeDefinition != null) if (typeDefinition != null)
{ {
fileName = WholeProjectDecompiler.CleanUpPath(typeDefinition.ReflectionName + ".xaml"); fileName = WholeProjectDecompiler.SanitizeFileName(typeDefinition.ReflectionName + ".xaml");
var partialTypeInfo = new PartialTypeInfo(typeDefinition); var partialTypeInfo = new PartialTypeInfo(typeDefinition);
foreach (var member in result.GeneratedMembers) foreach (var member in result.GeneratedMembers)
{ {

Loading…
Cancel
Save