shaka-packager/tools/gn/command_desc.cc

345 lines
12 KiB
C++
Raw Normal View History

// 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 <algorithm>
#include <set>
#include <sstream>
#include "base/command_line.h"
#include "tools/gn/commands.h"
#include "tools/gn/config.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/item.h"
#include "tools/gn/item_node.h"
#include "tools/gn/label.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
#include "tools/gn/target.h"
namespace commands {
namespace {
struct CompareTargetLabel {
bool operator()(const Target* a, const Target* b) const {
return a->label() < b->label();
}
};
void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
if (result->find(target->label()) != result->end())
return; // Already did this target.
result->insert(target->label());
const std::vector<const Target*>& deps = target->deps();
for (size_t i = 0; i < deps.size(); i++)
RecursiveCollectDeps(deps[i], result);
const std::vector<const Target*>& datadeps = target->datadeps();
for (size_t i = 0; i < datadeps.size(); i++)
RecursiveCollectDeps(datadeps[i], result);
}
// Prints dependencies of the given target (not the target itself).
void RecursivePrintDeps(const Target* target,
const Label& default_toolchain,
int indent_level) {
std::vector<const Target*> sorted_deps = target->deps();
const std::vector<const Target*> datadeps = target->datadeps();
for (size_t i = 0; i < datadeps.size(); i++)
sorted_deps.push_back(datadeps[i]);
std::sort(sorted_deps.begin(), sorted_deps.end(), CompareTargetLabel());
std::string indent(indent_level * 2, ' ');
for (size_t i = 0; i < sorted_deps.size(); i++) {
OutputString(indent +
sorted_deps[i]->label().GetUserVisibleName(default_toolchain) + "\n");
RecursivePrintDeps(sorted_deps[i], default_toolchain, indent_level + 1);
}
}
void PrintDeps(const Target* target, bool display_header) {
const CommandLine* cmdline = CommandLine::ForCurrentProcess();
Label toolchain_label = target->label().GetToolchainLabel();
// Tree mode is separate.
if (cmdline->HasSwitch("tree")) {
if (display_header)
OutputString("\nDependency tree:\n");
RecursivePrintDeps(target, toolchain_label, 1);
return;
}
// Collect the deps to display.
std::vector<Label> deps;
if (cmdline->HasSwitch("all")) {
if (display_header)
OutputString("\nAll recursive dependencies:\n");
std::set<Label> all_deps;
RecursiveCollectDeps(target, &all_deps);
for (std::set<Label>::iterator i = all_deps.begin();
i != all_deps.end(); ++i)
deps.push_back(*i);
} else {
if (display_header) {
OutputString("\nDirect dependencies "
"(try also \"--all\" and \"--tree\"):\n");
}
const std::vector<const Target*>& target_deps = target->deps();
for (size_t i = 0; i < target_deps.size(); i++)
deps.push_back(target_deps[i]->label());
const std::vector<const Target*>& target_datadeps = target->datadeps();
for (size_t i = 0; i < target_datadeps.size(); i++)
deps.push_back(target_datadeps[i]->label());
}
std::sort(deps.begin(), deps.end());
for (size_t i = 0; i < deps.size(); i++)
OutputString(" " + deps[i].GetUserVisibleName(toolchain_label) + "\n");
}
void PrintConfigs(const Target* target, bool display_header) {
// Configs (don't sort since the order determines how things are processed).
if (display_header)
OutputString("\nConfigs (in order applying):\n");
Label toolchain_label = target->label().GetToolchainLabel();
const std::vector<const Config*>& configs = target->configs();
for (size_t i = 0; i < configs.size(); i++) {
OutputString(" " +
configs[i]->label().GetUserVisibleName(toolchain_label) + "\n");
}
}
void PrintSources(const Target* target, bool display_header) {
if (display_header)
OutputString("\nSources:\n");
Target::FileList sources = target->sources();
std::sort(sources.begin(), sources.end());
for (size_t i = 0; i < sources.size(); i++)
OutputString(" " + sources[i].value() + "\n");
}
// Attempts to attribute the gen dependency of the given target to some source
// code and outputs the string to the output stream.
//
// The attribution of the source of the dependencies is stored in the ItemNode
// which is the parallel structure to the target dependency map, so we have
// to jump through a few loops to find everything.
void OutputSourceOfDep(const Target* target,
const Label& dep_label,
std::ostream& out) {
ItemTree& item_tree = target->settings()->build_settings()->item_tree();
base::AutoLock lock(item_tree.lock());
ItemNode* target_node = item_tree.GetExistingNodeLocked(target->label());
CHECK(target_node);
ItemNode* dep_node = item_tree.GetExistingNodeLocked(dep_label);
CHECK(dep_node);
const ItemNode::ItemNodeMap& direct_deps = target_node->direct_dependencies();
ItemNode::ItemNodeMap::const_iterator found = direct_deps.find(dep_node);
if (found == direct_deps.end())
return;
const Location& location = found->second.begin();
out << " (Added by " + location.file()->name().value() << ":"
<< location.line_number() << ")\n";
}
// Templatized writer for writing out different config value types.
template<typename T> struct DescValueWriter {};
template<> struct DescValueWriter<std::string> {
void operator()(const std::string& str, std::ostream& out) const {
out << " " << str << "\n";
}
};
template<> struct DescValueWriter<SourceFile> {
void operator()(const SourceFile& file, std::ostream& out) const {
out << " " << file.value() << "\n";
}
};
template<> struct DescValueWriter<SourceDir> {
void operator()(const SourceDir& dir, std::ostream& out) const {
out << " " << dir.value() << "\n";
}
};
// Writes a given config value type to the string, optionally with attribution.
// This should match RecursiveTargetConfigToStream in the order it traverses.
template<typename T> void OutputRecursiveTargetConfig(
const Target* target,
const char* header_name,
const std::vector<T>& (ConfigValues::* getter)() const) {
bool display_blame = CommandLine::ForCurrentProcess()->HasSwitch("blame");
DescValueWriter<T> writer;
std::ostringstream out;
// First write the values from the config itself.
if (!(target->config_values().*getter)().empty()) {
if (display_blame)
out << " From " << target->label().GetUserVisibleName(false) << "\n";
ConfigValuesToStream(target->config_values(), getter, writer, out);
}
// TODO(brettw) annotate where forced config includes came from!
// Then write the configs in order.
for (size_t i = 0; i < target->configs().size(); i++) {
const Config* config = target->configs()[i];
const ConfigValues& values = config->config_values();
if (!(values.*getter)().empty()) {
if (display_blame) {
out << " From " << config->label().GetUserVisibleName(false) << "\n";
OutputSourceOfDep(target, config->label(), out);
}
ConfigValuesToStream(values, getter, writer, out);
}
}
std::string out_str = out.str();
if (!out_str.empty()) {
OutputString(std::string(header_name) + "\n");
OutputString(out_str);
}
}
} // namespace
// desc ------------------------------------------------------------------------
const char kDesc[] = "desc";
const char kDesc_HelpShort[] =
"desc: Show lots of insightful information about a target.";
const char kDesc_Help[] =
"gn desc <target label> [<what to show>] [--blame] [--all | --tree]\n"
" Displays information about a given labeled target.\n"
"\n"
"Possibilities for <what to show>:\n"
" (If unspecified an overall summary will be displayed.)\n"
"\n"
" sources\n"
" Source files.\n"
"\n"
" configs\n"
" Shows configs applied to the given target, sorted in the order\n"
" they're specified. This includes both configs specified in the\n"
" \"configs\" variable, as well as configs pushed onto this target\n"
" via dependencies specifying \"all\" or \"direct\" dependent\n"
" configs.\n"
"\n"
" deps [--all | --tree]\n"
" Show immediate (or, when \"--all\" or \"--tree\" is specified,\n"
" recursive) dependencies of the given target. \"--tree\" shows them\n"
" in a tree format. Otherwise, they will be sorted alphabetically.\n"
" Both \"deps\" and \"datadeps\" will be included.\n"
"\n"
" defines [--blame]\n"
" includes [--blame]\n"
" cflags [--blame]\n"
" cflags_cc [--blame]\n"
" cflags_cxx [--blame]\n"
" ldflags [--blame]\n"
" Shows the given values taken from the target and all configs\n"
" applying. See \"--blame\" below.\n"
"\n"
" --blame\n"
" Used with any value specified by a config, this will name\n"
" the config that specified the value.\n"
"\n"
"Note:\n"
" This command will show the full name of directories and source files,\n"
" but when directories and source paths are written to the build file,\n"
" they will be adjusted to be relative to the build directory. So the\n"
" values for paths displayed by this command won't match (but should\n"
" mean the same thing.\n"
"\n"
"Examples:\n"
" gn desc //base:base\n"
" Summarizes the given target.\n"
"\n"
" gn desc :base_unittests deps --tree\n"
" Shows a dependency tree of the \"base_unittests\" project in\n"
" the current directory.\n"
"\n"
" gn desc //base defines --blame\n"
" Shows defines set for the //base:base target, annotated by where\n"
" each one was set from.\n";
int RunDesc(const std::vector<std::string>& args) {
if (args.size() != 1 && args.size() != 2) {
Err(Location(), "You're holding it wrong.",
"Usage: \"gn desc <target_name> <what to display>\"").PrintToStdout();
return 1;
}
const Target* target = GetTargetForDesc(args);
if (!target)
return 1;
#define CONFIG_VALUE_HANDLER(name) \
} else if (what == #name) { \
OutputRecursiveTargetConfig(target, #name, &ConfigValues::name);
if (args.size() == 2) {
// User specified one thing to display.
const std::string& what = args[1];
if (what == "configs") {
PrintConfigs(target, false);
} else if (what == "sources") {
PrintSources(target, false);
} else if (what == "deps") {
PrintDeps(target, false);
CONFIG_VALUE_HANDLER(defines)
CONFIG_VALUE_HANDLER(includes)
CONFIG_VALUE_HANDLER(cflags)
CONFIG_VALUE_HANDLER(cflags_c)
CONFIG_VALUE_HANDLER(cflags_cc)
CONFIG_VALUE_HANDLER(ldflags)
} else {
OutputString("Don't know how to display \"" + what + "\".\n");
return 1;
}
#undef CONFIG_VALUE_HANDLER
return 0;
}
// Display summary.
// Generally we only want to display toolchains on labels when the toolchain
// is different than the default one for this target (which we always print
// in the header).
Label target_toolchain = target->label().GetToolchainLabel();
// Header.
std::string title_target =
"Target: " + target->label().GetUserVisibleName(false);
std::string title_toolchain =
"Toolchain: " + target_toolchain.GetUserVisibleName(false);
OutputString(title_target + "\n", DECORATION_YELLOW);
OutputString(title_toolchain + "\n", DECORATION_YELLOW);
OutputString(std::string(
std::max(title_target.size(), title_toolchain.size()), '=') + "\n");
PrintSources(target, true);
PrintConfigs(target, true);
OutputString("\n (Use \"gn desc <label> <thing you want to see>\" to show "
"the actual values\n applied by the different configs. "
"See \"gn help desc\" for more.)\n");
PrintDeps(target, true);
return 0;
}
} // namespace commands