// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using EpicGames.UHT.Utils; namespace EpicGames.UHT.Tables { /// /// Delegate to invoke to run exporter /// /// Factory used to generate export tasks and outputs public delegate void UhtExporterDelegate(IUhtExportFactory factory); /// /// Export options /// [Flags] public enum UhtExporterOptions { /// /// No options /// None = 0, /// /// The exporter should be run by default /// Default = 1 << 0, /// /// Generated CPP files that match any filter in the CppFilters property of this UhtExporter will be included in the compilation process. /// For this option, all custom generated files should be placed into the same folder as the default unreal generated files. /// Make sure the CppFilters property is set up properly and does not conflict with other exporters (including the default). /// Header files should be included as normal in code. /// CompileOutput = 1 << 1, } /// /// Helper methods for testing flags. These methods perform better than the generic HasFlag which hits /// the GC and stalls. /// public static class UhtExporterOptionsExtensions { /// /// Test to see if any of the specified flags are set /// /// Current flags /// Flags to test for /// True if any of the flags are set public static bool HasAnyFlags(this UhtExporterOptions inFlags, UhtExporterOptions testFlags) { return (inFlags & testFlags) != 0; } /// /// Test to see if all of the specified flags are set /// /// Current flags /// Flags to test for /// True if all the flags are set public static bool HasAllFlags(this UhtExporterOptions inFlags, UhtExporterOptions testFlags) { return (inFlags & testFlags) == testFlags; } /// /// Test to see if a specific set of flags have a specific value. /// /// Current flags /// Flags to test for /// Expected value of the tested flags /// True if the given flags have a specific value. public static bool HasExactFlags(this UhtExporterOptions inFlags, UhtExporterOptions testFlags, UhtExporterOptions matchFlags) { return (inFlags & testFlags) == matchFlags; } } /// /// Defines an exporter /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class UhtExporterAttribute : Attribute { /// /// Name of the exporter /// public string Name { get; set; } = String.Empty; /// /// Description of the export. Used to display help /// public string Description { get; set; } = String.Empty; /// /// Exporters in plugins need to specify a module name /// public string ModuleName { get; set; } = String.Empty; /// /// Exporter options /// public UhtExporterOptions Options { get; set; } = UhtExporterOptions.None; /// /// Collection of filters used to delete old cpp files /// public string[]? CppFilters { get; set; } /// /// Collection of filters used to delete old h files /// public string[]? HeaderFilters { get; set; } /// /// Collection of filters for other file types /// public string[]? OtherFilters { get; set; } } /// /// Defines an exporter in the table /// public readonly struct UhtExporter { /// /// Name of the exporter /// public string Name { get; } /// /// Description of the export. Used to display help /// public string Description { get; } /// /// Exporters in plugins need to specify a module name /// public string ModuleName { get; } /// /// Exporter options /// public UhtExporterOptions Options { get; } /// /// Delegate to invoke to start export /// public UhtExporterDelegate Delegate { get; } /// /// Collection of filters used to delete old cpp files /// public IReadOnlyList CppFilters { get; } /// /// Collection of filters used to delete old h files /// public IReadOnlyList HeaderFilters { get; } /// /// Collection of filters for other file types /// public IReadOnlyList OtherFilters { get; } /// /// Construct an exporter table instance /// /// Source attribute /// Delegate to invoke public UhtExporter(UhtExporterAttribute attribute, UhtExporterDelegate exporterDelegate) { Name = attribute.Name; Description = attribute.Description; ModuleName = attribute.ModuleName; Options = attribute.Options; Delegate = exporterDelegate; CppFilters = attribute.CppFilters != null ? new List(attribute.CppFilters) : new List(); HeaderFilters = attribute.HeaderFilters != null ? new List(attribute.HeaderFilters) : new List(); OtherFilters = attribute.OtherFilters != null ? new List(attribute.OtherFilters) : new List(); } } /// /// Exporter table /// public class UhtExporterTable : IEnumerable { private readonly Dictionary _exporterValues = new(StringComparer.OrdinalIgnoreCase); /// /// Return the exporter associated with the given name /// /// /// Exporter associated with the name /// public bool TryGet(string name, out UhtExporter value) { return _exporterValues.TryGetValue(name, out value); } /// /// Handle an exporter attribute /// /// Containing type /// Method info /// Defining attribute /// Thrown if the attribute doesn't properly define an exporter. public void OnExporterAttribute(Type type, MethodInfo methodInfo, UhtExporterAttribute exporterAttribute) { if (String.IsNullOrEmpty(exporterAttribute.Name)) { throw new UhtIceException("An exporter must have a name"); } if (Assembly.GetExecutingAssembly() != type.Assembly) { if (String.IsNullOrEmpty(exporterAttribute.ModuleName)) { throw new UhtIceException("An exporter in a UBT plugin must specify a ModuleName"); } } UhtExporter exporterValue = new(exporterAttribute, (UhtExporterDelegate)Delegate.CreateDelegate(typeof(UhtExporterDelegate), methodInfo)); _exporterValues.Add(exporterAttribute.Name, exporterValue); } /// /// Return an enumerator for all the defined exporters /// /// Enumerator public IEnumerator GetEnumerator() { foreach (KeyValuePair kvp in _exporterValues) { yield return kvp.Value; } } /// /// Return an enumerator for all the defined exporters /// /// Enumerator IEnumerator IEnumerable.GetEnumerator() { foreach (KeyValuePair kvp in _exporterValues) { yield return kvp.Value; } } } }