|  | // 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_util.h" | 
|  | #include "tools/gn/commands.h" | 
|  | #include "tools/gn/config.h" | 
|  | #include "tools/gn/desc_builder.h" | 
|  | #include "tools/gn/setup.h" | 
|  | #include "tools/gn/standard_out.h" | 
|  | #include "tools/gn/switches.h" | 
|  | #include "tools/gn/target.h" | 
|  | #include "tools/gn/variables.h" | 
|  |  | 
|  | namespace commands { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Desc-specific command line switches. | 
|  | const char kBlame[] = "blame"; | 
|  | const char kTree[] = "tree"; | 
|  | const char kAll[] = "all"; | 
|  |  | 
|  | // 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; | 
|  | 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->is_none()) { | 
|  | OutputString(indent + "<null>\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Default handler for property | 
|  | void DefaultHandler(const std::string& name, const base::Value* value) { | 
|  | OutputString("\n"); | 
|  | OutputString(name); | 
|  | OutputString("\n"); | 
|  | PrintValue(value, 1); | 
|  | } | 
|  |  | 
|  | // Specific handler for properties that need different treatment | 
|  |  | 
|  | // 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]); | 
|  | 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) { | 
|  | const base::ListValue* list; | 
|  | if (value->GetAsList(&list)) { | 
|  | if (list->empty()) { | 
|  | base::Value str("(no visibility)"); | 
|  | DefaultHandler(name, &str); | 
|  | } else { | 
|  | DefaultHandler(name, value); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void PublicHandler(const std::string& name, const base::Value* value) { | 
|  | std::string p; | 
|  | if (value->GetAsString(&p)) { | 
|  | if (p == "*") { | 
|  | base::Value str("[All headers listed in the sources are public.]"); | 
|  | DefaultHandler(name, &str); | 
|  | return; | 
|  | } | 
|  | } | 
|  | DefaultHandler(name, value); | 
|  | } | 
|  |  | 
|  | void ConfigsHandler(const std::string& name, const base::Value* value) { | 
|  | bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | 
|  | if (tree) | 
|  | DefaultHandler(name + " tree (in order applying)", value); | 
|  | else | 
|  | DefaultHandler(name + " (in order applying, try also --tree)", value); | 
|  | } | 
|  |  | 
|  | void DepsHandler(const std::string& name, const base::Value* value) { | 
|  | bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | 
|  | bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | 
|  | if (tree) { | 
|  | DefaultHandler("Dependency tree", value); | 
|  | } else { | 
|  | if (!all) { | 
|  | DefaultHandler( | 
|  | "Direct dependencies " | 
|  | "(try also \"--all\", \"--tree\", or even \"--all --tree\")", | 
|  | value); | 
|  | } else { | 
|  | DefaultHandler("All recursive dependencies", value); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Outputs need special processing when output patterns are present. | 
|  | void ProcessOutputs(base::DictionaryValue* target) { | 
|  | base::ListValue* patterns = nullptr; | 
|  | base::ListValue* outputs = nullptr; | 
|  | target->GetList("output_patterns", &patterns); | 
|  | target->GetList(variables::kOutputs, &outputs); | 
|  |  | 
|  | if (outputs || patterns) { | 
|  | OutputString("\noutputs\n"); | 
|  | int indent = 1; | 
|  | if (patterns) { | 
|  | OutputString("  Output patterns\n"); | 
|  | indent = 2; | 
|  | PrintValue(patterns, indent); | 
|  | OutputString("\n  Resolved output file list\n"); | 
|  | } | 
|  | if (outputs) | 
|  | PrintValue(outputs, indent); | 
|  |  | 
|  | target->Remove("output_patterns", nullptr); | 
|  | target->Remove(variables::kOutputs, nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool PrintTarget(const Target* target, | 
|  | const std::string& what, | 
|  | bool single_target, | 
|  | 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, without any headers | 
|  | if (!what.empty() && dict->size() == 1 && single_target) { | 
|  | base::DictionaryValue::Iterator iter(*dict); | 
|  | PrintValue(&iter.value(), 0); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | OutputString("Target ", DECORATION_YELLOW); | 
|  | OutputString(target->label().GetUserVisibleName(false)); | 
|  | 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); | 
|  | 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); | 
|  |  | 
|  | #undef HANDLER | 
|  |  | 
|  | // Process the rest (if any) | 
|  | base::DictionaryValue::Iterator iter(*dict); | 
|  | while (!iter.IsAtEnd()) { | 
|  | DefaultHandler(iter.key(), &iter.value()); | 
|  | iter.Advance(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool PrintConfig(const Config* config, | 
|  | const std::string& what, | 
|  | bool single_config) { | 
|  | 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 | 
|  | if (!what.empty() && dict->size() == 1 && single_config) { | 
|  | base::DictionaryValue::Iterator iter(*dict); | 
|  | PrintValue(&iter.value(), 0); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | OutputString("Config: ", DECORATION_YELLOW); | 
|  | OutputString(config->label().GetUserVisibleName(false)); | 
|  | 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); | 
|  | 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); | 
|  |  | 
|  | #undef HANDLER | 
|  |  | 
|  | 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 <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) | 
|  | defines [--blame] | 
|  | depfile | 
|  | deps [--all] [--tree] (see below) | 
|  | include_dirs [--blame] | 
|  | inputs | 
|  | ldflags [--blame] | 
|  | lib_dirs | 
|  | libs | 
|  | outputs | 
|  | public_configs | 
|  | public | 
|  | script | 
|  | sources | 
|  | testonly | 
|  | visibility | 
|  |  | 
|  | 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 | 
|  | )" | 
|  |  | 
|  | ALL_TOOLCHAINS_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 | 
|  | and lib_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::kAllToolchains), | 
|  | &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; | 
|  |  | 
|  | 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, | 
|  | 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)) | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | }  // namespace commands |