You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#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]
662 lines
21 KiB
C++
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); |