123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Xml.Linq;
- using Unity.VisualScripting;
- using UnityEditor;
- using UnityEditor.Build;
- using UnityEditor.Build.Reporting;
- using UnityEditor.Callbacks;
- using UnityEditor.SceneManagement;
- using UnityEngine;
- using UnityEngine.SceneManagement;
-
- internal class LinkerCreator : IPreprocessBuildWithReport
- {
- private static string linkerPath => Path.Combine(BoltCore.Paths.persistentGenerated, "link.xml");
-
- public int callbackOrder { get; }
-
- private static ManagedStrippingLevel GetManagedStrippingLevel(BuildTargetGroup buildTarget)
- {
- #if UNITY_2023_1_OR_NEWER
- var namedBuildTarget = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(buildTarget);
- return PlayerSettings.GetManagedStrippingLevel(namedBuildTarget);
- #else
- return PlayerSettings.GetManagedStrippingLevel(buildTarget);
- #endif
- }
-
- public void OnPreprocessBuild(BuildReport report)
- {
- if (VSUsageUtility.isVisualScriptingUsed)
- {
- try
- {
- if (GetManagedStrippingLevel(EditorUserBuildSettings.selectedBuildTargetGroup) !=
- ManagedStrippingLevel.Disabled)
- {
- GenerateLinker();
- }
- }
- catch (Exception ex)
- {
- Debug.LogException(ex);
-
- DeleteLinker();
- }
- }
- }
-
- [PostProcessBuild]
- private static void OnPostprocessBuild(BuildTarget buildTarget, string path)
- {
- if (VSUsageUtility.isVisualScriptingUsed)
- {
- DeleteLinker();
- }
- }
-
- private static void DeleteLinker()
- {
- PathUtility.DeleteProjectFileIfExists(linkerPath, true);
- }
-
- // Automatically generates the link.xml file to prevent stripping.
- // Currently only used for plugin assemblies, because blanket preserving
- // all setting assemblies sometimes causes the IL2CPP process to fail.
- // For settings assemblies, the AOT stubs are good enough to fool
- // the static code analysis without needing this full coverage.
- // https://docs.unity3d.com/Manual/iphone-playerSizeOptimization.html
- // However, for FullSerializer, we need to preserve our custom assemblies.
- // This is mostly because IL2CPP will attempt to transform non-public
- // property setters used in deserialization into read-only accessors
- // that return false on PropertyInfo.CanWrite, but only in stripped builds.
- // Therefore, in stripped builds, FS will skip properties that should be
- // deserialized without any error (and that took hours of debugging to figure out).
- public static void GenerateLinker()
- {
- var linker = new XDocument();
-
- var linkerNode = new XElement("linker");
-
- if (!PluginContainer.initialized)
- PluginContainer.Initialize();
-
- foreach (var pluginAssembly in PluginContainer.plugins
- .SelectMany(plugin => plugin.GetType()
- .GetAttributes<PluginRuntimeAssemblyAttribute>()
- .Select(a => a.assemblyName))
- .Distinct())
- {
- var assemblyNode = new XElement("assembly");
- var fullnameAttribute = new XAttribute("fullname", pluginAssembly);
- var preserveAttribute = new XAttribute("preserve", "all");
- assemblyNode.Add(fullnameAttribute);
- assemblyNode.Add(preserveAttribute);
- linkerNode.Add(assemblyNode);
- }
-
- linker.Add(linkerNode);
-
- AddCustomNodesToLinker(linkerNode, PluginContainer.plugins);
-
- PathUtility.CreateDirectoryIfNeeded(BoltCore.Paths.transientGenerated);
-
- PathUtility.DeleteProjectFileIfExists(linkerPath, true);
-
- // Using ToString instead of Save to omit the <?xml> declaration,
- // which doesn't appear in the Unity documentation page for the linker.
- File.WriteAllText(linkerPath, linker.ToString());
- }
-
- private static void AddCustomNodesToLinker(XElement linkerNode, IEnumerable<Plugin> plugins)
- {
- var types = FindAllCustomTypes();
-
- var customTypes = types.Where(t => !plugins.Any(p => t.Assembly == p.runtimeAssembly));
-
- foreach (var type in customTypes)
- {
- var userAssembly = type.Assembly.GetName().Name;
-
- var assemblyNode = new XElement("assembly");
- var fullnameAttribute = new XAttribute("fullname", userAssembly);
- var preserveAttribute = new XAttribute("preserve", "all");
-
- assemblyNode.Add(fullnameAttribute);
-
- var customType = new XElement("type");
- var fullnameType = new XAttribute("fullname", type.FullName);
-
- customType.Add(fullnameType);
- customType.Add(preserveAttribute);
-
- assemblyNode.Add(customType);
-
- linkerNode.Add(assemblyNode);
- }
- }
-
- private static void ProcessSubGraphs(HashSet<Type> types, SubgraphUnit subgraph)
- {
- foreach (var unit in subgraph.nest.graph.units)
- {
- AddTypeToHashSet(types, unit);
- }
- }
-
- private static void AddTypeToHashSet(HashSet<Type> types, IUnit unit)
- {
- if (unit.GetType() == typeof(SubgraphUnit))
- {
- ProcessSubGraphs(types, (SubgraphUnit)unit);
- }
- else
- {
- types.Add(unit.GetType());
- }
- }
-
- private static HashSet<Type> FindGraphsOnAssets()
- {
- var scriptGraphAssets = AssetUtility.GetAllAssetsOfType<ScriptGraphAsset>();
-
- var types = new HashSet<Type>();
-
- var index = 0;
- var total = scriptGraphAssets.Count();
-
- foreach (var scriptGraphAsset in scriptGraphAssets)
- {
- if (EditorUtility.DisplayCancelableProgressBar($"Processing on assets {index}/{total}",
- $"Asset {scriptGraphAsset.name}", (float)index / (float)total))
- {
- break;
- }
-
- index++;
-
- if (scriptGraphAsset.graph != null)
- {
- foreach (var unit in scriptGraphAsset.graph.units)
- {
- AddTypeToHashSet(types, unit);
- }
- }
- }
-
- EditorUtility.ClearProgressBar();
-
- return types;
- }
-
- private static HashSet<Type> FindGraphsOnScenes(bool includeGraphAssets)
- {
- var activeScenePath = SceneManager.GetActiveScene().path;
- var scenePaths = EditorBuildSettings.scenes.Select(s => s.path).ToArray();
-
- var index = 0;
- var total = scenePaths.Count();
- var types = new HashSet<Type>();
-
- foreach (var scenePath in scenePaths)
- {
- index++;
-
- if (EditorUtility.DisplayCancelableProgressBar($"Processing scenes {index} / {total}", $"Scene {scenePath}",
- (float)index / (float)total))
- {
- break;
- }
-
- if (!string.IsNullOrEmpty(scenePath))
- {
- EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
-
- var scriptMachines = UnityObjectUtility.FindObjectsOfTypeIncludingInactive<ScriptMachine>();
-
- foreach (var scriptMachine in scriptMachines)
- {
- if (scriptMachine.nest != null &&
- (scriptMachine.nest.source == GraphSource.Macro && includeGraphAssets) ||
- scriptMachine.nest.source == GraphSource.Embed)
- {
- foreach (var unit in scriptMachine.graph.units)
- {
- AddTypeToHashSet(types, unit);
- }
- }
- }
- }
- }
-
- if (!string.IsNullOrEmpty(activeScenePath))
- {
- EditorSceneManager.OpenScene(activeScenePath);
- }
-
- GC.Collect();
-
- EditorUtility.ClearProgressBar();
-
- return types;
- }
-
- private static HashSet<Type> FindGraphsOnPrefabs(bool includeGraphAssets)
- {
- var types = new HashSet<Type>();
-
- var files = System.IO.Directory.GetFiles(Application.dataPath, "*.prefab",
- System.IO.SearchOption.AllDirectories);
-
- var index = 0;
- var total = files.Count();
-
- var currentScene = EditorSceneManager.GetActiveScene();
- var scenePath = currentScene.path;
-
- EditorSceneManager.NewScene(NewSceneSetup.EmptyScene);
-
- foreach (var file in files)
- {
- index++;
-
- if (EditorUtility.DisplayCancelableProgressBar($"Processing prefabs {index} / {total}", $"Prefab {file}",
- (float)index / (float)total))
- {
- break;
- }
-
- var prefabPath = file.Replace(Application.dataPath, "Assets");
-
- var prefab = UnityEditor.AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject;
-
- if (prefab != null)
- {
- FindGraphInPrefab(types, prefab, includeGraphAssets);
- prefab = null;
-
- EditorUtility.UnloadUnusedAssetsImmediate(true);
- }
- }
-
- EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
-
- EditorUtility.UnloadUnusedAssetsImmediate(true);
-
- GC.Collect();
-
- EditorUtility.ClearProgressBar();
-
- return types;
- }
-
- private static void FindGraphInPrefab(HashSet<Type> types, GameObject gameObject, bool includeGraphAssets)
- {
- var scriptMachines = gameObject.GetComponents<ScriptMachine>();
-
- foreach (var scriptMachine in scriptMachines)
- {
- if (scriptMachine.nest != null &&
- (scriptMachine.nest.source == GraphSource.Macro && includeGraphAssets) ||
- scriptMachine.nest.source == GraphSource.Embed)
- {
- foreach (var unit in scriptMachine.graph.units)
- {
- AddTypeToHashSet(types, unit);
- }
- }
- }
-
- foreach (Transform child in gameObject.transform)
- {
- FindGraphInPrefab(types, child.gameObject, includeGraphAssets);
- }
- }
-
- private static HashSet<Type> FindAllCustomTypes()
- {
- var types = new HashSet<Type>();
-
- var settings = (List<bool>)BoltCore.Configuration.GetMetadata("LinkerPropertyProviderSettings").value;
-
- if (settings[(int)BoltCoreConfiguration.LinkerScanTarget.GraphAssets])
- {
- types.AddRange(FindGraphsOnAssets());
- }
-
- var includeGraphAssets = !settings[(int)BoltCoreConfiguration.LinkerScanTarget.GraphAssets];
-
- if (settings[(int)BoltCoreConfiguration.LinkerScanTarget.EmbeddedSceneGraphs])
- {
- types.AddRange(FindGraphsOnScenes(includeGraphAssets));
- }
-
- if (settings[(int)BoltCoreConfiguration.LinkerScanTarget.EmbeddedPrefabGraphs])
- {
- types.AddRange(FindGraphsOnPrefabs(includeGraphAssets));
- }
-
- return types;
- }
- }
|