From a24e0185fc46209dd308fb06d2fe40d3f00f61f2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 19 Apr 2023 18:09:18 +0600 Subject: [PATCH] Generate fake ref assemblies with patched *Impl and [NotClientImplementable] interfaces --- nukebuild/Build.cs | 2 + nukebuild/Helpers.cs | 24 ++++++++ nukebuild/RefAssemblyGenerator.cs | 99 +++++++++++++++++++++++++++++++ nukebuild/_build.csproj | 14 ++--- 4 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 nukebuild/Helpers.cs create mode 100644 nukebuild/RefAssemblyGenerator.cs diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 40232947d9..630c532686 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -273,6 +273,8 @@ partial class Build : NukeBuild if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config, new NumergeNukeLogger())) throw new Exception("Package merge failed"); + RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." + + Parameters.Version + ".nupkg"); }); Target RunTests => _ => _ diff --git a/nukebuild/Helpers.cs b/nukebuild/Helpers.cs new file mode 100644 index 0000000000..d8d06559bf --- /dev/null +++ b/nukebuild/Helpers.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; +using Nuke.Common.Utilities; + +class Helpers +{ + public static IDisposable UseTempDir(out string dir) + { + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(path); + dir = path; + return DelegateDisposable.CreateBracket(null, () => + { + try + { + Directory.Delete(path, true); + } + catch + { + // ignore + } + }); + } +} diff --git a/nukebuild/RefAssemblyGenerator.cs b/nukebuild/RefAssemblyGenerator.cs new file mode 100644 index 0000000000..912f74cdf9 --- /dev/null +++ b/nukebuild/RefAssemblyGenerator.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; + +public class RefAssemblyGenerator +{ + static void PatchRefAssembly(string file) + { + + var reader = typeof(RefAssemblyGenerator).Assembly.GetManifestResourceStream("avalonia.snk"); + var snk = new byte[reader.Length]; + reader.Read(snk, 0, snk.Length); + + var def = AssemblyDef.Load(new MemoryStream(File.ReadAllBytes(file))); + + foreach(var t in def.ManifestModule.Types) + ProcessType(t); + def.Write(file, new ModuleWriterOptions(def.ManifestModule) + { + StrongNameKey = new StrongNameKey(snk), + }); + } + + static void ProcessType(TypeDef type) + { + foreach (var nested in type.NestedTypes) + ProcessType(nested); + if (type.IsInterface) + { + var hideMethods = type.Name.EndsWith("Impl"); + var injectMethod = hideMethods + || type.CustomAttributes.Any(a => + a.AttributeType.FullName.EndsWith("NotClientImplementableAttribute")); + + if (hideMethods) + { + foreach (var m in type.Methods) + { + m.Attributes |= MethodAttributes.Public | MethodAttributes.Assembly; + m.Attributes ^= MethodAttributes.Public; + } + } + + if(injectMethod) + { + type.Methods.Add(new MethodDefUser("NotClientImplementable", + new MethodSig(CallingConvention.Default, 0, type.Module.CorLibTypes.Void), + MethodAttributes.Assembly + | MethodAttributes.Abstract + | MethodAttributes.NewSlot + | MethodAttributes.HideBySig)); + } + } + } + + public static void GenerateRefAsmsInPackage(string packagePath) + { + using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite), + ZipArchiveMode.Update)) + { + foreach (var entry in archive.Entries.ToList()) + { + if (entry.FullName.StartsWith("ref/")) + entry.Delete(); + } + + foreach (var entry in archive.Entries.ToList()) + { + if (entry.FullName.StartsWith("lib/")) + { + if (entry.Name.EndsWith(".dll")) + { + using (Helpers.UseTempDir(out var temp)) + { + var file = Path.Combine(temp, entry.Name); + entry.ExtractToFile(file); + PatchRefAssembly(file); + archive.CreateEntryFromFile(file, "ref/" + entry.FullName.Substring(4)); + + } + } + else if (entry.Name.EndsWith(".xml")) + { + var newEntry = archive.CreateEntry("ref/" + entry.FullName.Substring(4), + CompressionLevel.Optimal); + using (var src = entry.Open()) + using (var dst = newEntry.Open()) + src.CopyTo(dst); + } + } + } + } + } +} \ No newline at end of file diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 13bac4b7db..cc3ce9f0b0 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -18,6 +18,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -31,18 +32,11 @@ - - - - - - - + + - - - +