| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <stddef.h> | |
| #include <map> | |
| #include <set> | |
| #include "base/command_line.h" | |
| #include "base/files/file_util.h" | |
| #include "base/strings/string_split.h" | |
| #include "base/strings/string_util.h" | |
| #include "gn/commands.h" | |
| #include "gn/config_values_extractors.h" | |
| #include "gn/deps_iterator.h" | |
| #include "gn/filesystem_utils.h" | |
| #include "gn/input_file.h" | |
| #include "gn/item.h" | |
| #include "gn/setup.h" | |
| #include "gn/standard_out.h" | |
| #include "gn/switches.h" | |
| #include "gn/target.h" | |
| namespace commands { | |
| namespace { | |
| using TargetSet = TargetSet; | |
| using TargetVector = std::vector<const Target*>; | |
| // Maps targets to the list of targets that depend on them. | |
| using DepMap = std::multimap<const Target*, const Target*>; | |
| // Populates the reverse dependency map for the targets in the Setup. | |
| void FillDepMap(Setup* setup, DepMap* dep_map) { | |
| for (auto* target : setup->builder().GetAllResolvedTargets()) { | |
| for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL)) | |
| dep_map->insert(std::make_pair(dep_pair.ptr, target)); | |
| } | |
| } | |
| // Forward declaration for function below. | |
| size_t RecursivePrintTargetDeps(const DepMap& dep_map, | |
| const Target* target, | |
| TargetSet* seen_targets, | |
| int indent_level); | |
| // Prints the target and its dependencies in tree form. If the set is non-null, | |
| // new targets encountered will be added to the set, and if a ref is in the set | |
| // already, it will not be recused into. When the set is null, all refs will be | |
| // printed. | |
| // | |
| // Returns the number of items printed. | |
| size_t RecursivePrintTarget(const DepMap& dep_map, | |
| const Target* target, | |
| TargetSet* seen_targets, | |
| int indent_level) { | |
| std::string indent(indent_level * 2, ' '); | |
| size_t count = 1; | |
| // Only print the toolchain for non-default-toolchain targets. | |
| OutputString(indent + target->label().GetUserVisibleName( | |
| !target->settings()->is_default())); | |
| bool print_children = true; | |
| if (seen_targets) { | |
| if (!seen_targets->add(target)) { | |
| // Already seen. | |
| print_children = false; | |
| // Only print "..." if something is actually elided, which means that | |
| // the current target has children. | |
| if (dep_map.lower_bound(target) != dep_map.upper_bound(target)) | |
| OutputString("..."); | |
| } | |
| } | |
| OutputString("\n"); | |
| if (print_children) { | |
| count += RecursivePrintTargetDeps(dep_map, target, seen_targets, | |
| indent_level + 1); | |
| } | |
| return count; | |
| } | |
| // Prints refs of the given target (not the target itself). See | |
| // RecursivePrintTarget. | |
| size_t RecursivePrintTargetDeps(const DepMap& dep_map, | |
| const Target* target, | |
| TargetSet* seen_targets, | |
| int indent_level) { | |
| DepMap::const_iterator dep_begin = dep_map.lower_bound(target); | |
| DepMap::const_iterator dep_end = dep_map.upper_bound(target); | |
| size_t count = 0; | |
| for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end; | |
| cur_dep++) { | |
| count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets, | |
| indent_level); | |
| } | |
| return count; | |
| } | |
| void RecursiveCollectChildRefs(const DepMap& dep_map, | |
| const Target* target, | |
| TargetSet* results); | |
| // Recursively finds all targets that reference the given one, and additionally | |
| // adds the current one to the list. | |
| void RecursiveCollectRefs(const DepMap& dep_map, | |
| const Target* target, | |
| TargetSet* results) { | |
| if (!results->add(target)) | |
| return; // Already found this target. | |
| RecursiveCollectChildRefs(dep_map, target, results); | |
| } | |
| // Recursively finds all targets that reference the given one. | |
| void RecursiveCollectChildRefs(const DepMap& dep_map, | |
| const Target* target, | |
| TargetSet* results) { | |
| DepMap::const_iterator dep_begin = dep_map.lower_bound(target); | |
| DepMap::const_iterator dep_end = dep_map.upper_bound(target); | |
| for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end; | |
| cur_dep++) | |
| RecursiveCollectRefs(dep_map, cur_dep->second, results); | |
| } | |
| bool TargetReferencesConfig(const Target* target, const Config* config) { | |
| for (const LabelConfigPair& cur : target->configs()) { | |
| if (cur.ptr == config) | |
| return true; | |
| } | |
| for (const LabelConfigPair& cur : target->public_configs()) { | |
| if (cur.ptr == config) | |
| return true; | |
| } | |
| return false; | |
| } | |
| void GetTargetsReferencingConfig(Setup* setup, | |
| const std::vector<const Target*>& all_targets, | |
| const Config* config, | |
| bool default_toolchain_only, | |
| UniqueVector<const Target*>* matches) { | |
| Label default_toolchain = setup->loader()->default_toolchain_label(); | |
| for (auto* target : all_targets) { | |
| if (default_toolchain_only) { | |
| // Only check targets in the default toolchain. | |
| if (target->label().GetToolchainLabel() != default_toolchain) | |
| continue; | |
| } | |
| if (TargetReferencesConfig(target, config)) | |
| matches->push_back(target); | |
| } | |
| } | |
| // Returns the number of matches printed. | |
| size_t DoTreeOutput(const DepMap& dep_map, | |
| const UniqueVector<const Target*>& implicit_target_matches, | |
| const UniqueVector<const Target*>& explicit_target_matches, | |
| bool all) { | |
| TargetSet seen_targets; | |
| size_t count = 0; | |
| // Implicit targets don't get printed themselves. | |
| for (const Target* target : implicit_target_matches) { | |
| if (all) | |
| count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0); | |
| else | |
| count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0); | |
| } | |
| // Explicit targets appear in the output. | |
| for (const Target* target : implicit_target_matches) { | |
| if (all) | |
| count += RecursivePrintTarget(dep_map, target, nullptr, 0); | |
| else | |
| count += RecursivePrintTarget(dep_map, target, &seen_targets, 0); | |
| } | |
| return count; | |
| } | |
| // Returns the number of matches printed. | |
| size_t DoAllListOutput( | |
| const DepMap& dep_map, | |
| const UniqueVector<const Target*>& implicit_target_matches, | |
| const UniqueVector<const Target*>& explicit_target_matches) { | |
| // Output recursive dependencies, uniquified and flattened. | |
| TargetSet results; | |
| for (const Target* target : implicit_target_matches) | |
| RecursiveCollectChildRefs(dep_map, target, &results); | |
| for (const Target* target : explicit_target_matches) { | |
| // Explicit targets also get added to the output themselves. | |
| results.insert(target); | |
| RecursiveCollectChildRefs(dep_map, target, &results); | |
| } | |
| FilterAndPrintTargetSet(false, results); | |
| return results.size(); | |
| } | |
| // Returns the number of matches printed. | |
| size_t DoDirectListOutput( | |
| const DepMap& dep_map, | |
| const UniqueVector<const Target*>& implicit_target_matches, | |
| const UniqueVector<const Target*>& explicit_target_matches) { | |
| TargetSet results; | |
| // Output everything that refers to the implicit ones. | |
| for (const Target* target : implicit_target_matches) { | |
| DepMap::const_iterator dep_begin = dep_map.lower_bound(target); | |
| DepMap::const_iterator dep_end = dep_map.upper_bound(target); | |
| for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end; | |
| cur_dep++) | |
| results.insert(cur_dep->second); | |
| } | |
| // And just output the explicit ones directly (these are the target matches | |
| // when referring to what references a file or config). | |
| for (const Target* target : explicit_target_matches) | |
| results.insert(target); | |
| FilterAndPrintTargetSet(false, results); | |
| return results.size(); | |
| } | |
| } // namespace | |
| const char kRefs[] = "refs"; | |
| const char kRefs_HelpShort[] = "refs: Find stuff referencing a target or file."; | |
| const char kRefs_Help[] = | |
| R"(gn refs | |
| gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all] | |
| [--default-toolchain] [--as=...] [--testonly=...] [--type=...] | |
| Finds reverse dependencies (which targets reference something). The input is | |
| a list containing: | |
| - Target label: The result will be which targets depend on it. | |
| - Config label: The result will be which targets list the given config in | |
| its "configs" or "public_configs" list. | |
| - Label pattern: The result will be which targets depend on any target | |
| matching the given pattern. Patterns will not match configs. These are not | |
| general regular expressions, see "gn help label_pattern" for details. | |
| - File name: The result will be which targets list the given file in its | |
| "inputs", "sources", "public", "data", or "outputs". Any input that does | |
| not contain wildcards and does not match a target or a config will be | |
| treated as a file. | |
| - Response file: If the input starts with an "@", it will be interpreted as | |
| a path to a file containing a list of labels or file names, one per line. | |
| This allows us to handle long lists of inputs without worrying about | |
| command line limits. | |
| Options | |
| --all | |
| When used without --tree, will recurse and display all unique | |
| dependencies of the given targets. For example, if the input is a target, | |
| this will output all targets that depend directly or indirectly on the | |
| input. If the input is a file, this will output all targets that depend | |
| directly or indirectly on that file. | |
| When used with --tree, turns off eliding to show a complete tree. | |
| )" | |
| TARGET_PRINTING_MODE_COMMAND_LINE_HELP "\n" DEFAULT_TOOLCHAIN_SWITCH_HELP | |
| R"( | |
| -q | |
| Quiet. If nothing matches, don't print any output. Without this option, if | |
| there are no matches there will be an informational message printed which | |
| might interfere with scripts processing the output. | |
| )" | |
| TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP | |
| R"( | |
| --tree | |
| Outputs a reverse dependency tree from the given target. Duplicates will | |
| be elided. Combine with --all to see a full dependency tree. | |
| Tree output can not be used with the filtering or output flags: --as, | |
| --type, --testonly. | |
| )" | |
| TARGET_TYPE_FILTER_COMMAND_LINE_HELP | |
| R"( | |
| Examples (target input) | |
| gn refs out/Debug //gn:gn | |
| Find all targets depending on the given exact target name. | |
| gn refs out/Debug //base:i18n --as=buildfile | xargs gvim | |
| Edit all .gn files containing references to //base:i18n | |
| gn refs out/Debug //base --all | |
| List all targets depending directly or indirectly on //base:base. | |
| gn refs out/Debug "//base/*" | |
| List all targets depending directly on any target in //base or | |
| its subdirectories. | |
| gn refs out/Debug "//base:*" | |
| List all targets depending directly on any target in | |
| //base/BUILD.gn. | |
| gn refs out/Debug //base --tree | |
| Print a reverse dependency tree of //base:base | |
| Examples (file input) | |
| gn refs out/Debug //base/macros.h | |
| Print target(s) listing //base/macros.h as a source. | |
| gn refs out/Debug //base/macros.h --tree | |
| Display a reverse dependency tree to get to the given file. This | |
| will show how dependencies will reference that file. | |
| gn refs out/Debug //base/macros.h //base/at_exit.h --all | |
| Display all unique targets with some dependency path to a target | |
| containing either of the given files as a source. | |
| gn refs out/Debug //base/macros.h --testonly=true --type=executable | |
| --all --as=output | |
| Display the executable file names of all test executables | |
| potentially affected by a change to the given file. | |
| )"; | |
| int RunRefs(const std::vector<std::string>& args) { | |
| if (args.size() <= 1) { | |
| Err(Location(), "Unknown command format. See \"gn help refs\"", | |
| "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"") | |
| .PrintToStdout(); | |
| return 1; | |
| } | |
| const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | |
| bool tree = cmdline->HasSwitch("tree"); | |
| bool all = cmdline->HasSwitch("all"); | |
| bool default_toolchain_only = cmdline->HasSwitch(switches::kDefaultToolchain); | |
| // Deliberately leaked to avoid expensive process teardown. | |
| Setup* setup = new Setup; | |
| if (!setup->DoSetup(args[0], false) || !setup->Run()) | |
| return 1; | |
| // The inputs are everything but the first arg (which is the build dir). | |
| std::vector<std::string> inputs; | |
| for (size_t i = 1; i < args.size(); i++) { | |
| if (args[i][0] == '@') { | |
| // The argument is as a path to a response file. | |
| std::string contents; | |
| bool ret = | |
| base::ReadFileToString(UTF8ToFilePath(args[i].substr(1)), &contents); | |
| if (!ret) { | |
| Err(Location(), "Response file " + args[i].substr(1) + " not found.") | |
| .PrintToStdout(); | |
| return 1; | |
| } | |
| for (const std::string& line : base::SplitString( | |
| contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | |
| if (!line.empty()) | |
| inputs.push_back(line); | |
| } | |
| } else { | |
| // The argument is a label or a path. | |
| inputs.push_back(args[i]); | |
| } | |
| } | |
| // Get the matches for the command-line input. | |
| UniqueVector<const Target*> target_matches; | |
| UniqueVector<const Config*> config_matches; | |
| UniqueVector<const Toolchain*> toolchain_matches; | |
| UniqueVector<SourceFile> file_matches; | |
| if (!ResolveFromCommandLineInput(setup, inputs, default_toolchain_only, | |
| &target_matches, &config_matches, | |
| &toolchain_matches, &file_matches)) | |
| return 1; | |
| // When you give a file or config as an input, you want the targets that are | |
| // associated with it. We don't want to just append this to the | |
| // target_matches, however, since these targets should actually be listed in | |
| // the output, while for normal targets you don't want to see the inputs, | |
| // only what refers to them. | |
| std::vector<const Target*> all_targets = | |
| setup->builder().GetAllResolvedTargets(); | |
| UniqueVector<const Target*> explicit_target_matches; | |
| for (const auto& file : file_matches) { | |
| std::vector<TargetContainingFile> target_containing; | |
| GetTargetsContainingFile(setup, all_targets, file, default_toolchain_only, | |
| &target_containing); | |
| // Extract just the Target*. | |
| for (const TargetContainingFile& pair : target_containing) | |
| explicit_target_matches.push_back(pair.first); | |
| } | |
| for (auto* config : config_matches) { | |
| GetTargetsReferencingConfig(setup, all_targets, config, | |
| default_toolchain_only, | |
| &explicit_target_matches); | |
| } | |
| // Tell the user if their input matches no files or labels. We need to check | |
| // both that it matched no targets and no configs. File input will already | |
| // have been converted to targets at this point. Configs will have been | |
| // converted to targets also, but there could be no targets referencing the | |
| // config, which is different than no config with that name. | |
| bool quiet = cmdline->HasSwitch("q"); | |
| if (!quiet && config_matches.empty() && explicit_target_matches.empty() && | |
| target_matches.empty()) { | |
| OutputString("The input matches no targets, configs, or files.\n", | |
| DECORATION_YELLOW); | |
| return 1; | |
| } | |
| // Construct the reverse dependency tree. | |
| DepMap dep_map; | |
| FillDepMap(setup, &dep_map); | |
| size_t cnt = 0; | |
| if (tree) | |
| cnt = DoTreeOutput(dep_map, target_matches, explicit_target_matches, all); | |
| else if (all) | |
| cnt = DoAllListOutput(dep_map, target_matches, explicit_target_matches); | |
| else | |
| cnt = DoDirectListOutput(dep_map, target_matches, explicit_target_matches); | |
| // If you ask for the references of a valid target, but that target has | |
| // nothing referencing it, we'll get here without having printed anything. | |
| if (!quiet && cnt == 0) | |
| OutputString("Nothing references this.\n", DECORATION_YELLOW); | |
| return 0; | |
| } | |
| } // namespace commands |