// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Linq;
using Tools.DotNETCommon;
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
///
Project
}
///
/// Where a plugin was loaded from. The order of this enum is important; in the case of name collisions, larger-valued types will take precedence. Plugins of the same type may not be duplicated.
///
public enum PluginType
{
///
/// Plugin is built-in to the engine
///
Engine,
///
/// Enterprise plugin
///
Enterprise,
///
/// Project-specific plugin, stored within a game project directory
///
Project,
///
/// Plugin found in an external directory (found in an AdditionalPluginDirectory listed in the project file, or referenced on the command line)
///
External,
///
/// Project-specific mod plugin
///
Mod,
}
///
/// 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;
///
/// The type of this plugin
///
public PluginType Type;
///
/// Constructs a PluginInfo object
///
/// Path to the plugin descriptor
/// The type of this plugin
public PluginInfo(FileReference InFile, PluginType InType)
{
Name = Path.GetFileNameWithoutExtension(InFile.FullName);
File = InFile;
Directory = File.Directory;
Descriptor = PluginDescriptor.FromFile(File);
Type = InType;
}
///
/// Determines whether the plugin should be enabled by default
///
public bool EnabledByDefault
{
get
{
if(Descriptor.bEnabledByDefault.HasValue)
{
return Descriptor.bEnabledByDefault.Value;
}
else
{
return (LoadedFrom == PluginLoadedFrom.Project);
}
}
}
///
/// Determines where the plugin was loaded from
///
public PluginLoadedFrom LoadedFrom
{
get
{
if(Type == PluginType.Engine || Type == PluginType.Enterprise)
{
return PluginLoadedFrom.Engine;
}
else
{
return PluginLoadedFrom.Project;
}
}
}
}
///
/// 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(Plugin.Type > ExistingPluginInfo.Type)
{
NameToPluginInfo[Plugin.Name] = Plugin;
}
else if(Plugin.Type == ExistingPluginInfo.Type)
{
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, string[] AdditionalDirectories)
{
List Plugins = new List();
// Read all the engine plugins
Plugins.AddRange(ReadEnginePlugins(EngineDirectoryName));
// Read all the project plugins
if (ProjectFileName != null)
{
Plugins.AddRange(ReadProjectPlugins(ProjectFileName.Directory));
}
// Scan for shared plugins in project specified additional directories
if(AdditionalDirectories != null)
{
foreach (string AdditionalDirectory in AdditionalDirectories)
{
DirectoryReference DirRef = DirectoryReference.Combine(ProjectFileName.Directory, AdditionalDirectory);
Plugins.AddRange(ReadPluginsFromDirectory(DirRef, PluginType.External));
}
}
return Plugins;
}
///
/// Enumerates all the plugin files available to the given project
///
/// Path to the project file
/// List of project files
public static IEnumerable EnumeratePlugins(FileReference ProjectFile)
{
DirectoryReference EnginePluginsDir = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Plugins");
foreach(FileReference PluginFile in EnumeratePlugins(EnginePluginsDir))
{
yield return PluginFile;
}
DirectoryReference EnterprisePluginsDir = DirectoryReference.Combine(UnrealBuildTool.EnterpriseDirectory, "Plugins");
foreach(FileReference PluginFile in EnumeratePlugins(EnterprisePluginsDir))
{
yield return PluginFile;
}
if(ProjectFile != null)
{
DirectoryReference ProjectPluginsDir = DirectoryReference.Combine(ProjectFile.Directory, "Plugins");
foreach(FileReference PluginFile in EnumeratePlugins(ProjectPluginsDir))
{
yield return PluginFile;
}
DirectoryReference ProjectModsDir = DirectoryReference.Combine(ProjectFile.Directory, "Mods");
foreach(FileReference PluginFile in EnumeratePlugins(ProjectModsDir))
{
yield return PluginFile;
}
}
}
///
/// 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 PluginsDir = DirectoryReference.Combine(EngineDirectory, "Plugins");
return ReadPluginsFromDirectory(PluginsDir, PluginType.Engine);
}
///
/// Read all the plugin descriptors under the given enterprise directory
///
/// The parent directory to look in.
/// Sequence of the found PluginInfo object.
public static IReadOnlyList ReadEnterprisePlugins(DirectoryReference EnterpriseDirectory)
{
DirectoryReference PluginsDir = DirectoryReference.Combine(EnterpriseDirectory, "Plugins");
return ReadPluginsFromDirectory(PluginsDir, PluginType.Enterprise);
}
///
/// 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)
{
List Plugins = new List();
Plugins.AddRange(ReadPluginsFromDirectory(DirectoryReference.Combine(ProjectDirectory, "Plugins"), PluginType.Project));
Plugins.AddRange(ReadPluginsFromDirectory(DirectoryReference.Combine(ProjectDirectory, "Mods"), PluginType.Mod));
return Plugins.AsReadOnly();
}
///
/// 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(DirectoryReference AdditionalDirectory)
{
return ReadPluginsFromDirectory(AdditionalDirectory, PluginType.External);
}
///
/// Read all the plugin descriptors under the given directory
///
/// The parent directory to look in.
/// The plugin type
/// Sequence of the found PluginInfo object.
public static IReadOnlyList ReadPluginsFromDirectory(DirectoryReference ParentDirectory, PluginType Type)
{
List Plugins;
if (!PluginInfoCache.TryGetValue(ParentDirectory, out Plugins))
{
Plugins = new List();
foreach (FileReference PluginFileName in EnumeratePlugins(ParentDirectory))
{
PluginInfo Plugin = new PluginInfo(PluginFileName, Type);
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();
DirectoryItem ParentDirectoryItem = DirectoryItem.GetItemByDirectoryReference(ParentDirectory);
if (ParentDirectoryItem.Exists)
{
using(ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue())
{
EnumeratePluginsInternal(ParentDirectoryItem, FileNames, Queue);
}
}
// Sort the filenames to ensure that the plugin order is deterministic; otherwise response files will change with each build.
FileNames = FileNames.OrderBy(x => x.FullName, StringComparer.OrdinalIgnoreCase).ToList();
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.
/// Queue for tasks to be executed
static void EnumeratePluginsInternal(DirectoryItem ParentDirectory, List FileNames, ThreadPoolWorkQueue Queue)
{
foreach (DirectoryItem ChildDirectory in ParentDirectory.EnumerateDirectories())
{
bool bSearchSubDirectories = true;
foreach (FileItem PluginFile in ChildDirectory.EnumerateFiles())
{
if(PluginFile.HasExtension(".uplugin"))
{
lock(FileNames)
{
FileNames.Add(PluginFile.Location);
}
bSearchSubDirectories = false;
}
}
if (bSearchSubDirectories)
{
Queue.Enqueue(() => EnumeratePluginsInternal(ChildDirectory, FileNames, Queue));
}
}
}
///
/// Determine if a plugin is enabled for a given project
///
/// The project to check. May be null.
/// Information about the plugin
/// The target platform
/// The target configuration
///
/// True if the plugin should be enabled for this project
public static bool IsPluginEnabledForProject(PluginInfo Plugin, ProjectDescriptor Project, UnrealTargetPlatform Platform, UnrealTargetConfiguration TargetConfiguration, TargetType Target)
{
if (!Plugin.Descriptor.SupportsTargetPlatform(Platform))
{
return false;
}
bool bEnabled = Plugin.EnabledByDefault;
if (Project != null && Project.Plugins != null)
{
foreach (PluginReferenceDescriptor PluginReference in Project.Plugins)
{
if (String.Compare(PluginReference.Name, Plugin.Name, true) == 0 && !PluginReference.bOptional)
{
bEnabled = PluginReference.IsEnabledForPlatform(Platform) && PluginReference.IsEnabledForTargetConfiguration(TargetConfiguration) && PluginReference.IsEnabledForTarget(Target);
}
}
}
return bEnabled;
}
}
}