2014-12-07 19:09:38 -05:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
|
KismetCompilerModule.cpp
|
|
|
|
|
=============================================================================*/
|
|
|
|
|
|
|
|
|
|
#include "KismetCompilerPrivatePCH.h"
|
|
|
|
|
|
|
|
|
|
#include "KismetCompiler.h"
|
|
|
|
|
#include "AnimBlueprintCompiler.h"
|
|
|
|
|
|
|
|
|
|
#include "Editor/UnrealEd/Public/Kismet2/KismetDebugUtilities.h"
|
|
|
|
|
#include "Editor/UnrealEd/Public/Kismet2/KismetReinstanceUtilities.h"
|
|
|
|
|
|
|
|
|
|
#include "UserDefinedStructureCompilerUtils.h"
|
|
|
|
|
|
|
|
|
|
DEFINE_LOG_CATEGORY(LogK2Compiler);
|
2015-01-15 15:57:51 -05:00
|
|
|
DECLARE_CYCLE_STAT(TEXT("Compile Time"), EKismetCompilerStats_CompileTime, STATGROUP_KismetCompiler);
|
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Compile Skeleton Class"), EKismetCompilerStats_CompileSkeletonClass, STATGROUP_KismetCompiler);
|
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Compile Generated Class"), EKismetCompilerStats_CompileGeneratedClass, STATGROUP_KismetCompiler);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
IMPLEMENT_MODULE( FKismet2CompilerModule, KismetCompiler );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "KismetCompiler"
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// FKismet2CompilerModule
|
|
|
|
|
|
|
|
|
|
struct FBlueprintIsBeingCompiledHelper
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
UBlueprint* Blueprint;
|
|
|
|
|
public:
|
|
|
|
|
FBlueprintIsBeingCompiledHelper(UBlueprint* InBlueprint) : Blueprint(InBlueprint)
|
|
|
|
|
{
|
|
|
|
|
check(NULL != Blueprint);
|
|
|
|
|
check(!Blueprint->bBeingCompiled);
|
|
|
|
|
Blueprint->bBeingCompiled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~FBlueprintIsBeingCompiledHelper()
|
|
|
|
|
{
|
|
|
|
|
Blueprint->bBeingCompiled = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Compiles a blueprint.
|
2014-10-07 00:40:02 -04:00
|
|
|
void FKismet2CompilerModule::CompileBlueprintInner(class UBlueprint* Blueprint, const FKismetCompilerOptions& CompileOptions, FCompilerResultsLog& Results, TArray<UObject*>* ObjLoaded)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
FBlueprintIsBeingCompiledHelper BeingCompiled(Blueprint);
|
|
|
|
|
|
|
|
|
|
Blueprint->CurrentMessageLog = &Results;
|
|
|
|
|
|
|
|
|
|
// Early out if blueprint parent is missing
|
|
|
|
|
if (Blueprint->ParentClass == NULL)
|
|
|
|
|
{
|
|
|
|
|
Results.Error(*LOCTEXT("KismetCompileError_MalformedParentClasss", "Blueprint @@ has missing or NULL parent class.").ToString(), Blueprint);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-04-23 19:55:18 -04:00
|
|
|
// Loop through all external compiler delegates attempting to compile the blueprint.
|
2014-12-15 15:31:42 -05:00
|
|
|
bool Compiled = false;
|
|
|
|
|
for ( IBlueprintCompiler* Compiler : Compilers )
|
2014-04-23 19:55:18 -04:00
|
|
|
{
|
2014-12-15 15:31:42 -05:00
|
|
|
if ( Compiler->CanCompile(Blueprint) )
|
2014-04-23 19:55:18 -04:00
|
|
|
{
|
2014-12-15 15:31:42 -05:00
|
|
|
Compiled = true;
|
|
|
|
|
Compiler->Compile(Blueprint, CompileOptions, Results, ObjLoaded);
|
2014-04-23 19:55:18 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if no one handles it, then use the default blueprint compiler.
|
2014-12-15 15:31:42 -05:00
|
|
|
if ( !Compiled )
|
2014-04-23 19:55:18 -04:00
|
|
|
{
|
|
|
|
|
if ( UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint) )
|
|
|
|
|
{
|
|
|
|
|
FAnimBlueprintCompiler Compiler(AnimBlueprint, Results, CompileOptions, ObjLoaded);
|
|
|
|
|
Compiler.Compile();
|
|
|
|
|
check(Compiler.NewClass);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FKismetCompilerContext Compiler(Blueprint, Results, CompileOptions, ObjLoaded);
|
|
|
|
|
Compiler.Compile();
|
|
|
|
|
check(Compiler.NewClass);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Blueprint->CurrentMessageLog = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-23 20:18:55 -04:00
|
|
|
|
|
|
|
|
void FKismet2CompilerModule::CompileStructure(class UUserDefinedStruct* Struct, FCompilerResultsLog& Results)
|
|
|
|
|
{
|
2014-10-07 00:40:02 -04:00
|
|
|
Results.SetSourceName(Struct->GetName());
|
|
|
|
|
BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileTime);
|
2014-04-23 20:18:55 -04:00
|
|
|
FUserDefinedStructureCompilerUtils::CompileStruct(Struct, Results, true);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
// Compiles a blueprint.
|
|
|
|
|
void FKismet2CompilerModule::CompileBlueprint(class UBlueprint* Blueprint, const FKismetCompilerOptions& CompileOptions, FCompilerResultsLog& Results, FBlueprintCompileReinstancer* ParentReinstancer, TArray<UObject*>* ObjLoaded)
|
|
|
|
|
{
|
|
|
|
|
SCOPE_SECONDS_COUNTER(GBlueprintCompileTime);
|
2014-10-07 00:40:02 -04:00
|
|
|
BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileTime);
|
|
|
|
|
|
|
|
|
|
Results.SetSourceName(Blueprint->GetName());
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
const bool bIsBrandNewBP = (Blueprint->SkeletonGeneratedClass == NULL) && (Blueprint->GeneratedClass == NULL) && (Blueprint->ParentClass != NULL) && !CompileOptions.bIsDuplicationInstigated;
|
|
|
|
|
|
2014-12-15 15:31:42 -05:00
|
|
|
for ( IBlueprintCompiler* Compiler : Compilers )
|
|
|
|
|
{
|
|
|
|
|
Compiler->PreCompile(Blueprint);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 14:22:46 -05:00
|
|
|
if (CompileOptions.CompileType != EKismetCompileType::Cpp)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2015-01-15 15:57:51 -05:00
|
|
|
BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileSkeletonClass);
|
2014-10-07 00:40:02 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
FBlueprintCompileReinstancer SkeletonReinstancer(Blueprint->SkeletonGeneratedClass);
|
|
|
|
|
|
|
|
|
|
FCompilerResultsLog SkeletonResults;
|
|
|
|
|
SkeletonResults.bSilentMode = true;
|
|
|
|
|
FKismetCompilerOptions SkeletonCompileOptions;
|
|
|
|
|
SkeletonCompileOptions.CompileType = EKismetCompileType::SkeletonOnly;
|
2014-10-07 00:40:02 -04:00
|
|
|
CompileBlueprintInner(Blueprint, SkeletonCompileOptions, SkeletonResults, ObjLoaded);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this was a full compile, take appropriate actions depending on the success of failure of the compile
|
|
|
|
|
if( CompileOptions.IsGeneratedClassCompileType() )
|
|
|
|
|
{
|
2015-01-15 15:57:51 -05:00
|
|
|
BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileGeneratedClass);
|
2014-10-07 00:40:02 -04:00
|
|
|
|
2014-04-23 19:55:18 -04:00
|
|
|
// Perform the full compile
|
2014-10-07 00:40:02 -04:00
|
|
|
CompileBlueprintInner(Blueprint, CompileOptions, Results, ObjLoaded);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
if (Results.NumErrors == 0)
|
|
|
|
|
{
|
|
|
|
|
// Blueprint is error free. Go ahead and fix up debug info
|
|
|
|
|
Blueprint->Status = (0 == Results.NumWarnings) ? BS_UpToDate : BS_UpToDateWithWarnings;
|
|
|
|
|
|
|
|
|
|
Blueprint->BlueprintSystemVersion = UBlueprint::GetCurrentBlueprintSystemVersion();
|
|
|
|
|
|
|
|
|
|
// Reapply breakpoints to the bytecode of the new class
|
|
|
|
|
for (int32 Index = 0; Index < Blueprint->Breakpoints.Num(); ++Index)
|
|
|
|
|
{
|
|
|
|
|
UBreakpoint* Breakpoint = Blueprint->Breakpoints[Index];
|
|
|
|
|
FKismetDebugUtilities::ReapplyBreakpoint(Breakpoint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Should never get errors from a brand new blueprint!
|
|
|
|
|
ensure(!bIsBrandNewBP || (Results.NumErrors == 0));
|
|
|
|
|
|
|
|
|
|
// There were errors. Compile the generated class to have function stubs
|
|
|
|
|
Blueprint->Status = BS_Error;
|
|
|
|
|
|
2014-10-21 06:32:50 -04:00
|
|
|
static const FBoolConfigValueHelper ReinstanceOnlyWhenNecessary(TEXT("Kismet"), TEXT("bReinstanceOnlyWhenNecessary"), GEngineIni);
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
// Reinstance objects here, so we can preserve their memory layouts to reinstance them again
|
|
|
|
|
if( ParentReinstancer != NULL )
|
|
|
|
|
{
|
|
|
|
|
ParentReinstancer->UpdateBytecodeReferences();
|
|
|
|
|
|
|
|
|
|
if(!Blueprint->bIsRegeneratingOnLoad)
|
|
|
|
|
{
|
2014-10-21 06:32:50 -04:00
|
|
|
ParentReinstancer->ReinstanceObjects(!ReinstanceOnlyWhenNecessary);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FBlueprintCompileReinstancer StubReinstancer(Blueprint->GeneratedClass);
|
|
|
|
|
|
|
|
|
|
// Toss the half-baked class and generate a stubbed out skeleton class that can be used
|
|
|
|
|
FCompilerResultsLog StubResults;
|
|
|
|
|
StubResults.bSilentMode = true;
|
|
|
|
|
FKismetCompilerOptions StubCompileOptions(CompileOptions);
|
|
|
|
|
StubCompileOptions.CompileType = EKismetCompileType::StubAfterFailure;
|
|
|
|
|
|
2014-10-07 00:40:02 -04:00
|
|
|
CompileBlueprintInner(Blueprint, StubCompileOptions, StubResults, ObjLoaded);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
StubReinstancer.UpdateBytecodeReferences();
|
|
|
|
|
if( !Blueprint->bIsRegeneratingOnLoad )
|
|
|
|
|
{
|
2014-10-21 06:32:50 -04:00
|
|
|
StubReinstancer.ReinstanceObjects(!ReinstanceOnlyWhenNecessary);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 15:31:42 -05:00
|
|
|
for ( IBlueprintCompiler* Compiler : Compilers )
|
|
|
|
|
{
|
|
|
|
|
Compiler->PostCompile(Blueprint);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
UPackage* Package = Blueprint->GetOutermost();
|
|
|
|
|
if( Package )
|
|
|
|
|
{
|
|
|
|
|
UMetaData* MetaData = Package->GetMetaData();
|
|
|
|
|
MetaData->RemoveMetaDataOutsidePackage();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Recover a corrupted blueprint
|
|
|
|
|
void FKismet2CompilerModule::RecoverCorruptedBlueprint(class UBlueprint* Blueprint)
|
|
|
|
|
{
|
|
|
|
|
UPackage* Package = Blueprint->GetOutermost();
|
|
|
|
|
|
|
|
|
|
// Get rid of any stale classes
|
|
|
|
|
for (FObjectIterator ObjIt; ObjIt; ++ObjIt)
|
|
|
|
|
{
|
|
|
|
|
UObject* TestObject = *ObjIt;
|
|
|
|
|
if (TestObject->GetOuter() == Package)
|
|
|
|
|
{
|
|
|
|
|
// This object is in the blueprint package; is it expected?
|
|
|
|
|
if (UClass* TestClass = Cast<UClass>(TestObject))
|
|
|
|
|
{
|
|
|
|
|
if ((TestClass != Blueprint->SkeletonGeneratedClass) && (TestClass != Blueprint->GeneratedClass))
|
|
|
|
|
{
|
|
|
|
|
// Unexpected UClass
|
|
|
|
|
FKismetCompilerUtilities::ConsignToOblivion(TestClass, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FKismet2CompilerModule::RemoveBlueprintGeneratedClasses(class UBlueprint* Blueprint)
|
|
|
|
|
{
|
|
|
|
|
if (Blueprint != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (Blueprint->GeneratedClass != NULL)
|
|
|
|
|
{
|
|
|
|
|
FKismetCompilerUtilities::ConsignToOblivion(Blueprint->GeneratedClass, Blueprint->bIsRegeneratingOnLoad);
|
|
|
|
|
Blueprint->GeneratedClass = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Blueprint->SkeletonGeneratedClass != NULL)
|
|
|
|
|
{
|
|
|
|
|
FKismetCompilerUtilities::ConsignToOblivion(Blueprint->SkeletonGeneratedClass, Blueprint->bIsRegeneratingOnLoad);
|
|
|
|
|
Blueprint->SkeletonGeneratedClass = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|