1
0

BuildTasksPatcher.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using System;
  2. using System.IO;
  3. using System.IO.Compression;
  4. using System.Linq;
  5. using Mono.Cecil;
  6. using Mono.Cecil.Cil;
  7. using Nuke.Common.Tooling;
  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, Tool ilRepackTool)
  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(GetAssemblyPath(typeof(Microsoft.Build.Framework.ITask)),
  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 cecilAsm = GetAssemblyPath(typeof(Mono.Cecil.AssemblyDefinition));
  74. var cecilRocksAsm = GetAssemblyPath(typeof(Mono.Cecil.Rocks.MethodBodyRocks));
  75. var cecilPdbAsm = GetAssemblyPath(typeof(Mono.Cecil.Pdb.PdbReaderProvider));
  76. var cecilMdbAsm = GetAssemblyPath(typeof(Mono.Cecil.Mdb.MdbReaderProvider));
  77. ilRepackTool.Invoke(
  78. $"/internalize /out:\"{output:nq}\" \"{temp:nq}\" \"{cecilAsm:nq}\" \"{cecilRocksAsm:nq}\" \"{cecilPdbAsm:nq}\" \"{cecilMdbAsm:nq}\"",
  79. tempDir);
  80. // 'hurr-durr assembly with the same name is already loaded' prevention
  81. using (var asm = AssemblyDefinition.ReadAssembly(output,
  82. new ReaderParameters
  83. {
  84. ReadWrite = true,
  85. InMemory = true,
  86. ReadSymbols = true,
  87. SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
  88. }))
  89. {
  90. asm.Name = new AssemblyNameDefinition(
  91. "Avalonia.Build.Tasks."
  92. + Guid.NewGuid().ToString().Replace("-", ""),
  93. new Version(0, 0, 0));
  94. var mainModule = asm.MainModule;
  95. // If we have SourceLink info copy to patched assembly.
  96. if (!string.IsNullOrEmpty(sourceLinkInfoContent))
  97. {
  98. mainModule.CustomDebugInformations.Add(new SourceLinkDebugInformation(sourceLinkInfoContent));
  99. }
  100. // Try to get SymbolWriter if it has it
  101. var reader = mainModule.SymbolReader;
  102. var hasDebugInfo = reader is not null;
  103. var proivder = reader?.GetWriterProvider() is ISymbolWriterProvider p
  104. ? new Wrapper(p, "Avalonia.Build.Tasks.dll")
  105. : default(ISymbolWriterProvider);
  106. var parameters = new WriterParameters
  107. {
  108. #if ISNETFULLFRAMEWORK
  109. StrongNameKeyPair = signingStep.KeyPair,
  110. #endif
  111. WriteSymbols = hasDebugInfo,
  112. SymbolWriterProvider = proivder,
  113. DeterministicMvid = hasDebugInfo,
  114. };
  115. asm.Write(patched, parameters);
  116. patched.Position = 0;
  117. }
  118. }
  119. finally
  120. {
  121. try
  122. {
  123. if (Directory.Exists(tempDir))
  124. Directory.Delete(tempDir, true);
  125. }
  126. catch
  127. {
  128. //ignore
  129. }
  130. }
  131. var fn = entry.FullName;
  132. entry.Delete();
  133. var newEntry = archive.CreateEntry(fn, CompressionLevel.Optimal);
  134. using (var s = newEntry.Open())
  135. patched.CopyTo(s);
  136. }
  137. }
  138. }
  139. }
  140. private static string GetAssemblyPath(Type typeInAssembly)
  141. => typeInAssembly.Assembly.GetModules()[0].FullyQualifiedName;
  142. }