[metadata] Adding metadata output to gn desc

Converts the metadata scope to a base::DictionaryValue and outputs it as
the 'metadata' key in `gn desc`.

Also does a minor refactoring of desc so that single-value requests are
dispatched to the appropriate handler (no impact on performance or
output, other than allowing the metadata to be printed the same in both
single- and multi-value contexts).

Change-Id: If0beb25cdc0f05e9140e4768891bfbbac444ecc6
Reviewed-on: https://gn-review.googlesource.com/c/3301
Commit-Queue: Julie Hockett <juliehockett@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index 2531c7c..9dd38c8 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -11,6 +11,7 @@
 
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "tools/gn/commands.h"
 #include "tools/gn/config.h"
@@ -30,6 +31,52 @@
 const char kTree[] = "tree";
 const char kAll[] = "all";
 
+void PrintDictValue(const base::Value* value,
+                    int indentLevel,
+                    bool use_first_indent) {
+  std::string indent(indentLevel * 2, ' ');
+  const base::ListValue* list_value = nullptr;
+  const base::DictionaryValue* dict_value = nullptr;
+  std::string string_value;
+  bool bool_value = false;
+  int int_value = 0;
+  if (use_first_indent)
+    OutputString(indent);
+  if (value->GetAsList(&list_value)) {
+    OutputString("[\n");
+    bool first = true;
+    for (const auto& v : *list_value) {
+      if (!first)
+        OutputString(",\n");
+      PrintDictValue(&v, indentLevel + 1, true);
+      first = false;
+    }
+    OutputString("\n" + indent + "]");
+  } else if (value->GetAsString(&string_value)) {
+    OutputString("\"" + string_value + "\"");
+  } else if (value->GetAsBoolean(&bool_value)) {
+    OutputString(bool_value ? "true" : "false");
+  } else if (value->GetAsDictionary(&dict_value)) {
+    OutputString("{\n");
+    std::string indent_plus_one((indentLevel + 1) * 2, ' ');
+    base::DictionaryValue::Iterator iter(*dict_value);
+    bool first = true;
+    while (!iter.IsAtEnd()) {
+      if (!first)
+        OutputString(",\n");
+      OutputString(indent_plus_one + iter.key() + " = ");
+      PrintDictValue(&iter.value(), indentLevel + 1, false);
+      iter.Advance();
+      first = false;
+    }
+    OutputString("\n" + indent + "}");
+  } else if (value->GetAsInteger(&int_value)) {
+    OutputString(base::IntToString(int_value));
+  } else if (value->is_none()) {
+    OutputString("<null>");
+  }
+}
+
 // Prints value with specified indentation level
 void PrintValue(const base::Value* value, int indentLevel) {
   std::string indent(indentLevel * 2, ' ');
@@ -37,6 +84,7 @@
   const base::DictionaryValue* dict_value = nullptr;
   std::string string_value;
   bool bool_value = false;
+  int int_value = 0;
   if (value->GetAsList(&list_value)) {
     for (const auto& v : *list_value) {
       PrintValue(&v, indentLevel);
@@ -56,13 +104,23 @@
       PrintValue(&iter.value(), indentLevel + 1);
       iter.Advance();
     }
+  } else if (value->GetAsInteger(&int_value)) {
+    OutputString(indent);
+    OutputString(base::IntToString(int_value));
+    OutputString("\n");
   } else if (value->is_none()) {
     OutputString(indent + "<null>\n");
   }
 }
 
 // Default handler for property
-void DefaultHandler(const std::string& name, const base::Value* value) {
+void DefaultHandler(const std::string& name,
+                    const base::Value* value,
+                    bool value_only) {
+  if (value_only) {
+    PrintValue(value, 0);
+    return;
+  }
   OutputString("\n");
   OutputString(name);
   OutputString("\n");
@@ -71,9 +129,32 @@
 
 // Specific handler for properties that need different treatment
 
+// Prints the dict in GN scope-sytle.
+void MetadataHandler(const std::string& name,
+                     const base::Value* value,
+                     bool value_only) {
+  if (value_only) {
+    PrintDictValue(value, 0, true);
+    OutputString("\n");
+    return;
+  }
+  OutputString("\n");
+  OutputString(name);
+  OutputString("\n");
+  PrintDictValue(value, 1, true);
+  OutputString("\n");
+}
+
 // Prints label and property value on one line, capitalizing the label.
-void LabelHandler(std::string name, const base::Value* value) {
-  name[0] = base::ToUpperASCII(name[0]);
+void LabelHandler(const std::string& name,
+                  const base::Value* value,
+                  bool value_only) {
+  if (value_only) {
+    PrintValue(value, 0);
+    return;
+  }
+  std::string label = name;
+  label[0] = base::ToUpperASCII(label[0]);
   std::string string_value;
   if (value->GetAsString(&string_value)) {
     OutputString(name + ": ", DECORATION_YELLOW);
@@ -81,51 +162,68 @@
   }
 }
 
-void VisibilityHandler(const std::string& name, const base::Value* value) {
+void VisibilityHandler(const std::string& name,
+                       const base::Value* value,
+                       bool value_only) {
+  if (value_only) {
+    PrintValue(value, 0);
+    return;
+  }
   const base::ListValue* list;
   if (value->GetAsList(&list)) {
     if (list->empty()) {
       base::Value str("(no visibility)");
-      DefaultHandler(name, &str);
+      DefaultHandler(name, &str, value_only);
     } else {
-      DefaultHandler(name, value);
+      DefaultHandler(name, value, value_only);
     }
   }
 }
 
-void PublicHandler(const std::string& name, const base::Value* value) {
+void PublicHandler(const std::string& name,
+                   const base::Value* value,
+                   bool value_only) {
+  if (value_only) {
+    PrintValue(value, 0);
+    return;
+  }
   std::string p;
   if (value->GetAsString(&p)) {
     if (p == "*") {
       base::Value str("[All headers listed in the sources are public.]");
-      DefaultHandler(name, &str);
+      DefaultHandler(name, &str, value_only);
       return;
     }
   }
-  DefaultHandler(name, value);
+  DefaultHandler(name, value, value_only);
 }
 
-void ConfigsHandler(const std::string& name, const base::Value* value) {
+void ConfigsHandler(const std::string& name,
+                    const base::Value* value,
+                    bool value_only) {
   bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
   if (tree)
-    DefaultHandler(name + " tree (in order applying)", value);
+    DefaultHandler(name + " tree (in order applying)", value, value_only);
   else
-    DefaultHandler(name + " (in order applying, try also --tree)", value);
+    DefaultHandler(name + " (in order applying, try also --tree)", value,
+                   value_only);
 }
 
-void DepsHandler(const std::string& name, const base::Value* value) {
+void DepsHandler(const std::string& name,
+                 const base::Value* value,
+                 bool value_only) {
   bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
   bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
   if (tree) {
-    DefaultHandler("Dependency tree", value);
+    DefaultHandler("Dependency tree", value, value_only);
   } else {
     if (!all) {
       DefaultHandler(
           "Direct dependencies "
           "(try also \"--all\", \"--tree\", or even \"--all --tree\")",
-          value);
+          value, value_only);
     } else {
-      DefaultHandler("All recursive dependencies", value);
+      DefaultHandler("All recursive dependencies", value, value_only);
     }
   }
 }
@@ -154,9 +252,59 @@
   }
 }
 
+using DescHandlerFunc = void (*)(const std::string& name,
+                                 const base::Value* value,
+                                 bool value_only);
+std::map<std::string, DescHandlerFunc> GetHandlers() {
+  return {{"type", LabelHandler},
+          {"toolchain", LabelHandler},
+          {variables::kVisibility, VisibilityHandler},
+          {variables::kMetadata, MetadataHandler},
+          {variables::kTestonly, DefaultHandler},
+          {variables::kCheckIncludes, DefaultHandler},
+          {variables::kAllowCircularIncludesFrom, DefaultHandler},
+          {variables::kSources, DefaultHandler},
+          {variables::kPublic, PublicHandler},
+          {variables::kInputs, DefaultHandler},
+          {variables::kConfigs, ConfigsHandler},
+          {variables::kPublicConfigs, ConfigsHandler},
+          {variables::kAllDependentConfigs, ConfigsHandler},
+          {variables::kScript, DefaultHandler},
+          {variables::kArgs, DefaultHandler},
+          {variables::kDepfile, DefaultHandler},
+          {"bundle_data", DefaultHandler},
+          {variables::kArflags, DefaultHandler},
+          {variables::kAsmflags, DefaultHandler},
+          {variables::kCflags, DefaultHandler},
+          {variables::kCflagsC, DefaultHandler},
+          {variables::kCflagsCC, DefaultHandler},
+          {variables::kCflagsObjC, DefaultHandler},
+          {variables::kCflagsObjCC, DefaultHandler},
+          {variables::kDefines, DefaultHandler},
+          {variables::kIncludeDirs, DefaultHandler},
+          {variables::kLdflags, DefaultHandler},
+          {variables::kPrecompiledHeader, DefaultHandler},
+          {variables::kPrecompiledSource, DefaultHandler},
+          {variables::kDeps, DepsHandler},
+          {variables::kLibs, DefaultHandler},
+          {variables::kLibDirs, DefaultHandler}};
+}
+
+void HandleProperty(const std::string& what,
+                    const std::map<std::string, DescHandlerFunc>& handler_map,
+                    std::unique_ptr<base::Value>& v,
+                    std::unique_ptr<base::DictionaryValue>& dict) {
+  if (dict->Remove(what, &v)) {
+    auto pair = handler_map.find(what);
+    if (pair != handler_map.end())
+      pair->second(what, v.get(), false);
+  }
+}
+
 bool PrintTarget(const Target* target,
                  const std::string& what,
                  bool single_target,
+                 const std::map<std::string, DescHandlerFunc>& handler_map,
                  bool all,
                  bool tree,
                  bool blame) {
@@ -168,10 +316,12 @@
                  "\".\n");
     return false;
   }
-  // Print single value, without any headers
+  // Print single value
   if (!what.empty() && dict->size() == 1 && single_target) {
     base::DictionaryValue::Iterator iter(*dict);
-    PrintValue(&iter.value(), 0);
+    auto pair = handler_map.find(what);
+    if (pair != handler_map.end())
+      pair->second(what, &iter.value(), true);
     return true;
   }
 
@@ -180,51 +330,47 @@
   OutputString("\n");
 
   std::unique_ptr<base::Value> v;
-#define HANDLER(property, handler_name) \
-  if (dict->Remove(property, &v)) {     \
-    handler_name(property, v.get());    \
-  }
-
   // Entries with DefaultHandler are present to enforce order
-  HANDLER("type", LabelHandler);
-  HANDLER("toolchain", LabelHandler);
-  HANDLER(variables::kVisibility, VisibilityHandler);
-  HANDLER(variables::kTestonly, DefaultHandler);
-  HANDLER(variables::kCheckIncludes, DefaultHandler);
-  HANDLER(variables::kAllowCircularIncludesFrom, DefaultHandler);
-  HANDLER(variables::kSources, DefaultHandler);
-  HANDLER(variables::kPublic, PublicHandler);
-  HANDLER(variables::kInputs, DefaultHandler);
-  HANDLER(variables::kConfigs, ConfigsHandler);
-  HANDLER(variables::kPublicConfigs, ConfigsHandler);
-  HANDLER(variables::kAllDependentConfigs, ConfigsHandler);
-  HANDLER(variables::kScript, DefaultHandler);
-  HANDLER(variables::kArgs, DefaultHandler);
-  HANDLER(variables::kDepfile, DefaultHandler);
+  HandleProperty("type", handler_map, v, dict);
+  HandleProperty("toolchain", handler_map, v, dict);
+  HandleProperty(variables::kVisibility, handler_map, v, dict);
+  HandleProperty(variables::kMetadata, handler_map, v, dict);
+  HandleProperty(variables::kTestonly, handler_map, v, dict);
+  HandleProperty(variables::kCheckIncludes, handler_map, v, dict);
+  HandleProperty(variables::kAllowCircularIncludesFrom, handler_map, v, dict);
+  HandleProperty(variables::kSources, handler_map, v, dict);
+  HandleProperty(variables::kPublic, handler_map, v, dict);
+  HandleProperty(variables::kInputs, handler_map, v, dict);
+  HandleProperty(variables::kConfigs, handler_map, v, dict);
+  HandleProperty(variables::kPublicConfigs, handler_map, v, dict);
+  HandleProperty(variables::kAllDependentConfigs, handler_map, v, dict);
+  HandleProperty(variables::kScript, handler_map, v, dict);
+  HandleProperty(variables::kArgs, handler_map, v, dict);
+  HandleProperty(variables::kDepfile, handler_map, v, dict);
   ProcessOutputs(dict.get());
-  HANDLER("bundle_data", DefaultHandler);
-  HANDLER(variables::kArflags, DefaultHandler);
-  HANDLER(variables::kAsmflags, DefaultHandler);
-  HANDLER(variables::kCflags, DefaultHandler);
-  HANDLER(variables::kCflagsC, DefaultHandler);
-  HANDLER(variables::kCflagsCC, DefaultHandler);
-  HANDLER(variables::kCflagsObjC, DefaultHandler);
-  HANDLER(variables::kCflagsObjCC, DefaultHandler);
-  HANDLER(variables::kDefines, DefaultHandler);
-  HANDLER(variables::kIncludeDirs, DefaultHandler);
-  HANDLER(variables::kLdflags, DefaultHandler);
-  HANDLER(variables::kPrecompiledHeader, DefaultHandler);
-  HANDLER(variables::kPrecompiledSource, DefaultHandler);
-  HANDLER(variables::kDeps, DepsHandler);
-  HANDLER(variables::kLibs, DefaultHandler);
-  HANDLER(variables::kLibDirs, DefaultHandler);
+  HandleProperty("bundle_data", handler_map, v, dict);
+  HandleProperty(variables::kArflags, handler_map, v, dict);
+  HandleProperty(variables::kAsmflags, handler_map, v, dict);
+  HandleProperty(variables::kCflags, handler_map, v, dict);
+  HandleProperty(variables::kCflagsC, handler_map, v, dict);
+  HandleProperty(variables::kCflagsCC, handler_map, v, dict);
+  HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
+  HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
+  HandleProperty(variables::kDefines, handler_map, v, dict);
+  HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
+  HandleProperty(variables::kLdflags, handler_map, v, dict);
+  HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
+  HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
+  HandleProperty(variables::kDeps, handler_map, v, dict);
+  HandleProperty(variables::kLibs, handler_map, v, dict);
+  HandleProperty(variables::kLibDirs, handler_map, v, dict);
 
-#undef HANDLER
+#undef HandleProperty
 
   // Process the rest (if any)
   base::DictionaryValue::Iterator iter(*dict);
   while (!iter.IsAtEnd()) {
-    DefaultHandler(iter.key(), &iter.value());
+    DefaultHandler(iter.key(), &iter.value(), false);
     iter.Advance();
   }
 
@@ -233,17 +379,20 @@
 
 bool PrintConfig(const Config* config,
                  const std::string& what,
-                 bool single_config) {
+                 bool single_config,
+                 const std::map<std::string, DescHandlerFunc>& handler_map) {
   std::unique_ptr<base::DictionaryValue> dict =
       DescBuilder::DescriptionForConfig(config, what);
   if (!what.empty() && dict->empty()) {
     OutputString("Don't know how to display \"" + what + "\" for a config.\n");
     return false;
   }
-  // Print single value, without any headers
+  // Print single value
   if (!what.empty() && dict->size() == 1 && single_config) {
     base::DictionaryValue::Iterator iter(*dict);
-    PrintValue(&iter.value(), 0);
+    auto pair = handler_map.find(what);
+    if (pair != handler_map.end())
+      pair->second(what, &iter.value(), true);
     return true;
   }
 
@@ -252,34 +401,29 @@
   OutputString("\n");
 
   std::unique_ptr<base::Value> v;
-#define HANDLER(property, handler_name) \
-  if (dict->Remove(property, &v)) {     \
-    handler_name(property, v.get());    \
-  }
-
-  HANDLER("toolchain", LabelHandler);
+  HandleProperty("toolchain", handler_map, v, dict);
   if (!config->configs().empty()) {
     OutputString(
         "(This is a composite config, the values below are after the\n"
         "expansion of the child configs.)\n");
   }
-  HANDLER(variables::kArflags, DefaultHandler);
-  HANDLER(variables::kAsmflags, DefaultHandler);
-  HANDLER(variables::kCflags, DefaultHandler);
-  HANDLER(variables::kCflagsC, DefaultHandler);
-  HANDLER(variables::kCflagsCC, DefaultHandler);
-  HANDLER(variables::kCflagsObjC, DefaultHandler);
-  HANDLER(variables::kCflagsObjCC, DefaultHandler);
-  HANDLER(variables::kDefines, DefaultHandler);
-  HANDLER(variables::kIncludeDirs, DefaultHandler);
-  HANDLER(variables::kInputs, DefaultHandler);
-  HANDLER(variables::kLdflags, DefaultHandler);
-  HANDLER(variables::kLibs, DefaultHandler);
-  HANDLER(variables::kLibDirs, DefaultHandler);
-  HANDLER(variables::kPrecompiledHeader, DefaultHandler);
-  HANDLER(variables::kPrecompiledSource, DefaultHandler);
+  HandleProperty(variables::kArflags, handler_map, v, dict);
+  HandleProperty(variables::kAsmflags, handler_map, v, dict);
+  HandleProperty(variables::kCflags, handler_map, v, dict);
+  HandleProperty(variables::kCflagsC, handler_map, v, dict);
+  HandleProperty(variables::kCflagsCC, handler_map, v, dict);
+  HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
+  HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
+  HandleProperty(variables::kDefines, handler_map, v, dict);
+  HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
+  HandleProperty(variables::kInputs, handler_map, v, dict);
+  HandleProperty(variables::kLdflags, handler_map, v, dict);
+  HandleProperty(variables::kLibs, handler_map, v, dict);
+  HandleProperty(variables::kLibDirs, handler_map, v, dict);
+  HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
+  HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
 
-#undef HANDLER
+#undef HandleProperty
 
   return true;
 }
@@ -323,6 +467,7 @@
   ldflags [--blame]
   lib_dirs
   libs
+  metadata
   outputs
   public_configs
   public
@@ -488,6 +633,7 @@
   } else {
     // Regular (non-json) formatted output
     bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1;
+    std::map<std::string, DescHandlerFunc> handlers = GetHandlers();
 
     bool printed_output = false;
     for (const Target* target : target_matches) {
@@ -495,7 +641,7 @@
         OutputString("\n\n");
       printed_output = true;
 
-      if (!PrintTarget(target, what_to_print, !multiple_outputs,
+      if (!PrintTarget(target, what_to_print, !multiple_outputs, handlers,
                        cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree),
                        cmdline->HasSwitch(kBlame)))
         return 1;
@@ -505,7 +651,7 @@
         OutputString("\n\n");
       printed_output = true;
 
-      if (!PrintConfig(config, what_to_print, !multiple_outputs))
+      if (!PrintConfig(config, what_to_print, !multiple_outputs, handlers))
         return 1;
     }
   }
diff --git a/tools/gn/desc_builder.cc b/tools/gn/desc_builder.cc
index b2fd716..209e9f4 100644
--- a/tools/gn/desc_builder.cc
+++ b/tools/gn/desc_builder.cc
@@ -5,6 +5,7 @@
 #include <memory>
 #include <set>
 
+#include "base/json/json_writer.h"
 #include "base/strings/string_number_conversions.h"
 #include "tools/gn/commands.h"
 #include "tools/gn/config.h"
@@ -14,7 +15,9 @@
 #include "tools/gn/input_file.h"
 #include "tools/gn/parse_tree.h"
 #include "tools/gn/runtime_deps.h"
+#include "tools/gn/scope.h"
 #include "tools/gn/settings.h"
+#include "tools/gn/standard_out.h"
 #include "tools/gn/substitution_writer.h"
 #include "tools/gn/variables.h"
 
@@ -47,6 +50,7 @@
 //   "deps : [ list of target dependencies ],
 //   "libs" : [ list of libraries ],
 //   "lib_dirs" : [ list of library directories ]
+//   "metadata" : [ dictionary of target metadata values ]
 // }
 //
 // Optionally, if "what" is specified while generating description, two other
@@ -144,6 +148,42 @@
     return RenderValue(lib.value());
   }
 
+  template <typename T>
+  base::Value ToBaseValue(const std::vector<T>& vector) {
+    base::ListValue res;
+    for (const auto& v : vector)
+      res.GetList().emplace_back(ToBaseValue(v));
+    return std::move(res);
+  }
+
+  base::Value ToBaseValue(const Scope* scope) {
+    base::DictionaryValue res;
+    Scope::KeyValueMap map;
+    scope->GetCurrentScopeValues(&map);
+    for (const auto& v : map)
+      res.SetKey(v.first, ToBaseValue(v.second));
+    return std::move(res);
+  }
+
+  base::Value ToBaseValue(const Value& val) {
+    switch (val.type()) {
+      case Value::STRING:
+        return base::Value(val.string_value());
+      case Value::INTEGER:
+        return base::Value(int(val.int_value()));
+      case Value::BOOLEAN:
+        return base::Value(val.boolean_value());
+      case Value::SCOPE:
+        return ToBaseValue(val.scope_value());
+      case Value::LIST:
+        return ToBaseValue(val.list_value());
+      case Value::NONE:
+        return base::Value();
+    }
+    NOTREACHED();
+    return base::Value();
+  }
+
   template <class VectorType>
   void FillInConfigVector(base::ListValue* out,
                           const VectorType& configs,
@@ -273,6 +313,14 @@
     }
 
     // General target meta variables.
+
+    if (what(variables::kMetadata)) {
+      base::DictionaryValue metadata;
+      for (const auto& v : target_->metadata().contents())
+        metadata.SetKey(v.first, ToBaseValue(v.second));
+      res->SetKey(variables::kMetadata, std::move(metadata));
+    }
+
     if (what(variables::kVisibility))
       res->SetWithoutPathExpansion(variables::kVisibility,
                                    target_->visibility().AsValue());