// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using System.Linq; namespace UnrealBuildTool { /// /// Where a plugin was loaded from /// public enum PluginLoadedFrom { /// /// Plugin is built-in to the engine /// Engine, /// /// Project-specific plugin, stored within a game project directory /// GameProject } /// /// Information about a single plugin /// [DebuggerDisplay("\\{{File}\\}")] public class PluginInfo { /// /// Plugin name /// public readonly string Name; /// /// Path to the plugin /// public readonly FileReference File; /// /// Path to the plugin's root directory /// public readonly DirectoryReference Directory; /// /// The plugin descriptor /// public PluginDescriptor Descriptor; /// /// Where does this plugin live? /// public PluginLoadedFrom LoadedFrom; /// /// Constructs a PluginInfo object /// /// /// Where this pl public PluginInfo(FileReference InFile, PluginLoadedFrom InLoadedFrom) { Name = Path.GetFileNameWithoutExtension(InFile.FullName); File = InFile; Directory = File.Directory; Descriptor = PluginDescriptor.FromFile(File, InLoadedFrom == PluginLoadedFrom.GameProject); LoadedFrom = InLoadedFrom; } } /// /// Class for enumerating plugin metadata /// public static class Plugins { /// /// Cache of plugins under each directory /// static Dictionary> PluginInfoCache = new Dictionary>(); /// /// Cache of plugin filenames under each directory /// static Dictionary> PluginFileCache = new Dictionary>(); /// /// Filters the list of plugins to ensure that any game plugins override engine plugins with the same name, and otherwise that no two /// plugins with the same name exist. /// /// List of plugins to filter /// Filtered list of plugins in the original order public static IEnumerable FilterPlugins(IEnumerable Plugins) { Dictionary NameToPluginInfo = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach(PluginInfo Plugin in Plugins) { PluginInfo ExistingPluginInfo; if(!NameToPluginInfo.TryGetValue(Plugin.Name, out ExistingPluginInfo)) { NameToPluginInfo.Add(Plugin.Name, Plugin); } else if(ExistingPluginInfo.LoadedFrom == PluginLoadedFrom.Engine && Plugin.LoadedFrom == PluginLoadedFrom.GameProject) { NameToPluginInfo[Plugin.Name] = Plugin; } else if(ExistingPluginInfo.LoadedFrom != PluginLoadedFrom.GameProject || Plugin.LoadedFrom != PluginLoadedFrom.Engine) { throw new BuildException(String.Format("Found '{0}' plugin in two locations ({1} and {2}). Plugin names must be unique.", Plugin.Name, ExistingPluginInfo.File, Plugin.File)); } } return Plugins.Where(x => NameToPluginInfo[x.Name] == x); } /// /// Read all the plugins available to a given project /// /// Path to the engine directory /// Path to the project file (or null) /// List of additional directories to scan for available plugins /// Sequence of PluginInfo objects, one for each discovered plugin public static List ReadAvailablePlugins(DirectoryReference EngineDirectoryName, FileReference ProjectFileName, List AdditionalDirectories) { List Plugins = new List(); // Read all the engine plugins DirectoryReference EnginePluginsDirectoryName = DirectoryReference.Combine(EngineDirectoryName, "Plugins"); Plugins.AddRange(ReadPluginsFromDirectory(EnginePluginsDirectoryName, PluginLoadedFrom.Engine)); // Read all the project plugins if (ProjectFileName != null) { DirectoryReference ProjectPluginsDir = DirectoryReference.Combine(ProjectFileName.Directory, "Plugins"); Plugins.AddRange(ReadPluginsFromDirectory(ProjectPluginsDir, PluginLoadedFrom.GameProject)); } // Scan for shared plugins in project specified additional directories foreach (DirectoryReference DirRef in AdditionalDirectories) { Plugins.AddRange(ReadPluginsFromDirectory(DirRef, PluginLoadedFrom.Engine)); } return Plugins; } /// /// Read all the plugin descriptors under the given engine directory /// /// The parent directory to look in. /// Sequence of the found PluginInfo object. public static IReadOnlyList ReadEnginePlugins(DirectoryReference EngineDirectory) { DirectoryReference EnginePluginsDirectory = DirectoryReference.Combine(EngineDirectory, "Plugins"); return ReadPluginsFromDirectory(EnginePluginsDirectory, PluginLoadedFrom.Engine); } /// /// Read all the plugin descriptors under the given project directory /// /// The parent directory to look in. /// Sequence of the found PluginInfo object. public static IReadOnlyList ReadProjectPlugins(DirectoryReference ProjectDirectory) { DirectoryReference ProjectPluginsDirectory = DirectoryReference.Combine(ProjectDirectory, "Plugins"); return ReadPluginsFromDirectory(ProjectPluginsDirectory, PluginLoadedFrom.GameProject); } /// /// Read all of the plugins found in the project specified additional plugin directories /// /// The list of additional directories to scan /// List of the found PluginInfo objects public static IReadOnlyList ReadAdditionalPlugins(List AdditionalDirectories) { List Plugins = new List(); // Scan for shared plugins in project specified additional directories foreach (DirectoryReference DirRef in AdditionalDirectories) { Plugins.AddRange(ReadPluginsFromDirectory(DirRef, PluginLoadedFrom.Engine)); } return Plugins; } /// /// Read all the plugin descriptors under the given directory /// /// The parent directory to look in. /// The directory type /// Sequence of the found PluginInfo object. public static IReadOnlyList ReadPluginsFromDirectory(DirectoryReference ParentDirectory, PluginLoadedFrom LoadedFrom) { List Plugins; if (!PluginInfoCache.TryGetValue(ParentDirectory, out Plugins)) { Plugins = new List(); foreach (FileReference PluginFileName in EnumeratePlugins(ParentDirectory)) { PluginInfo Plugin = new PluginInfo(PluginFileName, LoadedFrom); Plugins.Add(Plugin); } PluginInfoCache.Add(ParentDirectory, Plugins); } return Plugins; } /// /// Find paths to all the plugins under a given parent directory (recursively) /// /// Parent directory to look in. Plugins will be found in any *subfolders* of this directory. public static IEnumerable EnumeratePlugins(DirectoryReference ParentDirectory) { List FileNames; if (!PluginFileCache.TryGetValue(ParentDirectory, out FileNames)) { FileNames = new List(); if (DirectoryReference.Exists(ParentDirectory)) { EnumeratePluginsInternal(ParentDirectory, FileNames); } PluginFileCache.Add(ParentDirectory, FileNames); } return FileNames; } /// /// Find paths to all the plugins under a given parent directory (recursively) /// /// Parent directory to look in. Plugins will be found in any *subfolders* of this directory. /// List of filenames. Will have all the discovered .uplugin files appended to it. static void EnumeratePluginsInternal(DirectoryReference ParentDirectory, List FileNames) { foreach (DirectoryReference ChildDirectory in DirectoryReference.EnumerateDirectories(ParentDirectory)) { int InitialFileNamesCount = FileNames.Count; foreach (FileReference PluginFile in DirectoryReference.EnumerateFiles(ChildDirectory, "*.uplugin")) { FileNames.Add(PluginFile); } if (FileNames.Count == InitialFileNamesCount) { EnumeratePluginsInternal(ChildDirectory, FileNames); } } } } }