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