Files
UnrealEngineUWP/Engine/Source/Programs/UnrealHeaderTool/Private/CodeGenerator.cpp
Steve Robb 3caf54a446 Build time fixes.
* Fixed a bug where the shared PCH file was being nulled out if it wasn't from a global PCH environment.
* The PCH environment for .generated.cpp files is now taken taken from associated module's environment.
* Split .generated.cpp files now include the module PCH at the top of every file.
* PCH inclusion is now always forced.

#codereview robert.manuszewski,mike.fricker

[CL 2055711 by Steve Robb in Main branch]
2014-04-24 15:04:34 -04:00

5017 lines
180 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "UnrealHeaderTool.h"
#include "UniquePtr.h"
#include "ParserHelper.h"
#include "NativeClassExporter.h"
#include "HeaderParser.h"
#include "ClassMaps.h"
#include "Manifest.h"
#include "StringUtils.h"
/////////////////////////////////////////////////////
// Globals
FManifest GManifest;
static TArray<FString> ChangeMessages;
static bool bWriteContents = false;
static bool bVerifyContents = false;
static const bool bMultiLineUFUNCTION = true;
static const bool bMultiLineUPROPERTY = true;
static UClass* GenerateCodeForHeader(UObject* InParent, const TCHAR* Name, EObjectFlags Flags, const TCHAR* Buffer, int32& OutClassDeclLine);
FCompilerMetadataManager* GScriptHelper = NULL;
/** C++ name lookup helper */
FNameLookupCPP* NameLookupCPP;
/////////////////////////////////////////////////////
// FFlagAudit
//@todo: UCREMOVAL this is all audit stuff
static struct FFlagAudit
{
struct Pair
{
FString Name;
uint64 Flags;
Pair(UObject* Source, const TCHAR* FlagType, uint64 InFlags)
{
Name = Source->GetFullName() + TEXT("[") + FlagType + TEXT("]");
Flags = InFlags;
}
};
TArray<Pair> Items;
void Add(UObject* Source, const TCHAR* FlagType, uint64 Flags)
{
new (Items) Pair(Source, FlagType, Flags);
}
void WriteResults()
{
bool bDoDiff = false;
FString Filename;
FString RefFilename = FString(FPaths::GameSavedDir()) / TEXT("ReferenceFlags.txt");
if( !FParse::Param( FCommandLine::Get(), TEXT("WRITEFLAGS") ) )
{
return;
}
if( FParse::Param( FCommandLine::Get(), TEXT("WRITEREF") ) )
{
Filename = RefFilename;
}
else if( FParse::Param( FCommandLine::Get(), TEXT("VERIFYREF") ) )
{
Filename = FString(FPaths::GameSavedDir()) / TEXT("VerifyFlags.txt");
bDoDiff = true;
}
struct FComparePairByName
{
FORCEINLINE bool operator()( const FFlagAudit::Pair& A, const FFlagAudit::Pair& B ) const { return A.Name < B.Name; }
};
Items.Sort( FComparePairByName() );
int32 MaxLen = 0;
for (int32 Index = 0; Index < Items.Num(); Index++)
{
MaxLen = FMath::Max<int32>(Items[Index].Name.Len(), MaxLen);
}
MaxLen += 4;
FStringOutputDevice File;
for (int32 Index = 0; Index < Items.Num(); Index++)
{
File.Logf(TEXT("%s%s0x%016llx\r\n"), *Items[Index].Name, FCString::Spc(MaxLen - Items[Index].Name.Len()), Items[Index].Flags);
}
FFileHelper::SaveStringToFile(File, *Filename);
if (bDoDiff)
{
FString Verify = File;
FString Ref;
if (FFileHelper::LoadFileToString(Ref, *RefFilename))
{
FStringOutputDevice MisMatches;
TArray<FString> VerifyLines;
Verify.ParseIntoArray(&VerifyLines, TEXT("\n"), true);
TArray<FString> RefLines;
Ref.ParseIntoArray(&RefLines, TEXT("\n"), true);
check(VerifyLines.Num() == RefLines.Num()); // we aren't doing a sophisticated diff
for (int32 Index = 0; Index < RefLines.Num(); Index++)
{
if (RefLines[Index] != VerifyLines[Index])
{
MisMatches.Logf(TEXT("REF : %s"), *RefLines[Index]);
MisMatches.Logf(TEXT("VERIFY: %s"), *VerifyLines[Index]);
}
}
FString DiffFilename = FString(FPaths::GameSavedDir()) / TEXT("FlagsDiff.txt");
FFileHelper::SaveStringToFile(MisMatches, *DiffFilename);
}
}
}
} TheFlagAudit;
static FString Tabify(const TCHAR *Input)
{
FString Result(Input);
const int32 SpacesPerTab = 4;
Result.ReplaceInline(TEXT("\r\n"), TEXT("\n"));
TArray<FString> Lines;
Result.ParseIntoArray( &Lines, TEXT( "\n" ), false );
Result = TEXT("");
for (int32 Index = 0; Index < Lines.Num(); Index++)
{
if (Index)
{
Result += TEXT("\r\n");
}
FString& Line = Lines[Index];
int32 FirstNonWS = 0;
while (1)
{
TCHAR c = *(*Line + FirstNonWS);
if (c != TEXT('\t') && c != TEXT(' '))
{
break;
}
FirstNonWS++;
}
FString Start = Line.Left(FirstNonWS);
FString Remainder = *Line + FirstNonWS;
Start.ReplaceInline(TEXT("\t"), FCString::Spc(SpacesPerTab));
int32 NumTabs = Start.Len() / SpacesPerTab;
for (int32 Tab = 0; Tab < NumTabs; Tab++)
{
Result += TEXT("\t");
}
Result += FCString::Spc(Start.Len() - NumTabs * SpacesPerTab);
Result += Remainder;
}
return Result;
}
/**
* Helper function for finding the location of a package
* This is required as source now lives in several possible directories
*
* @param InPackage The name of the package of interest
* @param OutLocation The location of the given package, if found
* @param OutHeaderLocation The directory where generated headers should be placed
*
* @return bool true if found, false if not
*/
bool MakeCommandlet_FindPackageLocation(const TCHAR* InPackage, FString& OutLocation, FString& OutHeaderLocation)
{
// Mapping of processed packages to their locations
// An empty location string means it was processed but not found
static TMap<FString, FManifestModule*> CheckedPackageList;
FString CheckPackage(InPackage);
auto* ModuleInfoPtr = CheckedPackageList.FindRef(CheckPackage);
if (!ModuleInfoPtr)
{
auto* ModuleInfoPtr2 = GManifest.Modules.FindByPredicate([&](FManifestModule& Module) { return Module.Name == CheckPackage; });
if (ModuleInfoPtr2 && IFileManager::Get().DirectoryExists(*ModuleInfoPtr2->BaseDirectory))
{
ModuleInfoPtr = ModuleInfoPtr2;
CheckedPackageList.Add(CheckPackage, ModuleInfoPtr);
}
}
if (!ModuleInfoPtr)
return false;
OutLocation = ModuleInfoPtr->BaseDirectory;
OutHeaderLocation = ModuleInfoPtr->GeneratedIncludeDirectory;
return true;
}
FString Macroize(const TCHAR *MacroName, const TCHAR *StringToMacroize)
{
FString Result = StringToMacroize;
if (Result.Len())
{
Result.ReplaceInline(TEXT("\r\n"), TEXT("\n"));
Result.ReplaceInline(TEXT("\n"), TEXT(" \\\n"));
check(Result.EndsWith(TEXT(" \\\n")));
Result = Result.LeftChop(3);
Result += TEXT("\n\n\n");
Result.ReplaceInline(TEXT("\n"), TEXT("\r\n"));
}
return FString::Printf(TEXT("#define %s%s\r\n"), MacroName, Result.Len() ? TEXT(" \\") : TEXT("")) + Result;
}
struct FParmsAndReturnProperties
{
FParmsAndReturnProperties()
: Return(NULL)
{
}
#if PLATFORM_COMPILER_HAS_DEFAULTED_FUNCTIONS
FParmsAndReturnProperties(FParmsAndReturnProperties&&) = default;
FParmsAndReturnProperties(const FParmsAndReturnProperties&) = default;
FParmsAndReturnProperties& operator=(FParmsAndReturnProperties&&) = default;
FParmsAndReturnProperties& operator=(const FParmsAndReturnProperties&) = default;
#else
FParmsAndReturnProperties( FParmsAndReturnProperties&& Other) : Parms(MoveTemp(Other.Parms)), Return(MoveTemp(Other.Return)) {}
FParmsAndReturnProperties(const FParmsAndReturnProperties& Other) : Parms( Other.Parms ), Return( Other.Return ) {}
FParmsAndReturnProperties& operator=( FParmsAndReturnProperties&& Other) { Parms = MoveTemp(Other.Parms); Return = MoveTemp(Other.Return); return *this; }
FParmsAndReturnProperties& operator=(const FParmsAndReturnProperties& Other) { Parms = Other.Parms ; Return = Other.Return ; return *this; }
#endif
bool HasParms() const
{
return Parms.Num() || Return;
}
TArray<UProperty*> Parms;
UProperty* Return;
};
/**
* Get parameters and return type for a given function.
*
* @param Function The function to get the parameters for.
* @return An aggregate containing the parameters and return type of that function.
*/
FParmsAndReturnProperties GetFunctionParmsAndReturn(UFunction* Function)
{
FParmsAndReturnProperties Result;
for ( TFieldIterator<UProperty> It(Function); It; ++It)
{
UProperty* Field = *It;
if ((It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm)
{
Result.Parms.Add(Field);
}
else if (It->PropertyFlags & CPF_ReturnParm)
{
Result.Return = Field;
}
}
return Result;
}
/**
* Determines whether the glue version of the specified native function
* should be exported
*
* @param Function the function to check
* @return true if the glue version of the function should be exported.
*/
bool FNativeClassHeaderGenerator::ShouldExportFunction( UFunction* Function )
{
// export any script stubs for native functions declared in interface classes
bool bIsBlueprintNativeEvent = (Function->FunctionFlags & FUNC_BlueprintEvent) && (Function->FunctionFlags & FUNC_Native);
if (Function->GetOwnerClass()->HasAnyClassFlags(CLASS_Interface) && !bIsBlueprintNativeEvent)
return true;
// always export if the function is static
if (Function->FunctionFlags & FUNC_Static)
return true;
// don't export the function if this is not the original declaration and there is
// at least one parent version of the function that is declared native
for (UFunction* ParentFunction = Function->GetSuperFunction(); ParentFunction; ParentFunction = ParentFunction->GetSuperFunction())
{
if (ParentFunction->FunctionFlags & FUNC_Native)
return false;
}
return true;
}
FString CreateLiteralString(const FString& Str)
{
FString Result = TEXT("TEXT(\"");
// Have a reasonable guess at reserving the right size
Result.Reserve(Str.Len() + Result.Len());
bool bPreviousCharacterWasHex = false;
const TCHAR* Ptr = *Str;
while (TCHAR Ch = *Ptr++)
{
switch (Ch)
{
case TEXT('\r'): continue;
case TEXT('\n'): Result += TEXT("\\n"); bPreviousCharacterWasHex = false; break;
case TEXT('\\'): Result += TEXT("\\\\"); bPreviousCharacterWasHex = false; break;
case TEXT('\"'): Result += TEXT("\\\""); bPreviousCharacterWasHex = false; break;
default:
if (Ch < 31 || Ch >= 128)
{
Result += FString::Printf(TEXT("\\x%04x"), Ch);
bPreviousCharacterWasHex = true;
}
else
{
// We close and open the literal (with TEXT) here in order to ensure that successive hex characters aren't appended to the hex sequence, causing a different number
if (bPreviousCharacterWasHex && FCharWide::IsHexDigit(Ch))
{
Result += "\")TEXT(\"";
}
bPreviousCharacterWasHex = false;
Result += Ch;
}
break;
}
}
Result += TEXT("\")");
return Result;
}
static FString GetMetaDataCodeForObject(UObject* Object, const TCHAR* SymbolName, const TCHAR* Spaces)
{
TMap<FName, FString>* MetaData = UMetaData::GetMapForObject(Object);
FString Result;
if (MetaData && MetaData->Num())
{
typedef TKeyValuePair<FName, FString> KVPType;
TArray<KVPType> KVPs;
for (auto& KVP : *MetaData)
{
KVPs.Add(KVPType(KVP.Key, KVP.Value));
}
// We sort the metadata here so that we can get consistent output across multiple runs
// even when metadata is added in a different order
KVPs.Sort([](const KVPType& Lhs, const KVPType& Rhs) { return Lhs.Key < Rhs.Key; });
for (const auto& KVP : KVPs)
{
Result += FString::Printf(TEXT("%sMetaData->SetValue(%s, TEXT(\"%s\"), %s);\r\n"), Spaces, SymbolName, *KVP.Key.ToString(), *CreateLiteralString(KVP.Value));
}
}
return Result;
}
/**
* Returns a C++ code string for declaring a class or variable as an export, if the class is configured for that
*
* @return The API declaration string
*/
FString FNativeClassHeaderGenerator::MakeAPIDecl() const
{
FString APIDecl;
if( CurrentClass->HasAnyClassFlags( CLASS_RequiredAPI ) )
{
APIDecl = FString::Printf( TEXT( "%s_API " ), *API );
}
return APIDecl;
}
/**
* Exports the struct's C++ properties to the specified output device and adds special
* compiler directives for GCC to pack as we expect.
*
* @param Struct UStruct to export properties
* @param TextIndent Current text indentation
* @param ImportsDefaults whether this struct will be serialized with a default value
*/
void FNativeClassHeaderGenerator::ExportProperties( UStruct* Struct, int32 TextIndent, bool bAccessSpecifiers, class FStringOutputDevice* Output )
{
UProperty* Previous = NULL;
UProperty* PreviousNonEditorOnly = NULL;
UProperty* LastInSuper = NULL;
UStruct* InheritanceSuper = Struct->GetInheritanceSuper();
bool bEmittedHasEditorOnlyMacro = false;
bool bEmittedHasScriptAlign = false;
check(Output != NULL)
FStringOutputDevice& HeaderOutput = *Output;
// Find last property in the lowest base class that has any properties
UStruct* CurrentSuper = InheritanceSuper;
while (LastInSuper == NULL && CurrentSuper)
{
for( TFieldIterator<UProperty> It(CurrentSuper,EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
UProperty* Current = *It;
// Disregard properties with 0 size like functions.
if( It.GetStruct() == CurrentSuper && Current->ElementSize )
{
LastInSuper = Current;
}
}
// go up a layer in the hierarchy
CurrentSuper = CurrentSuper->GetSuperStruct();
}
EPropertyHeaderExportFlags CurrentExportType = PROPEXPORT_Public;
// find structs that are nothing but bytes, account for editor only properties being
// removed on consoles
int32 NumProperties = 0;
int32 NumByteProperties = 0;
int32 NumNonEditorOnlyProperties = 0;
int32 NumNonEditorOnlyByteProperties = 0;
for( TFieldIterator<UProperty> It(Struct, EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
// treat bitfield and bytes the same
bool bIsByteProperty = It->IsA(UByteProperty::StaticClass());// || It->IsA(UBoolProperty::StaticClass());
bool bIsEditorOnlyProperty = It->IsEditorOnlyProperty();
// count our propertie
NumProperties++;
if (bIsByteProperty)
{
NumByteProperties++;
}
if (!bIsEditorOnlyProperty)
{
NumNonEditorOnlyProperties++;
}
if (!bIsEditorOnlyProperty && bIsByteProperty)
{
NumNonEditorOnlyByteProperties++;
}
}
bool bCurrentlyInNotCPPBlock = false;
// Iterate over all properties in this struct.
for( TFieldIterator<UProperty> It(Struct, EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
UProperty* Current = *It;
FStringOutputDevice PropertyText;
// Disregard properties with 0 size like functions.
if (It.GetStruct() == Struct)
{
FString AccessSpecifier = TEXT("public");
if (bAccessSpecifiers)
{
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
// find the compiler token for this property
FTokenData* PropData = ClassData->FindTokenData(Current);
if ( PropData != NULL )
{
// if this property has a different access specifier, then export that now
if ( (PropData->Token.PropertyExportFlags & CurrentExportType) == 0 )
{
if ( (PropData->Token.PropertyExportFlags & PROPEXPORT_Private) != 0 )
{
CurrentExportType = PROPEXPORT_Private;
AccessSpecifier = TEXT("private");
}
else if ( (PropData->Token.PropertyExportFlags & PROPEXPORT_Protected) != 0 )
{
CurrentExportType = PROPEXPORT_Protected;
AccessSpecifier = TEXT("protected");
}
else
{
CurrentExportType = PROPEXPORT_Public;
AccessSpecifier = TEXT("public");
}
if ( AccessSpecifier.Len() )
{
// If we are changing the access specifier we need to emit the #endif for the WITH_EDITORONLY_DATA macro first otherwise the access specifier may
// only be conditionally compiled in.
if( bEmittedHasEditorOnlyMacro )
{
PropertyText.Logf( TEXT("#endif // WITH_EDITORONLY_DATA\r\n") );
bEmittedHasEditorOnlyMacro = false;
}
PropertyText.Logf(TEXT("%s:%s"), *AccessSpecifier, LINE_TERMINATOR);
}
}
}
}
// If we are switching from editor to non-editor or vice versa and the state of the WITH_EDITORONLY_DATA macro emission doesn't match, generate the
// #if or #endif appropriately.
bool RequiresHasEditorOnlyMacro = Current->IsEditorOnlyProperty();
if( !bEmittedHasEditorOnlyMacro && RequiresHasEditorOnlyMacro )
{
// Indent code and export CPP text.
PropertyText.Logf( TEXT("#if WITH_EDITORONLY_DATA\r\n") );
bEmittedHasEditorOnlyMacro = true;
}
else if( bEmittedHasEditorOnlyMacro && !RequiresHasEditorOnlyMacro )
{
PropertyText.Logf( TEXT("#endif // WITH_EDITORONLY_DATA\r\n") );
bEmittedHasEditorOnlyMacro = false;
}
// Export property specifiers
// Indent code and export CPP text.
{
FStringOutputDevice JustPropertyDecl;
JustPropertyDecl.Log( FCString::Spc(TextIndent+4) );
const FString* Dim = GArrayDimensions.Find(Current);
Current->ExportCppDeclaration( JustPropertyDecl, EExportedDeclaration::Member, Dim ? **Dim : NULL);
ApplyAlternatePropertyExportText(*It, JustPropertyDecl);
// Finish up line.
JustPropertyDecl.Log(TEXT(";"));
// Finish up line.
PropertyText.Logf(TEXT("%s\r\n"), *JustPropertyDecl);
}
LastInSuper = NULL;
Previous = Current;
if (!Current->IsEditorOnlyProperty())
{
PreviousNonEditorOnly = Current;
}
HeaderOutput.Log(PropertyText);
}
}
if (bCurrentlyInNotCPPBlock)
{
HeaderOutput.Log(TEXT("#endif\r\n"));
bCurrentlyInNotCPPBlock = false;
}
// End of property list. If we haven't generated the WITH_EDITORONLY_DATA #endif, do so now.
if (bEmittedHasEditorOnlyMacro)
{
HeaderOutput.Logf( TEXT("#endif // WITH_EDITORONLY_DATA\r\n") );
}
// if the last property that was exported wasn't public, emit a line to reset the access to "public" so that we don't interfere with cpptext
if ( CurrentExportType != PROPEXPORT_Public )
{
HeaderOutput.Logf(TEXT("public:%s"), LINE_TERMINATOR);
}
}
/** This table maps a singleton name to an extern declaration for it. For cross-module access, we add these to GeneratedFunctionDeclarations **/
static TMap<FString, FString> SingletonNameToExternDecl;
FString FNativeClassHeaderGenerator::GetSingletonName(UField* Item, bool bRequiresValidObject)
{
UClass* ItemClass = Cast<UClass>(Item);
if (ItemClass && ItemClass->HasAllClassFlags(CLASS_Intrinsic))
{
return FString::Printf(TEXT("%s::StaticClass()"), NameLookupCPP->GetNameCPP(ItemClass));
}
FString Suffix;
if (ItemClass && !bRequiresValidObject && ItemClass->HasAllClassFlags(CLASS_Native))
{
Suffix = TEXT("_NoRegister");
}
FString Result;
UObject* Outer = Item;
while (Outer)
{
if (Cast<UClass>(Outer) || Cast<UScriptStruct>(Outer))
{
FString OuterName = NameLookupCPP->GetNameCPP(Cast<UStruct>(Outer));
if (Result.Len())
{
Result = OuterName + TEXT("_") + Result;
}
else
{
Result = OuterName;
}
// Structs can also have UPackage outer.
if (Cast<UClass>(Outer) || Cast<UPackage>(Outer->GetOuter()))
{
break;
}
}
else if (Result.Len())
{
// Handle UEnums with UPackage outer.
Result = Outer->GetName() + TEXT("_") + Result;
}
else
{
Result = Outer->GetName();
}
Outer = Outer->GetOuter();
}
FString ClassString = NameLookupCPP->GetNameCPP(Item->GetClass());
// Can't use long package names in function names.
if (Result.StartsWith(TEXT("/Script/")))
{
Result = FPackageName::GetShortName(Result);
}
Result = FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + Result + Suffix + TEXT("()");
if (CastChecked<UPackage>(Item->GetOutermost()) != Package)
{
// this is a cross module reference, we need to include the right extern decl
FString* Extern = SingletonNameToExternDecl.Find(Result);
if (!Extern)
{
UE_LOG(LogCompile, Warning, TEXT("Could not find extern declaration for cross module reference %s\n"), *Result);
}
else
{
bool bAlreadyInSet = false;
UniqueCrossModuleReferences.Add(*Extern, &bAlreadyInSet);
if (!bAlreadyInSet)
{
CrossModuleGeneratedFunctionDeclarations.Log(*Extern);
}
}
}
return Result;
}
FString FNativeClassHeaderGenerator::PropertyNew(FString& Meta, UProperty* Prop, const FString& OuterString, const FString& PropMacro, const TCHAR* NameSuffix, const TCHAR* Spaces, const TCHAR* SourceStruct)
{
FString ExtraArgs;
FString PropNameDep = Prop->GetName();
if (Prop->HasAllPropertyFlags(CPF_Deprecated))
{
PropNameDep += TEXT("_DEPRECATED");
}
if (UObjectPropertyBase* ObjectProperty = Cast<UObjectPropertyBase>(Prop))
{
UClass *TargetClass = ObjectProperty->PropertyClass;
if (UClassProperty* ClassProperty = Cast<UClassProperty>(Prop))
{
TargetClass = ClassProperty->MetaClass;
}
if (UAssetClassProperty* SubclassOfProperty = Cast<UAssetClassProperty>(Prop))
{
TargetClass = SubclassOfProperty->MetaClass;
}
ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(TargetClass, false));
}
else if (UInterfaceProperty* InterfaceProperty = Cast<UInterfaceProperty>(Prop))
{
UClass *TargetClass = InterfaceProperty->InterfaceClass;
ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(TargetClass, false));
}
else if (UStructProperty* StructProperty = Cast<UStructProperty>(Prop))
{
UScriptStruct* Struct = StructProperty->Struct;
check(Struct);
ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(Struct));
}
else if (UByteProperty* ByteProperty = Cast<UByteProperty>(Prop))
{
if (ByteProperty->Enum)
{
ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(ByteProperty->Enum));
}
}
else if (UBoolProperty* BoolProperty = Cast<UBoolProperty>(Prop))
{
if (Cast<UArrayProperty>(BoolProperty->GetOuter()))
{
ExtraArgs = FString(TEXT(", 0")); // this is an array of C++ bools so the mask is irrelevant.
}
else
{
check(SourceStruct);
ExtraArgs = FString::Printf(TEXT(", CPP_BOOL_PROPERTY_BITMASK(%s, %s)"), *PropNameDep, SourceStruct);
}
ExtraArgs += FString::Printf(TEXT(", sizeof(%s), %s"), *BoolProperty->GetCPPType(NULL, 0), BoolProperty->IsNativeBool() ? TEXT("true") : TEXT("false"));
}
else if (UDelegateProperty* DelegateProperty = Cast<UDelegateProperty>(Prop))
{
UFunction *TargetFunction = DelegateProperty->SignatureFunction;
ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(TargetFunction));
}
else if (UMulticastDelegateProperty* MulticastDelegateProperty = Cast<UMulticastDelegateProperty>(Prop))
{
UFunction *TargetFunction = MulticastDelegateProperty->SignatureFunction;
ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(TargetFunction));
}
else if( UAttributeProperty* AttributeProperty = Cast<UAttributeProperty>(Prop) )
{
ExtraArgs = FString::Printf(TEXT(", sizeof(TAttribute<%s>)"), *AttributeProperty->Inner->GetCPPType() );
}
FString Constructor = FString::Printf(TEXT("new(%s, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) U%s(%s, 0x%016llx%s);"),
*OuterString,
*Prop->GetName(),
*Prop->GetClass()->GetName(),
*PropMacro,
Prop->PropertyFlags & ~CPF_ComputedFlags,
*ExtraArgs);
TheFlagAudit.Add(Prop, TEXT("PropertyFlags"), Prop->PropertyFlags);
FString Symbol = FString::Printf(TEXT("NewProp_%s%s"), *Prop->GetName(), NameSuffix);
FString Lines = FString::Printf(TEXT("%sUProperty* %s = %s\r\n"),
Spaces,
*Symbol,
*Constructor);
if (Prop->ArrayDim != 1)
{
Lines += FString::Printf(TEXT("%s%s->ArrayDim = CPP_ARRAY_DIM(%s, %s);\r\n"), Spaces, *Symbol, *PropNameDep, SourceStruct);
}
if (Prop->RepNotifyFunc != NAME_None)
{
Lines += FString::Printf(TEXT("%s%s->RepNotifyFunc = FName(TEXT(\"%s\"));\r\n"), Spaces, *Symbol, *Prop->RepNotifyFunc.ToString());
}
Meta += GetMetaDataCodeForObject(Prop, *Symbol, Spaces);
return Lines;
}
void FNativeClassHeaderGenerator::OutputProperties(FString& Meta, FOutputDevice& OutputDevice, const FString& OuterString, TArray<UProperty*>& Properties, const TCHAR* Spaces)
{
bool bEmittedHasEditorOnlyMacro = false;
for (int32 Index = Properties.Num() - 1; Index >= 0; Index--)
{
bool RequiresHasEditorOnlyMacro = Properties[Index]->IsEditorOnlyProperty();
if (!bEmittedHasEditorOnlyMacro && RequiresHasEditorOnlyMacro)
{
// Indent code and export CPP text.
OutputDevice.Logf( TEXT("#if WITH_EDITORONLY_DATA\r\n") );
bEmittedHasEditorOnlyMacro = true;
}
else if (bEmittedHasEditorOnlyMacro && !RequiresHasEditorOnlyMacro)
{
OutputDevice.Logf( TEXT("#endif // WITH_EDITORONLY_DATA\r\n") );
bEmittedHasEditorOnlyMacro = false;
}
OutputProperty(Meta, OutputDevice, OuterString, Properties[Index], TEXT(" "));
}
if (bEmittedHasEditorOnlyMacro)
{
OutputDevice.Logf( TEXT("#endif // WITH_EDITORONLY_DATA\r\n") );
}
}
inline FString GetEventStructParamsName(const TCHAR* ClassName, const TCHAR* FunctionName)
{
FString Result = FString::Printf(TEXT("%s_event%s_Parms"), ClassName, FunctionName);
return Result;
}
void FNativeClassHeaderGenerator::OutputProperty(FString& Meta, FOutputDevice& OutputDevice, const FString& OuterString, UProperty* Prop, const TCHAR* Spaces)
{
{
FString SourceStruct;
UFunction* Function = Cast<UFunction>(Prop->GetOuter());
if (Function)
{
while (Function->GetSuperFunction())
{
Function = Function->GetSuperFunction();
}
FString FunctionName = Function->GetName();
if( Function->HasAnyFunctionFlags( FUNC_Delegate ) )
{
FunctionName = FunctionName.LeftChop( FString( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ).Len() );
}
SourceStruct = GetEventStructParamsName(*CastChecked<UClass>(Function->GetOuter())->GetName(), *FunctionName);
}
else
{
SourceStruct = NameLookupCPP->GetNameCPP(CastChecked<UStruct>(Prop->GetOuter()));
}
FString PropMacroOuterClass;
FString PropNameDep = Prop->GetName();
if (Prop->HasAllPropertyFlags(CPF_Deprecated))
{
PropNameDep += TEXT("_DEPRECATED");
}
UBoolProperty* BoolProperty = Cast<UBoolProperty>(Prop);
if (BoolProperty)
{
OutputDevice.Logf(TEXT("%sCPP_BOOL_PROPERTY_BITMASK_STRUCT(%s, %s, %s);\r\n"),
Spaces,
*PropNameDep,
*SourceStruct,
*BoolProperty->GetCPPType(NULL, 0));
PropMacroOuterClass = FString::Printf(TEXT("FPostConstructInitializeProperties(), EC_CppProperty, CPP_BOOL_PROPERTY_OFFSET(%s, %s)"),
*PropNameDep, *SourceStruct);
}
else
{
PropMacroOuterClass = FString::Printf(TEXT("CPP_PROPERTY_BASE(%s, %s)"), *PropNameDep, *SourceStruct);
}
OutputDevice.Log(*PropertyNew(Meta, Prop, OuterString, PropMacroOuterClass, TEXT(""), Spaces, *SourceStruct));
}
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Prop);
UAttributeProperty* AttributeProperty = Cast<UAttributeProperty>(Prop);
if (ArrayProperty)
{
FString InnerOuterString = FString::Printf(TEXT("NewProp_%s"), *Prop->GetName());
FString PropMacroOuterArray = TEXT("FPostConstructInitializeProperties(), EC_CppProperty, 0");
OutputDevice.Log(*PropertyNew(Meta, ArrayProperty->Inner, InnerOuterString, PropMacroOuterArray, TEXT("_Inner"), Spaces));
}
else if(AttributeProperty)
{
FString InnerOuterString = FString::Printf(TEXT("NewProp_%s"), *Prop->GetName());
FString PropMacroOuterAttrib = TEXT("FPostConstructInitializeProperties(), EC_CppProperty, 0");
OutputDevice.Log(*PropertyNew(Meta, AttributeProperty->Inner, InnerOuterString, PropMacroOuterAttrib, TEXT("_Inner"), Spaces));
}
}
static FString BackSlashAndIndent(const FString& Text)
{
FString Trailer(TEXT(" \\\r\n "));
FString Result = FString(TEXT(" ")) + Text.Replace(TEXT("\r\n"), *Trailer);
return Result.LeftChop(Trailer.Len()) + TEXT("\r\n");
}
static bool IsAlwaysAccessible(UScriptStruct* Script)
{
FName ToTest = Script->GetFName();
if (ToTest == NAME_Matrix)
{
return false; // special case, the C++ FMatrix does not have the same members.
}
bool Result = Script->HasDefaults(); // if we have cpp struct ops in it for UHT, then we can assume it is always accessible
if( ToTest == NAME_Plane
|| ToTest == NAME_Vector
|| ToTest == NAME_Vector4
|| ToTest == NAME_Quat
|| ToTest == NAME_Color
)
{
check(Result);
}
return Result;
}
static void FindNoExportStructs(TArray<UScriptStruct*>& Structs, UStruct* Start)
{
while (Start)
{
if (UScriptStruct* StartScript = Cast<UScriptStruct>(Start))
{
if (StartScript->StructFlags & STRUCT_Native)
{
break;
}
if (!IsAlwaysAccessible(StartScript)) // these are a special cases that already exists and if wrong if exported nievely
{
// this will topologically sort them in reverse order
Structs.Remove(StartScript);
Structs.Add(StartScript);
}
}
for ( TFieldIterator<UProperty> ItInner(Start, EFieldIteratorFlags::ExcludeSuper); ItInner; ++ItInner )
{
UStructProperty* InnerStruct = Cast<UStructProperty>(*ItInner);
UArrayProperty* InnerArray = Cast<UArrayProperty>(*ItInner);
UAttributeProperty* InnerAttribute = Cast<UAttributeProperty>(*ItInner);
if (InnerArray)
{
InnerStruct = Cast<UStructProperty>(InnerArray->Inner);
}
else if(InnerAttribute)
{
InnerStruct = Cast<UStructProperty>(InnerAttribute->Inner);
}
if (InnerStruct)
{
FindNoExportStructs(Structs, InnerStruct->Struct);
}
}
Start = Start->GetSuperStruct();
}
}
FString GetPackageSingletonName(UPackage* Package)
{
static FString ClassString = NameLookupCPP->GetNameCPP(UPackage::StaticClass());
return FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + FPackageName::GetShortName(Package) + TEXT("()");
}
void FNativeClassHeaderGenerator::ExportGeneratedPackageInitCode(UPackage* Package)
{
FString ApiString = FString::Printf(TEXT("%s_API "), *API);
FString SingletonName(GetPackageSingletonName(Package));
if( GeneratedFunctionBodyTextSplit.Num() == 0 )
{
new(GeneratedFunctionBodyTextSplit) FStringOutputDeviceCountLines;
}
FStringOutputDevice& GeneratedFunctionText = GeneratedFunctionBodyTextSplit[GeneratedFunctionBodyTextSplit.Num()-1];
GeneratedFunctionDeclarations.Logf(TEXT(" %sclass UPackage* %s;\r\n"), *ApiString, *SingletonName);
GeneratedFunctionText.Logf(TEXT(" UPackage* %s\r\n"), *SingletonName);
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
GeneratedFunctionText.Logf(TEXT(" static UPackage* ReturnPackage = NULL;\r\n"));
GeneratedFunctionText.Logf(TEXT(" if (!ReturnPackage)\r\n"));
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
GeneratedFunctionText.Logf(TEXT(" ReturnPackage = CastChecked<UPackage>(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(TEXT(\"%s\")), false, false));\r\n"), *Package->GetName());
FString Meta = GetMetaDataCodeForObject(Package, TEXT("ReturnPackage"), TEXT(" "));
if (Meta.Len())
{
GeneratedFunctionText.Logf(TEXT("#if WITH_METADATA\r\n"));
GeneratedFunctionText.Logf(TEXT(" UMetaData* MetaData = ReturnPackage->GetMetaData();\r\n"));
GeneratedFunctionText.Log(*Meta);
GeneratedFunctionText.Logf(TEXT("#endif\r\n"));
}
GeneratedFunctionText.Logf(TEXT(" ReturnPackage->PackageFlags |= PKG_CompiledIn | 0x%08X;\r\n"), Package->PackageFlags & (PKG_ClientOptional|PKG_ServerSideOnly));
TheFlagAudit.Add(Package, TEXT("PackageFlags"), Package->PackageFlags);
{
FGuid Guid;
Guid.A = FCrc::StrCrc_DEPRECATED(*GeneratedFunctionText .ToUpper());
Guid.B = FCrc::StrCrc_DEPRECATED(*GeneratedFunctionDeclarations.ToUpper());
GeneratedFunctionText.Logf(TEXT(" FGuid Guid;\r\n"));
GeneratedFunctionText.Logf(TEXT(" Guid.A = 0x%08X;\r\n"), Guid.A);
GeneratedFunctionText.Logf(TEXT(" Guid.B = 0x%08X;\r\n"), Guid.B);
GeneratedFunctionText.Logf(TEXT(" Guid.C = 0x%08X;\r\n"), Guid.C);
GeneratedFunctionText.Logf(TEXT(" Guid.D = 0x%08X;\r\n"), Guid.D);
GeneratedFunctionText.Logf(TEXT(" ReturnPackage->SetGuid(Guid);\r\n"), Guid.D);
}
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
GeneratedFunctionText.Logf(TEXT(" return ReturnPackage;\r\n"));
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
}
void FNativeClassHeaderGenerator::ExportNativeGeneratedInitCode(UClass* Class)
{
// Emit the IMPLEMENT_CLASS macro to go in the generated cpp file (unless it's the temporary UHT class).
if (!Class->HasAnyClassFlags(CLASS_Temporary))
{
GeneratedPackageCPP.Logf(TEXT(" IMPLEMENT_CLASS(%s);\r\n"),NameLookupCPP->GetNameCPP(Class));
}
int32 MaxLinesPerCpp = 30000;
if ((GeneratedFunctionBodyTextSplit.Num() == 0) || (GeneratedFunctionBodyTextSplit[GeneratedFunctionBodyTextSplit.Num()-1].GetLineCount() > MaxLinesPerCpp))
{
new(GeneratedFunctionBodyTextSplit) FStringOutputDeviceCountLines;
}
FStringOutputDevice& GeneratedFunctionText = GeneratedFunctionBodyTextSplit[GeneratedFunctionBodyTextSplit.Num()-1];
check(!FriendText.Len());
// Emit code to build the UObjects that used to be in .u files
bool bIsNoExport = Class->HasAnyClassFlags(CLASS_NoExport|CLASS_Temporary);
FStringOutputDevice BodyText;
FStringOutputDevice CallSingletons;
FString ApiString = FString::Printf(TEXT("%s_API "), *API);
// enums
for ( TFieldIterator<UEnum> It(Class, EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
UEnum* Enum = *It;
FString SingletonName(GetSingletonName(Enum));
CallSingletons.Logf(TEXT(" OuterClass->LinkChild(%s);\r\n"), *SingletonName);
FString Extern = FString::Printf(TEXT(" %sclass UEnum* %s;\r\n"), *ApiString, *SingletonName);
SingletonNameToExternDecl.Add(SingletonName, Extern);
GeneratedFunctionDeclarations.Log(*Extern);
GeneratedFunctionText.Logf(TEXT(" UEnum* %s\r\n"), *SingletonName);
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
// Enums can either have a UClass or UPackage as outer (if declared in non-UClass header).
if (Enum->GetOuter()->IsA(UStruct::StaticClass()))
{
GeneratedFunctionText.Logf(TEXT(" UClass* Outer=%s;\r\n"), *GetSingletonName(CastChecked<UStruct>(Enum->GetOuter())));
}
else
{
GeneratedFunctionText.Logf(TEXT(" UPackage* Outer=%s;\r\n"), *GetPackageSingletonName(CastChecked<UPackage>(Enum->GetOuter())));
}
GeneratedFunctionText.Logf(TEXT(" static UEnum* ReturnEnum = NULL;\r\n"));
GeneratedFunctionText.Logf(TEXT(" if (!ReturnEnum)\r\n"));
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
GeneratedFunctionText.Logf(TEXT(" ReturnEnum = new(Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) UEnum(FPostConstructInitializeProperties());\r\n"), *Enum->GetName());
GeneratedFunctionText.Logf(TEXT(" TArray<FName> EnumNames;\r\n"));
for (int32 Index = 0; Index < Enum->NumEnums(); Index++)
{
GeneratedFunctionText.Logf(TEXT(" EnumNames.Add(FName(TEXT(\"%s\")));\r\n"), *Enum->GetEnum(Index).ToString());
}
GeneratedFunctionText.Logf(TEXT(" ReturnEnum->SetEnums(EnumNames, %s);\r\n"), Enum->IsNamespaceEnum() ? TEXT("true") : TEXT("false"));
FString Meta = GetMetaDataCodeForObject(Enum, TEXT("ReturnEnum"), TEXT(" "));
if (Meta.Len())
{
GeneratedFunctionText.Logf(TEXT("#if WITH_METADATA\r\n"));
GeneratedFunctionText.Logf(TEXT(" UMetaData* MetaData = ReturnEnum->GetOutermost()->GetMetaData();\r\n"));
GeneratedFunctionText.Log(*Meta);
GeneratedFunctionText.Logf(TEXT("#endif\r\n"));
}
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
GeneratedFunctionText.Logf(TEXT(" return ReturnEnum;\r\n"));
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
}
// structs
for ( TFieldIterator<UScriptStruct> It(Class, EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
UScriptStruct* ScriptStruct = *It;
UStruct* BaseStruct = ScriptStruct->GetSuperStruct();
FString SingletonName(GetSingletonName(ScriptStruct));
CallSingletons.Logf(TEXT(" OuterClass->LinkChild(%s);\r\n"), *SingletonName);
FString Extern = FString::Printf(TEXT(" %sclass UScriptStruct* %s;\r\n"), *ApiString, *SingletonName);
SingletonNameToExternDecl.Add(SingletonName, Extern);
GeneratedFunctionDeclarations.Log(*Extern);
GeneratedFunctionText.Logf(TEXT(" UScriptStruct* %s\r\n"), *SingletonName);
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
// if this is a no export struct, we will put a local struct here for offset determination
TArray<UScriptStruct*> Structs;
FindNoExportStructs(Structs, ScriptStruct);
if (Structs.Num())
{
TGuardValue<bool> Guard(bIsExportingForOffsetDeterminationOnly,true);
ExportMirrorsForNoexportStructs(Structs, 8, GeneratedFunctionText);
}
// Structs can either have a UClass or UPackage as outer (if delcared in non-UClass header).
if (ScriptStruct->GetOuter()->IsA(UStruct::StaticClass()))
{
GeneratedFunctionText.Logf(TEXT(" UStruct* Outer=%s;\r\n"), *GetSingletonName(CastChecked<UStruct>(ScriptStruct->GetOuter())));
}
else
{
GeneratedFunctionText.Logf(TEXT(" UPackage* Outer=%s;\r\n"), *GetPackageSingletonName(CastChecked<UPackage>(ScriptStruct->GetOuter())));
}
GeneratedFunctionText.Logf(TEXT(" static UScriptStruct* ReturnStruct = NULL;\r\n"));
GeneratedFunctionText.Logf(TEXT(" if (!ReturnStruct)\r\n"));
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
FString BaseStructString(TEXT("NULL"));
if (BaseStruct)
{
CastChecked<UScriptStruct>(BaseStruct); // this better actually be a script struct
BaseStructString = GetSingletonName(BaseStruct);
}
FString CppStructOpsString(TEXT("NULL"));
FString ExplicitSizeString;
FString ExplicitAlignmentString;
if ( (ScriptStruct->StructFlags&STRUCT_Native) != 0 )
{
//@todo .u we don't need the auto register versions of these (except for hotreload, which should be fixed)
CppStructOpsString = FString::Printf( TEXT("new UScriptStruct::TCppStructOps<%s>"), NameLookupCPP->GetNameCPP(ScriptStruct) );
}
else
{
ExplicitSizeString = FString::Printf( TEXT(", sizeof(%s), ALIGNOF(%s)"), NameLookupCPP->GetNameCPP(ScriptStruct), NameLookupCPP->GetNameCPP(ScriptStruct) );
}
GeneratedFunctionText.Logf(TEXT(" ReturnStruct = new(Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) UScriptStruct(FPostConstructInitializeProperties(), %s, %s, EStructFlags(0x%08X)%s);\r\n"),
*ScriptStruct->GetName(),
*BaseStructString,
*CppStructOpsString,
(uint32)(ScriptStruct->StructFlags & ~STRUCT_ComputedFlags),
*ExplicitSizeString
);
TheFlagAudit.Add(ScriptStruct, TEXT("StructFlags"), (uint64)(ScriptStruct->StructFlags & ~STRUCT_ComputedFlags));
TArray<UProperty*> Props;
for ( TFieldIterator<UProperty> ItInner(ScriptStruct, EFieldIteratorFlags::ExcludeSuper); ItInner; ++ItInner )
{
Props.Add(*ItInner);
}
FString OuterString = FString(TEXT("ReturnStruct"));
FString Meta = GetMetaDataCodeForObject(ScriptStruct, *OuterString, TEXT(" "));
OutputProperties(Meta, GeneratedFunctionText, OuterString, Props, TEXT(" "));
GeneratedFunctionText.Logf(TEXT(" ReturnStruct->StaticLink();\r\n"));
if (Meta.Len())
{
GeneratedFunctionText.Logf(TEXT("#if WITH_METADATA\r\n"));
GeneratedFunctionText.Logf(TEXT(" UMetaData* MetaData = ReturnStruct->GetOutermost()->GetMetaData();\r\n"));
GeneratedFunctionText.Log(*Meta);
GeneratedFunctionText.Logf(TEXT("#endif\r\n"));
}
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
GeneratedFunctionText.Logf(TEXT(" return ReturnStruct;\r\n"));
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
}
TArray<UFunction*> FunctionsToExport;
for( TFieldIterator<UFunction> Function(Class,EFieldIteratorFlags::ExcludeSuper); Function; ++Function )
{
FunctionsToExport.Add(*Function);
}
// Sort the list of functions
FunctionsToExport.Sort();
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(Class);
// Export the init code for each function
for (int32 FuncIndex = 0; FuncIndex < FunctionsToExport.Num(); FuncIndex++)
{
UFunction* Function = FunctionsToExport[FuncIndex];
UFunction* SuperFunction = Function->GetSuperFunction();
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData();
FString SingletonName(GetSingletonName(Function));
CallSingletons.Logf(TEXT(" OuterClass->LinkChild(%s);\r\n"), *SingletonName);
FString Extern = FString::Printf(TEXT(" %sclass UFunction* %s;\r\n"), *ApiString, *SingletonName);
SingletonNameToExternDecl.Add(SingletonName, Extern);
GeneratedFunctionDeclarations.Log(*Extern);
GeneratedFunctionText.Logf(TEXT(" UFunction* %s\r\n"), *SingletonName);
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
if (bIsNoExport || !(Function->FunctionFlags&FUNC_Event)) // non-events do not export a params struct, so lets do that locally for offset determination
{
TGuardValue<bool> Guard(bIsExportingForOffsetDeterminationOnly,true);
TArray<UScriptStruct*> Structs;
FindNoExportStructs(Structs, Function);
if (Structs.Num())
{
ExportMirrorsForNoexportStructs(Structs, 8, GeneratedFunctionText);
}
TArray<UFunction*> CallbackFunctions;
CallbackFunctions.Add(Function);
ExportEventParms(CallbackFunctions, NULL, 8, false, &GeneratedFunctionText);
}
GeneratedFunctionText.Logf(TEXT(" UClass* OuterClass=%s;\r\n"), *GetSingletonName(Class));
GeneratedFunctionText.Logf(TEXT(" static UFunction* ReturnFunction = NULL;\r\n"));
GeneratedFunctionText.Logf(TEXT(" if (!ReturnFunction)\r\n"));
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
FString SuperFunctionString(TEXT("NULL"));
if (SuperFunction)
{
SuperFunctionString = GetSingletonName(SuperFunction);
}
TArray<UProperty*> Props;
for ( TFieldIterator<UProperty> ItInner(Function,EFieldIteratorFlags::ExcludeSuper); ItInner; ++ItInner )
{
Props.Add(*ItInner);
}
FString StructureSize;
if (Props.Num())
{
UFunction* TempFunction = Function;
while (TempFunction->GetSuperFunction())
{
TempFunction = TempFunction->GetSuperFunction();
}
FString FunctionName = TempFunction->GetName();
if( TempFunction->HasAnyFunctionFlags( FUNC_Delegate ) )
{
FunctionName = FunctionName.LeftChop( FString( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ).Len() );
}
StructureSize = FString::Printf(TEXT(", sizeof(%s)"), *GetEventStructParamsName(*CastChecked<UClass>(TempFunction->GetOuter())->GetName(), *FunctionName));
}
GeneratedFunctionText.Logf(TEXT(" ReturnFunction = new(OuterClass, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) UFunction(FPostConstructInitializeProperties(), %s, 0x%08X, %d%s);\r\n"),
*Function->GetName(),
*SuperFunctionString,
Function->FunctionFlags,
(uint32)Function->RepOffset,
*StructureSize
);
TheFlagAudit.Add(Function, TEXT("FunctionFlags"), Function->FunctionFlags);
FString OuterString = FString(TEXT("ReturnFunction"));
FString Meta = GetMetaDataCodeForObject(Function, *OuterString, TEXT(" "));
for (int32 Index = Props.Num() - 1; Index >= 0; Index--)
{
OutputProperty(Meta, GeneratedFunctionText, OuterString, Props[Index], TEXT(" "));
}
if (FunctionData.FunctionFlags & (FUNC_NetRequest | FUNC_NetResponse))
{
GeneratedFunctionText.Logf(TEXT(" ReturnFunction->RPCId=%d;\r\n"), FunctionData.RPCId);
GeneratedFunctionText.Logf(TEXT(" ReturnFunction->RPCResponseId=%d;\r\n"), FunctionData.RPCResponseId);
}
GeneratedFunctionText.Logf(TEXT(" ReturnFunction->Bind();\r\n"));
GeneratedFunctionText.Logf(TEXT(" ReturnFunction->StaticLink();\r\n"));
if (Meta.Len())
{
GeneratedFunctionText.Logf(TEXT("#if WITH_METADATA\r\n"));
GeneratedFunctionText.Logf(TEXT(" UMetaData* MetaData = ReturnFunction->GetOutermost()->GetMetaData();\r\n"));
GeneratedFunctionText.Log(*Meta);
GeneratedFunctionText.Logf(TEXT("#endif\r\n"));
}
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
GeneratedFunctionText.Logf(TEXT(" return ReturnFunction;\r\n"));
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
}
// The class itself (unless it's a temporary UHT class).
if (!Class->HasAnyClassFlags(CLASS_Temporary))
{
// simple ::StaticClass wrapper to avoid header, link and DLL hell
{
FString SingletonNameNoRegister(GetSingletonName(Class, false));
FString Extern = FString::Printf(TEXT(" %sclass UClass* %s;\r\n"), *ApiString, *SingletonNameNoRegister);
SingletonNameToExternDecl.Add(SingletonNameNoRegister, Extern);
GeneratedFunctionDeclarations.Log(*Extern);
GeneratedFunctionText.Logf(TEXT(" UClass* %s\r\n"), *SingletonNameNoRegister);
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
GeneratedFunctionText.Logf(TEXT(" return %s::StaticClass();\r\n"), NameLookupCPP->GetNameCPP(Class));
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
}
FString SingletonName(GetSingletonName(Class));
FriendText.Logf(TEXT(" friend %sclass UClass* %s;\r\n"), *ApiString, *SingletonName);
FString Extern = FString::Printf(TEXT(" %sclass UClass* %s;\r\n"), *ApiString, *SingletonName);
SingletonNameToExternDecl.Add(SingletonName, Extern);
GeneratedFunctionDeclarations.Log(*Extern);
GeneratedFunctionText.Logf(TEXT(" UClass* %s\r\n"), *SingletonName);
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
GeneratedFunctionText.Logf(TEXT(" static UClass* OuterClass = NULL;\r\n"));
GeneratedFunctionText.Logf(TEXT(" if (!OuterClass)\r\n"));
GeneratedFunctionText.Logf(TEXT(" {\r\n"));
if (Class->GetSuperClass() && Class->GetSuperClass() != Class)
{
GeneratedFunctionText.Logf(TEXT(" %s;\r\n"), *GetSingletonName(Class->GetSuperClass()));
}
GeneratedFunctionText.Logf(TEXT(" %s;\r\n"), *GetPackageSingletonName(CastChecked<UPackage>(Class->GetOutermost())));
GeneratedFunctionText.Logf(TEXT(" OuterClass = %s::StaticClass();\r\n"), NameLookupCPP->GetNameCPP(Class));
GeneratedFunctionText.Logf(TEXT(" UObjectForceRegistration(OuterClass);\r\n"));
uint32 Flags = Class->ClassFlags & CLASS_SaveInCompiledInClasses;
GeneratedFunctionText.Logf(TEXT(" OuterClass->ClassFlags |= 0x%08X;\r\n"), Flags);
//if (Class->IsChildOf(UObject::StaticClass()))
//{
// GeneratedFunctionText.Logf(TEXT(" OuterClass->ClassAddReferencedObjects = &%s::AddReferencedObjects;\r\n"), NameLookupCPP->GetNameCPP(Class));
//}
TheFlagAudit.Add(Class, TEXT("ClassFlags"), Flags);
GeneratedFunctionText.Logf(TEXT("\r\n"));
GeneratedFunctionText.Log(CallSingletons);
GeneratedFunctionText.Logf(TEXT("\r\n"));
FString OuterString = FString(TEXT("OuterClass"));
FString Meta = GetMetaDataCodeForObject(Class, *OuterString, TEXT(" "));
// properties
{
TArray<UProperty*> Props;
for ( TFieldIterator<UProperty> ItInner(Class,EFieldIteratorFlags::ExcludeSuper); ItInner; ++ItInner )
{
Props.Add(*ItInner);
}
OutputProperties(Meta, GeneratedFunctionText, OuterString, Props, TEXT(" "));
}
// function table
{
// Grab and sort the list of functions in the function map
TArray<UFunction*> FunctionsInMap;
for (auto Function : TFieldRange<UFunction>(Class, EFieldIteratorFlags::ExcludeSuper))
{
FunctionsInMap.Add(Function);
}
FunctionsInMap.Sort();
// Emit code to construct each UFunction and rebuild the function map at runtime
for (UFunction* Function : FunctionsInMap)
{
GeneratedFunctionText.Logf(TEXT(" OuterClass->AddFunctionToFunctionMap(%s);\r\n"), *GetSingletonName(Function));
}
}
// class flags are handled by the intrinsic bootstrap code
//GeneratedFunctionText.Logf(TEXT(" OuterClass->ClassFlags = 0x%08X;\r\n"), Class->ClassFlags);
if (Class->ClassConfigName != NAME_None)
{
GeneratedFunctionText.Logf(TEXT(" OuterClass->ClassConfigName = FName(TEXT(\"%s\"));\r\n"), *Class->ClassConfigName.ToString());
}
for (auto& Inter : Class->Interfaces)
{
check(Inter.Class);
FString OffsetString(TEXT("0"));
if (Inter.PointerOffset)
{
OffsetString = FString::Printf(TEXT("VTABLE_OFFSET(%s, %s)"), NameLookupCPP->GetNameCPP(Class), NameLookupCPP->GetNameCPP(Inter.Class, true));
}
GeneratedFunctionText.Logf(TEXT(" OuterClass->Interfaces.Add(FImplementedInterface(%s, %s, %s ));\r\n"),
*GetSingletonName(Inter.Class, false),
*OffsetString,
Inter.bImplementedByK2 ? TEXT("true") : TEXT("false")
);
}
if (Class->ClassGeneratedBy)
{
UE_LOG(LogCompile, Fatal, TEXT("For intrinsic and compiled-in classes, ClassGeneratedBy should always be NULL"));
GeneratedFunctionText.Logf(TEXT(" OuterClass->ClassGeneratedBy = %s;\r\n"), *GetSingletonName(CastChecked<UClass>(Class->ClassGeneratedBy), false));
}
GeneratedFunctionText.Logf(TEXT(" OuterClass->StaticLink();\r\n"));
if (Meta.Len())
{
GeneratedFunctionText.Logf(TEXT("#if WITH_METADATA\r\n"));
GeneratedFunctionText.Logf(TEXT(" UMetaData* MetaData = OuterClass->GetOutermost()->GetMetaData();\r\n"));
GeneratedFunctionText.Log(*Meta);
GeneratedFunctionText.Logf(TEXT("#endif\r\n"));
}
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
GeneratedFunctionText.Logf(TEXT(" check(OuterClass->GetClass());\r\n"));
GeneratedFunctionText.Logf(TEXT(" return OuterClass;\r\n"));
GeneratedFunctionText.Logf(TEXT(" }\r\n"));
}
if (FriendText.Len())
{
if (bIsNoExport)
{
GeneratedFunctionText.Logf(TEXT(" /* friend declarations for pasting into noexport class %s\r\n"), NameLookupCPP->GetNameCPP(Class));
GeneratedFunctionText.Log(FriendText);
GeneratedFunctionText.Logf(TEXT(" */\r\n"));
FriendText.Empty();
}
}
FString SingletonName(GetSingletonName(Class));
SingletonName.ReplaceInline(TEXT("()"), TEXT("")); // function address
// Skip temporary UHT classes.
if (!Class->HasAnyClassFlags(CLASS_Temporary))
{
GeneratedFunctionText.Logf(TEXT(" static FCompiledInDefer Z_CompiledInDefer_UClass_%s(%s);\r\n"), NameLookupCPP->GetNameCPP(Class), *SingletonName);
}
}
void FNativeClassHeaderGenerator::ExportNatives(UClass* Class)
{
// Skip temporary UHT classes.
if (Class->HasAnyClassFlags(CLASS_NoExport|CLASS_Temporary))
return;
GeneratedPackageCPP.Logf(TEXT(" void %s::StaticRegisterNatives%s()\r\n"),NameLookupCPP->GetNameCPP(Class),NameLookupCPP->GetNameCPP(Class));
GeneratedPackageCPP.Logf(TEXT(" {\r\n"));
{
TArray<UFunction*> FunctionsToExport;
for (auto* Function : TFieldRange<UFunction>(Class, EFieldIteratorFlags::ExcludeSuper))
{
if( (Function->FunctionFlags & (FUNC_Native | FUNC_NetRequest)) == FUNC_Native )
{
FunctionsToExport.Add(Function);
}
}
FunctionsToExport.Sort();
for (auto Func : FunctionsToExport)
{
GeneratedPackageCPP.Logf(TEXT(" FNativeFunctionRegistrar::RegisterFunction(%s::StaticClass(),\"%s\",(Native)&%s::exec%s);\r\n"),
NameLookupCPP->GetNameCPP(Class),
*Func->GetName(),
Class->HasAnyClassFlags(CLASS_Interface) ? *FString::Printf(TEXT("I%s"), *Class->GetName()) : NameLookupCPP->GetNameCPP(Class),
*Func->GetName()
);
}
}
for (auto* Struct : TFieldRange<UScriptStruct>(Class, EFieldIteratorFlags::ExcludeSuper))
{
if (Struct->StructFlags & STRUCT_Native)
{
GeneratedPackageCPP.Logf( TEXT(" UScriptStruct::DeferCppStructOps(FName(TEXT(\"%s\")),new UScriptStruct::TCppStructOps<%s%s>);\r\n"), *Struct->GetName(), Struct->GetPrefixCPP(), *Struct->GetName() );
}
}
GeneratedPackageCPP.Logf(TEXT(" }\r\n"));
}
void FNativeClassHeaderGenerator::ExportInterfaceCallFunctions( const TArray<UFunction*>& InCallbackFunctions, FStringOutputDevice& HeaderOutput )
{
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
TArray<UFunction*> CallbackFunctions = InCallbackFunctions;
CallbackFunctions.Sort();
for (auto FuncIt = CallbackFunctions.CreateConstIterator(); FuncIt; ++FuncIt)
{
UFunction* Function = *FuncIt;
FString FunctionName = Function->GetName();
FString ClassName = CurrentClass->GetName();
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData();
const TCHAR* ConstQualifier = FunctionData.FunctionReference->HasAllFunctionFlags(FUNC_Const) ? TEXT("const ") : TEXT("");
FString ExtraParam = FString::Printf(TEXT("%sUObject* O"), ConstQualifier);
ExportNativeFunctionHeader(FunctionData, HeaderOutput, EExportFunctionType::Interface, EExportFunctionHeaderStyle::Declaration, *ExtraParam);
HeaderOutput.Logf( TEXT(";%s"), LINE_TERMINATOR );
ExportNativeFunctionHeader(FunctionData, GeneratedPackageCPP, EExportFunctionType::Interface, EExportFunctionHeaderStyle::Definition, *ExtraParam);
GeneratedPackageCPP.Logf( TEXT("%s%s{%s"), LINE_TERMINATOR, FCString::Spc(4), LINE_TERMINATOR );
GeneratedPackageCPP.Logf(TEXT("%scheck(O != NULL);%s"), FCString::Spc(8), LINE_TERMINATOR);
GeneratedPackageCPP.Logf(TEXT("%scheck(O->GetClass()->ImplementsInterface(U%s::StaticClass()));%s"), FCString::Spc(8), *ClassName, LINE_TERMINATOR);
auto Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference);
// See if we need to create Parms struct
bool bHasParms = Parameters.HasParms();
if (bHasParms)
{
FString EventParmStructName = GetEventStructParamsName(*Function->GetOwnerClass()->GetName(), *FunctionName);
GeneratedPackageCPP.Logf(TEXT("%s%s Parms;%s"), FCString::Spc(8), *EventParmStructName, LINE_TERMINATOR);
}
GeneratedPackageCPP.Logf(TEXT("%sUFunction* const Func = O->FindFunction(%s_%s);%s"), FCString::Spc(8), *API, *FunctionName, LINE_TERMINATOR);
GeneratedPackageCPP.Logf(TEXT("%sif (Func)%s"), FCString::Spc(8), LINE_TERMINATOR);
GeneratedPackageCPP.Logf(TEXT("%s{%s"), FCString::Spc(8), LINE_TERMINATOR);
// code to populate Parms struct
for (auto It = Parameters.Parms.CreateConstIterator(); It; ++It)
{
UProperty* Param = *It;
GeneratedPackageCPP.Logf(TEXT("%sParms.%s=%s;%s"), FCString::Spc(12), *Param->GetName(), *Param->GetName(), LINE_TERMINATOR);
}
GeneratedPackageCPP.Logf(TEXT("%sO->ProcessEvent(Func, %s);%s"), FCString::Spc(12), bHasParms ? TEXT("&Parms") : TEXT("NULL"), LINE_TERMINATOR);
for (auto It = Parameters.Parms.CreateConstIterator(); It; ++It)
{
UProperty* Param = *It;
if( Param->HasAllPropertyFlags(CPF_OutParm) && !Param->HasAnyPropertyFlags(CPF_ConstParm|CPF_ReturnParm))
{
GeneratedPackageCPP.Logf(TEXT("%s%s=Parms.%s;%s"), FCString::Spc(12), *Param->GetName(), *Param->GetName(), LINE_TERMINATOR);
}
}
GeneratedPackageCPP.Logf(TEXT("%s}%s"), FCString::Spc(8), LINE_TERMINATOR);
// else clause to call back into native if it's a BlueprintNativeEvent
if (Function->FunctionFlags & FUNC_Native)
{
GeneratedPackageCPP.Logf(TEXT("%selse if (auto I = (%sI%s*)(O->GetNativeInterfaceAddress(U%s::StaticClass())))%s"), FCString::Spc(8), ConstQualifier, *ClassName, *ClassName, LINE_TERMINATOR);
GeneratedPackageCPP.Logf(TEXT("%s{%s"), FCString::Spc(8), LINE_TERMINATOR);
GeneratedPackageCPP.Logf(FCString::Spc(12));
if (Parameters.Return)
{
GeneratedPackageCPP.Logf(TEXT("Parms.ReturnValue = "));
}
GeneratedPackageCPP.Logf(TEXT("I->%s_Implementation("), *FunctionName);
bool First = true;
for (auto It = Parameters.Parms.CreateConstIterator(); It; ++It)
{
UProperty* Param = *It;
if (!First)
{
GeneratedPackageCPP.Logf(TEXT(","));
}
First = false;
GeneratedPackageCPP.Logf(TEXT("%s"), *Param->GetName());
}
GeneratedPackageCPP.Logf(TEXT(");%s"), LINE_TERMINATOR);
GeneratedPackageCPP.Logf(TEXT("%s}%s"), FCString::Spc(8), LINE_TERMINATOR);
}
if (Parameters.Return)
{
GeneratedPackageCPP.Logf(TEXT("%sreturn Parms.ReturnValue;%s"), FCString::Spc(8), LINE_TERMINATOR);
}
GeneratedPackageCPP.Logf(TEXT("%s}%s"), FCString::Spc(4), LINE_TERMINATOR);
//
// also generate an empty implementation for the base event function, since we did the Execute_ version above
//
ExportNativeFunctionHeader(FunctionData, GeneratedPackageCPP, EExportFunctionType::Event, EExportFunctionHeaderStyle::Definition, NULL);
GeneratedPackageCPP.Logf(TEXT("%s%s{%s"), LINE_TERMINATOR, FCString::Spc(4), LINE_TERMINATOR);
// assert if this is ever called directly
GeneratedPackageCPP.Logf(TEXT("%scheck(0 && \"Do not directly call Event functions in Interfaces. Call Execute_%s instead.\");%s"), FCString::Spc(8), *FunctionName, LINE_TERMINATOR);
// satisfy compiler if it's expecting a return value
if (Parameters.Return)
{
FString EventParmStructName = GetEventStructParamsName(*Function->GetOwnerClass()->GetName(), *FunctionName);
GeneratedPackageCPP.Logf(TEXT("%s%s Parms;%s"), FCString::Spc(8), *EventParmStructName, LINE_TERMINATOR);
GeneratedPackageCPP.Logf(TEXT("%sreturn Parms.ReturnValue;%s"), FCString::Spc(8), LINE_TERMINATOR);
}
GeneratedPackageCPP.Logf(TEXT("%s}%s"), FCString::Spc(4), LINE_TERMINATOR);
}
}
void FNativeClassHeaderGenerator::ExportNames()
{
// Skip temporary UHT classes.
if (CurrentClass->HasAnyClassFlags(CLASS_NoExport|CLASS_Temporary))
{
return;
}
GeneratedHeaderText.Logf(
TEXT("#ifdef %s_%s_generated_h") LINE_TERMINATOR
TEXT("#error \"%s.generated.h already included, missing '#pragma once' in %s.h\"") LINE_TERMINATOR
TEXT("#endif") LINE_TERMINATOR
TEXT("#define %s_%s_generated_h") LINE_TERMINATOR
LINE_TERMINATOR,
*API, *CurrentClass->GetName(), *CurrentClass->GetName(), *CurrentClass->GetName(), *API, *CurrentClass->GetName()
);
TSet<FName> ClassReferencedNames;
for( TFieldIterator<UFunction> Function(CurrentClass,EFieldIteratorFlags::ExcludeSuper); Function; ++Function )
{
if ( Function->HasAnyFunctionFlags(FUNC_Event) && !Function->GetSuperFunction() )
{
FName FunctionName = Function->GetFName();
ClassReferencedNames.Add(FunctionName);
ReferencedNames.Add(FunctionName);
}
}
if (!ClassReferencedNames.Num())
{
return;
}
TArray<FString> Names;
for (const auto& Name : ClassReferencedNames)
{
Names.Add(Name.ToString());
}
Names.Sort();
for( int32 NameIndex=0; NameIndex<Names.Num(); NameIndex++ )
{
GeneratedHeaderText.Logf( TEXT("extern %s_API FName %s_%s;") LINE_TERMINATOR, *API, *API, *Names[NameIndex] );
}
}
/**
* Exports the C++ class declarations for a native interface class.
*/
void FNativeClassHeaderGenerator::ExportInterfaceClassDeclaration( UClass* Class )
{
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
TArray<UEnum*> Enums;
TArray<UScriptStruct*> Structs;
TArray<UFunction*> CallbackFunctions;
TArray<UFunction*> NativeFunctions;
TArray<UFunction*> DelegateFunctions;
// get the lists of fields that belong to this class that should be exported
RetrieveRelevantFields(Class, Enums, Structs, CallbackFunctions, NativeFunctions, DelegateFunctions);
ExportNames();
// export enum declarations
ExportEnums(Enums);
// export delegate definitions
ExportDelegateDefinitions(DelegateFunctions, false);
// Export enums declared in non-UClass headers.
ExportGeneratedEnumsInitCode(Enums);
// export boilerplate macros for struct
ExportGeneratedStructBodyMacros(Structs);
// export parameters structs for all events and delegates
ExportEventParms(CallbackFunctions, TEXT(""));
// export delegate wrapper function implementations
ExportDelegateDefinitions(DelegateFunctions, true);
UClass* SuperClass = Class->GetSuperClass();
// the name for the C++ version of the UClass
const TCHAR* ClassCPPName = NameLookupCPP->GetNameCPP( Class );
const TCHAR* SuperClassCPPName = NameLookupCPP->GetNameCPP( SuperClass );
// =============================================
// Export the UClass declaration
// Class definition.
ExportNatives(Class);
ExportNativeGeneratedInitCode(Class);
{
FStringOutputDevice UInterfaceBoilerplate;
// Export the class's native function registration.
UInterfaceBoilerplate.Logf(TEXT(" private:\r\n"));
UInterfaceBoilerplate.Logf(TEXT(" static void StaticRegisterNatives%s();\r\n"),NameLookupCPP->GetNameCPP(Class));
UInterfaceBoilerplate.Log(*FriendText);
FriendText.Empty();
UInterfaceBoilerplate.Logf( TEXT("public:\r\n") );
// Build the DECLARE_CLASS line
FString APIArg(API);
if( !Class->HasAnyClassFlags( CLASS_MinimalAPI ) )
{
APIArg = TEXT("NO");
}
const bool bCastedClass = Class->HasAnyCastFlag(CASTCLASS_AllFlags) && SuperClass && Class->ClassCastFlags != SuperClass->ClassCastFlags;
UInterfaceBoilerplate.Logf( TEXT(" DECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(CLASS_Abstract%s), %s, %s, %s_API)\r\n"),
ClassCPPName,
SuperClassCPPName,
*GetClassFlagExportText(Class),
bCastedClass ? *FString::Printf(TEXT("CASTCLASS_%s"), ClassCPPName) : TEXT("0"),
*FPackageName::GetShortName(Class->GetOuter()->GetName()),
*APIArg );
if (!Class->HasAnyClassFlags(CLASS_CustomConstructor))
{
UInterfaceBoilerplate.Logf(TEXT(" %s_API %s(const class FPostConstructInitializeProperties& PCIP);\r\n"), *APIArg, ClassCPPName);
}
UInterfaceBoilerplate.Logf(TEXT(" DECLARE_SERIALIZER(%s)\r\n"), ClassCPPName);
UInterfaceBoilerplate.Log(TEXT(" enum {IsIntrinsic=COMPILED_IN_INTRINSIC};\r\n"));
if (Class->ClassWithin != Class->GetSuperClass()->ClassWithin)
{
UInterfaceBoilerplate.Logf(TEXT(" DECLARE_WITHIN(%s)\r\n"), NameLookupCPP->GetNameCPP( Class->ClassWithin ) );
}
FString MacroName = TEXT("GENERATED_UINTERFACE_BODY()");
FString Macroized = Macroize(*MacroName, *UInterfaceBoilerplate);
GeneratedHeaderText.Log(TEXT("#undef GENERATED_UINTERFACE_BODY\r\n"));
GeneratedHeaderText.Log(*Macroized);
}
// =============================================
// Export the pure interface version of the class
// the name of the pure interface class
FString InterfaceCPPName = FString::Printf(TEXT("I%s"), *Class->GetName());
FString SuperInterfaceCPPName;
if ( SuperClass != NULL )
{
SuperInterfaceCPPName = FString::Printf(TEXT("I%s"), *SuperClass->GetName());
}
// C++ -> VM stubs (native function execs)
ExportNativeFunctions(NativeFunctions);
// VM -> C++ proxies (events).
ExportCallbackFunctions(CallbackFunctions);
// Thunk functions
{
FStringOutputDevice InterfaceBoilerplate;
InterfaceBoilerplate.Logf( TEXT("protected:\r\n\tvirtual ~%s() {}\r\npublic:\r\n"), *InterfaceCPPName );
InterfaceBoilerplate.Logf( TEXT("\ttypedef %s UClassType;\r\n"), ClassCPPName );
ExportInterfaceCallFunctions(CallbackFunctions, InterfaceBoilerplate);
// we'll need a way to get to the UObject portion of a native interface, so that we can safely pass native interfaces
// to script VM functions
if (SuperClass->IsChildOf(UInterface::StaticClass()))
{
InterfaceBoilerplate.Logf(TEXT("\tvirtual UObject* GetUObjectInterface%s()=0;\r\n"), *Class->GetName());
InterfaceBoilerplate.Logf(TEXT("\tvirtual const UObject* GetUObjectInterface%s() const=0;\r\n"), *Class->GetName());
}
// Replication, add in the declaration for GetLifetimeReplicatedProps() automatically if there are any net flagged properties
bool bNeedsRep = false;
for( TFieldIterator<UProperty> It(Class,EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
if( (It->PropertyFlags & CPF_Net) != 0 )
{
bNeedsRep = true;
break;
}
}
if (bNeedsRep)
{
InterfaceBoilerplate.Logf(TEXT("\tvirtual void GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const OVERRIDE;\r\n"));
}
const TCHAR* Suffix = TEXT("_INCLASS_IINTERFACE");
FString MacroName = FString(NameLookupCPP->GetNameCPP(Class)) + Suffix;
FString Macroized = Macroize(*MacroName, *InterfaceBoilerplate);
GeneratedHeaderText.Log(*Macroized);
InClassMacroCalls.Logf( TEXT("%s%s\r\n"), FCString::Spc(4), *MacroName);
}
}
/**
* Helper function to find an element in a TArray of pointers by GetFName().
* @param Array TArray of pointers.
* @param Name Name to search for.
* @return Index of the found element, or INDEX_NONE if not found.
*/
template <typename T>
int32 FindByIndirectName( const TArray<T>& Array, const FName& Name )
{
for ( int32 Index=0; Index < Array.Num(); ++Index )
{
if ( Array[Index]->GetFName() == Name )
{
return Index;
}
}
return INDEX_NONE;
}
/**
* Appends the header definition for an inheritance hierarchy of classes to the header.
* Wrapper for ExportClassHeaderRecursive().
*
* @param Class The class to be exported.
*/
void FNativeClassHeaderGenerator::ExportClassHeader( UClass* Class )
{
TArray<UClass*> DependencyChain;
TSet<const UClass*> VisitedSet;
ExportClassHeaderRecursive(Class, DependencyChain, VisitedSet, false);
}
void FNativeClassHeaderGenerator::ExportClassHeaderInner(UClass* Class, bool bValidNonTemporaryClass)
{
if ( !Class->HasAnyClassFlags(CLASS_Interface) )
{
TArray<UEnum*> Enums;
TArray<UScriptStruct*> Structs;
TArray<UFunction*> CallbackFunctions;
TArray<UFunction*> NativeFunctions;
TArray<UFunction*> DelegateFunctions;
// get the lists of fields that belong to this class that should be exported
RetrieveRelevantFields(Class, Enums, Structs, CallbackFunctions, NativeFunctions, DelegateFunctions);
ExportNames();
// export enum declarations
ExportEnums(Enums);
// export delegate definitions
ExportDelegateDefinitions(DelegateFunctions, false);
// Export enums declared in non-UClass headers.
ExportGeneratedEnumsInitCode(Enums);
// export boilerplate macros for structs
ExportGeneratedStructBodyMacros(Structs);
if (bValidNonTemporaryClass)
{
// export parameters structs for all events and delegates
ExportEventParms(CallbackFunctions, TEXT(""));
// export .proto files for any net service functions
ExportProtoMessage(CallbackFunctions);
// export .java files for any net service functions
ExportMCPMessage(CallbackFunctions);
// export delegate wrapper function implementations
ExportDelegateDefinitions(DelegateFunctions, true);
// Class definition.
ExportNatives(Class);
}
ExportNativeGeneratedInitCode(Class);
if (bValidNonTemporaryClass)
{
// C++ -> VM stubs (native function execs)
ExportNativeFunctions(NativeFunctions);
// VM -> C++ proxies (events and delegates).
ExportCallbackFunctions(CallbackFunctions);
UClass* SuperClass = Class->GetSuperClass();
const TCHAR* ClassCPPName = NameLookupCPP->GetNameCPP( Class );
const TCHAR* SuperClassCPPName = (SuperClass != NULL) ? NameLookupCPP->GetNameCPP( SuperClass ) : NULL;
{
FStringOutputDevice ClassBoilerplate;
// Export the class's native function registration.
ClassBoilerplate.Logf(TEXT(" private:\r\n"));
ClassBoilerplate.Logf(TEXT(" static void StaticRegisterNatives%s();\r\n"),NameLookupCPP->GetNameCPP(Class));
ClassBoilerplate.Log(FriendText);
FriendText.Empty();
ClassBoilerplate.Logf(TEXT(" public:\r\n"));
// Build the DECLARE_CLASS line
FString APIArg(API);
if( !Class->HasAnyClassFlags( CLASS_MinimalAPI ) )
{
APIArg = TEXT("NO");
}
const bool bCastedClass = Class->HasAnyCastFlag(CASTCLASS_AllFlags) && SuperClass && Class->ClassCastFlags != SuperClass->ClassCastFlags;
ClassBoilerplate.Logf( TEXT(" DECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(%s%s), %s, %s, %s_API)\r\n"),
ClassCPPName,
SuperClassCPPName ? SuperClassCPPName : TEXT("None"),
Class->HasAnyClassFlags(CLASS_Abstract) ? TEXT("CLASS_Abstract") : TEXT("0"),
*GetClassFlagExportText(Class),
bCastedClass ? *FString::Printf(TEXT("CASTCLASS_%s"), ClassCPPName) : TEXT("0"),
*FPackageName::GetShortName(*Class->GetOuter()->GetName()),
*APIArg );
if (!Class->HasAnyClassFlags(CLASS_CustomConstructor))
{
ClassBoilerplate.Logf(TEXT(" /** Standard constructor, called after all reflected properties have been initialized */"));
ClassBoilerplate.Logf(TEXT(" %s_API %s(const class FPostConstructInitializeProperties& PCIP);\r\n"), *APIArg, ClassCPPName);
}
ClassBoilerplate.Logf(TEXT(" DECLARE_SERIALIZER(%s)\r\n"), ClassCPPName);
ClassBoilerplate.Log(TEXT(" /** Indicates whether the class is compiled into the engine */"));
ClassBoilerplate.Log(TEXT(" enum {IsIntrinsic=COMPILED_IN_INTRINSIC};\r\n"));
if(SuperClass && Class->ClassWithin != SuperClass->ClassWithin)
{
ClassBoilerplate.Logf(TEXT(" DECLARE_WITHIN(%s)\r\n"), NameLookupCPP->GetNameCPP( Class->ClassWithin ) );
}
// export the class's config name
if ( SuperClass && Class->ClassConfigName != NAME_None && Class->ClassConfigName != SuperClass->ClassConfigName )
{
ClassBoilerplate.Logf(TEXT(" static const TCHAR* StaticConfigName() {return TEXT(\"%s\");}\r\n\r\n"), *Class->ClassConfigName.ToString());
}
{
// arrays for preventing multiple export of accessor function in situations where the native class
// implements multiple children of the same interface base class
TArray<UClass*> UObjectExportedInterfaces;
TArray<UClass*> ExportedInterfaces;
for (TArray<FImplementedInterface>::TIterator It(Class->Interfaces); It; ++It)
{
UClass* InterfaceClass = It->Class;
if ( InterfaceClass->HasAnyClassFlags(CLASS_Native) )
{
for ( UClass* IClass = InterfaceClass; IClass && IClass->HasAnyClassFlags(CLASS_Interface); IClass = IClass->GetSuperClass() )
{
if ( IClass != UInterface::StaticClass() && !UObjectExportedInterfaces.Contains(IClass) )
{
UObjectExportedInterfaces.Add(IClass);
ClassBoilerplate.Logf(TEXT("%svirtual UObject* GetUObjectInterface%s(){return this;}\r\n"), FCString::Spc(4), *IClass->GetName());
ClassBoilerplate.Logf(TEXT("%svirtual const UObject* GetUObjectInterface%s() const{return this;}\r\n"), FCString::Spc(4), *IClass->GetName());
}
}
}
}
}
// Replication, add in the declaration for GetLifetimeReplicatedProps() automatically if there are any net flagged properties
for( TFieldIterator<UProperty> It(Class,EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
if( (It->PropertyFlags & CPF_Net) != 0 )
{
ClassBoilerplate.Logf(TEXT("\tvirtual void GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const OVERRIDE;\r\n"));
break;
}
}
const TCHAR* Suffix = TEXT("_INCLASS");
FString MacroName = FString(NameLookupCPP->GetNameCPP(Class)) + Suffix;
FString Macroized = Macroize(*MacroName, *ClassBoilerplate);
GeneratedHeaderText.Log(*Macroized);
InClassMacroCalls.Logf( TEXT("%s%s\r\n"), FCString::Spc(4), *MacroName);
}
}
}
else
{
// this is an interface class
ExportInterfaceClassDeclaration(Class);
}
}
void FNativeClassHeaderGenerator::ExportClassHeaderWrapper( UClass* Class, bool bIsExportClass )
{
const bool bTemporaryJunkClass = Class->HasAnyClassFlags(CLASS_Temporary);
const bool bValidClass = !bTemporaryJunkClass;
check(!GeneratedHeaderText.Len());
ExportClassHeaderInner(Class, bValidClass);
if (!GeneratedHeaderText.Len() && !EnumForeachText.Len())
{
// Nothing to export
return;
}
GeneratedHeaderText.Logf(TEXT("#undef UCLASS_CURRENT_FILE_NAME\r\n"));
GeneratedHeaderText.Logf(TEXT("#define UCLASS_CURRENT_FILE_NAME %s\r\n\r\n\r\n"), NameLookupCPP->GetNameCPP(CurrentClass));
if (bValidClass)
{
{
GeneratedHeaderText.Logf(TEXT("#undef UCLASS\r\n"));
GeneratedHeaderText.Logf(TEXT("#undef UINTERFACE\r\n"));
FString MacroName = FString::Printf(TEXT("%s(...)"), Class->HasAnyClassFlags(CLASS_Interface) ? TEXT("UINTERFACE") : TEXT("UCLASS"));
FString Macroized = Macroize(*MacroName, *PrologMacroCalls);
GeneratedHeaderText.Log(*Macroized);
PrologMacroCalls.Empty();
}
{
GeneratedHeaderText.Logf(TEXT("#undef GENERATED_UCLASS_BODY\r\n"));
GeneratedHeaderText.Logf(TEXT("#undef GENERATED_IINTERFACE_BODY\r\n"));
FString MacroName = FString::Printf(TEXT("GENERATED_%s_BODY()"), Class->HasAnyClassFlags(CLASS_Interface) ? TEXT("IINTERFACE") : TEXT("UCLASS"));
FString Macroized = Macroize(*MacroName, *(FString(TEXT("public:\r\n")) + InClassMacroCalls + TEXT("public:\r\n")));
GeneratedHeaderText.Log(*Macroized);
InClassMacroCalls.Empty();
}
}
const FString SourceFilename = GClassSourceFileMap[Class];
const FString PkgName = FPackageName::GetShortName(Package);
FString NewFileName = SourceFilename.EndsWith(TEXT(".h")) ? SourceFilename : (SourceFilename + TEXT(".h"));
FString PkgDir;
FString GeneratedIncludeDirectory;
if (MakeCommandlet_FindPackageLocation(*PkgName, PkgDir, GeneratedIncludeDirectory) == false)
{
UE_LOG(LogCompile, Error, TEXT("Failed to find path for package %s"), *PkgName);
}
if (bIsExportClass)
{
const FString ClassHeaderPath(GeneratedIncludeDirectory / GClassHeaderNameWithNoPathMap[Class] + TEXT(".generated.h") );
GeneratedHeaderText += EnumForeachText;
FStringOutputDevice GeneratedHeaderTextWithCopyright;
GeneratedHeaderTextWithCopyright.Log(
TEXT("// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.\r\n")
TEXT("/*===========================================================================\r\n")
TEXT(" C++ class header boilerplate exported from UnrealHeaderTool.\r\n")
TEXT(" This is automatically generated by the tools.\r\n")
TEXT(" DO NOT modify this manually! Edit the corresponding .h files instead!\r\n")
TEXT("===========================================================================*/\r\n"));
GeneratedHeaderTextWithCopyright.Log(
LINE_TERMINATOR
TEXT("#include \"ObjectBase.h\"") LINE_TERMINATOR
LINE_TERMINATOR);
GeneratedHeaderTextWithCopyright.Log(*GeneratedHeaderText);
SaveHeaderIfChanged(*ClassHeaderPath, *GeneratedHeaderTextWithCopyright);
check(SourceFilename.EndsWith(TEXT(".h")));
if(bUseRelativePaths)
{
// When building Rocket, we need relative paths in the generated headers so that they can be installed to any directory.
FPaths::MakePathRelativeTo(NewFileName, *ClassHeaderPath);
NewFileName.ReplaceInline(TEXT("\\"), TEXT("/"));
}
// ...we cannot rebase paths relative to the install directory. It may be on a different drive.
else if (FPaths::MakePathRelativeTo(NewFileName, *GManifest.RootLocalPath))
{
NewFileName = GManifest.RootBuildPath / NewFileName;
}
auto IncludeStr = FString::Printf(
TEXT("#ifndef %s_%s_generated_h") LINE_TERMINATOR
TEXT(" #include \"%s\"") LINE_TERMINATOR
TEXT("#endif") LINE_TERMINATOR,
*API, *CurrentClass->GetName(), *NewFileName );
// Keep track of all of the UObject headers for this module, in the same order that we digest them in
// @todo uht: We're wrapping these includes in checks for header guards, ONLY because most existing UObject headers are missing '#pragma once'
ListOfAllUObjectHeaderIncludes .Logf(TEXT("%s"), *IncludeStr);
ListOfPublicClassesUObjectHeaderModuleIncludes.Logf(TEXT("%s"), *IncludeStr);
// For the 'Classes' mega-header, only include legacy UObjects classes from the 'Classes' folder. Private and Public classes are
// not included here -- you should include those yourself if you need them!
const bool bIsPublicClassesHeader = GPublicClassSet.Contains( Class );
if( bIsPublicClassesHeader )
{
ListOfPublicClassesUObjectHeaderGroupIncludes.Logf(TEXT("#include \"%s\"\r\n"), *NewFileName);
}
}
GeneratedHeaderText.Empty();
EnumForeachText.Empty();
}
void FNativeClassHeaderGenerator::ExportClassHeaderRecursive( UClass* Class, TArray<UClass*>& DependencyChain, TSet<const UClass*>& VisitedSet, bool bCheckDependenciesOnly )
{
bool bIsExportClass = GClassStrippedHeaderTextMap.Contains(Class) && Class->HasAnyClassFlags(CLASS_Native|CLASS_Temporary) && !Class->HasAnyClassFlags(CLASS_NoExport);
bool bIsCorrectHeader = GClassHeaderFilenameMap.FindRef(Class) == ClassHeaderFilename && Class->GetOuter() == Package;
// Check for circular header dependencies between export classes.
if ( bIsExportClass )
{
if ( !bIsCorrectHeader )
{
if ( DependencyChain.Num() == 0 )
{
// The first export class we found doesn't belong in this header: No need to keep exporting along this dependency path.
return;
}
// From now on, we're not going to export anything. Instead, we're going to check that no deeper dependency tries to export to this header file.
bCheckDependenciesOnly = true;
}
else if ( bCheckDependenciesOnly && !ClassHeaderFilename.IsEmpty() )
{
FString DependencyChainString;
for (UClass* DependencyClass : DependencyChain)
{
DependencyChainString += DependencyClass->GetName() + TEXT(" -> ");
}
DependencyChainString += Class->GetName();
UE_LOG(LogCompile, Warning, TEXT("Circular header dependency detected (%s), while exporting %s!"), *DependencyChainString, *(FPackageName::GetShortName(Package) + ClassHeaderFilename + TEXT("Classes.h")) );
}
}
// Check if the Class has already been exported, after we've checked for circular header dependencies.
if (GExportedClasses.Contains(Class))
{
return;
}
// Check for circular dependencies.
if (VisitedSet.Contains(Class))
{
UE_LOG(LogCompile, Error, TEXT("Circular dependency detected for class %s!"), *Class->GetName() );
return;
}
// Temporarily mark the Class as VISITED. Make sure to clear this flag before returning!
VisitedSet.Add(Class);
if ( bIsExportClass )
{
DependencyChain.Add( Class );
}
// Export the super class first.
if (UClass* SuperClass = Class->GetSuperClass())
{
ExportClassHeaderRecursive(SuperClass, DependencyChain, VisitedSet, bCheckDependenciesOnly);
}
// Export all classes we depend on.
for (const FName& DependencyClassName : *GClassDependentOnMap.FindOrAdd(Class))
{
// By this point, all classes should have been validated so we can strip the given class name when searching.
FString DependencyClassNameStripped = DependencyClassName.ToString();
// Handle #include directives as 'dependson' (allowing them to fail).
const bool bClassNameFromHeader = FHeaderParser::DependentClassNameFromHeader(*DependencyClassNameStripped, DependencyClassNameStripped);
DependencyClassNameStripped = GetClassNameWithPrefixRemoved( DependencyClassNameStripped );
// Only export the class if it's in Classes array (it may be in another package).
UClass* FoundClass = FindObject<UClass>(ANY_PACKAGE, *DependencyClassNameStripped);
if (FoundClass == NULL)
{
// Try again with temporary class name. This may be struct only header.
DependencyClassNameStripped = GetClassNameWithoutPrefix(DependencyClassName.ToString());
DependencyClassNameStripped = FHeaderParser::GenerateTemporaryClassName(*DependencyClassNameStripped);
FoundClass = FindObject<UClass>(ANY_PACKAGE, *DependencyClassNameStripped);
}
int32 ClassIndex = FindByIndirectName( Classes, *DependencyClassNameStripped );
if (UClass* DependsOnClass = (ClassIndex != INDEX_NONE) ? FoundClass : NULL)
{
ExportClassHeaderRecursive(DependsOnClass, DependencyChain, VisitedSet, bCheckDependenciesOnly);
}
else if ( !FoundClass && !bClassNameFromHeader )
{
UE_LOG(LogCompile, Error, TEXT("Unknown class %s used in dependson declaration in class %s!"), *DependencyClassName.ToString(), *Class->GetName() );
}
}
// Export class header.
if ( bIsCorrectHeader && !bCheckDependenciesOnly && !Class->HasAnyClassFlags(CLASS_Intrinsic) && Class->HasAnyClassFlags(CLASS_Native|CLASS_Temporary) )
{
CurrentClass = Class;
// Mark class as exported.
GExportedClasses.Add(Class);
ExportClassHeaderWrapper(Class, bIsExportClass);
}
// We're done visiting this Class.
VisitedSet.Remove( Class );
if ( bIsExportClass )
{
DependencyChain.Pop();
}
}
/**
* Returns a string in the format CLASS_Something|CLASS_Something which represents all class flags that are set for the specified
* class which need to be exported as part of the DECLARE_CLASS macro
*/
FString FNativeClassHeaderGenerator::GetClassFlagExportText( UClass* Class )
{
FString StaticClassFlagText;
check(Class);
if ( Class->HasAnyClassFlags(CLASS_Transient) )
{
StaticClassFlagText += TEXT(" | CLASS_Transient");
}
if( Class->HasAnyClassFlags(CLASS_DefaultConfig) )
{
StaticClassFlagText += TEXT(" | CLASS_DefaultConfig");
}
if( Class->HasAnyClassFlags(CLASS_Config) )
{
StaticClassFlagText += TEXT(" | CLASS_Config");
}
if ( Class->HasAnyClassFlags(CLASS_Interface) )
{
StaticClassFlagText += TEXT(" | CLASS_Interface");
}
if ( Class->HasAnyClassFlags(CLASS_Deprecated) )
{
StaticClassFlagText += TEXT(" | CLASS_Deprecated");
}
return StaticClassFlagText;
}
void FNativeClassHeaderGenerator::RetrieveRelevantFields(UClass* Class, TArray<UEnum*>& Enums, TArray<UScriptStruct*>& Structs, TArray<UFunction*>& CallbackFunctions, TArray<UFunction*>& NativeFunctions, TArray<UFunction*>& DelegateFunctions)
{
for ( TFieldIterator<UField> It(Class,EFieldIteratorFlags::ExcludeSuper); It; ++It )
{
UField* CurrentField = *It;
UClass* FieldClass = CurrentField->GetClass();
if ( FieldClass == UEnum::StaticClass() )
{
UEnum* Enum = (UEnum*)CurrentField;
Enums.Add(Enum);
}
else if ( FieldClass == UScriptStruct::StaticClass() )
{
UScriptStruct* Struct = (UScriptStruct*)CurrentField;
Structs.Add(Struct);
}
else if ( FieldClass == UFunction::StaticClass() )
{
bool bAdded = false;
UFunction* Function = (UFunction*)CurrentField;
if ( (Function->FunctionFlags&(FUNC_Event)) != 0 &&
Function->GetSuperFunction() == NULL )
{
CallbackFunctions.Add(Function);
bAdded = true;
}
else if ( (Function->FunctionFlags&(FUNC_Delegate)) != 0 &&
Function->GetSuperFunction() == NULL )
{
DelegateFunctions.Add(Function);
bAdded = true;
}
if ( (Function->FunctionFlags&FUNC_Native) != 0 )
{
NativeFunctions.Add(Function);
bAdded = true;
}
check(bAdded)
}
}
}
/**
* Exports the header text for the list of enums specified
*
* @param Enums the enums to export
*/
void FNativeClassHeaderGenerator::ExportEnums( const TArray<UEnum*>& Enums )
{
// Enum definitions.
for( int32 EnumIdx = Enums.Num() - 1; EnumIdx >= 0; EnumIdx-- )
{
UEnum* Enum = Enums[EnumIdx];
FString QualifierPrefix;
if (!Enum->ActualEnumNameInsideNamespace.IsEmpty())
{
QualifierPrefix = Enum->GetName() + TEXT("::");
}
// Export FOREACH macro
EnumForeachText.Logf( TEXT("#define FOREACH_ENUM_%s(op) "), *Enum->GetName().ToUpper() );
for (int32 i = 0; i < Enum->NumEnums() - 1; i++)
{
const FString QualifiedEnumValue = Enum->GetEnum(i).ToString();
EnumForeachText.Logf( TEXT("\\\r\n op(%s) "), *QualifiedEnumValue );
}
EnumForeachText.Logf( TEXT("\r\n") );
}
}
// Exports the header text for the list of structs specified (GENERATED_USTRUCT_BODY impls)
void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(const TArray<UScriptStruct*>& NativeStructs)
{
// reverse the order.
for (int32 i = NativeStructs.Num() - 1; i >= 0; --i)
{
UScriptStruct* Struct = NativeStructs[i];
// Export struct.
if (Struct->StructFlags & STRUCT_Native)
{
check(Struct->StructMacroDeclaredLineNumber != INDEX_NONE);
const FString FriendApiString = FString::Printf(TEXT("%s_API "), *API);
const FString StaticConstructionString = GetSingletonName(Struct);
FString RequiredAPI;
if (!(Struct->StructFlags & STRUCT_RequiredAPI))
{
RequiredAPI = FriendApiString;
}
const FString FriendLine = FString::Printf(TEXT(" friend %sclass UScriptStruct* %s;\r\n"), *FriendApiString, *StaticConstructionString);
const FString StaticClassLine = FString::Printf(TEXT(" %sstatic class UScriptStruct* StaticStruct();\r\n"), *RequiredAPI);
const FString CombinedLine = FriendLine + StaticClassLine;
const FString MacroName =
FString::Printf(TEXT("%s_USTRUCT_BODY_LINE_%d"), NameLookupCPP->GetNameCPP(CurrentClass), Struct->StructMacroDeclaredLineNumber);
const FString Macroized = Macroize(*MacroName, *CombinedLine);
GeneratedHeaderText.Log(*Macroized);
FString SingletonName = StaticConstructionString.Replace(TEXT("()"), TEXT("")); // function address
GeneratedPackageCPP.Logf(TEXT("class UScriptStruct* %s::StaticStruct()\r\n"), NameLookupCPP->GetNameCPP(Struct));
GeneratedPackageCPP.Logf(TEXT("{\r\n"));
GeneratedPackageCPP.Logf(TEXT(" static class UScriptStruct* Singleton = NULL;\r\n"));
GeneratedPackageCPP.Logf(TEXT(" if (!Singleton)\r\n"));
GeneratedPackageCPP.Logf(TEXT(" {\r\n"));
GeneratedPackageCPP.Logf(TEXT(" extern %sclass UScriptStruct* %s;\r\n"), *FriendApiString, *StaticConstructionString);
// UStructs can have UClass or UPackage outer (if declared in non-UClass headers).
if (Struct->GetOuter()->IsA(UStruct::StaticClass()))
{
GeneratedPackageCPP.Logf(TEXT(" Singleton = GetStaticStruct(%s, %s::StaticClass(), TEXT(\"%s\"));\r\n"), *SingletonName, NameLookupCPP->GetNameCPP(CastChecked<UStruct>(Struct->GetOuter())), *Struct->GetName());
}
else
{
FString PackageSingletonName = GetPackageSingletonName(CastChecked<UPackage>(Struct->GetOuter()));
GeneratedPackageCPP.Logf(TEXT(" extern %sclass UPackage* %s;\r\n"), *FriendApiString, *PackageSingletonName);
GeneratedPackageCPP.Logf(TEXT(" Singleton = GetStaticStruct(%s, %s, TEXT(\"%s\"));\r\n"), *SingletonName, *PackageSingletonName, *Struct->GetName());
}
GeneratedPackageCPP.Logf(TEXT(" }\r\n"));
GeneratedPackageCPP.Logf(TEXT(" return Singleton;\r\n"));
GeneratedPackageCPP.Logf(TEXT("}\r\n"));
GeneratedPackageCPP.Logf(TEXT("static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_%s(%s::StaticStruct);\r\n"), NameLookupCPP->GetNameCPP(Struct), NameLookupCPP->GetNameCPP(Struct));
// Generate StaticRegisterNatives equivalent for structs without classes.
if (!Struct->GetOuter()->IsA(UStruct::StaticClass()))
{
const FString ShortPackageName = FPackageName::GetShortName(Struct->GetOuter()->GetName());
GeneratedPackageCPP.Logf(TEXT("static struct FScriptStruct_%s_StaticRegisterNatives%s\r\n"), *ShortPackageName, NameLookupCPP->GetNameCPP(Struct));
GeneratedPackageCPP.Logf(TEXT("{\r\n"));
GeneratedPackageCPP.Logf(TEXT("\tFScriptStruct_%s_StaticRegisterNatives%s()\r\n"), *ShortPackageName, NameLookupCPP->GetNameCPP(Struct));
GeneratedPackageCPP.Logf(TEXT("\t{\r\n"));
GeneratedPackageCPP.Logf( TEXT("\t\tUScriptStruct::DeferCppStructOps(FName(TEXT(\"%s\")),new UScriptStruct::TCppStructOps<%s%s>);\r\n"), *Struct->GetName(), Struct->GetPrefixCPP(), *Struct->GetName() );
GeneratedPackageCPP.Logf(TEXT("\t}\r\n"));
GeneratedPackageCPP.Logf(TEXT("} ScriptStruct_%s_StaticRegisterNatives%s;\r\n"), *ShortPackageName, NameLookupCPP->GetNameCPP(Struct));
}
}
}
}
void FNativeClassHeaderGenerator::ExportGeneratedEnumsInitCode(const TArray<UEnum*>& Enums)
{
// reverse the order.
for (int32 i = Enums.Num() - 1; i >= 0; --i)
{
UEnum* Enum = Enums[i];
// Export Enum.
if (Enum->GetOuter()->IsA(UPackage::StaticClass()))
{
const FString FriendApiString = FString::Printf(TEXT("%s_API "), *API);
const FString StaticConstructionString = GetSingletonName(Enum);
FString SingletonName = StaticConstructionString.Replace(TEXT("()"), TEXT("")); // function address
GeneratedPackageCPP.Logf(TEXT("static class UEnum* %s_StaticEnum()\r\n"), *Enum->GetName());
GeneratedPackageCPP.Logf(TEXT("{\r\n"));
GeneratedPackageCPP.Logf(TEXT(" static class UEnum* Singleton = NULL;\r\n"));
GeneratedPackageCPP.Logf(TEXT(" if (!Singleton)\r\n"));
GeneratedPackageCPP.Logf(TEXT(" {\r\n"));
GeneratedPackageCPP.Logf(TEXT(" extern %sclass UEnum* %s;\r\n"), *FriendApiString, *StaticConstructionString);
FString PackageSingletonName = GetPackageSingletonName(CastChecked<UPackage>(Enum->GetOuter()));
GeneratedPackageCPP.Logf(TEXT(" extern %sclass UPackage* %s;\r\n"), *FriendApiString, *PackageSingletonName);
GeneratedPackageCPP.Logf(TEXT(" Singleton = GetStaticEnum(%s, %s, TEXT(\"%s\"));\r\n"), *SingletonName, *PackageSingletonName, *Enum->GetName());
GeneratedPackageCPP.Logf(TEXT(" }\r\n"));
GeneratedPackageCPP.Logf(TEXT(" return Singleton;\r\n"));
GeneratedPackageCPP.Logf(TEXT("}\r\n"));
GeneratedPackageCPP.Logf(TEXT("static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_%s(%s_StaticEnum);\r\n"), *Enum->GetName(), *Enum->GetName());
}
}
}
void FNativeClassHeaderGenerator::ExportMirrorsForNoexportStructs(const TArray<UScriptStruct*>& NativeStructs, int32 TextIndent, FStringOutputDevice& HeaderOutput)
{
// reverse the order.
for (int32 i = NativeStructs.Num() - 1; i >= 0; --i)
{
UScriptStruct* Struct = NativeStructs[i];
// Export struct.
HeaderOutput.Logf( TEXT("%sstruct %s"), FCString::Spc(TextIndent), NameLookupCPP->GetNameCPP( Struct ) );
if (Struct->GetSuperStruct() != NULL)
{
HeaderOutput.Logf(TEXT(" : public %s"), NameLookupCPP->GetNameCPP(Struct->GetSuperStruct()));
}
HeaderOutput.Logf( TEXT("\r\n%s{\r\n"), FCString::Spc(TextIndent) );
// Export the struct's CPP properties.
ExportProperties(Struct, TextIndent, /*bAccessSpecifiers=*/ false, &HeaderOutput);
HeaderOutput.Logf( TEXT("%s};\r\n"), FCString::Spc(TextIndent) );
HeaderOutput.Log( TEXT("\r\n") );
}
}
/**
* Exports the parameter struct declarations for the list of functions specified
*
* @param Function the function that (may) have parameters which need to be exported
* @return true if the structure generated is not completely empty
*/
bool FNativeClassHeaderGenerator::WillExportEventParms( UFunction* Function )
{
for( TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags&CPF_Parm); ++It )
{
return true;
}
return false;
}
void WriteEventFunctionPrologue(FStringOutputDevice& Output, int32 Indent, const FParmsAndReturnProperties& Parameters, const TCHAR* ClassName, const TCHAR* FunctionName)
{
// now the body - first we need to declare a struct which will hold the parameters for the event/delegate call
Output.Logf( TEXT("\r\n%s{\r\n"), FCString::Spc(Indent) );
// declare and zero-initialize the parameters and return value, if applicable
if (!Parameters.HasParms())
return;
FString EventStructName = GetEventStructParamsName(ClassName, FunctionName);
Output.Logf( TEXT("%s%s Parms;\r\n"), FCString::Spc(Indent + 4), *EventStructName );
// Declare a parameter struct for this event/delegate and assign the struct members using the values passed into the event/delegate call.
for (auto It = Parameters.Parms.CreateConstIterator(); It; ++It)
{
UProperty* Prop = *It;
const FString PropertyName = Prop->GetName();
if ( Prop->ArrayDim > 1 )
{
Output.Logf( TEXT("%sFMemory::Memcpy(Parms.%s,%s,sizeof(Parms.%s));\r\n"), FCString::Spc(Indent + 4), *PropertyName, *PropertyName, *PropertyName );
}
else
{
FString ValueAssignmentText = PropertyName;
if (Prop->IsA<UBoolProperty>())
{
ValueAssignmentText += TEXT(" ? true : false");
}
Output.Logf( TEXT("%sParms.%s=%s;\r\n"), FCString::Spc(Indent + 4), *PropertyName, *ValueAssignmentText );
}
}
}
void WriteEventFunctionEpilogue(FStringOutputDevice& Output, int32 Indent, const FParmsAndReturnProperties& Parameters, const TCHAR* ClassName, const TCHAR* FunctionName)
{
// Out parm copying.
for (auto It = Parameters.Parms.CreateConstIterator(); It; ++It)
{
UProperty* Prop = *It;
if (Prop->HasAnyPropertyFlags(CPF_OutParm) && (!Prop->HasAnyPropertyFlags(CPF_ConstParm) || Prop->IsA<UObjectPropertyBase>()))
{
const FString PropertyName = Prop->GetName();
if ( Prop->ArrayDim > 1 )
{
Output.Logf( TEXT("%sFMemory::Memcpy(&%s,&Parms.%s,sizeof(%s));\r\n"), FCString::Spc(Indent + 4), *PropertyName, *PropertyName, *PropertyName );
}
else
{
Output.Logf(TEXT("%s%s=Parms.%s;\r\n"), FCString::Spc(Indent + 4), *PropertyName, *PropertyName);
}
}
}
// Return value.
if (Parameters.Return)
{
// Make sure uint32 -> bool is supported
bool bBoolProperty = Parameters.Return->IsA(UBoolProperty::StaticClass());
Output.Logf( TEXT("%sreturn %sParms.%s;\r\n"), FCString::Spc(Indent + 4), bBoolProperty ? TEXT("!!") : TEXT(""), *Parameters.Return->GetName() );
}
Output.Logf( TEXT("%s}\r\n"), FCString::Spc(Indent) );
}
/**
* Exports C++ type definitions for delegates
*
* @param DelegateFunctions the functions that have parameters which need to be exported
* @param bWrapperImplementationsOnly True if only function implementations for delegate wrappers should be exported
*/
void FNativeClassHeaderGenerator::ExportDelegateDefinitions( const TArray<UFunction*>& DelegateFunctions, const bool bWrapperImplementationsOnly )
{
FStringOutputDevice HeaderOutput;
if( bWrapperImplementationsOnly )
{
// Export parameters structs for all delegates. We'll need these to declare our delegate execution function.
ExportEventParms( DelegateFunctions, TEXT(""), 0, true, &HeaderOutput);
}
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
UClass* Class = CurrentClass;
const FString ClassName = Class->GetName();
for ( int32 i = DelegateFunctions.Num() - 1; i >= 0 ; i-- )
{
UFunction* Function = DelegateFunctions[i];
check( Function->HasAnyFunctionFlags( FUNC_Delegate ) );
const bool bIsMulticastDelegate = Function->HasAnyFunctionFlags( FUNC_MulticastDelegate );
// Unmangle the function name
const FString DelegateName = Function->GetName().LeftChop( FString( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ).Len() );
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
FFuncInfo FunctionData = CompilerInfo->GetFunctionData();
if( bWrapperImplementationsOnly )
{
// Always export delegate wrapper functions as inline
FunctionData.FunctionExportFlags |= FUNCEXPORT_Inline;
}
// Add class name to beginning of function, to avoid collisions with other classes with the same delegate name in this scope
FString Delegate(TEXT("delegate"));
check(FunctionData.MarshallAndCallName.StartsWith(Delegate));
FString ShortName = *FunctionData.MarshallAndCallName + Delegate.Len();
FunctionData.MarshallAndCallName = FString::Printf( TEXT( "F%s_DelegateWrapper" ), *ShortName );
// Setup delegate parameter
const FString ExtraParam( FString::Printf( TEXT( "const %s& %s" ),
bIsMulticastDelegate ? TEXT( "FMulticastScriptDelegate" ) : TEXT( "FScriptDelegate" ),
*DelegateName ) );
// export the line that looks like: int32 Main(const FString& Parms)
ExportNativeFunctionHeader(FunctionData,HeaderOutput,EExportFunctionType::Event,EExportFunctionHeaderStyle::Declaration,*ExtraParam);
if( !bWrapperImplementationsOnly )
{
// Only exporting function prototype
HeaderOutput.Logf( TEXT( ";\r\n" ) );
}
else
{
auto Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference);
WriteEventFunctionPrologue(HeaderOutput, 0, Parameters, *ClassName, *DelegateName);
{
const TCHAR* DelegateType = bIsMulticastDelegate ? TEXT( "ProcessMulticastDelegate" ) : TEXT( "ProcessDelegate" );
const TCHAR* DelegateArg = Parameters.HasParms() ? TEXT("&Parms") : TEXT("NULL");
HeaderOutput.Logf( TEXT("%s%s.%s<UObject>(%s);\r\n"), FCString::Spc(4), *DelegateName, DelegateType, DelegateArg );
}
WriteEventFunctionEpilogue(HeaderOutput, 0, Parameters, *ClassName, *DelegateName);
}
}
if (HeaderOutput.Len())
{
if( !bWrapperImplementationsOnly )
{
// these are at the top, so we can output directly
GeneratedHeaderText.Log(*HeaderOutput);
GeneratedHeaderText.Log(TEXT("\r\n\r\n"));
}
else
{
const TCHAR* Suffix = TEXT("_DELEGATES");
FString MacroName = FString(NameLookupCPP->GetNameCPP(CurrentClass)) + Suffix;
FString Macroized = Macroize(*MacroName, *HeaderOutput);
GeneratedHeaderText.Log(*Macroized);
PrologMacroCalls.Logf( TEXT("%s%s\r\n"), FCString::Spc(0), *MacroName);
}
}
}
/**
* Export a single .proto declaration, recursing as necessary for sub declarations
*
* @param Out output device
* @param MessageName name of the message in the declaration
* @param Properties array of parameters in the function definition
* @param PropertyFlags flags to filter property array against
* @param Ident starting indentation level
*/
void ExportProtoDeclaration(FOutputDevice& Out, const FString& MessageName, TFieldIterator<UProperty>& Properties, uint64 PropertyFlags, int32 Indent)
{
Out.Logf(TEXT("%smessage CMsg%sMessage\r\n"), FCString::Spc(Indent), *MessageName);
Out.Logf(TEXT("%s{\r\n"), FCString::Spc(Indent));
static TMap<FString, FString> ToProtoTypeMappings;
static bool bInitMapping = false;
if (!bInitMapping)
{
// Explicit type mappings
ToProtoTypeMappings.Add(TEXT("FString"), TEXT("string"));
ToProtoTypeMappings.Add(TEXT("int32"), TEXT("int32"));
ToProtoTypeMappings.Add(TEXT("int64"), TEXT("int64"));
ToProtoTypeMappings.Add(TEXT("uint8"), TEXT("bytes"));
ToProtoTypeMappings.Add(TEXT("bool"), TEXT("bool"));
ToProtoTypeMappings.Add(TEXT("double"), TEXT("double"));
ToProtoTypeMappings.Add(TEXT("float"), TEXT("float"));
bInitMapping = true;
}
int32 FieldIdx = 1;
for(; Properties && (Properties->PropertyFlags & PropertyFlags); ++Properties)
{
UProperty* Property = *Properties;
UClass* PropClass = Property->GetClass();
// Skip out and return paramaters
if ((Property->PropertyFlags & CPF_RepSkip) || (Property->PropertyFlags & CPF_ReturnParm))
{
continue;
}
// export the property type text (e.g. FString; int32; TArray, etc.)
FString TypeText, ExtendedTypeText;
TypeText = Property->GetCPPType(&ExtendedTypeText, CPPF_None);
if (PropClass != UInterfaceProperty::StaticClass() && PropClass != UObjectProperty::StaticClass())
{
bool bIsRepeated = false;
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property);
UAttributeProperty* AttributeProperty = Cast<UAttributeProperty>(Property);
if (ArrayProperty != NULL || AttributeProperty != NULL)
{
UProperty* Inner = ArrayProperty ? ArrayProperty->Inner : AttributeProperty->Inner;
UClass* InnerPropClass = Inner->GetClass();
if (InnerPropClass != UInterfaceProperty::StaticClass() && InnerPropClass != UObjectProperty::StaticClass())
{
FString InnerExtendedTypeText;
FString InnerTypeText = Inner->GetCPPType(&InnerExtendedTypeText, CPPF_None);
TypeText = InnerTypeText;
ExtendedTypeText = InnerExtendedTypeText;
Property = Inner;
bIsRepeated = true;
}
else
{
FError::Throwf(TEXT("ExportProtoDeclaration - Unhandled property type '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
}
}
else if(Property->ArrayDim != 1)
{
bIsRepeated = true;
}
FString VariableTypeName = FString::Printf(TEXT("%s%s"), *TypeText, *ExtendedTypeText);
FString* ProtoTypeName = NULL;
UStructProperty* StructProperty = Cast<UStructProperty>(Property);
if (StructProperty != NULL)
{
TFieldIterator<UProperty> StructIt(StructProperty->Struct);
ExportProtoDeclaration(Out, VariableTypeName, StructIt, CPF_AllFlags, Indent + 4);
VariableTypeName = FString::Printf(TEXT("CMsg%sMessage"), *VariableTypeName);
ProtoTypeName = &VariableTypeName;
}
else
{
ProtoTypeName = ToProtoTypeMappings.Find(VariableTypeName);
}
Out.Log(FCString::Spc(Indent + 4));
if (bIsRepeated)
{
Out.Log( TEXT("repeated "));
}
else
{
Out.Log( TEXT("optional "));
}
if (ProtoTypeName != NULL)
{
Out.Logf( TEXT("%s %s = %d;\r\n"), **ProtoTypeName, *Property->GetNameCPP(), FieldIdx);
FieldIdx++;
}
else
{
FError::Throwf(TEXT("ExportProtoDeclaration - Unhandled property mapping '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
}
}
else
{
FError::Throwf(TEXT("ExportProtoDeclaration - Unhandled property type '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
}
}
Out.Logf( TEXT("%s}\r\n"), FCString::Spc(Indent) );
}
/**
* Generate a .proto message declaration for any functions marked as requiring one
*
* @param InCallbackFunctions array of functions for consideration to generate .proto definitions
* @param Indent starting indentation level
* @param Output optional output redirect
*/
void FNativeClassHeaderGenerator::ExportProtoMessage(const TArray<UFunction*>& InCallbackFunctions, int32 Indent, class FStringOutputDevice* Output)
{
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
// Parms struct definitions.
FStringOutputDevice HeaderOutput;
TArray<UFunction*> CallbackFunctions = InCallbackFunctions;
CallbackFunctions.Sort();
for (int32 Index = 0; Index < CallbackFunctions.Num(); Index++)
{
UFunction* Function = CallbackFunctions[Index];
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData();
if (FunctionData.FunctionExportFlags & FUNCEXPORT_NeedsProto)
{
if (WillExportEventParms(Function) && !Function->HasAnyFunctionFlags(FUNC_Delegate))
{
FString FunctionName = Function->GetName();
TFieldIterator<UProperty> CommentIt(Function);
FString ParameterList;
for(; CommentIt && (CommentIt->PropertyFlags & CPF_Parm); ++CommentIt)
{
UProperty* Param = *CommentIt;
FString TypeText, ExtendedTypeText;
TypeText = Param->GetCPPType(&ExtendedTypeText, CPPF_None);
FString ParamName = FString::Printf(TEXT("%s%s %s"), *TypeText, *ExtendedTypeText, *Param->GetName());
// add this property to the parameter list string
if (ParameterList.Len())
{
ParameterList += TCHAR(',');
}
ParameterList += ParamName;
}
HeaderOutput.Logf(TEXT("// %s%s(%s)\r\n"), FCString::Spc(Indent), *FunctionName, *ParameterList);
TFieldIterator<UProperty> ParamIt(Function);
ExportProtoDeclaration(HeaderOutput, FunctionName, ParamIt, CPF_Parm, Indent);
}
}
}
if (!Output)
{
GeneratedProtoText.Log(*HeaderOutput);
}
else
{
Output->Log(HeaderOutput);
}
}
// Java uses different coding standards for capitalization
static FString FixJavaName(const FString &StringIn)
{
FString FixedString = StringIn;
FixedString[0] = FChar::ToLower(FixedString[0]); // java classes/variable start lower case
FixedString.ReplaceInline(TEXT("ID"), TEXT("Id"), ESearchCase::CaseSensitive); // Id is standard instead of ID, some of our fnames use ID
return FixedString;
}
/**
* Export a single .java declaration, recursing as necessary for sub declarations
*
* @param Out output device
* @param MessageName name of the message in the declaration
* @param Properties array of parameters in the function definition
* @param PropertyFlags flags to filter property array against
* @param Ident starting indentation level
*/
void ExportMCPDeclaration(FOutputDevice& Out, const FString& MessageName, TFieldIterator<UProperty>& Properties, uint64 PropertyFlags, int32 Indent)
{
Out.Logf(TEXT("%spublic class %sCommand extends ProfileCommand\r\n"), FCString::Spc(Indent), *MessageName);
Out.Logf(TEXT("%s{\r\n"), FCString::Spc(Indent));
static TMap<FString, FString> ToMCPTypeMappings;
static TMap<FString, FString> ToAnnotationMappings;
static bool bInitMapping = false;
if (!bInitMapping)
{
// Explicit type mappings
ToMCPTypeMappings.Add(TEXT("FString"), TEXT("String"));
ToMCPTypeMappings.Add(TEXT("int32"), TEXT("int"));
ToMCPTypeMappings.Add(TEXT("int64"), TEXT("int"));
ToMCPTypeMappings.Add(TEXT("uint8"), TEXT("byte"));
ToMCPTypeMappings.Add(TEXT("bool"), TEXT("boolean"));
ToMCPTypeMappings.Add(TEXT("double"), TEXT("double"));
ToMCPTypeMappings.Add(TEXT("float"), TEXT("float"));
ToMCPTypeMappings.Add(TEXT("byte"), TEXT("byte"));
ToAnnotationMappings.Add(TEXT("FString"), TEXT("@NotBlankOrNull"));
bInitMapping = true;
}
FString ConstructorParams;
FString ConstructorText;
for(; Properties && (Properties->PropertyFlags & PropertyFlags); ++Properties)
{
UProperty* Property = *Properties;
UClass* PropClass = Property->GetClass();
// Skip out and return paramaters
if ((Property->PropertyFlags & CPF_RepSkip) || (Property->PropertyFlags & CPF_ReturnParm))
{
continue;
}
// export the property type text (e.g. FString; int32; TArray, etc.)
FString TypeText, ExtendedTypeText;
TypeText = Property->GetCPPType(&ExtendedTypeText, CPPF_None);
if (PropClass != UInterfaceProperty::StaticClass() && PropClass != UObjectProperty::StaticClass())
{
// TODO Implement arrays
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property);
if (ArrayProperty != NULL)
{
// skip array generation for Java, this should result in a List<TYPE> declaration, but we can do this by hand for now.
continue;
}
/*bool bIsRepeated = false;
UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property);
if (ArrayProperty != NULL)
{
UClass* InnerPropClass = ArrayProperty->Inner->GetClass();
if (InnerPropClass != UInterfaceProperty::StaticClass() && InnerPropClass != UObjectProperty::StaticClass())
{
FString InnerExtendedTypeText;
FString InnerTypeText = ArrayProperty->Inner->GetCPPType(&InnerExtendedTypeText, CPPF_None);
TypeText = InnerTypeText;
ExtendedTypeText = InnerExtendedTypeText;
Property = ArrayProperty->Inner;
bIsRepeated = true;
}
else
{
FError::Throwf(TEXT("ExportMCPDeclaration - Unhandled property type '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
}
}
else if(Property->ArrayDim != 1)
{
bIsRepeated = true;
}*/
FString VariableTypeName = FString::Printf(TEXT("%s%s"), *TypeText, *ExtendedTypeText);
FString PropertyName = FixJavaName(Property->GetNameCPP());
FString* MCPTypeName = NULL;
FString* AnnotationName = NULL;
UStructProperty* StructProperty = Cast<UStructProperty>(Property);
if (StructProperty != NULL)
{
// TODO Implement structs
/* TFieldIterator<UProperty> StructIt(StructProperty->Struct);
ExportMCPDeclaration(Out, VariableTypeName, StructIt, CPF_AllFlags, Indent + 4);
VariableTypeName = FString::Printf(TEXT("CMsg%sMessage"), *VariableTypeName);
MCPTypeName = &VariableTypeName;*/
}
else
{
UByteProperty* ByteProperty = Cast<UByteProperty>(Property);
if (ByteProperty != NULL && ByteProperty->Enum != NULL)
{
// treat enums like strings because that's how they'll be exported in JSON
MCPTypeName = ToMCPTypeMappings.Find(TEXT("FString"));
AnnotationName = ToAnnotationMappings.Find(TEXT("FString"));
}
else
{
MCPTypeName = ToMCPTypeMappings.Find(VariableTypeName);
AnnotationName = ToAnnotationMappings.Find(VariableTypeName);
}
}
if (AnnotationName != NULL && !AnnotationName->IsEmpty())
{
Out.Log(FCString::Spc(Indent + 4));
Out.Logf(TEXT("%s\r\n"), **AnnotationName);
}
if (MCPTypeName != NULL)
{
Out.Log(FCString::Spc(Indent + 4));
Out.Logf(TEXT("private %s %s;\r\n"), **MCPTypeName, *PropertyName);
ConstructorParams += FString::Printf(TEXT(", %s %s"), **MCPTypeName, *PropertyName);
ConstructorText += FString::Printf(TEXT("%sthis.%s = %s;\r\n"), FCString::Spc(Indent + 8), *PropertyName, *PropertyName);
}
else
{
FError::Throwf(TEXT("ExportMCPDeclaration - Unhandled property mapping '%s' (%s): %s"), *PropClass->GetName(), *VariableTypeName, *Property->GetPathName());
}
}
else
{
FError::Throwf(TEXT("ExportMCPDeclaration - Unhandled property type '%s': %s"), *PropClass->GetName(), *Property->GetPathName());
}
}
Out.Logf(TEXT("\r\n%spublic %sCommand(String epicId, String profileId%s)\r\n"), FCString::Spc(Indent + 4), *MessageName, *ConstructorParams);
Out.Logf(TEXT("%s{\r\n"), FCString::Spc(Indent + 4));
Out.Logf(TEXT("%ssuper(epicId, profileId);\r\n"), FCString::Spc(Indent + 8));
Out.Logf(TEXT("%s"), *ConstructorText);
Out.Logf(TEXT("%s}\r\n"), FCString::Spc(Indent + 4));
Out.Logf(TEXT("\r\n%s@Override\r\n"), FCString::Spc(Indent + 4));
Out.Logf(TEXT("%sprotected void execute(@Name(\"profile\") @NotNull ProfileEx profile)\r\n"), FCString::Spc(Indent + 4));
Out.Logf(TEXT("%s{\r\n"), FCString::Spc(Indent + 4));
Out.Logf(TEXT("%s}\r\n"), FCString::Spc(Indent + 4));
Out.Logf( TEXT("%s}\r\n"), FCString::Spc(Indent) );
}
/**
* Generate a .MCP message declaration for any functions marked as requiring one
*
* @param InCallbackFunctions array of functions for consideration to generate .proto definitions
* @param Indent starting indentation level
* @param Output optional output redirect
*/
void FNativeClassHeaderGenerator::ExportMCPMessage(const TArray<UFunction*>& InCallbackFunctions, int32 Indent, class FStringOutputDevice* Output)
{
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
// Parms struct definitions.
FStringOutputDevice HeaderOutput;
TArray<UFunction*> CallbackFunctions = InCallbackFunctions;
CallbackFunctions.Sort();
for (int32 Index = 0; Index < CallbackFunctions.Num(); Index++)
{
UFunction* Function = CallbackFunctions[Index];
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData();
if (FunctionData.FunctionExportFlags & FUNCEXPORT_NeedsMCP)
{
if (WillExportEventParms(Function) && !Function->HasAnyFunctionFlags(FUNC_Delegate))
{
FString FunctionName = Function->GetName();
TFieldIterator<UProperty> CommentIt(Function);
FString ParameterList;
for(; CommentIt && (CommentIt->PropertyFlags & CPF_Parm); ++CommentIt)
{
UProperty* Param = *CommentIt;
FString TypeText, ExtendedTypeText;
TypeText = Param->GetCPPType(&ExtendedTypeText, CPPF_None);
FString ParamName = FString::Printf(TEXT("%s%s %s"), *TypeText, *ExtendedTypeText, *Param->GetName());
// add this property to the parameter list string
if (ParameterList.Len())
{
ParameterList += TCHAR(',');
}
ParameterList += ParamName;
}
HeaderOutput.Logf(TEXT("// %s%s(%s)\r\n"), FCString::Spc(Indent), *FunctionName, *ParameterList);
TFieldIterator<UProperty> ParamIt(Function);
ExportMCPDeclaration(HeaderOutput, FunctionName, ParamIt, CPF_Parm, Indent);
}
}
}
if (!Output)
{
GeneratedMCPText.Log(*HeaderOutput);
}
else
{
Output->Log(HeaderOutput);
}
}
/**
* Exports the parameter struct declarations for the list of functions specified
*
* @param CallbackFunctions the functions that have parameters which need to be exported
*/
void FNativeClassHeaderGenerator::ExportEventParms( const TArray<UFunction*>& InCallbackFunctions, const TCHAR* MacroSuffix, int32 Indent, bool bOutputConstructor, class FStringOutputDevice* Output )
{
// Parms struct definitions.
FStringOutputDevice HeaderOutput;
TArray<UFunction*> CallbackFunctions = InCallbackFunctions;
CallbackFunctions.Sort();
for ( int32 Index = 0; Index < CallbackFunctions.Num(); Index++ )
{
UFunction* Function = CallbackFunctions[Index];
if (!WillExportEventParms(Function))
{
continue;
}
FString FunctionName = Function->GetName();
if( Function->HasAnyFunctionFlags( FUNC_Delegate ) )
{
FunctionName = FunctionName.LeftChop( FString( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ).Len() );
}
FString EventParmStructName = GetEventStructParamsName(*Function->GetOwnerClass()->GetName(), *FunctionName);
HeaderOutput.Logf( TEXT("%sstruct %s\r\n"), FCString::Spc(Indent), *EventParmStructName);
HeaderOutput.Logf( TEXT("%s{\r\n"), FCString::Spc(Indent) );
for( TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags&CPF_Parm); ++It )
{
UProperty* Prop = *It;
FStringOutputDevice PropertyText;
PropertyText.Log( FCString::Spc(4 + Indent) );
bool bEmitConst = Prop->HasAnyPropertyFlags(CPF_ConstParm) && Prop->IsA<UObjectProperty>();
//@TODO: UCREMOVAL: This is awful code duplication to avoid a double-const
{
// export 'const' for parameters
const bool bIsConstParam = (Prop->IsA(UInterfaceProperty::StaticClass()) && !Prop->HasAllPropertyFlags(CPF_OutParm)); //@TODO: This should be const once that flag exists
const bool bIsOnConstClass = (Prop->IsA(UObjectProperty::StaticClass()) && ((UObjectProperty*)Prop)->PropertyClass != NULL && ((UObjectProperty*)Prop)->PropertyClass->HasAnyClassFlags(CLASS_Const));
if (bIsConstParam || bIsOnConstClass)
{
bEmitConst = false; // ExportCppDeclaration will do it for us
}
}
if (bEmitConst)
{
PropertyText.Logf(TEXT("const "));
}
const FString* Dim = GArrayDimensions.Find(Prop);
Prop->ExportCppDeclaration(PropertyText, EExportedDeclaration::Local, Dim ? **Dim : NULL);
ApplyAlternatePropertyExportText(Prop, PropertyText);
PropertyText.Log( TEXT(";\r\n") );
HeaderOutput += *PropertyText;
}
// constructor must initialize the return property if it needs it
UProperty* Prop = Function->GetReturnProperty();
if (Prop && bOutputConstructor)
{
FStringOutputDevice InitializationAr;
UStructProperty* InnerStruct = Cast<UStructProperty>(Prop);
bool bNeedsOutput = true;
if (InnerStruct)
{
bNeedsOutput = InnerStruct->HasNoOpConstructor();
}
else if (
Cast<UNameProperty>(Prop) ||
Cast<UDelegateProperty>(Prop) ||
Cast<UMulticastDelegateProperty>(Prop) ||
Cast<UStrProperty>(Prop) ||
Cast<UTextProperty>(Prop) ||
Cast<UArrayProperty>(Prop) ||
Cast<UInterfaceProperty>(Prop) ||
Cast<UAttributeProperty>(Prop)
)
{
bNeedsOutput = false;
}
if (bNeedsOutput)
{
check(Prop->ArrayDim == 1); // can't return arrays
HeaderOutput.Logf(TEXT("\r\n%s/** Constructor, intializes return property only **/\r\n"), FCString::Spc(4 + Indent));
HeaderOutput.Logf(TEXT("%s%s()\r\n"), FCString::Spc(4 + Indent), *EventParmStructName);
HeaderOutput.Logf(TEXT("%s%s %s(%s)\r\n"), FCString::Spc(8 + Indent),TEXT(":") , *Prop->GetName(), *GetNullParameterValue(Prop,false,true));
HeaderOutput.Logf(TEXT("%s{\r\n"), FCString::Spc(4 + Indent));
HeaderOutput.Logf(TEXT("%s}\r\n"), FCString::Spc(4 + Indent));
}
}
HeaderOutput.Logf( TEXT("%s};\r\n"), FCString::Spc(Indent) );
}
if (!Output)
{
const TCHAR* Suffix = TEXT("_EVENTPARMS");
FString MacroName = FString(NameLookupCPP->GetNameCPP(CurrentClass)) + Suffix + MacroSuffix;
FString Macroized = Macroize(*MacroName, *HeaderOutput);
GeneratedHeaderText.Log(*Macroized);
PrologMacroCalls.Logf( TEXT("%s%s\r\n"), FCString::Spc(Indent), *MacroName);
}
else
{
Output->Log(HeaderOutput);
}
}
/**
* Get the intrinsic null value for this property
*
* @param Prop the property to get the null value for
* @param bMacroContext true when exporting the P_GET* macro, false when exporting the friendly C++ function header
*
* @return the intrinsic null value for the property (0 for ints, TEXT("") for strings, etc.)
*/
FString FNativeClassHeaderGenerator::GetNullParameterValue( UProperty* Prop, bool bMacroContext, bool bInitializer/*=false*/ )
{
UClass* PropClass = Prop->GetClass();
UObjectPropertyBase* ObjectProperty = Cast<UObjectPropertyBase>(Prop);
if (PropClass == UByteProperty::StaticClass()
|| PropClass == UIntProperty::StaticClass()
|| PropClass == UBoolProperty::StaticClass()
|| PropClass == UFloatProperty::StaticClass()
|| PropClass == UDoubleProperty::StaticClass())
{
// if we have a BoolProperty then set it to be false instead of 0
if( PropClass == UBoolProperty::StaticClass() )
{
return TEXT("false");
}
return TEXT("0");
}
else if ( PropClass == UNameProperty::StaticClass() )
{
return TEXT("NAME_None");
}
else if ( PropClass == UStrProperty::StaticClass() )
{
return TEXT("TEXT(\"\")");
}
else if ( PropClass == UTextProperty::StaticClass() )
{
return TEXT("FText::GetEmpty()");
}
else if ( PropClass == UArrayProperty::StaticClass()
|| PropClass == UDelegateProperty::StaticClass()
|| PropClass == UMulticastDelegateProperty::StaticClass()
|| PropClass == UAttributeProperty::StaticClass() )
{
FString Type, ExtendedType;
Type = Prop->GetCPPType(&ExtendedType,CPPF_OptionalValue);
return Type + ExtendedType + TEXT("()");
}
else if ( PropClass == UStructProperty::StaticClass() )
{
bool bHasNoOpConstuctor = CastChecked<UStructProperty>(Prop)->HasNoOpConstructor();
if (bInitializer && bHasNoOpConstuctor)
{
return TEXT("ForceInit");
}
FString Type, ExtendedType;
Type = Prop->GetCPPType(&ExtendedType,CPPF_OptionalValue);
return Type + ExtendedType + (bHasNoOpConstuctor ? TEXT("(ForceInit)") : TEXT("()"));
}
else if (ObjectProperty)
{
return TEXT("NULL");
}
else if ( PropClass == UInterfaceProperty::StaticClass() )
{
return TEXT("NULL");
}
UE_LOG(LogCompile, Fatal,TEXT("GetNullParameterValue - Unhandled property type '%s': %s"), *PropClass->GetName(), *Prop->GetPathName());
return TEXT("");
}
void FNativeClassHeaderGenerator::ExportNativeFunctionHeader( const FFuncInfo& FunctionData, FStringOutputDevice& HeaderOutput, EExportFunctionType::Type FunctionType, EExportFunctionHeaderStyle::Type FunctionHeaderStyle, const TCHAR* ExtraParam )
{
UFunction* Function = FunctionData.FunctionReference;
const bool bIsDelegate = Function->HasAnyFunctionFlags( FUNC_Delegate );
const bool bIsInterface = !bIsDelegate && Function->GetOwnerClass()->HasAnyClassFlags(CLASS_Interface);
const bool bIsK2Override = Function->HasAnyFunctionFlags( FUNC_BlueprintEvent );
HeaderOutput.Log( FCString::Spc( bIsDelegate ? 0 : 4) );
if (FunctionHeaderStyle == EExportFunctionHeaderStyle::Declaration)
{
// cpp implementation of functions never have these appendages
// If the function was marked as 'RequiredAPI', then add the *_API macro prefix. Note that if the class itself
// was marked 'RequiredAPI', this is not needed as C++ will exports all methods automatically.
if( !Function->GetOwnerClass()->HasAnyClassFlags( CLASS_RequiredAPI ) &&
( FunctionData.FunctionExportFlags & FUNCEXPORT_RequiredAPI ) )
{
HeaderOutput.Log( FString::Printf( TEXT( "%s_API " ), *API ) );
}
if(FunctionType == EExportFunctionType::Interface)
{
HeaderOutput.Log(TEXT("static "));
}
else if (bIsK2Override)
{
HeaderOutput.Log(TEXT("virtual "));
}
// if the owning class is an interface class
else if ( bIsInterface )
{
HeaderOutput.Log(TEXT("virtual "));
}
// this is not an event, the function is not a static function and the function is not marked final
else if ( FunctionType != EExportFunctionType::Event && !Function->HasAnyFunctionFlags(FUNC_Static) && !(FunctionData.FunctionExportFlags & FUNCEXPORT_Final) )
{
HeaderOutput.Log(TEXT("virtual "));
}
else if( FunctionData.FunctionExportFlags & FUNCEXPORT_Inline )
{
HeaderOutput.Log(TEXT("inline "));
}
}
if (auto Return = Function->GetReturnProperty())
{
FString ExtendedReturnType;
FString ReturnType = Return->GetCPPType(&ExtendedReturnType, (FunctionHeaderStyle == EExportFunctionHeaderStyle::Definition && (FunctionType != EExportFunctionType::Interface) ? CPPF_Implementation : 0) | CPPF_ArgumentOrReturnValue);
FStringOutputDevice ReplacementText(*ReturnType);
ApplyAlternatePropertyExportText(Return, ReplacementText);
HeaderOutput.Logf(TEXT("%s%s"), *ReplacementText, *ExtendedReturnType);
}
else
{
HeaderOutput.Log( TEXT("void") );
}
FString FunctionName;
if (FunctionHeaderStyle == EExportFunctionHeaderStyle::Definition)
{
FunctionName = FString(NameLookupCPP->GetNameCPP(CastChecked<UClass>(Function->GetOuter()), bIsInterface || FunctionType == EExportFunctionType::Interface)) + TEXT("::");
}
if (FunctionType == EExportFunctionType::Interface)
{
FunctionName += FString::Printf(TEXT("Execute_%s"), *Function->GetName());
}
else if (FunctionType == EExportFunctionType::Event)
{
FunctionName += FunctionData.MarshallAndCallName;
}
else
{
FunctionName += FunctionData.CppImplName;
}
HeaderOutput.Logf( TEXT(" %s("), *FunctionName);
int32 ParmCount=0;
// Emit extra parameter if we have one
if( ExtraParam )
{
HeaderOutput += ExtraParam;
++ParmCount;
}
for( TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags&(CPF_Parm|CPF_ReturnParm))==CPF_Parm; ++It )
{
UProperty* Property = *It;
if( ParmCount++ )
{
HeaderOutput.Log(TEXT(", "));
}
FStringOutputDevice PropertyText;
const FString* Dim = GArrayDimensions.Find(Property);
Property->ExportCppDeclaration( PropertyText, EExportedDeclaration::Parameter, Dim ? **Dim : NULL );
ApplyAlternatePropertyExportText(Property, PropertyText);
HeaderOutput += PropertyText;
}
HeaderOutput.Log( TEXT(")") );
if (FunctionType != EExportFunctionType::Interface)
{
if (!bIsDelegate && Function->HasAllFunctionFlags(FUNC_Const))
{
HeaderOutput.Log( TEXT(" const") );
}
if (bIsInterface && FunctionHeaderStyle == EExportFunctionHeaderStyle::Declaration)
{
// all methods in interface classes are pure virtuals
HeaderOutput.Log(TEXT("=0"));
}
}
}
/**
* Export the actual internals to a standard thunk function
*
* @param RPCWrappers output device for writing
* @param FunctionData function data for the current function
* @param Parameters list of parameters in the function
* @param Return return parameter for the function
*/
void FNativeClassHeaderGenerator::ExportFunctionThunk(FStringOutputDevice& RPCWrappers, const FFuncInfo& FunctionData, const TArray<UProperty*>& Parameters, UProperty* Return)
{
// export the GET macro for this parameter
FString ParameterList;
for ( int32 ParameterIndex = 0; ParameterIndex < Parameters.Num(); ParameterIndex++ )
{
UProperty* Param = Parameters[ParameterIndex];
FString EvalBaseText = TEXT("P_GET_"); // e.g. P_GET_STR
FString EvalModifierText; // e.g. _REF
FString EvalParameterText; // e.g. (UObject*,NULL)
FString TypeText;
if ( Param->ArrayDim > 1 )
{
EvalBaseText += TEXT("ARRAY");
TypeText = Param->GetCPPType();
}
else
{
EvalBaseText += Param->GetCPPMacroType(TypeText);
}
FStringOutputDevice ReplacementText(*TypeText);
ApplyAlternatePropertyExportText(Param, ReplacementText);
TypeText = ReplacementText;
FString DefaultValueText;
FString ParamPrefix;
// if this property is an out parm, add the REF tag
if (Param->PropertyFlags & CPF_OutParm)
{
EvalModifierText += TEXT("_REF");
ParamPrefix = TEXT("Out_");
}
// if this property requires a specialization, add a comma to the type name so we can print it out easily
if ( TypeText != TEXT("") )
{
TypeText += TCHAR(',');
}
FString ParamName = ParamPrefix + Param->GetName();
EvalParameterText = FString::Printf(TEXT("(%s%s%s)"), *TypeText, *ParamName, *DefaultValueText);
RPCWrappers.Logf(TEXT("%s%s%s%s;%s"), FCString::Spc(8), *EvalBaseText, *EvalModifierText, *EvalParameterText, LINE_TERMINATOR);
// add this property to the parameter list string
if ( ParameterList.Len() )
{
ParameterList += TCHAR(',');
}
{
UDelegateProperty* DelegateProp = Cast< UDelegateProperty >( Param );
if( DelegateProp != NULL )
{
// For delegates, add an explicit conversion to the specific type of delegate before passing it along
const FString FunctionName = DelegateProp->SignatureFunction->GetName().LeftChop( FString( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ).Len() );
const FString CPPDelegateName = FString( TEXT("F" ) ) + FunctionName;
ParamName = FString::Printf( TEXT( "%s(%s)" ), *CPPDelegateName, *ParamName );
}
}
{
UMulticastDelegateProperty* MulticastDelegateProp = Cast< UMulticastDelegateProperty >( Param );
if( MulticastDelegateProp != NULL )
{
// For delegates, add an explicit conversion to the specific type of delegate before passing it along
const FString FunctionName = MulticastDelegateProp->SignatureFunction->GetName().LeftChop( FString( HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX ).Len() );
const FString CPPDelegateName = FString( TEXT("F" ) ) + FunctionName;
ParamName = FString::Printf( TEXT( "%s(%s)" ), *CPPDelegateName, *ParamName );
}
}
UByteProperty* ByteProp = Cast< UByteProperty >( Param );
if ((ByteProp != NULL) && (ByteProp->Enum != NULL))
{
UEnum* Enum = ByteProp->Enum;
FString FullyQualifiedEnumName = Enum->GetName();
if (!Enum->ActualEnumNameInsideNamespace.IsEmpty())
{
FullyQualifiedEnumName += TEXT("::");
FullyQualifiedEnumName += Enum->ActualEnumNameInsideNamespace;
}
// For enums, add an explicit conversion
if (!(ByteProp->PropertyFlags & CPF_OutParm))
{
ParamName = FString::Printf( TEXT( "%s(%s)" ), *FullyQualifiedEnumName, *ParamName );
}
else
{
ParamName = FString::Printf( TEXT( "(TEnumAsByte<%s>&)(%s)" ), *FullyQualifiedEnumName, *ParamName );
}
}
ParameterList += ParamName;
}
RPCWrappers.Logf(TEXT("%sP_FINISH;%s"), FCString::Spc(8), LINE_TERMINATOR);
// Call the validate function if there is one
if ( !( FunctionData.FunctionExportFlags & FUNCEXPORT_CppStatic ) && ( FunctionData.FunctionFlags & FUNC_NetValidate ) )
{
// Call the validate function, and if it fails, return (which will NOT execute the _Implementation function below)
// NOTE - We don't need to set "Result", since RPC calls don't return any values...
RPCWrappers.Logf(TEXT("%sif (!this->%s(%s))%s"), FCString::Spc(8), *FunctionData.CppValidationImplName, *ParameterList, LINE_TERMINATOR);
RPCWrappers.Logf(TEXT("%s{%s"), FCString::Spc(8), LINE_TERMINATOR);
RPCWrappers.Logf(TEXT("%sRPC_ValidateFailed(TEXT(\"%s\"));%s"), FCString::Spc(8+4), *FunctionData.CppValidationImplName, LINE_TERMINATOR);
RPCWrappers.Logf(TEXT("%sreturn;%s"), FCString::Spc(8+4), LINE_TERMINATOR); // If we got here, the validation function check failed
RPCWrappers.Logf(TEXT("%s}%s"), FCString::Spc(8), LINE_TERMINATOR);
}
// write out the return value
RPCWrappers.Log(FCString::Spc(8));
if ( Return != NULL )
{
FString ReturnType, ReturnExtendedType;
ReturnType = Return->GetCPPType(&ReturnExtendedType);
FStringOutputDevice ReplacementText(*ReturnType);
ApplyAlternatePropertyExportText(Return, ReplacementText);
ReturnType = ReplacementText;
RPCWrappers.Logf(TEXT("*(%s%s*)Result="), *ReturnType, *ReturnExtendedType);
}
// export the call to the C++ version
if (FunctionData.FunctionExportFlags & FUNCEXPORT_CppStatic)
{
RPCWrappers.Logf(TEXT("%s::%s(%s);%s"), NameLookupCPP->GetNameCPP(CurrentClass), *FunctionData.CppImplName, *ParameterList, LINE_TERMINATOR);
}
else
{
RPCWrappers.Logf(TEXT("this->%s(%s);%s"), *FunctionData.CppImplName, *ParameterList, LINE_TERMINATOR);
}
}
void FNativeClassHeaderGenerator::ExportNativeFunctions(const TArray<UFunction*>& NativeFunctions)
{
FStringOutputDevice RPCWrappers;
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
//@todo: UCREMOVAL this multipass stuff is only used for diffs, remove it
TArray<UFunction*> SortedNativeFunctions = NativeFunctions;
SortedNativeFunctions.Sort();
// export the C++ stubs
for (int32 Pass = 0; Pass < 2; Pass++)
{
for ( int32 i = NativeFunctions.Num() - 1; i >= 0; i-- )
{
UFunction* Function = Pass ? SortedNativeFunctions[i] : NativeFunctions[i];
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData();
// Custom thunks don't get any C++ stub function generated
if (FunctionData.FunctionExportFlags & FUNCEXPORT_CustomThunk)
continue;
// All functions that are declared in class headers are by definition native
check(Function->FunctionFlags & FUNC_Native);
// Should we emit these to RPC wrappers or just ignore them?
const bool bWillBeProgrammerTyped = FunctionData.CppImplName == Function->GetName();
FStringOutputDevice WrongPass;
FStringOutputDevice& DestinationForDecl = (!bWillBeProgrammerTyped && Pass) ? RPCWrappers : WrongPass;
// Declare validation function if needed
if ( FunctionData.FunctionFlags & FUNC_NetValidate )
{
FString ParameterList;
for ( TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags&(CPF_Parm|CPF_ReturnParm))==CPF_Parm; ++It )
{
UProperty* Property = *It;
if ( ParameterList.Len() )
{
ParameterList += TEXT(", ");
}
FStringOutputDevice PropertyText;
const FString* Dim = GArrayDimensions.Find(Property);
Property->ExportCppDeclaration(PropertyText, EExportedDeclaration::Parameter, Dim ? **Dim : NULL);
ApplyAlternatePropertyExportText(Property, PropertyText);
ParameterList += PropertyText;
}
DestinationForDecl.Logf( TEXT("%sbool %s(%s);\r\n"), FCString::Spc( 4 ), *FunctionData.CppValidationImplName, *ParameterList );
}
ExportNativeFunctionHeader(FunctionData, DestinationForDecl, EExportFunctionType::Function, EExportFunctionHeaderStyle::Declaration);
DestinationForDecl.Log(TEXT(";"));
if (bMultiLineUFUNCTION)
{
DestinationForDecl.Log(TEXT("\r\n"));
}
DestinationForDecl.Log(TEXT("\r\n"));
// if this function was originally declared in a base class, and it isn't a static function,
// only the C++ function header will be exported
if (!Pass || !ShouldExportFunction(Function))
{
continue;
}
// export the script wrappers
RPCWrappers.Logf(TEXT("%sDECLARE_FUNCTION(%s)"), FCString::Spc(4), *FunctionData.UnMarshallAndCallName);
RPCWrappers.Logf(TEXT("%s%s{%s"), LINE_TERMINATOR, FCString::Spc(4), LINE_TERMINATOR);
auto Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference);
ExportFunctionThunk(RPCWrappers, FunctionData, Parameters.Parms, Parameters.Return);
RPCWrappers.Logf(TEXT("%s}%s"), FCString::Spc(4), LINE_TERMINATOR);
}
}
const TCHAR* Suffix = TEXT("_RPC_WRAPPERS");
FString MacroName = FString(NameLookupCPP->GetNameCPP(CurrentClass)) + Suffix;
FString Macroized = Macroize(*MacroName, *RPCWrappers);
GeneratedHeaderText.Log(*Macroized);
InClassMacroCalls.Logf( TEXT("%s%s\r\n"), FCString::Spc(4), *MacroName);
}
/**
* Exports the methods which trigger UnrealScript events and delegates.
*
* @param CallbackFunctions the functions to export
*/
void FNativeClassHeaderGenerator::ExportCallbackFunctions( const TArray<UFunction*>& InCallbackFunctions )
{
FStringOutputDevice RPCWrappers;
FStringOutputDevice RPCWrappersCPP;
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
// Skip interfaces; they don't have any callbacks
if (CurrentClass->HasAnyClassFlags(CLASS_Interface))
{
return;
}
TArray<UFunction*> CallbackFunctions = InCallbackFunctions;
CallbackFunctions.Sort();
for ( int32 CallbackIndex = 0; CallbackIndex < CallbackFunctions.Num(); CallbackIndex++ )
{
UFunction* Function = CallbackFunctions[ CallbackIndex ];
// Never expecting to export delegate functions this way
check( !Function->HasAnyFunctionFlags( FUNC_Delegate ) );
UClass* Class = CurrentClass;
FFunctionData* CompilerInfo = ClassData->FindFunctionData(Function);
check(CompilerInfo);
// cache the TCHAR* for a few strings we'll use a lot here
FString FunctionName = Function->GetName();
const FFuncInfo& FunctionData = CompilerInfo->GetFunctionData();
if (FunctionData.FunctionFlags & FUNC_NetResponse)
{
// Net response functions don't go into the VM
continue;
}
FString ClassName = Class->GetName();
const bool bWillBeProgrammerTyped = Function->GetName() == FunctionData.MarshallAndCallName;
// Emit the declaration if the programmer isn't responsible for declaring this wrapper
if (!bWillBeProgrammerTyped)
{
// export the line that looks like: int32 Main(const FString& Parms)
ExportNativeFunctionHeader(FunctionData, RPCWrappers, EExportFunctionType::Event, EExportFunctionHeaderStyle::Declaration);
RPCWrappers.Log(TEXT(";\r\n"));
if (bMultiLineUFUNCTION)
{
RPCWrappers.Log(TEXT("\r\n"));
}
}
// Emit the thunk implementation
ExportNativeFunctionHeader(FunctionData, RPCWrappersCPP, EExportFunctionType::Event, EExportFunctionHeaderStyle::Definition);
auto Parameters = GetFunctionParmsAndReturn(FunctionData.FunctionReference);
WriteEventFunctionPrologue(RPCWrappersCPP, 4, Parameters, *ClassName, *FunctionName);
{
// Cast away const just in case, because ProcessEvent isn't const
RPCWrappersCPP.Logf(
TEXT("%s%sProcessEvent(FindFunctionChecked(%s_%s),%s);\r\n"),
FCString::Spc(8),
(Function->HasAllFunctionFlags(FUNC_Const)) ? *FString::Printf(TEXT("const_cast<%s*>(this)->"), NameLookupCPP->GetNameCPP(Class)) : TEXT(""),
*API,
*FunctionName,
Parameters.HasParms() ? TEXT("&Parms") : TEXT("NULL")
);
}
WriteEventFunctionEpilogue(RPCWrappersCPP, 4, Parameters, *ClassName, *FunctionName);
}
if (!CurrentClass->HasAnyClassFlags(CLASS_NoExport|CLASS_Temporary))
{
GeneratedPackageCPP.Log(*RPCWrappersCPP);
}
// else drop the implementation on the floor
const TCHAR* Suffix = TEXT("_CALLBACK_WRAPPERS");
FString MacroName = FString(NameLookupCPP->GetNameCPP(CurrentClass)) + Suffix;
FString Macroized = Macroize(*MacroName, *RPCWrappers);
GeneratedHeaderText.Log(*Macroized);
InClassMacroCalls.Logf( TEXT("%s%s\r\n"), FCString::Spc(4), *MacroName);
}
/**
* Determines if the property has alternate export text associated with it and if so replaces the text in PropertyText with the
* alternate version. (for example, structs or properties that specify a native type using export-text). Should be called immediately
* after ExportCppDeclaration()
*
* @param Prop the property that is being exported
* @param PropertyText the string containing the text exported from ExportCppDeclaration
*/
void FNativeClassHeaderGenerator::ApplyAlternatePropertyExportText( UProperty* Prop, FStringOutputDevice& PropertyText )
{
if (bIsExportingForOffsetDeterminationOnly)
{
UDelegateProperty* DelegateProperty = Cast<UDelegateProperty>(Prop);
UMulticastDelegateProperty* MulticastDelegateProperty = Cast<UMulticastDelegateProperty>(Prop);
if (DelegateProperty || MulticastDelegateProperty)
{
FString Original = Prop->GetCPPType();
FString PlaceholderOfSameSizeAndAlignemnt;
if (DelegateProperty)
{
PlaceholderOfSameSizeAndAlignemnt = TEXT("FScriptDelegate");
}
else
{
PlaceholderOfSameSizeAndAlignemnt = TEXT("FMulticastScriptDelegate");
}
PropertyText.ReplaceInline(*Original, *PlaceholderOfSameSizeAndAlignemnt, ESearchCase::CaseSensitive);
}
}
// find the class info for this class
FClassMetaData* ClassData = GScriptHelper->FindClassData(CurrentClass);
// find the compiler token for this property
FTokenData* PropData = ClassData->FindTokenData(Prop);
UObjectProperty* ObjectProp = Cast<UObjectProperty>(Prop);
if ( ObjectProp == NULL )
{
UArrayProperty* ArrayProp = Cast<UArrayProperty>(Prop);
UAttributeProperty* AttributeProp = Cast<UAttributeProperty>(Prop);
if ( ArrayProp != NULL )
{
ObjectProp = Cast<UObjectProperty>(ArrayProp->Inner);
}
else if( AttributeProp != NULL )
{
ObjectProp = Cast<UObjectProperty>(AttributeProp->Inner);
}
}
if ( ObjectProp != NULL && PropData && PropData->Token.ExportInfo.Len() > 0)
{
PropertyText.ReplaceInline(*(FString("class ") + NameLookupCPP->GetNameCPP(ObjectProp->PropertyClass) + TEXT("*")), *PropData->Token.ExportInfo);
return;
}
UStructProperty* StructProp = Cast<UStructProperty>(Prop);
if ( StructProp == NULL )
{
UArrayProperty* ArrayProp = Cast<UArrayProperty>(Prop);
UAttributeProperty* AttributeProp = Cast<UAttributeProperty>(Prop);
if ( ArrayProp != NULL )
{
StructProp = Cast<UStructProperty>(ArrayProp->Inner);
}
else if (AttributeProp != NULL)
{
StructProp = Cast<UStructProperty>(AttributeProp->Inner);
}
}
if ( StructProp )
{
if ( PropData != NULL )
{
FString ExportText;
if ( PropData->Token.ExportInfo.Len() > 0 )
{
ExportText = PropData->Token.ExportInfo;
}
else
{
// we didn't have any export-text associated with the variable, so check if we have
// anything for the struct itself
FStructData* StructData = ClassData->FindStructData(StructProp->Struct);
if ( StructData != NULL && StructData->StructData.ExportInfo.Len() )
{
ExportText = StructData->StructData.ExportInfo;
}
}
if ( ExportText.Len() > 0 )
{
(FString&)PropertyText = PropertyText.Replace(NameLookupCPP->GetNameCPP(StructProp->Struct), *ExportText);
}
}
}
}
/**
* Sorts the list of header files being exported from a package according to their dependency on each other.
*
* @param HeaderDependencyMap a mapping of header filenames to a list of header filenames that must be processed before that one.
* @param SortedHeaderFilenames [out] receives the sorted list of header filenames.
*/
bool FNativeClassHeaderGenerator::SortHeaderDependencyMap(const TMap<const FString*, HeaderDependents>& HeaderDependencyMap, TArray<const FString*>& SortedHeaderFilenames) const
{
SortedHeaderFilenames.Empty(HeaderDependencyMap.Num());
while (SortedHeaderFilenames.Num() < HeaderDependencyMap.Num())
{
bool bAddedSomething = false;
// Find headers with no dependencies and add those to the list.
for (auto It = HeaderDependencyMap.CreateConstIterator(); It; ++It)
{
auto Header = It->Key;
if (SortedHeaderFilenames.Contains(Header))
continue;
bool bHasRemainingDependencies = false;
for (auto It2 = It->Value.CreateConstIterator(); It2; ++It2)
{
if (!SortedHeaderFilenames.Contains(*It2))
{
bHasRemainingDependencies = true;
break;
}
}
if (!bHasRemainingDependencies)
{
// Add it to the list.
SortedHeaderFilenames.AddUnique(Header);
bAddedSomething = true;
}
}
// Circular dependency error?
if (!bAddedSomething)
return false;
}
return true;
}
bool FNativeClassHeaderGenerator::FindInterDependency( TMap<const FString*, HeaderDependents>& HeaderDependencyMap, const FString* Header, const FString*& OutHeader1, const FString*& OutHeader2 )
{
TSet<const FString*> VisitedHeaders;
return FindInterDependencyRecursive( HeaderDependencyMap, Header, VisitedHeaders, OutHeader1, OutHeader2 );
}
/**
* Finds to headers that are dependent on each other.
*
* @param HeaderDependencyMap A map of headers and their dependencies. Each header is represented as an index into a TArray of the actual filename strings.
* @param HeaderIndex A header to scan for any inter-dependency.
* @param VisitedHeaders Must be filled with false values before the first call (must be large enough to be indexed by all headers).
* @param OutHeader1 [out] Receives the first inter-dependent header index.
* @param OutHeader2 [out] Receives the second inter-dependent header index.
* @return true if an inter-dependency was found.
*/
bool FNativeClassHeaderGenerator::FindInterDependencyRecursive( TMap<const FString*, HeaderDependents>& HeaderDependencyMap, const FString* Header, TSet<const FString*>& VisitedHeaders, const FString*& OutHeader1, const FString*& OutHeader2 )
{
VisitedHeaders.Add(Header);
for (auto It = HeaderDependencyMap[Header].CreateConstIterator(); It; ++It)
{
auto DependentHeader = *It;
if (VisitedHeaders.Contains(DependentHeader))
{
OutHeader1 = Header;
OutHeader2 = DependentHeader;
return true;
}
if ( FindInterDependencyRecursive( HeaderDependencyMap, DependentHeader, VisitedHeaders, OutHeader1, OutHeader2 ) )
{
return true;
}
}
return false;
}
/**
* Finds a dependency chain between two class header files.
* Wrapper around FindDependencyChainRecursive().
*
* @param Class A class to scan for a dependency chain between the two headers.
* @param Header1 First class header filename.
* @param Header2 Second class header filename.
* @param DependencyChain [out] Receives dependency chain, if found.
* @return true if a dependency chain was found and filled in.
*/
bool FNativeClassHeaderGenerator::FindDependencyChain( FClass* Class, const FString& Header1, const FString& Header2, TArray<FClass*>& DependencyChain )
{
DependencyChain.Empty();
return FindDependencyChainRecursive( Class, Header1, Header2, false, DependencyChain );
}
/**
* Finds a dependency chain between two class header files.
*
* @param Class A class to scan for a dependency chain between the two headers.
* @param Header1 First class header filename.
* @param Header2 Second class header filename.
* @param bChainStarted Whether Header1 has been found and we've started to fill in DependencyChain. Must be false to begin with.
* @param DependencyChain [out] Receives dependency chain, if found. Must be empty before the call.
* @return true if a dependency chain was found and filled in.
*/
bool FNativeClassHeaderGenerator::FindDependencyChainRecursive( FClass* Class, const FString& Header1, const FString& Header2, bool bChainStarted, TArray<FClass*>& DependencyChain )
{
bool bIsExportClass = Class->HasAnyClassFlags(CLASS_Native) && !Class->HasAnyClassFlags(CLASS_NoExport|CLASS_Intrinsic|CLASS_Temporary);
if ( bIsExportClass )
{
auto HeaderFilename = GClassHeaderFilenameMap.FindRef(Class);
bChainStarted = bChainStarted || HeaderFilename == Header1;
if ( bChainStarted )
{
DependencyChain.Add( Class );
if ( HeaderFilename == Header2 )
{
return true;
}
}
}
FClass* SuperClass = Class->GetSuperClass();
if ( SuperClass && SuperClass->GetOuter() == Class->GetOuter() && FindDependencyChainRecursive( SuperClass, Header1, Header2, bChainStarted, DependencyChain ) )
{
return true;
}
for (FClass* InterfaceClass : Class->GetInterfaceTypes())
{
if ( InterfaceClass->GetOuter() == Class->GetOuter() && FindDependencyChainRecursive( InterfaceClass, Header1, Header2, bChainStarted, DependencyChain ) )
{
return true;
}
}
if ( bIsExportClass && bChainStarted )
{
DependencyChain.RemoveAtSwap( DependencyChain.Num() - 1 );
}
return false;
}
// Constructor.
FNativeClassHeaderGenerator::FNativeClassHeaderGenerator( UPackage* InPackage, FClasses& AllClasses, bool InAllowSaveExportedHeaders, bool bInUseRelativePaths )
: CurrentClass (NULL)
, API (FPackageName::GetShortName(InPackage).ToUpper())
, Package (InPackage)
, bIsExportingForOffsetDeterminationOnly(false)
, bAllowSaveExportedHeaders (InAllowSaveExportedHeaders)
, bFailIfGeneratedCodeChanges (false)
, bUseRelativePaths (bInUseRelativePaths)
{
const FString PackageName = FPackageName::GetShortName(Package);
GeneratedCPPFilenameBase = PackageName + TEXT(".generated");
GeneratedProtoFilenameBase = PackageName + TEXT(".generated");
GeneratedMCPFilenameBase = PackageName + TEXT(".generated");
bFailIfGeneratedCodeChanges = FParse::Param(FCommandLine::Get(), TEXT("FailIfGeneratedCodeChanges"));
// Tag native classes in this package for export.
Classes = AllClasses.GetClassesInPackage();
TMap<FName,UClass*> ClassNameMap;
for ( int32 ClassIndex = 0; ClassIndex < Classes.Num(); ClassIndex++ )
{
ClassNameMap.Add(Classes[ClassIndex]->GetFName(), Classes[ClassIndex]);
}
class UnsortedHeaderFilenamesType
{
public:
FString* AddUnique(const FString& Str)
{
for (auto& Ptr : Data)
{
if (*Ptr == Str)
return &*Ptr;
}
return Data[Data.Add(MakeUnique<FString>(Str))].Get();
}
TArray<TUniquePtr<FString>> Data;
};
UnsortedHeaderFilenamesType UnsortedHeaderFilenames;
TArray<const FString*> HeaderFilenames; // These reference the strings stored in UnsortedHeaderFilenames
bool bCircularDependencyDetected = false;
{
TMap<const FString*, HeaderDependents> HeaderDependencyMap;
TArray<FClass*> ClassesInPackage = AllClasses.GetClassesInPackage(Package);
for (FClass* Cls : ClassesInPackage)
{
auto ClsHeaderFilename = GClassHeaderFilenameMap.FindRef(Cls);
bool bIsExportClass = Cls->HasAnyClassFlags(CLASS_Native) && !Cls->HasAnyClassFlags(CLASS_NoExport|CLASS_Intrinsic|CLASS_Temporary);
if ( bIsExportClass )
{
auto* HeaderFilename = UnsortedHeaderFilenames.AddUnique(ClsHeaderFilename);
auto& Dependencies = HeaderDependencyMap.FindOrAdd(HeaderFilename);
// Add the super class' header as a dependency if it's different.
UClass* SuperClass = Cls->GetSuperClass();
bool bIsDependentExportClass = SuperClass && SuperClass->HasAnyClassFlags(CLASS_Native) && !SuperClass->HasAnyClassFlags(CLASS_NoExport|CLASS_Intrinsic|CLASS_Temporary);
if ( bIsDependentExportClass && SuperClass->GetOuter() == Cls->GetOuter() )
{
auto SuperClassHeaderFilename = GClassHeaderFilenameMap.FindRef(SuperClass);
if ( SuperClassHeaderFilename != ClsHeaderFilename )
{
auto* DependentHeaderFilename = UnsortedHeaderFilenames.AddUnique(SuperClassHeaderFilename);
Dependencies.AddUnique(DependentHeaderFilename);
}
}
// Add base interface headers as dependencies, if they're different.
for (TArray<FImplementedInterface>::TIterator It(Cls->Interfaces); It; ++It)
{
UClass* InterfaceClass = It->Class;
bool bIsDependentExportClass = InterfaceClass->HasAnyClassFlags(CLASS_Native) && !InterfaceClass->HasAnyClassFlags(CLASS_NoExport|CLASS_Intrinsic|CLASS_Temporary);
if ( bIsDependentExportClass && InterfaceClass->GetOuter() == Cls->GetOuter() )
{
auto InterfaceClassHeaderFilename = GClassHeaderFilenameMap.FindRef(InterfaceClass);
if ( InterfaceClassHeaderFilename != ClsHeaderFilename )
{
auto* DependentHeaderFilename = UnsortedHeaderFilenames.AddUnique(InterfaceClassHeaderFilename);
Dependencies.AddUnique(DependentHeaderFilename);
}
}
}
}
else if (Cls->HasAnyClassFlags(CLASS_Temporary))
{
// Struct only headers can generate header groups too so make sure their added too.
auto* HeaderFilename = UnsortedHeaderFilenames.AddUnique(ClsHeaderFilename);
HeaderDependencyMap.FindOrAdd(HeaderFilename);
}
}
bCircularDependencyDetected = !SortHeaderDependencyMap(HeaderDependencyMap, HeaderFilenames);
if (bCircularDependencyDetected)
{
// Find one circular path (though there may be multiple).
for (auto HeaderIt = HeaderDependencyMap.CreateConstIterator(); HeaderIt; ++HeaderIt)
{
const FString* Header = HeaderIt->Key;
const FString* ClassHeaderFilename1;
const FString* ClassHeaderFilename2;
if (!FindInterDependency(HeaderDependencyMap, Header, ClassHeaderFilename1, ClassHeaderFilename2))
continue;
TArray<FClass*> DependencyChain1;
for (FClass* Class : ClassesInPackage)
{
FindDependencyChain( Class, *ClassHeaderFilename1, *ClassHeaderFilename2, DependencyChain1 );
if (DependencyChain1.Num())
break;
}
TArray<FClass*> DependencyChain2;
for (FClass* Class : ClassesInPackage)
{
FindDependencyChain( Class, *ClassHeaderFilename2, *ClassHeaderFilename1, DependencyChain2 );
if (DependencyChain2.Num())
break;
}
if (!DependencyChain1.Num() || !DependencyChain2.Num())
continue;
FString DependencyChainString1;
for (FClass* Dependency : DependencyChain1)
{
if (!DependencyChainString1.IsEmpty())
{
DependencyChainString1 += TEXT(" -> ");
}
DependencyChainString1 += Dependency->GetName();
}
FString DependencyChainString2;
for (FClass* Dependency : DependencyChain2)
{
if (!DependencyChainString1.IsEmpty())
{
DependencyChainString2 += TEXT(" -> ");
}
DependencyChainString2 += Dependency->GetName();
}
UE_LOG(LogCompile, Error, TEXT("Header interdependency: %s <-> %s (%s and %s)."),
*(PackageName + *ClassHeaderFilename1 + TEXT("Classes.h")),
*(PackageName + *ClassHeaderFilename2 + TEXT("Classes.h")),
*DependencyChainString1,
*DependencyChainString2 );
break;
}
UE_LOG(LogCompile, Error, TEXT("Interdependent headers detected - aborting!") );
}
}
bool bHasNamesForExport = false;
TempHeaderPaths.Empty();
PackageHeaderPaths.Empty();
// Reset header generation output strings
GeneratedPackageCPP .Empty();
GeneratedProtoText .Empty();
GeneratedMCPText .Empty();
CrossModuleGeneratedFunctionDeclarations .Empty();
UniqueCrossModuleReferences .Empty();
GeneratedFunctionDeclarations .Empty();
GeneratedFunctionBodyTextSplit .Empty();
ListOfPublicClassesUObjectHeaderModuleIncludes.Empty();
for (auto HeaderFilename : HeaderFilenames)
{
if (bCircularDependencyDetected)
break;
ClassHeaderFilename = *HeaderFilename;
FString PkgDir;
FString GeneratedIncludeDirectory;
if (MakeCommandlet_FindPackageLocation(*PackageName, PkgDir, GeneratedIncludeDirectory) == false)
{
UE_LOG(LogCompile, Error, TEXT("Failed to find path for package %s"), *PackageName);
}
FString ClassesHeaderName = PackageName + ClassHeaderFilename + TEXT("Classes.h");
ClassesHeaderPath = GeneratedIncludeDirectory / ClassesHeaderName;
int32 ClassCount = 0;
for (UClass* Class : Classes)
{
if( Class->GetOuter()==Package && Class->HasAnyClassFlags(CLASS_Native|CLASS_Temporary) && GClassHeaderFilenameMap.FindRef(Class) == ClassHeaderFilename )
{
if (GClassStrippedHeaderTextMap.Contains(Class) && !Class->HasAnyClassFlags(CLASS_NoExport))
{
ClassCount++;
// Skip temporary classes autogenerated in struct-only headers.
if (!Class->HasAnyClassFlags(CLASS_Temporary))
{
Class->UnMark(OBJECTMARK_TagImp);
Class->Mark(OBJECTMARK_TagExp);
}
}
}
else
{
Class->UnMark(EObjectMark(OBJECTMARK_TagImp | OBJECTMARK_TagExp));
}
}
if( ClassCount == 0 )
{
continue;
}
ClassesHeaders.Add(ClassesHeaderName);
CurrentClass = NULL;
ListOfPublicClassesUObjectHeaderGroupIncludes.Empty();
ListOfAllUObjectHeaderIncludes.Empty();
OriginalHeader.Empty();
PreHeaderText.Empty();
// Load the original header file into memory
FFileHelper::LoadFileToString(OriginalHeader,*ClassesHeaderPath);
UE_LOG(LogCompile, Log, TEXT("Autogenerating C++ header: %s"), *ClassHeaderFilename );
// Write the classes and enums header prefixes.
PreHeaderText.Logf(
TEXT("// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.\r\n")
TEXT("/*===========================================================================\r\n")
TEXT(" C++ class boilerplate exported from UnrealHeaderTool.\r\n")
TEXT(" This is automatically generated by the tools.\r\n")
TEXT(" DO NOT modify this manually! Edit the corresponding .h files instead!\r\n")
TEXT("===========================================================================*/\r\n")
TEXT("#pragma once\r\n")
TEXT("\r\n")
);
// if a global auto-include file exists, generate a line to have that file included
FString GlobalAutoIncludeFilename = PackageName + ClassHeaderFilename + TEXT("GlobalIncludes.h");
const FString StandardHeaderFileLocation = PkgDir / TEXT("Public");
if ( IFileManager::Get().FileSize(*(StandardHeaderFileLocation / GlobalAutoIncludeFilename)) > 0 )
{
PreHeaderText.Logf(TEXT("#include \"%s\"\r\n\r\n"), *GlobalAutoIncludeFilename);
}
FString HeaderAPI = API;
if( ClassHeaderFilename.Len() > 0 )
{
HeaderAPI = HeaderAPI + TEXT("_") + ClassHeaderFilename.ToUpper();
}
// Export an include line for each header
for (UClass* Class : Classes)
{
if ( GClassHeaderFilenameMap.FindRef(Class) == ClassHeaderFilename && Class->GetOuter() == Package )
{
ExportClassHeader(Class);
}
}
ListOfPublicClassesUObjectHeaderGroupIncludes.Logf( TEXT("\r\n") );
// build the full header file out of its pieces
const FString FullClassesHeader = FString::Printf(
TEXT("%s\r\n%s"),
*PreHeaderText,
*ListOfPublicClassesUObjectHeaderGroupIncludes
);
// Save the classes header if it has changed.
SaveHeaderIfChanged(*ClassesHeaderPath,*FullClassesHeader);
}
if (HeaderFilenames.Num() == 0)
{
// If no headers are generated, check if there's any no export classes that need to have the inl file generated.
for (UClass* Class : Classes)
{
if ( Class->GetOuter() == Package && !Class->HasAnyClassFlags(CLASS_Intrinsic|CLASS_Temporary) )
{
ExportClassHeader(Class);
}
}
}
// now export the names for the functions in this package
// notice we always export this file (as opposed to only exporting if we have any marked names)
// because there would be no way to know when the file was created otherwise
if (!bCircularDependencyDetected)
{
// Export .generated.cpp
ExportGeneratedCPP();
}
ExportGeneratedProto();
ExportGeneratedMCP();
// Export all changed headers from their temp files to the .h files
ExportUpdatedHeaders(PackageName);
// Delete stale *.generated.h files
DeleteUnusedGeneratedHeaders();
}
void FNativeClassHeaderGenerator::DeleteUnusedGeneratedHeaders()
{
TArray<FString> AllIntermediateFolders;
for (const auto& PackageHeader : PackageHeaderPaths)
{
const FString IntermediatePath = FPaths::GetPath(PackageHeader);
if (AllIntermediateFolders.Contains(IntermediatePath))
continue;
AllIntermediateFolders.Add( IntermediatePath );
TArray<FString> AllHeaders;
IFileManager::Get().FindFiles( AllHeaders, *(IntermediatePath / TEXT("*.generated.h")), true, false );
for (const auto& Header : AllHeaders)
{
const FString HeaderPath = IntermediatePath / Header;
if (PackageHeaderPaths.Contains(HeaderPath))
continue;
// Check intrinsic classes. Get the class name from file name by removing .generated.h.
const FString HeaderFilename = FPaths::GetBaseFilename(HeaderPath);
const int32 GeneratedIndex = HeaderFilename.Find(TEXT(".generated"), ESearchCase::IgnoreCase, ESearchDir::FromEnd);
const FString ClassName = HeaderFilename.Mid(0, GeneratedIndex);
UClass* IntrinsicClass = FindObject<UClass>(ANY_PACKAGE, *ClassName);
if (!IntrinsicClass || !IntrinsicClass->HasAnyClassFlags(CLASS_Intrinsic))
{
IFileManager::Get().Delete(*HeaderPath);
}
}
}
}
/**
* Dirty hack global variable to allow different result codes passed through
* exceptions. Needs to be fixed in future versions of UHT.
*/
ECompilationResult::Type GCompilationResult = ECompilationResult::OtherCompilationError;
bool FNativeClassHeaderGenerator::SaveHeaderIfChanged(const TCHAR* HeaderPath, const TCHAR* InNewHeaderContents)
{
if ( !bAllowSaveExportedHeaders )
{
// Return false indicating that the header did not need updating
return false;
}
FString Tabified = Tabify(InNewHeaderContents);
const TCHAR* NewHeaderContents = *Tabified;
static bool bTestedCmdLine = false;
if (!bTestedCmdLine)
{
bTestedCmdLine = true;
if( FParse::Param( FCommandLine::Get(), TEXT("WRITEREF") ) )
{
bWriteContents = true;
UE_LOG(LogCompile, Log, TEXT("********************************* Writing reference generated code to ReferenceGeneratedCode."));
UE_LOG(LogCompile, Log, TEXT("********************************* Deleting all files in ReferenceGeneratedCode."));
IFileManager::Get().DeleteDirectory(*(FString(FPaths::GameSavedDir()) / TEXT("ReferenceGeneratedCode/")), false, true);
IFileManager::Get().MakeDirectory(*(FString(FPaths::GameSavedDir()) / TEXT("ReferenceGeneratedCode/")));
}
else if( FParse::Param( FCommandLine::Get(), TEXT("VERIFYREF") ) )
{
bVerifyContents = true;
UE_LOG(LogCompile, Log, TEXT("********************************* Writing generated code to VerifyGeneratedCode and comparing to ReferenceGeneratedCode"));
UE_LOG(LogCompile, Log, TEXT("********************************* Deleting all files in VerifyGeneratedCode."));
IFileManager::Get().DeleteDirectory(*(FString(FPaths::GameSavedDir()) / TEXT("VerifyGeneratedCode/")), false, true);
IFileManager::Get().MakeDirectory(*(FString(FPaths::GameSavedDir()) / TEXT("VerifyGeneratedCode/")));
}
}
if (bWriteContents || bVerifyContents)
{
FString Ref = FString(FPaths::GameSavedDir()) / TEXT("ReferenceGeneratedCode") / FPaths::GetCleanFilename(HeaderPath);
FString Verify = FString(FPaths::GameSavedDir()) / TEXT("VerifyGeneratedCode") / FPaths::GetCleanFilename(HeaderPath);
if (bWriteContents)
{
int32 i;
for (i = 0 ;i < 10; i++)
{
if (FFileHelper::SaveStringToFile(NewHeaderContents, *Ref))
{
break;
}
FPlatformProcess::Sleep(1.0f); // I don't know why this fails after we delete the directory
}
check(i<10);
}
else
{
int32 i;
for (i = 0 ;i < 10; i++)
{
if (FFileHelper::SaveStringToFile(NewHeaderContents, *Verify))
{
break;
}
FPlatformProcess::Sleep(1.0f); // I don't know why this fails after we delete the directory
}
check(i<10);
FString RefHeader;
FString Message;
if (!FFileHelper::LoadFileToString(RefHeader, *Ref))
{
Message = FString::Printf(TEXT("********************************* %s appears to be a new generated file."), *FPaths::GetCleanFilename(HeaderPath));
}
else
{
if (FCString::Strcmp(NewHeaderContents, *RefHeader) != 0)
{
Message = FString::Printf(TEXT("********************************* %s has changed."), *FPaths::GetCleanFilename(HeaderPath));
}
}
if (Message.Len())
{
UE_LOG(LogCompile, Log, TEXT("%s"), *Message);
ChangeMessages.AddUnique(Message);
}
}
}
FString OriginalHeaderLocal;
FFileHelper::LoadFileToString(OriginalHeaderLocal, HeaderPath);
const bool bHasChanged = OriginalHeaderLocal.Len() == 0 || FCString::Strcmp(*OriginalHeaderLocal, NewHeaderContents);
if (bHasChanged)
{
if (bFailIfGeneratedCodeChanges)
{
FString ConflictPath = FString(HeaderPath) + TEXT(".conflict");
FFileHelper::SaveStringToFile(NewHeaderContents, *ConflictPath);
GCompilationResult = ECompilationResult::FailedDueToHeaderChange;
FError::Throwf(TEXT("ERROR: '%s': Changes to generated code are not allowed - conflicts written to '%s'"), HeaderPath, *ConflictPath);
}
// save the updated version to a tmp file so that the user can see what will be changing
const FString TmpHeaderFilename = GenerateTempHeaderName( HeaderPath, false );
// delete any existing temp file
IFileManager::Get().Delete( *TmpHeaderFilename, false, true );
if ( !FFileHelper::SaveStringToFile(NewHeaderContents, *TmpHeaderFilename) )
{
UE_LOG(LogCompile, Warning, TEXT("Failed to save header export preview: '%s'"), *TmpHeaderFilename);
}
TempHeaderPaths.Add(TmpHeaderFilename);
}
// Remember this header filename to be able to check for any old (unused) headers later.
PackageHeaderPaths.Add( FString(HeaderPath).Replace( TEXT( "\\" ), TEXT( "/" ) ) );
return bHasChanged;
}
/**
* Create a temp header file name from the header name
*
* @param CurrentFilename The filename off of which the current filename will be generated
* @param bReverseOperation Get the header from the temp file name instead
*
* @return The generated string
*/
FString FNativeClassHeaderGenerator::GenerateTempHeaderName( FString CurrentFilename, bool bReverseOperation )
{
return bReverseOperation
? CurrentFilename.Replace(TEXT(".tmp"), TEXT(""))
: CurrentFilename + TEXT(".tmp");
}
/**
* Exports the temp header files into the .h files, then deletes the temp files.
*
* @param PackageName Name of the package being saved
*/
void FNativeClassHeaderGenerator::ExportUpdatedHeaders(FString PackageName)
{
for (auto It = TempHeaderPaths.CreateConstIterator(); It; ++It)
{
const FString& TmpFilename = *It;
FString Filename = GenerateTempHeaderName( TmpFilename, true );
if (!IFileManager::Get().Move(*Filename, *TmpFilename, true, true))
{
UE_LOG(LogCompile, Error, TEXT("%s"), *FString::Printf(TEXT("Error exporting %s: couldn't write file '%s'"),*PackageName,*Filename));
}
else
{
UE_LOG(LogCompile, Log, TEXT("Exported updated C++ header: %s"), *Filename);
}
}
}
/**
* Exports protobuffer definitions from boilerplate that was generated for a package.
* They are exported to a file using the name <PackageName>.generated.proto
*/
void FNativeClassHeaderGenerator::ExportGeneratedProto()
{
if (GeneratedProtoText.Len())
{
UE_LOG(LogCompile, Log, TEXT("Autogenerating boilerplate proto: %s.proto"), *GeneratedProtoFilenameBase );
FStringOutputDevice ProtoPreamble;
ProtoPreamble.Logf(
TEXT("// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.") LINE_TERMINATOR
TEXT("/*===========================================================================") LINE_TERMINATOR
TEXT(" Purpose: The file defines our Google Protocol Buffers which are used in over ") LINE_TERMINATOR
TEXT(" the wire messages between servers as well as between clients and servers.") LINE_TERMINATOR
TEXT(" This is automatically generated by UnrealHeaderTool.") LINE_TERMINATOR
TEXT(" DO NOT modify this manually! Edit the corresponding .h files instead!") LINE_TERMINATOR
TEXT("===========================================================================*/") LINE_TERMINATOR
LINE_TERMINATOR
TEXT("// We care more about speed than code size") LINE_TERMINATOR
TEXT("option optimize_for = SPEED;") LINE_TERMINATOR
TEXT("// We don't use the service generation functionality") LINE_TERMINATOR
TEXT("option cc_generic_services = false;") LINE_TERMINATOR
LINE_TERMINATOR
);
FString PkgName = FPackageName::GetShortName(Package);
FString PkgDir;
FString GeneratedIncludeDirectory;
if (MakeCommandlet_FindPackageLocation(*PkgName, PkgDir, GeneratedIncludeDirectory) == false)
{
UE_LOG(LogCompile, Error, TEXT("Failed to find path for package %s"), *PkgName);
}
FString HeaderPath = GeneratedIncludeDirectory / GeneratedProtoFilenameBase + TEXT(".proto");
SaveHeaderIfChanged(*HeaderPath, *(ProtoPreamble + GeneratedProtoText));
}
}
/**
* Exports MCPbuffer definitions from boilerplate that was generated for a package.
* They are exported to a file using the name <PackageName>.generated.MCP
*/
void FNativeClassHeaderGenerator::ExportGeneratedMCP()
{
if (GeneratedMCPText.Len())
{
UE_LOG(LogCompile, Log, TEXT("Autogenerating boilerplate MCP: %s.java"), *GeneratedMCPFilenameBase );
FStringOutputDevice MCPPreamble;
MCPPreamble.Logf(
TEXT("// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.") LINE_TERMINATOR
TEXT("/*===========================================================================") LINE_TERMINATOR
TEXT(" Purpose: The file defines java heaers for MCP rpc messages. ") LINE_TERMINATOR
TEXT(" DO NOT modify this manually! Edit the corresponding .h files instead!") LINE_TERMINATOR
TEXT("===========================================================================*/") LINE_TERMINATOR
);
FString PkgName = FPackageName::GetShortName(Package);
FString PkgDir;
FString GeneratedIncludeDirectory;
if (MakeCommandlet_FindPackageLocation(*PkgName, PkgDir, GeneratedIncludeDirectory) == false)
{
UE_LOG(LogCompile, Error, TEXT("Failed to find path for package %s"), *PkgName);
}
FString HeaderPath = GeneratedIncludeDirectory / GeneratedMCPFilenameBase + TEXT(".java");
SaveHeaderIfChanged(*HeaderPath, *(MCPPreamble + GeneratedMCPText));
}
}
/**
* Exports C++ definitions for boilerplate that was generated for a package.
* They are exported to a file using the name <PackageName>.generated.cpp
* @param ReferencedNames list of function names to export.
*/
void FNativeClassHeaderGenerator::ExportGeneratedCPP()
{
FStringOutputDevice GeneratedCPPPreamble;
FStringOutputDevice GeneratedCPPClassesIncludes;
FStringOutputDevice GeneratedCPPEpilogue;
FStringOutputDevice GeneratedCPPText;
TArray<FStringOutputDevice> GeneratedCPPFiles;
UE_LOG(LogCompile, Log, TEXT("Autogenerating boilerplate cpp: %s.cpp"), *GeneratedCPPFilenameBase );
GeneratedCPPPreamble.Logf(
TEXT("// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.") LINE_TERMINATOR
TEXT("/*===========================================================================") LINE_TERMINATOR
TEXT(" Boilerplate C++ definitions for a single module.") LINE_TERMINATOR
TEXT(" This is automatically generated by UnrealHeaderTool.") LINE_TERMINATOR
TEXT(" DO NOT modify this manually! Edit the corresponding .h files instead!") LINE_TERMINATOR
TEXT("===========================================================================*/") LINE_TERMINATOR
LINE_TERMINATOR
);
FString ModulePCHInclude;
const auto* ModuleInfo = GPackageToManifestModuleMap.FindChecked(Package);
if (ModuleInfo->PCH.Len())
{
ModulePCHInclude = FString::Printf(TEXT("#include \"%s\"") LINE_TERMINATOR, *ModuleInfo->PCH);
}
// Write out the ordered class dependencies into a single header that we can easily include
FString DepHeaderPathname = ModuleInfo->GeneratedCPPFilenameBase + TEXT(".dep.h");
SaveHeaderIfChanged(*DepHeaderPathname, *(GeneratedCPPPreamble + ListOfPublicClassesUObjectHeaderModuleIncludes));
// Write out our include to the .dep.h file
GeneratedCPPClassesIncludes.Logf(TEXT("#include \"%s\"") LINE_TERMINATOR, *FPaths::GetCleanFilename(DepHeaderPathname));
GeneratedCPPText.Log(*GeneratedPackageCPP);
// Add all names marked for export to a list (for sorting)
TArray<FString> Names;
for (TSet<FName>::TIterator It(ReferencedNames); It; ++It)
{
Names.Add(It->ToString());
}
// Autogenerate names (alphabetically sorted).
Names.Sort();
for( int32 NameIndex=0; NameIndex<Names.Num(); NameIndex++ )
{
GeneratedCPPText.Logf( TEXT("FName %s_%s = FName(TEXT(\"%s\"));") LINE_TERMINATOR, *API, *Names[NameIndex], *Names[NameIndex] );
}
GeneratedCPPEpilogue.Logf(
LINE_TERMINATOR
);
FString PkgName = FPackageName::GetShortName(Package);
FString PkgDir;
if (GeneratedFunctionDeclarations.Len() || CrossModuleGeneratedFunctionDeclarations.Len())
{
ExportGeneratedPackageInitCode(Package);
}
TArray<FString> NumberedHeaderNames;
// Generate each of the .generated.cpp files
for( int32 FileIdx=0;FileIdx<GeneratedFunctionBodyTextSplit.Num();FileIdx++ )
{
FStringOutputDevice FileText;
// The first file has all of the GeneratedCPPText, only the functions are split.
if( FileIdx == 0)
{
FileText = GeneratedCPPText;
}
if (GeneratedFunctionDeclarations.Len() || CrossModuleGeneratedFunctionDeclarations.Len())
{
FileText.Logf(TEXT("#if USE_COMPILED_IN_NATIVES\r\n"));
if (CrossModuleGeneratedFunctionDeclarations.Len())
{
FileText.Logf(TEXT("// Cross Module References\r\n"));
FileText.Log(CrossModuleGeneratedFunctionDeclarations);
FileText.Logf(TEXT("\r\n"));
}
FileText.Log(GeneratedFunctionDeclarations);
FileText.Log(GeneratedFunctionBodyTextSplit[FileIdx]);
FileText.Logf(TEXT("#endif\r\n"));
}
FString HeaderPath = ModuleInfo->GeneratedCPPFilenameBase + (GeneratedFunctionBodyTextSplit.Num() > 1 ? *FString::Printf(TEXT(".%d.cpp"), FileIdx + 1) : TEXT(".cpp"));
SaveHeaderIfChanged(*HeaderPath, *(GeneratedCPPPreamble + ModulePCHInclude + GeneratedCPPClassesIncludes + FileText + GeneratedCPPEpilogue));
if (GeneratedFunctionBodyTextSplit.Num() > 1)
{
NumberedHeaderNames.Add(FPaths::GetCleanFilename(HeaderPath));
}
}
if (GeneratedFunctionBodyTextSplit.Num() > 1)
{
FStringOutputDevice FileText;
for (int32 i=0; i < NumberedHeaderNames.Num(); ++i)
{
FileText.Logf(TEXT("#include \"%s\"") LINE_TERMINATOR, *NumberedHeaderNames[i]);
}
FString HeaderPath = ModuleInfo->GeneratedCPPFilenameBase + TEXT(".cpp");
SaveHeaderIfChanged(*HeaderPath, *(GeneratedCPPPreamble + ModulePCHInclude + FileText + GeneratedCPPEpilogue));
}
if( FParse::Param( FCommandLine::Get(), TEXT("GenerateConstructors") ) && AllConstructors.Len())
{
const FString PrivateLoc = PkgDir / PkgName / TEXT("Private");
FString ConstructorPath = PrivateLoc / PkgName + TEXT("Constructors.cpp");
FString ConstructorsWithIfdef(TEXT("// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.\r\n"));
ConstructorsWithIfdef += *Tabify(*AllConstructors);
SaveHeaderIfChanged(*ConstructorPath,*ConstructorsWithIfdef);
}
}
ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename)
{
check(GIsUCCMakeStandaloneHeaderGenerator);
ECompilationResult::Type Result = ECompilationResult::Succeeded;
if ( !FParse::Param( FCommandLine::Get(), TEXT("IgnoreWarnings")) )
{
GWarn->TreatWarningsAsErrors = true;
}
FString ModuleInfoPath = FPaths::GetPath(ModuleInfoFilename);
// Load the manifest file, giving a list of all modules to be processed, pre-sorted by dependency ordering
try
{
GManifest = FManifest::LoadFromFile(ModuleInfoFilename);
}
catch (const TCHAR* Ex)
{
UE_LOG(LogCompile, Error, TEXT("Failed to load manifest file '%s': %s"), *ModuleInfoFilename, Ex);
return GCompilationResult;
}
NameLookupCPP = new FNameLookupCPP();
FCompilerMetadataManager* ScriptHelper = new FCompilerMetadataManager();
GScriptHelper = ScriptHelper;
// Load classes for editing.
int32 NumFailures = 0;
// Three passes. 1) Public 'Classes' headers (legacy) 2) Public headers 3) Private headers
for( int32 PassIndex = 0; PassIndex < 3; ++PassIndex )
{
enum
{
PublicClassesHeaders,
PublicHeaders,
PrivateHeaders,
} CurrentlyProcessing;
{
switch( PassIndex )
{
case 0:
default:
CurrentlyProcessing = PublicClassesHeaders;
break;
case 1:
CurrentlyProcessing = PublicHeaders;
break;
case 2:
CurrentlyProcessing = PrivateHeaders;
break;
};
}
for (const auto& Module : GManifest.Modules)
{
if (Result != ECompilationResult::Succeeded)
{
break;
}
// We'll make an ordered list of all UObject headers we care about.
// @todo uht: Ideally 'dependson' would not be allowed from public -> private, or NOT at all for new style headers
const TArray<FString>& UObjectHeaders =
(CurrentlyProcessing == PublicClassesHeaders) ? Module.PublicUObjectClassesHeaders :
(CurrentlyProcessing == PublicHeaders ) ? Module.PublicUObjectHeaders :
Module.PrivateUObjectHeaders;
if (!UObjectHeaders.Num())
continue;
UPackage* Package = Cast<UPackage>( StaticFindObjectFast( UPackage::StaticClass(), NULL, FName(*Module.LongPackageName), false, false ) );
if( Package == NULL )
{
Package = CreatePackage( NULL, *Module.LongPackageName );
}
GPackageToManifestModuleMap.Add(Package, &Module);
// Set some package flags for indicating that this package contains script
// NOTE: We do this even if we didn't have to create the package, because CoreUObject is compiled into UnrealHeaderTool and we still
// want to make sure our flags get set
Package->PackageFlags |= PKG_ContainsScript;
Package->PackageFlags &= ~(PKG_ClientOptional|PKG_ServerSideOnly);
Package->PackageFlags |= PKG_Compiling;
for (const FString& Filename : UObjectHeaders)
{
// Best faith effort at a useful line number for errors occurring during this initial pre-parsing
int32 ClassDeclLine = -1;
#if !PLATFORM_EXCEPTIONS_DISABLED
try
#endif
{
// Import class.
const FString ClassName = FPaths::GetBaseFilename(Filename);
const FString FullModulePath = FPaths::ConvertRelativePathToFull(ModuleInfoPath, Filename);
FString HeaderFile;
if (!FFileHelper::LoadFileToString(HeaderFile, *FullModulePath))
FError::Throwf(TEXT( "UnrealHeaderTool was unable to load source file '%s'"), *FullModulePath);
UClass* ResultClass = GenerateCodeForHeader(Package, *ClassName, RF_Public|RF_Standalone, *HeaderFile, ClassDeclLine);
GClassSourceFileMap.Add(ResultClass, Filename);
if( CurrentlyProcessing == PublicClassesHeaders )
{
GPublicClassSet.Add(ResultClass);
}
// Save metadata for the class path, both for it's include path and relative to the module base directory
if(FullModulePath.StartsWith(Module.BaseDirectory))
{
// Get the path relative to the module directory
const TCHAR *ModuleRelativePath = *FullModulePath + Module.BaseDirectory.Len();
GClassModuleRelativePathMap.Add(ResultClass, ModuleRelativePath);
// Add the include path
const TCHAR *IncludePath = ModuleRelativePath;
if(*IncludePath == '/')
{
IncludePath++;
}
while(*IncludePath != 0 && *IncludePath != '/')
{
IncludePath++;
}
if(*IncludePath == '/' && *(IncludePath + 1) != 0)
{
GClassIncludePathMap.Add(ResultClass, IncludePath + 1);
}
}
}
#if !PLATFORM_EXCEPTIONS_DISABLED
catch( TCHAR* ErrorMsg )
{
TGuardValue<ELogTimes::Type> DisableLogTimes(GPrintLogTimes, ELogTimes::None);
FString Prefix;
if (ClassDeclLine != -1)
{
const FString AbsFilename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*Filename);
Prefix = FString::Printf(TEXT("%s(%i): "), *AbsFilename, ClassDeclLine);
}
FString FormattedErrorMessage = FString::Printf(TEXT("%sError: %s\r\n"), *Prefix, ErrorMsg);
Result = GCompilationResult;
UE_LOG(LogCompile, Log, TEXT("%s"), *FormattedErrorMessage);
GWarn->Log(ELogVerbosity::Error, FormattedErrorMessage);
++NumFailures;
}
#endif
}
if(Result == ECompilationResult::Succeeded && NumFailures != 0)
{
Result = ECompilationResult::OtherCompilationError;
}
}
}
// Save generated headers
if ( Result == ECompilationResult::Succeeded )
{
// Verify that all script declared superclasses exist.
for (const UClass* ScriptClass : TObjectRange<UClass>())
{
const UClass* ScriptSuperClass = ScriptClass->GetSuperClass();
if (ScriptSuperClass && !ScriptSuperClass->HasAnyClassFlags(CLASS_Intrinsic) && GClassStrippedHeaderTextMap.Contains(ScriptClass) && !GClassStrippedHeaderTextMap.Contains(ScriptSuperClass))
{
UE_LOG(LogCompile, Error, TEXT("Superclass %s of class %s not found"), *ScriptSuperClass->GetName(), *ScriptClass->GetName());
Result = ECompilationResult::OtherCompilationError;
++NumFailures;
}
}
if (Result == ECompilationResult::Succeeded)
{
for (const auto& Module : GManifest.Modules)
{
if (UPackage* Package = Cast<UPackage>( StaticFindObjectFast( UPackage::StaticClass(), NULL, FName(*Module.LongPackageName), false, false ) ))
{
Result = FHeaderParser::ParseAllHeadersInside(GWarn, Package, Module.SaveExportedHeaders, GManifest.UseRelativePaths);
if (Result != ECompilationResult::Succeeded)
{
++NumFailures;
break;
}
}
}
}
}
// Avoid TArray slack for meta data.
GScriptHelper->Shrink();
if( bWriteContents )
{
UE_LOG(LogCompile, Log, TEXT("********************************* Wrote reference generated code to ReferenceGeneratedCode."));
}
else if( bVerifyContents )
{
UE_LOG(LogCompile, Log, TEXT("********************************* Wrote generated code to VerifyGeneratedCode and compared to ReferenceGeneratedCode"));
for (FString& Msg : ChangeMessages)
{
UE_LOG(LogCompile, Error, TEXT("%s"), *Msg);
}
TArray<FString> RefFileNames;
IFileManager::Get().FindFiles( RefFileNames, *(FString(FPaths::GameSavedDir()) / TEXT("ReferenceGeneratedCode/*.*")), true, false );
TArray<FString> VerFileNames;
IFileManager::Get().FindFiles( VerFileNames, *(FString(FPaths::GameSavedDir()) / TEXT("VerifyGeneratedCode/*.*")), true, false );
if (RefFileNames.Num() != VerFileNames.Num())
{
UE_LOG(LogCompile, Error, TEXT("Number of generated files mismatch ref=%d, ver=%d"), RefFileNames.Num(), VerFileNames.Num());
}
}
TheFlagAudit.WriteResults();
delete NameLookupCPP;
NameLookupCPP = NULL;
delete ScriptHelper;
ScriptHelper = NULL;
GIsRequestingExit = true;
if(Result == ECompilationResult::Succeeded && NumFailures > 0)
{
return ECompilationResult::OtherCompilationError;
}
return Result;
}
UClass* GenerateCodeForHeader
(
UObject* InParent,
const TCHAR* Name,
EObjectFlags Flags,
const TCHAR* Buffer,
int32& OutClassDeclLine
)
{
// Support for headers without UClasses.
bool bNonClassHeader = false;
const TCHAR* InBuffer = Buffer;
// Import the script text.
TArray<FName> DependentOn;
// is the parsed class name an interface?
bool bClassIsAnInterface = false;
// Parse the header to extract the information needed
FStringOutputDevice ClassHeaderTextStrippedOfCppText;
FString ClassName;
FString BaseClassName;
FHeaderParser::SimplifiedClassParse(Buffer, /*out*/ bClassIsAnInterface, /*out*/ DependentOn, /*out*/ ClassName, /*out*/ BaseClassName, /*out*/ OutClassDeclLine, ClassHeaderTextStrippedOfCppText);
// In case no UClass is defined, generate the default temporary UClass.
if (ClassName.IsEmpty())
{
ClassName = FString::Printf(TEXT("U%s"), Name);
BaseClassName = TEXT("UObject");
bNonClassHeader = true;
}
FString ClassNameStripped = GetClassNameWithPrefixRemoved( *ClassName );
// Ensure the base class has any valid prefix and exists as a valid class. Checking for the 'correct' prefix will occur during compilation
FString BaseClassNameStripped;
if (!BaseClassName.IsEmpty())
{
BaseClassNameStripped = GetClassNameWithPrefixRemoved(BaseClassName);
if( !FHeaderParser::ClassNameHasValidPrefix(BaseClassName, BaseClassNameStripped) )
FError::Throwf(TEXT("No prefix or invalid identifier for base class %s.\nClass names must match Unreal prefix specifications (e.g., \"UObject\" or \"AActor\")"), *BaseClassName );
if (DependentOn.ContainsByPredicate([&](const FName& Dependency){ FString DependencyStr = Dependency.ToString(); return !DependencyStr.Contains(TEXT(".generated.h")) && FPaths::GetBaseFilename(DependencyStr) == ClassNameStripped; }))
FError::Throwf(TEXT("Class '%s' contains a dependency (#include or DependsOn) to itself"), *ClassName);
}
//UE_LOG(LogCompile, Log, TEXT("Class: %s extends %s"),*ClassName,*BaseClassName);
// Handle failure and non-class headers.
if (BaseClassName.IsEmpty() && (ClassName != TEXT("UObject")))
{
FError::Throwf(TEXT("Class '%s' must inherit UObject or a UObject-derived class"), *ClassName );
}
if (ClassName == BaseClassName)
{
FError::Throwf(TEXT("Class '%s' cannot inherit from itself"), *ClassName );
}
// In case the file system and the class disagree on the case of the
// class name replace the fname with the one from the script class file
// This is needed because not all source control systems respect the
// original filename's case
FName ClassNameReplace(*ClassName,FNAME_Replace_Not_Safe_For_Threading);
// All classes must start with a valid unreal prefix
if (!FHeaderParser::ClassNameHasValidPrefix(ClassName, Name) || !FHeaderParser::ClassNameHasValidPrefix(ClassNameReplace.ToString(), Name))
{
FError::Throwf(TEXT("Invalid class name '%s'. The class name must match the name of the class header file it is contained in ('%s'), with an appropriate prefix added (A for Actors, U for other classes)"), *ClassNameReplace.ToString(), Name);
}
// Use stripped class name for processing and replace as we did above
FName ClassNameStrippedReplace(*ClassNameStripped, FNAME_Replace_Not_Safe_For_Threading);
UClass* ResultClass = FindObject<UClass>(InParent, *ClassNameStripped);
// if we aren't generating headers, then we shouldn't set misaligned object, since it won't get cleared
const static bool bVerboseOutput = FParse::Param(FCommandLine::Get(), TEXT("VERBOSE"));
// Gracefully update an existing hardcoded class.
// Note: Most 'native' classes will not have RF_Native set at this point, only classes in Core will go down this path
if ((ResultClass != NULL) && ResultClass->HasAnyFlags(RF_Native) )
{
UClass* SuperClass = ResultClass->GetSuperClass();
if ((SuperClass != NULL) && (SuperClass->GetName() != BaseClassNameStripped))
{
// the code that handles the DependsOn list in the script compiler doesn't work correctly if we manually add the Object class to a class's DependsOn list
// if Object is also the class's parent. The only way this can happen (since specifying a parent class in a DependsOn statement is a compiler error) is
// in this block of code, so just handle that here rather than trying to make the script compiler handle this gracefully
if (BaseClassNameStripped != TEXT("Object"))
{
// we're changing the parent of a native class, which may result in the
// child class being parsed before the new parent class, so add the new
// parent class to this class's DependsOn() array to guarantee that it
// will be parsed before this class
DependentOn.AddUnique(*BaseClassNameStripped);
}
// if the new parent class is an existing native class, attempt to change the parent for this class to the new class
UClass* NewSuperClass = FindObject<UClass>(ANY_PACKAGE, *BaseClassNameStripped);
if (NewSuperClass != NULL)
{
check(0); // we don't support changing base clases anymore
}
}
}
else
{
// detect if the same class name is used in multiple packages
if (ResultClass == NULL)
{
UClass* ConflictingClass = FindObject<UClass>(ANY_PACKAGE, *ClassNameStripped, true);
if (ConflictingClass != NULL)
{
UE_LOG(LogCompile, Warning, TEXT("Duplicate class name: %s also exists in file %s"), *ClassName, *ConflictingClass->GetOutermost()->GetName());
}
}
// Create new class.
ResultClass = new( InParent, *ClassNameStripped, Flags ) UClass( FPostConstructInitializeProperties(),NULL );
GClassHeaderNameWithNoPathMap.Add(ResultClass, ClassNameStripped);
// add CLASS_Interface flag if the class is an interface
// NOTE: at this pre-parsing/importing stage, we cannot know if our super class is an interface or not,
// we leave the validation to the main header parser
if (bClassIsAnInterface)
{
ResultClass->ClassFlags |= CLASS_Interface;
}
// In case there actually was no class, mark it as UHT temporary.
if (bNonClassHeader)
{
const FString DummyClassName = FHeaderParser::GenerateTemporaryClassName(*ResultClass->GetName());
ResultClass->ClassFlags |= CLASS_Temporary | CLASS_Native;
ResultClass->Rename(*DummyClassName, NULL, REN_DontCreateRedirectors);
}
// Find or forward-declare base class.
ResultClass->SetSuperStruct( FindObject<UClass>( InParent, *BaseClassNameStripped ) );
if (ResultClass->GetSuperStruct() == NULL)
{
ResultClass->SetSuperStruct( FindObject<UClass>( ANY_PACKAGE, *BaseClassNameStripped ) );
}
if (ResultClass->GetSuperStruct() == NULL)
{
// don't know its parent class yet
ResultClass->SetSuperStruct( new(InParent, *BaseClassNameStripped) UClass(FPostConstructInitializeProperties(),NULL) );
}
if (ResultClass->GetSuperStruct() != NULL)
{
ResultClass->ClassCastFlags |= ResultClass->GetSuperClass()->ClassCastFlags;
}
if ( bVerboseOutput )
{
UE_LOG(LogCompile, Log, TEXT("Imported: %s"), *ResultClass->GetFullName() );
}
}
if (bVerboseOutput)
{
for (const auto& Dependency : DependentOn)
{
UE_LOG(LogCompile, Log, TEXT("\tAdding %s as a dependency"), *Dependency.ToString());
}
}
// Set class info.
GClassStrippedHeaderTextMap.Emplace(ResultClass, MoveTemp(ClassHeaderTextStrippedOfCppText));
GClassDependentOnMap .Emplace(ResultClass, MoveTemp(DependentOn));
return ResultClass;
}