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);
}
}
}