2015-09-04 14:07:57 -04:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "BlueprintNativeCodeGenPCH.h"
|
|
|
|
|
#include "BlueprintNativeCodeGenUtils.h"
|
2015-09-18 14:01:11 -04:00
|
|
|
#include "NativeCodeGenCommandlineParams.h"
|
2015-09-04 14:07:57 -04:00
|
|
|
#include "BlueprintNativeCodeGenCoordinator.h"
|
|
|
|
|
#include "Kismet2/KismetReinstanceUtilities.h" // for FBlueprintCompileReinstancer
|
|
|
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
|
|
|
#include "KismetCompilerModule.h"
|
|
|
|
|
#include "Engine/Blueprint.h"
|
|
|
|
|
#include "Engine/UserDefinedStruct.h"
|
|
|
|
|
#include "Engine/UserDefinedEnum.h"
|
|
|
|
|
#include "Kismet2/KismetEditorUtilities.h" // for CompileBlueprint()
|
2015-09-09 18:51:40 -04:00
|
|
|
#include "OutputDevice.h" // for GWarn
|
2015-09-18 14:01:11 -04:00
|
|
|
#include "GameProjectUtils.h" // for GenerateGameModuleBuildFile
|
|
|
|
|
|
|
|
|
|
DEFINE_LOG_CATEGORY(LogBlueprintCodeGen)
|
2015-09-04 14:07:57 -04:00
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* BlueprintNativeCodeGenUtilsImpl
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
|
namespace BlueprintNativeCodeGenUtilsImpl
|
|
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
static bool WipeTargetPaths(const TArray<FString>& TargetPaths);
|
2015-09-09 18:51:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
2015-09-18 14:01:11 -04:00
|
|
|
static bool BlueprintNativeCodeGenUtilsImpl::WipeTargetPaths(const TArray<FString>& TargetPaths)
|
2015-09-09 18:51:40 -04:00
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
IFileManager& FileManager = IFileManager::Get();
|
2015-09-09 18:51:40 -04:00
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
bool bSuccess = true;
|
|
|
|
|
for (const FString& Path : TargetPaths)
|
2015-09-09 18:51:40 -04:00
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
if (FileManager.FileExists(*Path))
|
2015-09-09 18:51:40 -04:00
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
bSuccess &= FileManager.Delete(*Path);
|
|
|
|
|
}
|
|
|
|
|
else if (FileManager.DirectoryExists(*Path))
|
2015-09-09 18:51:40 -04:00
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
bSuccess &= FileManager.DeleteDirectory(*Path, /*RequireExists =*/false, /*Tree =*/true);
|
|
|
|
|
}
|
2015-09-09 18:51:40 -04:00
|
|
|
}
|
|
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
return bSuccess;
|
2015-09-09 18:51:40 -04:00
|
|
|
}
|
|
|
|
|
|
2015-09-04 14:07:57 -04:00
|
|
|
/*******************************************************************************
|
|
|
|
|
* FBlueprintNativeCodeGenUtils
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
2015-09-18 14:01:11 -04:00
|
|
|
bool FBlueprintNativeCodeGenUtils::GenerateCodeModule(const FNativeCodeGenCommandlineParams& CommandParams)
|
2015-09-04 14:07:57 -04:00
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
FScopedFeedbackContext ScopedErrorTracker;
|
|
|
|
|
FBlueprintNativeCodeGenCoordinator Coordinator(CommandParams);
|
|
|
|
|
|
|
|
|
|
if (ScopedErrorTracker.HasErrors())
|
|
|
|
|
{
|
|
|
|
|
// creating the coordinator/manifest produced an error... do not carry on!
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const FBlueprintNativeCodeGenManifest& Manifest = Coordinator.GetManifest();
|
|
|
|
|
|
|
|
|
|
if (CommandParams.bWipeRequested && !CommandParams.bPreviewRequested)
|
|
|
|
|
{
|
|
|
|
|
TArray<FString> TargetPaths = Manifest.GetTargetPaths();
|
|
|
|
|
if (!BlueprintNativeCodeGenUtilsImpl::WipeTargetPaths(TargetPaths))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogBlueprintCodeGen, Warning, TEXT("Failed to wipe target files/directories."));
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-04 14:07:57 -04:00
|
|
|
|
2015-09-09 18:51:40 -04:00
|
|
|
TSharedPtr<FString> HeaderSource(new FString());
|
2015-09-18 14:01:11 -04:00
|
|
|
TSharedPtr<FString> CppSource(new FString());
|
2015-09-09 18:51:40 -04:00
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
auto ConvertSingleAsset = [&Coordinator, &HeaderSource, &CppSource](FConvertedAssetRecord& ConversionRecord)->bool
|
2015-09-04 14:07:57 -04:00
|
|
|
{
|
2015-09-18 16:22:03 -04:00
|
|
|
FScopedFeedbackContext NestedErrorTracker;
|
2015-09-04 14:07:57 -04:00
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
UObject* AssetObj = ConversionRecord.AssetPtr.Get();
|
|
|
|
|
if (AssetObj != nullptr)
|
|
|
|
|
{
|
|
|
|
|
FBlueprintNativeCodeGenUtils::GenerateCppCode(AssetObj, HeaderSource, CppSource);
|
|
|
|
|
}
|
2015-09-04 14:07:57 -04:00
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
bool bSuccess = !HeaderSource->IsEmpty() || !CppSource->IsEmpty();
|
2015-09-04 14:07:57 -04:00
|
|
|
if (!HeaderSource->IsEmpty())
|
|
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
if (!FFileHelper::SaveStringToFile(*HeaderSource, *ConversionRecord.GeneratedHeaderPath))
|
|
|
|
|
{
|
|
|
|
|
bSuccess &= false;
|
|
|
|
|
ConversionRecord.GeneratedHeaderPath.Empty();
|
|
|
|
|
}
|
|
|
|
|
HeaderSource->Empty(HeaderSource->Len());
|
2015-09-04 14:07:57 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ConversionRecord.GeneratedHeaderPath.Empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!CppSource->IsEmpty())
|
|
|
|
|
{
|
2015-09-18 14:01:11 -04:00
|
|
|
if (!FFileHelper::SaveStringToFile(*CppSource, *ConversionRecord.GeneratedCppPath))
|
|
|
|
|
{
|
|
|
|
|
bSuccess &= false;
|
|
|
|
|
ConversionRecord.GeneratedCppPath.Empty();
|
|
|
|
|
}
|
|
|
|
|
CppSource->Empty(CppSource->Len());
|
2015-09-04 14:07:57 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ConversionRecord.GeneratedCppPath.Empty();
|
|
|
|
|
}
|
2015-09-18 16:22:03 -04:00
|
|
|
return bSuccess && !NestedErrorTracker.HasErrors();
|
2015-09-18 14:01:11 -04:00
|
|
|
};
|
2015-09-09 18:51:40 -04:00
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
FBlueprintNativeCodeGenCoordinator::FConversionDelegate ConversionDelegate = FBlueprintNativeCodeGenCoordinator::FConversionDelegate::CreateLambda(ConvertSingleAsset);
|
|
|
|
|
if (CommandParams.bPreviewRequested)
|
|
|
|
|
{
|
|
|
|
|
ConversionDelegate.BindLambda([](FConvertedAssetRecord& ConversionRecord){ return true; });
|
2015-09-04 14:07:57 -04:00
|
|
|
}
|
|
|
|
|
|
2015-09-18 14:01:11 -04:00
|
|
|
const bool bSuccess = Coordinator.ProcessConversionQueue(ConversionDelegate);
|
|
|
|
|
// save the manifest regardless of success (so we can get an idea of how successful it was)
|
|
|
|
|
Manifest.Save();
|
|
|
|
|
return bSuccess & !ScopedErrorTracker.HasErrors();
|
2015-09-04 14:07:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void FBlueprintNativeCodeGenUtils::GenerateCppCode(UObject* Obj, TSharedPtr<FString> OutHeaderSource, TSharedPtr<FString> OutCppSource)
|
|
|
|
|
{
|
|
|
|
|
auto UDEnum = Cast<UUserDefinedEnum>(Obj);
|
|
|
|
|
auto UDStruct = Cast<UUserDefinedStruct>(Obj);
|
|
|
|
|
auto BPGC = Cast<UClass>(Obj);
|
|
|
|
|
auto InBlueprintObj = BPGC ? Cast<UBlueprint>(BPGC->ClassGeneratedBy) : Cast<UBlueprint>(Obj);
|
|
|
|
|
|
|
|
|
|
OutHeaderSource->Empty();
|
|
|
|
|
OutCppSource->Empty();
|
|
|
|
|
|
|
|
|
|
if (InBlueprintObj)
|
|
|
|
|
{
|
|
|
|
|
check(InBlueprintObj->GetOutermost() != GetTransientPackage());
|
|
|
|
|
checkf(InBlueprintObj->GeneratedClass, TEXT("Invalid generated class for %s"), *InBlueprintObj->GetName());
|
|
|
|
|
check(OutHeaderSource.IsValid());
|
|
|
|
|
check(OutCppSource.IsValid());
|
|
|
|
|
|
|
|
|
|
auto BlueprintObj = InBlueprintObj;
|
|
|
|
|
{
|
|
|
|
|
TSharedPtr<FBlueprintCompileReinstancer> Reinstancer = FBlueprintCompileReinstancer::Create(BlueprintObj->GeneratedClass);
|
|
|
|
|
|
|
|
|
|
IKismetCompilerInterface& Compiler = FModuleManager::LoadModuleChecked<IKismetCompilerInterface>(KISMET_COMPILER_MODULENAME);
|
|
|
|
|
|
|
|
|
|
TGuardValue<bool> GuardTemplateNameFlag(GCompilingBlueprint, true);
|
|
|
|
|
FCompilerResultsLog Results;
|
|
|
|
|
|
|
|
|
|
FKismetCompilerOptions CompileOptions;
|
|
|
|
|
CompileOptions.CompileType = EKismetCompileType::Cpp;
|
|
|
|
|
CompileOptions.OutCppSourceCode = OutCppSource;
|
|
|
|
|
CompileOptions.OutHeaderSourceCode = OutHeaderSource;
|
|
|
|
|
Compiler.CompileBlueprint(BlueprintObj, CompileOptions, Results);
|
|
|
|
|
|
|
|
|
|
if (EBlueprintType::BPTYPE_Interface == BlueprintObj->BlueprintType && OutCppSource.IsValid())
|
|
|
|
|
{
|
|
|
|
|
OutCppSource->Empty(); // ugly temp hack
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FKismetEditorUtilities::CompileBlueprint(BlueprintObj);
|
|
|
|
|
}
|
|
|
|
|
else if ((UDEnum || UDStruct) && OutHeaderSource.IsValid())
|
|
|
|
|
{
|
|
|
|
|
IKismetCompilerInterface& Compiler = FModuleManager::LoadModuleChecked<IKismetCompilerInterface>(KISMET_COMPILER_MODULENAME);
|
|
|
|
|
if (UDEnum)
|
|
|
|
|
{
|
|
|
|
|
*OutHeaderSource = Compiler.GenerateCppCodeForEnum(UDEnum);
|
|
|
|
|
}
|
|
|
|
|
else if (UDStruct)
|
|
|
|
|
{
|
|
|
|
|
*OutHeaderSource = Compiler.GenerateCppCodeForStruct(UDStruct);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ensure(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-18 14:01:11 -04:00
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* FScopedFeedbackContext
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
FBlueprintNativeCodeGenUtils::FScopedFeedbackContext::FScopedFeedbackContext()
|
|
|
|
|
: OldContext(GWarn)
|
|
|
|
|
, ErrorCount(0)
|
|
|
|
|
, WarningCount(0)
|
|
|
|
|
{
|
|
|
|
|
TreatWarningsAsErrors = GWarn->TreatWarningsAsErrors;
|
|
|
|
|
GWarn = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
FBlueprintNativeCodeGenUtils::FScopedFeedbackContext::~FScopedFeedbackContext()
|
|
|
|
|
{
|
|
|
|
|
GWarn = OldContext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
bool FBlueprintNativeCodeGenUtils::FScopedFeedbackContext::HasErrors()
|
|
|
|
|
{
|
|
|
|
|
return (ErrorCount > 0) || (TreatWarningsAsErrors && (WarningCount > 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void FBlueprintNativeCodeGenUtils::FScopedFeedbackContext::Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category)
|
|
|
|
|
{
|
|
|
|
|
switch (Verbosity)
|
|
|
|
|
{
|
|
|
|
|
case ELogVerbosity::Warning:
|
|
|
|
|
{
|
|
|
|
|
++WarningCount;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ELogVerbosity::Error:
|
|
|
|
|
case ELogVerbosity::Fatal:
|
|
|
|
|
{
|
|
|
|
|
++ErrorCount;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OldContext->Serialize(V, Verbosity, Category);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void FBlueprintNativeCodeGenUtils::FScopedFeedbackContext::Flush()
|
|
|
|
|
{
|
|
|
|
|
WarningCount = ErrorCount = 0;
|
|
|
|
|
OldContext->Flush();
|
|
|
|
|
}
|