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