| // 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 RecursiveCollectChildDeps(const Target* target, std::set<Label>* result); |
| |
| 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()); |
| |
| RecursiveCollectChildDeps(target, result); |
| } |
| |
| void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result) { |
| 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; |
| RecursiveCollectChildDeps(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"); |
| } |
| |
| // 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(" " + lib_dirs[i].value() + "\n"); |
| } |
| |
| void PrintLibs(const Target* target, bool display_header) { |
| const OrderedSet<std::string>& 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] + "\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()); |
| |
| const ItemNode* target_node = target->item_node(); |
| 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; |
| |
| 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(target, config->label(), 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()) { |
| OutputString("\n" + 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" |
| " include_dirs [--blame]\n" |
| " cflags [--blame]\n" |
| " cflags_cc [--blame]\n" |
| " cflags_cxx [--blame]\n" |
| " ldflags [--blame]\n" |
| " lib_dirs\n" |
| " libs\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. This doesn't currently work\n" |
| " for libs and lib_dirs because those are inherited and are more\n" |
| " complicated to figure out the blame (patches welcome).\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"; |
| |
| #define OUTPUT_CONFIG_VALUE(name, type) \ |
| OutputRecursiveTargetConfig<type>(target, #name, &ConfigValues::name); |
| |
| 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, type) \ |
| } else if (what == #name) { OUTPUT_CONFIG_VALUE(name, type) |
| |
| 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); |
| } else if (what == "lib_dirs") { |
| PrintLibDirs(target, false); |
| } else if (what == "libs") { |
| PrintLibs(target, false); |
| |
| CONFIG_VALUE_HANDLER(defines, std::string) |
| CONFIG_VALUE_HANDLER(include_dirs, SourceDir) |
| CONFIG_VALUE_HANDLER(cflags, std::string) |
| CONFIG_VALUE_HANDLER(cflags_c, std::string) |
| CONFIG_VALUE_HANDLER(cflags_cc, std::string) |
| CONFIG_VALUE_HANDLER(cflags_objc, std::string) |
| CONFIG_VALUE_HANDLER(cflags_objcc, std::string) |
| CONFIG_VALUE_HANDLER(ldflags, std::string) |
| |
| } 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. |
| 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_toolchain.GetUserVisibleName(false) + "\n"); |
| |
| PrintSources(target, true); |
| PrintConfigs(target, true); |
| |
| OUTPUT_CONFIG_VALUE(defines, std::string) |
| OUTPUT_CONFIG_VALUE(include_dirs, SourceDir) |
| OUTPUT_CONFIG_VALUE(cflags, std::string) |
| OUTPUT_CONFIG_VALUE(cflags_c, std::string) |
| OUTPUT_CONFIG_VALUE(cflags_cc, std::string) |
| OUTPUT_CONFIG_VALUE(cflags_objc, std::string) |
| OUTPUT_CONFIG_VALUE(cflags_objcc, std::string) |
| OUTPUT_CONFIG_VALUE(ldflags, std::string) |
| PrintLibs(target, true); |
| PrintLibDirs(target, true); |
| |
| PrintDeps(target, true); |
| |
| return 0; |
| } |
| |
| } // namespace commands |