xref: /third_party/gn/src/gn/command_refs.cc (revision 6d528ed9)
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
25namespace commands {
26
27namespace {
28
29using TargetSet = TargetSet;
30using TargetVector = std::vector<const Target*>;
31
32// Maps targets to the list of targets that depend on them.
33using DepMap = std::multimap<const Target*, const Target*>;
34
35// Populates the reverse dependency map for the targets in the Setup.
36void 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.
44size_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.
55size_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.
88size_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
103void 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.
109void 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.
118void 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
128bool 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
140void 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.
158size_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.
185size_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.
205size_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
231const char kRefs[] = "refs";
232const char kRefs_HelpShort[] = "refs: Find stuff referencing a target or file.";
233const 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
261Options
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
300Examples (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=buildfile | 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
322Examples (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
341int 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