Files
UnrealEngine/Engine/Source/Programs/BuildStorageTool/Private/BuildStorageTool.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

532 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BuildStorageTool.h"
#include "Experimental/ZenServerInterface.h"
#include "Features/IModularFeatures.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/Notifications/NotificationManager.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "HAL/PlatformProcess.h"
#include "Misc/CompilationResult.h"
#include "Misc/MessageDialog.h"
#include "Misc/Optional.h"
#include "Misc/Timespan.h"
#include "Modules/BuildVersion.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
#include "RequiredProgramMainCPPInclude.h"
#include "SBuildActivity.h"
#include "SBuildSelection.h"
#include "SBuildLogin.h"
#include "SMessageDialog.h"
#include "StandaloneRenderer.h"
#include "BuildStorageToolStyle.h"
#include "BuildStorageToolHelpWidget.h"
#include "Tasks/Task.h"
#include "Templates/SharedPointer.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Widgets/Text/STextBlock.h"
#include "ZenServiceInstanceManager.h"
#include "BuildServiceInstanceManager.h"
#include "Version/AppVersionDefines.h"
#include "Parameters/BuildStorageToolParametersBuilder.h"
#include "OutputLogCreationParams.h"
#include "OutputLogModule.h"
#include "OutputLogSettings.h"
#if PLATFORM_WINDOWS
#include "Runtime/Launch/Resources/Windows/Resource.h"
#include "Windows/WindowsApplication.h"
#include "Windows/AllowWindowsPlatformTypes.h"
#include <shellapi.h>
#include "Windows/HideWindowsPlatformTypes.h"
#elif PLATFORM_LINUX
#include "UnixCommonStartup.h"
#elif PLATFORM_MAC
#include "Mac/MacProgramDelegate.h"
#include "LaunchEngineLoop.h"
#else
#error "Unsupported platform!"
#endif
#define LOCTEXT_NAMESPACE "BuildStorageTool"
// These macros are not properly defined by UBT in the case of an engine program with bTreatAsEngineModule=true
// So define them here as a workaround
#define IMPLEMENT_ENCRYPTION_KEY_REGISTRATION()
#define IMPLEMENT_SIGNING_KEY_REGISTRATION()
IMPLEMENT_APPLICATION(BuildStorageTool, "BuildStorageTool");
DEFINE_LOG_CATEGORY(LogBuildStorageTool);
static void OnRequestExit()
{
RequestEngineExit(TEXT("BuildStorageTool Closed"));
}
static void HideOnCloseOverride(const TSharedRef<SWindow>& WindowBeingClosed)
{
WindowBeingClosed->HideWindow();
}
class FBuildStorageToolApp
{
FCriticalSection CriticalSection;
FSlateApplication& Slate;
TSharedPtr<SWindow> Window;
TSharedPtr<SNotificationItem> CompileNotification;
TSharedPtr<UE::Zen::FServiceInstanceManager> ZenServiceInstanceManager;
TSharedPtr<UE::Zen::Build::FServiceInstanceManager> BuildServiceInstanceManager;
const FBuildStorageToolParameters& ToolParameters;
std::atomic<bool> bLatentExclusiveOperationActive = false;
void ExitTool()
{
FSlateApplication::Get().RequestDestroyWindow(Window.ToSharedRef());
}
void OnAboutCommandPressed()
{
FSlateApplication& SlateApplicaton = FSlateApplication::Get();
if (SlateApplicaton.GetActiveModalWindow() != nullptr)
{
return;
}
TSharedPtr<SWidget> ParentWidget = SlateApplicaton.GetUserFocusedWidget(0);
if (!ensure(ParentWidget))
{
return;
}
// Determine the position of the window so that it will spawn near the mouse, but not go off the screen.
const FVector2D CursorPos = FSlateApplication::Get().GetCursorPos();
FSlateRect Anchor(CursorPos.X, CursorPos.Y, CursorPos.X, CursorPos.Y);
FVector2D AdjustedSummonLocation = FSlateApplication::Get().CalculatePopupWindowPosition(Anchor, FVector2D(441, 537), true, FVector2D::ZeroVector, Orient_Horizontal);
TSharedPtr<SWindow> WindowDialog = SNew(SWindow)
.AutoCenter(EAutoCenter::None)
.ScreenPosition(AdjustedSummonLocation)
.SupportsMaximize(false)
.SupportsMinimize(false)
.SizingRule(ESizingRule::Autosized)
.Title(LOCTEXT("HelpAboutHeader", "About"));
WindowDialog->SetContent(SNew(SBuildStorageToolHelpWidget)
.ToolParameters(&ToolParameters)
);
FSlateApplication::Get().AddModalWindow(WindowDialog.ToSharedRef(), ParentWidget);
}
bool CanExecuteExclusiveAction()
{
return !bLatentExclusiveOperationActive;
}
TSharedRef< SWidget > MakeMainMenu()
{
FMenuBarBuilder MenuBuilder( nullptr );
{
MenuBuilder.AddPullDownMenu(
LOCTEXT( "FileMenu", "File" ),
LOCTEXT( "FileMenu_ToolTip", "Opens the file menu" ),
FOnGetContent::CreateRaw( this, &FBuildStorageToolApp::FillFileMenu ) );
MenuBuilder.AddPullDownMenu(
LOCTEXT("ToolsMenu", "Tools"),
LOCTEXT("ToolsMenu_ToolTip", "Opens the tools menu"),
FOnGetContent::CreateRaw(this, &FBuildStorageToolApp::FillToolsMenu) );
MenuBuilder.AddPullDownMenu(
LOCTEXT( "HelpMenu", "Help" ),
LOCTEXT( "HelpMenu_ToolTip", "Opens the help menu" ),
FOnGetContent::CreateRaw( this, &FBuildStorageToolApp::FillHelpMenu ) );
}
// Create the menu bar
TSharedRef< SWidget > MenuBarWidget = MenuBuilder.MakeWidget();
MenuBarWidget->SetVisibility( EVisibility::Visible ); // Work around for menu bar not showing on Mac
return MenuBarWidget;
}
void OpenLogWindow()
{
/*** Output Log Widget ***/
FOutputLogModule& OutputLogModule = FModuleManager::Get().LoadModuleChecked<FOutputLogModule>("OutputLog");
// hide the debug console
IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OutputLogModule.HideConsole"));
if(ensure(CVar))
{
CVar->Set(true);
}
// setup OutputLog settings
UOutputLogSettings* Settings = GetMutableDefault<UOutputLogSettings>();
if(Settings)
{
Settings->CategoryColorizationMode = ELogCategoryColorizationMode::ColorizeWholeLine;
}
// Open new slate window
FSlateApplication::Get().AddWindowAsNativeChild(
SNew(SWindow)
.Title(LOCTEXT("LogWindowTitle", "Build Storage Tool Log"))
.ClientSize(FVector2D(1200.0f, 600.0f))
.ActivationPolicy(EWindowActivationPolicy::Always)
.SizingRule(ESizingRule::UserSized)
.IsTopmostWindow(false)
.FocusWhenFirstShown(false)
.SupportsMaximize(true)
.SupportsMinimize(true)
.HasCloseButton(true)
[
OutputLogModule.MakeOutputLogWidget(FOutputLogCreationParams())
],
Window.ToSharedRef()
);
}
TSharedRef<SWidget> FillToolsMenu()
{
const bool bCloseSelfOnly = false;
const bool bSearchable = false;
const bool bRecursivelySearchable = false;
FMenuBuilder MenuBuilder(true,
nullptr,
TSharedPtr<FExtender>(),
bCloseSelfOnly,
&FCoreStyle::Get(),
bSearchable,
NAME_None,
bRecursivelySearchable);
MenuBuilder.AddMenuEntry(
LOCTEXT("OpenLogWindow", "Open Log Window"),
LOCTEXT("OpenLogWindow_ToolTip", "Opens the Log Window"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateRaw( this, &FBuildStorageToolApp::OpenLogWindow )
),
NAME_None,
EUserInterfaceActionType::Button
);
return MenuBuilder.MakeWidget();
}
TSharedRef<SWidget> FillFileMenu()
{
const bool bCloseSelfOnly = false;
const bool bSearchable = false;
const bool bRecursivelySearchable = false;
FMenuBuilder MenuBuilder(true,
nullptr,
TSharedPtr<FExtender>(),
bCloseSelfOnly,
&FCoreStyle::Get(),
bSearchable,
NAME_None,
bRecursivelySearchable);
MenuBuilder.AddMenuEntry(
LOCTEXT("Exit", "Exit"),
LOCTEXT("Exit_ToolTip", "Exits the Storage Server UI"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateRaw( this, &FBuildStorageToolApp::ExitTool ),
FCanExecuteAction::CreateRaw(this, &FBuildStorageToolApp::CanExecuteExclusiveAction)
),
NAME_None,
EUserInterfaceActionType::Button
);
return MenuBuilder.MakeWidget();
}
TSharedRef<SWidget> FillHelpMenu()
{
const bool bCloseSelfOnly = false;
const bool bSearchable = false;
const bool bRecursivelySearchable = false;
FMenuBuilder MenuBuilder(true,
nullptr,
TSharedPtr<FExtender>(),
bCloseSelfOnly,
&FCoreStyle::Get(),
bSearchable,
NAME_None,
bRecursivelySearchable);
MenuBuilder.AddMenuEntry(
LOCTEXT("About", "About"),
LOCTEXT("About_ToolTip", "Show the about dialog"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateRaw( this, &FBuildStorageToolApp::OnAboutCommandPressed ),
FCanExecuteAction::CreateRaw(this, &FBuildStorageToolApp::CanExecuteExclusiveAction)
),
NAME_None,
EUserInterfaceActionType::Button
);
return MenuBuilder.MakeWidget();
}
public:
FBuildStorageToolApp(FSlateApplication& InSlate, const FBuildStorageToolParameters& InToolParameters)
: Slate(InSlate), ToolParameters(InToolParameters)
{
ZenServiceInstanceManager = MakeShared<UE::Zen::FServiceInstanceManager>();
BuildServiceInstanceManager = MakeShared<UE::Zen::Build::FServiceInstanceManager>();
InstallMessageDialogOverride();
}
virtual ~FBuildStorageToolApp()
{
RemoveMessageDialogOverride();
}
void Run()
{
Window =
SNew(SWindow)
.Title(GetWindowTitle())
.ClientSize(FVector2D(1000.0f, 600.0f))
.ActivationPolicy(EWindowActivationPolicy::Always)
.SizingRule(ESizingRule::UserSized)
.IsTopmostWindow(false)
.FocusWhenFirstShown(false)
.SupportsMaximize(true)
.SupportsMinimize(true)
.HasCloseButton(true)
[
SNew(SBorder)
.BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
// Menu
+ SVerticalBox::Slot()
.AutoHeight()
[
MakeMainMenu()
]
// Login panel
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 10.0f, 5.0f, 0.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
[
SNew(SBuildLogin)
.ZenServiceInstance(ZenServiceInstanceManager.ToSharedRef(), &UE::Zen::FServiceInstanceManager::GetZenServiceInstance)
.BuildServiceInstance(BuildServiceInstanceManager.ToSharedRef(), &UE::Zen::Build::FServiceInstanceManager::GetBuildServiceInstance)
]
// Selection panel
+ SVerticalBox::Slot()
.Padding(0.0f, 10.0f, 5.0f, 10.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SBuildSelection)
.ZenServiceInstance(ZenServiceInstanceManager.ToSharedRef(), &UE::Zen::FServiceInstanceManager::GetZenServiceInstance)
.BuildServiceInstance(BuildServiceInstanceManager.ToSharedRef(), &UE::Zen::Build::FServiceInstanceManager::GetBuildServiceInstance)
.OnBuildTransferStarted_Lambda([this]
(UE::Zen::Build::FBuildServiceInstance::FBuildTransfer Transfer, FStringView Name, FStringView Platform)
{
BuildActivity->AddBuildTransfer(Transfer, Name, Platform);
})
]
// Activity panel
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 10.0f, 5.0f, 0.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Bottom)
[
SAssignNew(BuildActivity, SBuildActivity)
.ZenServiceInstance(ZenServiceInstanceManager.ToSharedRef(), &UE::Zen::FServiceInstanceManager::GetZenServiceInstance)
.BuildServiceInstance(BuildServiceInstanceManager.ToSharedRef(), &UE::Zen::Build::FServiceInstanceManager::GetBuildServiceInstance)
]
]
];
Slate.AddWindow(Window.ToSharedRef(), true);
// Setting focus seems to have to happen after the Window has been added
Slate.ClearKeyboardFocus(EFocusCause::Cleared);
// loop until the app is ready to quit
while (!IsEngineExitRequested())
{
BeginExitIfRequested();
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
FTSTicker::GetCoreTicker().Tick(FApp::GetDeltaTime());
Slate.PumpMessages();
Slate.Tick();
FPlatformProcess::Sleep(1.0f / 30.0f);
}
// Make sure the window is hidden, because it might take a while for the background thread to finish.
Window->HideWindow();
}
private:
FText GetWindowTitle()
{
FText ToolName = LOCTEXT("WindowTitle", "Unreal Build Storage Tool");
#if defined(BUILD_STORAGE_TOOL_CHANGELIST_STRING)
return FText::Format(LOCTEXT("WindowTitle_VersionFormat", "{0} ({1})"), ToolName, FText::FromString(BUILD_STORAGE_TOOL_CHANGELIST_STRING));
#else
return ToolName;
#endif
}
EAppReturnType::Type OnModalMessageDialog(EAppMsgCategory InMessageCategory, EAppMsgType::Type InMessage, const FText& InText, const FText& InTitle)
{
if (IsInGameThread() && FSlateApplication::IsInitialized() && FSlateApplication::Get().CanAddModalWindow())
{
return OpenModalMessageDialog_Internal(InMessageCategory, InMessage, InText, InTitle, Window);
}
else
{
return FPlatformMisc::MessageBoxExt(InMessage, *InText.ToString(), *InTitle.ToString());
}
}
void InstallMessageDialogOverride()
{
FCoreDelegates::ModalMessageDialog.BindRaw(this, &FBuildStorageToolApp::OnModalMessageDialog);
}
void RemoveMessageDialogOverride()
{
FCoreDelegates::ModalMessageDialog.Unbind();
}
TSharedPtr<SBuildActivity> BuildActivity;
};
int BuildStorageToolMain(const TCHAR* CmdLine)
{
ON_SCOPE_EXIT
{
RequestEngineExit(TEXT("Exiting"));
FEngineLoop::AppPreExit();
FModuleManager::Get().UnloadModulesAtShutdown();
FEngineLoop::AppExit();
};
const FTaskTagScope Scope(ETaskTag::EGameThread);
// start up the main loop
GEngineLoop.PreInit(CmdLine);
// ensure that the backlog is enabled
if(GLog)
{
GLog->EnableBacklog(true);
}
FSystemWideCriticalSection SystemWideBuildStorageToolCritSec(TEXT("BuildStorageTool"));
if (!SystemWideBuildStorageToolCritSec.IsValid())
{
return true;
}
check(GConfig && GConfig->IsReadyForUse());
// Initialize high DPI mode
FSlateApplication::InitHighDPI(true);
FBuildStorageToolParametersBuilder ParametersBuilder;
FBuildStorageToolParameters Parameters = ParametersBuilder.Build();
{
// Create the platform slate application (what FSlateApplication::Get() returns)
TSharedRef<FSlateApplication> Slate = FSlateApplication::Create(MakeShareable(FPlatformApplicationMisc::CreateApplication()));
{
// Initialize renderer
TSharedRef<FSlateRenderer> SlateRenderer = GetStandardStandaloneRenderer();
// Try to initialize the renderer. It's possible that we launched when the driver crashed so try a few times before giving up.
bool bRendererInitialized = Slate->InitializeRenderer(SlateRenderer, true);
if (!bRendererInitialized)
{
// Close down the Slate application
FSlateApplication::Shutdown();
return false;
}
// Set the normal UE IsEngineExitRequested() when outer frame is closed
Slate->SetExitRequestedHandler(FSimpleDelegate::CreateStatic(&OnRequestExit));
// Prepare the custom Slate styles
FBuildStorageToolStyle::Initialize();
// Set the icon
FAppStyle::SetAppStyleSet(FBuildStorageToolStyle::Get());
// Run the inner application loop
FBuildStorageToolApp App(Slate.Get(), Parameters);
App.Run();
// Clean up the custom styles
FBuildStorageToolStyle::Shutdown();
}
// Close down the Slate application
FSlateApplication::Shutdown();
}
return true;
}
#if PLATFORM_WINDOWS
int WINAPI WinMain(_In_ HINSTANCE hCurrInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
hInstance = hCurrInstance;
return BuildStorageToolMain(GetCommandLineW())? 0 : 1;
}
#elif PLATFORM_LINUX
int UnixMainWrapper(const TCHAR* CommandLine)
{
return BuildStorageToolMain(CommandLine) ? 0 : 1;
};
int main(int argc, char* argv[])
{
return CommonUnixMain(argc, argv, &UnixMainWrapper);
}
#elif PLATFORM_MAC
int32 MacMainWrapper(const TCHAR* CommandLine)
{
return BuildStorageToolMain(CommandLine) ? 0 : 1;
};
int main(int argc, char* argv[])
{
[MacProgramDelegate mainWithArgc : argc argv : argv programMain : MacMainWrapper programExit : FEngineLoop::AppExit] ;
}
#else
#error "Unsupported platform!"
#endif
#undef LOCTEXT_NAMESPACE