Files
UnrealEngineUWP/Engine/Source/Runtime/Projects/Private/ModuleDescriptor.cpp
Robert Manuszewski 97cc1e1be1 Copying //UE4/Dev-Core to //UE4/Main
==========================
MAJOR FEATURES + CHANGES
==========================

Change 2799478 on 2015/12/11 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Added Dev Stream custom versions.

	- Each stream now has its own custom version
	- Developers working in a stream should only modify their respective version

Change 2789867 on 2015/12/04 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	UnrealHeaderTool plugins no longer need to be a separate plugin and don't require UnrealHeaderTool source code changes to work.

	- Merged ScriptGeneratorPlugin with ScriptPlugin
	- Introduced the concept of UHT plugin support for plugins so that UHT's source files don't need to be modified to make it work with external plugins
	- Added RuntimeNoProgram module type (module that can be used at runtime but not by program targets).
	- Fixed logic with project file path setting in the engine. It will no longer try to crate a full path from an already rooted path.

Change 2796114 on 2015/12/09 by Steve.Robb@Dev-Core

	TEnumRange - enabled ranged-based for iteration over enum values.

Change 2789843 on 2015/12/04 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	More thorough way of verifying GC cluster assumptions.

Change 2794221 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	Don't merge GC clusters by default

Change 2797824 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Added the option to load all symbols for stack walking in non-monolithic builds.

Change 2790539 on 2015/12/04 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	Stats/Profiler - Better handling for live connection, using the same path as file profiles, added FStatsLoadedState to separate runtime stats state from the loaded one

Change 2794183 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Always reset events when returning them to pool.

Change 2794406 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	Fixing -unversioned flag being completely ignored when cooking

Change 2794563 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	Making sure string referenced assets don't get cooked if referenced by editor-only properties.

Change 2795124 on 2015/12/08 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	Profiler - Fixed bad data in min/max/avg inclusive times, added min/max/avg num calls, fixed event graph tooltip not displaying correct values

Change 2796208 on 2015/12/09 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	Profiler - Remove support for multiple instances in the profiler (game thread is always visible, a few crash fixes, cleaned code a bit)

Change 2797658 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Allocation verification helpers. Helps with tracking down memory stomps, freeing the same pointers multiple times.

Change 2797699 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Fix incorrect asset loading in Cooked game data (by bozaro)

	PR #1844

Change 2798173 on 2015/12/10 by Steve.Robb@Dev-Core

	Migration of Fortnite to use engine's TEnumRange.

Change 2798217 on 2015/12/10 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	PR #1331
	[Core] Added a stomp allocator that allows finding memory overruns, underruns, and read/write after free. (Contributed by Pablo Zurita)

Change 2799605 on 2015/12/11 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Fixing a crash when cancelling async loading caused by detaching linker from objects that had RF_NeedLoad flag set.

Change 2799849 on 2015/12/11 by Steve.Robb@Dev-Core

	Migration of Ocean to use engine's TEnumRange.

Change 2803144 on 2015/12/15 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Changed export tagging archive to also serialize class default objects using the normal Serialize path so that it can collect all custom versions used by exports.

Change 2803206 on 2015/12/15 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	#jira UE-24177
	Audit ôshippingö defines in engine

Change 2804868 on 2015/12/16 by Steve.Robb@Dev-Core

	Removal of stats from MallocBinned2, to be readded later.

#lockdown Nick.Penwarden

[CL 2805158 by Robert Manuszewski in Main branch]
2015-12-16 11:52:36 -05:00

425 lines
12 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "ProjectsPrivatePCH.h"
#define LOCTEXT_NAMESPACE "ModuleDescriptor"
ELoadingPhase::Type ELoadingPhase::FromString( const TCHAR *String )
{
ELoadingPhase::Type TestType = (ELoadingPhase::Type)0;
for(; TestType < ELoadingPhase::Max; TestType = (ELoadingPhase::Type)(TestType + 1))
{
const TCHAR *TestString = ToString(TestType);
if(FCString::Stricmp(String, TestString) == 0)
{
break;
}
}
return TestType;
}
const TCHAR* ELoadingPhase::ToString( const ELoadingPhase::Type Value )
{
switch( Value )
{
case Default:
return TEXT( "Default" );
case PostDefault:
return TEXT( "PostDefault" );
case PreDefault:
return TEXT( "PreDefault" );
case PostConfigInit:
return TEXT( "PostConfigInit" );
case PreLoadingScreen:
return TEXT( "PreLoadingScreen" );
case PostEngineInit:
return TEXT( "PostEngineInit" );
default:
ensureMsgf( false, TEXT( "Unrecognized ELoadingPhase value: %i" ), Value );
return NULL;
}
}
EHostType::Type EHostType::FromString( const TCHAR *String )
{
EHostType::Type TestType = (EHostType::Type)0;
for(; TestType < EHostType::Max; TestType = (EHostType::Type)(TestType + 1))
{
const TCHAR *TestString = ToString(TestType);
if(FCString::Stricmp(String, TestString) == 0)
{
break;
}
}
return TestType;
}
const TCHAR* EHostType::ToString( const EHostType::Type Value )
{
switch( Value )
{
case Runtime:
return TEXT( "Runtime" );
case RuntimeNoCommandlet:
return TEXT( "RuntimeNoCommandlet" );
case RuntimeNoProgram:
return TEXT("RuntimeNoProgram");
case Developer:
return TEXT( "Developer" );
case Editor:
return TEXT( "Editor" );
case EditorNoCommandlet:
return TEXT( "EditorNoCommandlet" );
case Program:
return TEXT("Program");
case ServerOnly:
return TEXT("ServerOnly");
default:
ensureMsgf( false, TEXT( "Unrecognized EModuleType value: %i" ), Value );
return NULL;
}
}
FModuleDescriptor::FModuleDescriptor(const FName InName, EHostType::Type InType, ELoadingPhase::Type InLoadingPhase)
: Name(InName)
, Type(InType)
, LoadingPhase(InLoadingPhase)
{
}
bool FModuleDescriptor::Read(const FJsonObject& Object, FText& OutFailReason)
{
// Read the module name
TSharedPtr<FJsonValue> NameValue = Object.TryGetField(TEXT("Name"));
if(!NameValue.IsValid() || NameValue->Type != EJson::String)
{
OutFailReason = LOCTEXT("ModuleWithoutAName", "Found a 'Module' entry with a missing 'Name' field");
return false;
}
Name = FName(*NameValue->AsString());
// Read the module type
TSharedPtr<FJsonValue> TypeValue = Object.TryGetField(TEXT("Type"));
if(!TypeValue.IsValid() || TypeValue->Type != EJson::String)
{
OutFailReason = FText::Format( LOCTEXT( "ModuleWithoutAType", "Found Module entry '{0}' with a missing 'Type' field" ), FText::FromName(Name) );
return false;
}
Type = EHostType::FromString(*TypeValue->AsString());
if(Type == EHostType::Max)
{
OutFailReason = FText::Format( LOCTEXT( "ModuleWithInvalidType", "Module entry '{0}' specified an unrecognized module Type '{1}'" ), FText::FromName(Name), FText::FromString(TypeValue->AsString()) );
return false;
}
// Read the loading phase
TSharedPtr<FJsonValue> LoadingPhaseValue = Object.TryGetField(TEXT("LoadingPhase"));
if(LoadingPhaseValue.IsValid() && LoadingPhaseValue->Type == EJson::String)
{
LoadingPhase = ELoadingPhase::FromString(*LoadingPhaseValue->AsString());
if(LoadingPhase == ELoadingPhase::Max)
{
OutFailReason = FText::Format( LOCTEXT( "ModuleWithInvalidLoadingPhase", "Module entry '{0}' specified an unrecognized module LoadingPhase '{1}'" ), FText::FromName(Name), FText::FromString(LoadingPhaseValue->AsString()) );
return false;
}
}
// Read the whitelisted platforms
TSharedPtr<FJsonValue> WhitelistPlatformsValue = Object.TryGetField(TEXT("WhitelistPlatforms"));
if(WhitelistPlatformsValue.IsValid() && WhitelistPlatformsValue->Type == EJson::Array)
{
const TArray< TSharedPtr< FJsonValue > >& PlatformsArray = WhitelistPlatformsValue->AsArray();
for(int Idx = 0; Idx < PlatformsArray.Num(); Idx++)
{
WhitelistPlatforms.Add(PlatformsArray[Idx]->AsString());
}
}
// Read the blacklisted platforms
TSharedPtr<FJsonValue> BlacklistPlatformsValue = Object.TryGetField(TEXT("BlacklistPlatforms"));
if(BlacklistPlatformsValue.IsValid() && BlacklistPlatformsValue->Type == EJson::Array)
{
const TArray< TSharedPtr< FJsonValue > >& PlatformsArray = BlacklistPlatformsValue->AsArray();
for(int Idx = 0; Idx < PlatformsArray.Num(); Idx++)
{
BlacklistPlatforms.Add(PlatformsArray[Idx]->AsString());
}
}
// Read the additional dependencies
TSharedPtr<FJsonValue> AdditionalDependenciesValue = Object.TryGetField(TEXT("AdditionalDependencies"));
if (AdditionalDependenciesValue.IsValid() && AdditionalDependenciesValue->Type == EJson::Array)
{
const TArray< TSharedPtr< FJsonValue > >& DepArray = AdditionalDependenciesValue->AsArray();
for (int Idx = 0; Idx < DepArray.Num(); Idx++)
{
AdditionalDependencies.Add(DepArray[Idx]->AsString());
}
}
return true;
}
bool FModuleDescriptor::ReadArray(const FJsonObject& Object, const TCHAR* Name, TArray<FModuleDescriptor>& OutModules, FText& OutFailReason)
{
bool bResult = true;
TSharedPtr<FJsonValue> ModulesArrayValue = Object.TryGetField(Name);
if(ModulesArrayValue.IsValid() && ModulesArrayValue->Type == EJson::Array)
{
const TArray< TSharedPtr< FJsonValue > >& ModulesArray = ModulesArrayValue->AsArray();
for(int Idx = 0; Idx < ModulesArray.Num(); Idx++)
{
const TSharedPtr<FJsonValue>& ModuleValue = ModulesArray[Idx];
if(ModuleValue.IsValid() && ModuleValue->Type == EJson::Object)
{
FModuleDescriptor Descriptor;
if(Descriptor.Read(*ModuleValue->AsObject().Get(), OutFailReason))
{
OutModules.Add(Descriptor);
}
else
{
bResult = false;
}
}
else
{
OutFailReason = LOCTEXT( "ModuleWithInvalidModulesArray", "The 'Modules' array has invalid contents and was not able to be loaded." );
bResult = false;
}
}
}
return bResult;
}
void FModuleDescriptor::Write(TJsonWriter<>& Writer) const
{
Writer.WriteObjectStart();
Writer.WriteValue(TEXT("Name"), Name.ToString());
Writer.WriteValue(TEXT("Type"), FString(EHostType::ToString(Type)));
Writer.WriteValue(TEXT("LoadingPhase"), FString(ELoadingPhase::ToString(LoadingPhase)));
if (WhitelistPlatforms.Num() > 0)
{
Writer.WriteArrayStart(TEXT("WhitelistPlatforms"));
for(int Idx = 0; Idx < WhitelistPlatforms.Num(); Idx++)
{
Writer.WriteValue(WhitelistPlatforms[Idx]);
}
Writer.WriteArrayEnd();
}
if (BlacklistPlatforms.Num() > 0)
{
Writer.WriteArrayStart(TEXT("BlacklistPlatforms"));
for(int Idx = 0; Idx < BlacklistPlatforms.Num(); Idx++)
{
Writer.WriteValue(BlacklistPlatforms[Idx]);
}
Writer.WriteArrayEnd();
}
if (AdditionalDependencies.Num() > 0)
{
Writer.WriteArrayStart(TEXT("AdditionalDependencies"));
for (int Idx = 0; Idx < AdditionalDependencies.Num(); Idx++)
{
Writer.WriteValue(AdditionalDependencies[Idx]);
}
Writer.WriteArrayEnd();
}
Writer.WriteObjectEnd();
}
void FModuleDescriptor::WriteArray(TJsonWriter<>& Writer, const TCHAR* Name, const TArray<FModuleDescriptor>& Modules)
{
if(Modules.Num() > 0)
{
Writer.WriteArrayStart(Name);
for(int Idx = 0; Idx < Modules.Num(); Idx++)
{
Modules[Idx].Write(Writer);
}
Writer.WriteArrayEnd();
}
}
bool FModuleDescriptor::IsCompiledInCurrentConfiguration() const
{
// Cache the string for the current platform
static FString UBTPlatform(FPlatformMisc::GetUBTPlatform());
// Check the platform is whitelisted
if(WhitelistPlatforms.Num() > 0 && !WhitelistPlatforms.Contains(UBTPlatform))
{
return false;
}
// Check the platform is not blacklisted
if(BlacklistPlatforms.Num() > 0 && BlacklistPlatforms.Contains(UBTPlatform))
{
return false;
}
// Check the module is compatible with this target. This should match UEBuildTarget.ShouldIncludePluginModule in UBT
switch (Type)
{
case EHostType::Runtime:
case EHostType::RuntimeNoCommandlet:
return true;
case EHostType::RuntimeNoProgram:
#if IS_PROGRAM
return false;
#else
return true;
#endif
case EHostType::Developer:
#if WITH_UNREAL_DEVELOPER_TOOLS
return true;
#endif
break;
case EHostType::Editor:
case EHostType::EditorNoCommandlet:
#if WITH_EDITOR
return true;
#endif
break;
case EHostType::Program:
#if IS_PROGRAM
return true;
#endif
break;
case EHostType::ServerOnly:
return !FPlatformProperties::IsClientOnly();
}
return false;
}
bool FModuleDescriptor::IsLoadedInCurrentConfiguration() const
{
// Check that the module is built for this configuration
if(!IsCompiledInCurrentConfiguration())
{
return false;
}
// Check that the runtime environment allows it to be loaded
switch (Type)
{
case EHostType::RuntimeNoProgram:
#if IS_PROGRAM
return false;
#endif // else fall through to EHostType::Runtime below
case EHostType::Runtime:
#if (WITH_ENGINE || WITH_PLUGIN_SUPPORT)
return true;
#endif
break;
case EHostType::RuntimeNoCommandlet:
#if WITH_ENGINE || WITH_PLUGIN_SUPPORT
if(!IsRunningCommandlet()) return true;
#endif
break;
case EHostType::Developer:
#if WITH_UNREAL_DEVELOPER_TOOLS
return true;
#endif
break;
case EHostType::Editor:
#if WITH_EDITOR
if(GIsEditor) return true;
#endif
break;
case EHostType::EditorNoCommandlet:
#if WITH_EDITOR
if(GIsEditor && !IsRunningCommandlet()) return true;
#endif
break;
case EHostType::Program:
#if WITH_PLUGIN_SUPPORT && IS_PROGRAM
return true;
#endif
break;
case EHostType::ServerOnly:
return !FPlatformProperties::IsClientOnly();
}
return false;
}
void FModuleDescriptor::LoadModulesForPhase(ELoadingPhase::Type LoadingPhase, const TArray<FModuleDescriptor>& Modules, TMap<FName, EModuleLoadResult>& ModuleLoadErrors)
{
FScopedSlowTask SlowTask(Modules.Num());
for(int Idx = 0; Idx < Modules.Num(); Idx++)
{
SlowTask.EnterProgressFrame(1);
const FModuleDescriptor& Descriptor = Modules[Idx];
// Don't need to do anything if this module is already loaded
if( !FModuleManager::Get().IsModuleLoaded( Descriptor.Name ) )
{
if( LoadingPhase == Descriptor.LoadingPhase && Descriptor.IsLoadedInCurrentConfiguration() )
{
// @todo plugin: DLL search problems. Plugins that statically depend on other modules within this plugin may not be found? Need to test this.
// NOTE: Loading this module may cause other modules to become loaded, both in the engine or game, or other modules
// that are part of this project or plugin. That's totally fine.
EModuleLoadResult FailureReason;
const TSharedPtr<IModuleInterface>& ModuleInterface = FModuleManager::Get().LoadModuleWithFailureReason( Descriptor.Name, FailureReason );
if( ModuleInterface.IsValid() )
{
// Module loaded OK (or was already loaded.)
}
else
{
// The module failed to load. Note this in the ModuleLoadErrors list.
ModuleLoadErrors.Add(Descriptor.Name, FailureReason);
}
}
}
}
}
bool FModuleDescriptor::CheckModuleCompatibility(const TArray<FModuleDescriptor>& Modules, bool bGameModules, TArray<FString>& OutIncompatibleFiles)
{
bool bResult = true;
for(int Idx = 0; Idx < Modules.Num(); Idx++)
{
const FModuleDescriptor &Module = Modules[Idx];
if (Module.IsCompiledInCurrentConfiguration() && !FModuleManager::Get().IsModuleUpToDate(Module.Name))
{
OutIncompatibleFiles.Add(FModuleManager::GetCleanModuleFilename(Module.Name, bGameModules));
bResult = false;
}
}
return bResult;
}
#undef LOCTEXT_NAMESPACE