345 lines
12 KiB
C++
345 lines
12 KiB
C++
|
// 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
|