[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());