Optimize OrderOnlyDeps computation using ResolvedTargetData caching

This is a follow-up to https://gn-review.googlesource.com/c/gn/+/22400
to address unresolved review comments.

- Add GetOrderOnlyDeps() to ResolvedTargetData to compute and cache order-only dependencies of targets in a bottom-up manner.
- Generalize this by making group() targets export the order-only dependencies of their direct dependents.
- Refactor NinjaBinaryTargetWriter::GetOrderOnlyDepsFromNonLinkableDeps to use this cached data and simplify duplicate elimination using UniqueVector<OutputFile>.

Bug: 502431091
Change-Id: I6a2486a00bc9efe98629799601e8c149113bbdcb
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/22540
Commit-Queue: Takuto Ikuta <tikuta@google.com>
Reviewed-by: David Turner <digit@google.com>
diff --git a/src/gn/ninja_binary_target_writer.cc b/src/gn/ninja_binary_target_writer.cc
index aad2eb2..b075df3 100644
--- a/src/gn/ninja_binary_target_writer.cc
+++ b/src/gn/ninja_binary_target_writer.cc
@@ -551,56 +551,9 @@
 std::vector<OutputFile>
 NinjaBinaryTargetWriter::GetOrderOnlyDepsFromNonLinkableDeps(
     const UniqueVector<const Target*>& non_linkable_deps) const {
-  std::vector<const Target*> group_stack;
-  std::vector<OutputFile> outputs_to_write;
-  std::set<std::string> seen_outputs;
-
-  auto add_output = [&](const OutputFile& output) {
-    if (seen_outputs.insert(output.value()).second) {
-      outputs_to_write.push_back(output);
-    }
-  };
-
-  auto process_dep = [&](const Target* dep) {
-    if (dep->output_type() == Target::GROUP) {
-      group_stack.push_back(dep);
-    } else if (dep->has_dependency_output()) {
-      OutputFile dep_output = dep->dependency_output();
-      if (dep->output_type() == Target::SOURCE_SET) {
-        dep_output.value().append(".linkdeps");
-      }
-      add_output(dep_output);
-    }
-  };
-
-  for (auto* dep : non_linkable_deps) {
-    process_dep(dep);
+  UniqueVector<OutputFile> outputs_to_write;
+  for (const Target* dep : non_linkable_deps) {
+    outputs_to_write.Append(resolved().GetOrderOnlyDeps(dep));
   }
-
-  // Recursively expand dependencies of groups to avoid unnecessary
-  // dependencies. If a group depends on a source set, we depend on its
-  // .linkdeps instead of the group itself. This prevents including non-object
-  // files (like .dwo files) in order-only dependencies. This is crucial for
-  // remote linking to avoid uploading unnecessary files, which increases data
-  // transfer and could hit file count limits.
-  std::set<const Target*> visited_groups;
-  while (!group_stack.empty()) {
-    const Target* current = group_stack.back();
-    group_stack.pop_back();
-
-    if (!visited_groups.insert(current).second)
-      continue;
-
-    auto add_deps = [&](const LabelTargetVector& deps) {
-      for (const auto& pair : deps) {
-        process_dep(pair.ptr);
-      }
-    };
-
-    add_deps(current->public_deps());
-    add_deps(current->private_deps());
-    add_deps(current->data_deps());
-  }
-
-  return outputs_to_write;
+  return outputs_to_write.release();
 }
diff --git a/src/gn/resolved_target_data.cc b/src/gn/resolved_target_data.cc
index 86ab233..7b4a495 100644
--- a/src/gn/resolved_target_data.cc
+++ b/src/gn/resolved_target_data.cc
@@ -310,3 +310,28 @@
   }
   info->has_swift_values = true;
 }
+
+void ResolvedTargetData::ComputeOrderOnlyDeps(TargetInfo* info) const {
+  UniqueVector<OutputFile> all_order_only_deps;
+  const Target* target = info->target;
+
+  if (target->output_type() == Target::GROUP) {
+    auto add_deps = [&](base::span<const Target*> deps) {
+      for (const Target* dep : deps) {
+        const TargetInfo* dep_info = GetTargetOrderOnlyDeps(dep);
+        all_order_only_deps.Append(dep_info->order_only_deps);
+      }
+    };
+    add_deps(info->deps.public_deps());
+    add_deps(info->deps.private_deps());
+    add_deps(info->deps.data_deps());
+  } else if (target->has_dependency_output()) {
+    OutputFile dep_output = target->dependency_output();
+    if (target->output_type() == Target::SOURCE_SET) {
+      dep_output.value().append(".linkdeps");
+    }
+    all_order_only_deps.push_back(dep_output);
+  }
+
+  info->order_only_deps = all_order_only_deps.release();
+}
diff --git a/src/gn/resolved_target_data.h b/src/gn/resolved_target_data.h
index cc04fdb..a184e3d 100644
--- a/src/gn/resolved_target_data.h
+++ b/src/gn/resolved_target_data.h
@@ -13,6 +13,7 @@
 
 #include "base/containers/span.h"
 #include "gn/lib_file.h"
+#include "gn/output_file.h"
 #include "gn/resolved_target_deps.h"
 #include "gn/source_dir.h"
 #include "gn/target.h"
@@ -145,6 +146,12 @@
     return info->swift_values->modules;
   }
 
+  // Retrieves an ordered list of all order-only dependency outputs for this
+  // target.
+  const std::vector<OutputFile>& GetOrderOnlyDeps(const Target* target) const {
+    return GetTargetOrderOnlyDeps(target)->order_only_deps;
+  }
+
  private:
   // The information associated with a given Target pointer.
   struct TargetInfo {
@@ -167,6 +174,7 @@
     std::atomic<bool> has_module_deps_information = false;
     std::atomic<bool> has_rust_libs = false;
     std::atomic<bool> has_swift_values = false;
+    std::atomic<bool> has_order_only_deps = false;
 
     // Only valid if |has_lib_info| is true.
     std::vector<SourceDir> lib_dirs;
@@ -205,6 +213,9 @@
             public_modules(std::move(public_modules)) {}
     };
     std::unique_ptr<SwiftValues> swift_values;
+
+    // Only valid if |has_order_only_deps| is true.
+    std::vector<OutputFile> order_only_deps;
   };
 
   // Retrieve TargetInfo value associated with |target|. Create
@@ -296,6 +307,18 @@
     return info;
   }
 
+  const TargetInfo* GetTargetOrderOnlyDeps(const Target* target) const {
+    TargetInfo* info = GetTargetInfo(target);
+    if (!info->has_order_only_deps.load(std::memory_order_acquire)) {
+      std::lock_guard<std::mutex> lock(info->mutex);
+      if (!info->has_order_only_deps.load(std::memory_order_relaxed)) {
+        ComputeOrderOnlyDeps(info);
+        info->has_order_only_deps.store(true, std::memory_order_release);
+      }
+    }
+    return info;
+  }
+
   // Compute the portion of TargetInfo guarded by one of the |has_xxx|
   // booleans. This performs recursive and expensive computations and
   // should only be called once per TargetInfo instance.
@@ -306,6 +329,7 @@
   void ComputeModuleDepsInformation(TargetInfo* info) const;
   void ComputeRustLibs(TargetInfo* info) const;
   void ComputeSwiftValues(TargetInfo* info) const;
+  void ComputeOrderOnlyDeps(TargetInfo* info) const;
 
   // Helper function used by ComputeInheritedLibs().
   void ComputeInheritedLibsFor(