• 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 = TargetSet;
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->add(target)) {
69       // Already seen.
70       print_children = false;
71       // Only print "..." if something is actually elided, which means that
72       // the current target has children.
73       if (dep_map.lower_bound(target) != dep_map.upper_bound(target))
74         OutputString("...");
75     }
76   }
77 
78   OutputString("\n");
79   if (print_children) {
80     count += RecursivePrintTargetDeps(dep_map, target, seen_targets,
81                                       indent_level + 1);
82   }
83   return count;
84 }
85 
86 // Prints refs of the given target (not the target itself). See
87 // RecursivePrintTarget.
RecursivePrintTargetDeps(const DepMap & dep_map,const Target * target,TargetSet * seen_targets,int indent_level)88 size_t RecursivePrintTargetDeps(const DepMap& dep_map,
89                                 const Target* target,
90                                 TargetSet* seen_targets,
91                                 int indent_level) {
92   DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
93   DepMap::const_iterator dep_end = dep_map.upper_bound(target);
94   size_t count = 0;
95   for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
96        cur_dep++) {
97     count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets,
98                                   indent_level);
99   }
100   return count;
101 }
102 
103 void RecursiveCollectChildRefs(const DepMap& dep_map,
104                                const Target* target,
105                                TargetSet* results);
106 
107 // Recursively finds all targets that reference the given one, and additionally
108 // adds the current one to the list.
RecursiveCollectRefs(const DepMap & dep_map,const Target * target,TargetSet * results)109 void RecursiveCollectRefs(const DepMap& dep_map,
110                           const Target* target,
111                           TargetSet* results) {
112   if (!results->add(target))
113     return;  // Already found this target.
114   RecursiveCollectChildRefs(dep_map, target, results);
115 }
116 
117 // Recursively finds all targets that reference the given one.
RecursiveCollectChildRefs(const DepMap & dep_map,const Target * target,TargetSet * results)118 void RecursiveCollectChildRefs(const DepMap& dep_map,
119                                const Target* target,
120                                TargetSet* results) {
121   DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
122   DepMap::const_iterator dep_end = dep_map.upper_bound(target);
123   for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
124        cur_dep++)
125     RecursiveCollectRefs(dep_map, cur_dep->second, results);
126 }
127 
TargetReferencesConfig(const Target * target,const Config * config)128 bool TargetReferencesConfig(const Target* target, const Config* config) {
129   for (const LabelConfigPair& cur : target->configs()) {
130     if (cur.ptr == config)
131       return true;
132   }
133   for (const LabelConfigPair& cur : target->public_configs()) {
134     if (cur.ptr == config)
135       return true;
136   }
137   return false;
138 }
139 
GetTargetsReferencingConfig(Setup * setup,const std::vector<const Target * > & all_targets,const Config * config,bool default_toolchain_only,UniqueVector<const Target * > * matches)140 void GetTargetsReferencingConfig(Setup* setup,
141                                  const std::vector<const Target*>& all_targets,
142                                  const Config* config,
143                                  bool default_toolchain_only,
144                                  UniqueVector<const Target*>* matches) {
145   Label default_toolchain = setup->loader()->default_toolchain_label();
146   for (auto* target : all_targets) {
147     if (default_toolchain_only) {
148       // Only check targets in the default toolchain.
149       if (target->label().GetToolchainLabel() != default_toolchain)
150         continue;
151     }
152     if (TargetReferencesConfig(target, config))
153       matches->push_back(target);
154   }
155 }
156 
157 // 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)158 size_t DoTreeOutput(const DepMap& dep_map,
159                     const UniqueVector<const Target*>& implicit_target_matches,
160                     const UniqueVector<const Target*>& explicit_target_matches,
161                     bool all) {
162   TargetSet seen_targets;
163   size_t count = 0;
164 
165   // Implicit targets don't get printed themselves.
166   for (const Target* target : implicit_target_matches) {
167     if (all)
168       count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
169     else
170       count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
171   }
172 
173   // Explicit targets appear in the output.
174   for (const Target* target : implicit_target_matches) {
175     if (all)
176       count += RecursivePrintTarget(dep_map, target, nullptr, 0);
177     else
178       count += RecursivePrintTarget(dep_map, target, &seen_targets, 0);
179   }
180 
181   return count;
182 }
183 
184 // 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)185 size_t DoAllListOutput(
186     const DepMap& dep_map,
187     const UniqueVector<const Target*>& implicit_target_matches,
188     const UniqueVector<const Target*>& explicit_target_matches) {
189   // Output recursive dependencies, uniquified and flattened.
190   TargetSet results;
191 
192   for (const Target* target : implicit_target_matches)
193     RecursiveCollectChildRefs(dep_map, target, &results);
194   for (const Target* target : explicit_target_matches) {
195     // Explicit targets also get added to the output themselves.
196     results.insert(target);
197     RecursiveCollectChildRefs(dep_map, target, &results);
198   }
199 
200   FilterAndPrintTargetSet(false, results);
201   return results.size();
202 }
203 
204 // 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)205 size_t DoDirectListOutput(
206     const DepMap& dep_map,
207     const UniqueVector<const Target*>& implicit_target_matches,
208     const UniqueVector<const Target*>& explicit_target_matches) {
209   TargetSet results;
210 
211   // Output everything that refers to the implicit ones.
212   for (const Target* target : implicit_target_matches) {
213     DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
214     DepMap::const_iterator dep_end = dep_map.upper_bound(target);
215     for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
216          cur_dep++)
217       results.insert(cur_dep->second);
218   }
219 
220   // And just output the explicit ones directly (these are the target matches
221   // when referring to what references a file or config).
222   for (const Target* target : explicit_target_matches)
223     results.insert(target);
224 
225   FilterAndPrintTargetSet(false, results);
226   return results.size();
227 }
228 
229 }  // namespace
230 
231 const char kRefs[] = "refs";
232 const char kRefs_HelpShort[] = "refs: Find stuff referencing a target or file.";
233 const char kRefs_Help[] =
234     R"(gn refs
235 
236   gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all]
237           [--default-toolchain] [--as=...] [--testonly=...] [--type=...]
238 
239   Finds reverse dependencies (which targets reference something). The input is
240   a list containing:
241 
242    - Target label: The result will be which targets depend on it.
243 
244    - Config label: The result will be which targets list the given config in
245      its "configs" or "public_configs" list.
246 
247    - Label pattern: The result will be which targets depend on any target
248      matching the given pattern. Patterns will not match configs. These are not
249      general regular expressions, see "gn help label_pattern" for details.
250 
251    - File name: The result will be which targets list the given file in its
252      "inputs", "sources", "public", "data", or "outputs". Any input that does
253      not contain wildcards and does not match a target or a config will be
254      treated as a file.
255 
256    - Response file: If the input starts with an "@", it will be interpreted as
257      a path to a file containing a list of labels or file names, one per line.
258      This allows us to handle long lists of inputs without worrying about
259      command line limits.
260 
261 Options
262 
263   --all
264       When used without --tree, will recurse and display all unique
265       dependencies of the given targets. For example, if the input is a target,
266       this will output all targets that depend directly or indirectly on the
267       input. If the input is a file, this will output all targets that depend
268       directly or indirectly on that file.
269 
270       When used with --tree, turns off eliding to show a complete tree.
271 
272 )"
273 
274     TARGET_PRINTING_MODE_COMMAND_LINE_HELP "\n" DEFAULT_TOOLCHAIN_SWITCH_HELP
275 
276     R"(
277   -q
278      Quiet. If nothing matches, don't print any output. Without this option, if
279      there are no matches there will be an informational message printed which
280      might interfere with scripts processing the output.
281 
282 )"
283 
284     TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
285 
286     R"(
287   --tree
288       Outputs a reverse dependency tree from the given target. Duplicates will
289       be elided. Combine with --all to see a full dependency tree.
290 
291       Tree output can not be used with the filtering or output flags: --as,
292       --type, --testonly.
293 
294 )"
295 
296     TARGET_TYPE_FILTER_COMMAND_LINE_HELP
297 
298     R"(
299 
300 Examples (target input)
301 
302   gn refs out/Debug //gn:gn
303       Find all targets depending on the given exact target name.
304 
305   gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim
306       Edit all .gn files containing references to //base:i18n
307 
308   gn refs out/Debug //base --all
309       List all targets depending directly or indirectly on //base:base.
310 
311   gn refs out/Debug "//base/*"
312       List all targets depending directly on any target in //base or
313       its subdirectories.
314 
315   gn refs out/Debug "//base:*"
316       List all targets depending directly on any target in
317       //base/BUILD.gn.
318 
319   gn refs out/Debug //base --tree
320       Print a reverse dependency tree of //base:base
321 
322 Examples (file input)
323 
324   gn refs out/Debug //base/macros.h
325       Print target(s) listing //base/macros.h as a source.
326 
327   gn refs out/Debug //base/macros.h --tree
328       Display a reverse dependency tree to get to the given file. This
329       will show how dependencies will reference that file.
330 
331   gn refs out/Debug //base/macros.h //base/at_exit.h --all
332       Display all unique targets with some dependency path to a target
333       containing either of the given files as a source.
334 
335   gn refs out/Debug //base/macros.h --testonly=true --type=executable
336           --all --as=output
337       Display the executable file names of all test executables
338       potentially affected by a change to the given file.
339 )";
340 
RunRefs(const std::vector<std::string> & args)341 int RunRefs(const std::vector<std::string>& args) {
342   if (args.size() <= 1) {
343     Err(Location(), "Unknown command format. See \"gn help refs\"",
344         "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"")
345         .PrintToStdout();
346     return 1;
347   }
348 
349   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
350   bool tree = cmdline->HasSwitch("tree");
351   bool all = cmdline->HasSwitch("all");
352   bool default_toolchain_only = cmdline->HasSwitch(switches::kDefaultToolchain);
353 
354   // Deliberately leaked to avoid expensive process teardown.
355   Setup* setup = new Setup;
356   if (!setup->DoSetup(args[0], false) || !setup->Run())
357     return 1;
358 
359   // The inputs are everything but the first arg (which is the build dir).
360   std::vector<std::string> inputs;
361   for (size_t i = 1; i < args.size(); i++) {
362     if (args[i][0] == '@') {
363       // The argument is as a path to a response file.
364       std::string contents;
365       bool ret =
366           base::ReadFileToString(UTF8ToFilePath(args[i].substr(1)), &contents);
367       if (!ret) {
368         Err(Location(), "Response file " + args[i].substr(1) + " not found.")
369             .PrintToStdout();
370         return 1;
371       }
372       for (const std::string& line : base::SplitString(
373                contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
374         if (!line.empty())
375           inputs.push_back(line);
376       }
377     } else {
378       // The argument is a label or a path.
379       inputs.push_back(args[i]);
380     }
381   }
382 
383   // Get the matches for the command-line input.
384   UniqueVector<const Target*> target_matches;
385   UniqueVector<const Config*> config_matches;
386   UniqueVector<const Toolchain*> toolchain_matches;
387   UniqueVector<SourceFile> file_matches;
388   if (!ResolveFromCommandLineInput(setup, inputs, default_toolchain_only,
389                                    &target_matches, &config_matches,
390                                    &toolchain_matches, &file_matches))
391     return 1;
392 
393   // When you give a file or config as an input, you want the targets that are
394   // associated with it. We don't want to just append this to the
395   // target_matches, however, since these targets should actually be listed in
396   // the output, while for normal targets you don't want to see the inputs,
397   // only what refers to them.
398   std::vector<const Target*> all_targets =
399       setup->builder().GetAllResolvedTargets();
400   UniqueVector<const Target*> explicit_target_matches;
401   for (const auto& file : file_matches) {
402     std::vector<TargetContainingFile> target_containing;
403     GetTargetsContainingFile(setup, all_targets, file, default_toolchain_only,
404                              &target_containing);
405 
406     // Extract just the Target*.
407     for (const TargetContainingFile& pair : target_containing)
408       explicit_target_matches.push_back(pair.first);
409   }
410   for (auto* config : config_matches) {
411     GetTargetsReferencingConfig(setup, all_targets, config,
412                                 default_toolchain_only,
413                                 &explicit_target_matches);
414   }
415 
416   // Tell the user if their input matches no files or labels. We need to check
417   // both that it matched no targets and no configs. File input will already
418   // have been converted to targets at this point. Configs will have been
419   // converted to targets also, but there could be no targets referencing the
420   // config, which is different than no config with that name.
421   bool quiet = cmdline->HasSwitch("q");
422   if (!quiet && config_matches.empty() && explicit_target_matches.empty() &&
423       target_matches.empty()) {
424     OutputString("The input matches no targets, configs, or files.\n",
425                  DECORATION_YELLOW);
426     return 1;
427   }
428 
429   // Construct the reverse dependency tree.
430   DepMap dep_map;
431   FillDepMap(setup, &dep_map);
432 
433   size_t cnt = 0;
434   if (tree)
435     cnt = DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
436   else if (all)
437     cnt = DoAllListOutput(dep_map, target_matches, explicit_target_matches);
438   else
439     cnt = DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
440 
441   // If you ask for the references of a valid target, but that target has
442   // nothing referencing it, we'll get here without having printed anything.
443   if (!quiet && cnt == 0)
444     OutputString("Nothing references this.\n", DECORATION_YELLOW);
445 
446   return 0;
447 }
448 
449 }  // namespace commands
450