using System; using System.Collections.Generic; using System.Reflection; using System.Xml; using System.IO; using System.Linq; using System.Text; namespace UnityEditor.Build.Pipeline.Utilities { /// /// This can be used to create a LinkXml for your build. This will ensure that the desired runtime types are packed into the build. /// public class LinkXmlGenerator { Dictionary m_TypeConversion = new Dictionary(); HashSet m_Types = new HashSet(); HashSet m_Assemblies = new HashSet(); /// /// Obsolete, no longer does anything. /// [Obsolete] protected Dictionary> serializedClassesPerAssembly = null; Dictionary> m_SerializedClassesPerAssembly = new Dictionary>(); /// /// Constructs and returns a LinkXmlGenerator object that contains default UnityEditor to UnityEngine type conversions. /// /// LinkXmlGenerator object with the default UnityEngine type conversions. public static LinkXmlGenerator CreateDefault() { var linker = new LinkXmlGenerator(); var types = GetEditorTypeConversions(); foreach (var pair in types) linker.SetTypeConversion(pair.Key, pair.Value); return linker; } /// /// Returns the set of UnityEditor types that have valid runtime direct mappings. /// /// Array of KeyValuePairs containing the editor type and it's equivalent runtime type. public static KeyValuePair[] GetEditorTypeConversions() { var editor = Assembly.GetAssembly(typeof(UnityEditor.BuildPipeline)); return new[] { new KeyValuePair(typeof(UnityEditor.Animations.AnimatorController), typeof(UnityEngine.RuntimeAnimatorController)), new KeyValuePair(editor.GetType("UnityEditor.Audio.AudioMixerController"), typeof(UnityEngine.Audio.AudioMixer)), new KeyValuePair(editor.GetType("UnityEditor.Audio.AudioMixerGroupController"), typeof(UnityEngine.Audio.AudioMixerGroup)), new KeyValuePair(typeof(UnityEditor.MonoScript), typeof(UnityEngine.Object)), }; } /// /// Add runtime assembly to the LinkXml Generator. /// /// The desired runtime assemblies. public void AddAssemblies(params Assembly[] assemblies) { if (assemblies == null) return; foreach (var a in assemblies) AddAssemblyInternal(a); } /// /// Add runtime assembly to the LinkXml Generator. /// /// The desired runtime assemblies. public void AddAssemblies(IEnumerable assemblies) { if (assemblies == null) return; foreach (var a in assemblies) AddAssemblyInternal(a); } /// /// Add runtime type to the LinkXml Generator. /// /// The desired runtime types. public void AddTypes(params Type[] types) { if (types == null) return; foreach (var t in types) AddTypeInternal(t); } /// /// Add runtime type to the LinkXml Generator. /// /// The desired runtime types. public void AddTypes(IEnumerable types) { if (types == null) return; foreach (var t in types) AddTypeInternal(t); } /// /// Add SerializedReference class type from fully qualified name to the Generator, those will end up in PreservedTypes.xml /// /// The SerializeReference instance fully qualified name we want to preserve. public void AddSerializedClass(IEnumerable serializedRefTypes) { if (serializedRefTypes == null) return; foreach (var t in serializedRefTypes) { var indexOfAssembly = t.IndexOf(':'); if (indexOfAssembly != -1) AddSerializedClassInternal(t.Substring(0, indexOfAssembly), t.Substring(indexOfAssembly + 1, t.Length - (indexOfAssembly + 1))); } } private void AddTypeInternal(Type t) { if (t == null) return; Type convertedType; if (m_TypeConversion.TryGetValue(t, out convertedType)) m_Types.Add(convertedType); else m_Types.Add(t); } private void AddSerializedClassInternal(string assemblyName, string classWithNameSpace) { if (string.IsNullOrEmpty(assemblyName)) return; if (string.IsNullOrEmpty(classWithNameSpace)) return; if (!m_SerializedClassesPerAssembly.TryGetValue(assemblyName, out HashSet types)) m_SerializedClassesPerAssembly[assemblyName] = types = new HashSet(); types.Add(classWithNameSpace); } private void AddAssemblyInternal(Assembly a) { if (a == null) return; m_Assemblies.Add(a); } /// /// Setup runtime type conversion /// /// Convert from type. /// Convert to type. public void SetTypeConversion(Type a, Type b) { m_TypeConversion[a] = b; } /// /// Save the LinkXml to the specified path. /// /// The path to save the linker xml file. public void Save(string path) { var assemblyMap = new Dictionary>(); foreach (var a in m_Assemblies) { if (!assemblyMap.TryGetValue(a, out _)) assemblyMap.Add(a, new List()); } foreach (var t in m_Types) { var a = t.Assembly; List types; if (!assemblyMap.TryGetValue(a, out types)) assemblyMap.Add(a, types = new List()); types.Add(t); } XmlDocument doc = new XmlDocument(); var linker = doc.AppendChild(doc.CreateElement("linker")); foreach (var k in assemblyMap.OrderBy(a => a.Key.FullName)) { var assembly = linker.AppendChild(doc.CreateElement("assembly")); var attr = doc.CreateAttribute("fullname"); attr.Value = k.Key.FullName; if (assembly.Attributes != null) { assembly.Attributes.Append(attr); if (m_Assemblies.Contains(k.Key)) { var preserveAssembly = doc.CreateAttribute("preserve"); preserveAssembly.Value = "all"; assembly.Attributes.Append(preserveAssembly); } foreach (var t in k.Value.OrderBy(t => t.FullName)) { var typeEl = assembly.AppendChild(doc.CreateElement("type")); var tattr = doc.CreateAttribute("fullname"); tattr.Value = t.FullName; if (typeEl.Attributes != null) { typeEl.Attributes.Append(tattr); var pattr = doc.CreateAttribute("preserve"); pattr.Value = "all"; typeEl.Attributes.Append(pattr); } } //Add serialize reference classes which are contained in the current assembly var assemblyName = k.Key.GetName().Name; if (m_SerializedClassesPerAssembly.ContainsKey(assemblyName)) { //Add content for this foreach (var t in m_SerializedClassesPerAssembly[assemblyName]) { var typeEl = assembly.AppendChild(doc.CreateElement("type")); var tattr = doc.CreateAttribute("fullname"); tattr.Value = t; if (typeEl.Attributes != null) { typeEl.Attributes.Append(tattr); var pattr = doc.CreateAttribute("preserve"); pattr.Value = "nothing"; typeEl.Attributes.Append(pattr); var sattr = doc.CreateAttribute("serialized"); sattr.Value = "true"; typeEl.Attributes.Append(sattr); } } m_SerializedClassesPerAssembly.Remove(assemblyName); } } } //Add serialize reference classes which are contained in other assemblies not yet removed. foreach (var k in m_SerializedClassesPerAssembly.OrderBy(a => a.Key)) { var assembly = linker.AppendChild(doc.CreateElement("assembly")); var attr = doc.CreateAttribute("fullname"); attr.Value = k.Key; if (assembly.Attributes != null) { assembly.Attributes.Append(attr); //Add content for this foreach (var t in k.Value.OrderBy(t => t)) { var typeEl = assembly.AppendChild(doc.CreateElement("type")); var tattr = doc.CreateAttribute("fullname"); tattr.Value = t; if (typeEl.Attributes != null) { typeEl.Attributes.Append(tattr); var pattr = doc.CreateAttribute("preserve"); pattr.Value = "nothing"; typeEl.Attributes.Append(pattr); var sattr = doc.CreateAttribute("serialized"); sattr.Value = "true"; typeEl.Attributes.Append(sattr); } } } } doc.Save(path); } } }