// Copyright Epic Games, Inc. All Rights Reserved. #include "DerivedDataBuildWorkerRegistry.h" #include "Algo/Find.h" #include "Containers/SharedString.h" #include "Containers/StringView.h" #include "Containers/UnrealString.h" #include "DerivedDataBuildKey.h" #include "DerivedDataBuildPrivate.h" #include "DerivedDataBuildWorker.h" #include "Features/IModularFeatures.h" #include "HAL/CriticalSection.h" #include "IO/IoHash.h" #include "Misc/Guid.h" #include "Misc/ScopeRWLock.h" #include "Templates/Function.h" #include "Templates/Tuple.h" namespace UE::DerivedData::Private { class FBuildWorkerInternal final : public FBuildWorker, public FBuildWorkerBuilder { public: inline explicit FBuildWorkerInternal(IBuildWorkerFactory* InFactory) : Factory(InFactory) { } void Build(); inline FStringView GetName() const final { return WorkerName; } inline FStringView GetPath() const final { return WorkerPath; } inline FStringView GetHostPlatform() const final { return HostPlatform; } inline FGuid GetBuildSystemVersion() const final { return BuildSystemVersion; } void FindFileData(TConstArrayView RawHashes, IRequestOwner& Owner, FOnBuildWorkerFileDataComplete&& OnComplete) const final; void IterateFunctions(TFunctionRef Visitor) const final; void IterateFiles(TFunctionRef Visitor) const final; void IterateExecutables(TFunctionRef Visitor) const final; void IterateEnvironment(TFunctionRef Visitor) const final; private: inline void SetName(FStringView Name) final { WorkerName = Name; } inline void SetPath(FStringView Path) final { WorkerPath = Path; } inline void SetHostPlatform(FStringView Name) final { HostPlatform = Name; } inline void SetBuildSystemVersion(const FGuid& Version) final { BuildSystemVersion = Version; } void AddFunction(FUtf8StringView Name, const FGuid& Version) final; void AddFile(FStringView Path, const FIoHash& RawHash, uint64 RawSize) final; void AddExecutable(FStringView Path, const FIoHash& RawHash, uint64 RawSize) final; void SetEnvironment(FStringView Name, FStringView Value) final; private: FString WorkerName; FString WorkerPath; FString HostPlatform; FGuid BuildSystemVersion; TArray> Functions; TArray> Files; TArray> Executables; TArray> Environment; IBuildWorkerFactory* Factory; }; void FBuildWorkerInternal::Build() { Functions.Sort(); Files.Sort(); Executables.Sort(); Environment.Sort(); } void FBuildWorkerInternal::FindFileData(TConstArrayView RawHashes, IRequestOwner& Owner, FOnBuildWorkerFileDataComplete&& OnComplete) const { return Factory->FindFileData(RawHashes, Owner, MoveTemp(OnComplete)); } void FBuildWorkerInternal::IterateFunctions(TFunctionRef Visitor) const { for (const TTuple& Function : Functions) { Function.ApplyAfter(Visitor); } } void FBuildWorkerInternal::IterateFiles(TFunctionRef Visitor) const { for (const TTuple& File : Files) { File.ApplyAfter(Visitor); } } void FBuildWorkerInternal::IterateExecutables(TFunctionRef Visitor) const { for (const TTuple& Executable : Executables) { Executable.ApplyAfter(Visitor); } } void FBuildWorkerInternal::IterateEnvironment(TFunctionRef Visitor) const { for (const TTuple& Variable : Environment) { Variable.ApplyAfter(Visitor); } } void FBuildWorkerInternal::AddFunction(FUtf8StringView Name, const FGuid& Version) { UE_CLOG(!Version.IsValid(), LogDerivedDataBuild, Error, TEXT("Version of zero is not allowed in build function with the name %s in build worker '%s'."), *WriteToString<32>(Name), *WorkerName); Functions.Emplace(Name, Version); } void FBuildWorkerInternal::AddFile(FStringView Path, const FIoHash& RawHash, uint64 RawSize) { Files.Emplace(Path, RawHash, RawSize); } void FBuildWorkerInternal::AddExecutable(FStringView Path, const FIoHash& RawHash, uint64 RawSize) { Executables.Emplace(Path, RawHash, RawSize); } void FBuildWorkerInternal::SetEnvironment(FStringView Name, FStringView Value) { Environment.Emplace(Name, Value); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class FBuildWorkerRegistry final : public IBuildWorkerRegistry { public: FBuildWorkerRegistry(); ~FBuildWorkerRegistry(); FBuildWorker* FindWorker( const FUtf8SharedString& Function, const FGuid& FunctionVersion, const FGuid& BuildSystemVersion, IBuildWorkerExecutor*& OutWorkerExecutor) const final; private: void OnModularFeatureRegistered(const FName& Type, IModularFeature* ModularFeature); void OnModularFeatureUnregistered(const FName& Type, IModularFeature* ModularFeature); void AddWorker(IBuildWorkerFactory* Factory); void RemoveWorker(IBuildWorkerFactory* Factory); private: mutable FRWLock Lock; IBuildWorkerExecutor* Executor = nullptr; TMap> Workers; TMultiMap, FBuildWorker*> Functions; }; FBuildWorkerRegistry::FBuildWorkerRegistry() { IModularFeatures& ModularFeatures = IModularFeatures::Get(); if (ModularFeatures.IsModularFeatureAvailable(IBuildWorkerExecutor::FeatureName)) { Executor = &ModularFeatures.GetModularFeature(IBuildWorkerExecutor::FeatureName); } for (IBuildWorkerFactory* Worker : ModularFeatures.GetModularFeatureImplementations(IBuildWorkerFactory::FeatureName)) { AddWorker(Worker); } ModularFeatures.OnModularFeatureRegistered().AddRaw(this, &FBuildWorkerRegistry::OnModularFeatureRegistered); ModularFeatures.OnModularFeatureUnregistered().AddRaw(this, &FBuildWorkerRegistry::OnModularFeatureUnregistered); } FBuildWorkerRegistry::~FBuildWorkerRegistry() { IModularFeatures& ModularFeatures = IModularFeatures::Get(); ModularFeatures.OnModularFeatureUnregistered().RemoveAll(this); ModularFeatures.OnModularFeatureRegistered().RemoveAll(this); } void FBuildWorkerRegistry::OnModularFeatureRegistered(const FName& Type, IModularFeature* ModularFeature) { if (!Executor && Type == IBuildWorkerExecutor::FeatureName) { FWriteScopeLock WriteLock(Lock); Executor = static_cast(ModularFeature); } else if (Type == IBuildWorkerFactory::FeatureName) { AddWorker(static_cast(ModularFeature)); } } void FBuildWorkerRegistry::OnModularFeatureUnregistered(const FName& Type, IModularFeature* ModularFeature) { if (Executor == ModularFeature && Type == IBuildWorkerExecutor::FeatureName) { IModularFeature* NextExecutor = nullptr; IModularFeatures& ModularFeatures = IModularFeatures::Get(); if (ModularFeatures.IsModularFeatureAvailable(IBuildWorkerExecutor::FeatureName)) { NextExecutor = &ModularFeatures.GetModularFeature(IBuildWorkerExecutor::FeatureName); } FWriteScopeLock WriteLock(Lock); Executor = static_cast(NextExecutor); } else if (Type == IBuildWorkerFactory::FeatureName) { RemoveWorker(static_cast(ModularFeature)); } } void FBuildWorkerRegistry::AddWorker(IBuildWorkerFactory* Factory) { TUniquePtr Worker = MakeUnique(Factory); Factory->Build(*Worker); Worker->Build(); FWriteScopeLock WriteLock(Lock); Worker->IterateFunctions([this, Worker = Worker.Get()](FUtf8StringView Name, const FGuid& Version) { Functions.Emplace(MakeTuple(FUtf8SharedString(Name), Version), Worker); }); Workers.Emplace(Factory, MoveTemp(Worker)); } void FBuildWorkerRegistry::RemoveWorker(IBuildWorkerFactory* Factory) { FWriteScopeLock WriteLock(Lock); TUniquePtr& Worker = Workers.FindChecked(Factory); Worker->IterateFunctions([this, Worker = Worker.Get()](FUtf8StringView Name, const FGuid& Version) { Functions.Remove(MakeTuple(FUtf8SharedString(Name), Version), Worker); }); Workers.Remove(Factory); } FBuildWorker* FBuildWorkerRegistry::FindWorker( const FUtf8SharedString& Function, const FGuid& FunctionVersion, const FGuid& BuildSystemVersion, IBuildWorkerExecutor*& OutWorkerExecutor) const { FReadScopeLock ReadLock(Lock); if (Executor) { TConstArrayView ExecutorHostPlatforms = Executor->GetHostPlatforms(); TArray> FunctionWorkers; Functions.MultiFind(MakeTuple(Function, FunctionVersion), FunctionWorkers); for (FBuildWorker* Worker : FunctionWorkers) { if (Worker->GetBuildSystemVersion() == BuildSystemVersion && Algo::Find(ExecutorHostPlatforms, Worker->GetHostPlatform())) { OutWorkerExecutor = Executor; return Worker; } } } OutWorkerExecutor = nullptr; return nullptr; } IBuildWorkerRegistry* CreateBuildWorkerRegistry() { return new FBuildWorkerRegistry(); } } // UE::DerivedData::Private