Source Code Generator

pull/1/head
Srkmn 2 years ago
parent aa37b5442e
commit fcc07fcdbe
  1. 964
      Private/SourceCodeGenerator.cpp
  2. 101
      Public/SourceCodeGenerator.h
  3. 40
      SourceCodeGenerator.Build.cs

@ -0,0 +1,964 @@
#include "SourceCodeGenerator.h"
#include "Features/IModularFeatures.h"
#include "Misc/FileHelper.h"
#include "UObject/CoreNet.h"
#include "UObject/Interface.h"
#define LOCTEXT_NAMESPACE "FSourceCodeGeneratorModule"
void FSourceCodeGeneratorModule::StartupModule()
{
IModularFeatures::Get().RegisterModularFeature(TEXT("ScriptGenerator"), this);
}
void FSourceCodeGeneratorModule::ShutdownModule()
{
IModularFeatures::Get().UnregisterModularFeature(TEXT("ScriptGenerator"), this);
}
FString FSourceCodeGeneratorModule::GetGeneratedCodeModuleName() const
{
return TEXT("UnrealCSharp");
}
bool FSourceCodeGeneratorModule::SupportsTarget(const FString& TargetName) const
{
const auto SettingFile = FPaths::Combine(
FPaths::ConvertRelativePathToFull(FPaths::GetPath(FPaths::GetProjectFilePath())),
TEXT("Config/DefaultUnrealCSharpEditorSetting.ini"));
auto bEnableExport = false;
if (FPaths::FileExists(SettingFile))
{
GConfig->GetBool(
TEXT("/Script/UnrealCSharpCore.UnrealCSharpEditorSetting"),
TEXT("bEnableExport"),
bEnableExport,
SettingFile);
}
return bEnableExport;
}
bool FSourceCodeGeneratorModule::ShouldExportClassesForModule(const FString& ModuleName,
EBuildModuleType::Type ModuleType,
const FString& ModuleGeneratedIncludeDirectory) const
{
return ExportModules.Contains(ModuleName) &&
(ModuleType == EBuildModuleType::EngineRuntime || ModuleType == EBuildModuleType::GameRuntime);
}
void FSourceCodeGeneratorModule::Initialize(const FString& RootLocalPath, const FString& RootBuildPath,
const FString& OutputDirectory, const FString& IncludeBase)
{
OutputPath = OutputDirectory;
const auto ProjectPath = FPaths::ConvertRelativePathToFull(FPaths::GetPath(FPaths::GetProjectFilePath()));
const auto ProjectSourcePath = FPaths::Combine(ProjectPath, TEXT("Source/"));
const auto ProjectPluginsPath = FPaths::Combine(ProjectPath, TEXT("Plugins/"));
const auto EngineSourcePath = FPaths::EngineSourceDir();
const auto EnginePluginsPath = FPaths::EnginePluginsDir();
const auto ConfigFilePath = FPaths::Combine(ProjectPath, TEXT("Config/DefaultUnrealCSharpEditorSetting.ini"));
const auto ConfigSection = FString(TEXT("/Script/UnrealCSharpCore.UnrealCSharpEditorSetting"));
GetModules(ProjectSourcePath, Project);
GetModules(ProjectSourcePath, HeaderPath);
GetModules(ProjectPluginsPath, HeaderPath);
GetPlugins(ProjectPluginsPath, HeaderPath);
GetModules(FPaths::Combine(EngineSourcePath, TEXT("Developer/")), HeaderPath);
GetModules(FPaths::Combine(EngineSourcePath, TEXT("Editor/")), HeaderPath);
GetModules(FPaths::Combine(EngineSourcePath, TEXT("Programs/")), HeaderPath);
GetModules(FPaths::Combine(EngineSourcePath, TEXT("Runtime/")), HeaderPath);
GetModules(EnginePluginsPath, HeaderPath);
GetPlugins(EnginePluginsPath, HeaderPath);
if (FPaths::FileExists(ConfigFilePath))
{
GConfig->GetArray(*ConfigSection, TEXT("+ExportModule"), ExportModules, ConfigFilePath);
}
}
void FSourceCodeGeneratorModule::ExportClass(UClass* Class, const FString& SourceHeaderFilename,
const FString& GeneratedHeaderFilename, bool bHasChanged)
{
if (CanExportClass(Class))
{
TSet<UClass*> DependencyClasses = {Class};
FBigStringBuilder StringBuilder;
FBigStringBuilder BodyBuilder;
StringBuilder.Append(TEXT(
"#pragma once\r\n\r\n"
"PRAGMA_DISABLE_DEPRECATION_WARNINGS\r\n\r\n"));
BodyBuilder.Appendf(TEXT(
"namespace\r\n"
"{\r\n"
"\tstruct FRegister%s\r\n"
"\t{\r\n"
"\t\tFRegister%s()\r\n"
"\t\t{\r\n"
"\t\t\tTBindingClassBuilder<%s>(NAMESPACE_BINDING)\r\n"
),
*Class->GetName(),
*Class->GetName(),
*(Class->GetPrefixCPP() + Class->GetName())
);
for (TFieldIterator<FProperty> Property(Class, EFieldIterationFlags::None); Property; ++Property)
{
if (CanExportProperty(*Property))
{
GetDependencyClasses(*Property, DependencyClasses);
ExportProperty(BodyBuilder, Class, *Property);
}
}
for (TFieldIterator<UFunction> Function(Class, EFieldIterationFlags::None); Function; ++Function)
{
if (CanExportFunction(*Function))
{
for (TFieldIterator<FProperty> Property(*Function); Property; ++Property)
{
GetDependencyClasses(*Property, DependencyClasses);
}
ExportFunction(BodyBuilder, Class, *Function);
}
}
for (const auto DependencyClass : DependencyClasses)
{
StringBuilder.Append(GetInclude(DependencyClass));
}
StringBuilder.Append(TEXT("\r\n"));
BodyBuilder.RemoveSuffix(2);
BodyBuilder.Appendf(TEXT(
";\r\n"
"\t\t}\r\n"
"\t};\r\n"
"\r\n"
"\t[[maybe_unused]] FRegister%s Register%s;\r\n"
"}\r\n\r\n"
),
*Class->GetName(),
*Class->GetName()
);
StringBuilder.Append(BodyBuilder);
StringBuilder.Append(TEXT("PRAGMA_ENABLE_DEPRECATION_WARNINGS\r\n"));
const auto FilePath = FPaths::Combine(OutputPath, *Class->GetName() + BindingSuffix);
SaveIfChanged(FilePath, StringBuilder.ToString());
ExportClasses.AddUnique(Class);
}
}
void FSourceCodeGeneratorModule::FinishExport()
{
TMap<UPackage*, TArray<FString>> Packages;
for (const auto ExportClass : ExportClasses)
{
Packages.FindOrAdd(ExportClass->GetPackage()).Add(*ExportClass->GetName());
}
for (auto& [Key, Value] : Packages)
{
Value.Sort();
FBigStringBuilder StringBuilder;
StringBuilder.Append(TEXT("#pragma once\r\n\r\n"));
for (const auto& Name : Value)
{
StringBuilder.Append(GenerateInclude(*Name + BindingSuffix));
}
const auto FilePath = FPaths::Combine(
OutputPath, *FPaths::GetCleanFilename(Key->GetName()) + HeaderSuffix);
SaveIfChanged(FilePath, StringBuilder.ToString());
}
}
FString FSourceCodeGeneratorModule::GetGeneratorName() const
{
return TEXT("UnrealCSharp Source Code Generator");
}
bool FSourceCodeGeneratorModule::CanExportClass(const UClass* Class)
{
return !Class->HasAnyClassFlags(CLASS_Interface) &&
IsClassTypeSupported(Class);
}
bool FSourceCodeGeneratorModule::CanExportFunction(const UFunction* Function)
{
if (const auto OwnerClass = Function->GetOwnerClass())
{
if (!OwnerClass->HasAnyClassFlags(CLASS_RequiredAPI) &&
!Function->HasAnyFunctionFlags(FUNC_RequiredAPI))
{
return false;
}
}
if (Function->HasAnyFunctionFlags(FUNC_Delegate | FUNC_MulticastDelegate))
{
return false;
}
if (!Function->HasAnyFunctionFlags(FUNC_Public))
{
return false;
}
if (Function->HasAnyFunctionFlags(FUNC_EditorOnly))
{
return false;
}
if (Function->HasMetaData(FName(TEXT("CustomThunk"))))
{
return false;
}
if (const auto ReturnProperty = Function->GetReturnProperty())
{
if (ReturnProperty->IsA<FMapProperty>())
{
return false;
}
}
for (TFieldIterator<FProperty> Property(Function); Property; ++Property)
{
if (!IsPropertyTypeSupported(*Property))
{
return false;
}
}
return true;
}
bool FSourceCodeGeneratorModule::CanExportProperty(const FProperty* Property)
{
return !Property->HasAnyPropertyFlags(CPF_EditorOnly) &&
Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPublic) &&
IsPropertyTypeSupported(Property);
}
void FSourceCodeGeneratorModule::ExportFunction(FBigStringBuilder& StringBuilder,
const UClass* Class,
const UFunction* Function)
{
StringBuilder.Appendf(TEXT(
"\t\t\t\t.Function(\"%s\", BINDING_OVERLOAD(%s, &%s::%s"
),
*Function->GetName(),
*GetFunctionSignature(Class, Function),
*(Class->GetPrefixCPP() + Class->GetName()),
*Function->GetName());
auto bHasParameters = false;
for (TFieldIterator<FProperty> Property(Function); Property; ++Property)
{
if (Property->HasAnyPropertyFlags(CPF_Parm) && !Property->HasAnyPropertyFlags(CPF_ReturnParm))
{
bHasParameters = true;
break;
}
}
if (bHasParameters)
{
StringBuilder.Appendf(TEXT(
", %s"
),
*GetFunctionParamName(Function));
}
if (!Function->HasAnyFunctionFlags(FUNC_Static))
{
StringBuilder.Append(TEXT(", EFunctionInteract::Virtual"));
}
if (bHasParameters)
{
StringBuilder.Append(GetFunctionDefaultValue(Function));
}
StringBuilder.Append(TEXT("))\r\n"));
}
void FSourceCodeGeneratorModule::ExportProperty(FBigStringBuilder& StringBuilder,
const UClass* Class,
const FProperty* Property)
{
StringBuilder.Appendf(TEXT(
"\t\t\t\t.Property(\"%s\", BINDING_PROPERTY(&%s::%s))\r\n"
),
*Property->GetNameCPP(),
*(Class->GetPrefixCPP() + Class->GetName()),
*Property->GetNameCPP());
}
bool FSourceCodeGeneratorModule::IsClassTypeSupported(const UClass* Class)
{
return !Class->HasAnyClassFlags(CLASS_Deprecated) &&
(Class == UObject::StaticClass() ||
Class == UClass::StaticClass() ||
Class->HasAnyClassFlags(CLASS_RequiredAPI | CLASS_MinimalAPI));
}
bool FSourceCodeGeneratorModule::IsPropertyTypeSupported(const FProperty* Property)
{
if (const auto ArrayProperty = CastField<FArrayProperty>(Property))
{
return IsPropertyTypeSupported(ArrayProperty->Inner);
}
if (const auto SetProperty = CastField<FSetProperty>(Property))
{
return IsPropertyTypeSupported(SetProperty->ElementProp);
}
if (const auto MapProperty = CastField<FMapProperty>(Property))
{
return IsPropertyTypeSupported(MapProperty->KeyProp) &&
IsPropertyTypeSupported(MapProperty->ValueProp);
}
if (const auto ObjectProperty = CastField<FObjectProperty>(Property))
{
if (!IsClassTypeSupported(ObjectProperty->PropertyClass))
{
return false;
}
}
if (const auto ObjectPtrProperty = CastField<FObjectPtrProperty>(Property))
{
if (!IsClassTypeSupported(ObjectPtrProperty->PropertyClass))
{
return false;
}
}
if (const auto InterfaceProperty = CastField<FInterfaceProperty>(Property))
{
if (InterfaceProperty->InterfaceClass == UInterface::StaticClass())
{
return false;
}
}
if (Property->IsA<FEnumProperty>())
{
return false;
}
if (Property->IsA<FByteProperty>())
{
return false;
}
if (Property->IsA<FDelegateProperty>())
{
return false;
}
if (Property->IsA<FMulticastDelegateProperty>())
{
return false;
}
if (Property->IsA<FFieldPathProperty>())
{
return false;
}
if (const auto StructProperty = CastField<FStructProperty>(Property))
{
if (!StructProperty->HasNoOpConstructor())
{
return false;
}
}
auto bIsBitField = false;
if (const auto BoolProperty = CastField<FBoolProperty>(Property))
{
if (!BoolProperty->IsNativeBool())
{
bIsBitField = true;
}
}
return !bIsBitField && !(Property->ArrayDim > 1);
}
FString FSourceCodeGeneratorModule::GetReturnPropertySignature(const FProperty* Property)
{
if (Property == nullptr)
{
return TEXT("void");
}
FString String;
auto bIsConst = false;
if (Property->HasAnyPropertyFlags(CPF_ConstParm) || Property->HasMetaData(FName(TEXT("NativeConst"))))
{
String.Append(TEXT("const "));
bIsConst = true;
}
GetPropertySignature(Property, String);
if (Property->IsA<FStrProperty>())
{
if (bIsConst &&
!Property->HasAnyPropertyFlags(CPF_ConstParm))
{
String.Append(TEXT("&"));
}
}
else if (Property->HasAnyPropertyFlags(CPF_ReferenceParm) &&
Property->HasAnyPropertyFlags(CPF_ConstParm))
{
String.Append(TEXT("&"));
}
return String;
}
FString FSourceCodeGeneratorModule::GetParamPropertySignature(const FProperty* Property)
{
if (Property == nullptr)
{
return TEXT("void");
}
FString String;
auto bIsConst = false;
if (Property->HasAnyPropertyFlags(CPF_ConstParm) || Property->HasMetaData(FName(TEXT("NativeConst"))))
{
String.Append(TEXT("const "));
bIsConst = true;
}
GetPropertySignature(Property, String);
if (Property->IsA<FStrProperty>())
{
if (bIsConst &&
!Property->HasAnyPropertyFlags(CPF_ConstParm) ||
Property->HasAnyPropertyFlags(CPF_OutParm))
{
String.Append(TEXT("&"));
}
}
else if (Property->HasAnyPropertyFlags(CPF_OutParm) ||
Property->HasAnyPropertyFlags(CPF_ReferenceParm))
{
String.Append(TEXT("&"));
}
return String;
}
FString FSourceCodeGeneratorModule::GetFunctionSignature(const UClass* Class, const UFunction* Function)
{
FString String;
String.Append(GetReturnPropertySignature(Function->GetReturnProperty()));
if (Function->HasAnyFunctionFlags(FUNC_Static))
{
String.Append(TEXT("(*)"));
}
else
{
String.Appendf(TEXT(
"(%s::*)"
),
*(Class->GetPrefixCPP() + Class->GetName())
);
}
String.Append(TEXT("("));
auto bHasParameters = false;
for (TFieldIterator<FProperty> Property(Function); Property; ++Property)
{
if (!Property->HasAnyPropertyFlags(CPF_ReturnParm))
{
String.Append(GetParamPropertySignature(*Property));
String.Append(TEXT(", "));
bHasParameters = true;
}
}
if (bHasParameters)
{
String.LeftInline(String.Len() - 2);
}
String.Append(TEXT(")"));
if (Function->HasAnyFunctionFlags(FUNC_Const))
{
String.Append(TEXT("const"));
}
return String;
}
FString FSourceCodeGeneratorModule::GetFunctionParamName(const UFunction* Function)
{
FSmallStringBuilder Builder;
Builder.Append(TEXT("TArray<FString>{"));
auto bIsConst = false;
for (TFieldIterator<FProperty> Property(Function); Property; ++Property)
{
if (!Property->HasAnyPropertyFlags(CPF_ReturnParm))
{
Builder.Appendf(TEXT(
"\"%s\", "
),
*Property->GetName()
);
bIsConst = true;
}
}
if (bIsConst)
{
Builder.RemoveSuffix(2);
}
Builder.Append(TEXT("}"));
return Builder.ToString();
}
FString FSourceCodeGeneratorModule::GetFunctionDefaultValue(const UFunction* Function)
{
if (!Function->HasAnyFunctionFlags(FUNC_BlueprintCallable))
{
return FString();
}
FSmallStringBuilder Builder;
const auto MetaDataMap = UMetaData::GetMapForObject(Function);
for (TFieldIterator<FProperty> Property(Function); Property; ++Property)
{
if (Property->HasAnyPropertyFlags(CPF_Parm) && !Property->HasAnyPropertyFlags(CPF_ReturnParm))
{
if (FString Value; FindDefaultValue(MetaDataMap, *Property, Value))
{
Builder.Append(TEXT(", "));
if (Value == TEXT("NULL"))
{
Builder.Append(TEXT("nullptr"));
}
else
{
Builder.Append(Value);
}
}
}
}
return Builder.ToString();
}
bool FSourceCodeGeneratorModule::FindDefaultValue(const TMap<FName, FString>* MetaDataMap, const FProperty* Param,
FString& OutString)
{
if (MetaDataMap == nullptr || Param == nullptr)
{
return false;
}
const FName ParamName(*Param->GetName());
const FName CppKey(*(FString(TEXT("CPP_Default_")) + ParamName.ToString()));
if (!MetaDataMap->Contains(CppKey))
{
return false;
}
if (Param->IsA<FObjectProperty>()
|| Param->IsA<FClassPtrProperty>()
|| Param->IsA<FObjectPtrProperty>())
{
OutString = TEXT("nullptr");
}
else
{
FString MetaData;
if (const auto CppDefaultValue = MetaDataMap->Find(CppKey))
{
if (!CppDefaultValue->IsEmpty())
{
MetaData = *CppDefaultValue;
}
}
if (const auto StructProperty = CastField<FStructProperty>(Param))
{
if (StructProperty->Struct == TBaseStructure<FVector>::Get())
{
TArray<FString> Value;
MetaData.ParseIntoArray(Value, TEXT(","));
if (Value.Num() == 3)
{
OutString = FString::Printf(TEXT(
"FVector(%lf, %lf, %lf)"
),
TCString<TCHAR>::Atod(*Value[0]),
TCString<TCHAR>::Atod(*Value[1]),
TCString<TCHAR>::Atod(*Value[2])
);
}
}
else if (StructProperty->Struct == TBaseStructure<FVector2D>::Get())
{
if (FVector2D Value; Value.InitFromString(MetaData))
{
OutString = FString::Printf(TEXT(
"FVector2D(%lf, %lf)"
),
Value.X,
Value.Y
);
}
}
else if (StructProperty->Struct == TBaseStructure<FRotator>::Get())
{
TArray<FString> Value;
MetaData.ParseIntoArray(Value, TEXT(","));
if (Value.Num() == 3)
{
OutString = FString::Printf(TEXT(
"FRotator(%lf, %lf, %lf)"
),
TCString<TCHAR>::Atod(*Value[0]),
TCString<TCHAR>::Atod(*Value[1]),
TCString<TCHAR>::Atod(*Value[2])
);
}
}
else if (StructProperty->Struct == TBaseStructure<FLinearColor>::Get())
{
if (FLinearColor Value; Value.InitFromString(MetaData))
{
OutString = FString::Printf(TEXT(
"FLinearColor(%ff, %ff, %ff, %ff)"
),
Value.R,
Value.G,
Value.B,
Value.A
);
}
}
if (OutString.IsEmpty())
{
OutString = FString::Printf(TEXT(
"%s%s()"
),
StructProperty->Struct->GetPrefixCPP(),
*StructProperty->Struct->GetName()
);
}
}
else if (Param->IsA<FStrProperty>())
{
OutString = FString::Printf(TEXT(
"FString(\"%s\")"
),
*MetaData);
}
else if (Param->IsA<FNameProperty>())
{
OutString = FString::Printf(TEXT(
"TEXT(\"%s\")"
),
*MetaData);
}
else if (Param->IsA<FTextProperty>())
{
static auto InvText = FString(TEXT("INVTEXT"));
if (MetaData.Contains(InvText))
{
MetaData.MidInline(InvText.Len() + 2, MetaData.Len() - InvText.Len() - 4);
}
OutString = FString::Printf(TEXT(
"INVTEXT(\"%s\")"
),
*MetaData
);
}
else
{
OutString = MetaData;
}
}
return !OutString.IsEmpty();
}
void FSourceCodeGeneratorModule::GetPropertySignature(const FProperty* Property, FString& String)
{
if (const auto ArrayProperty = CastField<FArrayProperty>(Property))
{
String.Appendf(TEXT(
"TArray<%s>"
),
*ArrayProperty->Inner->GetCPPType()
);
}
else if (const auto SetProperty = CastField<FSetProperty>(Property))
{
String.Appendf(TEXT(
"TSet<%s>"
),
*SetProperty->ElementProp->GetCPPType()
);
}
else if (const auto MapProperty = CastField<FMapProperty>(Property))
{
String.Appendf(TEXT(
"TMap<%s, %s>"
),
*MapProperty->KeyProp->GetCPPType(),
*MapProperty->ValueProp->GetCPPType()
);
}
else if (const auto InterfaceProperty = CastField<FInterfaceProperty>(Property))
{
String.Appendf(TEXT(
"TScriptInterface<I%s>"
),
*InterfaceProperty->InterfaceClass->GetName()
);
}
else
{
String.Append(Property->GetCPPType());
}
}
void FSourceCodeGeneratorModule::GetDependencyClasses(const FProperty* Property, TSet<UClass*>& DependencyClasses)
{
if (const auto ClassProperty = CastField<FClassProperty>(Property))
{
if (ClassProperty->MetaClass != nullptr)
{
DependencyClasses.Add(ClassProperty->MetaClass);
}
}
else if (const auto ObjectProperty = CastField<FObjectProperty>(Property))
{
if (ObjectProperty->PropertyClass != nullptr)
{
DependencyClasses.Add(ObjectProperty->PropertyClass);
}
}
else if (const auto ObjectPtrProperty = CastField<FObjectPtrProperty>(Property))
{
if (ObjectPtrProperty->PropertyClass != nullptr)
{
DependencyClasses.Add(ObjectPtrProperty->PropertyClass);
}
}
else if (const auto SoftClassProperty = CastField<FSoftClassProperty>(Property))
{
if (SoftClassProperty->MetaClass != nullptr)
{
DependencyClasses.Add(SoftClassProperty->MetaClass);
}
}
else if (const auto SoftObjectProperty = CastField<FSoftObjectProperty>(Property))
{
if (SoftObjectProperty->PropertyClass != nullptr)
{
DependencyClasses.Add(SoftObjectProperty->PropertyClass);
}
}
else if (const auto ArrayProperty = CastField<FArrayProperty>(Property))
{
if (ArrayProperty->Inner != nullptr)
{
GetDependencyClasses(ArrayProperty->Inner, DependencyClasses);
}
}
}
void FSourceCodeGeneratorModule::SaveIfChanged(const FString& FileName, const FString& String)
{
if (FPaths::FileExists(FileName))
{
if (FString Result; FFileHelper::LoadFileToString(Result, *FileName) && Result == String)
{
return;
}
}
FFileHelper::SaveStringToFile(String, *FileName, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
}
FString FSourceCodeGeneratorModule::GetHeaderFile(UClass* Class)
{
FString ModuleRelativePath = Class->GetMetaData(TEXT("ModuleRelativePath"));
if (ModuleRelativePath.IsEmpty())
{
if (Class == UInterface::StaticClass())
{
ModuleRelativePath = TEXT("Public/UObject/Interface.h");
}
else if (Class == UPackageMap::StaticClass())
{
ModuleRelativePath = TEXT("Public/UObject/CoreNet.h");
}
}
return FPaths::Combine(HeaderPath.FindRef(FPaths::GetCleanFilename(Class->GetPackage()->GetName())),
ModuleRelativePath);
}
FString FSourceCodeGeneratorModule::GenerateInclude(const FString& FileName)
{
return FString::Printf(TEXT(
"#include \"%s\"\n"
),
*FileName
);
}
FString FSourceCodeGeneratorModule::GetInclude(UClass* Class)
{
return GenerateInclude(GetHeaderFile(Class));
}
void FSourceCodeGeneratorModule::GetPlugins(const FString& InPathName, TMap<FString, FString>& Plugins)
{
const auto Suffix = FString(TEXT("*.uplugin"));
if (auto& FileManager = IFileManager::Get(); FileManager.DirectoryExists(*InPathName))
{
TArray<FString> FileNames;
FileManager.FindFilesRecursive(FileNames, *InPathName, *Suffix, true, false);
for (const auto& FileName : FileNames)
{
Plugins.Add(
FPaths::GetCleanFilename(FileName).Left(
FPaths::GetCleanFilename(FileName).Len() - Suffix.Len() + 1),
FPaths::ConvertRelativePathToFull(FPaths::GetPath(FileName)));
}
}
}
void FSourceCodeGeneratorModule::GetModules(const FString& InPathName, TMap<FString, FString>& Modules)
{
const auto Suffix = FString(TEXT("*.Build.cs"));
if (auto& FileManager = IFileManager::Get(); FileManager.DirectoryExists(*InPathName))
{
TArray<FString> FileNames;
FileManager.FindFilesRecursive(FileNames, *InPathName, *Suffix, true, false);
for (const auto& FileName : FileNames)
{
Modules.Add(
FPaths::GetCleanFilename(FileName).Left(
FPaths::GetCleanFilename(FileName).Len() - Suffix.Len() + 1),
FPaths::ConvertRelativePathToFull(FPaths::GetPath(FileName)));
}
}
}
void FSourceCodeGeneratorModule::GetModules(const FString& InPathName, TSet<FString>& Modules)
{
const auto Suffix = FString(TEXT("*.Build.cs"));
if (auto& FileManager = IFileManager::Get(); FileManager.DirectoryExists(*InPathName))
{
TArray<FString> FileNames;
FileManager.FindFilesRecursive(FileNames, *InPathName, *Suffix, true, false);
for (const auto& FileName : FileNames)
{
Modules.Add(
FPaths::GetCleanFilename(FileName).Left(
FPaths::GetCleanFilename(FileName).Len() - Suffix.Len() + 1));
}
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FSourceCodeGeneratorModule, SourceCodeGenerator)

@ -0,0 +1,101 @@
#pragma once
#include "CoreMinimal.h"
#include "IScriptGeneratorPluginInterface.h"
class FSourceCodeGeneratorModule : public IScriptGeneratorPluginInterface
{
private:
static constexpr auto LargeInitialBufferSizes = 256 * 1024;
static constexpr auto SmallInitialBufferSizes = 1 * 1024;
typedef TStringBuilder<LargeInitialBufferSizes> FBigStringBuilder;
typedef TStringBuilder<SmallInitialBufferSizes> FSmallStringBuilder;
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
public:
/** IScriptGeneratorPluginInterface implementation */
virtual FString GetGeneratedCodeModuleName() const override;
virtual bool SupportsTarget(const FString& TargetName) const override;
virtual bool ShouldExportClassesForModule(const FString& ModuleName, EBuildModuleType::Type ModuleType,
const FString& ModuleGeneratedIncludeDirectory) const override;
virtual void Initialize(const FString& RootLocalPath, const FString& RootBuildPath,
const FString& OutputDirectory, const FString& IncludeBase) override;
virtual void ExportClass(UClass* Class, const FString& SourceHeaderFilename,
const FString& GeneratedHeaderFilename, bool bHasChanged) override;
virtual void FinishExport() override;
virtual FString GetGeneratorName() const override;
private:
static bool CanExportClass(const UClass* Class);
static bool CanExportFunction(const UFunction* Function);
static bool CanExportProperty(const FProperty* Property);
static void ExportFunction(FBigStringBuilder& StringBuilder, const UClass* Class, const UFunction* Function);
static void ExportProperty(FBigStringBuilder& StringBuilder, const UClass* Class, const FProperty* Property);
static bool IsClassTypeSupported(const UClass* Class);
static bool IsPropertyTypeSupported(const FProperty* Property);
static FString GetReturnPropertySignature(const FProperty* Property);
static FString GetParamPropertySignature(const FProperty* Property);
static FString GetFunctionSignature(const UClass* Class, const UFunction* Function);
static FString GetFunctionParamName(const UFunction* Function);
static FString GetFunctionDefaultValue(const UFunction* Function);
static bool FindDefaultValue(const TMap<FName, FString>* MetaMap, const FProperty* Param, FString& String);
static void GetPropertySignature(const FProperty* Property, FString& String);
static void GetDependencyClasses(const FProperty* Property, TSet<UClass*>& DependencyClasses);
static void SaveIfChanged(const FString& FileName, const FString& String);
FString GetHeaderFile(UClass* Class);
static FString GenerateInclude(const FString& FileName);
FString GetInclude(UClass* Class);
static void GetPlugins(const FString& InPathName, TMap<FString, FString>& Plugins);
static void GetModules(const FString& InPathName, TMap<FString, FString>& Modules);
static void GetModules(const FString& InPathName, TSet<FString>& Modules);
private:
FString OutputPath;
TArray<FString> ExportModules;
TSet<FString> Project;
TMap<FString, FString> HeaderPath;
TArray<UClass*> ExportClasses;
const FString BindingSuffix = FString(TEXT(".binding.inl"));
const FString HeaderSuffix = FString(TEXT(".header.inl"));
};

@ -0,0 +1,40 @@
using UnrealBuildTool;
namespace UnrealBuildTool.Rules
{
public class SourceCodeGenerator : ModuleRules
{
public SourceCodeGenerator(ReadOnlyTargetRules Target) : base(Target)
{
bEnforceIWYU = false;
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[]
{
"Programs/UnrealHeaderTool/Public",
}
);
PrivateIncludePaths.AddRange(
new string[]
{
"SourceCodeGenerator/Private",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
}
);
PublicDefinitions.Add("HACK_HEADER_GENERATOR=1");
}
}
}
Loading…
Cancel
Save