You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Enable UbaController module and plugin for Mac and Linux. - Abstracts string literals for UBA as Mac/Linux use ANSICHAR while Windows uses WIDECHAR. - Fix URL output of Horde lease (remove double slash character). #jira UE-217573 #rb henrik.karlsson [CL 35811885 by laura hermanns in ue5-main branch]
311 lines
9.1 KiB
C++
311 lines
9.1 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "UbaControllerModule.h"
|
|
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "UbaJobProcessor.h"
|
|
#include "Features/IModularFeatures.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "Misc/CommandLine.h"
|
|
#include "Misc/CoreDelegates.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "CoreMinimal.h"
|
|
|
|
|
|
DEFINE_LOG_CATEGORY(LogUbaController);
|
|
|
|
namespace UbaControllerModule
|
|
{
|
|
static constexpr int32 SubFolderCount = 32;
|
|
|
|
static bool bDumpTraceFiles = true;
|
|
static FAutoConsoleVariableRef CVarDumpTraceFiles(
|
|
TEXT("r.UbaController.DumpTraceFiles"),
|
|
bDumpTraceFiles,
|
|
TEXT("If true, UBA controller dumps trace files for later use with UBA visualizer in the Saved folder under UbaController (Enabled by default)"));
|
|
|
|
static FString MakeAndGetDebugInfoPath()
|
|
{
|
|
// Build machines should dump to the AutomationTool/Saved/Logs directory and they will upload as build artifacts via the AutomationTool.
|
|
FString BaseDebugInfoPath = FPaths::ProjectSavedDir();
|
|
if (GIsBuildMachine)
|
|
{
|
|
BaseDebugInfoPath = FPaths::Combine(*FPaths::EngineDir(), TEXT("Programs"), TEXT("AutomationTool"), TEXT("Saved"), TEXT("Logs"));
|
|
}
|
|
|
|
FString AbsoluteDebugInfoDirectory = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*(BaseDebugInfoPath / TEXT("UbaController")));
|
|
FPaths::NormalizeDirectoryName(AbsoluteDebugInfoDirectory);
|
|
|
|
// Create directory if it doesn't exit yet
|
|
if (!IFileManager::Get().DirectoryExists(*AbsoluteDebugInfoDirectory))
|
|
{
|
|
IFileManager::Get().MakeDirectory(*AbsoluteDebugInfoDirectory, true);
|
|
}
|
|
|
|
return AbsoluteDebugInfoDirectory;
|
|
}
|
|
|
|
FString GetTempDir()
|
|
{
|
|
const FString HordeSharedDir = FPlatformMisc::GetEnvironmentVariable(TEXT("UE_HORDE_SHARED_DIR"));
|
|
if (!HordeSharedDir.IsEmpty())
|
|
return HordeSharedDir;
|
|
return FPlatformProcess::UserTempDir();
|
|
}
|
|
|
|
};
|
|
|
|
FUbaControllerModule::FUbaControllerModule()
|
|
: bSupported(false)
|
|
, bModuleInitialized(false)
|
|
, bControllerInitialized(false)
|
|
, RootWorkingDirectory(FPaths::Combine(*UbaControllerModule::GetTempDir(), TEXT("UbaControllerWorkingDir")))
|
|
, WorkingDirectory(FPaths::Combine(RootWorkingDirectory, FGuid::NewGuid().ToString(EGuidFormats::Digits)))
|
|
, NextFileID(0)
|
|
, NextTaskID(0)
|
|
{
|
|
}
|
|
|
|
FUbaControllerModule::~FUbaControllerModule()
|
|
{
|
|
if (JobDispatcherThread)
|
|
{
|
|
JobDispatcherThread->Stop();
|
|
// Wait until the thread is done
|
|
FPlatformProcess::ConditionalSleep([&](){ return JobDispatcherThread && JobDispatcherThread->IsWorkDone(); },0.1f);
|
|
}
|
|
|
|
CleanWorkingDirectory();
|
|
}
|
|
|
|
static bool IsUbaControllerEnabled()
|
|
{
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("NoUbaController")))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if UbaController is enabled via command line argument
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("Uba")))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check if UbaController is enabled via INI configuration in [UbaController] section.
|
|
FString EnabledState;
|
|
GConfig->GetString(TEXT("UbaController"), TEXT("Enabled"), EnabledState, GEngineIni);
|
|
|
|
// This "Enabled" parameter is a tri-state so we have to parse it as a string.
|
|
// Those strings can include the comments (starting with ';') from the INI file, so we have to trim that section from the string.
|
|
const int32 EnabledStateCommentPosition = EnabledState.Find(TEXT(";"));
|
|
if (EnabledStateCommentPosition != INDEX_NONE)
|
|
{
|
|
EnabledState.RemoveAt(EnabledStateCommentPosition, EnabledState.Len() - EnabledStateCommentPosition);
|
|
EnabledState.RemoveSpacesInline();
|
|
}
|
|
|
|
if (EnabledState.Equals(TEXT("True"), ESearchCase::IgnoreCase) || (EnabledState.Equals(TEXT("BuildMachineOnly"), ESearchCase::IgnoreCase) && GIsBuildMachine))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FUbaControllerModule::IsSupported()
|
|
{
|
|
if (bControllerInitialized)
|
|
{
|
|
return bSupported;
|
|
}
|
|
|
|
const bool bEnabled = IsUbaControllerEnabled();
|
|
|
|
bSupported = FPlatformProcess::SupportsMultithreading() && bEnabled;
|
|
return bSupported;
|
|
}
|
|
|
|
void FUbaControllerModule::CleanWorkingDirectory() const
|
|
{
|
|
if (UE::GetMultiprocessId() != 0) // Only director is allowed to clean
|
|
{
|
|
return;
|
|
}
|
|
|
|
IFileManager& FileManager = IFileManager::Get();
|
|
|
|
if (!RootWorkingDirectory.IsEmpty())
|
|
{
|
|
if (!FileManager.DeleteDirectory(*RootWorkingDirectory, false, true))
|
|
{
|
|
UE_LOG(LogUbaController, Log, TEXT("%s => Failed to delete current working Directory => %s"), ANSI_TO_TCHAR(__FUNCTION__), *RootWorkingDirectory);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FUbaControllerModule::HasTasksDispatchedOrPending() const
|
|
{
|
|
return !PendingRequestedCompilationTasks.IsEmpty() || (JobDispatcherThread.IsValid() && JobDispatcherThread->HasJobsInFlight());
|
|
}
|
|
|
|
FString GetUbaBinariesPath()
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
#if PLATFORM_CPU_ARM_FAMILY
|
|
const TCHAR* BinariesArch = TEXT("arm64");
|
|
#else
|
|
const TCHAR* BinariesArch = TEXT("x64");
|
|
#endif
|
|
return FPaths::Combine(FPaths::EngineDir(), TEXT("Binaries"), TEXT("Win64"), TEXT("UnrealBuildAccelerator"), BinariesArch);
|
|
#elif PLATFORM_MAC
|
|
return FPaths::Combine(FPaths::EngineDir(), TEXT("Binaries"), TEXT("Mac"), TEXT("UnrealBuildAccelerator"));
|
|
#elif PLATFORM_LINUX
|
|
return FPaths::Combine(FPaths::EngineDir(), TEXT("Binaries"), TEXT("Linux"), TEXT("UnrealBuildAccelerator"));
|
|
#else
|
|
#error Unsupported platform to compile UbaController plugin. Only Win64, Mac, and Linux are supported!
|
|
#endif
|
|
}
|
|
|
|
void FUbaControllerModule::LoadDependencies()
|
|
{
|
|
const FString UbaBinariesPath = GetUbaBinariesPath();
|
|
FPlatformProcess::AddDllDirectory(*UbaBinariesPath);
|
|
FPlatformProcess::GetDllHandle(*(FPaths::Combine(UbaBinariesPath, "UbaHost.dll")));
|
|
}
|
|
|
|
void FUbaControllerModule::StartupModule()
|
|
{
|
|
check(!bModuleInitialized);
|
|
|
|
LoadDependencies();
|
|
|
|
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureType(), this);
|
|
|
|
bModuleInitialized = true;
|
|
|
|
FCoreDelegates::OnEnginePreExit.AddLambda([&]()
|
|
{
|
|
if (bControllerInitialized && JobDispatcherThread)
|
|
{
|
|
JobDispatcherThread->Stop();
|
|
FPlatformProcess::ConditionalSleep([&]() { return JobDispatcherThread && JobDispatcherThread->IsWorkDone(); }, 0.1f);
|
|
JobDispatcherThread = nullptr;
|
|
}
|
|
});
|
|
}
|
|
|
|
void FUbaControllerModule::ShutdownModule()
|
|
{
|
|
check(bModuleInitialized);
|
|
|
|
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureType(), this);
|
|
|
|
if (bControllerInitialized)
|
|
{
|
|
// Stop the jobs thread
|
|
if (JobDispatcherThread)
|
|
{
|
|
JobDispatcherThread->Stop();
|
|
// Wait until the thread is done
|
|
FPlatformProcess::ConditionalSleep([&](){ return JobDispatcherThread && JobDispatcherThread->IsWorkDone(); },0.1f);
|
|
}
|
|
|
|
FTask* Task;
|
|
while (PendingRequestedCompilationTasks.Dequeue(Task))
|
|
{
|
|
FDistributedBuildTaskResult Result;
|
|
Result.ReturnCode = 0;
|
|
Result.bCompleted = false;
|
|
Task->Promise.SetValue(Result);
|
|
delete Task;
|
|
}
|
|
|
|
PendingRequestedCompilationTasks.Empty();
|
|
}
|
|
|
|
CleanWorkingDirectory();
|
|
bModuleInitialized = false;
|
|
bControllerInitialized = false;
|
|
}
|
|
|
|
void FUbaControllerModule::InitializeController()
|
|
{
|
|
// We should never Initialize the controller twice
|
|
if (ensureAlwaysMsgf(!bControllerInitialized, TEXT("Multiple initialization of UBA controller!")))
|
|
{
|
|
CleanWorkingDirectory();
|
|
|
|
if (IsSupported())
|
|
{
|
|
IFileManager::Get().MakeDirectory(*WorkingDirectory, true);
|
|
|
|
// Pre-create the directories so we don't have to explicitly register them to uba later
|
|
for (int32 It = 0; It != UbaControllerModule::SubFolderCount; ++It)
|
|
{
|
|
IFileManager::Get().MakeDirectory(*FString::Printf(TEXT("%s/%d"), *WorkingDirectory, It));
|
|
}
|
|
|
|
if (UbaControllerModule::bDumpTraceFiles)
|
|
{
|
|
DebugInfoPath = UbaControllerModule::MakeAndGetDebugInfoPath();
|
|
}
|
|
|
|
JobDispatcherThread = MakeShared<FUbaJobProcessor>(*this);
|
|
JobDispatcherThread->StartThread();
|
|
}
|
|
|
|
bControllerInitialized = true;
|
|
}
|
|
}
|
|
|
|
FString FUbaControllerModule::CreateUniqueFilePath()
|
|
{
|
|
check(bSupported);
|
|
int32 ID = NextFileID++;
|
|
int32 FolderID = ID % UbaControllerModule::SubFolderCount; // We use sub folders to be nicer to file system (we can end up with 20000 files in one folder otherwise)
|
|
return FString::Printf(TEXT("%s/%d/%d.uba"), *WorkingDirectory, FolderID, ID);
|
|
}
|
|
|
|
TFuture<FDistributedBuildTaskResult> FUbaControllerModule::EnqueueTask(const FTaskCommandData& CommandData)
|
|
{
|
|
check(bSupported);
|
|
|
|
TPromise<FDistributedBuildTaskResult> Promise;
|
|
TFuture<FDistributedBuildTaskResult> Future = Promise.GetFuture();
|
|
|
|
// Enqueue the new task
|
|
FTask* Task = new FTask(NextTaskID++, CommandData, MoveTemp(Promise));
|
|
{
|
|
PendingRequestedCompilationTasks.Enqueue(Task);
|
|
}
|
|
|
|
JobDispatcherThread->HandleTaskQueueUpdated(CommandData.InputFileName);
|
|
|
|
return MoveTemp(Future);
|
|
}
|
|
|
|
bool FUbaControllerModule::PollStats(FDistributedBuildStats& OutStats)
|
|
{
|
|
return JobDispatcherThread != nullptr && JobDispatcherThread->PollStats(OutStats);
|
|
}
|
|
|
|
void FUbaControllerModule::ReportJobProcessed(const FTaskResponse& InTaskResponse, FTask* CompileTask)
|
|
{
|
|
if (CompileTask)
|
|
{
|
|
FDistributedBuildTaskResult Result;
|
|
Result.ReturnCode = InTaskResponse.ReturnCode;
|
|
Result.bCompleted = true;
|
|
CompileTask->Promise.SetValue(Result);
|
|
delete CompileTask;
|
|
}
|
|
}
|
|
|
|
UBACONTROLLER_API FUbaControllerModule& FUbaControllerModule::Get()
|
|
{
|
|
return FModuleManager::LoadModuleChecked<FUbaControllerModule>(TEXT("UbaController"));
|
|
}
|
|
|
|
IMPLEMENT_MODULE(FUbaControllerModule, UbaController);
|