[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / gn / command_refs.cc
blob742b43b350338652429fdd0fad767718ed6fb877
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
3 // found in the LICENSE file.
5 #include <map>
6 #include <set>
8 #include "base/command_line.h"
9 #include "base/files/file_util.h"
10 #include "base/strings/string_split.h"
11 #include "tools/gn/commands.h"
12 #include "tools/gn/deps_iterator.h"
13 #include "tools/gn/filesystem_utils.h"
14 #include "tools/gn/input_file.h"
15 #include "tools/gn/item.h"
16 #include "tools/gn/setup.h"
17 #include "tools/gn/standard_out.h"
18 #include "tools/gn/target.h"
20 namespace commands {
22 namespace {
24 typedef std::set<const Target*> TargetSet;
25 typedef std::vector<const Target*> TargetVector;
27 // Maps targets to the list of targets that depend on them.
28 typedef std::multimap<const Target*, const Target*> DepMap;
30 // Populates the reverse dependency map for the targets in the Setup.
31 void FillDepMap(Setup* setup, DepMap* dep_map) {
32 for (const auto& target : setup->builder()->GetAllResolvedTargets()) {
33 for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL))
34 dep_map->insert(std::make_pair(dep_pair.ptr, target));
38 // Forward declaration for function below.
39 size_t RecursivePrintTargetDeps(const DepMap& dep_map,
40 const Target* target,
41 TargetSet* seen_targets,
42 int indent_level);
44 // Prints the target and its dependencies in tree form. If the set is non-null,
45 // new targets encountered will be added to the set, and if a ref is in the set
46 // already, it will not be recused into. When the set is null, all refs will be
47 // printed.
49 // Returns the number of items printed.
50 size_t RecursivePrintTarget(const DepMap& dep_map,
51 const Target* target,
52 TargetSet* seen_targets,
53 int indent_level) {
54 std::string indent(indent_level * 2, ' ');
55 size_t count = 1;
57 // Only print the toolchain for non-default-toolchain targets.
58 OutputString(indent + target->label().GetUserVisibleName(
59 !target->settings()->is_default()));
61 bool print_children = true;
62 if (seen_targets) {
63 if (seen_targets->find(target) == seen_targets->end()) {
64 // New target, mark it visited.
65 seen_targets->insert(target);
66 } else {
67 // Already seen.
68 print_children = false;
69 // Only print "..." if something is actually elided, which means that
70 // the current target has children.
71 if (dep_map.lower_bound(target) != dep_map.upper_bound(target))
72 OutputString("...");
76 OutputString("\n");
77 if (print_children) {
78 count += RecursivePrintTargetDeps(dep_map, target, seen_targets,
79 indent_level + 1);
81 return count;
84 // Prints refs of the given target (not the target itself). See
85 // RecursivePrintTarget.
86 size_t RecursivePrintTargetDeps(const DepMap& dep_map,
87 const Target* target,
88 TargetSet* seen_targets,
89 int indent_level) {
90 DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
91 DepMap::const_iterator dep_end = dep_map.upper_bound(target);
92 size_t count = 0;
93 for (DepMap::const_iterator cur_dep = dep_begin;
94 cur_dep != dep_end; cur_dep++) {
95 count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets,
96 indent_level);
98 return count;
101 void RecursiveCollectChildRefs(const DepMap& dep_map,
102 const Target* target,
103 TargetSet* results);
105 // Recursively finds all targets that reference the given one, and additionally
106 // adds the current one to the list.
107 void RecursiveCollectRefs(const DepMap& dep_map,
108 const Target* target,
109 TargetSet* results) {
110 if (results->find(target) != results->end())
111 return; // Already found this target.
112 results->insert(target);
113 RecursiveCollectChildRefs(dep_map, target, results);
116 // Recursively finds all targets that reference the given one.
117 void RecursiveCollectChildRefs(const DepMap& dep_map,
118 const Target* target,
119 TargetSet* results) {
120 DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
121 DepMap::const_iterator dep_end = dep_map.upper_bound(target);
122 for (DepMap::const_iterator cur_dep = dep_begin;
123 cur_dep != dep_end; cur_dep++)
124 RecursiveCollectRefs(dep_map, cur_dep->second, results);
127 bool TargetContainsFile(const Target* target, const SourceFile& file) {
128 for (const auto& cur_file : target->sources()) {
129 if (cur_file == file)
130 return true;
132 for (const auto& cur_file : target->public_headers()) {
133 if (cur_file == file)
134 return true;
136 for (const auto& cur_file : target->inputs()) {
137 if (cur_file == file)
138 return true;
140 for (const auto& cur_file : target->data()) {
141 if (cur_file == file.value())
142 return true;
145 std::vector<SourceFile> outputs;
146 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
147 for (const auto& cur_file : outputs) {
148 if (cur_file == file)
149 return true;
151 return false;
154 void GetTargetsContainingFile(Setup* setup,
155 const std::vector<const Target*>& all_targets,
156 const SourceFile& file,
157 bool all_toolchains,
158 UniqueVector<const Target*>* matches) {
159 Label default_toolchain = setup->loader()->default_toolchain_label();
160 for (const auto& target : all_targets) {
161 if (!all_toolchains) {
162 // Only check targets in the default toolchain.
163 if (target->label().GetToolchainLabel() != default_toolchain)
164 continue;
166 if (TargetContainsFile(target, file))
167 matches->push_back(target);
171 bool TargetReferencesConfig(const Target* target, const Config* config) {
172 for (const LabelConfigPair& cur : target->configs()) {
173 if (cur.ptr == config)
174 return true;
176 for (const LabelConfigPair& cur : target->public_configs()) {
177 if (cur.ptr == config)
178 return true;
180 return false;
183 void GetTargetsReferencingConfig(Setup* setup,
184 const std::vector<const Target*>& all_targets,
185 const Config* config,
186 bool all_toolchains,
187 UniqueVector<const Target*>* matches) {
188 Label default_toolchain = setup->loader()->default_toolchain_label();
189 for (const auto& target : all_targets) {
190 if (!all_toolchains) {
191 // Only check targets in the default toolchain.
192 if (target->label().GetToolchainLabel() != default_toolchain)
193 continue;
195 if (TargetReferencesConfig(target, config))
196 matches->push_back(target);
200 // Returns the number of matches printed.
201 size_t DoTreeOutput(const DepMap& dep_map,
202 const UniqueVector<const Target*>& implicit_target_matches,
203 const UniqueVector<const Target*>& explicit_target_matches,
204 bool all) {
205 TargetSet seen_targets;
206 size_t count = 0;
208 // Implicit targets don't get printed themselves.
209 for (const Target* target : implicit_target_matches) {
210 if (all)
211 count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
212 else
213 count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
216 // Explicit targets appear in the output.
217 for (const Target* target : implicit_target_matches) {
218 if (all)
219 count += RecursivePrintTarget(dep_map, target, nullptr, 0);
220 else
221 count += RecursivePrintTarget(dep_map, target, &seen_targets, 0);
224 return count;
227 // Returns the number of matches printed.
228 size_t DoAllListOutput(
229 const DepMap& dep_map,
230 const UniqueVector<const Target*>& implicit_target_matches,
231 const UniqueVector<const Target*>& explicit_target_matches) {
232 // Output recursive dependencies, uniquified and flattened.
233 TargetSet results;
235 for (const Target* target : implicit_target_matches)
236 RecursiveCollectChildRefs(dep_map, target, &results);
237 for (const Target* target : explicit_target_matches) {
238 // Explicit targets also get added to the output themselves.
239 results.insert(target);
240 RecursiveCollectChildRefs(dep_map, target, &results);
243 FilterAndPrintTargetSet(false, results);
244 return results.size();
247 // Returns the number of matches printed.
248 size_t DoDirectListOutput(
249 const DepMap& dep_map,
250 const UniqueVector<const Target*>& implicit_target_matches,
251 const UniqueVector<const Target*>& explicit_target_matches) {
252 TargetSet results;
254 // Output everything that refers to the implicit ones.
255 for (const Target* target : implicit_target_matches) {
256 DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
257 DepMap::const_iterator dep_end = dep_map.upper_bound(target);
258 for (DepMap::const_iterator cur_dep = dep_begin;
259 cur_dep != dep_end; cur_dep++)
260 results.insert(cur_dep->second);
263 // And just output the explicit ones directly (these are the target matches
264 // when referring to what references a file or config).
265 for (const Target* target : explicit_target_matches)
266 results.insert(target);
268 FilterAndPrintTargetSet(false, results);
269 return results.size();
272 } // namespace
274 const char kRefs[] = "refs";
275 const char kRefs_HelpShort[] =
276 "refs: Find stuff referencing a target or file.";
277 const char kRefs_Help[] =
278 "gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* "
279 "[--all]\n"
280 " [--all-toolchains] [--as=...] [--testonly=...] [--type=...]\n"
281 "\n"
282 " Finds reverse dependencies (which targets reference something). The\n"
283 " input is a list containing:\n"
284 "\n"
285 " - Target label: The result will be which targets depend on it.\n"
286 "\n"
287 " - Config label: The result will be which targets list the given\n"
288 " config in its \"configs\" or \"public_configs\" list.\n"
289 "\n"
290 " - Label pattern: The result will be which targets depend on any\n"
291 " target matching the given pattern. Patterns will not match\n"
292 " configs. These are not general regular expressions, see\n"
293 " \"gn help label_pattern\" for details.\n"
294 "\n"
295 " - File name: The result will be which targets list the given file in\n"
296 " its \"inputs\", \"sources\", \"public\", \"data\", or \"outputs\".\n"
297 " Any input that does not contain wildcards and does not match a\n"
298 " target or a config will be treated as a file.\n"
299 "\n"
300 " - Response file: If the input starts with an \"@\", it will be\n"
301 " interpreted as a path to a file containing a list of labels or\n"
302 " file names, one per line. This allows us to handle long lists\n"
303 " of inputs without worrying about command line limits.\n"
304 "\n"
305 "Options\n"
306 "\n"
307 " --all\n"
308 " When used without --tree, will recurse and display all unique\n"
309 " dependencies of the given targets. For example, if the input is\n"
310 " a target, this will output all targets that depend directly or\n"
311 " indirectly on the input. If the input is a file, this will output\n"
312 " all targets that depend directly or indirectly on that file.\n"
313 "\n"
314 " When used with --tree, turns off eliding to show a complete tree.\n"
315 "\n"
316 " --all-toolchains\n"
317 " Normally only inputs in the default toolchain will be included.\n"
318 " This switch will turn on matching all toolchains.\n"
319 "\n"
320 " For example, a file is in a target might be compiled twice:\n"
321 " once in the default toolchain and once in a secondary one. Without\n"
322 " this flag, only the default toolchain one will be matched and\n"
323 " printed (potentially with its recursive dependencies, depending on\n"
324 " the other options). With this flag, both will be printed\n"
325 " (potentially with both of their recursive dependencies).\n"
326 "\n"
327 TARGET_PRINTING_MODE_COMMAND_LINE_HELP
328 "\n"
329 " -q\n"
330 " Quiet. If nothing matches, don't print any output. Without this\n"
331 " option, if there are no matches there will be an informational\n"
332 " message printed which might interfere with scripts processing the\n"
333 " output.\n"
334 "\n"
335 TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
336 "\n"
337 " --tree\n"
338 " Outputs a reverse dependency tree from the given target.\n"
339 " Duplicates will be elided. Combine with --all to see a full\n"
340 " dependency tree.\n"
341 "\n"
342 " Tree output can not be used with the filtering or output flags:\n"
343 " --as, --type, --testonly.\n"
344 "\n"
345 TARGET_TYPE_FILTER_COMMAND_LINE_HELP
346 "\n"
347 "Examples (target input)\n"
348 "\n"
349 " gn refs out/Debug //tools/gn:gn\n"
350 " Find all targets depending on the given exact target name.\n"
351 "\n"
352 " gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim\n"
353 " Edit all .gn files containing references to //base:i18n\n"
354 "\n"
355 " gn refs out/Debug //base --all\n"
356 " List all targets depending directly or indirectly on //base:base.\n"
357 "\n"
358 " gn refs out/Debug \"//base/*\"\n"
359 " List all targets depending directly on any target in //base or\n"
360 " its subdirectories.\n"
361 "\n"
362 " gn refs out/Debug \"//base:*\"\n"
363 " List all targets depending directly on any target in\n"
364 " //base/BUILD.gn.\n"
365 "\n"
366 " gn refs out/Debug //base --tree\n"
367 " Print a reverse dependency tree of //base:base\n"
368 "\n"
369 "Examples (file input)\n"
370 "\n"
371 " gn refs out/Debug //base/macros.h\n"
372 " Print target(s) listing //base/macros.h as a source.\n"
373 "\n"
374 " gn refs out/Debug //base/macros.h --tree\n"
375 " Display a reverse dependency tree to get to the given file. This\n"
376 " will show how dependencies will reference that file.\n"
377 "\n"
378 " gn refs out/Debug //base/macros.h //base/at_exit.h --all\n"
379 " Display all unique targets with some dependency path to a target\n"
380 " containing either of the given files as a source.\n"
381 "\n"
382 " gn refs out/Debug //base/macros.h --testonly=true --type=executable\n"
383 " --all --as=output\n"
384 " Display the executable file names of all test executables\n"
385 " potentially affected by a change to the given file.\n";
387 int RunRefs(const std::vector<std::string>& args) {
388 if (args.size() <= 1) {
389 Err(Location(), "You're holding it wrong.",
390 "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"")
391 .PrintToStdout();
392 return 1;
395 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
396 bool tree = cmdline->HasSwitch("tree");
397 bool all = cmdline->HasSwitch("all");
398 bool all_toolchains = cmdline->HasSwitch("all-toolchains");
400 Setup* setup = new Setup;
401 setup->build_settings().set_check_for_bad_items(false);
402 if (!setup->DoSetup(args[0], false) || !setup->Run())
403 return 1;
405 // The inputs are everything but the first arg (which is the build dir).
406 std::vector<std::string> inputs;
407 for (size_t i = 1; i < args.size(); i++) {
408 if (args[i][0] == '@') {
409 // The argument is as a path to a response file.
410 std::string contents;
411 bool ret = base::ReadFileToString(UTF8ToFilePath(args[i].substr(1)),
412 &contents);
413 if (!ret) {
414 Err(Location(), "Response file " + args[i].substr(1) + " not found.")
415 .PrintToStdout();
416 return 1;
418 for (const std::string& line : base::SplitString(
419 contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
420 if (!line.empty())
421 inputs.push_back(line);
423 } else {
424 // The argument is a label or a path.
425 inputs.push_back(args[i]);
429 // Get the matches for the command-line input.
430 UniqueVector<const Target*> target_matches;
431 UniqueVector<const Config*> config_matches;
432 UniqueVector<const Toolchain*> toolchain_matches;
433 UniqueVector<SourceFile> file_matches;
434 if (!ResolveFromCommandLineInput(setup, inputs, all_toolchains,
435 &target_matches, &config_matches,
436 &toolchain_matches, &file_matches))
437 return 1;
439 // When you give a file or config as an input, you want the targets that are
440 // associated with it. We don't want to just append this to the
441 // target_matches, however, since these targets should actually be listed in
442 // the output, while for normal targets you don't want to see the inputs,
443 // only what refers to them.
444 std::vector<const Target*> all_targets =
445 setup->builder()->GetAllResolvedTargets();
446 UniqueVector<const Target*> explicit_target_matches;
447 for (const auto& file : file_matches) {
448 GetTargetsContainingFile(setup, all_targets, file, all_toolchains,
449 &explicit_target_matches);
451 for (const auto& config : config_matches) {
452 GetTargetsReferencingConfig(setup, all_targets, config, all_toolchains,
453 &explicit_target_matches);
456 // Tell the user if their input matches no files or labels. We need to check
457 // both that it matched no targets and no configs. File input will already
458 // have been converted to targets at this point. Configs will have been
459 // converted to targets also, but there could be no targets referencing the
460 // config, which is different than no config with that name.
461 bool quiet = cmdline->HasSwitch("q");
462 if (!quiet && config_matches.empty() &&
463 explicit_target_matches.empty() && target_matches.empty()) {
464 OutputString("The input matches no targets, configs, or files.\n",
465 DECORATION_YELLOW);
466 return 1;
469 // Construct the reverse dependency tree.
470 DepMap dep_map;
471 FillDepMap(setup, &dep_map);
473 size_t cnt = 0;
474 if (tree)
475 cnt = DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
476 else if (all)
477 cnt = DoAllListOutput(dep_map, target_matches, explicit_target_matches);
478 else
479 cnt = DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
481 // If you ask for the references of a valid target, but that target has
482 // nothing referencing it, we'll get here without having printed anything.
483 if (!quiet && cnt == 0)
484 OutputString("Nothing references this.\n", DECORATION_YELLOW);
486 return 0;
489 } // namespace commands