Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1690)

Unified Diff: tools/gn/command_refs.cc

Issue 500423003: Enhance GN diagnostic tools (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: use patterns for gn check Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/command_ls.cc ('k') | tools/gn/commands.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/command_refs.cc
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index e54d1f393c9047424bb2ad658f7ad7b0114d1666..d00149f372add32a03c9a771fa78796aca362f25 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <map>
#include <set>
#include "base/command_line.h"
@@ -9,7 +10,6 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/input_file.h"
#include "tools/gn/item.h"
-#include "tools/gn/pattern.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
#include "tools/gn/target.h"
@@ -18,12 +18,142 @@ namespace commands {
namespace {
-// Returns the file path generating this record.
-base::FilePath FilePathForRecord(const BuilderRecord* record) {
- if (!record->item())
- return base::FilePath(FILE_PATH_LITERAL("=UNRESOLVED DEPENDENCY="));
- return record->item()->defined_from()->GetRange().begin().file()
- ->physical_name();
+typedef std::set<const Target*> TargetSet;
+typedef std::vector<const Target*> TargetVector;
+
+// Maps targets to the list of targets that depend on them.
+typedef std::multimap<const Target*, const Target*> DepMap;
+
+// Populates the reverse dependency map for the targets in the Setup.
+void FillDepMap(Setup* setup, DepMap* dep_map) {
+ std::vector<const Target*> targets =
+ setup->builder()->GetAllResolvedTargets();
+
+ for (size_t target_i = 0; target_i < targets.size(); target_i++) {
+ const Target* target = targets[target_i];
+
+ // Add all deps to the map.
+ const LabelTargetVector& deps = target->deps();
+ for (size_t dep_i = 0; dep_i < deps.size(); dep_i++)
+ dep_map->insert(std::make_pair(deps[dep_i].ptr, target));
+
+ // Include data deps as well.
+ const LabelTargetVector& datadeps = target->datadeps();
+ for (size_t dep_i = 0; dep_i < datadeps.size(); dep_i++)
+ dep_map->insert(std::make_pair(datadeps[dep_i].ptr, target));
+ }
+}
+
+// Returns the file path generating this item.
+base::FilePath FilePathForItem(const Item* item) {
+ return item->defined_from()->GetRange().begin().file()->physical_name();
+}
+
+// Prints the targets which are the result of a query. This list is sorted
+// and, if as_files is set, the unique filenames matching those targets will
+// be used.
+void OutputResultSet(const TargetSet& results, bool as_files) {
+ if (results.empty())
+ return;
+
+ if (as_files) {
+ // Output the set of unique source files.
+ std::set<std::string> unique_files;
+ for (TargetSet::const_iterator iter = results.begin();
+ iter != results.end(); ++iter)
+ unique_files.insert(FilePathToUTF8(FilePathForItem(*iter)));
+
+ for (std::set<std::string>::const_iterator iter = unique_files.begin();
+ iter != unique_files.end(); ++iter) {
+ OutputString(*iter + "\n");
+ }
+ } else {
+ // Output sorted and uniquified list of labels. The set will sort the
+ // labels.
+ std::set<Label> unique_labels;
+ for (TargetSet::const_iterator iter = results.begin();
+ iter != results.end(); ++iter)
+ unique_labels.insert((*iter)->label());
+
+ // Grab the label of the default toolchain from a random target.
+ Label default_tc_label =
+ (*results.begin())->settings()->default_toolchain_label();
+
+ for (std::set<Label>::const_iterator iter = unique_labels.begin();
+ iter != unique_labels.end(); ++iter) {
+ // Print toolchain only for ones not in the default toolchain.
+ OutputString(iter->GetUserVisibleName(
+ iter->GetToolchainLabel() != default_tc_label));
+ OutputString("\n");
+ }
+ }
+}
+
+// Prints refs 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 ref is
+// in the set already, it will not be recused into. When the set is null, all
+// refs will be printed.
+void RecursivePrintTree(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* seen_targets,
+ int indent_level) {
+ std::string indent(indent_level * 2, ' ');
+
+ DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
+ DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+ for (DepMap::const_iterator cur_dep = dep_begin;
+ cur_dep != dep_end; cur_dep++) {
+ const Target* cur_target = cur_dep->second;
+
+ // Only print the toolchain for non-default-toolchain targets.
+ OutputString(indent + cur_target->label().GetUserVisibleName(
+ !cur_target->settings()->is_default()));
+
+ bool print_children = true;
+ if (seen_targets) {
+ if (seen_targets->find(cur_target) == seen_targets->end()) {
+ // New target, mark it visited.
+ seen_targets->insert(cur_target);
+ } else {
+ // Already seen.
+ print_children = false;
+ // Only print "..." if something is actually elided, which means that
+ // the current target has children.
+ if (dep_map.lower_bound(cur_target) != dep_map.upper_bound(cur_target))
+ OutputString("...");
+ }
+ }
+
+ OutputString("\n");
+ if (print_children)
+ RecursivePrintTree(dep_map, cur_target, seen_targets, indent_level + 1);
+ }
+}
+
+void RecursiveCollectChildRefs(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* results);
+
+// Recursively finds all targets that reference the given one, and additionally
+// adds the current one to the list.
+void RecursiveCollectRefs(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* results) {
+ if (results->find(target) != results->end())
+ return; // Already found this target.
+ results->insert(target);
+ RecursiveCollectChildRefs(dep_map, target, results);
+}
+
+// Recursively finds all targets that reference the given one.
+void RecursiveCollectChildRefs(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* results) {
+ DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
+ DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+ for (DepMap::const_iterator cur_dep = dep_begin;
+ cur_dep != dep_end; cur_dep++)
+ RecursiveCollectRefs(dep_map, cur_dep->second, results);
}
} // namespace
@@ -32,95 +162,130 @@ const char kRefs[] = "refs";
const char kRefs_HelpShort[] =
"refs: Find stuff referencing a target, directory, or config.";
const char kRefs_Help[] =
- "gn refs <label_pattern> [--files]\n"
+ "gn refs <build_dir> <label_pattern> [--files] [--tree] [--all]\n"
+ " [--all-toolchains]\n"
"\n"
- " Finds code referencing a given label. The label can be a\n"
- " target or config name. Unlike most other commands, unresolved\n"
- " dependencies will be tolerated. This allows you to use this command\n"
- " to find references to targets you're in the process of moving.\n"
+ " Finds which targets reference a given target or targets (reverse\n"
+ " dependencies).\n"
"\n"
- " By default, the mapping from source item to dest item (where the\n"
- " pattern matches the dest item). See \"gn help pattern\" for\n"
- " information on pattern-matching rules.\n"
+ " The <label_pattern> can take exact labels or patterns that match more\n"
+ " than one (although not general regular expressions).\n"
+ " See \"gn help label_pattern\" for details.\n"
+ "\n"
+ " --all\n"
+ " When used without --tree, will recurse and display all unique\n"
+ " dependencies of the given targets. When used with --tree, turns\n"
+ " off eliding to show a complete tree.\n"
+ "\n"
+ " --all-toolchains\n"
+ " Make the label pattern matche all toolchains. If the label pattern\n"
+ " does not specify an explicit toolchain, labels from all toolchains\n"
+ " will be matched (normally only the default toolchain is matched\n"
+ " when no toolchain is specified).\n"
"\n"
- "Option:\n"
" --files\n"
" Output unique filenames referencing a matched target or config.\n"
+ " These will be relative to the source root directory such that they\n"
+ " are suitable for piping to other commands.\n"
+ "\n"
+ " --tree\n"
+ " Outputs a reverse dependency tree from the given target. The label\n"
+ " pattern must match one target exactly. Duplicates will be elided.\n"
+ " Combine with --all to see a full dependency tree.\n"
"\n"
- "Examples:\n"
- " gn refs \"//tools/gn/*\"\n"
- " Find all targets depending on any target or config in the\n"
- " \"tools/gn\" directory.\n"
+ "Examples\n"
"\n"
- " gn refs //tools/gn:gn\n"
+ " gn refs out/Debug //tools/gn:gn\n"
" Find all targets depending on the given exact target name.\n"
"\n"
- " gn refs \"*gtk*\" --files\n"
- " Find all unique buildfiles with a dependency on a target that has\n"
- " the substring \"gtk\" in the name.\n";
+ " gn refs out/Debug //base:i18n --files | xargs gvim\n"
+ " Edit all files containing references to //base:i18n\n"
+ "\n"
+ " gn refs out/Debug //base --all\n"
+ " List all targets depending directly or indirectly on //base:base.\n"
+ "\n"
+ " gn refs out/Debug \"//base/*\"\n"
+ " List all targets depending directly on any target in //base or\n"
+ " its subdirectories.\n"
+ "\n"
+ " gn refs out/Debug \"//base:*\"\n"
+ " List all targets depending directly on any target in\n"
+ " //base/BUILD.gn.\n"
+ "\n"
+ " gn refs out/Debug //base --tree\n"
+ " Print a reverse dependency tree of //base:base\n";
int RunRefs(const std::vector<std::string>& args) {
- if (args.size() != 1 && args.size() != 2) {
+ if (args.size() != 2) {
Err(Location(), "You're holding it wrong.",
- "Usage: \"gn refs <label_pattern>\"").PrintToStdout();
+ "Usage: \"gn refs <build_dir> <label_pattern>\"").PrintToStdout();
return 1;
}
- Pattern pattern(args[0]);
- // Check for common errors on input.
- if (args[0].find('*') == std::string::npos) {
- // We need to begin with a "//" and have a colon if there's no "*" or it
- // will be impossible to match anything.
- if (args[0].size() < 2 ||
- (args[0][0] != '/' && args[0][1] != '/') ||
- args[0].find(':') == std::string::npos) {
- OutputString("Assuming \"*" + args[0] +
- "*\". See \"gn help refs\" for more information.\n",
- DECORATION_YELLOW);
- pattern = Pattern("*" + args[0] + "*");
- }
- }
+ const CommandLine* cmdline = CommandLine::ForCurrentProcess();
+ bool tree = cmdline->HasSwitch("tree");
+ bool all = cmdline->HasSwitch("all");
+ bool all_toolchains = cmdline->HasSwitch("all-toolchains");
+ bool files = cmdline->HasSwitch("files");
Setup* setup = new Setup;
setup->set_check_for_bad_items(false);
- // TODO(brettw) bug 343726: Use a temporary directory instead of this
- // default one to avoid messing up any build that's in there.
- if (!setup->DoSetup("//out/Default/") || !setup->Run())
+ if (!setup->DoSetup(args[0]) || !setup->Run())
return 1;
- std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords();
+ // Figure out the target or targets that the user is querying.
+ std::vector<const Target*> query;
+ if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains,
+ &query))
+ return 1;
+ if (query.empty()) {
+ OutputString("\"" + args[1] + "\" matches no targets.\n");
+ return 0;
+ }
- const CommandLine* cmdline = CommandLine::ForCurrentProcess();
+ // Construct the reverse dependency tree.
+ DepMap dep_map;
+ FillDepMap(setup, &dep_map);
- bool file_output = cmdline->HasSwitch("files");
- std::set<std::string> unique_output;
-
- for (size_t record_index = 0; record_index < records.size(); record_index++) {
- const BuilderRecord* record = records[record_index];
- const BuilderRecord::BuilderRecordSet& deps = record->all_deps();
- for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin();
- d != deps.end(); ++d) {
- std::string label = (*d)->label().GetUserVisibleName(false);
- if (pattern.MatchesString(label)) {
- // Got a match.
- if (file_output) {
- unique_output.insert(FilePathToUTF8(FilePathForRecord(record)));
- break; // Found a match for this target's file, don't need more.
- } else {
- // We can get dupes when there are differnet toolchains involved,
- // so we want to send all output through the de-duper.
- unique_output.insert(
- record->item()->label().GetUserVisibleName(false) + " -> " +
- label);
- }
- }
+ if (tree) {
+ // Output dependency tree.
+ if (files) {
+ Err(NULL, "--files option can't be used with --tree option.")
+ .PrintToStdout();
+ return 1;
+ }
+ if (query.size() != 1) {
+ Err(NULL, "Query matches more than one target.",
+ "--tree only supports a single target as input.").PrintToStdout();
+ return 1;
}
+ if (all) {
+ // Recursively print all targets.
+ RecursivePrintTree(dep_map, query[0], NULL, 0);
+ } else {
+ // Recursively print unique targets.
+ TargetSet seen_targets;
+ RecursivePrintTree(dep_map, query[0], &seen_targets, 0);
+ }
+ } else if (all) {
+ // Output recursive dependencies, uniquified and flattened.
+ TargetSet results;
+ for (size_t query_i = 0; query_i < query.size(); query_i++)
+ RecursiveCollectChildRefs(dep_map, query[query_i], &results);
+ OutputResultSet(results, files);
+ } else {
+ // Output direct references of everything in the query.
+ TargetSet results;
+ for (size_t query_i = 0; query_i < query.size(); query_i++) {
+ DepMap::const_iterator dep_begin = dep_map.lower_bound(query[query_i]);
+ DepMap::const_iterator dep_end = dep_map.upper_bound(query[query_i]);
+ for (DepMap::const_iterator cur_dep = dep_begin;
+ cur_dep != dep_end; cur_dep++)
+ results.insert(cur_dep->second);
+ }
+ OutputResultSet(results, files);
}
- for (std::set<std::string>::iterator i = unique_output.begin();
- i != unique_output.end(); ++i)
- OutputString(*i + "\n");
-
return 0;
}
« no previous file with comments | « tools/gn/command_ls.cc ('k') | tools/gn/commands.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698