ConditionalCompilationUtility.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Reflection;
  6. using UnityEditor;
  7. using Debug = UnityEngine.Debug;
  8. namespace CurvedUI.ConditionalCompilation
  9. {
  10. /// <summary>
  11. /// Conditional Compilation Utility (CCU) by Unity
  12. /// https://github.com/Unity-Technologies/EditorXR/blob/development/Scripts/Utilities/Editor/ConditionalCompilationUtility.cs
  13. ///
  14. /// The Conditional Compilation Utility (CCU) will add defines to the build settings once dependendent classes have been detected.
  15. /// In order for this to be specified in any project without the project needing to include the CCU, at least one custom attribute
  16. /// must be created in the following form:
  17. ///
  18. /// [Conditional(UNITY_CCU)] // | This is necessary for CCU to pick up the right attributes
  19. /// public class OptionalDependencyAttribute : Attribute // | Must derive from System.Attribute
  20. /// {
  21. /// public string dependentClass; // | Required field specifying the fully qualified dependent class
  22. /// public string define; // | Required field specifying the define to add
  23. /// }
  24. ///
  25. /// Then, simply specify the assembly attribute(s) you created:
  26. /// [assembly: OptionalDependency("UnityEngine.InputNew.InputSystem", "USE_NEW_INPUT")]
  27. /// [assembly: OptionalDependency("Valve.VR.IVRSystem", "ENABLE_STEAMVR_INPUT")]
  28. /// </summary>
  29. [InitializeOnLoad]
  30. public static class ConditionalCompilationUtility
  31. {
  32. const string k_EnableCCU = "UNITY_CCU";
  33. public static bool enabled {
  34. get
  35. {
  36. var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
  37. return PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Contains(k_EnableCCU);
  38. }
  39. }
  40. public static string[] defines { private set; get; }
  41. static ConditionalCompilationUtility()
  42. {
  43. var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
  44. var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(';').ToList();
  45. if (!defines.Contains(k_EnableCCU, StringComparer.OrdinalIgnoreCase))
  46. {
  47. defines.Add(k_EnableCCU);
  48. PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", defines.ToArray()));
  49. // This will trigger another re-compile, which needs to happen, so all the custom attributes will be visible
  50. return;
  51. }
  52. var ccuDefines = new List<string> { k_EnableCCU };
  53. var conditionalAttributeType = typeof(ConditionalAttribute);
  54. const string kDependentClass = "dependentClass";
  55. const string kDefine = "define";
  56. var attributeTypes = GetAssignableTypes(typeof(Attribute), type =>
  57. {
  58. var conditionals = (ConditionalAttribute[])type.GetCustomAttributes(conditionalAttributeType, true);
  59. foreach (var conditional in conditionals)
  60. {
  61. if (string.Equals(conditional.ConditionString, k_EnableCCU, StringComparison.OrdinalIgnoreCase))
  62. {
  63. var dependentClassField = type.GetField(kDependentClass);
  64. if (dependentClassField == null)
  65. {
  66. Debug.LogErrorFormat("[CCU] Attribute type {0} missing field: {1}", type.Name, kDependentClass);
  67. return false;
  68. }
  69. var defineField = type.GetField(kDefine);
  70. if (defineField == null)
  71. {
  72. Debug.LogErrorFormat("[CCU] Attribute type {0} missing field: {1}", type.Name, kDefine);
  73. return false;
  74. }
  75. }
  76. return true;
  77. }
  78. return false;
  79. });
  80. var dependencies = new Dictionary<string, string>();
  81. ForEachAssembly(assembly =>
  82. {
  83. var typeAttributes = assembly.GetCustomAttributes(false).Cast<Attribute>();
  84. foreach (var typeAttribute in typeAttributes)
  85. {
  86. if (attributeTypes.Contains(typeAttribute.GetType()))
  87. {
  88. var t = typeAttribute.GetType();
  89. // These fields were already validated in a previous step
  90. var dependentClass = t.GetField(kDependentClass).GetValue(typeAttribute) as string;
  91. var define = t.GetField(kDefine).GetValue(typeAttribute) as string;
  92. if (!string.IsNullOrEmpty(dependentClass) && !string.IsNullOrEmpty(define) && !dependencies.ContainsKey(dependentClass))
  93. dependencies.Add(dependentClass, define);
  94. }
  95. }
  96. });
  97. ForEachAssembly(assembly =>
  98. {
  99. foreach (var dependency in dependencies)
  100. {
  101. var type = assembly.GetType(dependency.Key);
  102. if (type != null)
  103. {
  104. var define = dependency.Value;
  105. if (!defines.Contains(define, StringComparer.OrdinalIgnoreCase))
  106. defines.Add(define);
  107. ccuDefines.Add(define);
  108. }
  109. }
  110. });
  111. ConditionalCompilationUtility.defines = ccuDefines.ToArray();
  112. PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", defines.ToArray()));
  113. }
  114. static void ForEachAssembly(Action<Assembly> callback)
  115. {
  116. var assemblies = AppDomain.CurrentDomain.GetAssemblies();
  117. foreach (var assembly in assemblies)
  118. {
  119. try
  120. {
  121. callback(assembly);
  122. }
  123. catch (ReflectionTypeLoadException)
  124. {
  125. // Skip any assemblies that don't load properly
  126. continue;
  127. }
  128. }
  129. }
  130. static void ForEachType(Action<Type> callback)
  131. {
  132. ForEachAssembly(assembly =>
  133. {
  134. var types = assembly.GetTypes();
  135. foreach (var t in types)
  136. callback(t);
  137. });
  138. }
  139. static IEnumerable<Type> GetAssignableTypes(Type type, Func<Type, bool> predicate = null)
  140. {
  141. var list = new List<Type>();
  142. ForEachType(t =>
  143. {
  144. if (type.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && (predicate == null || predicate(t)))
  145. list.Add(t);
  146. });
  147. return list;
  148. }
  149. }
  150. }