using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using UnityEditor;
using Debug = UnityEngine.Debug;
namespace CurvedUI.ConditionalCompilation
{
///
/// Conditional Compilation Utility (CCU) by Unity
/// https://github.com/Unity-Technologies/EditorXR/blob/development/Scripts/Utilities/Editor/ConditionalCompilationUtility.cs
///
/// The Conditional Compilation Utility (CCU) will add defines to the build settings once dependendent classes have been detected.
/// In order for this to be specified in any project without the project needing to include the CCU, at least one custom attribute
/// must be created in the following form:
///
/// [Conditional(UNITY_CCU)] // | This is necessary for CCU to pick up the right attributes
/// public class OptionalDependencyAttribute : Attribute // | Must derive from System.Attribute
/// {
/// public string dependentClass; // | Required field specifying the fully qualified dependent class
/// public string define; // | Required field specifying the define to add
/// }
///
/// Then, simply specify the assembly attribute(s) you created:
/// [assembly: OptionalDependency("UnityEngine.InputNew.InputSystem", "USE_NEW_INPUT")]
/// [assembly: OptionalDependency("Valve.VR.IVRSystem", "ENABLE_STEAMVR_INPUT")]
///
[InitializeOnLoad]
public static class ConditionalCompilationUtility
{
const string k_EnableCCU = "UNITY_CCU";
public static bool enabled {
get
{
var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
return PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Contains(k_EnableCCU);
}
}
public static string[] defines { private set; get; }
static ConditionalCompilationUtility()
{
var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(';').ToList();
if (!defines.Contains(k_EnableCCU, StringComparer.OrdinalIgnoreCase))
{
defines.Add(k_EnableCCU);
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", defines.ToArray()));
// This will trigger another re-compile, which needs to happen, so all the custom attributes will be visible
return;
}
var ccuDefines = new List { k_EnableCCU };
var conditionalAttributeType = typeof(ConditionalAttribute);
const string kDependentClass = "dependentClass";
const string kDefine = "define";
var attributeTypes = GetAssignableTypes(typeof(Attribute), type =>
{
var conditionals = (ConditionalAttribute[])type.GetCustomAttributes(conditionalAttributeType, true);
foreach (var conditional in conditionals)
{
if (string.Equals(conditional.ConditionString, k_EnableCCU, StringComparison.OrdinalIgnoreCase))
{
var dependentClassField = type.GetField(kDependentClass);
if (dependentClassField == null)
{
Debug.LogErrorFormat("[CCU] Attribute type {0} missing field: {1}", type.Name, kDependentClass);
return false;
}
var defineField = type.GetField(kDefine);
if (defineField == null)
{
Debug.LogErrorFormat("[CCU] Attribute type {0} missing field: {1}", type.Name, kDefine);
return false;
}
}
return true;
}
return false;
});
var dependencies = new Dictionary();
ForEachAssembly(assembly =>
{
var typeAttributes = assembly.GetCustomAttributes(false).Cast();
foreach (var typeAttribute in typeAttributes)
{
if (attributeTypes.Contains(typeAttribute.GetType()))
{
var t = typeAttribute.GetType();
// These fields were already validated in a previous step
var dependentClass = t.GetField(kDependentClass).GetValue(typeAttribute) as string;
var define = t.GetField(kDefine).GetValue(typeAttribute) as string;
if (!string.IsNullOrEmpty(dependentClass) && !string.IsNullOrEmpty(define) && !dependencies.ContainsKey(dependentClass))
dependencies.Add(dependentClass, define);
}
}
});
ForEachAssembly(assembly =>
{
foreach (var dependency in dependencies)
{
var type = assembly.GetType(dependency.Key);
if (type != null)
{
var define = dependency.Value;
if (!defines.Contains(define, StringComparer.OrdinalIgnoreCase))
defines.Add(define);
ccuDefines.Add(define);
}
}
});
ConditionalCompilationUtility.defines = ccuDefines.ToArray();
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", defines.ToArray()));
}
static void ForEachAssembly(Action callback)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
callback(assembly);
}
catch (ReflectionTypeLoadException)
{
// Skip any assemblies that don't load properly
continue;
}
}
}
static void ForEachType(Action callback)
{
ForEachAssembly(assembly =>
{
var types = assembly.GetTypes();
foreach (var t in types)
callback(t);
});
}
static IEnumerable GetAssignableTypes(Type type, Func predicate = null)
{
var list = new List();
ForEachType(t =>
{
if (type.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && (predicate == null || predicate(t)))
list.Add(t);
});
return list;
}
}
}