CompilationFinishedHook.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using UnityEditor;
  6. using UnityEditor.Compilation;
  7. using UnityEngine;
  8. using UnityAssembly = UnityEditor.Compilation.Assembly;
  9. namespace Mirror.Weaver
  10. {
  11. public static class CompilationFinishedHook
  12. {
  13. const string MirrorRuntimeAssemblyName = "Mirror";
  14. const string MirrorWeaverAssemblyName = "Mirror.Weaver";
  15. // delegate for subscription to Weaver warning messages
  16. public static Action<string> OnWeaverWarning;
  17. // delete for subscription to Weaver error messages
  18. public static Action<string> OnWeaverError;
  19. // controls weather Weaver errors are reported direct to the Unity console (tests enable this)
  20. public static bool UnityLogEnabled = true;
  21. // warning message handler that also calls OnWarningMethod delegate
  22. static void HandleWarning(string msg)
  23. {
  24. if (UnityLogEnabled) Debug.LogWarning(msg);
  25. OnWeaverWarning?.Invoke(msg);
  26. }
  27. // error message handler that also calls OnErrorMethod delegate
  28. static void HandleError(string msg)
  29. {
  30. if (UnityLogEnabled) Debug.LogError(msg);
  31. OnWeaverError?.Invoke(msg);
  32. }
  33. [InitializeOnLoadMethod]
  34. public static void OnInitializeOnLoad()
  35. {
  36. CompilationPipeline.assemblyCompilationFinished += OnCompilationFinished;
  37. // We only need to run this once per session
  38. // after that, all assemblies will be weaved by the event
  39. if (!SessionState.GetBool("MIRROR_WEAVED", false))
  40. {
  41. // reset session flag
  42. SessionState.SetBool("MIRROR_WEAVED", true);
  43. SessionState.SetBool("MIRROR_WEAVE_SUCCESS", true);
  44. WeaveExistingAssemblies();
  45. }
  46. }
  47. public static void WeaveExistingAssemblies()
  48. {
  49. foreach (UnityAssembly assembly in CompilationPipeline.GetAssemblies())
  50. {
  51. if (File.Exists(assembly.outputPath))
  52. {
  53. OnCompilationFinished(assembly.outputPath, new CompilerMessage[0]);
  54. }
  55. }
  56. #if UNITY_2019_3_OR_NEWER
  57. EditorUtility.RequestScriptReload();
  58. #else
  59. UnityEditorInternal.InternalEditorUtility.RequestScriptReload();
  60. #endif
  61. }
  62. static string FindMirrorRuntime()
  63. {
  64. foreach (UnityAssembly assembly in CompilationPipeline.GetAssemblies())
  65. {
  66. if (assembly.name == MirrorRuntimeAssemblyName)
  67. {
  68. return assembly.outputPath;
  69. }
  70. }
  71. return "";
  72. }
  73. static bool CompilerMessagesContainError(CompilerMessage[] messages)
  74. {
  75. return messages.Any(msg => msg.type == CompilerMessageType.Error);
  76. }
  77. static void OnCompilationFinished(string assemblyPath, CompilerMessage[] messages)
  78. {
  79. // Do nothing if there were compile errors on the target
  80. if (CompilerMessagesContainError(messages))
  81. {
  82. Debug.Log("Weaver: stop because compile errors on target");
  83. return;
  84. }
  85. // Should not run on the editor only assemblies
  86. if (assemblyPath.Contains("-Editor") || assemblyPath.Contains(".Editor"))
  87. {
  88. return;
  89. }
  90. // don't weave mirror files
  91. string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
  92. if (assemblyName == MirrorRuntimeAssemblyName || assemblyName == MirrorWeaverAssemblyName)
  93. {
  94. return;
  95. }
  96. // find Mirror.dll
  97. string mirrorRuntimeDll = FindMirrorRuntime();
  98. if (string.IsNullOrEmpty(mirrorRuntimeDll))
  99. {
  100. Debug.LogError("Failed to find Mirror runtime assembly");
  101. return;
  102. }
  103. if (!File.Exists(mirrorRuntimeDll))
  104. {
  105. // this is normal, it happens with any assembly that is built before mirror
  106. // such as unity packages or your own assemblies
  107. // those don't need to be weaved
  108. // if any assembly depends on mirror, then it will be built after
  109. return;
  110. }
  111. // find UnityEngine.CoreModule.dll
  112. string unityEngineCoreModuleDLL = UnityEditorInternal.InternalEditorUtility.GetEngineCoreModuleAssemblyPath();
  113. if (string.IsNullOrEmpty(unityEngineCoreModuleDLL))
  114. {
  115. Debug.LogError("Failed to find UnityEngine assembly");
  116. return;
  117. }
  118. HashSet<string> dependencyPaths = GetDependecyPaths(assemblyPath);
  119. dependencyPaths.Add(Path.GetDirectoryName(mirrorRuntimeDll));
  120. dependencyPaths.Add(Path.GetDirectoryName(unityEngineCoreModuleDLL));
  121. Log.Warning = HandleWarning;
  122. Log.Error = HandleError;
  123. if (!Weaver.WeaveAssembly(assemblyPath, dependencyPaths.ToArray()))
  124. {
  125. // Set false...will be checked in \Editor\EnterPlayModeSettingsCheck.CheckSuccessfulWeave()
  126. SessionState.SetBool("MIRROR_WEAVE_SUCCESS", false);
  127. if (UnityLogEnabled) Debug.LogError("Weaving failed for: " + assemblyPath);
  128. }
  129. }
  130. static HashSet<string> GetDependecyPaths(string assemblyPath)
  131. {
  132. // build directory list for later asm/symbol resolving using CompilationPipeline refs
  133. HashSet<string> dependencyPaths = new HashSet<string>
  134. {
  135. Path.GetDirectoryName(assemblyPath)
  136. };
  137. foreach (UnityAssembly unityAsm in CompilationPipeline.GetAssemblies())
  138. {
  139. if (unityAsm.outputPath == assemblyPath)
  140. {
  141. foreach (string unityAsmRef in unityAsm.compiledAssemblyReferences)
  142. {
  143. dependencyPaths.Add(Path.GetDirectoryName(unityAsmRef));
  144. }
  145. }
  146. }
  147. return dependencyPaths;
  148. }
  149. }
  150. }