| Index: tools/gn/command_desc.cc
|
| diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
|
| index 72525e97fd05304987f23e9e5280efb2f74dc2d0..ea1bab3d88b5bc4886a2366a48fc9c68902cf2a3 100644
|
| --- a/tools/gn/command_desc.cc
|
| +++ b/tools/gn/command_desc.cc
|
| @@ -9,14 +9,18 @@
|
| #include <sstream>
|
|
|
| #include "base/command_line.h"
|
| -#include "base/json/json_writer.h"
|
| -#include "base/memory/ptr_util.h"
|
| -#include "base/strings/string_util.h"
|
| +#include "build/build_config.h"
|
| #include "tools/gn/commands.h"
|
| #include "tools/gn/config.h"
|
| -#include "tools/gn/desc_builder.h"
|
| +#include "tools/gn/config_values_extractors.h"
|
| +#include "tools/gn/deps_iterator.h"
|
| +#include "tools/gn/filesystem_utils.h"
|
| +#include "tools/gn/item.h"
|
| +#include "tools/gn/label.h"
|
| +#include "tools/gn/runtime_deps.h"
|
| #include "tools/gn/setup.h"
|
| #include "tools/gn/standard_out.h"
|
| +#include "tools/gn/substitution_writer.h"
|
| #include "tools/gn/switches.h"
|
| #include "tools/gn/target.h"
|
| #include "tools/gn/variables.h"
|
| @@ -28,258 +32,725 @@
|
| // 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.get(), indentLevel);
|
| - }
|
| - } else if (value->GetAsString(&string_value)) {
|
| - OutputString(indent);
|
| - OutputString(string_value);
|
| +
|
| +// Prints the given directory in a nice way for the user to view.
|
| +std::string FormatSourceDir(const SourceDir& dir) {
|
| +#if defined(OS_WIN)
|
| + // On Windows we fix up system absolute paths to look like native ones.
|
| + // Internally, they'll look like "/C:\foo\bar/"
|
| + if (dir.is_system_absolute()) {
|
| + std::string buf = dir.value();
|
| + if (buf.size() > 3 && buf[2] == ':') {
|
| + buf.erase(buf.begin()); // Erase beginning slash.
|
| + return buf;
|
| + }
|
| + }
|
| +#endif
|
| + return dir.value();
|
| +}
|
| +
|
| +void RecursiveCollectChildDeps(const Target* target,
|
| + std::set<const Target*>* result);
|
| +
|
| +void RecursiveCollectDeps(const Target* target,
|
| + std::set<const Target*>* result) {
|
| + if (result->find(target) != result->end())
|
| + return; // Already did this target.
|
| + result->insert(target);
|
| +
|
| + RecursiveCollectChildDeps(target, result);
|
| +}
|
| +
|
| +void RecursiveCollectChildDeps(const Target* target,
|
| + std::set<const Target*>* result) {
|
| + for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
|
| + RecursiveCollectDeps(pair.ptr, result);
|
| +}
|
| +
|
| +// Prints dependencies of the given target (not the target itself). If the
|
| +// set is non-null, new targets encountered will be added to the set, and if
|
| +// a dependency is in the set already, it will not be recused into. When the
|
| +// set is null, all dependencies will be printed.
|
| +void RecursivePrintDeps(const Target* target,
|
| + const Label& default_toolchain,
|
| + std::set<const Target*>* seen_targets,
|
| + int indent_level) {
|
| + // Combine all deps into one sorted list.
|
| + std::vector<LabelTargetPair> sorted_deps;
|
| + for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
|
| + sorted_deps.push_back(pair);
|
| + std::sort(sorted_deps.begin(), sorted_deps.end(),
|
| + LabelPtrLabelLess<Target>());
|
| +
|
| + std::string indent(indent_level * 2, ' ');
|
| + for (const auto& pair : sorted_deps) {
|
| + const Target* cur_dep = pair.ptr;
|
| +
|
| + OutputString(indent +
|
| + cur_dep->label().GetUserVisibleName(default_toolchain));
|
| + bool print_children = true;
|
| + if (seen_targets) {
|
| + if (seen_targets->find(cur_dep) == seen_targets->end()) {
|
| + // New target, mark it visited.
|
| + seen_targets->insert(cur_dep);
|
| + } else {
|
| + // Already seen.
|
| + print_children = false;
|
| + // Only print "..." if something is actually elided, which means that
|
| + // the current target has children.
|
| + if (!cur_dep->public_deps().empty() ||
|
| + !cur_dep->private_deps().empty() ||
|
| + !cur_dep->data_deps().empty())
|
| + OutputString("...");
|
| + }
|
| + }
|
| +
|
| OutputString("\n");
|
| - } else if (value->GetAsBoolean(&bool_value)) {
|
| - OutputString(indent);
|
| - OutputString(bool_value ? "true" : "false");
|
| + if (print_children) {
|
| + RecursivePrintDeps(cur_dep, default_toolchain, seen_targets,
|
| + indent_level + 1);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void PrintDeps(const Target* target, bool display_header) {
|
| + const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
|
| + Label toolchain_label = target->label().GetToolchainLabel();
|
| +
|
| + // Tree mode is separate.
|
| + if (cmdline->HasSwitch(kTree)) {
|
| + if (display_header)
|
| + OutputString("\nDependency tree\n");
|
| +
|
| + if (cmdline->HasSwitch("all")) {
|
| + // Show all tree deps with no eliding.
|
| + RecursivePrintDeps(target, toolchain_label, nullptr, 1);
|
| + } else {
|
| + // Don't recurse into duplicates.
|
| + std::set<const Target*> seen_targets;
|
| + RecursivePrintDeps(target, toolchain_label, &seen_targets, 1);
|
| + }
|
| + return;
|
| + }
|
| +
|
| + // Collect the deps to display.
|
| + if (cmdline->HasSwitch("all")) {
|
| + // Show all dependencies.
|
| + if (display_header)
|
| + OutputString("\nAll recursive dependencies\n");
|
| +
|
| + std::set<const Target*> all_deps;
|
| + RecursiveCollectChildDeps(target, &all_deps);
|
| + FilterAndPrintTargetSet(display_header, all_deps);
|
| + } else {
|
| + std::vector<const Target*> deps;
|
| + // Show direct dependencies only.
|
| + if (display_header) {
|
| + OutputString(
|
| + "\nDirect dependencies "
|
| + "(try also \"--all\", \"--tree\", or even \"--all --tree\")\n");
|
| + }
|
| + for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
|
| + deps.push_back(pair.ptr);
|
| + std::sort(deps.begin(), deps.end());
|
| + FilterAndPrintTargets(display_header, &deps);
|
| + }
|
| +}
|
| +
|
| +// libs and lib_dirs are special in that they're inherited. We don't currently
|
| +// implement a blame feature for this since the bottom-up inheritance makes
|
| +// this difficult.
|
| +void PrintLibDirs(const Target* target, bool display_header) {
|
| + const OrderedSet<SourceDir>& lib_dirs = target->all_lib_dirs();
|
| + if (lib_dirs.empty())
|
| + return;
|
| +
|
| + if (display_header)
|
| + OutputString("\nlib_dirs\n");
|
| +
|
| + for (size_t i = 0; i < lib_dirs.size(); i++)
|
| + OutputString(" " + FormatSourceDir(lib_dirs[i]) + "\n");
|
| +}
|
| +
|
| +void PrintLibs(const Target* target, bool display_header) {
|
| + const OrderedSet<LibFile>& libs = target->all_libs();
|
| + if (libs.empty())
|
| + return;
|
| +
|
| + if (display_header)
|
| + OutputString("\nlibs\n");
|
| +
|
| + for (size_t i = 0; i < libs.size(); i++)
|
| + OutputString(" " + libs[i].value() + "\n");
|
| +}
|
| +
|
| +void PrintPublic(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\npublic\n");
|
| +
|
| + if (target->all_headers_public()) {
|
| + OutputString(" [All headers listed in the sources are public.]\n");
|
| + return;
|
| + }
|
| +
|
| + Target::FileList public_headers = target->public_headers();
|
| + std::sort(public_headers.begin(), public_headers.end());
|
| + for (const auto& hdr : public_headers)
|
| + OutputString(" " + hdr.value() + "\n");
|
| +}
|
| +
|
| +void PrintCheckIncludes(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\ncheck_includes\n");
|
| +
|
| + if (target->check_includes())
|
| + OutputString(" true\n");
|
| + else
|
| + OutputString(" false\n");
|
| +}
|
| +
|
| +void PrintAllowCircularIncludesFrom(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\nallow_circular_includes_from\n");
|
| +
|
| + Label toolchain_label = target->label().GetToolchainLabel();
|
| + for (const auto& cur : target->allow_circular_includes_from())
|
| + OutputString(" " + cur.GetUserVisibleName(toolchain_label) + "\n");
|
| +}
|
| +
|
| +void PrintVisibility(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\nvisibility\n");
|
| +
|
| + OutputString(target->visibility().Describe(2, false));
|
| +}
|
| +
|
| +void PrintTestonly(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\ntestonly\n");
|
| +
|
| + if (target->testonly())
|
| + OutputString(" true\n");
|
| + else
|
| + OutputString(" false\n");
|
| +}
|
| +
|
| +// Recursively prints subconfigs of a config.
|
| +void PrintSubConfigs(const Config* config, int indent_level) {
|
| + if (config->configs().empty())
|
| + return;
|
| +
|
| + std::string indent(indent_level * 2, ' ');
|
| + Label toolchain_label = config->label().GetToolchainLabel();
|
| + for (const auto& pair : config->configs()) {
|
| + OutputString(
|
| + indent + pair.label.GetUserVisibleName(toolchain_label) + "\n");
|
| + PrintSubConfigs(pair.ptr, indent_level + 1);
|
| + }
|
| +}
|
| +
|
| +// This allows configs stored as either std::vector<LabelConfigPair> or
|
| +// UniqueVector<LabelConfigPair> to be printed.
|
| +template <class VectorType>
|
| +void PrintConfigsVector(const Item* item,
|
| + const VectorType& configs,
|
| + const std::string& heading,
|
| + bool display_header) {
|
| + if (configs.empty())
|
| + return;
|
| +
|
| + bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
|
| +
|
| + // Don't sort since the order determines how things are processed.
|
| + if (display_header) {
|
| + if (tree)
|
| + OutputString("\n" + heading + " tree (in order applying)\n");
|
| + else
|
| + OutputString("\n" + heading + " (in order applying, try also --tree)\n");
|
| + }
|
| +
|
| + Label toolchain_label = item->label().GetToolchainLabel();
|
| + for (const auto& config : configs) {
|
| + OutputString(" " + config.label.GetUserVisibleName(toolchain_label) +
|
| + "\n");
|
| + if (tree)
|
| + PrintSubConfigs(config.ptr, 2); // 2 = start with double-indent.
|
| + }
|
| +}
|
| +
|
| +void PrintConfigs(const Target* target, bool display_header) {
|
| + PrintConfigsVector(target, target->configs().vector(), "configs",
|
| + display_header);
|
| +}
|
| +
|
| +void PrintConfigs(const Config* config, bool display_header) {
|
| + PrintConfigsVector(config, config->configs().vector(), "configs",
|
| + display_header);
|
| +}
|
| +
|
| +void PrintPublicConfigs(const Target* target, bool display_header) {
|
| + PrintConfigsVector(target, target->public_configs(),
|
| + "public_configs", display_header);
|
| +}
|
| +
|
| +void PrintAllDependentConfigs(const Target* target, bool display_header) {
|
| + PrintConfigsVector(target, target->all_dependent_configs(),
|
| + "all_dependent_configs", display_header);
|
| +}
|
| +
|
| +void PrintFileList(const Target::FileList& files,
|
| + const std::string& header,
|
| + bool indent_extra,
|
| + bool display_header) {
|
| + if (files.empty())
|
| + return;
|
| +
|
| + if (display_header)
|
| + OutputString("\n" + header + "\n");
|
| +
|
| + std::string indent = indent_extra ? " " : " ";
|
| +
|
| + Target::FileList sorted = files;
|
| + std::sort(sorted.begin(), sorted.end());
|
| + for (const auto& elem : sorted)
|
| + OutputString(indent + elem.value() + "\n");
|
| +}
|
| +
|
| +void PrintSources(const Target* target, bool display_header) {
|
| + PrintFileList(target->sources(), "sources", false, display_header);
|
| +}
|
| +
|
| +void PrintInputs(const Target* target, bool display_header) {
|
| + PrintFileList(target->inputs(), "inputs", false, display_header);
|
| +}
|
| +
|
| +void PrintOutputs(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\noutputs\n");
|
| +
|
| + if (target->output_type() == Target::ACTION) {
|
| + // Action, print out outputs, don't apply sources to it.
|
| + for (const auto& elem : target->action_values().outputs().list()) {
|
| + OutputString(" " + elem.AsString() + "\n");
|
| + }
|
| + } else if (target->output_type() == Target::CREATE_BUNDLE) {
|
| + std::vector<SourceFile> output_files;
|
| + target->bundle_data().GetOutputsAsSourceFiles(target->settings(),
|
| + &output_files);
|
| + PrintFileList(output_files, std::string(), true, false);
|
| + } else if (target->output_type() == Target::ACTION_FOREACH) {
|
| + const SubstitutionList& outputs = target->action_values().outputs();
|
| + if (!outputs.required_types().empty()) {
|
| + // Display the pattern and resolved pattern separately, since there are
|
| + // subtitutions used.
|
| + OutputString(" Output pattern\n");
|
| + for (const auto& elem : outputs.list())
|
| + OutputString(" " + elem.AsString() + "\n");
|
| +
|
| + // Now display what that resolves to given the sources.
|
| + OutputString("\n Resolved output file list\n");
|
| + }
|
| +
|
| + // Resolved output list.
|
| + std::vector<SourceFile> output_files;
|
| + SubstitutionWriter::ApplyListToSources(target->settings(), outputs,
|
| + target->sources(), &output_files);
|
| + PrintFileList(output_files, std::string(), true, false);
|
| + } else {
|
| + DCHECK(target->IsBinary());
|
| + const Tool* tool = target->toolchain()->GetToolForTargetFinalOutput(target);
|
| +
|
| + std::vector<OutputFile> output_files;
|
| + SubstitutionWriter::ApplyListToLinkerAsOutputFile(
|
| + target, tool, tool->outputs(), &output_files);
|
| +
|
| + std::vector<SourceFile> output_files_as_source_file;
|
| + for (const OutputFile& output_file : output_files) {
|
| + output_files_as_source_file.push_back(
|
| + output_file.AsSourceFile(target->settings()->build_settings()));
|
| + }
|
| +
|
| + PrintFileList(output_files_as_source_file, std::string(), true, false);
|
| + }
|
| +}
|
| +
|
| +void PrintScript(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\nscript\n");
|
| + OutputString(" " + target->action_values().script().value() + "\n");
|
| +}
|
| +
|
| +void PrintArgs(const Target* target, bool display_header) {
|
| + if (display_header)
|
| + OutputString("\nargs\n");
|
| + for (const auto& elem : target->action_values().args().list()) {
|
| + OutputString(" " + elem.AsString() + "\n");
|
| + }
|
| +}
|
| +
|
| +void PrintDepfile(const Target* target, bool display_header) {
|
| + if (target->action_values().depfile().empty())
|
| + return;
|
| + if (display_header)
|
| + OutputString("\ndepfile\n");
|
| + OutputString(" " + target->action_values().depfile().AsString() + "\n");
|
| +}
|
| +
|
| +// Attribute the origin for attributing from where a target came from. Does
|
| +// nothing if the input is null or it does not have a location.
|
| +void OutputSourceOfDep(const ParseNode* origin, std::ostream& out) {
|
| + if (!origin)
|
| + return;
|
| + Location location = origin->GetRange().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<SourceDir> {
|
| + void operator()(const SourceDir& dir, std::ostream& out) const {
|
| + out << " " << FormatSourceDir(dir) << "\n";
|
| + }
|
| +};
|
| +
|
| +template<> struct DescValueWriter<LibFile> {
|
| + void operator()(const LibFile& lib, std::ostream& out) const {
|
| + if (lib.is_source_file())
|
| + out << " " << lib.source_file().value() << "\n";
|
| + else
|
| + out << " " << lib.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 =
|
| + base::CommandLine::ForCurrentProcess()->HasSwitch(kBlame);
|
| +
|
| + DescValueWriter<T> writer;
|
| + std::ostringstream out;
|
| +
|
| + for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
|
| + if ((iter.cur().*getter)().empty())
|
| + continue;
|
| +
|
| + // Optional blame sub-head.
|
| + if (display_blame) {
|
| + const Config* config = iter.GetCurrentConfig();
|
| + if (config) {
|
| + // Source of this value is a config.
|
| + out << " From " << config->label().GetUserVisibleName(false) << "\n";
|
| + OutputSourceOfDep(iter.origin(), out);
|
| + } else {
|
| + // Source of this value is the target itself.
|
| + out << " From " << target->label().GetUserVisibleName(false) << "\n";
|
| + }
|
| + }
|
| +
|
| + // Actual values.
|
| + ConfigValuesToStream(iter.cur(), getter, writer, out);
|
| + }
|
| +
|
| + std::string out_str = out.str();
|
| + if (!out_str.empty()) {
|
| + if (header_name)
|
| + OutputString("\n" + std::string(header_name) + "\n");
|
| + OutputString(out_str);
|
| + }
|
| +}
|
| +
|
| +template<typename T> void OutputConfigValueArray(
|
| + const ConfigValues& values,
|
| + const char* header_name,
|
| + const std::vector<T>& (ConfigValues::* getter)() const) {
|
| + std::ostringstream out;
|
| +
|
| + DescValueWriter<T> writer;
|
| + for (const T& cur : (values.*getter)())
|
| + writer(cur, out);
|
| +
|
| + std::string out_str = out.str();
|
| + if (!out_str.empty()) {
|
| + if (header_name)
|
| + OutputString("\n" + std::string(header_name) + "\n");
|
| + OutputString(out_str);
|
| + }
|
| +}
|
| +
|
| +void PrintRuntimeDeps(const Target* target) {
|
| + bool display_blame =
|
| + base::CommandLine::ForCurrentProcess()->HasSwitch(kBlame);
|
| + Label toolchain = target->label().GetToolchainLabel();
|
| +
|
| + const Target* previous_from = NULL;
|
| + for (const auto& pair : ComputeRuntimeDeps(target)) {
|
| + if (display_blame) {
|
| + // Generally a target's runtime deps will be listed sequentially, so
|
| + // group them and don't duplicate the "from" label for two in a row.
|
| + if (previous_from == pair.second) {
|
| + OutputString(" "); // Just indent.
|
| + } else {
|
| + previous_from = pair.second;
|
| + OutputString("From ");
|
| + OutputString(pair.second->label().GetUserVisibleName(toolchain));
|
| + OutputString("\n "); // Make the file name indented.
|
| + }
|
| + }
|
| + OutputString(pair.first.value());
|
| 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->IsType(base::Value::TYPE_NULL)) {
|
| - 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::StringValue 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::StringValue 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);
|
| - }
|
| + }
|
| +}
|
| +
|
| +// If "what" is empty, prints all PCH info. If "what" is nonempty, prints only
|
| +// the things that match (if any). Returns true if anything was printed.
|
| +bool PrintPrecompiledHeaderInfo(const ConfigValues& values,
|
| + const std::string& what,
|
| + bool display_headers) {
|
| + bool found_match = false;
|
| + if (what == variables::kPrecompiledHeader || what.empty()) {
|
| + if (!values.precompiled_header().empty()) {
|
| + if (display_headers)
|
| + OutputString("\nprecompiled_header\n");
|
| + OutputString(values.precompiled_header() + "\n");
|
| + }
|
| + found_match = true;
|
| + }
|
| + if (what == variables::kPrecompiledSource || what.empty()) {
|
| + if (!values.precompiled_source().is_null()) {
|
| + if (display_headers)
|
| + OutputString("\nprecompiled_source\n");
|
| + OutputString(values.precompiled_source().value() + "\n");
|
| + }
|
| + found_match = true;
|
| + }
|
| + return found_match;
|
| }
|
|
|
| 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()) {
|
| + bool display_target_header) {
|
| + if (display_target_header) {
|
| + OutputString("Target: ", DECORATION_YELLOW);
|
| + OutputString(target->label().GetUserVisibleName(false) + "\n");
|
| + OutputString("Type: ", DECORATION_YELLOW);
|
| + OutputString(std::string(
|
| + Target::GetStringForOutputType(target->output_type())) + "\n");
|
| + OutputString("Toolchain: ", DECORATION_YELLOW);
|
| + OutputString(
|
| + target->label().GetToolchainLabel().GetUserVisibleName(false) + "\n");
|
| + }
|
| +
|
| + // Display headers when outputting everything.
|
| + bool display_headers = what.empty();
|
| + bool is_binary_output = target->IsBinary();
|
| +
|
| + bool found_match = false;
|
| +
|
| + // General target meta variables.
|
| + if (what.empty() || what == variables::kVisibility) {
|
| + PrintVisibility(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kTestonly) {
|
| + PrintTestonly(target, display_headers);
|
| + found_match = true;
|
| + }
|
| +
|
| + // Binary target meta variables.
|
| + if (is_binary_output) {
|
| + if (what.empty() || what == variables::kCheckIncludes) {
|
| + PrintCheckIncludes(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kAllowCircularIncludesFrom) {
|
| + PrintAllowCircularIncludesFrom(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + }
|
| +
|
| + // Sources and inputs.
|
| + if (what.empty() || what == variables::kSources) {
|
| + PrintSources(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kPublic) {
|
| + PrintPublic(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kInputs) {
|
| + PrintInputs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| +
|
| + // Configs. Configs set directly on a target are only relevant for binary
|
| + // targets
|
| + if (is_binary_output && (what.empty() || what == variables::kConfigs)) {
|
| + PrintConfigs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| +
|
| + // Dependent/public configs can be applied to anything.
|
| + if (what.empty() || what == variables::kPublicConfigs) {
|
| + PrintPublicConfigs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kAllDependentConfigs) {
|
| + PrintAllDependentConfigs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| +
|
| + // Action values.
|
| + if (target->output_type() == Target::ACTION ||
|
| + target->output_type() == Target::ACTION_FOREACH) {
|
| + if (what.empty() || what == variables::kScript) {
|
| + PrintScript(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kArgs) {
|
| + PrintArgs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kDepfile) {
|
| + PrintDepfile(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + }
|
| +
|
| + // Outputs.
|
| + if (target->output_type() != Target::SOURCE_SET &&
|
| + target->output_type() != Target::GROUP) {
|
| + if (what.empty() || what == variables::kOutputs) {
|
| + PrintOutputs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + }
|
| +
|
| + // Values from configs only apply to binary targets.
|
| + if (is_binary_output) {
|
| + #define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
|
| + if (what.empty() || what == #name) { \
|
| + OutputRecursiveTargetConfig<type>( \
|
| + target, display_headers ? #name : nullptr, &ConfigValues::name); \
|
| + found_match = true; \
|
| + }
|
| +
|
| + CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(defines, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir)
|
| + CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string)
|
| + // Libs and lib_dirs are handled specially below.
|
| +
|
| + #undef CONFIG_VALUE_ARRAY_HANDLER
|
| +
|
| + found_match |= PrintPrecompiledHeaderInfo(target->config_values(),
|
| + what, display_headers);
|
| + }
|
| +
|
| + // Deps
|
| + if (what.empty() || what == "deps") {
|
| + PrintDeps(target, display_headers);
|
| + found_match = true;
|
| + }
|
| +
|
| + // Runtime deps are special, print only when explicitly asked for and not in
|
| + // overview mode.
|
| + if (what == "runtime_deps") {
|
| + PrintRuntimeDeps(target);
|
| + found_match = true;
|
| + }
|
| +
|
| + // Libs can be part of any target and get recursively pushed up the chain,
|
| + // so display them regardless of target type.
|
| + if (what.empty() || what == variables::kLibs) {
|
| + PrintLibs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| + if (what.empty() || what == variables::kLibDirs) {
|
| + PrintLibDirs(target, display_headers);
|
| + found_match = true;
|
| + }
|
| +
|
| + if (!found_match) {
|
| OutputString("Don't know how to display \"" + what + "\" for \"" +
|
| - Target::GetStringForOutputType(target->output_type()) +
|
| - "\".\n");
|
| + 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()) {
|
| + bool display_config_header) {
|
| + const ConfigValues& values = config->resolved_values();
|
| +
|
| + if (display_config_header) {
|
| + OutputString("Config: ", DECORATION_YELLOW);
|
| + OutputString(config->label().GetUserVisibleName(false) + "\n");
|
| + OutputString("Toolchain: ", DECORATION_YELLOW);
|
| + OutputString(
|
| + config->label().GetToolchainLabel().GetUserVisibleName(false) + "\n");
|
| + if (what.empty() && !config->configs().empty()) {
|
| + OutputString(
|
| + "(This is a composite config, the values below are after the\n"
|
| + "expansion of the child configs.)\n");
|
| + }
|
| + }
|
| +
|
| + // Display headers when outputting everything.
|
| + bool display_headers = what.empty();
|
| +
|
| + if (what.empty() || what == variables::kConfigs)
|
| + PrintConfigs(config, display_headers);
|
| +
|
| +#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
|
| + if (what.empty() || what == #name) { \
|
| + OutputConfigValueArray<type>(values, display_headers ? #name : nullptr, \
|
| + &ConfigValues::name); \
|
| + found_match = true; \
|
| + }
|
| +
|
| + bool found_match = false;
|
| +
|
| + CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(defines, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir)
|
| + CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string)
|
| + CONFIG_VALUE_ARRAY_HANDLER(lib_dirs, SourceDir)
|
| + CONFIG_VALUE_ARRAY_HANDLER(libs, LibFile)
|
| +
|
| +#undef CONFIG_VALUE_ARRAY_HANDLER
|
| +
|
| + // Handles all PCH-related variables.
|
| + found_match |= PrintPrecompiledHeaderInfo(config->resolved_values(),
|
| + what, display_headers);
|
| +
|
| + if (!found_match) {
|
| 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::kLdflags, DefaultHandler);
|
| - HANDLER(variables::kLibs, DefaultHandler);
|
| - HANDLER(variables::kLibDirs, DefaultHandler);
|
| - HANDLER(variables::kPrecompiledHeader, DefaultHandler);
|
| - HANDLER(variables::kPrecompiledSource, DefaultHandler);
|
| -
|
| -#undef HANDLER
|
| -
|
| return true;
|
| }
|
|
|
| @@ -291,8 +762,7 @@
|
| const char kDesc_HelpShort[] =
|
| "desc: Show lots of insightful information about a target or config.";
|
| const char kDesc_Help[] =
|
| - "gn desc <out_dir> <label or pattern> [<what to show>] [--blame] "
|
| - "[--format=json]\n"
|
| + "gn desc <out_dir> <label or pattern> [<what to show>] [--blame]\n"
|
| "\n"
|
| " Displays information about a given target or config. The build\n"
|
| " build parameters will be taken for the build in the given <out_dir>.\n"
|
| @@ -344,9 +814,6 @@
|
| "\n"
|
| ALL_TOOLCHAINS_SWITCH_HELP
|
| "\n"
|
| - " --format=json\n"
|
| - " Format the output as JSON instead of text.\n"
|
| - "\n"
|
| "Target flags\n"
|
| "\n"
|
| " --blame\n"
|
| @@ -455,52 +922,28 @@
|
| if (args.size() == 3)
|
| what_to_print = args[2];
|
|
|
| - bool json = cmdline->GetSwitchValueASCII("format") == "json";
|
| -
|
| - if (json) {
|
| - // Convert all targets/configs to JSON, serialize and print them
|
| - auto res = base::WrapUnique(new base::DictionaryValue());
|
| - if (!target_matches.empty()) {
|
| - for (const auto* target : target_matches) {
|
| - res->Set(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->Set(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;
|
| - }
|
| + bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1;
|
| +
|
| + // Display headers for each target when printing all values, or when printing
|
| + // multiple targets or configs.
|
| + bool display_item_header = multiple_outputs || what_to_print.empty();
|
| +
|
| + 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, display_item_header))
|
| + return 1;
|
| + }
|
| + for (const Config* config : config_matches) {
|
| + if (printed_output)
|
| + OutputString("\n\n");
|
| + printed_output = true;
|
| +
|
| + if (!PrintConfig(config, what_to_print, display_item_header))
|
| + return 1;
|
| }
|
|
|
| return 0;
|
|
|