• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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