blob: a499384c559b435b5af65c0753be14bb8e4693ca [file] [log] [blame]
// 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 "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";
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) {
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);
}
}
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},
{variables::kDataKeys, DefaultHandler},
{variables::kRebase, DefaultHandler},
{variables::kWalkKeys, DefaultHandler},
{variables::kWriteOutputConversion, 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) {
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::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());
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);
HandleProperty(variables::kDataKeys, handler_map, v, dict);
HandleProperty(variables::kRebase, handler_map, v, dict);
HandleProperty(variables::kWalkKeys, 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::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 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)
include_dirs [--blame]
inputs
ldflags [--blame]
lib_dirs
libs
metadata
output_conversion
outputs
public_configs
public
rebase
script
sources
testonly
visibility
walk_keys
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;
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