BuildTasksPatcher.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. using System;
  2. using System.IO;
  3. using System.IO.Compression;
  4. using System.Linq;
  5. using ILRepacking;
  6. using Mono.Cecil;
  7. using Mono.Cecil.Cil;
  8. public class BuildTasksPatcher
  9. {
  10. /// <summary>
  11. /// This helper class, avoid argument null exception
  12. /// when cecil write AssemblyNameDefinition on MemoryStream.
  13. /// </summary>
  14. private class Wrapper : ISymbolWriterProvider
  15. {
  16. readonly ISymbolWriterProvider _provider;
  17. readonly string _filename;
  18. public Wrapper(ISymbolWriterProvider provider, string filename)
  19. {
  20. _provider = provider;
  21. _filename = filename;
  22. }
  23. public ISymbolWriter GetSymbolWriter(ModuleDefinition module, string fileName) =>
  24. _provider.GetSymbolWriter(module, string.IsNullOrWhiteSpace(fileName) ? _filename : fileName);
  25. public ISymbolWriter GetSymbolWriter(ModuleDefinition module, Stream symbolStream) =>
  26. _provider.GetSymbolWriter(module, symbolStream);
  27. }
  28. private static string GetSourceLinkInfo(string path)
  29. {
  30. try
  31. {
  32. using (var asm = AssemblyDefinition.ReadAssembly(path,
  33. new ReaderParameters
  34. {
  35. ReadWrite = true,
  36. InMemory = true,
  37. ReadSymbols = true,
  38. SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
  39. }))
  40. {
  41. if (asm.MainModule.CustomDebugInformations?.OfType<SourceLinkDebugInformation>()?.FirstOrDefault() is { } sli)
  42. {
  43. return sli.Content;
  44. }
  45. }
  46. }
  47. catch
  48. {
  49. }
  50. return null;
  51. }
  52. public static void PatchBuildTasksInPackage(string packagePath)
  53. {
  54. using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite),
  55. ZipArchiveMode.Update))
  56. {
  57. foreach (var entry in archive.Entries.ToList())
  58. {
  59. if (entry.Name == "Avalonia.Build.Tasks.dll")
  60. {
  61. var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
  62. Directory.CreateDirectory(tempDir);
  63. var temp = Path.Combine(tempDir, entry.Name);
  64. var output = temp + ".output";
  65. File.Copy(typeof(Microsoft.Build.Framework.ITask).Assembly.GetModules()[0].FullyQualifiedName,
  66. Path.Combine(tempDir, "Microsoft.Build.Framework.dll"));
  67. var patched = new MemoryStream();
  68. try
  69. {
  70. entry.ExtractToFile(temp, true);
  71. // Get Original SourceLinkInfo Content
  72. var sourceLinkInfoContent = GetSourceLinkInfo(temp);
  73. var repack = new ILRepacking.ILRepack(new RepackOptions()
  74. {
  75. Internalize = true,
  76. InputAssemblies = new[]
  77. {
  78. temp,
  79. typeof(Mono.Cecil.AssemblyDefinition).Assembly.GetModules()[0].FullyQualifiedName,
  80. typeof(Mono.Cecil.Rocks.MethodBodyRocks).Assembly.GetModules()[0].FullyQualifiedName,
  81. typeof(Mono.Cecil.Pdb.PdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName,
  82. typeof(Mono.Cecil.Mdb.MdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName,
  83. },
  84. SearchDirectories = Array.Empty<string>(),
  85. DebugInfo = true, // Allowed read debug info
  86. OutputFile = output
  87. });
  88. repack.Repack();
  89. // 'hurr-durr assembly with the same name is already loaded' prevention
  90. using (var asm = AssemblyDefinition.ReadAssembly(output,
  91. new ReaderParameters
  92. {
  93. ReadWrite = true,
  94. InMemory = true,
  95. ReadSymbols = true,
  96. SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
  97. }))
  98. {
  99. asm.Name = new AssemblyNameDefinition(
  100. "Avalonia.Build.Tasks."
  101. + Guid.NewGuid().ToString().Replace("-", ""),
  102. new Version(0, 0, 0));
  103. var mainModule = asm.MainModule;
  104. // If we have SourceLink info copy to patched assembly.
  105. if (!string.IsNullOrEmpty(sourceLinkInfoContent))
  106. {
  107. mainModule.CustomDebugInformations.Add(new SourceLinkDebugInformation(sourceLinkInfoContent));
  108. }
  109. // Try to get SymbolWriter if it has it
  110. var reader = mainModule.SymbolReader;
  111. var hasDebugInfo = reader is not null;
  112. var proivder = reader?.GetWriterProvider() is ISymbolWriterProvider p
  113. ? new Wrapper(p, "Avalonia.Build.Tasks.dll")
  114. : default(ISymbolWriterProvider);
  115. var parameters = new WriterParameters
  116. {
  117. #if ISNETFULLFRAMEWORK
  118. StrongNameKeyPair = signingStep.KeyPair,
  119. #endif
  120. WriteSymbols = hasDebugInfo,
  121. SymbolWriterProvider = proivder,
  122. DeterministicMvid = hasDebugInfo,
  123. };
  124. asm.Write(patched, parameters);
  125. patched.Position = 0;
  126. }
  127. }
  128. finally
  129. {
  130. try
  131. {
  132. if (Directory.Exists(tempDir))
  133. Directory.Delete(tempDir, true);
  134. }
  135. catch
  136. {
  137. //ignore
  138. }
  139. }
  140. var fn = entry.FullName;
  141. entry.Delete();
  142. var newEntry = archive.CreateEntry(fn, CompressionLevel.Optimal);
  143. using (var s = newEntry.Open())
  144. patched.CopyTo(s);
  145. }
  146. }
  147. }
  148. }
  149. }