Files
UnrealEngineUWP/Engine/Source/Runtime/Projects/Private/ModuleDescriptor.cpp
Jaroslaw Palczynski 6c305898e0 UE-8578: Slate Widget fails to compile once added to a new project
Slate Widget was failing, because of missing Slate dependencies. Testing introduced a couple of problems which all was fixed by this CL:
1. I introduced AdditionalDependencies in .uproject file and change "Add Code To Project..." procedure to fill this array if needed. UBT reads this field and builds the project with required modules. Needed for Slate classes.
2. Changed UHT to #include missing headers in generated.h files if it was missing an include for it's super class. It was causing problems if we were trying to add a subclass of BrushShape -- BrushShape.h didn't have #include "Brush.h" and UBrushShape was inheriting from UBrush.
3. Above problems also occured for Slate classes, but not all of them was UCLASSes, so I had to fixed that manually.
4. "Add Code To Project..." functionality was not invalidating UBT makefiles, which lead to omitting new source files during hot-reloading (even thought it was reporting a success). This change also should improve a bit performance, cause right now there is no "gathering" step -- there is only invalidate step which is a lot quicker.
5. Fixed "Selected Class Source" link to source class in Slate Widget and Slate Widget Style class.

#codereview Robert.Manuszewski

[CL 2481488 by Jaroslaw Palczynski in Main branch]
2015-03-17 09:34:18 -04:00

402 lines
11 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 Developer:
return TEXT( "Developer" );
case Editor:
return TEXT( "Editor" );
case EditorNoCommandlet:
return TEXT( "EditorNoCommandlet" );
case Program:
return TEXT("Program");
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::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;
}
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::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;
}
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::CheckModuleCompatbility(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