diff --git a/nukebuild/RefAssemblyGenerator.cs b/nukebuild/RefAssemblyGenerator.cs index 61cb04c438..2c5724e3ab 100644 --- a/nukebuild/RefAssemblyGenerator.cs +++ b/nukebuild/RefAssemblyGenerator.cs @@ -1,39 +1,69 @@ -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; +using ILRepacking; +using Mono.Cecil; +using Mono.Cecil.Cil; public class RefAssemblyGenerator { - static void PatchRefAssembly(string file) + class Resolver : DefaultAssemblyResolver, IAssemblyResolver + { + private readonly string _dir; + Dictionary _cache = new(); + + public Resolver(string dir) + { + _dir = dir; + } + + public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + if (_cache.TryGetValue(name.Name, out var asm)) + return asm; + var path = Path.Combine(_dir, name.Name + ".dll"); + if (File.Exists(path)) + return _cache[name.Name] = AssemblyDefinition.ReadAssembly(path, parameters); + return base.Resolve(name, parameters); + } + } + + public 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))); - - var obsoleteAttribute = new TypeRefUser(def.ManifestModule, "System", "ObsoleteAttribute", def.ManifestModule.CorLibTypes.AssemblyRef); - var obsoleteCtor = def.ManifestModule.Import(new MemberRefUser(def.ManifestModule, ".ctor", - new MethodSig(CallingConvention.Default, 0, def.ManifestModule.CorLibTypes.Void, new TypeSig[] - { - def.ManifestModule.CorLibTypes.String - }), obsoleteAttribute)); - - foreach(var t in def.ManifestModule.Types) + + var def = AssemblyDefinition.ReadAssembly(file, new ReaderParameters + { + ReadWrite = true, + InMemory = true, + ReadSymbols = true, + SymbolReaderProvider = new DefaultSymbolReaderProvider(false), + AssemblyResolver = new Resolver(Path.GetDirectoryName(file)) + }); + + var obsoleteAttribute = def.MainModule.ImportReference(new TypeReference("System", "ObsoleteAttribute", def.MainModule, + def.MainModule.TypeSystem.CoreLibrary)); + var obsoleteCtor = def.MainModule.ImportReference(new MethodReference(".ctor", + def.MainModule.TypeSystem.Void, obsoleteAttribute) + { + Parameters = { new ParameterDefinition(def.MainModule.TypeSystem.String) } + }); + + foreach(var t in def.MainModule.Types) ProcessType(t, obsoleteCtor); - def.Write(file, new ModuleWriterOptions(def.ManifestModule) + def.Write(file, new WriterParameters() { - StrongNameKey = new StrongNameKey(snk), + StrongNameKeyBlob = snk, + WriteSymbols = def.MainModule.HasSymbols, + SymbolWriterProvider = new EmbeddedPortablePdbWriterProvider(), + DeterministicMvid = def.MainModule.HasSymbols }); } - static void ProcessType(TypeDef type, MemberRef obsoleteCtor) + static void ProcessType(TypeDefinition type, MethodReference obsoleteCtor) { foreach (var nested in type.NestedTypes) ProcessType(nested, obsoleteCtor); @@ -59,12 +89,11 @@ public class RefAssemblyGenerator if(injectMethod) { - type.Methods.Add(new MethodDefUser("NotClientImplementable", - new MethodSig(CallingConvention.Default, 0, type.Module.CorLibTypes.Void), + type.Methods.Add(new MethodDefinition("NotClientImplementable", MethodAttributes.Assembly | MethodAttributes.Abstract | MethodAttributes.NewSlot - | MethodAttributes.HideBySig)); + | MethodAttributes.HideBySig, type.Module.TypeSystem.Void)); } var forceUnstable = type.CustomAttributes.Any(a => @@ -80,21 +109,24 @@ public class RefAssemblyGenerator } } - static void MarkAsUnstable(IMemberDef def, MemberRef obsoleteCtor, bool force) + static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, bool force) { if (!force || def.HasCustomAttributes == false || def.CustomAttributes.All(a => a.AttributeType.FullName != "Avalonia.Metadata.UnstableAttribute")) return; - if (def.CustomAttributes.Any(a => a.TypeFullName == "System.ObsoleteAttribute")) + if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute")) return; - def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor, new CAArgument[] + def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor) { - new(def.Module.CorLibTypes.String, - "This is a part of unstable API and can be changed in minor releases. You have been warned") - })); + ConstructorArguments = + { + new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String, + "This is a part of unstable API and can be changed in minor releases. You have been warned") + } + }); } public static void GenerateRefAsmsInPackage(string packagePath) @@ -110,29 +142,30 @@ public class RefAssemblyGenerator foreach (var entry in archive.Entries.ToList()) { - if (entry.FullName.StartsWith("lib/")) + if (entry.FullName.StartsWith("lib/") && entry.Name.EndsWith(".xml")) { - 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); - } + var newEntry = archive.CreateEntry("ref/" + entry.FullName.Substring(4), + CompressionLevel.Optimal); + using (var src = entry.Open()) + using (var dst = newEntry.Open()) + src.CopyTo(dst); } } + + var libs = archive.Entries.Where(e => e.FullName.StartsWith("lib/") && e.FullName.EndsWith(".dll")) + .Select((e => new { s = e.FullName.Split('/'), e = e })) + .Select(e => new { Tfm = e.s[1], Name = e.s[2], Entry = e.e }) + .GroupBy(x => x.Tfm); + foreach(var tfm in libs) + using (Helpers.UseTempDir(out var temp)) + { + foreach (var l in tfm) + l.Entry.ExtractToFile(Path.Combine(temp, l.Name)); + foreach (var l in tfm) + PatchRefAssembly(Path.Combine(temp, l.Name)); + foreach (var l in tfm) + archive.CreateEntryFromFile(Path.Combine(temp, l.Name), $"ref/{l.Tfm}/{l.Name}"); + } } } } \ No newline at end of file diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index cc3ce9f0b0..d03746766e 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -18,7 +18,6 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive