// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Reflection; using System.Diagnostics; using UnrealBuildTool; using EpicGames.Core; using System.Threading.Tasks; using UnrealBuildBase; namespace AutomationTool { /// /// Compiles and loads script assemblies. /// public static class ScriptManager { private static Dictionary ScriptCommands; public static readonly HashSet AllScriptAssemblies = new HashSet(); public static HashSet BuildProducts { get; private set; } = new HashSet(); /// /// Populates ScriptCommands /// static void EnumerateScriptCommands() { ScriptCommands = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach (Assembly CompiledScripts in AllScriptAssemblies) { try { foreach (Type ClassType in CompiledScripts.GetTypes()) { if (ClassType.IsSubclassOf(typeof(BuildCommand)) && ClassType.IsAbstract == false) { if (ScriptCommands.ContainsKey(ClassType.Name) == false) { ScriptCommands.Add(ClassType.Name, ClassType); } else { bool IsSame = string.Equals(ClassType.AssemblyQualifiedName, ScriptCommands[ClassType.Name].AssemblyQualifiedName); if (IsSame == false) { Log.TraceWarning("Unable to add command {0} twice. Previous: {1}, Current: {2}", ClassType.Name, ClassType.AssemblyQualifiedName, ScriptCommands[ClassType.Name].AssemblyQualifiedName); } } } } } catch (ReflectionTypeLoadException LoadEx) { foreach (Exception SubEx in LoadEx.LoaderExceptions) { Log.TraceWarning("Got type loader exception: {0}", SubEx.ToString()); } throw new AutomationException("Failed to add commands from {0}. {1}", CompiledScripts, LoadEx); } catch (Exception Ex) { throw new AutomationException("Failed to add commands from {0}. {1}", CompiledScripts, Ex); } } } /// /// Enumerate the contents of the output directories /// static void EnumerateBuildProducts() { BuildProducts = new HashSet(); HashSet OutputDirs = new HashSet(); foreach (Assembly CompiledAssembly in AllScriptAssemblies) { DirectoryReference AssemblyDirectory = FileReference.FromString(CompiledAssembly.Location).Directory; AssemblyDirectory = DirectoryReference.FindCorrectCase(AssemblyDirectory); if (OutputDirs.Add(AssemblyDirectory)) { BuildProducts.UnionWith(DirectoryReference.EnumerateFiles(AssemblyDirectory)); // If there's "runtimes" sub-directory, include all the files contained therein foreach (DirectoryReference SubDir in DirectoryReference.EnumerateDirectories(AssemblyDirectory)) { if (String.Equals(SubDir.GetDirectoryName(), "runtimes")) { BuildProducts.UnionWith(DirectoryReference.EnumerateFiles(SubDir, "*", SearchOption.AllDirectories)); } } } } } /// /// Loads all precompiled assemblies (DLLs that end with *Scripts.dll). /// /// Projects to load /// List of compiled assemblies public static void LoadScriptAssemblies(IEnumerable AssemblyPaths) { foreach (FileReference AssemblyLocation in AssemblyPaths) { // Load the assembly into our app domain CommandUtils.LogLog("Loading script DLL: {0}", AssemblyLocation); try { AssemblyUtils.AddFileToAssemblyCache(AssemblyLocation.FullName); // Add a resolver for the Assembly directory, so that its dependencies may be found alongside it AssemblyUtils.InstallRecursiveAssemblyResolver(AssemblyLocation.Directory.FullName); Assembly Assembly = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(AssemblyLocation.FullName)); AllScriptAssemblies.Add(Assembly); } catch (Exception Ex) { throw new AutomationException("Failed to load script DLL: {0}: {1}", AssemblyLocation, Ex.Message); } } Platform.InitializePlatforms(AllScriptAssemblies); EnumerateScriptCommands(); EnumerateBuildProducts(); } public static Dictionary Commands { get { return ScriptCommands; } } } }