Files
UnrealEngineUWP/Engine/Source/Developer/BlueprintNativeCodeGen/Private/BlueprintNativeCodeGenModule.cpp
Mike Beach 73c6bbc883 Copying //UE4/Dev-Blueprints to Dev-Main (//UE4/Dev-Main)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2842642 on 2016/01/25 by Maciej.Mroz

	Blueprint C++ Conversion: fixed dependency (headers) list builder

Change 2842648 on 2016/01/25 by Maciej.Mroz

	AssetPtr has implicit constructor from StringReference

Change 2842652 on 2016/01/25 by Maciej.Mroz

	Minor improvements in Orion headers. Necessary to compile the project with converted Blueprints

Change 2842653 on 2016/01/25 by Maciej.Mroz

	Blueprint C++ Conversion:
	Split UberGraph into subfunctions

Change 2843917 on 2016/01/26 by Michael.Schoell

	Replacing variable nodes with other variable nodes will now correctly mark the Blueprint as structurally modified.

	#jira UE-24925 - Using "Replace variable with..." does not mark a blueprint as needing compilation

Change 2844300 on 2016/01/26 by Maciej.Mroz

	Blueprint C++ Conversion: improvements in constructor
	- UProperties for inaccessigle variables are reused
	- Arrays of structs use "StaticStruct()->InitializeStruct.." and diff-serialization.

Change 2845536 on 2016/01/27 by Ben.Cosh

	Refactor of the Blueprint Profiler core to enable execution wire heatmaps.

	Still to do:

	- Sequence node handling - spotted as a bug last minute
	- Some functions still require a refactor, I'll pick this is up in next changes
	- Alternative statistic display widgets
	- enable blueprint breakpoints when profiling?! not sure we want this but adding for visibility

Change 2845619 on 2016/01/27 by Michael.Schoell

	BP-Version bump to resolve TODO in K2Node_Event.

Change 2845824 on 2016/01/27 by Michael.Schoell

	BP-Version bump to resolve TODO in K2Node_FunctionEntry.

Change 2847390 on 2016/01/28 by Maciej.Mroz

	AssetPtr constructor from StringReference in explicit

Change 2847894 on 2016/01/28 by Maciej.Mroz

	Blueprint C++ Conversion:
	Fixed pathologically included headers.

Change 2848662 on 2016/01/29 by Ben.Cosh

	Fix for problems closing the blueprint editor introduced with CL 2845536

	#UE-26153 - Unable to open the same blueprint after closing blueprint editor.
	#UE-26090 - Crash when closing the editor with the blueprint editor open

Change 2848922 on 2016/01/29 by Maciej.Mroz

	Blueprint C++ Conversion:
	Removed unnecessary switch and StateStack in ubergraph subfunctions

Change 2848934 on 2016/01/29 by Maciej.Mroz

	 FEnumEditorUtils::EnsureAllDisplayNamesExist modifies UUserDefinedEnum::DisplayNames only when it's necessary
	It should fix the "Saving FText XXX which has been initialized from FString at cook time " warning

Change 2849251 on 2016/01/29 by Michael.Schoell

	Fixed issues with the "Set Members..." node for structs not providing modified literals and other by-value data in the output connection.

	Reworked the compiler to handle terms with bPassedByReference in new ways. Variables will always mark their terms as bPassedByReference and the "Set Members..." node will leverage the value to know whether it needs to create a local output variable on the node or forward the reference.

	Break nodes will adapt their output terms to reflect the state of the input term, effectively forwarding the bPassedByReference state.

	#jira UE-24451 - "Set Members..." node does not return by-ref as expected when the input pin is connected to a literal (or otherwise truly by-value) pin.

Change 2849263 on 2016/01/29 by Michael.Schoell

	Submit for missing file from CL# 2849251

Change 2849269 on 2016/01/29 by Michael.Schoell

	Improvements to localization support in SBlueprintPalette (items in the MyBlueprint window as well as the BP context menu when selecting nodes).

Change 2849925 on 2016/01/29 by Mike.Beach

	Hanlding deferred loading placeholder-classes in UObjectPropertyBase::CheckValidObject (considering placeholder objects valid, when loading with deferring is enabled).

Change 2850484 on 2016/01/31 by Maciej.Mroz

	Fixed crash when converting Widget Blueprint

Change 2850485 on 2016/01/31 by Maciej.Mroz

	Blueprint C++ Conversion:
	KCST_UnconditionalGoto requires switch, when it's generated by UK2Node_ExecutionSequence

Change 2850859 on 2016/02/01 by Ben.Cosh

	Fix for issue with debug instance filter causing an out of bounds access on the debug function stack array.
	#UE-25552 - Debugging specific instances of Fortnite's player pawn generic bp causes crashes
	#Proj UnrealEd

Change 2850997 on 2016/02/01 by Maciej.Mroz

	Blueprint C++ Conversion:
	Fixed a lot of errors caused by cl#2842642

Change 2851965 on 2016/02/01 by Mike.Beach

	Preventing pin watches from saving/retaining old split pins that have since been deleted (recombine).

	#jira  UE-26299

[CL 2865780 by Mike Beach in Main branch]
2016-02-12 17:00:45 -05:00

662 lines
21 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "BlueprintNativeCodeGenPCH.h"
#include "AssetRegistryModule.h"
#include "BlueprintNativeCodeGenManifest.h"
#include "BlueprintNativeCodeGenModule.h"
#include "BlueprintNativeCodeGenUtils.h"
#include "IBlueprintCompilerCppBackendModule.h"
#include "BlueprintEditorUtils.h"
#include "Engine/Blueprint.h" // for EBlueprintType
/*******************************************************************************
* NativizationCookControllerImpl
******************************************************************************/
namespace NativizationCookControllerImpl
{
// If you change this plugin name you must update logic in CookCommand.Automation.cs
static const TCHAR* DefaultPluginName = TEXT("NativizedAssets");
}
/*******************************************************************************
* FBlueprintNativeCodeGenModule
******************************************************************************/
class FBlueprintNativeCodeGenModule : public IBlueprintNativeCodeGenModule
, public IBlueprintNativeCodeGenCore
{
public:
FBlueprintNativeCodeGenModule()
{
}
//~ Begin IBlueprintNativeCodeGenModule interface
virtual void Convert(UPackage* Package, ESavePackageResult ReplacementType, const TCHAR* PlatformName) override;
virtual void SaveManifest(int32 Id = -1) override;
virtual void MergeManifest(int32 ManifestIdentifier) override;
virtual void FinalizeManifest() override;
virtual void GenerateStubs() override;
virtual void GenerateFullyConvertedClasses() override;
virtual void MarkUnconvertedBlueprintAsNecessary(TAssetPtr<UBlueprint> BPPtr) override;
virtual const TMultiMap<FName, TAssetSubclassOf<UObject>>& GetFunctionsBoundToADelegate() override;
protected:
virtual void Initialize(const FNativeCodeGenInitData& InitData) override;
virtual void InitializeForRerunDebugOnly(const TArray< TPair< FString, FString > >& CodegenTargets) override;
//~ End IBlueprintNativeCodeGenModule interface
//~ Begin FScriptCookReplacmentCoordinator interface
virtual EReplacementResult IsTargetedForReplacement(const UPackage* Package) const override;
virtual EReplacementResult IsTargetedForReplacement(const UObject* Object) const override;
virtual UClass* FindReplacedClass(const UClass* Class) const override;
//~ End FScriptCookReplacmentCoordinator interface
private:
void ReadConfig();
void FillTargetedForReplacementQuery();
void FillIsFunctionUsedInADelegate();
FBlueprintNativeCodeGenManifest& GetManifest(const TCHAR* PlatformName);
void GenerateSingleStub(UBlueprint* BP, const TCHAR* PlatformName);
void CollectBoundFunctions(UBlueprint* BP);
void GenerateSingleAsset(UField* ForConversion, const TCHAR* PlatformName);
TMap< FString, TUniquePtr<FBlueprintNativeCodeGenManifest> > Manifests;
TArray<FString> ExcludedAssetTypes;
TArray<TAssetSubclassOf<UBlueprint>> ExcludedBlueprintTypes;
TSet<FStringAssetReference> ExcludedAssets;
TArray<FString> TargetPlatformNames;
// A stub-wrapper must be generated only if the BP is really accessed/required by some other generated code.
TSet<TAssetPtr<UBlueprint>> StubsRequiredByGeneratedCode;
TSet<TAssetPtr<UBlueprint>> AllPotentialStubs;
TSet<TAssetPtr<UBlueprint>> ToGenerate;
TMultiMap<FName, TAssetSubclassOf<UObject>> FunctionsBoundToADelegate; // is a function could be bound to a delegate, then it must have UFUNCTION macro. So we cannot optimize it.
};
void FBlueprintNativeCodeGenModule::ReadConfig()
{
GConfig->GetArray(TEXT("BlueprintNativizationSettings"), TEXT("ExcludedAssetTypes"), ExcludedAssetTypes, GEditorIni);
{
TArray<FString> ExcludedBlueprintTypesPath;
GConfig->GetArray(TEXT("BlueprintNativizationSettings"), TEXT("ExcludedBlueprintTypes"), ExcludedBlueprintTypesPath, GEditorIni);
for (FString& Path : ExcludedBlueprintTypesPath)
{
TAssetSubclassOf<UBlueprint> ClassPtr;
ClassPtr = FStringAssetReference(Path);
ClassPtr.LoadSynchronous();
ExcludedBlueprintTypes.Add(ClassPtr);
}
}
TArray<FString> ExcludedAssetPaths;
GConfig->GetArray(TEXT("BlueprintNativizationSettings"), TEXT("ExcludedAssets"), ExcludedAssetPaths, GEditorIni);
for (FString& Path : ExcludedAssetPaths)
{
ExcludedAssets.Add(FStringAssetReference(Path));
}
}
void FBlueprintNativeCodeGenModule::MarkUnconvertedBlueprintAsNecessary(TAssetPtr<UBlueprint> BPPtr)
{
StubsRequiredByGeneratedCode.Add(BPPtr);
}
void FBlueprintNativeCodeGenModule::FillTargetedForReplacementQuery()
{
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
auto& ConversionQueryDelegate = BackEndModule.OnIsTargetedForConversionQuery();
auto ShouldConvert = [](const UObject* AssetObj)
{
if (ensure(IBlueprintNativeCodeGenCore::Get()))
{
EReplacementResult ReplacmentResult = IBlueprintNativeCodeGenCore::Get()->IsTargetedForReplacement(AssetObj);
return ReplacmentResult == EReplacementResult::ReplaceCompletely;
}
return false;
};
ConversionQueryDelegate.BindStatic(ShouldConvert);
auto LocalMarkUnconvertedBlueprintAsNecessary = [](TAssetPtr<UBlueprint> BPPtr)
{
IBlueprintNativeCodeGenModule::Get().MarkUnconvertedBlueprintAsNecessary(BPPtr);
};
BackEndModule.OnIncludingUnconvertedBP().BindStatic(LocalMarkUnconvertedBlueprintAsNecessary);
}
namespace
{
void GetFieldFormPackage(const UPackage* Package, UStruct*& OutStruct, UEnum*& OutEnum, EObjectFlags ExcludedFlags = RF_Transient)
{
TArray<UObject*> Objects;
GetObjectsWithOuter(Package, Objects, false);
for (auto Entry : Objects)
{
if (Entry->HasAnyFlags(ExcludedFlags))
{
continue;
}
if (UClass* AsClass = Cast<UClass>(Entry))
{
// Not a placeholder
if (AsClass->GetName().Contains(TEXT("PLACEHOLDER")))
{
continue;
}
// Not a skeleton class
if (UBlueprint* GeneratingBP = Cast<UBlueprint>(AsClass->ClassGeneratedBy))
{
if (AsClass != GeneratingBP->GeneratedClass)
{
continue;
}
}
}
OutStruct = Cast<UStruct>(Entry);
if (OutStruct)
{
break;
}
OutEnum = Cast<UEnum>(Entry);
if (OutEnum)
{
break;
}
}
}
}
void FBlueprintNativeCodeGenModule::CollectBoundFunctions(UBlueprint* BP)
{
TArray<UFunction*> Functions = IBlueprintCompilerCppBackendModule::CollectBoundFunctions(BP);
for (UFunction* Func : Functions)
{
if (Func)
{
FunctionsBoundToADelegate.AddUnique(Func->GetFName(), Func->GetOwnerClass());
}
}
}
const TMultiMap<FName, TAssetSubclassOf<UObject>>& FBlueprintNativeCodeGenModule::GetFunctionsBoundToADelegate()
{
return FunctionsBoundToADelegate;
}
void FBlueprintNativeCodeGenModule::FillIsFunctionUsedInADelegate()
{
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
auto IsFunctionUsed = [](const UFunction* InFunction) -> bool
{
auto& TargetFunctionsBoundToADelegate = IBlueprintNativeCodeGenModule::Get().GetFunctionsBoundToADelegate();
return InFunction && (nullptr != TargetFunctionsBoundToADelegate.FindPair(InFunction->GetFName(), InFunction->GetOwnerClass()));
};
BackEndModule.GetIsFunctionUsedInADelegateCallback().BindStatic(IsFunctionUsed);
}
void FBlueprintNativeCodeGenModule::Initialize(const FNativeCodeGenInitData& InitData)
{
ReadConfig();
IBlueprintNativeCodeGenCore::Register(this);
// Each platform will need a manifest, because each platform could cook different assets:
for (auto& Platform : InitData.CodegenTargets)
{
const TCHAR* TargetDirectory = *Platform.Value;
FString OutputPath = FPaths::Combine(TargetDirectory, NativizationCookControllerImpl::DefaultPluginName);
Manifests.Add(FString(*Platform.Key), TUniquePtr<FBlueprintNativeCodeGenManifest>(new FBlueprintNativeCodeGenManifest(NativizationCookControllerImpl::DefaultPluginName, OutputPath)));
TargetPlatformNames.Add(Platform.Key);
}
FillTargetedForReplacementQuery();
FillIsFunctionUsedInADelegate();
}
void FBlueprintNativeCodeGenModule::InitializeForRerunDebugOnly(const TArray< TPair< FString, FString > >& CodegenTargets)
{
ReadConfig();
IBlueprintNativeCodeGenCore::Register(this);
FillTargetedForReplacementQuery();
FillIsFunctionUsedInADelegate();
for (const auto& Platform : CodegenTargets)
{
// load the old manifest:
const TCHAR* TargetDirectory = *Platform.Value;
FString OutputPath = FPaths::Combine(TargetDirectory, NativizationCookControllerImpl::DefaultPluginName, *FBlueprintNativeCodeGenPaths::GetDefaultManifestPath());
Manifests.Add(FString(*Platform.Key), TUniquePtr<FBlueprintNativeCodeGenManifest>(new FBlueprintNativeCodeGenManifest(FPaths::ConvertRelativePathToFull(OutputPath))));
//FBlueprintNativeCodeGenManifest OldManifest(FPaths::ConvertRelativePathToFull(OutputPath));
// reconvert every assets listed in the manifest:
for (const auto& ConversionTarget : GetManifest(*Platform.Key).GetConversionRecord())
{
// load the package:
UPackage* Package = LoadPackage(nullptr, *ConversionTarget.Value.TargetObjPath, LOAD_None);
if (!Package)
{
UE_LOG(LogBlueprintCodeGen, Error, TEXT("Unable to load the package: %s"), *ConversionTarget.Value.TargetObjPath);
continue;
}
// reconvert it
Convert(Package, ESavePackageResult::ReplaceCompletely, *Platform.Key);
}
// reconvert every unconverted dependency listed in the manifest:
for (const auto& ConversionTarget : GetManifest(*Platform.Key).GetUnconvertedDependencies())
{
// load the package:
UPackage* Package = LoadPackage(nullptr, *ConversionTarget.Key.GetPlainNameString(), LOAD_None);
UStruct* Struct = nullptr;
UEnum* Enum = nullptr;
GetFieldFormPackage(Package, Struct, Enum);
UBlueprint* BP = Cast<UBlueprint>(CastChecked<UClass>(Struct)->ClassGeneratedBy);
if (ensure(BP))
{
CollectBoundFunctions(BP);
GenerateSingleStub(BP, *Platform.Key);
}
}
for (TAssetPtr<UBlueprint>& BPPtr : ToGenerate)
{
UBlueprint* BP = BPPtr.LoadSynchronous();
if (ensure(BP))
{
GenerateSingleAsset(BP->GeneratedClass, *Platform.Key);
}
}
}
}
void FBlueprintNativeCodeGenModule::GenerateFullyConvertedClasses()
{
for (TAssetPtr<UBlueprint>& BPPtr : ToGenerate)
{
UBlueprint* BP = BPPtr.LoadSynchronous();
if (ensure(BP))
{
for (const FString& PlatformName : TargetPlatformNames)
{
GenerateSingleAsset(BP->GeneratedClass, *PlatformName);
}
}
}
}
FBlueprintNativeCodeGenManifest& FBlueprintNativeCodeGenModule::GetManifest(const TCHAR* PlatformName)
{
FString PlatformNameStr(PlatformName);
TUniquePtr<FBlueprintNativeCodeGenManifest>* Result = Manifests.Find(PlatformNameStr);
check(Result->IsValid());
return **Result;
}
void FBlueprintNativeCodeGenModule::GenerateSingleStub(UBlueprint* BP, const TCHAR* PlatformName)
{
UClass* Class = BP ? BP->GeneratedClass : nullptr;
if (!ensure(Class))
{
return;
}
// no PCHFilename should be necessary
const IAssetRegistry& Registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
FAssetData AssetInfo = Registry.GetAssetByObjectPath(*Class->GetPathName());
FString FileContents;
TUniquePtr<IBlueprintCompilerCppBackend> Backend_CPP(IBlueprintCompilerCppBackendModuleInterface::Get().Create());
// Apparently we can only generate wrappers for classes, so any logic that results in non classes requesting
// wrappers will fail here:
FileContents = Backend_CPP->GenerateWrapperForClass(Class);
if (!FileContents.IsEmpty())
{
FFileHelper::SaveStringToFile(FileContents, *(GetManifest(PlatformName).CreateUnconvertedDependencyRecord(AssetInfo.PackageName, AssetInfo).GeneratedWrapperPath));
}
// The stub we generate still may have dependencies on other modules, so make sure the module dependencies are
// still recorded so that the .build.cs is generated correctly. Without this you'll get include related errors
// (or possibly linker errors) in stub headers:
GetManifest(PlatformName).GatherModuleDependencies(BP->GetOutermost());
}
void FBlueprintNativeCodeGenModule::GenerateSingleAsset(UField* ForConversion, const TCHAR* PlatformName)
{
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
auto& BackendPCHQuery = BackEndModule.OnPCHFilenameQuery();
const IAssetRegistry& Registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
FAssetData AssetInfo = Registry.GetAssetByObjectPath(*ForConversion->GetPathName());
FBlueprintNativeCodeGenPaths TargetPaths = GetManifest(PlatformName).GetTargetPaths();
BackendPCHQuery.BindLambda([TargetPaths]()->FString
{
return TargetPaths.RuntimePCHFilename();
});
FConvertedAssetRecord& ConversionRecord = GetManifest(PlatformName).CreateConversionRecord(*ForConversion->GetPathName(), AssetInfo);
TSharedPtr<FString> HeaderSource(new FString());
TSharedPtr<FString> CppSource(new FString());
FBlueprintNativeCodeGenUtils::GenerateCppCode(ForConversion, HeaderSource, CppSource);
bool bSuccess = !HeaderSource->IsEmpty() || !CppSource->IsEmpty();
// Run the cpp first, because we cue off of the presence of a header for a valid conversion record (see
// FConvertedAssetRecord::IsValid)
if (!CppSource->IsEmpty())
{
if (!FFileHelper::SaveStringToFile(*CppSource, *ConversionRecord.GeneratedCppPath))
{
bSuccess &= false;
ConversionRecord.GeneratedCppPath.Empty();
}
CppSource->Empty(CppSource->Len());
}
else
{
ConversionRecord.GeneratedCppPath.Empty();
}
if (bSuccess && !HeaderSource->IsEmpty())
{
if (!FFileHelper::SaveStringToFile(*HeaderSource, *ConversionRecord.GeneratedHeaderPath))
{
bSuccess &= false;
ConversionRecord.GeneratedHeaderPath.Empty();
}
HeaderSource->Empty(HeaderSource->Len());
}
else
{
ConversionRecord.GeneratedHeaderPath.Empty();
}
check(bSuccess);
if (bSuccess)
{
GetManifest(PlatformName).GatherModuleDependencies(ForConversion->GetOutermost());
}
BackendPCHQuery.Unbind();
}
void FBlueprintNativeCodeGenModule::GenerateStubs()
{
TSet<TAssetPtr<UBlueprint>> AlreadyGenerated;
while (AlreadyGenerated.Num() < StubsRequiredByGeneratedCode.Num())
{
const int32 OldGeneratedNum = AlreadyGenerated.Num();
for (TAssetPtr<UBlueprint>& BPPtr : StubsRequiredByGeneratedCode)
{
bool bAlreadyGenerated = false;
AlreadyGenerated.Add(BPPtr, &bAlreadyGenerated);
if (bAlreadyGenerated)
{
continue;
}
ensureMsgf(AllPotentialStubs.Contains(BPPtr), TEXT("A required blueprint doesn't generate stub: %s"), *BPPtr.ToString());
for (auto& PlatformName : TargetPlatformNames)
{
GenerateSingleStub(BPPtr.LoadSynchronous(), *PlatformName);
}
}
if (!ensure(OldGeneratedNum != AlreadyGenerated.Num()))
{
break;
}
}
UE_LOG(LogBlueprintCodeGen, Log, TEXT("GenerateStubs - all unconverted bp: %d, generated wrapers: %d"), AllPotentialStubs.Num(), StubsRequiredByGeneratedCode.Num());
}
void FBlueprintNativeCodeGenModule::Convert(UPackage* Package, ESavePackageResult CookResult, const TCHAR* PlatformName)
{
// Find the struct/enum to convert:
UStruct* Struct = nullptr;
UEnum* Enum = nullptr;
GetFieldFormPackage(Package, Struct, Enum);
// First we gather information about bound functions.
UClass* AsClass = Cast<UClass>(Struct);
UBlueprint* BP = AsClass ? Cast<UBlueprint>(AsClass->ClassGeneratedBy) : nullptr;
if (BP)
{
CollectBoundFunctions(BP);
}
if (CookResult != ESavePackageResult::ReplaceCompletely && CookResult != ESavePackageResult::GenerateStub)
{
// nothing to convert
return;
}
if (Struct == nullptr && Enum == nullptr)
{
ensure(false);
return;
}
if (CookResult == ESavePackageResult::GenerateStub)
{
if (ensure(BP))
{
ensure(!ToGenerate.Contains(BP));
AllPotentialStubs.Add(BP);
}
}
else
{
check(CookResult == ESavePackageResult::ReplaceCompletely);
if (AsClass)
{
if (ensure(BP))
{
ensure(!AllPotentialStubs.Contains(BP));
ToGenerate.Add(BP);
}
}
else
{
UField* ForConversion = Struct ? (UField*)Struct : (UField*)Enum;
GenerateSingleAsset(ForConversion, PlatformName);
}
}
}
void FBlueprintNativeCodeGenModule::SaveManifest(int32 Id )
{
for (auto& PlatformName : TargetPlatformNames)
{
GetManifest(*PlatformName).Save(Id);
}
}
void FBlueprintNativeCodeGenModule::MergeManifest(int32 ManifestIdentifier)
{
for (auto& PlatformName : TargetPlatformNames)
{
FBlueprintNativeCodeGenManifest& CurrentManifest = GetManifest(*PlatformName);
FBlueprintNativeCodeGenManifest OtherManifest = FBlueprintNativeCodeGenManifest(CurrentManifest.GetTargetPaths().ManifestFilePath() + FString::FromInt(ManifestIdentifier));
CurrentManifest.Merge(OtherManifest);
}
}
void FBlueprintNativeCodeGenModule::FinalizeManifest()
{
for(auto& PlatformName : TargetPlatformNames)
{
GetManifest(*PlatformName).Save(-1);
check(FBlueprintNativeCodeGenUtils::FinalizePlugin(GetManifest(*PlatformName)));
}
}
UClass* FBlueprintNativeCodeGenModule::FindReplacedClass(const UClass* Class) const
{
// we're only looking to replace class types:
while (Class)
{
if (Class == UUserDefinedEnum::StaticClass())
{
return UEnum::StaticClass();
}
if (Class == UUserDefinedStruct::StaticClass())
{
return UScriptStruct::StaticClass();
}
if (Class == UBlueprintGeneratedClass::StaticClass())
{
return UDynamicClass::StaticClass();
}
Class = Class->GetSuperClass();
}
return nullptr;
}
EReplacementResult FBlueprintNativeCodeGenModule::IsTargetedForReplacement(const UPackage* Package) const
{
// non-native packages with enums and structs should be converted, unless they are blacklisted:
UStruct* Struct = nullptr;
UEnum* Enum = nullptr;
GetFieldFormPackage(Package, Struct, Enum, RF_NoFlags);
UObject* Target = Struct;
if (Target == nullptr)
{
Target = Enum;
}
return IsTargetedForReplacement(Target);
}
EReplacementResult FBlueprintNativeCodeGenModule::IsTargetedForReplacement(const UObject* Object) const
{
const UStruct* Struct = Cast<UStruct>(Object);
const UEnum* Enum = Cast<UEnum>(Object);
if (Struct == nullptr && Enum == nullptr)
{
return EReplacementResult::DontReplace;
}
EReplacementResult Result = EReplacementResult::ReplaceCompletely;
if (const UClass* BlueprintClass = Cast<UClass>(Struct))
{
if (UBlueprint* Blueprint = Cast<UBlueprint>(BlueprintClass->ClassGeneratedBy))
{
const EBlueprintType UnconvertableBlueprintTypes[] = {
//BPTYPE_Const, // WTF is a "const" Blueprint?
BPTYPE_MacroLibrary,
BPTYPE_LevelScript,
};
EBlueprintType BlueprintType = Blueprint->BlueprintType;
for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(UnconvertableBlueprintTypes); ++TypeIndex)
{
if (BlueprintType == UnconvertableBlueprintTypes[TypeIndex])
{
Result = EReplacementResult::GenerateStub;
}
}
static const FBoolConfigValueHelper DontNativizeDataOnlyBP(TEXT("BlueprintNativizationSettings"), TEXT("bDontNativizeDataOnlyBP"));
if (DontNativizeDataOnlyBP)
{
if (FBlueprintEditorUtils::IsDataOnlyBlueprint(Blueprint))
{
return EReplacementResult::DontReplace;
}
}
for (UBlueprintGeneratedClass* ParentClassIt = Cast<UBlueprintGeneratedClass>(BlueprintClass->GetSuperClass())
; ParentClassIt; ParentClassIt = Cast<UBlueprintGeneratedClass>(ParentClassIt->GetSuperClass()))
{
EReplacementResult ParentResult = IsTargetedForReplacement(ParentClassIt);
if (ParentResult != EReplacementResult::ReplaceCompletely)
{
Result = EReplacementResult::GenerateStub;
}
}
for (TAssetSubclassOf<UBlueprint> ExcludedBlueprintTypeAsset : ExcludedBlueprintTypes)
{
UClass* ExcludedBPClass = ExcludedBlueprintTypeAsset.Get();
if (!ExcludedBPClass)
{
ExcludedBPClass = ExcludedBlueprintTypeAsset.LoadSynchronous();
}
if (ExcludedBPClass && Blueprint->IsA(ExcludedBPClass))
{
Result = EReplacementResult::GenerateStub;
}
}
}
}
auto IsObjectFromDeveloperPackage = [](const UObject* Obj) -> bool
{
return Obj && Obj->GetOutermost()->HasAllPackagesFlags(PKG_Developer);
};
auto IsDeveloperObject = [&](const UObject* Obj) -> bool
{
if (Obj)
{
if (IsObjectFromDeveloperPackage(Obj))
{
return true;
}
const UStruct* StructToTest = Obj->IsA<UStruct>() ? CastChecked<const UStruct>(Obj) : Obj->GetClass();
for (; StructToTest; StructToTest = StructToTest->GetSuperStruct())
{
if (IsObjectFromDeveloperPackage(StructToTest))
{
return true;
}
}
}
return false;
};
if (Object && (IsEditorOnlyObject(Object) || IsDeveloperObject(Object)))
{
UE_LOG(LogBlueprintCodeGen, Warning, TEXT("Object %s depends on Editor or Development stuff. Is shouldn't be cooked."), *GetPathNameSafe(Object));
return EReplacementResult::DontReplace;
}
// check blacklists:
// we can't use FindObject, because we may be converting a type while saving
if (Enum && ExcludedAssetTypes.Find(Enum->GetPathName()) != INDEX_NONE)
{
Result = EReplacementResult::GenerateStub;
}
while (Struct)
{
if (ExcludedAssetTypes.Find(Struct->GetPathName()) != INDEX_NONE)
{
Result = EReplacementResult::GenerateStub;
}
Struct = Struct->GetSuperStruct();
}
if (ExcludedAssets.Contains(Object->GetOutermost()))
{
Result = EReplacementResult::GenerateStub;
}
return Result;
}
IMPLEMENT_MODULE(FBlueprintNativeCodeGenModule, BlueprintNativeCodeGen);