[tvos] Search for `kTargetXcodePlatform` from all arguments

This introduces GetArgFromAllArguments() and uses it to
search for `kTargetXcodePlatform` from all arguments.

Bug: chromium:391914246

Change-Id: I990a9609d05e3f154e65116f24e0591229569b2d
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/18920
Commit-Queue: David Turner <digit@google.com>
Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: David Turner <digit@google.com>
diff --git a/src/gn/args.cc b/src/gn/args.cc
index c67f8bc..28c9304 100644
--- a/src/gn/args.cc
+++ b/src/gn/args.cc
@@ -139,6 +139,46 @@
   return &found->second;
 }
 
+std::vector<const Settings*> Args::GetSortedToolchainsLocked() const {
+  std::vector<const Settings*> toolchains;
+  toolchains.reserve(declared_arguments_per_toolchain_.size());
+  for (const auto& map_pair : declared_arguments_per_toolchain_) {
+    toolchains.push_back(map_pair.first);
+  }
+  std::sort(toolchains.begin(), toolchains.end(),
+            [](const Settings* a, const Settings* b) -> bool {
+              // NOTE: There can be multiple default toolchains in the map!
+              // which happens when declare_args() blocks are found in args.gn
+              // or some of its imports. This uses a Settings instance with
+              // an empty label, where `is_default()` returns true.
+              if (a->is_default() != b->is_default())
+                return a->is_default();
+              return a->toolchain_label() < b->toolchain_label();
+            });
+  return toolchains;
+}
+
+std::optional<Value> Args::GetArgFromAllArguments(const char* name) const {
+  // First, look into overrides defined in .gn
+  const Value* override = GetArgOverride(name);
+  if (override)
+    return std::make_optional(*override);
+
+  // Second, look into each toolchain definition, the default one
+  // always appear first here.
+  std::lock_guard<std::mutex> lock(lock_);
+
+  for (const Settings* toolchain : GetSortedToolchainsLocked()) {
+     const auto& value_map = declared_arguments_per_toolchain_[toolchain];
+     auto it = value_map.find(name);
+     if (it != value_map.end())
+       return std::make_optional(it->second);
+  }
+
+  // No match
+  return std::nullopt;
+}
+
 void Args::SetupRootScope(Scope* dest,
                           const Scope::KeyValueMap& toolchain_overrides) const {
   std::lock_guard<std::mutex> lock(lock_);
@@ -273,21 +313,7 @@
   // Sort the toolchains from declared_arguments_per_toolchain_ so
   // the return value will be deterministic. Always prioritize
   // the default toolchain.
-  std::vector<const Settings*> toolchains;
-  toolchains.reserve(declared_arguments_per_toolchain_.size());
-  for (const auto& map_pair : declared_arguments_per_toolchain_) {
-    toolchains.push_back(map_pair.first);
-  }
-  std::sort(toolchains.begin(), toolchains.end(),
-            [](const Settings* a, const Settings* b) -> bool {
-              // NOTE: There can be multiple default toolchains in the map!
-              // which happens when declare_args() blocks are found in args.gn
-              // or some of its imports. This uses a Settings instance with
-              // an empty label, where `is_default()` returns true.
-              if (a->is_default() != b->is_default())
-                return a->is_default();
-              return a->toolchain_label() < b->toolchain_label();
-            });
+  std::vector<const Settings*> toolchains = GetSortedToolchainsLocked();
 
   // Default values.
   for (const auto& toolchain : toolchains) {
diff --git a/src/gn/args.h b/src/gn/args.h
index 2914c59..0bc1821 100644
--- a/src/gn/args.h
+++ b/src/gn/args.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <mutex>
+#include <optional>
 #include <set>
 #include <string_view>
 #include <unordered_map>
@@ -56,6 +57,10 @@
   // argument is set.
   const Value* GetArgOverride(const char* name) const;
 
+  // Similar to above, except it searches for `name` from all arguments. If it
+  // has an override, it returns `override_value`.
+  std::optional<Value> GetArgFromAllArguments(const char* name) const;
+
   // Sets up the root scope for a toolchain. This applies the default system
   // flags and saves the toolchain overrides so they can be applied to
   // declare_args blocks that appear when loading files in that toolchain.
@@ -116,6 +121,10 @@
   // toolchain.
   Scope::KeyValueMap& OverridesForToolchainLocked(Scope* scope) const;
 
+  // Returns toolchains in a deterministic way. Always prioritize
+  // the default toolchain. Requires the lock being acquired.
+  std::vector<const Settings*> GetSortedToolchainsLocked() const;
+
   // Since this is called during setup which we assume is single-threaded,
   // this is not protected by the lock. It should be set only during init.
   Scope::KeyValueMap overrides_;
diff --git a/src/gn/args_unittest.cc b/src/gn/args_unittest.cc
index 650db6d..d2ae970 100644
--- a/src/gn/args_unittest.cc
+++ b/src/gn/args_unittest.cc
@@ -79,3 +79,36 @@
   ASSERT_NE(nullptr, setup.scope()->GetValue("c"));
   EXPECT_EQ(Value(nullptr, "cvalue2"), *setup.scope()->GetValue("c"));
 }
+
+// Ensure that GetArgFromAllArguments() searches for an arg from all arguments.
+TEST(ArgsTest, VerifyGetArgFromAllArguments) {
+  TestWithScope setup;
+  Args args1;
+  Err err;
+
+  Scope::KeyValueMap key_value_map;
+  Value a_value = Value(nullptr, "avalue");
+  key_value_map["a"] = a_value;
+  EXPECT_TRUE(args1.DeclareArgs(key_value_map, setup.scope(), &err));
+
+  // Should not find "a" from overrides.
+  ASSERT_EQ(nullptr, args1.GetArgOverride("a"));
+
+  // Should find "a" from all args as it's declared.
+  EXPECT_EQ(a_value, *args1.GetArgFromAllArguments("a"));
+
+  // Should not find "b" from all args as it's not declared.
+  EXPECT_FALSE(args1.GetArgFromAllArguments("b"));
+
+  Args args2;
+  args2.AddArgOverrides(key_value_map);
+
+  // Should find "a" from overrides.
+  const Value* a_value_from_ovderrides = args2.GetArgOverride("a");
+  ASSERT_NE(nullptr, a_value_from_ovderrides);
+  EXPECT_EQ(a_value, *a_value_from_ovderrides);
+
+  // Should find "a" from all args since GetArgFromAllArguments() includes
+  // overrides.
+  EXPECT_EQ(a_value, *args2.GetArgFromAllArguments("a"));
+}
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc
index ed4b33e..4f4911a 100644
--- a/src/gn/xcode_writer.cc
+++ b/src/gn/xcode_writer.cc
@@ -90,8 +90,9 @@
     const Args& args,
     const ParseNode* node,
     Err* err) {
-  const Value* target_xcode_platform_value =
-      args.GetArgOverride(variables::kTargetXcodePlatform);
+  std::optional<Value> target_xcode_platform_value =
+      args.GetArgFromAllArguments(variables::kTargetXcodePlatform);
+
   if (!target_xcode_platform_value) {
     return WRITER_TARGET_XCODE_PLATFORM_IPHONEOS;
   }