// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License. #include "AssetRegistry/DirectiveUtilDependencyCycleFinder.h" namespace DirectiveUtilitiesEditor { FDependencyCycleFinder::FDependencyCycleFinder(const TMap>& InGraph) : Graph(InGraph) { } TArray FDependencyCycleFinder::Find() const { const TArray FinishOrder = BuildFinishOrder(); const TMap> ReverseGraph = BuildReverseGraph(); TSet Visited; TArray Cycles; for (int32 Index = FinishOrder.Num() - 1; Index >= 0; --Index) { const FName Root = FinishOrder[Index]; if (Visited.Contains(Root)) { continue; } FDirectiveUtilAssetDependencyCycle Cycle; TArray Pending = {Root}; Visited.Add(Root); while (!Pending.IsEmpty()) { const FName Package = Pending.Pop(EAllowShrinking::No); Cycle.Packages.Add(Package); const TArray* Referencers = ReverseGraph.Find(Package); if (!Referencers) { continue; } for (const FName Referencer : *Referencers) { if (!Visited.Contains(Referencer)) { Visited.Add(Referencer); Pending.Add(Referencer); } } } const TArray* RootDependencies = Cycle.Packages.Num() == 1 ? Graph.Find(Cycle.Packages[0]) : nullptr; const bool bSelfCycle = RootDependencies && RootDependencies->Contains(Cycle.Packages[0]); if (Cycle.Packages.Num() > 1 || bSelfCycle) { Cycle.Packages.Sort(FNameLexicalLess()); Cycles.Add(MoveTemp(Cycle)); } } Cycles.Sort([](const FDirectiveUtilAssetDependencyCycle& Left, const FDirectiveUtilAssetDependencyCycle& Right) { return Left.Packages[0].LexicalLess(Right.Packages[0]); }); return Cycles; } TArray FDependencyCycleFinder::BuildFinishOrder() const { TArray Packages; Graph.GetKeys(Packages); Packages.Sort(FNameLexicalLess()); TArray FinishOrder; FinishOrder.Reserve(Packages.Num()); TSet Visited; for (const FName Root : Packages) { if (Visited.Contains(Root)) { continue; } TArray Pending = {{Root, 0}}; Visited.Add(Root); while (!Pending.IsEmpty()) { FTraversalFrame& Frame = Pending.Last(); const TArray* Dependencies = Graph.Find(Frame.Package); if (Dependencies && Frame.NextDependencyIndex < Dependencies->Num()) { const FName Dependency = (*Dependencies)[Frame.NextDependencyIndex++]; if (!Visited.Contains(Dependency)) { Visited.Add(Dependency); Pending.Add({Dependency, 0}); } continue; } FinishOrder.Add(Frame.Package); Pending.Pop(EAllowShrinking::No); } } return FinishOrder; } TMap> FDependencyCycleFinder::BuildReverseGraph() const { TMap> ReverseGraph; for (const TPair>& Node : Graph) { ReverseGraph.FindOrAdd(Node.Key); for (const FName Dependency : Node.Value) { ReverseGraph.FindOrAdd(Dependency).Add(Node.Key); } } for (TPair>& Node : ReverseGraph) { Node.Value.Sort(FNameLexicalLess()); } return ReverseGraph; } }