Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c19c5e97e | ||
|
|
0f2fd1382f | ||
|
|
e5c27effd7 | ||
|
|
7bcb7823a6 | ||
|
|
197fbbe6ce | ||
|
|
bf9949b5cb | ||
|
|
fbe837fca9 | ||
|
|
4aac4cc1fc | ||
|
|
4a3273ec31 | ||
|
|
fa2f24a764 | ||
|
|
90c5058a4c | ||
|
|
66d28f5ff8 | ||
|
|
5c3d42462f | ||
|
|
969682bc95 |
No files matched your search
@@ -72,3 +72,6 @@ Plugins/**/Intermediate/*
|
|||||||
|
|
||||||
# Cache files for the editor to use
|
# Cache files for the editor to use
|
||||||
DerivedDataCache/*
|
DerivedDataCache/*
|
||||||
|
|
||||||
|
obj/*
|
||||||
|
SourceCodeGenerator.ubtplugin.csproj.props
|
||||||
@@ -1,980 +0,0 @@
|
|||||||
#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 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (const auto SetProperty = CastField<FSetProperty>(Property))
|
|
||||||
{
|
|
||||||
if (SetProperty->ElementProp != nullptr)
|
|
||||||
{
|
|
||||||
GetDependencyClasses(SetProperty->ElementProp, DependencyClasses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (const auto MapProperty = CastField<FMapProperty>(Property))
|
|
||||||
{
|
|
||||||
if (MapProperty->KeyProp != nullptr && MapProperty->ValueProp != nullptr)
|
|
||||||
{
|
|
||||||
GetDependencyClasses(MapProperty->KeyProp, DependencyClasses);
|
|
||||||
|
|
||||||
GetDependencyClasses(MapProperty->ValueProp, 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)
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
#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"));
|
|
||||||
};
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,873 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using EpicGames.Core;
|
||||||
|
using EpicGames.UHT.Types;
|
||||||
|
using EpicGames.UHT.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EpicGames.UHT.Tables;
|
||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
namespace SourceCodeGeneratorUbtPlugin
|
||||||
|
{
|
||||||
|
[UnrealHeaderTool]
|
||||||
|
internal class SourceCodeGenerator
|
||||||
|
{
|
||||||
|
[UhtExporter(Name = "UnrealCSharp", Description = "UnrealCSharp Source Code Generator",
|
||||||
|
Options = UhtExporterOptions.Default, ModuleName = "UnrealCSharp")]
|
||||||
|
private static void SourceCodeGeneratorExporter(IUhtExportFactory factory)
|
||||||
|
{
|
||||||
|
var SettingFilePath = Path.Combine(new string(factory.Session.ProjectDirectory),
|
||||||
|
"Config",
|
||||||
|
"DefaultUnrealCSharpEditorSetting.ini");
|
||||||
|
|
||||||
|
if (File.Exists(SettingFilePath))
|
||||||
|
{
|
||||||
|
var SettingConfigFile = new ConfigFile(new FileReference(SettingFilePath));
|
||||||
|
|
||||||
|
if (SettingConfigFile.TryGetSection("/Script/UnrealCSharpCore.UnrealCSharpEditorSetting",
|
||||||
|
out var SettingConfigSection))
|
||||||
|
{
|
||||||
|
var SettingConfigHierarchySection = new ConfigHierarchySection(
|
||||||
|
new List<ConfigFileSection> { SettingConfigSection });
|
||||||
|
|
||||||
|
if (SettingConfigHierarchySection.TryGetValue("bEnableExport",
|
||||||
|
out var Value))
|
||||||
|
{
|
||||||
|
if (bool.Parse(Value.ToLower()))
|
||||||
|
{
|
||||||
|
new SourceCodeGenerator(factory).Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IUhtExportFactory Factory;
|
||||||
|
|
||||||
|
private UhtSession Session => Factory.Session;
|
||||||
|
|
||||||
|
private ConcurrentQueue<UhtClass> ExportClasses = new();
|
||||||
|
|
||||||
|
private HashSet<string> Project = new();
|
||||||
|
|
||||||
|
private Dictionary<string, string> HeaderPath = new();
|
||||||
|
|
||||||
|
private const string BindingSuffix = ".binding.inl";
|
||||||
|
|
||||||
|
private const string HeaderSuffix = ".header.inl";
|
||||||
|
|
||||||
|
private readonly HashSet<string> ClassBlacklist = new();
|
||||||
|
|
||||||
|
private const string GeneratedHeaderComment =
|
||||||
|
"/*===========================================================================\n" +
|
||||||
|
" Generated code exported from UnrealCSharp SourceCodeGenerator.\n" +
|
||||||
|
" DO NOT modify this manually!\n" +
|
||||||
|
"===========================================================================*/\n" +
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
public SourceCodeGenerator(IUhtExportFactory factory)
|
||||||
|
{
|
||||||
|
Factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Export all the classes in all the packages
|
||||||
|
/// </summary>
|
||||||
|
private void Generate()
|
||||||
|
{
|
||||||
|
if (Session is { EngineDirectory: not null, ProjectDirectory: not null })
|
||||||
|
{
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.ProjectDirectory, "Source/")), Project);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.ProjectDirectory, "Source/")), HeaderPath);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.ProjectDirectory, "Plugins/")), HeaderPath);
|
||||||
|
|
||||||
|
GetPlugins(Path.GetFullPath(Path.Combine(Session.ProjectDirectory, "Plugins/")), HeaderPath);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.EngineDirectory, "Source/Developer/")), HeaderPath);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.EngineDirectory, "Source/Editor/")), HeaderPath);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.EngineDirectory, "Source/Programs/")), HeaderPath);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.EngineDirectory, "Source/Runtime/")), HeaderPath);
|
||||||
|
|
||||||
|
GetModules(Path.GetFullPath(Path.Combine(Session.EngineDirectory, "Plugins/")), HeaderPath);
|
||||||
|
|
||||||
|
GetPlugins(Path.GetFullPath(Path.Combine(Session.EngineDirectory, "Plugins/")), HeaderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var SettingFilePath = Path.Combine(new string(Session.ProjectDirectory),
|
||||||
|
"Config",
|
||||||
|
"DefaultUnrealCSharpEditorSetting.ini");
|
||||||
|
|
||||||
|
var ExportModule = new List<string>();
|
||||||
|
|
||||||
|
if (File.Exists(SettingFilePath))
|
||||||
|
{
|
||||||
|
var SettingConfigFile = new ConfigFile(new FileReference(SettingFilePath));
|
||||||
|
|
||||||
|
if (SettingConfigFile.TryGetSection("/Script/UnrealCSharpCore.UnrealCSharpEditorSetting",
|
||||||
|
out var SettingConfigSection))
|
||||||
|
{
|
||||||
|
var SettingConfigHierarchySection = new ConfigHierarchySection(
|
||||||
|
new List<ConfigFileSection> { SettingConfigSection });
|
||||||
|
|
||||||
|
if (SettingConfigHierarchySection.TryGetValues("ExportModule",
|
||||||
|
out var Values))
|
||||||
|
{
|
||||||
|
foreach (var value in Values)
|
||||||
|
{
|
||||||
|
ExportModule.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingConfigHierarchySection.TryGetValues("ClassBlacklist", out var BlacklistValues))
|
||||||
|
{
|
||||||
|
foreach (var value in BlacklistValues)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
ClassBlacklist.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Task?> tasks = new();
|
||||||
|
|
||||||
|
foreach (var module in Session.Modules)
|
||||||
|
{
|
||||||
|
if (module.Module.ModuleType != UHTModuleType.EngineRuntime &&
|
||||||
|
module.Module.ModuleType != UHTModuleType.GameRuntime)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ExportModule.All(x =>
|
||||||
|
string.Compare(x, module.Module.Name, StringComparison.OrdinalIgnoreCase) != 0))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueClassExports(module.ScriptPackage, tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
var waitTasks = tasks.Where(Task => Task != null).Cast<Task>().ToArray();
|
||||||
|
|
||||||
|
if (waitTasks.Length > 0)
|
||||||
|
{
|
||||||
|
Task.WaitAll(waitTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collect the classes to be exported for the given package and type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type to test for exporting</param>
|
||||||
|
/// <param name="tasks">Collection of queued tasks</param>
|
||||||
|
private void QueueClassExports(UhtType type, List<Task?> tasks)
|
||||||
|
{
|
||||||
|
if (type is UhtClass classObj)
|
||||||
|
{
|
||||||
|
if (CanExportClass(classObj))
|
||||||
|
{
|
||||||
|
tasks.Add(Factory.CreateTask(_ => { ExportClass(classObj); }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in type.Children)
|
||||||
|
{
|
||||||
|
QueueClassExports(child, tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test to see if the given class should be exported
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="classObj">Class to test</param>
|
||||||
|
/// <returns>True if the class should be exported, false if not</returns>
|
||||||
|
protected virtual bool CanExportClass(UhtClass classObj)
|
||||||
|
{
|
||||||
|
return !ClassBlacklist.Contains(classObj.SourceName) &&
|
||||||
|
!classObj.ClassFlags.HasAnyFlags(EClassFlags.Interface) &&
|
||||||
|
IsClassTypeSupported(classObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test to see if the given function should be exported
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="function">Function to test</param>
|
||||||
|
/// <returns>True if the function should be exported</returns>
|
||||||
|
protected virtual bool CanExportFunction(UhtFunction function)
|
||||||
|
{
|
||||||
|
if (function.Outer is UhtClass classObj)
|
||||||
|
{
|
||||||
|
if (!classObj.ClassFlags.HasAnyFlags(EClassFlags.RequiredAPI) &&
|
||||||
|
!function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.RequiredAPI))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.FunctionType != UhtFunctionType.Function)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function.FunctionFlags.HasAnyFlags(EFunctionFlags.Public))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.Deprecated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.EditorOnly))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.CustomThunk))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function is { HasReturnProperty: true, ReturnProperty: UhtMapProperty })
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in function.Children)
|
||||||
|
{
|
||||||
|
if (child is UhtProperty property && !IsPropertyTypeSupported(property))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test to see if the given property should be exported
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="property">Property to test</param>
|
||||||
|
/// <returns>True if the property should be exported</returns>
|
||||||
|
protected virtual bool CanExportProperty(UhtProperty property)
|
||||||
|
{
|
||||||
|
return !property.Deprecated &&
|
||||||
|
!property.PropertyFlags.HasAnyFlags(EPropertyFlags.EditorOnly) &&
|
||||||
|
property.PropertyFlags.HasAnyFlags(EPropertyFlags.NativeAccessSpecifierPublic) &&
|
||||||
|
IsPropertyTypeSupported(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsClassTypeSupported(UhtClass classObj)
|
||||||
|
{
|
||||||
|
return !classObj.Deprecated &&
|
||||||
|
(classObj == classObj.Session.UObject ||
|
||||||
|
classObj == classObj.Session.UClass ||
|
||||||
|
classObj.ClassFlags.HasAnyFlags(EClassFlags.RequiredAPI | EClassFlags.MinimalAPI));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsPropertyTypeSupported(UhtProperty property)
|
||||||
|
{
|
||||||
|
if (property is UhtObjectProperty objectProperty)
|
||||||
|
{
|
||||||
|
if (!IsClassTypeSupported(objectProperty.Class))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtInterfaceProperty interfaceProperty)
|
||||||
|
{
|
||||||
|
if (interfaceProperty.InterfaceClass == interfaceProperty.Session.IInterface)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtDelegateProperty or UhtMulticastDelegateProperty or UhtFieldPathProperty)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtStructProperty { ScriptStruct.HasNoOpConstructor: false })
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtArrayProperty arrayProperty)
|
||||||
|
{
|
||||||
|
return IsPropertyTypeSupported(arrayProperty.ValueProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtSetProperty setProperty)
|
||||||
|
{
|
||||||
|
return IsPropertyTypeSupported(setProperty.ValueProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtMapProperty mapProperty)
|
||||||
|
{
|
||||||
|
return IsPropertyTypeSupported(mapProperty.KeyProperty) &&
|
||||||
|
IsPropertyTypeSupported(mapProperty.ValueProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
return property is { IsBitfield: false, IsStaticArray: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Export the given class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="classObj">Class to export</param>
|
||||||
|
private void ExportClass(UhtClass classObj)
|
||||||
|
{
|
||||||
|
using BorrowStringBuilder borrower = new(StringBuilderCache.Big);
|
||||||
|
|
||||||
|
if (ExportClass(borrower.StringBuilder, classObj))
|
||||||
|
{
|
||||||
|
var fileName = Factory.MakePath(classObj.EngineName, BindingSuffix);
|
||||||
|
|
||||||
|
SaveIfChanged(fileName, borrower.StringBuilder);
|
||||||
|
|
||||||
|
ExportClasses.Enqueue(classObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Finish()
|
||||||
|
{
|
||||||
|
var packages = new Dictionary<UhtPackage, List<string>>();
|
||||||
|
|
||||||
|
foreach (var ExportClass in ExportClasses)
|
||||||
|
{
|
||||||
|
if (!packages.ContainsKey(ExportClass.Package))
|
||||||
|
{
|
||||||
|
packages.Add(ExportClass.Package, new List<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
packages[ExportClass.Package].AddUnique(ExportClass.EngineName);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var package in packages)
|
||||||
|
{
|
||||||
|
package.Value.Sort();
|
||||||
|
|
||||||
|
var borrow = new BorrowStringBuilder(StringBuilderCache.Big);
|
||||||
|
|
||||||
|
var builder = borrow.StringBuilder;
|
||||||
|
|
||||||
|
builder.Append(GeneratedHeaderComment);
|
||||||
|
|
||||||
|
builder.Append("#pragma once\r\n\r\n");
|
||||||
|
|
||||||
|
foreach (var value in package.Value)
|
||||||
|
{
|
||||||
|
builder.Append(GenerateInclude($"{value}{BindingSuffix}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = Factory.MakePath(package.Key.Module, HeaderSuffix);
|
||||||
|
|
||||||
|
SaveIfChanged(fileName, builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ExportClass(StringBuilder builder, UhtClass classObj)
|
||||||
|
{
|
||||||
|
builder.Append(GeneratedHeaderComment);
|
||||||
|
|
||||||
|
builder.Append("#pragma once\r\n\r\n" +
|
||||||
|
"PRAGMA_DISABLE_DEPRECATION_WARNINGS\r\n\r\n");
|
||||||
|
|
||||||
|
var DependencyClasses = new HashSet<UhtClass> { classObj };
|
||||||
|
|
||||||
|
using BorrowStringBuilder bodyBorrower = new(StringBuilderCache.Big);
|
||||||
|
|
||||||
|
var bodyBuilder = bodyBorrower.StringBuilder;
|
||||||
|
|
||||||
|
bodyBuilder.AppendFormat("namespace\r\n" +
|
||||||
|
"{{\r\n" +
|
||||||
|
"\tstruct FRegister{0}\r\n" +
|
||||||
|
"\t{{\r\n" +
|
||||||
|
"\t\tFRegister{1}()\r\n" +
|
||||||
|
"\t\t{{\r\n" +
|
||||||
|
"\t\t\tTBindingClassBuilder<{2}>(NAMESPACE_BINDING)\r\n",
|
||||||
|
classObj.EngineName,
|
||||||
|
classObj.EngineName,
|
||||||
|
classObj.SourceName);
|
||||||
|
|
||||||
|
var bExportProperty = false;
|
||||||
|
|
||||||
|
foreach (var child in classObj.Children)
|
||||||
|
{
|
||||||
|
if (child is UhtProperty property && CanExportProperty(property))
|
||||||
|
{
|
||||||
|
GetDependencyClasses(property, DependencyClasses);
|
||||||
|
|
||||||
|
ExportProperty(bodyBuilder, classObj, property);
|
||||||
|
|
||||||
|
bExportProperty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bExportFunction = false;
|
||||||
|
|
||||||
|
foreach (var function in classObj.Functions.Reverse())
|
||||||
|
{
|
||||||
|
if (CanExportFunction(function))
|
||||||
|
{
|
||||||
|
foreach (var child in function.Children)
|
||||||
|
{
|
||||||
|
if (child is UhtProperty property)
|
||||||
|
{
|
||||||
|
GetDependencyClasses(property, DependencyClasses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportFunction(bodyBuilder, classObj, function);
|
||||||
|
|
||||||
|
bExportFunction = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var DependencyClass in DependencyClasses)
|
||||||
|
{
|
||||||
|
builder.Append(GetInclude(DependencyClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append("\r\n");
|
||||||
|
|
||||||
|
bodyBuilder.Remove(bodyBuilder.Length - 2, 2);
|
||||||
|
|
||||||
|
bodyBuilder.AppendFormat(";\r\n" +
|
||||||
|
"\t\t}}\r\n" +
|
||||||
|
"\t}};\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"\t[[maybe_unused]] FRegister{0} Register{1};\r\n" +
|
||||||
|
"}}\r\n\r\n",
|
||||||
|
classObj.EngineName,
|
||||||
|
classObj.EngineName);
|
||||||
|
|
||||||
|
builder.Append(bodyBuilder);
|
||||||
|
|
||||||
|
builder.Append("PRAGMA_ENABLE_DEPRECATION_WARNINGS\r\n");
|
||||||
|
|
||||||
|
return bExportProperty || bExportFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetDependencyClasses(UhtProperty property, HashSet<UhtClass> DependencyClasses)
|
||||||
|
{
|
||||||
|
if (property is UhtClassProperty classProperty)
|
||||||
|
{
|
||||||
|
if (classProperty.MetaClass != null)
|
||||||
|
{
|
||||||
|
DependencyClasses.Add(classProperty.MetaClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (property is UhtObjectProperty objectProperty)
|
||||||
|
{
|
||||||
|
DependencyClasses.Add(objectProperty.Class);
|
||||||
|
}
|
||||||
|
else if (property is UhtSoftClassProperty softClassProperty)
|
||||||
|
{
|
||||||
|
if (softClassProperty.MetaClass != null)
|
||||||
|
{
|
||||||
|
DependencyClasses.Add(softClassProperty.MetaClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (property is UhtSoftObjectProperty softObjectProperty)
|
||||||
|
{
|
||||||
|
DependencyClasses.Add(softObjectProperty.Class);
|
||||||
|
}
|
||||||
|
else if (property is UhtArrayProperty arrayProperty)
|
||||||
|
{
|
||||||
|
GetDependencyClasses(arrayProperty.ValueProperty, DependencyClasses);
|
||||||
|
}
|
||||||
|
else if (property is UhtSetProperty setProperty)
|
||||||
|
{
|
||||||
|
GetDependencyClasses(setProperty.ValueProperty, DependencyClasses);
|
||||||
|
}
|
||||||
|
else if (property is UhtMapProperty mapProperty)
|
||||||
|
{
|
||||||
|
GetDependencyClasses(mapProperty.KeyProperty, DependencyClasses);
|
||||||
|
|
||||||
|
GetDependencyClasses(mapProperty.ValueProperty, DependencyClasses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExportFunction(StringBuilder builder, UhtClass classObj, UhtFunction function)
|
||||||
|
{
|
||||||
|
builder.Append(
|
||||||
|
$"\t\t\t\t.Function(\"{function.SourceName}\", BINDING_OVERLOAD({GetFunctionSignature(classObj, function)}, &{classObj.SourceName}::{function.SourceName}");
|
||||||
|
|
||||||
|
if (function.HasParameters)
|
||||||
|
{
|
||||||
|
builder.Append($", {GetFunctionParamName(function)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function.FunctionFlags.HasAnyFlags(EFunctionFlags.Static))
|
||||||
|
{
|
||||||
|
builder.Append(", EFunctionInteract::Virtual");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.HasParameters)
|
||||||
|
{
|
||||||
|
builder.Append(GetFunctionDefaultValue(function));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append("))\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExportProperty(StringBuilder builder, UhtClass classObj, UhtProperty property)
|
||||||
|
{
|
||||||
|
builder.Append(
|
||||||
|
$"\t\t\t\t.Property(\"{property.SourceName}\", BINDING_PROPERTY(&{classObj.SourceName}::{property.SourceName}))\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetParamPropertySignature(UhtProperty? property)
|
||||||
|
{
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
return "void";
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
var caps = property.PropertyCaps;
|
||||||
|
|
||||||
|
var isInterfaceProp = property is UhtInterfaceProperty;
|
||||||
|
|
||||||
|
var passCppArgsByRef = caps.HasAnyFlags(UhtPropertyCaps.PassCppArgsByRef);
|
||||||
|
|
||||||
|
var isConstParam = property.PropertyFlags.HasAnyFlags(EPropertyFlags.ConstParm) ||
|
||||||
|
(isInterfaceProp && !property.PropertyFlags.HasAnyFlags(EPropertyFlags.OutParm));
|
||||||
|
|
||||||
|
var isConstArgsByRef = property.ArrayDimensions == null && passCppArgsByRef &&
|
||||||
|
!property.PropertyFlags.HasAnyFlags(
|
||||||
|
EPropertyFlags.ConstParm | EPropertyFlags.OutParm);
|
||||||
|
|
||||||
|
var isOnConstClass = false;
|
||||||
|
|
||||||
|
if (property is UhtObjectProperty objectProperty)
|
||||||
|
{
|
||||||
|
isOnConstClass = objectProperty.Class.ClassFlags.HasAnyFlags(EClassFlags.Const) ||
|
||||||
|
objectProperty.MetaData.TryGetValue(UhtNames.NativeConst, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldHaveRef =
|
||||||
|
property.PropertyFlags.HasAnyFlags(EPropertyFlags.OutParm | EPropertyFlags.ReferenceParm);
|
||||||
|
|
||||||
|
var constAtTheBeginning = isOnConstClass || isConstArgsByRef || (isConstParam && !shouldHaveRef);
|
||||||
|
|
||||||
|
if (constAtTheBeginning)
|
||||||
|
{
|
||||||
|
builder.Append("const ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtEnumProperty enumProperty)
|
||||||
|
{
|
||||||
|
builder.Append(enumProperty.Enum.CppType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
property.AppendText(builder, UhtPropertyTextType.ExportMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromConstClass = false;
|
||||||
|
|
||||||
|
if (property.Outer is UhtClass outerClass)
|
||||||
|
{
|
||||||
|
fromConstClass = outerClass.ClassFlags.HasAnyFlags(EClassFlags.Const);
|
||||||
|
}
|
||||||
|
|
||||||
|
var constAtTheEnd = (fromConstClass || (isConstParam && shouldHaveRef)) && !constAtTheBeginning;
|
||||||
|
|
||||||
|
if (constAtTheEnd)
|
||||||
|
{
|
||||||
|
builder.Append(" const");
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldHaveRef = passCppArgsByRef;
|
||||||
|
|
||||||
|
if (shouldHaveRef ||
|
||||||
|
property.ArrayDimensions == null &&
|
||||||
|
(passCppArgsByRef ||
|
||||||
|
property.PropertyFlags.HasAnyFlags(EPropertyFlags.OutParm | EPropertyFlags.ReferenceParm)))
|
||||||
|
{
|
||||||
|
if (property is UhtArrayProperty or UhtStrProperty or UhtTextProperty or UhtInterfaceProperty
|
||||||
|
or UhtSoftObjectProperty)
|
||||||
|
{
|
||||||
|
if (property.RefQualifier != UhtPropertyRefQualifier.None)
|
||||||
|
{
|
||||||
|
builder.Append('&');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Append('&');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetReturnPropertySignature(UhtProperty? property)
|
||||||
|
{
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
return "void";
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
if (property.PropertyFlags.HasAnyFlags(EPropertyFlags.ConstParm) ||
|
||||||
|
property.RefQualifier == UhtPropertyRefQualifier.ConstRef)
|
||||||
|
{
|
||||||
|
builder.Append("const ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property is UhtEnumProperty enumProperty)
|
||||||
|
{
|
||||||
|
builder.Append(enumProperty.Enum.CppType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
property.AppendText(builder, UhtPropertyTextType.ExportMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property.RefQualifier == UhtPropertyRefQualifier.ConstRef)
|
||||||
|
{
|
||||||
|
builder.Append('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFunctionSignature(UhtClass classObj, UhtFunction function)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.Append(GetReturnPropertySignature(function.ReturnProperty));
|
||||||
|
|
||||||
|
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Static))
|
||||||
|
{
|
||||||
|
builder.Append("(*)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Append($"({classObj.SourceName}::*)");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append('(');
|
||||||
|
|
||||||
|
foreach (var child in function.Children)
|
||||||
|
{
|
||||||
|
if (child is UhtProperty property)
|
||||||
|
{
|
||||||
|
if (!property.PropertyFlags.HasAnyFlags(EPropertyFlags.ReturnParm))
|
||||||
|
{
|
||||||
|
builder.Append(GetParamPropertySignature(property));
|
||||||
|
|
||||||
|
builder.Append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.HasParameters)
|
||||||
|
{
|
||||||
|
builder.Remove(builder.Length - 2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(')');
|
||||||
|
|
||||||
|
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Const))
|
||||||
|
{
|
||||||
|
builder.Append("const");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFunctionParamName(UhtFunction function)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.Append("TArray<FString>{");
|
||||||
|
|
||||||
|
foreach (var child in function.Children)
|
||||||
|
{
|
||||||
|
if (child is UhtProperty property)
|
||||||
|
{
|
||||||
|
if (!property.PropertyFlags.HasAnyFlags(EPropertyFlags.ReturnParm))
|
||||||
|
{
|
||||||
|
builder.Append($"\"{property.SourceName}\", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Remove(builder.Length - 2, 2);
|
||||||
|
|
||||||
|
builder.Append('}');
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFunctionDefaultValue(UhtFunction function)
|
||||||
|
{
|
||||||
|
if (!function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintCallable))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var child in function.Children)
|
||||||
|
{
|
||||||
|
if (child is UhtProperty property)
|
||||||
|
{
|
||||||
|
if (!property.PropertyFlags.HasAnyFlags(EPropertyFlags.ReturnParm))
|
||||||
|
{
|
||||||
|
if (property.DefaultValueTokens != null)
|
||||||
|
{
|
||||||
|
builder.Append(", ");
|
||||||
|
|
||||||
|
var DefaultValue = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var ValueToken in property.DefaultValueTokens)
|
||||||
|
{
|
||||||
|
DefaultValue.Append(ValueToken.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DefaultValue.ToString() == "NULL")
|
||||||
|
{
|
||||||
|
builder.Append("nullptr");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.Append(DefaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetHeaderFile(UhtClass classObj)
|
||||||
|
{
|
||||||
|
return Path.Combine(HeaderPath[classObj.Package.Module.ShortName], classObj.HeaderFile.FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateInclude(string file)
|
||||||
|
{
|
||||||
|
return $"#include \"{file}\"\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetInclude(UhtClass classObj)
|
||||||
|
{
|
||||||
|
return GenerateInclude(GetHeaderFile(classObj));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveIfChanged(string file, StringBuilder builder)
|
||||||
|
{
|
||||||
|
if (File.Exists(file) && File.ReadAllText(file) == builder.ToString())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Factory.CommitOutput(file, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetPlugins(string InPathName, Dictionary<string, string> Plugins)
|
||||||
|
{
|
||||||
|
var Suffix = "*.uplugin";
|
||||||
|
|
||||||
|
var DirectoryInfo = new DirectoryInfo(InPathName);
|
||||||
|
|
||||||
|
foreach (var Item in DirectoryInfo.GetFiles(Suffix))
|
||||||
|
{
|
||||||
|
if (Item.DirectoryName != null)
|
||||||
|
{
|
||||||
|
Plugins[Item.Name.Remove(Item.Name.Length - Suffix.Length + 1)] = Item.DirectoryName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var Directories in DirectoryInfo.GetDirectories())
|
||||||
|
{
|
||||||
|
foreach (var Item in Directories.GetFiles(Suffix, SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
if (Item.DirectoryName != null)
|
||||||
|
{
|
||||||
|
Plugins[Item.Name.Remove(Item.Name.Length - Suffix.Length + 1)] = Item.DirectoryName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetModules(string InPathName, Dictionary<string, string> Modules)
|
||||||
|
{
|
||||||
|
var Suffix = "*.Build.cs";
|
||||||
|
|
||||||
|
var DirectoryInfo = new DirectoryInfo(InPathName);
|
||||||
|
|
||||||
|
foreach (var Item in DirectoryInfo.GetFiles(Suffix))
|
||||||
|
{
|
||||||
|
if (Item.DirectoryName != null)
|
||||||
|
{
|
||||||
|
Modules[Item.Name.Remove(Item.Name.Length - Suffix.Length + 1)] = Item.DirectoryName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var Directories in DirectoryInfo.GetDirectories())
|
||||||
|
{
|
||||||
|
foreach (var Item in Directories.GetFiles(Suffix, SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
if (Item.DirectoryName != null)
|
||||||
|
{
|
||||||
|
Modules[Item.Name.Remove(Item.Name.Length - Suffix.Length + 1)] = Item.DirectoryName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetModules(string InPathName, HashSet<string> Modules)
|
||||||
|
{
|
||||||
|
var Suffix = "*.Build.cs";
|
||||||
|
|
||||||
|
var DirectoryInfo = new DirectoryInfo(InPathName);
|
||||||
|
|
||||||
|
foreach (var Item in DirectoryInfo.GetFiles(Suffix))
|
||||||
|
{
|
||||||
|
Modules.Add(Item.Name.Remove(Item.Name.Length - Suffix.Length + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var Directories in DirectoryInfo.GetDirectories())
|
||||||
|
{
|
||||||
|
foreach (var Item in Directories.GetFiles(Suffix, SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
if (Item.DirectoryName != null)
|
||||||
|
{
|
||||||
|
Modules.Add(Item.Name.Remove(Item.Name.Length - Suffix.Length + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="SourceCodeGenerator.ubtplugin.csproj.props" Condition="Exists('SourceCodeGenerator.ubtplugin.csproj.props')" />
|
||||||
|
<Import Project="$(EngineDir)\Source\Programs\Shared\UnrealEngine.csproj.props" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Development</Configuration>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||||
|
<Configurations>Debug;Release;Development</Configurations>
|
||||||
|
<RootNamespace>SourceCodeGenerator</RootNamespace>
|
||||||
|
<AssemblyName>SourceCodeGenerator</AssemblyName>
|
||||||
|
<OutputPath>..\..\..\..\Binaries\DotNET\UnrealBuildTool\Plugins\SourceCodeGenerator\</OutputPath>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Development|AnyCPU' ">
|
||||||
|
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="EpicGames.Build">
|
||||||
|
<HintPath>$(EngineDir)\Binaries\DotNET\UnrealBuildTool\EpicGames.Build.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EpicGames.Core">
|
||||||
|
<HintPath>$(EngineDir)\Binaries\DotNET\UnrealBuildTool\EpicGames.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EpicGames.UHT">
|
||||||
|
<HintPath>$(EngineDir)\Binaries\DotNET\UnrealBuildTool\EpicGames.UHT.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnrealBuildTool">
|
||||||
|
<HintPath>$(EngineDir)\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
Reference in new issue
Block a user