366 lines
10 KiB
C++
366 lines
10 KiB
C++
|
|
#include "CSProcHelper.h"
|
|||
|
|
#include "UnrealSharpProcHelper.h"
|
|||
|
|
#include "XmlFile.h"
|
|||
|
|
#include "XmlNode.h"
|
|||
|
|
#include "Misc/App.h"
|
|||
|
|
#include "Misc/Paths.h"
|
|||
|
|
#include "Interfaces/IPluginManager.h"
|
|||
|
|
#include "Misc/MessageDialog.h"
|
|||
|
|
|
|||
|
|
bool FCSProcHelper::InvokeCommand(const FString& ProgramPath, const FString& Arguments, int32& OutReturnCode, FString& Output, const FString* InWorkingDirectory)
|
|||
|
|
{
|
|||
|
|
double StartTime = FPlatformTime::Seconds();
|
|||
|
|
FString ProgramName = FPaths::GetBaseFilename(ProgramPath);
|
|||
|
|
FString WorkingDirectory = InWorkingDirectory ? *InWorkingDirectory : FPaths::GetPath(ProgramPath);
|
|||
|
|
|
|||
|
|
FString ErrorMessage;
|
|||
|
|
FPlatformProcess::ExecProcess(*ProgramPath, *Arguments, &OutReturnCode, &Output, &ErrorMessage, *WorkingDirectory);
|
|||
|
|
|
|||
|
|
if (OutReturnCode != 0)
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Error, TEXT("%s task failed (Args: %s) with return code %d. Error: %s"), *ProgramName, *Arguments, OutReturnCode, *Output)
|
|||
|
|
|
|||
|
|
FText DialogText = FText::FromString(FString::Printf(TEXT("%s task failed: \n %s"), *ProgramName, *Output));
|
|||
|
|
FMessageDialog::Open(EAppMsgType::Ok, DialogText);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double EndTime = FPlatformTime::Seconds();
|
|||
|
|
double ElapsedTime = EndTime - StartTime;
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Log, TEXT("%s with args (%s) took %f seconds to execute."), *ProgramName, *Arguments, ElapsedTime);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool FCSProcHelper::InvokeUnrealSharpBuildTool(const FString& BuildAction, const TMap<FString, FString>& AdditionalArguments)
|
|||
|
|
{
|
|||
|
|
FString PluginFolder = FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin(UE_PLUGIN_NAME)->GetBaseDir());
|
|||
|
|
FString DotNetPath = GetDotNetExecutablePath();
|
|||
|
|
|
|||
|
|
FString Args;
|
|||
|
|
Args += FString::Printf(TEXT(" --Action %s"), *BuildAction);
|
|||
|
|
Args += FString::Printf(TEXT(" --EngineDirectory \"%s\""), *FPaths::ConvertRelativePathToFull(FPaths::EngineDir()));
|
|||
|
|
Args += FString::Printf(TEXT(" --ProjectDirectory \"%s\""), *FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()));
|
|||
|
|
Args += FString::Printf(TEXT(" --ProjectName %s"), FApp::GetProjectName());
|
|||
|
|
Args += FString::Printf(TEXT(" --PluginDirectory \"%s\""), *PluginFolder);
|
|||
|
|
Args += FString::Printf(TEXT(" --DotNetPath \"%s\""), *DotNetPath);
|
|||
|
|
|
|||
|
|
if (AdditionalArguments.Num())
|
|||
|
|
{
|
|||
|
|
Args += TEXT(" --AdditionalArgs");
|
|||
|
|
for (const TPair<FString, FString>& Argument : AdditionalArguments)
|
|||
|
|
{
|
|||
|
|
Args += FString::Printf(TEXT(" %s=%s"), *Argument.Key, *Argument.Value);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int32 ReturnCode = 0;
|
|||
|
|
FString Output;
|
|||
|
|
FString WorkingDirectory = GetPluginAssembliesPath();
|
|||
|
|
return InvokeCommand(GetUnrealSharpBuildToolPath(), Args, ReturnCode, Output, &WorkingDirectory);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetLatestHostFxrPath()
|
|||
|
|
{
|
|||
|
|
FString DotNetRoot = GetDotNetDirectory();
|
|||
|
|
FString HostFxrRoot = FPaths::Combine(DotNetRoot, "host", "fxr");
|
|||
|
|
|
|||
|
|
TArray<FString> Folders;
|
|||
|
|
IFileManager::Get().FindFiles(Folders, *(HostFxrRoot / "*"), true, true);
|
|||
|
|
|
|||
|
|
FString HighestVersion = "0.0.0";
|
|||
|
|
for (const FString &Folder : Folders)
|
|||
|
|
{
|
|||
|
|
if (Folder > HighestVersion)
|
|||
|
|
{
|
|||
|
|
HighestVersion = Folder;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (HighestVersion == "0.0.0")
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Failed to find hostfxr version in %s"), *HostFxrRoot);
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (HighestVersion < DOTNET_MAJOR_VERSION)
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Hostfxr version %s is less than the required version %s"), *HighestVersion, TEXT(DOTNET_MAJOR_VERSION));
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#ifdef _WIN32
|
|||
|
|
return FPaths::Combine(HostFxrRoot, HighestVersion, HOSTFXR_WINDOWS);
|
|||
|
|
#elif defined(__APPLE__)
|
|||
|
|
return FPaths::Combine(HostFxrRoot, HighestVersion, HOSTFXR_MAC);
|
|||
|
|
#else
|
|||
|
|
return FPaths::Combine(HostFxrRoot, HighestVersion, HOSTFXR_LINUX);
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetRuntimeHostPath()
|
|||
|
|
{
|
|||
|
|
#if WITH_EDITOR
|
|||
|
|
return GetLatestHostFxrPath();
|
|||
|
|
#else
|
|||
|
|
#ifdef _WIN32
|
|||
|
|
return FPaths::Combine(GetPluginAssembliesPath(), HOSTFXR_WINDOWS);
|
|||
|
|
#elif defined(__APPLE__)
|
|||
|
|
return FPaths::Combine(GetPluginAssembliesPath(), HOSTFXR_MAC);
|
|||
|
|
#else
|
|||
|
|
return FPaths::Combine(GetPluginAssembliesPath(), HOSTFXR_LINUX);
|
|||
|
|
#endif
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetPathToSolution()
|
|||
|
|
{
|
|||
|
|
static FString SolutionPath = GetScriptFolderDirectory() / GetUserManagedProjectName() + ".sln";
|
|||
|
|
return SolutionPath;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetPluginAssembliesPath()
|
|||
|
|
{
|
|||
|
|
#if WITH_EDITOR
|
|||
|
|
return FPaths::Combine(GetPluginDirectory(), "Binaries", "Managed");
|
|||
|
|
#else
|
|||
|
|
return GetUserAssemblyDirectory();
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetUnrealSharpPluginsPath()
|
|||
|
|
{
|
|||
|
|
return GetPluginAssembliesPath() / "UnrealSharp.Plugins.dll";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetRuntimeConfigPath()
|
|||
|
|
{
|
|||
|
|
return GetPluginAssembliesPath() / "UnrealSharp.runtimeconfig.json";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetUserAssemblyDirectory()
|
|||
|
|
{
|
|||
|
|
return FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectDir(), "Binaries", "Managed"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetUnrealSharpMetadataPath()
|
|||
|
|
{
|
|||
|
|
return FPaths::Combine(GetUserAssemblyDirectory(), "UnrealSharp.assemblyloadorder.json");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void FCSProcHelper::GetProjectNamesByLoadOrder(TArray<FString>& UserProjectNames, const bool bIncludeGlue)
|
|||
|
|
{
|
|||
|
|
const FString ProjectMetadataPath = GetUnrealSharpMetadataPath();
|
|||
|
|
|
|||
|
|
if (!FPaths::FileExists(ProjectMetadataPath))
|
|||
|
|
{
|
|||
|
|
// Can be null at the start of the project.
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString JsonString;
|
|||
|
|
if (!FFileHelper::LoadFileToString(JsonString, *ProjectMetadataPath))
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Failed to load UnrealSharp metadata file at: %s"), *ProjectMetadataPath);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TSharedPtr<FJsonObject> JsonObject;
|
|||
|
|
if (!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(JsonString), JsonObject) || !JsonObject.IsValid())
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Failed to parse UnrealSharp metadata at: %s"), *ProjectMetadataPath);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (const TSharedPtr<FJsonValue>& OrderEntry : JsonObject->GetArrayField(TEXT("AssemblyLoadingOrder")))
|
|||
|
|
{
|
|||
|
|
FString ProjectName = OrderEntry->AsString();
|
|||
|
|
|
|||
|
|
if (!bIncludeGlue && ProjectName.EndsWith(TEXT("Glue")))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UserProjectNames.Add(OrderEntry->AsString());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
void FCSProcHelper::GetAssemblyPathsByLoadOrder(TArray<FString>& AssemblyPaths, const bool bIncludeGlue)
|
|||
|
|
{
|
|||
|
|
FString AbsoluteFolderPath = GetUserAssemblyDirectory();
|
|||
|
|
|
|||
|
|
TArray<FString> ProjectNames;
|
|||
|
|
GetProjectNamesByLoadOrder(ProjectNames, bIncludeGlue);
|
|||
|
|
|
|||
|
|
for (const FString& ProjectName : ProjectNames)
|
|||
|
|
{
|
|||
|
|
const FString AssemblyPath = FPaths::Combine(AbsoluteFolderPath, ProjectName + TEXT(".dll"));
|
|||
|
|
AssemblyPaths.Add(AssemblyPath);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void FCSProcHelper::GetAllProjectPaths(TArray<FString>& ProjectPaths, bool bIncludeProjectGlue)
|
|||
|
|
{
|
|||
|
|
// Use the FileManager to find files matching the pattern
|
|||
|
|
IFileManager::Get().FindFilesRecursive(ProjectPaths,
|
|||
|
|
*GetScriptFolderDirectory(),
|
|||
|
|
TEXT("*.csproj"),
|
|||
|
|
true,
|
|||
|
|
false,
|
|||
|
|
false);
|
|||
|
|
|
|||
|
|
TArray<FString> PluginFilePaths;
|
|||
|
|
IPluginManager::Get().FindPluginsUnderDirectory(FPaths::ProjectPluginsDir(), PluginFilePaths);
|
|||
|
|
|
|||
|
|
for (const FString& PluginFilePath : PluginFilePaths)
|
|||
|
|
{
|
|||
|
|
FString ScriptDirectory = FPaths::GetPath(PluginFilePath) / "Script";
|
|||
|
|
IFileManager::Get().FindFilesRecursive(ProjectPaths,
|
|||
|
|
*ScriptDirectory,
|
|||
|
|
TEXT("*.csproj"),
|
|||
|
|
true,
|
|||
|
|
false,
|
|||
|
|
false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int32 i = ProjectPaths.Num() - 1; i >= 0; i--)
|
|||
|
|
{
|
|||
|
|
if (bIncludeProjectGlue || !ProjectPaths[i].EndsWith("Glue.csproj"))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ProjectPaths.RemoveAt(i);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool FCSProcHelper::IsProjectReloadable(FStringView ProjectPath)
|
|||
|
|
{
|
|||
|
|
FXmlFile ProjectFile(ProjectPath.GetData());
|
|||
|
|
if (!ProjectFile.IsValid())
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Warning, TEXT("Failed to parse project file as XML: %s"),
|
|||
|
|
ProjectPath.GetData());
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const FXmlNode* RootNode = ProjectFile.GetRootNode();
|
|||
|
|
if (!RootNode)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Look through all PropertyGroup elements
|
|||
|
|
for (const TArray<FXmlNode*>& ProjectNodes = RootNode->GetChildrenNodes();
|
|||
|
|
const FXmlNode* Node : ProjectNodes)
|
|||
|
|
{
|
|||
|
|
if (Node->GetTag() == TEXT("PropertyGroup"))
|
|||
|
|
{
|
|||
|
|
if (const FXmlNode* RoslynComponentNode = Node->FindChildNode(TEXT("ExcludeFromWeaver"));
|
|||
|
|
RoslynComponentNode &&
|
|||
|
|
RoslynComponentNode->GetContent().Equals(TEXT("true"), ESearchCase::IgnoreCase))
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetUnrealSharpBuildToolPath()
|
|||
|
|
{
|
|||
|
|
#if PLATFORM_WINDOWS
|
|||
|
|
return FPaths::ConvertRelativePathToFull(GetPluginAssembliesPath() / "UnrealSharpBuildTool.exe");
|
|||
|
|
#else
|
|||
|
|
return FPaths::ConvertRelativePathToFull(GetPluginAssembliesPath() / "UnrealSharpBuildTool");
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetDotNetDirectory()
|
|||
|
|
{
|
|||
|
|
const FString PathVariable = FPlatformMisc::GetEnvironmentVariable(TEXT("PATH"));
|
|||
|
|
|
|||
|
|
TArray<FString> Paths;
|
|||
|
|
PathVariable.ParseIntoArray(Paths, FPlatformMisc::GetPathVarDelimiter());
|
|||
|
|
|
|||
|
|
#if defined(_WIN32)
|
|||
|
|
FString PathDotnet = "Program Files\\dotnet\\";
|
|||
|
|
#elif defined(__APPLE__)
|
|||
|
|
FString PathDotnet = "/usr/local/share/dotnet/";
|
|||
|
|
return PathDotnet;
|
|||
|
|
#endif
|
|||
|
|
for (FString &Path : Paths)
|
|||
|
|
{
|
|||
|
|
if (!Path.Contains(PathDotnet))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!FPaths::DirectoryExists(Path))
|
|||
|
|
{
|
|||
|
|
UE_LOG(LogUnrealSharpProcHelper, Warning, TEXT("Found path to DotNet, but the directory doesn't exist: %s"), *Path);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Path;
|
|||
|
|
}
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetDotNetExecutablePath()
|
|||
|
|
{
|
|||
|
|
#if defined(_WIN32)
|
|||
|
|
return GetDotNetDirectory() + "dotnet.exe";
|
|||
|
|
#else
|
|||
|
|
return GetDotNetDirectory() + "dotnet";
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString& FCSProcHelper::GetPluginDirectory()
|
|||
|
|
{
|
|||
|
|
static FString PluginDirectory;
|
|||
|
|
|
|||
|
|
if (PluginDirectory.IsEmpty())
|
|||
|
|
{
|
|||
|
|
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(UE_PLUGIN_NAME);
|
|||
|
|
check(Plugin);
|
|||
|
|
PluginDirectory = Plugin->GetBaseDir();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return PluginDirectory;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetUnrealSharpDirectory()
|
|||
|
|
{
|
|||
|
|
return FPaths::Combine(GetPluginDirectory(), "Managed", "UnrealSharp");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetGeneratedClassesDirectory()
|
|||
|
|
{
|
|||
|
|
return FPaths::Combine(GetUnrealSharpDirectory(), "UnrealSharp", "Generated");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const FString& FCSProcHelper::GetScriptFolderDirectory()
|
|||
|
|
{
|
|||
|
|
static FString ScriptFolderDirectory = FPaths::ProjectDir() / "Script";
|
|||
|
|
return ScriptFolderDirectory;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const FString& FCSProcHelper::GetPluginsDirectory()
|
|||
|
|
{
|
|||
|
|
static FString PluginsDirectory = FPaths::ProjectDir() / "Plugins";
|
|||
|
|
return PluginsDirectory;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const FString& FCSProcHelper::GetProjectGlueFolderPath()
|
|||
|
|
{
|
|||
|
|
static FString ProjectGlueFolderPath = GetScriptFolderDirectory() / FApp::GetProjectName() + TEXT(".Glue");
|
|||
|
|
return ProjectGlueFolderPath;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FString FCSProcHelper::GetUserManagedProjectName()
|
|||
|
|
{
|
|||
|
|
return FString::Printf(TEXT("Managed%s"), FApp::GetProjectName());
|
|||
|
|
}
|