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

Side by Side 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, 3 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 unified diff | Download patch
« no previous file with comments | « tools/gn/command_ls.cc ('k') | tools/gn/commands.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <map>
5 #include <set> 6 #include <set>
6 7
7 #include "base/command_line.h" 8 #include "base/command_line.h"
8 #include "tools/gn/commands.h" 9 #include "tools/gn/commands.h"
9 #include "tools/gn/filesystem_utils.h" 10 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/input_file.h" 11 #include "tools/gn/input_file.h"
11 #include "tools/gn/item.h" 12 #include "tools/gn/item.h"
12 #include "tools/gn/pattern.h"
13 #include "tools/gn/setup.h" 13 #include "tools/gn/setup.h"
14 #include "tools/gn/standard_out.h" 14 #include "tools/gn/standard_out.h"
15 #include "tools/gn/target.h" 15 #include "tools/gn/target.h"
16 16
17 namespace commands { 17 namespace commands {
18 18
19 namespace { 19 namespace {
20 20
21 // Returns the file path generating this record. 21 typedef std::set<const Target*> TargetSet;
22 base::FilePath FilePathForRecord(const BuilderRecord* record) { 22 typedef std::vector<const Target*> TargetVector;
23 if (!record->item()) 23
24 return base::FilePath(FILE_PATH_LITERAL("=UNRESOLVED DEPENDENCY=")); 24 // Maps targets to the list of targets that depend on them.
25 return record->item()->defined_from()->GetRange().begin().file() 25 typedef std::multimap<const Target*, const Target*> DepMap;
26 ->physical_name(); 26
27 // Populates the reverse dependency map for the targets in the Setup.
28 void FillDepMap(Setup* setup, DepMap* dep_map) {
29 std::vector<const Target*> targets =
30 setup->builder()->GetAllResolvedTargets();
31
32 for (size_t target_i = 0; target_i < targets.size(); target_i++) {
33 const Target* target = targets[target_i];
34
35 // Add all deps to the map.
36 const LabelTargetVector& deps = target->deps();
37 for (size_t dep_i = 0; dep_i < deps.size(); dep_i++)
38 dep_map->insert(std::make_pair(deps[dep_i].ptr, target));
39
40 // Include data deps as well.
41 const LabelTargetVector& datadeps = target->datadeps();
42 for (size_t dep_i = 0; dep_i < datadeps.size(); dep_i++)
43 dep_map->insert(std::make_pair(datadeps[dep_i].ptr, target));
44 }
45 }
46
47 // Returns the file path generating this item.
48 base::FilePath FilePathForItem(const Item* item) {
49 return item->defined_from()->GetRange().begin().file()->physical_name();
50 }
51
52 // Prints the targets which are the result of a query. This list is sorted
53 // and, if as_files is set, the unique filenames matching those targets will
54 // be used.
55 void OutputResultSet(const TargetSet& results, bool as_files) {
56 if (results.empty())
57 return;
58
59 if (as_files) {
60 // Output the set of unique source files.
61 std::set<std::string> unique_files;
62 for (TargetSet::const_iterator iter = results.begin();
63 iter != results.end(); ++iter)
64 unique_files.insert(FilePathToUTF8(FilePathForItem(*iter)));
65
66 for (std::set<std::string>::const_iterator iter = unique_files.begin();
67 iter != unique_files.end(); ++iter) {
68 OutputString(*iter + "\n");
69 }
70 } else {
71 // Output sorted and uniquified list of labels. The set will sort the
72 // labels.
73 std::set<Label> unique_labels;
74 for (TargetSet::const_iterator iter = results.begin();
75 iter != results.end(); ++iter)
76 unique_labels.insert((*iter)->label());
77
78 // Grab the label of the default toolchain from a random target.
79 Label default_tc_label =
80 (*results.begin())->settings()->default_toolchain_label();
81
82 for (std::set<Label>::const_iterator iter = unique_labels.begin();
83 iter != unique_labels.end(); ++iter) {
84 // Print toolchain only for ones not in the default toolchain.
85 OutputString(iter->GetUserVisibleName(
86 iter->GetToolchainLabel() != default_tc_label));
87 OutputString("\n");
88 }
89 }
90 }
91
92 // Prints refs of the given target (not the target itself). If the set is
93 // non-null, new targets encountered will be added to the set, and if a ref is
94 // in the set already, it will not be recused into. When the set is null, all
95 // refs will be printed.
96 void RecursivePrintTree(const DepMap& dep_map,
97 const Target* target,
98 TargetSet* seen_targets,
99 int indent_level) {
100 std::string indent(indent_level * 2, ' ');
101
102 DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
103 DepMap::const_iterator dep_end = dep_map.upper_bound(target);
104 for (DepMap::const_iterator cur_dep = dep_begin;
105 cur_dep != dep_end; cur_dep++) {
106 const Target* cur_target = cur_dep->second;
107
108 // Only print the toolchain for non-default-toolchain targets.
109 OutputString(indent + cur_target->label().GetUserVisibleName(
110 !cur_target->settings()->is_default()));
111
112 bool print_children = true;
113 if (seen_targets) {
114 if (seen_targets->find(cur_target) == seen_targets->end()) {
115 // New target, mark it visited.
116 seen_targets->insert(cur_target);
117 } else {
118 // Already seen.
119 print_children = false;
120 // Only print "..." if something is actually elided, which means that
121 // the current target has children.
122 if (dep_map.lower_bound(cur_target) != dep_map.upper_bound(cur_target))
123 OutputString("...");
124 }
125 }
126
127 OutputString("\n");
128 if (print_children)
129 RecursivePrintTree(dep_map, cur_target, seen_targets, indent_level + 1);
130 }
131 }
132
133 void RecursiveCollectChildRefs(const DepMap& dep_map,
134 const Target* target,
135 TargetSet* results);
136
137 // Recursively finds all targets that reference the given one, and additionally
138 // adds the current one to the list.
139 void RecursiveCollectRefs(const DepMap& dep_map,
140 const Target* target,
141 TargetSet* results) {
142 if (results->find(target) != results->end())
143 return; // Already found this target.
144 results->insert(target);
145 RecursiveCollectChildRefs(dep_map, target, results);
146 }
147
148 // Recursively finds all targets that reference the given one.
149 void RecursiveCollectChildRefs(const DepMap& dep_map,
150 const Target* target,
151 TargetSet* results) {
152 DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
153 DepMap::const_iterator dep_end = dep_map.upper_bound(target);
154 for (DepMap::const_iterator cur_dep = dep_begin;
155 cur_dep != dep_end; cur_dep++)
156 RecursiveCollectRefs(dep_map, cur_dep->second, results);
27 } 157 }
28 158
29 } // namespace 159 } // namespace
30 160
31 const char kRefs[] = "refs"; 161 const char kRefs[] = "refs";
32 const char kRefs_HelpShort[] = 162 const char kRefs_HelpShort[] =
33 "refs: Find stuff referencing a target, directory, or config."; 163 "refs: Find stuff referencing a target, directory, or config.";
34 const char kRefs_Help[] = 164 const char kRefs_Help[] =
35 "gn refs <label_pattern> [--files]\n" 165 "gn refs <build_dir> <label_pattern> [--files] [--tree] [--all]\n"
166 " [--all-toolchains]\n"
36 "\n" 167 "\n"
37 " Finds code referencing a given label. The label can be a\n" 168 " Finds which targets reference a given target or targets (reverse\n"
38 " target or config name. Unlike most other commands, unresolved\n" 169 " dependencies).\n"
39 " dependencies will be tolerated. This allows you to use this command\n"
40 " to find references to targets you're in the process of moving.\n"
41 "\n" 170 "\n"
42 " By default, the mapping from source item to dest item (where the\n" 171 " The <label_pattern> can take exact labels or patterns that match more\n"
43 " pattern matches the dest item). See \"gn help pattern\" for\n" 172 " than one (although not general regular expressions).\n"
44 " information on pattern-matching rules.\n" 173 " See \"gn help label_pattern\" for details.\n"
45 "\n" 174 "\n"
46 "Option:\n" 175 " --all\n"
176 " When used without --tree, will recurse and display all unique\n"
177 " dependencies of the given targets. When used with --tree, turns\n"
178 " off eliding to show a complete tree.\n"
179 "\n"
180 " --all-toolchains\n"
181 " Make the label pattern matche all toolchains. If the label pattern\n"
182 " does not specify an explicit toolchain, labels from all toolchains\n"
183 " will be matched (normally only the default toolchain is matched\n"
184 " when no toolchain is specified).\n"
185 "\n"
47 " --files\n" 186 " --files\n"
48 " Output unique filenames referencing a matched target or config.\n" 187 " Output unique filenames referencing a matched target or config.\n"
188 " These will be relative to the source root directory such that they\n"
189 " are suitable for piping to other commands.\n"
49 "\n" 190 "\n"
50 "Examples:\n" 191 " --tree\n"
51 " gn refs \"//tools/gn/*\"\n" 192 " Outputs a reverse dependency tree from the given target. The label\n"
52 " Find all targets depending on any target or config in the\n" 193 " pattern must match one target exactly. Duplicates will be elided.\n"
53 " \"tools/gn\" directory.\n" 194 " Combine with --all to see a full dependency tree.\n"
54 "\n" 195 "\n"
55 " gn refs //tools/gn:gn\n" 196 "Examples\n"
197 "\n"
198 " gn refs out/Debug //tools/gn:gn\n"
56 " Find all targets depending on the given exact target name.\n" 199 " Find all targets depending on the given exact target name.\n"
57 "\n" 200 "\n"
58 " gn refs \"*gtk*\" --files\n" 201 " gn refs out/Debug //base:i18n --files | xargs gvim\n"
59 " Find all unique buildfiles with a dependency on a target that has\n" 202 " Edit all files containing references to //base:i18n\n"
60 " the substring \"gtk\" in the name.\n"; 203 "\n"
204 " gn refs out/Debug //base --all\n"
205 " List all targets depending directly or indirectly on //base:base.\n"
206 "\n"
207 " gn refs out/Debug \"//base/*\"\n"
208 " List all targets depending directly on any target in //base or\n"
209 " its subdirectories.\n"
210 "\n"
211 " gn refs out/Debug \"//base:*\"\n"
212 " List all targets depending directly on any target in\n"
213 " //base/BUILD.gn.\n"
214 "\n"
215 " gn refs out/Debug //base --tree\n"
216 " Print a reverse dependency tree of //base:base\n";
61 217
62 int RunRefs(const std::vector<std::string>& args) { 218 int RunRefs(const std::vector<std::string>& args) {
63 if (args.size() != 1 && args.size() != 2) { 219 if (args.size() != 2) {
64 Err(Location(), "You're holding it wrong.", 220 Err(Location(), "You're holding it wrong.",
65 "Usage: \"gn refs <label_pattern>\"").PrintToStdout(); 221 "Usage: \"gn refs <build_dir> <label_pattern>\"").PrintToStdout();
66 return 1; 222 return 1;
67 } 223 }
68 224
69 Pattern pattern(args[0]); 225 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
70 // Check for common errors on input. 226 bool tree = cmdline->HasSwitch("tree");
71 if (args[0].find('*') == std::string::npos) { 227 bool all = cmdline->HasSwitch("all");
72 // We need to begin with a "//" and have a colon if there's no "*" or it 228 bool all_toolchains = cmdline->HasSwitch("all-toolchains");
73 // will be impossible to match anything. 229 bool files = cmdline->HasSwitch("files");
74 if (args[0].size() < 2 ||
75 (args[0][0] != '/' && args[0][1] != '/') ||
76 args[0].find(':') == std::string::npos) {
77 OutputString("Assuming \"*" + args[0] +
78 "*\". See \"gn help refs\" for more information.\n",
79 DECORATION_YELLOW);
80 pattern = Pattern("*" + args[0] + "*");
81 }
82 }
83 230
84 Setup* setup = new Setup; 231 Setup* setup = new Setup;
85 setup->set_check_for_bad_items(false); 232 setup->set_check_for_bad_items(false);
86 // TODO(brettw) bug 343726: Use a temporary directory instead of this 233 if (!setup->DoSetup(args[0]) || !setup->Run())
87 // default one to avoid messing up any build that's in there.
88 if (!setup->DoSetup("//out/Default/") || !setup->Run())
89 return 1; 234 return 1;
90 235
91 std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords(); 236 // Figure out the target or targets that the user is querying.
92 237 std::vector<const Target*> query;
93 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); 238 if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains,
94 239 &query))
95 bool file_output = cmdline->HasSwitch("files"); 240 return 1;
96 std::set<std::string> unique_output; 241 if (query.empty()) {
97 242 OutputString("\"" + args[1] + "\" matches no targets.\n");
98 for (size_t record_index = 0; record_index < records.size(); record_index++) { 243 return 0;
99 const BuilderRecord* record = records[record_index];
100 const BuilderRecord::BuilderRecordSet& deps = record->all_deps();
101 for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin();
102 d != deps.end(); ++d) {
103 std::string label = (*d)->label().GetUserVisibleName(false);
104 if (pattern.MatchesString(label)) {
105 // Got a match.
106 if (file_output) {
107 unique_output.insert(FilePathToUTF8(FilePathForRecord(record)));
108 break; // Found a match for this target's file, don't need more.
109 } else {
110 // We can get dupes when there are differnet toolchains involved,
111 // so we want to send all output through the de-duper.
112 unique_output.insert(
113 record->item()->label().GetUserVisibleName(false) + " -> " +
114 label);
115 }
116 }
117 }
118 } 244 }
119 245
120 for (std::set<std::string>::iterator i = unique_output.begin(); 246 // Construct the reverse dependency tree.
121 i != unique_output.end(); ++i) 247 DepMap dep_map;
122 OutputString(*i + "\n"); 248 FillDepMap(setup, &dep_map);
249
250 if (tree) {
251 // Output dependency tree.
252 if (files) {
253 Err(NULL, "--files option can't be used with --tree option.")
254 .PrintToStdout();
255 return 1;
256 }
257 if (query.size() != 1) {
258 Err(NULL, "Query matches more than one target.",
259 "--tree only supports a single target as input.").PrintToStdout();
260 return 1;
261 }
262 if (all) {
263 // Recursively print all targets.
264 RecursivePrintTree(dep_map, query[0], NULL, 0);
265 } else {
266 // Recursively print unique targets.
267 TargetSet seen_targets;
268 RecursivePrintTree(dep_map, query[0], &seen_targets, 0);
269 }
270 } else if (all) {
271 // Output recursive dependencies, uniquified and flattened.
272 TargetSet results;
273 for (size_t query_i = 0; query_i < query.size(); query_i++)
274 RecursiveCollectChildRefs(dep_map, query[query_i], &results);
275 OutputResultSet(results, files);
276 } else {
277 // Output direct references of everything in the query.
278 TargetSet results;
279 for (size_t query_i = 0; query_i < query.size(); query_i++) {
280 DepMap::const_iterator dep_begin = dep_map.lower_bound(query[query_i]);
281 DepMap::const_iterator dep_end = dep_map.upper_bound(query[query_i]);
282 for (DepMap::const_iterator cur_dep = dep_begin;
283 cur_dep != dep_end; cur_dep++)
284 results.insert(cur_dep->second);
285 }
286 OutputResultSet(results, files);
287 }
123 288
124 return 0; 289 return 0;
125 } 290 }
126 291
127 } // namespace commands 292 } // namespace commands
OLDNEW
« 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