Make `gn args --list` deterministic

This fixes Args::GetAllArguments() to return deterministic results,
even when declare_args() are used in or some of its
imported files.

While this is ugly, this is used by the Fuchsia build in order to
override variables in third-party .gni files. This also creates
two Settings instance that return `is_default() == true`:

- The first is just the regular default toolchain.
- The second one has an empty label, and is used by the scope
  created when parsing the file.

The std::sort() comparator assumed there was only one default
toolchain, this resulted in practice in non-deterministic
outputs for the list of arguments.

This CL fixes the issue by ensuring the comparator places
the empty toolchain first, then the default one second,
followed by all others sorted by their label.

Bug: 327

Change-Id: I342d285a15339572adf72c2ddb29bbd81b814d10
Commit-Queue: David Turner <>
Reviewed-by: Brett Wilson <>
diff --git a/src/gn/ b/src/gn/
index fbc0ee4..3b765f8 100644
--- a/src/gn/
+++ b/src/gn/
@@ -268,25 +268,31 @@
   std::lock_guard<std::mutex> lock(lock_);
-  // Sort the keys from declared_arguments_per_toolchain_ so
+  // Sort the toolchains from declared_arguments_per_toolchain_ so
   // the return value will be deterministic. Always prioritize
   // the default toolchain.
-  std::vector<const Settings*> keys;
-  keys.reserve(declared_arguments_per_toolchain_.size());
+  std::vector<const Settings*> toolchains;
+  toolchains.reserve(declared_arguments_per_toolchain_.size());
   for (const auto& map_pair : declared_arguments_per_toolchain_) {
-    keys.push_back(map_pair.first);
+    toolchains.push_back(map_pair.first);
-  std::sort(keys.begin(), keys.end(),
+  std::sort(toolchains.begin(), toolchains.end(),
             [](const Settings* a, const Settings* b) -> bool {
-              return a->is_default() ||
-                     a->toolchain_label() < b->toolchain_label();
+              // NOTE: There can be multiple default toolchains in the map!
+              // which happens when declare_args() blocks are found in
+              // 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();
   // Default values.
-  for (const auto& key : keys) {
-    const auto& value = declared_arguments_per_toolchain_[key];
-    for (const auto& arg : value)
+  for (const auto& toolchain : toolchains) {
+    const auto& value_map = declared_arguments_per_toolchain_[toolchain];
+    for (const auto& arg : value_map) {
       result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second)));
+    }
   // Merge in overrides.