| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <memory> | 
 | #include <set> | 
 | #include <sstream> | 
 |  | 
 | #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 "gn/commands.h" | 
 | #include "gn/config.h" | 
 | #include "gn/desc_builder.h" | 
 | #include "gn/setup.h" | 
 | #include "gn/standard_out.h" | 
 | #include "gn/switches.h" | 
 | #include "gn/target.h" | 
 | #include "gn/variables.h" | 
 | #include "gn/rust_variables.h" | 
 |  | 
 | namespace commands { | 
 |  | 
 | namespace { | 
 |  | 
 | // Desc-specific command line switches. | 
 | const char kBlame[] = "blame"; | 
 | 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, ' '); | 
 |   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 (value->GetAsList(&list_value)) { | 
 |     for (const auto& v : *list_value) { | 
 |       PrintValue(&v, indentLevel); | 
 |     } | 
 |   } else if (value->GetAsString(&string_value)) { | 
 |     OutputString(indent); | 
 |     OutputString(string_value); | 
 |     OutputString("\n"); | 
 |   } else if (value->GetAsBoolean(&bool_value)) { | 
 |     OutputString(indent); | 
 |     OutputString(bool_value ? "true" : "false"); | 
 |     OutputString("\n"); | 
 |   } else if (value->GetAsDictionary(&dict_value)) { | 
 |     base::DictionaryValue::Iterator iter(*dict_value); | 
 |     while (!iter.IsAtEnd()) { | 
 |       OutputString(indent + iter.key() + "\n"); | 
 |       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, | 
 |                     bool value_only) { | 
 |   if (value_only) { | 
 |     PrintValue(value, 0); | 
 |     return; | 
 |   } | 
 |   OutputString("\n"); | 
 |   OutputString(name); | 
 |   OutputString("\n"); | 
 |   PrintValue(value, 1); | 
 | } | 
 |  | 
 | // 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(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); | 
 |     OutputString(string_value + "\n"); | 
 |   } | 
 | } | 
 |  | 
 | 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, value_only); | 
 |     } else { | 
 |       DefaultHandler(name, value, value_only); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | 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, value_only); | 
 |       return; | 
 |     } | 
 |   } | 
 |   DefaultHandler(name, value, value_only); | 
 | } | 
 |  | 
 | 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, value_only); | 
 |   else | 
 |     DefaultHandler(name + " (in order applying, try also --tree)", value, | 
 |                    value_only); | 
 | } | 
 |  | 
 | 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, value_only); | 
 |   } else { | 
 |     if (!all) { | 
 |       DefaultHandler( | 
 |           "Direct dependencies " | 
 |           "(try also \"--all\", \"--tree\", or even \"--all --tree\")", | 
 |           value, value_only); | 
 |     } else { | 
 |       DefaultHandler("All recursive dependencies", value, value_only); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | // Outputs need special processing when output patterns are present. | 
 | void ProcessOutputs(base::DictionaryValue* target, bool files_only) { | 
 |   base::ListValue* patterns = nullptr; | 
 |   base::ListValue* outputs = nullptr; | 
 |   target->GetList("output_patterns", &patterns); | 
 |   target->GetList(variables::kOutputs, &outputs); | 
 |  | 
 |   int indent = 0; | 
 |   if (outputs || patterns) { | 
 |     if (!files_only) { | 
 |       OutputString("\noutputs\n"); | 
 |       indent = 1; | 
 |     } | 
 |     if (patterns) { | 
 |       if (!files_only) { | 
 |         OutputString("  Output patterns\n"); | 
 |         indent = 2; | 
 |       } | 
 |       PrintValue(patterns, indent); | 
 |       if (!files_only) | 
 |         OutputString("\n  Resolved output file list\n"); | 
 |     } | 
 |     if (outputs) | 
 |       PrintValue(outputs, indent); | 
 |  | 
 |     target->Remove("output_patterns", nullptr); | 
 |     target->Remove(variables::kOutputs, nullptr); | 
 |   } | 
 | } | 
 |  | 
 | 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::kFrameworkDirs, DefaultHandler}, | 
 |           {variables::kFrameworks, DefaultHandler}, | 
 |           {variables::kIncludeDirs, DefaultHandler}, | 
 |           {variables::kLdflags, DefaultHandler}, | 
 |           {variables::kPrecompiledHeader, DefaultHandler}, | 
 |           {variables::kPrecompiledSource, DefaultHandler}, | 
 |           {variables::kDeps, DepsHandler}, | 
 |           {variables::kLibs, DefaultHandler}, | 
 |           {variables::kLibDirs, DefaultHandler}, | 
 |           {variables::kDataKeys, DefaultHandler}, | 
 |           {variables::kRebase, DefaultHandler}, | 
 |           {variables::kWalkKeys, DefaultHandler}, | 
 |           {variables::kWeakFrameworks, DefaultHandler}, | 
 |           {variables::kWriteOutputConversion, DefaultHandler}, | 
 |           {variables::kRustCrateName, DefaultHandler}, | 
 |           {variables::kRustCrateRoot, DefaultHandler}, | 
 |           {"runtime_deps", 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) { | 
 |   std::unique_ptr<base::DictionaryValue> dict = | 
 |       DescBuilder::DescriptionForTarget(target, what, all, tree, blame); | 
 |   if (!what.empty() && dict->empty()) { | 
 |     OutputString("Don't know how to display \"" + what + "\" for \"" + | 
 |                  Target::GetStringForOutputType(target->output_type()) + | 
 |                  "\".\n"); | 
 |     return false; | 
 |   } | 
 |   // Print single value | 
 |   if (!what.empty() && dict->size() == 1 && single_target) { | 
 |     if (what == variables::kOutputs) { | 
 |       ProcessOutputs(dict.get(), true); | 
 |       return true; | 
 |     } | 
 |     base::DictionaryValue::Iterator iter(*dict); | 
 |     auto pair = handler_map.find(what); | 
 |     if (pair != handler_map.end()) | 
 |       pair->second(what, &iter.value(), true); | 
 |     return true; | 
 |   } | 
 |  | 
 |   OutputString("Target ", DECORATION_YELLOW); | 
 |   OutputString(target->label().GetUserVisibleName(false)); | 
 |   OutputString("\n"); | 
 |  | 
 |   std::unique_ptr<base::Value> v; | 
 |   // Entries with DefaultHandler are present to enforce order | 
 |   HandleProperty("type", handler_map, v, dict); | 
 |   HandleProperty("toolchain", handler_map, v, dict); | 
 |   HandleProperty(variables::kRustCrateName, handler_map, v, dict); | 
 |   HandleProperty(variables::kRustCrateRoot, 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(), false); | 
 |   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::kFrameworkDirs, handler_map, v, dict); | 
 |   HandleProperty(variables::kFrameworks, 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); | 
 |   HandleProperty(variables::kDataKeys, handler_map, v, dict); | 
 |   HandleProperty(variables::kRebase, handler_map, v, dict); | 
 |   HandleProperty(variables::kWalkKeys, handler_map, v, dict); | 
 |   HandleProperty(variables::kWeakFrameworks, handler_map, v, dict); | 
 |   HandleProperty(variables::kWriteOutputConversion, handler_map, v, dict); | 
 |  | 
 | #undef HandleProperty | 
 |  | 
 |   // Process the rest (if any) | 
 |   base::DictionaryValue::Iterator iter(*dict); | 
 |   while (!iter.IsAtEnd()) { | 
 |     DefaultHandler(iter.key(), &iter.value(), false); | 
 |     iter.Advance(); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool PrintConfig(const Config* config, | 
 |                  const std::string& what, | 
 |                  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 | 
 |   if (!what.empty() && dict->size() == 1 && single_config) { | 
 |     base::DictionaryValue::Iterator iter(*dict); | 
 |     auto pair = handler_map.find(what); | 
 |     if (pair != handler_map.end()) | 
 |       pair->second(what, &iter.value(), true); | 
 |     return true; | 
 |   } | 
 |  | 
 |   OutputString("Config: ", DECORATION_YELLOW); | 
 |   OutputString(config->label().GetUserVisibleName(false)); | 
 |   OutputString("\n"); | 
 |  | 
 |   std::unique_ptr<base::Value> v; | 
 |   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"); | 
 |   } | 
 |   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::kFrameworkDirs, handler_map, v, dict); | 
 |   HandleProperty(variables::kFrameworks, 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); | 
 |   HandleProperty(variables::kWeakFrameworks, handler_map, v, dict); | 
 |  | 
 | #undef HandleProperty | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // desc ------------------------------------------------------------------------ | 
 |  | 
 | const char kDesc[] = "desc"; | 
 | const char kDesc_HelpShort[] = | 
 |     "desc: Show lots of insightful information about a target or config."; | 
 | const char kDesc_Help[] = | 
 |     R"(gn desc | 
 |  | 
 |   gn desc <out_dir> <label or pattern> [<what to show>] [--blame] | 
 |           [--format=json] | 
 |  | 
 |   Displays information about a given target or config. The build parameters | 
 |   will be taken for the build in the given <out_dir>. | 
 |  | 
 |   The <label or pattern> can be a target label, a config label, or a label | 
 |   pattern (see "gn help label_pattern"). A label pattern will only match | 
 |   targets. | 
 |  | 
 | Possibilities for <what to show> | 
 |  | 
 |   (If unspecified an overall summary will be displayed.) | 
 |  | 
 |   all_dependent_configs | 
 |   allow_circular_includes_from | 
 |   arflags [--blame] | 
 |   args | 
 |   cflags [--blame] | 
 |   cflags_c [--blame] | 
 |   cflags_cc [--blame] | 
 |   check_includes | 
 |   configs [--tree] (see below) | 
 |   data_keys | 
 |   defines [--blame] | 
 |   depfile | 
 |   deps [--all] [--tree] (see below) | 
 |   framework_dirs | 
 |   frameworks | 
 |   include_dirs [--blame] | 
 |   inputs | 
 |   ldflags [--blame] | 
 |   lib_dirs | 
 |   libs | 
 |   metadata | 
 |   output_conversion | 
 |   outputs | 
 |   public_configs | 
 |   public | 
 |   rebase | 
 |   script | 
 |   sources | 
 |   testonly | 
 |   visibility | 
 |   walk_keys | 
 |   weak_frameworks | 
 |  | 
 |   runtime_deps | 
 |       Compute all runtime deps for the given target. This is a computed list | 
 |       and does not correspond to any GN variable, unlike most other values | 
 |       here. | 
 |  | 
 |       The output is a list of file names relative to the build directory. See | 
 |       "gn help runtime_deps" for how this is computed. This also works with | 
 |       "--blame" to see the source of the dependency. | 
 |  | 
 | Shared flags | 
 |  | 
 | )" | 
 |  | 
 |     DEFAULT_TOOLCHAIN_SWITCH_HELP | 
 |  | 
 |     R"( | 
 |   --format=json | 
 |       Format the output as JSON instead of text. | 
 |  | 
 | Target flags | 
 |  | 
 |   --blame | 
 |       Used with any value specified on a config, this will name the config that | 
 |       causes that target to get the flag. This doesn't currently work for libs, | 
 |       lib_dirs, frameworks, weak_frameworks and framework_dirs because those are | 
 |       inherited and are more complicated to figure out the blame (patches | 
 |       welcome). | 
 |  | 
 | Configs | 
 |  | 
 |   The "configs" section will list all configs that apply. For targets this will | 
 |   include configs specified in the "configs" variable of the target, and also | 
 |   configs pushed onto this target via public or "all dependent" configs. | 
 |  | 
 |   Configs can have child configs. Specifying --tree will show the hierarchy. | 
 |  | 
 | Printing outputs | 
 |  | 
 |   The "outputs" section will list all outputs that apply, including the outputs | 
 |   computed from the tool definition (eg for "executable", "static_library", ... | 
 |   targets). | 
 |  | 
 | Printing deps | 
 |  | 
 |   Deps will include all public, private, and data deps (TODO this could be | 
 |   clarified and enhanced) sorted in order applying. The following may be used: | 
 |  | 
 |   --all | 
 |       Collects all recursive dependencies and prints a sorted flat list. Also | 
 |       usable with --tree (see below). | 
 |  | 
 | )" | 
 |  | 
 |     TARGET_PRINTING_MODE_COMMAND_LINE_HELP | 
 |     "\n" TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP | 
 |  | 
 |     R"( | 
 |   --tree | 
 |       Print a dependency tree. By default, duplicates will be elided with "..." | 
 |       but when --all and -tree are used together, no eliding will be performed. | 
 |  | 
 |       The "deps", "public_deps", and "data_deps" will all be included in the | 
 |       tree. | 
 |  | 
 |       Tree output can not be used with the filtering or output flags: --as, | 
 |       --type, --testonly. | 
 |  | 
 | )" | 
 |  | 
 |     TARGET_TYPE_FILTER_COMMAND_LINE_HELP | 
 |  | 
 |     R"( | 
 | Note | 
 |  | 
 |   This command will show the full name of directories and source files, but | 
 |   when directories and source paths are written to the build file, they will be | 
 |   adjusted to be relative to the build directory. So the values for paths | 
 |   displayed by this command won't match (but should mean the same thing). | 
 |  | 
 | Examples | 
 |  | 
 |   gn desc out/Debug //base:base | 
 |       Summarizes the given target. | 
 |  | 
 |   gn desc out/Foo :base_unittests deps --tree | 
 |       Shows a dependency tree of the "base_unittests" project in | 
 |       the current directory. | 
 |  | 
 |   gn desc out/Debug //base defines --blame | 
 |       Shows defines set for the //base:base target, annotated by where | 
 |       each one was set from. | 
 | )"; | 
 |  | 
 | int RunDesc(const std::vector<std::string>& args) { | 
 |   if (args.size() != 2 && args.size() != 3) { | 
 |     Err(Location(), "You're holding it wrong.", | 
 |         "Usage: \"gn desc <out_dir> <target_name> [<what to display>]\"") | 
 |         .PrintToStdout(); | 
 |     return 1; | 
 |   } | 
 |   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | 
 |  | 
 |   // Deliberately leaked to avoid expensive process teardown. | 
 |   Setup* setup = new Setup; | 
 |   if (!setup->DoSetup(args[0], false)) | 
 |     return 1; | 
 |   if (!setup->Run()) | 
 |     return 1; | 
 |  | 
 |   // Resolve target(s) and config from inputs. | 
 |   UniqueVector<const Target*> target_matches; | 
 |   UniqueVector<const Config*> config_matches; | 
 |   UniqueVector<const Toolchain*> toolchain_matches; | 
 |   UniqueVector<SourceFile> file_matches; | 
 |  | 
 |   std::vector<std::string> target_list; | 
 |   target_list.push_back(args[1]); | 
 |  | 
 |   if (!ResolveFromCommandLineInput( | 
 |           setup, target_list, cmdline->HasSwitch(switches::kDefaultToolchain), | 
 |           &target_matches, &config_matches, &toolchain_matches, &file_matches)) | 
 |     return 1; | 
 |  | 
 |   std::string what_to_print; | 
 |   if (args.size() == 3) | 
 |     what_to_print = args[2]; | 
 |  | 
 |   bool json = cmdline->GetSwitchValueASCII("format") == "json"; | 
 |  | 
 |   if (target_matches.empty() && config_matches.empty()) { | 
 |     OutputString( | 
 |         "The input " + args[1] + " matches no targets, configs or files.\n", | 
 |         DECORATION_YELLOW); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   if (json) { | 
 |     // Convert all targets/configs to JSON, serialize and print them | 
 |     auto res = std::make_unique<base::DictionaryValue>(); | 
 |     if (!target_matches.empty()) { | 
 |       for (const auto* target : target_matches) { | 
 |         res->SetWithoutPathExpansion( | 
 |             target->label().GetUserVisibleName( | 
 |                 target->settings()->default_toolchain_label()), | 
 |             DescBuilder::DescriptionForTarget( | 
 |                 target, what_to_print, cmdline->HasSwitch(kAll), | 
 |                 cmdline->HasSwitch(kTree), cmdline->HasSwitch(kBlame))); | 
 |       } | 
 |     } else if (!config_matches.empty()) { | 
 |       for (const auto* config : config_matches) { | 
 |         res->SetWithoutPathExpansion( | 
 |             config->label().GetUserVisibleName(false), | 
 |             DescBuilder::DescriptionForConfig(config, what_to_print)); | 
 |       } | 
 |     } | 
 |     std::string s; | 
 |     base::JSONWriter::WriteWithOptions( | 
 |         *res.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s); | 
 |     OutputString(s); | 
 |   } 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) { | 
 |       if (printed_output) | 
 |         OutputString("\n\n"); | 
 |       printed_output = true; | 
 |  | 
 |       if (!PrintTarget(target, what_to_print, !multiple_outputs, handlers, | 
 |                        cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree), | 
 |                        cmdline->HasSwitch(kBlame))) | 
 |         return 1; | 
 |     } | 
 |     for (const Config* config : config_matches) { | 
 |       if (printed_output) | 
 |         OutputString("\n\n"); | 
 |       printed_output = true; | 
 |  | 
 |       if (!PrintConfig(config, what_to_print, !multiple_outputs, handlers)) | 
 |         return 1; | 
 |     } | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | }  // namespace commands |