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.
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"
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
,
41 TargetSet
* seen_targets
,
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
49 // Returns the number of items printed.
50 size_t RecursivePrintTarget(const DepMap
& dep_map
,
52 TargetSet
* seen_targets
,
54 std::string
indent(indent_level
* 2, ' ');
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;
63 if (seen_targets
->find(target
) == seen_targets
->end()) {
64 // New target, mark it visited.
65 seen_targets
->insert(target
);
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
))
78 count
+= RecursivePrintTargetDeps(dep_map
, target
, seen_targets
,
84 // Prints refs of the given target (not the target itself). See
85 // RecursivePrintTarget.
86 size_t RecursivePrintTargetDeps(const DepMap
& dep_map
,
88 TargetSet
* seen_targets
,
90 DepMap::const_iterator dep_begin
= dep_map
.lower_bound(target
);
91 DepMap::const_iterator dep_end
= dep_map
.upper_bound(target
);
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
,
101 void RecursiveCollectChildRefs(const DepMap
& dep_map
,
102 const Target
* target
,
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
)
132 for (const auto& cur_file
: target
->public_headers()) {
133 if (cur_file
== file
)
136 for (const auto& cur_file
: target
->inputs()) {
137 if (cur_file
== file
)
140 for (const auto& cur_file
: target
->data()) {
141 if (cur_file
== file
)
147 void GetTargetsContainingFile(Setup
* setup
,
148 const std::vector
<const Target
*>& all_targets
,
149 const SourceFile
& file
,
151 UniqueVector
<const Target
*>* matches
) {
152 Label default_toolchain
= setup
->loader()->default_toolchain_label();
153 for (const auto& target
: all_targets
) {
154 if (!all_toolchains
) {
155 // Only check targets in the default toolchain.
156 if (target
->label().GetToolchainLabel() != default_toolchain
)
159 if (TargetContainsFile(target
, file
))
160 matches
->push_back(target
);
164 bool TargetReferencesConfig(const Target
* target
, const Config
* config
) {
165 for (const LabelConfigPair
& cur
: target
->configs()) {
166 if (cur
.ptr
== config
)
169 for (const LabelConfigPair
& cur
: target
->public_configs()) {
170 if (cur
.ptr
== config
)
176 void GetTargetsReferencingConfig(Setup
* setup
,
177 const std::vector
<const Target
*>& all_targets
,
178 const Config
* config
,
180 UniqueVector
<const Target
*>* matches
) {
181 Label default_toolchain
= setup
->loader()->default_toolchain_label();
182 for (const auto& target
: all_targets
) {
183 if (!all_toolchains
) {
184 // Only check targets in the default toolchain.
185 if (target
->label().GetToolchainLabel() != default_toolchain
)
188 if (TargetReferencesConfig(target
, config
))
189 matches
->push_back(target
);
193 // Returns the number of matches printed.
194 size_t DoTreeOutput(const DepMap
& dep_map
,
195 const UniqueVector
<const Target
*>& implicit_target_matches
,
196 const UniqueVector
<const Target
*>& explicit_target_matches
,
198 TargetSet seen_targets
;
201 // Implicit targets don't get printed themselves.
202 for (const Target
* target
: implicit_target_matches
) {
204 count
+= RecursivePrintTargetDeps(dep_map
, target
, nullptr, 0);
206 count
+= RecursivePrintTargetDeps(dep_map
, target
, &seen_targets
, 0);
209 // Explicit targets appear in the output.
210 for (const Target
* target
: implicit_target_matches
) {
212 count
+= RecursivePrintTarget(dep_map
, target
, nullptr, 0);
214 count
+= RecursivePrintTarget(dep_map
, target
, &seen_targets
, 0);
220 // Returns the number of matches printed.
221 size_t DoAllListOutput(
222 const DepMap
& dep_map
,
223 const UniqueVector
<const Target
*>& implicit_target_matches
,
224 const UniqueVector
<const Target
*>& explicit_target_matches
) {
225 // Output recursive dependencies, uniquified and flattened.
228 for (const Target
* target
: implicit_target_matches
)
229 RecursiveCollectChildRefs(dep_map
, target
, &results
);
230 for (const Target
* target
: explicit_target_matches
) {
231 // Explicit targets also get added to the output themselves.
232 results
.insert(target
);
233 RecursiveCollectChildRefs(dep_map
, target
, &results
);
236 FilterAndPrintTargetSet(false, results
);
237 return results
.size();
240 // Returns the number of matches printed.
241 size_t DoDirectListOutput(
242 const DepMap
& dep_map
,
243 const UniqueVector
<const Target
*>& implicit_target_matches
,
244 const UniqueVector
<const Target
*>& explicit_target_matches
) {
247 // Output everything that refers to the implicit ones.
248 for (const Target
* target
: implicit_target_matches
) {
249 DepMap::const_iterator dep_begin
= dep_map
.lower_bound(target
);
250 DepMap::const_iterator dep_end
= dep_map
.upper_bound(target
);
251 for (DepMap::const_iterator cur_dep
= dep_begin
;
252 cur_dep
!= dep_end
; cur_dep
++)
253 results
.insert(cur_dep
->second
);
256 // And just output the explicit ones directly (these are the target matches
257 // when referring to what references a file or config).
258 for (const Target
* target
: explicit_target_matches
)
259 results
.insert(target
);
261 FilterAndPrintTargetSet(false, results
);
262 return results
.size();
267 const char kRefs
[] = "refs";
268 const char kRefs_HelpShort
[] =
269 "refs: Find stuff referencing a target or file.";
270 const char kRefs_Help
[] =
271 "gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* "
273 " [--all-toolchains] [--as=...] [--testonly=...] [--type=...]\n"
275 " Finds reverse dependencies (which targets reference something). The\n"
276 " input is a list containing:\n"
278 " - Target label: The result will be which targets depend on it.\n"
280 " - Config label: The result will be which targets list the given\n"
281 " config in its \"configs\" or \"public_configs\" list.\n"
283 " - Label pattern: The result will be which targets depend on any\n"
284 " target matching the given pattern. Patterns will not match\n"
285 " configs. These are not general regular expressions, see\n"
286 " \"gn help label_pattern\" for details.\n"
288 " - File name: The result will be which targets list the given file in\n"
289 " its \"inputs\", \"sources\", \"public\", or \"data\". Any input\n"
290 " that does not contain wildcards and does not match a target or a\n"
291 " config will be treated as a file.\n"
293 " - Response file: If the input starts with an \"@\", it will be\n"
294 " interpreted as a path to a file containing a list of labels or\n"
295 " file names, one per line. This allows us to handle long lists\n"
296 " of inputs without worrying about command line limits.\n"
301 " When used without --tree, will recurse and display all unique\n"
302 " dependencies of the given targets. For example, if the input is\n"
303 " a target, this will output all targets that depend directly or\n"
304 " indirectly on the input. If the input is a file, this will output\n"
305 " all targets that depend directly or indirectly on that file.\n"
307 " When used with --tree, turns off eliding to show a complete tree.\n"
309 " --all-toolchains\n"
310 " Normally only inputs in the default toolchain will be included.\n"
311 " This switch will turn on matching all toolchains.\n"
313 " For example, a file is in a target might be compiled twice:\n"
314 " once in the default toolchain and once in a secondary one. Without\n"
315 " this flag, only the default toolchain one will be matched and\n"
316 " printed (potentially with its recursive dependencies, depending on\n"
317 " the other options). With this flag, both will be printed\n"
318 " (potentially with both of their recursive dependencies).\n"
320 TARGET_PRINTING_MODE_COMMAND_LINE_HELP
323 " Quiet. If nothing matches, don't print any output. Without this\n"
324 " option, if there are no matches there will be an informational\n"
325 " message printed which might interfere with scripts processing the\n"
328 TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
331 " Outputs a reverse dependency tree from the given target.\n"
332 " Duplicates will be elided. Combine with --all to see a full\n"
333 " dependency tree.\n"
335 " Tree output can not be used with the filtering or output flags:\n"
336 " --as, --type, --testonly.\n"
338 TARGET_TYPE_FILTER_COMMAND_LINE_HELP
340 "Examples (target input)\n"
342 " gn refs out/Debug //tools/gn:gn\n"
343 " Find all targets depending on the given exact target name.\n"
345 " gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim\n"
346 " Edit all .gn files containing references to //base:i18n\n"
348 " gn refs out/Debug //base --all\n"
349 " List all targets depending directly or indirectly on //base:base.\n"
351 " gn refs out/Debug \"//base/*\"\n"
352 " List all targets depending directly on any target in //base or\n"
353 " its subdirectories.\n"
355 " gn refs out/Debug \"//base:*\"\n"
356 " List all targets depending directly on any target in\n"
357 " //base/BUILD.gn.\n"
359 " gn refs out/Debug //base --tree\n"
360 " Print a reverse dependency tree of //base:base\n"
362 "Examples (file input)\n"
364 " gn refs out/Debug //base/macros.h\n"
365 " Print target(s) listing //base/macros.h as a source.\n"
367 " gn refs out/Debug //base/macros.h --tree\n"
368 " Display a reverse dependency tree to get to the given file. This\n"
369 " will show how dependencies will reference that file.\n"
371 " gn refs out/Debug //base/macros.h //base/basictypes.h --all\n"
372 " Display all unique targets with some dependency path to a target\n"
373 " containing either of the given files as a source.\n"
375 " gn refs out/Debug //base/macros.h --testonly=true --type=executable\n"
376 " --all --as=output\n"
377 " Display the executable file names of all test executables\n"
378 " potentially affected by a change to the given file.\n";
380 int RunRefs(const std::vector
<std::string
>& args
) {
381 if (args
.size() <= 1) {
382 Err(Location(), "You're holding it wrong.",
383 "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"")
388 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
389 bool tree
= cmdline
->HasSwitch("tree");
390 bool all
= cmdline
->HasSwitch("all");
391 bool all_toolchains
= cmdline
->HasSwitch("all-toolchains");
393 Setup
* setup
= new Setup
;
394 setup
->set_check_for_bad_items(false);
395 if (!setup
->DoSetup(args
[0], false) || !setup
->Run())
398 // The inputs are everything but the first arg (which is the build dir).
399 std::vector
<std::string
> inputs
;
400 for (size_t i
= 1; i
< args
.size(); i
++) {
401 if (args
[i
][0] == '@') {
402 // The argument is as a path to a response file.
403 std::string contents
;
404 std::vector
<std::string
> lines
;
405 bool ret
= base::ReadFileToString(UTF8ToFilePath(args
[i
].substr(1)),
408 Err(Location(), "Response file " + args
[i
].substr(1) + " not found.")
412 base::SplitString(contents
, '\n', &lines
);
413 for (const auto& line
: lines
) {
415 inputs
.push_back(line
);
418 // The argument is a label or a path.
419 inputs
.push_back(args
[i
]);
423 // Get the matches for the command-line input.
424 UniqueVector
<const Target
*> target_matches
;
425 UniqueVector
<const Config
*> config_matches
;
426 UniqueVector
<const Toolchain
*> toolchain_matches
;
427 UniqueVector
<SourceFile
> file_matches
;
428 if (!ResolveFromCommandLineInput(setup
, inputs
, all_toolchains
,
429 &target_matches
, &config_matches
,
430 &toolchain_matches
, &file_matches
))
433 // When you give a file or config as an input, you want the targets that are
434 // associated with it. We don't want to just append this to the
435 // target_matches, however, since these targets should actually be listed in
436 // the output, while for normal targets you don't want to see the inputs,
437 // only what refers to them.
438 std::vector
<const Target
*> all_targets
=
439 setup
->builder()->GetAllResolvedTargets();
440 UniqueVector
<const Target
*> explicit_target_matches
;
441 for (const auto& file
: file_matches
) {
442 GetTargetsContainingFile(setup
, all_targets
, file
, all_toolchains
,
443 &explicit_target_matches
);
445 for (const auto& config
: config_matches
) {
446 GetTargetsReferencingConfig(setup
, all_targets
, config
, all_toolchains
,
447 &explicit_target_matches
);
450 // Tell the user if their input matches no files or labels. We need to check
451 // both that it matched no targets and no configs. File input will already
452 // have been converted to targets at this point. Configs will have been
453 // converted to targets also, but there could be no targets referencing the
454 // config, which is different than no config with that name.
455 bool quiet
= cmdline
->HasSwitch("q");
456 if (!quiet
&& config_matches
.empty() &&
457 explicit_target_matches
.empty() && target_matches
.empty()) {
458 OutputString("The input matches no targets, configs, or files.\n",
463 // Construct the reverse dependency tree.
465 FillDepMap(setup
, &dep_map
);
469 cnt
= DoTreeOutput(dep_map
, target_matches
, explicit_target_matches
, all
);
471 cnt
= DoAllListOutput(dep_map
, target_matches
, explicit_target_matches
);
473 cnt
= DoDirectListOutput(dep_map
, target_matches
, explicit_target_matches
);
475 // If you ask for the references of a valid target, but that target has
476 // nothing referencing it, we'll get here without having printed anything.
477 if (!quiet
&& cnt
== 0)
478 OutputString("Nothing references this.\n", DECORATION_YELLOW
);
483 } // namespace commands