summaryrefslogtreecommitdiff
path: root/gn/tools/gn/command_refs.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gn/tools/gn/command_refs.cc')
-rw-r--r--gn/tools/gn/command_refs.cc498
1 files changed, 498 insertions, 0 deletions
diff --git a/gn/tools/gn/command_refs.cc b/gn/tools/gn/command_refs.cc
new file mode 100644
index 00000000000..92ecf10376b
--- /dev/null
+++ b/gn/tools/gn/command_refs.cc
@@ -0,0 +1,498 @@
+// 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 "tools/gn/commands.h"
+#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/deps_iterator.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/item.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/switches.h"
+#include "tools/gn/target.h"
+
+namespace commands {
+
+namespace {
+
+typedef std::set<const Target*> TargetSet;
+typedef std::vector<const Target*> TargetVector;
+
+// Maps targets to the list of targets that depend on them.
+typedef std::multimap<const Target*, const Target*> DepMap;
+
+// 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->find(target) == seen_targets->end()) {
+ // New target, mark it visited.
+ seen_targets->insert(target);
+ } else {
+ // 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->find(target) != results->end())
+ return; // Already found this target.
+ results->insert(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 TargetContainsFile(const Target* target, const SourceFile& file) {
+ for (const auto& cur_file : target->sources()) {
+ if (cur_file == file)
+ return true;
+ }
+ for (const auto& cur_file : target->public_headers()) {
+ if (cur_file == file)
+ return true;
+ }
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ for (const auto& cur_file : iter.cur().inputs()) {
+ if (cur_file == file)
+ return true;
+ }
+ }
+ for (const auto& cur_file : target->data()) {
+ if (cur_file == file.value())
+ return true;
+ if (cur_file.back() == '/' &&
+ base::StartsWith(file.value(), cur_file, base::CompareCase::SENSITIVE))
+ return true;
+ }
+
+ if (target->action_values().script().value() == file.value())
+ return true;
+
+ std::vector<SourceFile> output_sources;
+ target->action_values().GetOutputsAsSourceFiles(target, &output_sources);
+ for (const auto& cur_file : output_sources) {
+ if (cur_file == file)
+ return true;
+ }
+
+ for (const auto& cur_file : target->computed_outputs()) {
+ if (cur_file.AsSourceFile(target->settings()->build_settings()) == file)
+ return true;
+ }
+ return false;
+}
+
+void GetTargetsContainingFile(Setup* setup,
+ const std::vector<const Target*>& all_targets,
+ const SourceFile& file,
+ bool all_toolchains,
+ UniqueVector<const Target*>* matches) {
+ Label default_toolchain = setup->loader()->default_toolchain_label();
+ for (auto* target : all_targets) {
+ if (!all_toolchains) {
+ // Only check targets in the default toolchain.
+ if (target->label().GetToolchainLabel() != default_toolchain)
+ continue;
+ }
+ if (TargetContainsFile(target, file))
+ matches->push_back(target);
+ }
+}
+
+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 all_toolchains,
+ UniqueVector<const Target*>* matches) {
+ Label default_toolchain = setup->loader()->default_toolchain_label();
+ for (auto* target : all_targets) {
+ if (!all_toolchains) {
+ // 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 <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)*
+ [--all] [--all-toolchains] [--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.
+)"
+
+ ALL_TOOLCHAINS_SWITCH_HELP "\n" TARGET_PRINTING_MODE_COMMAND_LINE_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 //tools/gn:gn
+ Find all targets depending on the given exact target name.
+
+ gn refs out/Debug //base:i18n --as=buildfiles | 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(), "You're holding it wrong.",
+ "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 all_toolchains = cmdline->HasSwitch(switches::kAllToolchains);
+
+ 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, all_toolchains,
+ &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) {
+ GetTargetsContainingFile(setup, all_targets, file, all_toolchains,
+ &explicit_target_matches);
+ }
+ for (auto* config : config_matches) {
+ GetTargetsReferencingConfig(setup, all_targets, config, all_toolchains,
+ &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