Files
UnrealEngineUWP/Engine/Source/Developer/BlueprintNativeCodeGen/Private/BlueprintNativeCodeGenModule.cpp
Dan Oconnor 2c80e4ea2e Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 4006870)
#lockdown Nick.Penwarden
#rb None
#rnx

============================
  MAJOR FEATURES & CHANGES
============================

Change 3967603 by Marc.Audy

	Fix force feedback events being played on the correct controller if the OffsetPlayerGamepadIds feature is in use
	#jira UE-56077

Change 3969399 by Phillip.Kavan

	Partially fix runtime failures for converted circular dependency test actors in a nativized QAGame.

	#jira UE-56287

Change 3970181 by Phillip.Kavan

	Improved handling of circular dependencies during Blueprint nativization between converted and unconverted assets.

	Change summary:
	- Removed FGatherConvertedClassDependencies::MarkUnconvertedClassAsNecessary(); no longer in use.
	- Modified FFindAssetsToInclude::MaybeIncludeObjectAsDependency() to remove the case that was marking assets for stub class generation; no longer needed.
	- Modified FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to detect, report on and handle circular references between converted and unconverted assets (ensuring that both sides are converted).

	#jira UE-56292

Change 3970185 by Phillip.Kavan

	Fix initialization of special UMG struct types in nativized Blueprint ctor code.

	Change summary:
	- Modified FBackendHelperUMG::SpecialStructureConstructorUMG() to emit the proper constructor call based on the value of the 'ForcedTime' field. Also modified to emit updated constructor code for the FMovieSceneSegment struct's 'Range' member, which is now a TRange<> type.
	- Fixed a typo in the TBaseStructure<FInt32Range> case in FEmitDefaultValueHelper::SpecialStructureConstructor().

	#jira UE-56487

Change 3971520 by mason.seay

	Adjusted widget anchor to fix screen positioning

Change 3972611 by Mieszko.Zielinski

	Fixed some of the vislogger macros using old 'Actor' rather than new 'LogOwner' parameter #UE4

	#jira UE-56978

Change 3973564 by Phillip.Kavan

	Fix missing dependency on linker response file. Prevents target being relinked when build environment changes.

	- Mirrored from //UE4/Dev-Core (3973331).

	#jira UE-56277

Change 3974308 by Phillip.Kavan

	Fix crash when choosing to delete multiple bookmarks selected in the Bookmarks tree view.

	Change summary:
	- Modified SBlueprintBookmarks::OnDeleteSelectedTreeViewItems() to make a local copy of the reference to the entry in the source array.
	- Modified FBlueprintEditor::RemoveBookmark() to save local config settings *after* modifying the persistent Bookmarks array (it was previously happening before).

	#jira UE-55598

Change 3975506 by Mason.Seay

	Code and blueprints for subobject testing

Change 3977558 by Mason.Seay

	Checking in missed header file

Change 3978137 by Ben.Zeigler

	#jira UE-57154 Fix crash where component instance data cache would duplicate external objects during the null component pre serialize

Change 3978326 by Ben.Zeigler

	#jira UE-57062 Only look for class redirects for soft object paths if the string starts with /script, some of the class redirect match strings are overly broad and were catching assets

Change 3978393 by Ben.Zeigler

	Fix FAssetData::IsUAsset comment to match what it actually does, old comment implied it was false for maps which is wrong

Change 3978837 by Marc.Audy

	Make GetAutoEmitTermParameters and IsAutoCreateRefTerm static and optimize both

Change 3978838 by Marc.Audy

	(4.19.1) PR #4480: Crash fix trying to open the console command on Windows (Contributed by bakjos)
	#jira UE-54908
	#jira UE-54215

Change 3979156 by Marc.Audy

	PR #4615: Update WindDirectionalSourceComponent.h (Contributed by c4tnt)
	#jira UE-57071

Change 3982289 by Ben.Zeigler

	Fix several issues with unloaded blueprints and the class picker/viewer
	#jira UE-53646 Reimplement CR #4369 so setting a Class blueprint variable to a blueprint class will correctly allow unloaded blueprint children to be selected in the picker
	Added FName defines to FBlueprintTags for the asset registry tags used for unloaded blueprints, and removed the ParentClassPackage tag as it was redundant with ParentClass
	Fixed it so Soft Class Reference properties can be set back to none with the picker
	Removed FUnloadedClassDragDropOp as it did not work correctly and replaced uses with FAssetDragDrop, which was already used for content browser drags
	Changed internals of class viewer to properly store and use the paths to the actual generated class

Change 3982684 by Ben.Zeigler

	Add DevelopmentAlwaysCook setting to the asset manager cook rule, this works like AlwaysCook in development and like NeverCook in production. DevelopmentCook works like Unknown in development

Change 3982762 by Mieszko.Zielinski

	PR #4614: Fix UBTTask_RotateToFaceBBEntry when trying to face a position where i. (Contributed by Goutye)

	I've improved the original PR by relaxing condition constraints, and removing some unnecessary vector normalization (since FVecotr::CosineAngle2D normalizes the input parameters anyway).


	#jira UE-57070

Change 3982866 by Marc.Audy

	Defer Destroying an Actor if it is in the process of beginning play
	#jira UE-57091

Change 3982965 by Mieszko.Zielinski

	PR #3567: add API declarations to BlueprintNodeHelpers (Contributed by kayama-shift)


Change 3987399 by Dan.Oconnor

	Remove unused ReloadObjectArc and unimplemented ArchiveReplaceArchetype

	#jira None

Change 3987956 by Mieszko.Zielinski

	Fixed inconsistincies in BT category naming in BP #UE4

	#jira UE-57262

Change 3988308 by Mieszko.Zielinski

	Fixed 'navmesh needs to be rebuild' displaying even after navigation building with navmesh auto building disabled #UE4

	#jira UE-57286

Change 3989309 by Mieszko.Zielinski

	A support AI pluggin that links the aimodules #UE4

Change 3989311 by Mieszko.Zielinski

	Moved the HTNPlanner plugin into the newly created Plugins/AI subfolder #UE4

Change 3989506 by mason.seay

	WIP Navigation Test Map

Change 3990826 by Phillip.Kavan

	Fix existing placements of nativized Blueprint actors with a nonzero (legacy) root component scene transform at the CDO level.

	This corrects a long-standing issue that was introduced back in 2947488.

	Change summary:
	- Modified AActor::PostSpawnInitialize() to zero out RelativeLocation and RelativeRotation when spawning actors with a native scene root inherited from a converted Blueprint class.
	- Modified FNonativeComponentData to remove the HandledAsSpecialProperty() override for skipping the RelativeLocation and RelativeRotation properties on root components when emitting code to initialize the CDO for a converted Actor BP with a non-native root component.

	#jira UE-50452

Change 3993174 by mason.seay

	Cleared out unnecessary BP logic

Change 3994370 by Ben.Zeigler

	Remove manual AI includes, the AI support plugin correctly pulls in those references now

Change 3995399 by Marc.Audy

	When destroying the world clear out level collections
	#jira UE-57257

Change 3995731 by Marc.Audy

	Fix UEngine::CommitMapChange issues with new streaming level state machine
	* Level needs to be added to array before determining current state
	* Should be adding, not setting streaming levels from fake world

	#jira UE-57424

Change 3996917 by Ben.Zeigler

	ObjectWriter should not reset the soft object weak pointer, it should be a const operation

Change 3996921 by Ben.Zeigler

	#jira UE-57288 Improve ensures for invalid primary asset types, it only complains if type is set and gives a better error

Change 3997164 by Dan.Oconnor

	Don't purge classes until they are recompiled and relink all child classes immediately after a blueprint generated class has changed. This allows iterative compilation logic to behave as it did before the compilation manager
	#jira UE-55667

Change 3997674 by paulo.souza

	Reverting camera setup changes from the renaming

Change 3997901 by mason.seay

	Updates to subobject test bp's

Change 3998028 by mason.seay

	Move logic to Event Graph

Change 3998051 by Mason.Seay

	Adding QAGame ClassGroup to actively tested QAGame components for organization

Change 3999438 by Dan.Oconnor

	Fix deterministic cooking issues by avoiding use of MakeUniqueObjectName when instantiating component templates
	#jira FORT-81409

Change 4000265 by Mason.Seay

	Checking in QAGame classes again to (hopefully) fix property issues

Change 4000339 by Marc.Audy

	PR #4652: AttachComponent Message Fix (Contributed by nonlin)
	#jira UE-57656

Change 4001338 by Dan.Oconnor

	Fix reinstancing bug introduced in 3591939 which could cause some reinstanced objects to have references within them replaced. If the object in question was an actor it could take ownership of components owned by another actor and destroy them, resulting in a crash when the other actor tried to use its components

	#jira UE-57335

Change 4001400 by Marc.Audy

	Per https://udn.unrealengine.com/questions/407745/childactorcomponent-and-modifications-to-component.html don't redirect or reset loaders when renaming components as part of the component instance data cache.

Change 4001530 by Marc.Audy

	Use a clean default object to calculate the empty archive size.

Change 4002791 by Marc.Audy

	Fix ensure when loading sublevel that has been redirected
	#jira UE-57516

Change 4002812 by mason.seay

	Added BP code to test Watching Sets and Maps

Change 4002947 by mason.seay

	More code for watch values testing

Change 4003059 by paulo.souza

	Fixes to volumetric lighting and sphere capture bakes

Change 4003509 by Dan.Oconnor

	Reset bIsFakingTouched and bIsGameFakingTouch when the game viewport is unregistered. This prevents the PIE session from tainting editor session behavior when UInputSettings::bUseMouseForTouch is set
	#jira UE-57724

Change 4005596 by Phillip.Kavan

	Fix random crashes when running multiple global blueprint search tabs in parallel just after editor launch.

	Change summary:
	- Refactored global FiB APIs to use thread-safe shared pointer types when referencing imaginary Blueprint data, which is a shared resource.
	- Modified FFindInBlueprintSearchManager::ContinueSearchQuery() to ensure that parsing imaginary Blueprint data only happens on a single thread.

	#jira UE-57046

Change 4005657 by Dan.Oconnor

	Don't attach OldRoot component if it's not outered to the current actor
	#jira UE-55300

Change 4005709 by Phillip.Kavan

	Ensure rather than assert on failed child promotion when destroying a non-root scene component within an Actor's scene component hierarchy.

	#jira UE-52921

Change 4005825 by paulo.souza

	New camera angle + touch rotation tests + "Action RPG" logos

Change 4006058 by Sam.Deiter

	Removing this as it is not needed.

Change 3980745 by Marc.Audy

	Apply CL# 3977198 from Dev-Core
	#author Ben.Marsh
	Remove INI file override for editor stack size on Windows. This is rarely valid since editor targets share build products with other games by deafult. Fix to add linker response file as prerequisite exposed targets overriding this as a bug.

[CL 4006923 by Dan Oconnor in Main branch]
2018-04-16 20:19:04 -04:00

1256 lines
44 KiB
C++

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "BlueprintNativeCodeGenModule.h"
#include "Engine/Blueprint.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Misc/ConfigCacheIni.h"
#include "UObject/UObjectHash.h"
#include "UObject/Package.h"
#include "Templates/Greater.h"
#include "Components/ActorComponent.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "AssetData.h"
#include "Engine/UserDefinedEnum.h"
#include "Engine/UserDefinedStruct.h"
#include "Settings/ProjectPackagingSettings.h"
#include "PlatformInfo.h"
#include "AssetRegistryModule.h"
#include "BlueprintNativeCodeGenManifest.h"
#include "Blueprint/BlueprintSupport.h"
#include "BlueprintCompilerCppBackendInterface.h"
#include "IBlueprintCompilerCppBackendModule.h"
#include "BlueprintNativeCodeGenUtils.h"
#include "Engine/SCS_Node.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Engine/InheritableComponentHandler.h"
#include "Animation/AnimBlueprint.h"
#include "Interfaces/ITargetPlatform.h"
/*******************************************************************************
* FBlueprintNativeCodeGenModule
******************************************************************************/
class FBlueprintNativeCodeGenModule : public IBlueprintNativeCodeGenModule
, public IBlueprintNativeCodeGenCore
{
public:
FBlueprintNativeCodeGenModule()
{
}
//~ Begin IModuleInterface interface
virtual void ShutdownModule();
//~ End IModuleInterface interface
//~ Begin IBlueprintNativeCodeGenModule interface
virtual void Convert(UPackage* Package, ESavePackageResult ReplacementType, const FName PlatformName) override;
virtual void SaveManifest() override;
virtual void MergeManifest(int32 ManifestIdentifier) override;
virtual void FinalizeManifest() override;
virtual void GenerateStubs() override;
virtual void GenerateFullyConvertedClasses() override;
void MarkUnconvertedBlueprintAsNecessary(TSoftObjectPtr<UBlueprint> BPPtr, const FCompilerNativizationOptions& NativizationOptions) override;
virtual const TMultiMap<FName, TSoftClassPtr<UObject>>& GetFunctionsBoundToADelegate() override;
FFileHelper::EEncodingOptions ForcedEncoding() const
{
return FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM;
}
virtual const FCompilerNativizationOptions& GetNativizationOptionsForPlatform(const ITargetPlatform* Platform) const override;
virtual void FillPlatformNativizationDetails(const ITargetPlatform* Platform, FPlatformNativizationDetails& OutDetails) override;
protected:
virtual void Initialize(const FNativeCodeGenInitData& InitData) override;
virtual void InitializeForRerunDebugOnly(const TArray<FPlatformNativizationDetails>& CodegenTargets) override;
//~ End IBlueprintNativeCodeGenModule interface
//~ Begin FScriptCookReplacmentCoordinator interface
virtual EReplacementResult IsTargetedForReplacement(const UPackage* Package, const FCompilerNativizationOptions& NativizationOptions) const override;
virtual EReplacementResult IsTargetedForReplacement(const UObject* Object, const FCompilerNativizationOptions& NativizationOptions) const override;
virtual UClass* FindReplacedClassForObject(const UObject* Object, const FCompilerNativizationOptions& NativizationOptions) const override;
virtual UObject* FindReplacedNameAndOuter(UObject* Object, FName& OutName, const FCompilerNativizationOptions& NativizationOptions) const override;
//~ End FScriptCookReplacmentCoordinator interface
private:
void ReadConfig();
void FillTargetedForReplacementQuery();
void FillIsFunctionUsedInADelegate();
FBlueprintNativeCodeGenManifest& GetManifest(const FName PlatformName);
void GenerateSingleStub(UBlueprint* BP, const FName PlatformName);
void CollectBoundFunctions(UBlueprint* BP);
void GenerateSingleAsset(UField* ForConversion, const FName PlatformName, TSharedPtr<FNativizationSummary> NativizationSummary = TSharedPtr<FNativizationSummary>());
void ReplaceAsset(const UObject* InAsset, const FCompilerNativizationOptions& NativizationOptions) const;
void GatherClassAssetsReferencedByStruct(TSet<const UBlueprintGeneratedClass*>& Assets, const UStruct* OuterStruct, const UStruct* InnerStruct = nullptr) const;
void ReplaceAssetsWithCircularReferenceTo(const UBlueprintGeneratedClass* InClass, const FCompilerNativizationOptions& NativizationOptions) const;
bool HasCircularReferenceWithAnyConvertedAsset(const UBlueprintGeneratedClass* InClass, const FCompilerNativizationOptions& NativizationOptions) const;
struct FStatePerPlatform
{
// A stub-wrapper must be generated only if the BP is really accessed/required by some other generated code.
TSet<TSoftObjectPtr<UBlueprint>> StubsRequiredByGeneratedCode;
TSet<TSoftObjectPtr<UStruct>> UDSAssetsToGenerate;
TSet<TSoftObjectPtr<UBlueprint>> BPAssetsToGenerate;
// Cached values from IsTargetedForReplacement
mutable TMap<FSoftObjectPath, EReplacementResult> CachedIsTargetedForReplacement;
};
TMap< FName, FStatePerPlatform > StatesPerPlatform;
TMap< FName, TUniquePtr<FBlueprintNativeCodeGenManifest> > Manifests;
// Children of these classes won't be nativized
TArray<FString> ExcludedAssetTypes;
// Eg: +ExcludedBlueprintTypes=/Script/Engine.AnimBlueprint
TArray<TSoftClassPtr<UBlueprint>> ExcludedBlueprintTypes;
// Individually excluded assets
TSet<FSoftObjectPath> ExcludedAssets;
// Excluded folders. It excludes only BPGCs, enums and structures are still converted.
TArray<FString> ExcludedFolderPaths;
TArray<FName> TargetPlatformNames;
TMultiMap<FName, TSoftClassPtr<UObject>> FunctionsBoundToADelegate; // is a function could be bound to a delegate, then it must have UFUNCTION macro. So we cannot optimize it.
};
const FCompilerNativizationOptions& FBlueprintNativeCodeGenModule::GetNativizationOptionsForPlatform(const ITargetPlatform* Platform) const
{
const FName PlatformName = ensure(Platform) ? Platform->GetPlatformInfo().PlatformInfoName : NAME_None;
const TUniquePtr<FBlueprintNativeCodeGenManifest>* Result = Manifests.Find(PlatformName);
if (ensure(Result && Result->IsValid()))
{
const FBlueprintNativeCodeGenManifest& Manifest = **Result;
return Manifest.GetCompilerNativizationOptions();
}
UE_LOG(LogBlueprintCodeGen, Error, TEXT("Cannot find manifest for platform: %s"), *PlatformName.ToString());
static const FCompilerNativizationOptions FallbackNativizationOptions{};
return FallbackNativizationOptions;
}
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)
{
TSoftClassPtr<UBlueprint> ClassPtr;
ClassPtr = FSoftObjectPath(Path);
ClassPtr.LoadSynchronous();
ExcludedBlueprintTypes.Add(ClassPtr);
}
}
TArray<FString> ExcludedAssetPaths;
GConfig->GetArray(TEXT("BlueprintNativizationSettings"), TEXT("ExcludedAssets"), ExcludedAssetPaths, GEditorIni);
for (FString& Path : ExcludedAssetPaths)
{
ExcludedAssets.Add(FSoftObjectPath(Path));
}
GConfig->GetArray(TEXT("BlueprintNativizationSettings"), TEXT("ExcludedFolderPaths"), ExcludedFolderPaths, GEditorIni);
}
void FBlueprintNativeCodeGenModule::MarkUnconvertedBlueprintAsNecessary(TSoftObjectPtr<UBlueprint> BPPtr, const FCompilerNativizationOptions& NativizationOptions)
{
FStatePerPlatform* StateForCurrentPlatform = StatesPerPlatform.Find(NativizationOptions.PlatformName);
if (ensure(StateForCurrentPlatform))
{
const UBlueprint* BP = BPPtr.Get();
UE_LOG(LogBlueprintCodeGen, Log, TEXT("Requiring stub class for unconverted Blueprint asset: %s"), BP ? *BP->GetName() : TEXT(""));
StateForCurrentPlatform->StubsRequiredByGeneratedCode.Add(BPPtr);
}
}
void FBlueprintNativeCodeGenModule::FillTargetedForReplacementQuery()
{
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
auto& ConversionQueryDelegate = BackEndModule.OnIsTargetedForConversionQuery();
auto ShouldConvert = [](const UObject* AssetObj, const FCompilerNativizationOptions& NativizationOptions)
{
if (ensure(IBlueprintNativeCodeGenCore::Get()))
{
EReplacementResult ReplacmentResult = IBlueprintNativeCodeGenCore::Get()->IsTargetedForReplacement(AssetObj, NativizationOptions);
return ReplacmentResult == EReplacementResult::ReplaceCompletely;
}
return false;
};
ConversionQueryDelegate.BindStatic(ShouldConvert);
auto LocalMarkUnconvertedBlueprintAsNecessary = [](TSoftObjectPtr<UBlueprint> BPPtr, const FCompilerNativizationOptions& NativizationOptions)
{
IBlueprintNativeCodeGenModule::Get().MarkUnconvertedBlueprintAsNecessary(BPPtr, NativizationOptions);
};
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 (UObject* Entry : Objects)
{
if (Entry->HasAnyFlags(ExcludedFlags))
{
continue;
}
if (FBlueprintSupport::IsDeferredDependencyPlaceholder(Entry))
{
continue;
}
// Not a skeleton class
if (UClass* AsClass = Cast<UClass>(Entry))
{
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, TSoftClassPtr<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)
{
StatesPerPlatform.Reset();
for (const FPlatformNativizationDetails& Platform : InitData.CodegenTargets)
{
StatesPerPlatform.Add(Platform.PlatformName);
}
ReadConfig();
IBlueprintNativeCodeGenCore::Register(this);
// Each platform will need a manifest, because each platform could cook different assets:
for (const FPlatformNativizationDetails& Platform : InitData.CodegenTargets)
{
const FString TargetPath = FBlueprintNativeCodeGenPaths::GetDefaultPluginPath(Platform.PlatformName);
const FBlueprintNativeCodeGenManifest& Manifest = *Manifests.Add(Platform.PlatformName, TUniquePtr<FBlueprintNativeCodeGenManifest>(new FBlueprintNativeCodeGenManifest(TargetPath, Platform.CompilerNativizationOptions, InitData.ManifestIdentifier)));
TargetPlatformNames.Add(Platform.PlatformName);
// Clear source code folder
const FString SourceCodeDir = Manifest.GetTargetPaths().PluginRootDir();
UE_LOG(LogBlueprintCodeGen, Log, TEXT("Clear nativized source code directory: %s"), *SourceCodeDir);
IFileManager::Get().DeleteDirectory(*SourceCodeDir, false, true);
}
FillTargetedForReplacementQuery();
FillIsFunctionUsedInADelegate();
}
void FBlueprintNativeCodeGenModule::InitializeForRerunDebugOnly(const TArray<FPlatformNativizationDetails>& CodegenTargets)
{
StatesPerPlatform.Reset();
for (const FPlatformNativizationDetails& Platform : CodegenTargets)
{
StatesPerPlatform.Add(Platform.PlatformName);
}
ReadConfig();
IBlueprintNativeCodeGenCore::Register(this);
FillTargetedForReplacementQuery();
FillIsFunctionUsedInADelegate();
for (const FPlatformNativizationDetails& Platform : CodegenTargets)
{
// load the old manifest:
FString OutputPath = FBlueprintNativeCodeGenPaths::GetDefaultManifestFilePath(Platform.PlatformName);
Manifests.Add(Platform.PlatformName, 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.PlatformName).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.PlatformName);
}
// reconvert every unconverted dependency listed in the manifest:
for (const auto& ConversionTarget : GetManifest(Platform.PlatformName).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.PlatformName);
}
}
FStatePerPlatform* State = StatesPerPlatform.Find(Platform.PlatformName);
check(State);
for (TSoftObjectPtr<UStruct>& UDSPtr : State->UDSAssetsToGenerate)
{
UStruct* UDS = UDSPtr.LoadSynchronous();
if (ensure(UDS))
{
GenerateSingleAsset(UDS, Platform.PlatformName);
}
}
for (TSoftObjectPtr<UBlueprint>& BPPtr : State->BPAssetsToGenerate)
{
UBlueprint* BP = BPPtr.LoadSynchronous();
if (ensure(BP))
{
GenerateSingleAsset(BP->GeneratedClass, Platform.PlatformName);
}
}
}
}
void FBlueprintNativeCodeGenModule::ShutdownModule()
{
// Clear the current coordinator reference.
IBlueprintNativeCodeGenCore::Register(nullptr);
// Reset compiler module delegate function bindings.
IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
BackEndModule.GetIsFunctionUsedInADelegateCallback().Unbind();
BackEndModule.OnIsTargetedForConversionQuery().Unbind();
BackEndModule.OnIncludingUnconvertedBP().Unbind();
}
void FBlueprintNativeCodeGenModule::GenerateFullyConvertedClasses()
{
TSharedPtr<FNativizationSummary> NativizationSummary(new FNativizationSummary());
{
IBlueprintCompilerCppBackendModule& CodeGenBackend = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
CodeGenBackend.NativizationSummary() = NativizationSummary;
}
for (const FName& PlatformName : TargetPlatformNames)
{
FStatePerPlatform* State = StatesPerPlatform.Find(PlatformName);
check(State);
for (TSoftObjectPtr<UStruct>& UDSPtr : State->UDSAssetsToGenerate)
{
UStruct* UDS = UDSPtr.LoadSynchronous();
if (ensure(UDS))
{
GenerateSingleAsset(UDS, PlatformName, NativizationSummary);
}
}
for (TSoftObjectPtr<UBlueprint>& BPPtr : State->BPAssetsToGenerate)
{
UBlueprint* BP = BPPtr.LoadSynchronous();
if (ensure(BP))
{
GenerateSingleAsset(BP->GeneratedClass, PlatformName, NativizationSummary);
}
}
}
if (NativizationSummary->InaccessiblePropertyStat.Num())
{
UE_LOG(LogBlueprintCodeGen, Display, TEXT("Nativization Summary - Inaccessible Properties:"));
NativizationSummary->InaccessiblePropertyStat.ValueSort(TGreater<int32>());
for (auto& Iter : NativizationSummary->InaccessiblePropertyStat)
{
UE_LOG(LogBlueprintCodeGen, Display, TEXT("\t %s \t - %d"), *Iter.Key.ToString(), Iter.Value);
}
}
{
UE_LOG(LogBlueprintCodeGen, Display, TEXT("Nativization Summary - AnimBP:"));
UE_LOG(LogBlueprintCodeGen, Display, TEXT("Name, Children, Non-empty Functions (Empty Functions), Variables, FunctionUsage, VariableUsage"));
for (auto& Iter : NativizationSummary->AnimBlueprintStat)
{
UE_LOG(LogBlueprintCodeGen, Display
, TEXT("%s, %d, %d (%d), %d, %d, %d")
, *Iter.Key.ToString()
, Iter.Value.Children
, Iter.Value.Functions - Iter.Value.ReducibleFunctions
, Iter.Value.ReducibleFunctions
, Iter.Value.Variables
, Iter.Value.FunctionUsage
, Iter.Value.VariableUsage);
}
}
UE_LOG(LogBlueprintCodeGen, Display, TEXT("Nativization Summary - Shared Variables From Graph: %d"), NativizationSummary->MemberVariablesFromGraph);
}
FBlueprintNativeCodeGenManifest& FBlueprintNativeCodeGenModule::GetManifest(const FName PlatformName)
{
TUniquePtr<FBlueprintNativeCodeGenManifest>* Result = Manifests.Find(PlatformName);
check(Result->IsValid());
return **Result;
}
void FBlueprintNativeCodeGenModule::GenerateSingleStub(UBlueprint* BP, const FName PlatformName)
{
if (!ensure(BP))
{
return;
}
UClass* Class = BP->GeneratedClass;
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, GetManifest(PlatformName).GetCompilerNativizationOptions());
if (!FileContents.IsEmpty())
{
FFileHelper::SaveStringToFile(FileContents
, *(GetManifest(PlatformName).CreateUnconvertedDependencyRecord(AssetInfo.PackageName, AssetInfo).GeneratedWrapperPath)
, ForcedEncoding());
}
// 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 FName PlatformName, TSharedPtr<FNativizationSummary> NativizationSummary)
{
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, NativizationSummary, GetManifest(PlatformName).GetCompilerNativizationOptions());
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, ForcedEncoding()))
{
bSuccess = false;
ConversionRecord.GeneratedCppPath.Empty();
}
CppSource->Empty(CppSource->Len());
}
else
{
ConversionRecord.GeneratedCppPath.Empty();
}
if (bSuccess && !HeaderSource->IsEmpty())
{
if (!FFileHelper::SaveStringToFile(*HeaderSource, *ConversionRecord.GeneratedHeaderPath, ForcedEncoding()))
{
bSuccess = false;
ConversionRecord.GeneratedHeaderPath.Empty();
}
HeaderSource->Empty(HeaderSource->Len());
}
else
{
ConversionRecord.GeneratedHeaderPath.Empty();
}
if (bSuccess)
{
GetManifest(PlatformName).GatherModuleDependencies(ForConversion->GetOutermost());
}
else
{
UE_LOG(LogBlueprintCodeGen, Error, TEXT("FBlueprintNativeCodeGenModule::GenerateSingleAsset error: %s"), *GetPathNameSafe(ForConversion));
}
BackendPCHQuery.Unbind();
}
void FBlueprintNativeCodeGenModule::GenerateStubs()
{
for (FName& PlatformName : TargetPlatformNames)
{
FStatePerPlatform* StateForCurrentPlatform = StatesPerPlatform.Find(PlatformName);
if (!ensure(StateForCurrentPlatform))
{
continue;
}
TSet<TSoftObjectPtr<UBlueprint>> AlreadyGenerated;
while (AlreadyGenerated.Num() < StateForCurrentPlatform->StubsRequiredByGeneratedCode.Num())
{
const int32 OldGeneratedNum = AlreadyGenerated.Num();
TSet<TSoftObjectPtr<UBlueprint>> LocalCopyStubsRequiredByGeneratedCode = StateForCurrentPlatform->StubsRequiredByGeneratedCode;
for (TSoftObjectPtr<UBlueprint>& BPPtr : LocalCopyStubsRequiredByGeneratedCode)
{
bool bAlreadyGenerated = false;
AlreadyGenerated.Add(BPPtr, &bAlreadyGenerated);
if (bAlreadyGenerated)
{
continue;
}
GenerateSingleStub(BPPtr.LoadSynchronous(), PlatformName);
}
// make sure there was any progress
if (!ensure(OldGeneratedNum != AlreadyGenerated.Num()))
{
break;
}
}
}
}
void FBlueprintNativeCodeGenModule::Convert(UPackage* Package, ESavePackageResult CookResult, const FName 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;
}
FStatePerPlatform* State = StatesPerPlatform.Find(PlatformName);
if (!ensure(State))
{
return;
}
if (CookResult == ESavePackageResult::GenerateStub)
{
// No stub is generated for structs and enums.
ensure(!BP || !State->BPAssetsToGenerate.Contains(BP));
}
else
{
check(CookResult == ESavePackageResult::ReplaceCompletely);
if (AsClass)
{
if (ensure(BP))
{
State->BPAssetsToGenerate.Add(BP);
}
}
else if (Struct)
{
State->UDSAssetsToGenerate.Add(Struct);
}
else
{
GenerateSingleAsset((UField*)Enum, PlatformName);
}
}
}
void FBlueprintNativeCodeGenModule::SaveManifest()
{
for (FName& PlatformName : TargetPlatformNames)
{
GetManifest(PlatformName).Save();
}
}
void FBlueprintNativeCodeGenModule::MergeManifest(int32 ManifestIdentifier)
{
for (FName& PlatformName : TargetPlatformNames)
{
FBlueprintNativeCodeGenManifest& CurrentManifest = GetManifest(PlatformName);
FBlueprintNativeCodeGenManifest OtherManifest = FBlueprintNativeCodeGenManifest(CurrentManifest.GetTargetPaths().ManifestFilePath(ManifestIdentifier));
CurrentManifest.Merge(OtherManifest);
}
}
void FBlueprintNativeCodeGenModule::FinalizeManifest()
{
IBlueprintCompilerCppBackendModule& CodeGenBackend = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
TSharedPtr<FNativizationSummary> NativizationSummary = CodeGenBackend.NativizationSummary();
for(FName& PlatformName : TargetPlatformNames)
{
FBlueprintNativeCodeGenManifest& Manifest = GetManifest(PlatformName);
if (Manifest.GetConversionRecord().Num() > 0)
{
if (NativizationSummary.IsValid())
{
TSet<TSoftObjectPtr<UPackage>>* RequiredModules = NativizationSummary->ModulesRequiredByPlatform.Find(Manifest.GetCompilerNativizationOptions().PlatformName);
if (RequiredModules)
{
for (TSoftObjectPtr<UPackage> ItPackage : *RequiredModules)
{
if (UPackage* Pkg = ItPackage.Get())
{
Manifest.AddSingleModuleDependency(Pkg);
}
}
}
}
ensure(Manifest.GetManifestChunkId() == -1); // ensure this was intended to be the root manifest
Manifest.Save();
check(FBlueprintNativeCodeGenUtils::FinalizePlugin(Manifest));
}
}
}
UClass* FBlueprintNativeCodeGenModule::FindReplacedClassForObject(const UObject* Object, const FCompilerNativizationOptions& NativizationOptions) const
{
// we're only looking to replace class types:
if (Object && Object->IsA<UField>())
{
if (IsTargetedForReplacement(Object, NativizationOptions) == EReplacementResult::ReplaceCompletely)
{
for (const UClass* Class = Object->GetClass(); Class; Class = Class->GetSuperClass())
{
if (Class == UUserDefinedEnum::StaticClass())
{
return UEnum::StaticClass();
}
if (Class == UUserDefinedStruct::StaticClass())
{
return UScriptStruct::StaticClass();
}
if (Class == UBlueprintGeneratedClass::StaticClass())
{
return UDynamicClass::StaticClass();
}
}
}
else if (const UByteProperty* ByteProperty = Cast<UByteProperty>(Object))
{
// User-Defined Enum values are compiled as Byte properties, but get converted to Enum class properties during nativization. Thus,
// we have to account for that here and switch the property class to be an Enum property, since that's what will be generated by UHT.
// If we don't do this, then a dependent asset's import table will contain the incorrect property class for this value, if referenced.
if (ByteProperty->Enum && IsTargetedForReplacement(ByteProperty->Enum, NativizationOptions) == EReplacementResult::ReplaceCompletely)
{
return UEnumProperty::StaticClass();
}
}
}
return nullptr;
}
UObject* FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter(UObject* Object, FName& OutName, const FCompilerNativizationOptions& NativizationOptions) const
{
OutName = NAME_None;
auto GetOuterBPGC = [](UObject* FirstOuter) -> UBlueprintGeneratedClass*
{
UBlueprintGeneratedClass* BPGC = nullptr;
for (UObject* OuterObject = FirstOuter; OuterObject && !BPGC; OuterObject = OuterObject->GetOuter())
{
if (OuterObject->HasAnyFlags(RF_ClassDefaultObject))
{
return nullptr;
}
BPGC = Cast<UBlueprintGeneratedClass>(OuterObject);
}
return BPGC;
};
UActorComponent* ActorComponent = Cast<UActorComponent>(Object);
if (ActorComponent)
{
//if is child of a BPGC and not child of a CDO
UBlueprintGeneratedClass* BPGC = GetOuterBPGC(ActorComponent->GetOuter());
FName NewName = NAME_None;
UObject* OuterCDO = nullptr;
for (UBlueprintGeneratedClass* SuperBPGC = BPGC; SuperBPGC && (NewName == NAME_None); SuperBPGC = Cast<UBlueprintGeneratedClass>(SuperBPGC->GetSuperClass()))
{
if (SuperBPGC->InheritableComponentHandler)
{
FComponentKey FoundKey = SuperBPGC->InheritableComponentHandler->FindKey(ActorComponent);
if (FoundKey.IsValid())
{
NewName = FoundKey.IsSCSKey() ? FoundKey.GetSCSVariableName() : ActorComponent->GetFName();
OuterCDO = BPGC->GetDefaultObject(false);
break;
}
}
if (SuperBPGC->SimpleConstructionScript)
{
for (auto Node : SuperBPGC->SimpleConstructionScript->GetAllNodes())
{
if (Node->ComponentTemplate == ActorComponent)
{
NewName = Node->GetVariableName();
if (NewName != NAME_None)
{
OuterCDO = BPGC->GetDefaultObject(false);
break;
}
}
}
}
}
if (OuterCDO && (EReplacementResult::ReplaceCompletely == IsTargetedForReplacement(OuterCDO->GetClass(), NativizationOptions)))
{
OutName = NewName;
UE_LOG(LogBlueprintCodeGen, Log, TEXT("Object '%s' has replaced name '%s' and outer: '%s'"), *GetPathNameSafe(Object), *OutName.ToString(), *GetPathNameSafe(OuterCDO));
return OuterCDO;
}
}
else
{
UChildActorComponent* OuterCAC = Cast<UChildActorComponent>(Object->GetOuter());
if (OuterCAC && OuterCAC->GetChildActorTemplate() == Object)
{
UBlueprintGeneratedClass* BPGC = GetOuterBPGC(OuterCAC->GetOuter());
if (BPGC && (EReplacementResult::ReplaceCompletely == IsTargetedForReplacement(BPGC, NativizationOptions)))
{
return BPGC;
}
}
}
return nullptr;
}
EReplacementResult FBlueprintNativeCodeGenModule::IsTargetedForReplacement(const UPackage* Package, const FCompilerNativizationOptions& NativizationOptions) 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, NativizationOptions);
}
EReplacementResult FBlueprintNativeCodeGenModule::IsTargetedForReplacement(const UObject* Object, const FCompilerNativizationOptions& NativizationOptions) const
{
if (Object == nullptr)
{
return EReplacementResult::DontReplace;
}
const UUserDefinedStruct* const UDStruct = Cast<const UUserDefinedStruct>(Object);
const UUserDefinedEnum* const UDEnum = Cast<const UUserDefinedEnum>(Object);
const UBlueprintGeneratedClass* const BlueprintClass = Cast<const UBlueprintGeneratedClass>(Object);
if (UDStruct == nullptr && UDEnum == nullptr && BlueprintClass == nullptr)
{
return EReplacementResult::DontReplace;
}
const FStatePerPlatform* StateForCurrentPlatform = StatesPerPlatform.Find(NativizationOptions.PlatformName);
check(StateForCurrentPlatform);
const FSoftObjectPath ObjectKey(Object);
{
const EReplacementResult* const CachedValue = StateForCurrentPlatform->CachedIsTargetedForReplacement.Find(ObjectKey); //THe referenced returned by FindOrAdd could be invalid later, when filled.
if (CachedValue)
{
return *CachedValue;
}
}
const UBlueprint* const Blueprint = BlueprintClass ? Cast<UBlueprint>(BlueprintClass->ClassGeneratedBy) : nullptr;
const UProjectPackagingSettings* const PackagingSettings = GetDefault<UProjectPackagingSettings>();
const bool bNativizeOnlySelectedBPs = PackagingSettings && PackagingSettings->BlueprintNativizationMethod == EProjectPackagingBlueprintNativizationMethod::Exclusive;
auto ObjectIsNotReplacedAtAll = [&]() -> bool
{
// EDITOR ON DEVELOPMENT OBJECT
{
auto IsDeveloperObject = [](const UObject* Obj) -> bool
{
auto IsObjectFromDeveloperPackage = [](const UObject* InObj) -> bool
{
return InObj && InObj->GetOutermost()->HasAllPackagesFlags(PKG_Developer);
};
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, Verbose, TEXT("Object %s depends on Editor or Development stuff. It shouldn't be cooked."), *GetPathNameSafe(Object));
return true;
}
}
// DATA ONLY BP
{
static const FBoolConfigValueHelper DontNativizeDataOnlyBP(TEXT("BlueprintNativizationSettings"), TEXT("bDontNativizeDataOnlyBP"));
if (DontNativizeDataOnlyBP && !bNativizeOnlySelectedBPs && Blueprint && FBlueprintEditorUtils::IsDataOnlyBlueprint(Blueprint))
{
return true;
}
}
// Don't convert objects like Default__WidgetBlueprintGeneratedClass
if (Object && (Object->HasAnyFlags(RF_ClassDefaultObject)))
{
return true;
}
return false;
};
auto ObjectGeneratesOnlyStub = [&]() -> bool
{
// ExcludedFolderPaths
{
const FString ObjPathName = Object->GetPathName();
for (const FString& ExcludedPath : ExcludedFolderPaths)
{
if (ObjPathName.StartsWith(ExcludedPath))
{
return true;
}
}
for (const FString& ExcludedPath : NativizationOptions.ExcludedFolderPaths)
{
if (ObjPathName.StartsWith(ExcludedPath))
{
return true;
}
}
}
// ExcludedAssetTypes
{
// we can't use FindObject, because we may be converting a type while saving
if (UDEnum && ExcludedAssetTypes.Find(UDEnum->GetPathName()) != INDEX_NONE)
{
return true;
}
const UStruct* LocStruct = Cast<const UStruct>(Object);
while (LocStruct)
{
if (ExcludedAssetTypes.Find(LocStruct->GetPathName()) != INDEX_NONE)
{
return true;
}
LocStruct = LocStruct->GetSuperStruct();
}
}
// ExcludedAssets
{
if (ExcludedAssets.Contains(Object->GetOutermost()))
{
return true;
}
if (NativizationOptions.ExcludedAssets.Contains(Object->GetOutermost()))
{
return true;
}
}
if (Blueprint && BlueprintClass)
{
// Reducible AnimBP
{
static const FBoolConfigValueHelper NativizeAnimBPOnlyWhenNonReducibleFuncitons(TEXT("BlueprintNativizationSettings"), TEXT("bNativizeAnimBPOnlyWhenNonReducibleFuncitons"));
if (NativizeAnimBPOnlyWhenNonReducibleFuncitons)
{
if (const UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint))
{
ensure(AnimBlueprint->bHasBeenRegenerated);
if (AnimBlueprint->bHasAnyNonReducibleFunction == UBlueprint::EIsBPNonReducible::No)
{
UE_LOG(LogBlueprintCodeGen, Log, TEXT("AnimBP %s without non-reducible functions is excluded from nativization"), *GetPathNameSafe(Blueprint));
return true;
}
}
}
}
// Unconvertable Blueprint
{
const EBlueprintType UnconvertableBlueprintTypes[] = {
//BPTYPE_Const, // What is a "const" Blueprint?
BPTYPE_MacroLibrary,
BPTYPE_LevelScript,
};
const EBlueprintType BlueprintType = Blueprint->BlueprintType;
for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(UnconvertableBlueprintTypes); ++TypeIndex)
{
if (BlueprintType == UnconvertableBlueprintTypes[TypeIndex])
{
return true;
}
}
}
// ExcludedBlueprintTypes
for (TSoftClassPtr<UBlueprint> ExcludedBlueprintTypeAsset : ExcludedBlueprintTypes)
{
UClass* ExcludedBPClass = ExcludedBlueprintTypeAsset.Get();
if (!ExcludedBPClass)
{
ExcludedBPClass = ExcludedBlueprintTypeAsset.LoadSynchronous();
}
if (ExcludedBPClass && Blueprint->IsA(ExcludedBPClass))
{
return true;
}
}
const bool bFlaggedForNativization = (Blueprint->NativizationFlag == EBlueprintNativizationFlag::Dependency) ?
PackagingSettings->IsBlueprintAssetInNativizationList(Blueprint) :
(Blueprint->NativizationFlag == EBlueprintNativizationFlag::ExplicitlyEnabled);
// Blueprint is not selected
if (bNativizeOnlySelectedBPs && !bFlaggedForNativization && !FBlueprintEditorUtils::ShouldNativizeImplicitly(Blueprint))
{
return true;
}
// Parent Class in not converted
for (const UBlueprintGeneratedClass* ParentClassIt = Cast<UBlueprintGeneratedClass>(BlueprintClass->GetSuperClass())
; ParentClassIt; ParentClassIt = Cast<UBlueprintGeneratedClass>(ParentClassIt->GetSuperClass()))
{
const EReplacementResult ParentResult = IsTargetedForReplacement(ParentClassIt, NativizationOptions);
if (ParentResult != EReplacementResult::ReplaceCompletely)
{
if (bNativizeOnlySelectedBPs)
{
UE_LOG(LogBlueprintCodeGen, Error, TEXT("BP %s is selected for nativization, but its parent class %s is not nativized."), *GetPathNameSafe(Blueprint), *GetPathNameSafe(ParentClassIt));
}
return true;
}
}
// Interface class not converted
TArray<UClass*> InterfaceClasses;
FBlueprintEditorUtils::FindImplementedInterfaces(Blueprint, false, InterfaceClasses);
for (const UClass* InterfaceClassIt : InterfaceClasses)
{
const UBlueprintGeneratedClass* InterfaceBPGC = Cast<const UBlueprintGeneratedClass>(InterfaceClassIt);
if (InterfaceBPGC)
{
const EReplacementResult InterfaceResult = IsTargetedForReplacement(InterfaceBPGC, NativizationOptions);
if (InterfaceResult != EReplacementResult::ReplaceCompletely)
{
if (bNativizeOnlySelectedBPs)
{
UE_LOG(LogBlueprintCodeGen, Error, TEXT("BP %s is selected for nativization, but BP interface class %s is not nativized."), *GetPathNameSafe(Blueprint), *GetPathNameSafe(InterfaceClassIt));
}
return true;
}
}
else if (InterfaceClassIt->GetCppTypeInfo()->IsAbstract())
{
UE_LOG(LogBlueprintCodeGen, Error, TEXT("BP %s is selected for nativization, but it cannot be nativized because it currently implements an interface class (%s) that declares one or more pure virtual functions."), *GetPathNameSafe(Blueprint), *GetPathNameSafe(InterfaceClassIt));
return true;
}
}
}
return false;
};
EReplacementResult Result = EReplacementResult::ReplaceCompletely;
if (ObjectIsNotReplacedAtAll())
{
Result = EReplacementResult::DontReplace;
}
else if (ObjectGeneratesOnlyStub())
{
Result = EReplacementResult::GenerateStub;
}
StateForCurrentPlatform->CachedIsTargetedForReplacement.Add(ObjectKey, Result);
if (BlueprintClass)
{
if (Result == EReplacementResult::ReplaceCompletely)
{
// Look for any circular references with unconverted assets. We'll need to convert those as well in order to avoid creating an EDL cycle.
ReplaceAssetsWithCircularReferenceTo(BlueprintClass, NativizationOptions);
}
else if(HasCircularReferenceWithAnyConvertedAsset(BlueprintClass, NativizationOptions))
{
UE_LOG(LogBlueprintCodeGen, Log, TEXT("Forcing '%s' to be replaced as it has a circular reference to a converted asset"), *BlueprintClass->GetName());
// Force unconverted assets to be replaced if it has a circular reference with any converted asset.
ReplaceAsset(BlueprintClass, NativizationOptions);
}
}
return Result;
}
void FBlueprintNativeCodeGenModule::ReplaceAsset(const UObject* InAsset, const FCompilerNativizationOptions& NativizationOptions) const
{
if (InAsset)
{
const FStatePerPlatform* StateForCurrentPlatform = StatesPerPlatform.Find(NativizationOptions.PlatformName);
check(StateForCurrentPlatform);
const FSoftObjectPath ObjectKey(InAsset);
StateForCurrentPlatform->CachedIsTargetedForReplacement.FindChecked(ObjectKey) = EReplacementResult::ReplaceCompletely;
}
}
void FBlueprintNativeCodeGenModule::GatherClassAssetsReferencedByStruct(TSet<const UBlueprintGeneratedClass*>& Assets, const UStruct* OuterStruct, const UStruct* InnerStruct) const
{
if (OuterStruct)
{
for (const UProperty* Property = (InnerStruct ? InnerStruct->PropertyLink : OuterStruct->PropertyLink); Property; Property = Property->PropertyLinkNext)
{
TArray<const UProperty*, TInlineAllocator<2>> InnerPropertyList;
if (const UArrayProperty* ArrayProperty = Cast<const UArrayProperty>(Property))
{
InnerPropertyList.Add(ArrayProperty->Inner);
}
else if (const USetProperty* SetProperty = Cast<const USetProperty>(Property))
{
InnerPropertyList.Add(SetProperty->ElementProp);
}
else if (const UMapProperty* MapProperty = Cast<const UMapProperty>(Property))
{
InnerPropertyList.Add(MapProperty->KeyProp);
InnerPropertyList.Add(MapProperty->ValueProp);
}
else
{
InnerPropertyList.Add(Property);
}
for (const UProperty* InnerProperty : InnerPropertyList)
{
if (const UStructProperty* StructProperty = Cast<UStructProperty>(InnerProperty))
{
GatherClassAssetsReferencedByStruct(Assets, OuterStruct, StructProperty->Struct);
}
else
{
const UBlueprintGeneratedClass* BPGC = nullptr;
if (const UObjectPropertyBase* ObjectProperty = Cast<UObjectPropertyBase>(InnerProperty))
{
BPGC = Cast<const UBlueprintGeneratedClass>(ObjectProperty->PropertyClass);
}
else if (const UClassProperty* ClassProperty = Cast<UClassProperty>(InnerProperty))
{
BPGC = Cast<const UBlueprintGeneratedClass>(ClassProperty->MetaClass);
}
if (BPGC)
{
Assets.Add(BPGC);
}
}
}
}
}
}
void FBlueprintNativeCodeGenModule::ReplaceAssetsWithCircularReferenceTo(const UBlueprintGeneratedClass* InClass, const FCompilerNativizationOptions& NativizationOptions) const
{
TSet<const UBlueprintGeneratedClass*> ForwardReferencedAssets;
GatherClassAssetsReferencedByStruct(ForwardReferencedAssets, InClass);
for (const UBlueprintGeneratedClass* ForwardReference : ForwardReferencedAssets)
{
const EReplacementResult Result = IsTargetedForReplacement(ForwardReference, NativizationOptions);
if (Result != EReplacementResult::ReplaceCompletely)
{
bool bForceConvert = false;
if (ForwardReference->IsChildOf(InClass))
{
UE_LOG(LogBlueprintCodeGen, Log, TEXT("Forcing '%s' to be replaced as it has a circular reference with '%s'"), *ForwardReference->GetName(), *InClass->GetName());
ReplaceAsset(ForwardReference, NativizationOptions);
}
else
{
TSet<const UBlueprintGeneratedClass*> ReverseReferencedAssets;
GatherClassAssetsReferencedByStruct(ReverseReferencedAssets, ForwardReference);
for (const UBlueprintGeneratedClass* ReverseReference : ReverseReferencedAssets)
{
if (ReverseReference->IsChildOf(InClass))
{
UE_LOG(LogBlueprintCodeGen, Log, TEXT("Forcing '%s' to be replaced as it has a circular reference to '%s'"), *ForwardReference->GetName(), *InClass->GetName());
ReplaceAsset(ForwardReference, NativizationOptions);
break;
}
}
}
}
}
}
bool FBlueprintNativeCodeGenModule::HasCircularReferenceWithAnyConvertedAsset(const UBlueprintGeneratedClass* InClass, const FCompilerNativizationOptions& NativizationOptions) const
{
TSet<const UBlueprintGeneratedClass*> ForwardReferencedAssets;
GatherClassAssetsReferencedByStruct(ForwardReferencedAssets, InClass);
for (const UBlueprintGeneratedClass* ForwardReference : ForwardReferencedAssets)
{
const EReplacementResult Result = IsTargetedForReplacement(ForwardReference, NativizationOptions);
if (Result == EReplacementResult::ReplaceCompletely)
{
TSet<const UBlueprintGeneratedClass*> ReverseReferencedAssets;
GatherClassAssetsReferencedByStruct(ReverseReferencedAssets, ForwardReference);
for (const UBlueprintGeneratedClass* ReverseReference : ReverseReferencedAssets)
{
if (ReverseReference->IsChildOf(InClass))
{
return true;
}
}
}
}
return false;
}
void FBlueprintNativeCodeGenModule::FillPlatformNativizationDetails(const ITargetPlatform* Platform, FPlatformNativizationDetails& Details)
{
check(Platform);
const PlatformInfo::FPlatformInfo& PlatformInfo = Platform->GetPlatformInfo();
Details.PlatformName = PlatformInfo.TargetPlatformName;
Details.CompilerNativizationOptions.PlatformName = Details.PlatformName;
Details.CompilerNativizationOptions.ClientOnlyPlatform = Platform->IsClientOnly();
Details.CompilerNativizationOptions.ServerOnlyPlatform = Platform->IsServerOnly();
auto GatherExcludedStuff = [&](const TCHAR* KeyForExcludedModules, const TCHAR* KeyForExcludedPaths, const TCHAR* KeyForExcludedAssets)
{
const TCHAR* ConfigSection = TEXT("BlueprintNativizationSettings");
{
TArray<FString> ExcludedModuls;
GConfig->GetArray(ConfigSection, KeyForExcludedModules, ExcludedModuls, GEditorIni);
for (const FString& NameStr : ExcludedModuls)
{
Details.CompilerNativizationOptions.ExcludedModules.Add(FName(*NameStr));
}
}
GConfig->GetArray(ConfigSection, KeyForExcludedPaths, Details.CompilerNativizationOptions.ExcludedFolderPaths, GEditorIni);
{
TArray<FString> ExcludedAssetPaths;
GConfig->GetArray(ConfigSection, KeyForExcludedAssets, ExcludedAssetPaths, GEditorIni);
for (FString& Path : ExcludedAssetPaths)
{
Details.CompilerNativizationOptions.ExcludedAssets.Add(FSoftObjectPath(Path));
}
}
};
if (Details.CompilerNativizationOptions.ServerOnlyPlatform)
{
GatherExcludedStuff(TEXT("ModulsExcludedFromNativizedServer"), TEXT("ExcludedFolderPathsFromServer"), TEXT("ExcludedAssetsFromServer"));
}
if (Details.CompilerNativizationOptions.ClientOnlyPlatform)
{
GatherExcludedStuff(TEXT("ModulsExcludedFromNativizedClient"), TEXT("ExcludedFolderPathsFromClient"), TEXT("ExcludedAssetsFromClient"));
}
}
IMPLEMENT_MODULE(FBlueprintNativeCodeGenModule, BlueprintNativeCodeGen);