Files
UnrealEngineUWP/Engine/Source/Developer/Virtualization/Private/VirtualizationSourceControlBackend.cpp

174 lines
6.7 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Virtualization/IVirtualizationBackend.h"
#include "ISourceControlModule.h"
#include "ISourceControlProvider.h"
#include "Misc/Parse.h"
#include "SourceControlOperations.h"
#include "Virtualization/PayloadId.h"
#include "VirtualizationSourceControlUtilities.h"
#include "VirtualizationUtilities.h"
// When the SourceControl module (or at least the perforce source control module) is thread safe we
// can enable this and stop using the hacky work around 'TryToDownloadFileFromBackgroundThread'
#define IS_SOURCE_CONTROL_THREAD_SAFE 0
namespace UE::Virtualization
{
/**
* This backend can be used to access payloads stored in source control.
* The backend doesn't 'check out' a payload file but instead will just download the payload as
* a binary blob.
* It is assumed that the files are stored with the same path convention as the file system
* backend, found in Utils::PayloadIdToPath.
*
* Ini file setup:
* 'Name'=(Type=SourceControl, DepotRoot="//XXX/")
* Where 'Name' is the backend name in the hierarchy and 'XXX' is the path in the source control
* depot where the payload files are being stored.
*/
class FSourceControlBackend : public IVirtualizationBackend
{
public:
FSourceControlBackend(FStringView ConfigName)
: IVirtualizationBackend(EOperations::Pull)
{
}
virtual bool Initialize(const FString& ConfigEntry) override
{
TRACE_CPUPROFILER_EVENT_SCOPE(FSourceControlBackend::Initialize);
// We require that a valid depot root has been provided
if (!FParse::Value(*ConfigEntry, TEXT("DepotRoot="), DepotRoot))
{
UE_LOG(LogVirtualization, Error, TEXT("'DepotRoot=' not found in the config file"));
return false;
}
ISourceControlModule& SSCModule = ISourceControlModule::Get();
// We require perforce as the source control provider as it is currently the only one that has the virtualization functionality implemented
const FName SourceControlName = SSCModule.GetProvider().GetName();
if (SourceControlName.IsNone())
{
// No source control provider is set so we can try to set it to "Perforce"
// Note this call will fatal error if "Perforce" is not a valid option
SSCModule.SetProvider(FName("Perforce"));
}
else if (SourceControlName != TEXT("Perforce"))
{
UE_LOG(LogVirtualization, Error, TEXT("Attempting to initialize FSourceControlBackend but source control is '%s' and only Perforce is currently supported!"), *SourceControlName.ToString());
return false;
}
ISourceControlProvider& SCCProvider = SSCModule.GetProvider();
if (!SCCProvider.IsAvailable())
{
SCCProvider.Init();
}
// When a source control depot is set up a file named 'payload_metainfo.txt' should be submitted to it's root.
// This allows us to check for the existence of the file to confirm that the depot root is indeed valid.
const FString PayloadMetaInfoPath = FString::Printf(TEXT("%spayload_metainfo.txt"), *DepotRoot);
#if IS_SOURCE_CONTROL_THREAD_SAFE
TSharedRef<FDownloadFile, ESPMode::ThreadSafe> DownloadCommand = ISourceControlOperation::Create<FDownloadFile>();
if (SCCProvider.Execute(DownloadCommand, PayloadMetaInfoPath, EConcurrency::Synchronous) != ECommandResult::Succeeded)
{
UE_LOG(LogVirtualization, Error, TEXT("Failed to find 'payload_metainfo.txt' in the depot '%s', is your config set up correctly?"), *DepotRoot);
return false;
}
#else
TSharedRef<FDownloadFile, ESPMode::ThreadSafe> DownloadCommand = ISourceControlOperation::Create<FDownloadFile>();
if (!SCCProvider.TryToDownloadFileFromBackgroundThread(DownloadCommand, PayloadMetaInfoPath))
{
UE_LOG(LogVirtualization, Error, TEXT("Failed to find 'payload_metainfo.txt' in the depot '%s', is your config set up correctly?"), *DepotRoot);
return false;
}
#endif //IS_SOURCE_CONTROL_THREAD_SAFE
FSharedBuffer MetaInfoBuffer = DownloadCommand->GetFileData(PayloadMetaInfoPath);
if (MetaInfoBuffer.IsNull())
{
UE_LOG(LogVirtualization, Error, TEXT("Failed to find 'payload_metainfo.txt' in the depot '%s', is your config set up correctly?"), *DepotRoot);
return false;
}
// Currently we do not do anything with the payload meta info, in the future we could structure
// it's format to include more information that might be worth logging or something.
// But for now being able to pull the payload meta info path at least shows that we can use the
// depot.
return true;
}
virtual EPushResult PushData(const FPayloadId& Id, const FCompressedBuffer& Payload) override
{
// This backend will not actually push data to source control, that will be done by
// a separate submission tool. Since files submitted to source control are there forever
// we don't want the user to be uploaded new entries each time that they save the asset
// but instead upload once when the package is committed to source control.
// TODO: As we put the pieces together it is likely that we will change the backend API
// so that we don't need dummy implementations of PushData for backends that don't need it.
// Especially considering that it is most likely that no backend will push.
checkNoEntry();
return EPushResult::Failed;
}
virtual FCompressedBuffer PullData(const FPayloadId& Id) override
{
TRACE_CPUPROFILER_EVENT_SCOPE(FSourceControlBackend::PullData);
TStringBuilder<512> DepotPath;
CreateDepotPath(Id, DepotPath);
ISourceControlProvider& SCCProvider = ISourceControlModule::Get().GetProvider();
#if IS_SOURCE_CONTROL_THREAD_SAFE
Improve error handling and logging in the file and jupiter Mirage backends. #rb Per.Larsson #rnx #preflight 60f9212d1f926d0001d41223 * VirtualizationJupiterBackend - When pulling a payload we should assume that a 400 error response when trying to GET the payload header means that the payload is not in Jupiter. -- Not being able to find the payload should not log an error, instead we can make a note of it in the verbose log (similar to the file system backend) * VirtualizationFileBackend - Moved the formatting of system errors to it's own function. - Log the system error when failing to write a payload during a push as well as a pull. - We now check that the FileArchive wrote correctly to disk and delete the output file and fail the push if it did not. -- A future piece of work will change the logic to write to a tmp file at the root of the file store and them move the file to the final location to cut down on the potential of leaving corrupted files around (similar to the process when we save packages) * Perforce - The FDownloadFile command now takes an optional parameter EVerbosity that can allow the caller to choose the level of logging output that the command will generate. - The source control backend for Mirage now opts to supress the logging of the full perforce command when we are pulling payloads as we can generate many hundreds or thousands of requests and the info is not useful to users. -- We continue to log the command when validating the depot as this is the most likely command to fail so having the info in the log may prove useful. #ROBOMERGE-SOURCE: CL 16921815 in //UE5/Main/... #ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v836-16769935) [CL 16921818 by paul chipchase in ue5-release-engine-test branch]
2021-07-22 05:45:28 -04:00
TSharedRef<FDownloadFile, ESPMode::ThreadSafe> DownloadCommand = ISourceControlOperation::Create<FDownloadFile>(FDownloadFile::EVerbosity::None);
if (SCCProvider.Execute(DownloadCommand, DepotPath.ToString(), EConcurrency::Synchronous) != ECommandResult::Succeeded)
{
return FCompressedBuffer();
}
#else
Improve error handling and logging in the file and jupiter Mirage backends. #rb Per.Larsson #rnx #preflight 60f9212d1f926d0001d41223 * VirtualizationJupiterBackend - When pulling a payload we should assume that a 400 error response when trying to GET the payload header means that the payload is not in Jupiter. -- Not being able to find the payload should not log an error, instead we can make a note of it in the verbose log (similar to the file system backend) * VirtualizationFileBackend - Moved the formatting of system errors to it's own function. - Log the system error when failing to write a payload during a push as well as a pull. - We now check that the FileArchive wrote correctly to disk and delete the output file and fail the push if it did not. -- A future piece of work will change the logic to write to a tmp file at the root of the file store and them move the file to the final location to cut down on the potential of leaving corrupted files around (similar to the process when we save packages) * Perforce - The FDownloadFile command now takes an optional parameter EVerbosity that can allow the caller to choose the level of logging output that the command will generate. - The source control backend for Mirage now opts to supress the logging of the full perforce command when we are pulling payloads as we can generate many hundreds or thousands of requests and the info is not useful to users. -- We continue to log the command when validating the depot as this is the most likely command to fail so having the info in the log may prove useful. #ROBOMERGE-SOURCE: CL 16921815 in //UE5/Main/... #ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v836-16769935) [CL 16921818 by paul chipchase in ue5-release-engine-test branch]
2021-07-22 05:45:28 -04:00
TSharedRef<FDownloadFile> DownloadCommand = ISourceControlOperation::Create<FDownloadFile>(FDownloadFile::EVerbosity::None);
if (!SCCProvider.TryToDownloadFileFromBackgroundThread(DownloadCommand, DepotPath.ToString()))
{
return FCompressedBuffer();
}
#endif
// The payload was created by FCompressedBuffer::Compress so we can return it
// as a FCompressedBuffer.
FSharedBuffer Buffer = DownloadCommand->GetFileData(DepotPath);
return FCompressedBuffer::FromCompressed(Buffer);
}
virtual FString GetDebugString() const override
{
return FString(TEXT("SourceControl"));
}
void CreateDepotPath(const FPayloadId& PayloadId, FStringBuilderBase& OutPath)
{
TStringBuilder<52> PayloadPath;
Utils::PayloadIdToPath(PayloadId, PayloadPath);
OutPath << DepotRoot << PayloadPath;
}
private:
/** The root where the virtualized payloads are stored in source control */
FString DepotRoot;
};
UE_REGISTER_VIRTUALIZATION_BACKEND_FACTORY(FSourceControlBackend, SourceControl);
} // namespace UE::Virtualization