暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

LinkerCreator.cs 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Xml.Linq;
  6. using Unity.VisualScripting;
  7. using UnityEditor;
  8. using UnityEditor.Build;
  9. using UnityEditor.Build.Reporting;
  10. using UnityEditor.Callbacks;
  11. using UnityEditor.SceneManagement;
  12. using UnityEngine;
  13. using UnityEngine.SceneManagement;
  14. internal class LinkerCreator : IPreprocessBuildWithReport
  15. {
  16. private static string linkerPath => Path.Combine(BoltCore.Paths.persistentGenerated, "link.xml");
  17. public int callbackOrder { get; }
  18. private static ManagedStrippingLevel GetManagedStrippingLevel(BuildTargetGroup buildTarget)
  19. {
  20. #if UNITY_2023_1_OR_NEWER
  21. var namedBuildTarget = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(buildTarget);
  22. return PlayerSettings.GetManagedStrippingLevel(namedBuildTarget);
  23. #else
  24. return PlayerSettings.GetManagedStrippingLevel(buildTarget);
  25. #endif
  26. }
  27. public void OnPreprocessBuild(BuildReport report)
  28. {
  29. if (VSUsageUtility.isVisualScriptingUsed)
  30. {
  31. try
  32. {
  33. if (GetManagedStrippingLevel(EditorUserBuildSettings.selectedBuildTargetGroup) !=
  34. ManagedStrippingLevel.Disabled)
  35. {
  36. GenerateLinker();
  37. }
  38. }
  39. catch (Exception ex)
  40. {
  41. Debug.LogException(ex);
  42. DeleteLinker();
  43. }
  44. }
  45. }
  46. [PostProcessBuild]
  47. private static void OnPostprocessBuild(BuildTarget buildTarget, string path)
  48. {
  49. if (VSUsageUtility.isVisualScriptingUsed)
  50. {
  51. DeleteLinker();
  52. }
  53. }
  54. private static void DeleteLinker()
  55. {
  56. PathUtility.DeleteProjectFileIfExists(linkerPath, true);
  57. }
  58. // Automatically generates the link.xml file to prevent stripping.
  59. // Currently only used for plugin assemblies, because blanket preserving
  60. // all setting assemblies sometimes causes the IL2CPP process to fail.
  61. // For settings assemblies, the AOT stubs are good enough to fool
  62. // the static code analysis without needing this full coverage.
  63. // https://docs.unity3d.com/Manual/iphone-playerSizeOptimization.html
  64. // However, for FullSerializer, we need to preserve our custom assemblies.
  65. // This is mostly because IL2CPP will attempt to transform non-public
  66. // property setters used in deserialization into read-only accessors
  67. // that return false on PropertyInfo.CanWrite, but only in stripped builds.
  68. // Therefore, in stripped builds, FS will skip properties that should be
  69. // deserialized without any error (and that took hours of debugging to figure out).
  70. public static void GenerateLinker()
  71. {
  72. var linker = new XDocument();
  73. var linkerNode = new XElement("linker");
  74. if (!PluginContainer.initialized)
  75. PluginContainer.Initialize();
  76. foreach (var pluginAssembly in PluginContainer.plugins
  77. .SelectMany(plugin => plugin.GetType()
  78. .GetAttributes<PluginRuntimeAssemblyAttribute>()
  79. .Select(a => a.assemblyName))
  80. .Distinct())
  81. {
  82. var assemblyNode = new XElement("assembly");
  83. var fullnameAttribute = new XAttribute("fullname", pluginAssembly);
  84. var preserveAttribute = new XAttribute("preserve", "all");
  85. assemblyNode.Add(fullnameAttribute);
  86. assemblyNode.Add(preserveAttribute);
  87. linkerNode.Add(assemblyNode);
  88. }
  89. linker.Add(linkerNode);
  90. AddCustomNodesToLinker(linkerNode, PluginContainer.plugins);
  91. PathUtility.CreateDirectoryIfNeeded(BoltCore.Paths.transientGenerated);
  92. PathUtility.DeleteProjectFileIfExists(linkerPath, true);
  93. // Using ToString instead of Save to omit the <?xml> declaration,
  94. // which doesn't appear in the Unity documentation page for the linker.
  95. File.WriteAllText(linkerPath, linker.ToString());
  96. }
  97. private static void AddCustomNodesToLinker(XElement linkerNode, IEnumerable<Plugin> plugins)
  98. {
  99. var types = FindAllCustomTypes();
  100. var customTypes = types.Where(t => !plugins.Any(p => t.Assembly == p.runtimeAssembly));
  101. foreach (var type in customTypes)
  102. {
  103. var userAssembly = type.Assembly.GetName().Name;
  104. var assemblyNode = new XElement("assembly");
  105. var fullnameAttribute = new XAttribute("fullname", userAssembly);
  106. var preserveAttribute = new XAttribute("preserve", "all");
  107. assemblyNode.Add(fullnameAttribute);
  108. var customType = new XElement("type");
  109. var fullnameType = new XAttribute("fullname", type.FullName);
  110. customType.Add(fullnameType);
  111. customType.Add(preserveAttribute);
  112. assemblyNode.Add(customType);
  113. linkerNode.Add(assemblyNode);
  114. }
  115. }
  116. private static void ProcessSubGraphs(HashSet<Type> types, SubgraphUnit subgraph)
  117. {
  118. foreach (var unit in subgraph.nest.graph.units)
  119. {
  120. AddTypeToHashSet(types, unit);
  121. }
  122. }
  123. private static void AddTypeToHashSet(HashSet<Type> types, IUnit unit)
  124. {
  125. if (unit.GetType() == typeof(SubgraphUnit))
  126. {
  127. ProcessSubGraphs(types, (SubgraphUnit)unit);
  128. }
  129. else
  130. {
  131. types.Add(unit.GetType());
  132. }
  133. }
  134. private static HashSet<Type> FindGraphsOnAssets()
  135. {
  136. var scriptGraphAssets = AssetUtility.GetAllAssetsOfType<ScriptGraphAsset>();
  137. var types = new HashSet<Type>();
  138. var index = 0;
  139. var total = scriptGraphAssets.Count();
  140. foreach (var scriptGraphAsset in scriptGraphAssets)
  141. {
  142. if (EditorUtility.DisplayCancelableProgressBar($"Processing on assets {index}/{total}",
  143. $"Asset {scriptGraphAsset.name}", (float)index / (float)total))
  144. {
  145. break;
  146. }
  147. index++;
  148. if (scriptGraphAsset.graph != null)
  149. {
  150. foreach (var unit in scriptGraphAsset.graph.units)
  151. {
  152. AddTypeToHashSet(types, unit);
  153. }
  154. }
  155. }
  156. EditorUtility.ClearProgressBar();
  157. return types;
  158. }
  159. private static HashSet<Type> FindGraphsOnScenes(bool includeGraphAssets)
  160. {
  161. var activeScenePath = SceneManager.GetActiveScene().path;
  162. var scenePaths = EditorBuildSettings.scenes.Select(s => s.path).ToArray();
  163. var index = 0;
  164. var total = scenePaths.Count();
  165. var types = new HashSet<Type>();
  166. foreach (var scenePath in scenePaths)
  167. {
  168. index++;
  169. if (EditorUtility.DisplayCancelableProgressBar($"Processing scenes {index} / {total}", $"Scene {scenePath}",
  170. (float)index / (float)total))
  171. {
  172. break;
  173. }
  174. if (!string.IsNullOrEmpty(scenePath))
  175. {
  176. EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
  177. var scriptMachines = UnityObjectUtility.FindObjectsOfTypeIncludingInactive<ScriptMachine>();
  178. foreach (var scriptMachine in scriptMachines)
  179. {
  180. if (scriptMachine.nest != null &&
  181. (scriptMachine.nest.source == GraphSource.Macro && includeGraphAssets) ||
  182. scriptMachine.nest.source == GraphSource.Embed)
  183. {
  184. foreach (var unit in scriptMachine.graph.units)
  185. {
  186. AddTypeToHashSet(types, unit);
  187. }
  188. }
  189. }
  190. }
  191. }
  192. if (!string.IsNullOrEmpty(activeScenePath))
  193. {
  194. EditorSceneManager.OpenScene(activeScenePath);
  195. }
  196. GC.Collect();
  197. EditorUtility.ClearProgressBar();
  198. return types;
  199. }
  200. private static HashSet<Type> FindGraphsOnPrefabs(bool includeGraphAssets)
  201. {
  202. var types = new HashSet<Type>();
  203. var files = System.IO.Directory.GetFiles(Application.dataPath, "*.prefab",
  204. System.IO.SearchOption.AllDirectories);
  205. var index = 0;
  206. var total = files.Count();
  207. var currentScene = EditorSceneManager.GetActiveScene();
  208. var scenePath = currentScene.path;
  209. EditorSceneManager.NewScene(NewSceneSetup.EmptyScene);
  210. foreach (var file in files)
  211. {
  212. index++;
  213. if (EditorUtility.DisplayCancelableProgressBar($"Processing prefabs {index} / {total}", $"Prefab {file}",
  214. (float)index / (float)total))
  215. {
  216. break;
  217. }
  218. var prefabPath = file.Replace(Application.dataPath, "Assets");
  219. var prefab = UnityEditor.AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject;
  220. if (prefab != null)
  221. {
  222. FindGraphInPrefab(types, prefab, includeGraphAssets);
  223. prefab = null;
  224. EditorUtility.UnloadUnusedAssetsImmediate(true);
  225. }
  226. }
  227. EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
  228. EditorUtility.UnloadUnusedAssetsImmediate(true);
  229. GC.Collect();
  230. EditorUtility.ClearProgressBar();
  231. return types;
  232. }
  233. private static void FindGraphInPrefab(HashSet<Type> types, GameObject gameObject, bool includeGraphAssets)
  234. {
  235. var scriptMachines = gameObject.GetComponents<ScriptMachine>();
  236. foreach (var scriptMachine in scriptMachines)
  237. {
  238. if (scriptMachine.nest != null &&
  239. (scriptMachine.nest.source == GraphSource.Macro && includeGraphAssets) ||
  240. scriptMachine.nest.source == GraphSource.Embed)
  241. {
  242. foreach (var unit in scriptMachine.graph.units)
  243. {
  244. AddTypeToHashSet(types, unit);
  245. }
  246. }
  247. }
  248. foreach (Transform child in gameObject.transform)
  249. {
  250. FindGraphInPrefab(types, child.gameObject, includeGraphAssets);
  251. }
  252. }
  253. private static HashSet<Type> FindAllCustomTypes()
  254. {
  255. var types = new HashSet<Type>();
  256. var settings = (List<bool>)BoltCore.Configuration.GetMetadata("LinkerPropertyProviderSettings").value;
  257. if (settings[(int)BoltCoreConfiguration.LinkerScanTarget.GraphAssets])
  258. {
  259. types.AddRange(FindGraphsOnAssets());
  260. }
  261. var includeGraphAssets = !settings[(int)BoltCoreConfiguration.LinkerScanTarget.GraphAssets];
  262. if (settings[(int)BoltCoreConfiguration.LinkerScanTarget.EmbeddedSceneGraphs])
  263. {
  264. types.AddRange(FindGraphsOnScenes(includeGraphAssets));
  265. }
  266. if (settings[(int)BoltCoreConfiguration.LinkerScanTarget.EmbeddedPrefabGraphs])
  267. {
  268. types.AddRange(FindGraphsOnPrefabs(includeGraphAssets));
  269. }
  270. return types;
  271. }
  272. }