// 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);
}
}
}
}
}