Files
UnrealEngine/Engine/Source/Programs/UnrealBuildAccelerator/Common/Private/UbaProcessStartInfoHolder.cpp
2025-05-18 13:04:45 +08:00

168 lines
5.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UbaProcessStartInfoHolder.h"
#include "UbaConfig.h"
namespace uba
{
bool ProcessStartInfoHolder::Expand()
{
#if PLATFORM_WINDOWS
// Special handling to avoid calling cmd.exe if not needed
if (!Contains(application, TC("cmd.exe")))
return false;
const tchar* argsBegin = argumentsStr.c_str();
// Check if application is repeated as first argument, in that case consume it
const tchar* firstArgBegin = argsBegin;
const tchar* firstArgEnd = nullptr;
if (*firstArgBegin == '\"')
{
++firstArgBegin;
firstArgEnd = TStrchr(firstArgBegin, '\"');
}
else
firstArgEnd = TStrchr(firstArgBegin, ' ');
if (!firstArgEnd)
return false;
return InternalExpand(firstArgBegin, firstArgEnd);
#else
return false;
#endif
}
UBA_NOINLINE bool ProcessStartInfoHolder::InternalExpand(const tchar* firstArgBegin, const tchar* firstArgEnd)
{
// Separate function to hide away large stack object
#if PLATFORM_WINDOWS
const tchar* argsBegin = argumentsStr.c_str();
const tchar* argsEnd = argsBegin + argumentsStr.size();
StringBuffer<32*1024> commands;
commands.Append(firstArgBegin, firstArgEnd - firstArgBegin);
if (commands.Contains(application))
argsBegin = firstArgEnd;
while (argsBegin && *argsBegin == ' ')
++argsBegin;
commands.Clear();
// Parse switches... only supported is /C right now
if (!StartsWith(argsBegin, TC("/C ")))
return false;
argsBegin += 3;
while (argsBegin && *argsBegin == ' ')
++argsBegin;
if (argsBegin && *argsBegin == '/') // Unknown switch, don't try to expand cmd
return false;
if (argsBegin && *argsBegin == '\"')
{
++argsBegin;
--argsEnd;
}
while (argsBegin && *argsBegin == ' ')
++argsBegin;
commands.Append(argsBegin, argsEnd - argsBegin);
// It could be that there is just a chain of commands to set working dir, in that case we strip out cmd.exe
const tchar* andPos = nullptr;
if (commands.Contains(TC(" && "), true, &andPos))
{
if (Contains(andPos + 4, TC(" && "))) // If more than one && we don't try to expand cmd.exe
return false;
if (!commands.StartsWith(TC("cd /D"))) // First command is not cd, don't try to expand cmd.exe
return false;
const tchar* workDirStart = commands.data + 6;
workingDirStr.assign(workDirStart, andPos - workDirStart);
StringBuffer<> fixed;
FixPath(workingDirStr.data(), nullptr, 0, fixed);
workingDirStr = fixed.data;
workingDir = workingDirStr.c_str();
const tchar* commandLine = andPos + 4;
while (*commandLine && *commandLine == ' ')
++commandLine;
const tchar* applicationBegin = commandLine;
const tchar* applicationEnd = nullptr;
if (*applicationBegin == '\"')
{
++applicationBegin;
applicationEnd = TStrchr(applicationBegin, '\"');
}
else
applicationEnd = TStrchr(applicationBegin, ' ');
if (!applicationEnd)
applicationEnd = applicationBegin + TStrlen(applicationBegin);
applicationStr.assign(applicationBegin, applicationEnd - applicationBegin);
FixPath(applicationStr.data(), nullptr, 0, fixed.Clear());
applicationStr = fixed.data;
application = applicationStr.c_str();
argsBegin = *applicationEnd == '\"' ? applicationEnd + 1 : applicationEnd;
while (*argsBegin && *argsBegin == ' ')
++argsBegin;
argumentsStr = argsBegin;
arguments = argumentsStr.c_str();
return true;
}
else
{
// TODO: This is super hacky... but we don't want to spawn the cmd.exe just to copy a file since the overhead can be half a second
// "C:\WINDOWS\system32\cmd.exe" /C "copy /Y "E:\dev\fn\Engine\Source\Runtime\RenderCore\RenderCore.natvis" "E:\dev\fn\Engine\Intermediate\Build\Win64\x64\UnrealPak\Development\RenderCore\RenderCore.natvis" 1>nul"
if (!commands.StartsWith(TC("copy /Y \"")))
return false;
const tchar* fromFileBegin = commands.data + 9;
const tchar* fromFileEnd = TStrchr(fromFileBegin, '\"');
if (!fromFileEnd)
return false;
const tchar* toFileBegin = TStrchr(fromFileEnd + 1, '\"');
if (!toFileBegin)
return false;
++toFileBegin;
const tchar* toFileEnd = TStrchr(toFileBegin, '\"');
if (!toFileEnd)
return false;
applicationStr = TC("ubacopy");
application = applicationStr.c_str();
argumentsStr = fromFileBegin;
arguments = argumentsStr.c_str();
return true;
}
#endif
return false;
}
void ProcessStartInfoHolder::Apply(const Config& config, const uba::tchar* configTable)
{
const ConfigTable* tablePtr = config.GetTable(configTable);
if (!tablePtr)
return;
const ConfigTable& table = *tablePtr;
if (table.GetValueAsString(applicationStr, TC("Application")))
application = applicationStr.c_str();
if (table.GetValueAsString(argumentsStr, TC("Arguments")))
arguments = argumentsStr.c_str();
if (table.GetValueAsString(workingDirStr, TC("WorkingDir")))
workingDir = workingDirStr.c_str();
if (table.GetValueAsString(descriptionStr, TC("Description")))
description = descriptionStr.c_str();
if (table.GetValueAsString(logFileStr, TC("LogFile")))
logFile = logFileStr.c_str();
if (table.GetValueAsString(breadcrumbsStr, TC("Breadcrumbs")))
breadcrumbs = breadcrumbsStr.c_str();
table.GetValueAsU32(priorityClass, TC("PriorityClass"));
table.GetValueAsBool(trackInputs, TC("TrackInputs"));
table.GetValueAsBool(useCustomAllocator, TC("UseCustomAllocator"));
table.GetValueAsBool(writeOutputFilesOnFail, TC("WriteOutputFilesOnFail"));
table.GetValueAsBool(startSuspended, TC("StartSuspended"));
table.GetValueAsU64(rootsHandle, TC("RootsHandle"));
}
}