Chromium Code Reviews| Index: tools/gn/command_refs.cc | 
| diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc | 
| index d75ca505136f3d7451530eb688250be404883864..69c705a7a9a3562ee999a1a40cf4eb0acfbd3fc5 100644 | 
| --- a/tools/gn/command_refs.cc | 
| +++ b/tools/gn/command_refs.cc | 
| @@ -33,47 +33,7 @@ void FillDepMap(Setup* setup, DepMap* dep_map) { | 
| } | 
| } | 
| -// Returns the file path generating this item. | 
| -base::FilePath FilePathForItem(const Item* item) { | 
| - return item->defined_from()->GetRange().begin().file()->physical_name(); | 
| -} | 
| - | 
| -// Prints the targets which are the result of a query. This list is sorted | 
| -// and, if as_files is set, the unique filenames matching those targets will | 
| -// be used. | 
| -void OutputResultSet(const TargetSet& results, bool as_files) { | 
| - if (results.empty()) | 
| - return; | 
| - | 
| - if (as_files) { | 
| - // Output the set of unique source files. | 
| - std::set<std::string> unique_files; | 
| - for (const auto& cur : results) | 
| - unique_files.insert(FilePathToUTF8(FilePathForItem(cur))); | 
| - | 
| - for (const auto& cur : unique_files) | 
| - OutputString(cur + "\n"); | 
| - } else { | 
| - // Output sorted and uniquified list of labels. The set will sort the | 
| - // labels. | 
| - std::set<Label> unique_labels; | 
| - for (const auto& cur : results) | 
| - unique_labels.insert(cur->label()); | 
| - | 
| - // Grab the label of the default toolchain from a random target. | 
| - Label default_tc_label = | 
| - (*results.begin())->settings()->default_toolchain_label(); | 
| - | 
| - for (const auto& cur : unique_labels) { | 
| - // Print toolchain only for ones not in the default toolchain. | 
| - OutputString(cur.GetUserVisibleName( | 
| - cur.GetToolchainLabel() != default_tc_label)); | 
| - OutputString("\n"); | 
| - } | 
| - } | 
| -} | 
| - | 
| -// Forward declatation for function below. | 
| +// Forward declaration for function below. | 
| void RecursivePrintTargetDeps(const DepMap& dep_map, | 
| const Target* target, | 
| TargetSet* seen_targets, | 
| @@ -174,83 +134,185 @@ bool TargetContainsFile(const Target* target, const SourceFile& file) { | 
| } | 
| void GetTargetsContainingFile(Setup* setup, | 
| - const std::string& input, | 
| + const std::vector<const Target*>& all_targets, | 
| + const SourceFile& file, | 
| bool all_toolchains, | 
| - std::vector<const Target*>* matches) { | 
| - SourceDir cur_dir = | 
| - SourceDirForCurrentDirectory(setup->build_settings().root_path()); | 
| - SourceFile file = cur_dir.ResolveRelativeFile( | 
| - input, setup->build_settings().root_path_utf8()); | 
| - | 
| + UniqueVector<const Target*>* matches) { | 
| Label default_toolchain = setup->loader()->default_toolchain_label(); | 
| + for (const 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); | 
| + } | 
| +} | 
| - std::vector<const Target*> all_targets = | 
| - setup->builder()->GetAllResolvedTargets(); | 
| +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 (const 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)) | 
| + if (TargetReferencesConfig(target, config)) | 
| matches->push_back(target); | 
| } | 
| } | 
| +void DoTreeOutput(const DepMap& dep_map, | 
| + const UniqueVector<const Target*>& implicit_target_matches, | 
| + const UniqueVector<const Target*>& explicit_target_matches, | 
| + bool all) { | 
| + TargetSet seen_targets; | 
| + | 
| + // Implicit targets don't get printed themselves. | 
| + for (const Target* target : implicit_target_matches) { | 
| + if (all) | 
| + RecursivePrintTargetDeps(dep_map, target, nullptr, 0); | 
| + else | 
| + RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0); | 
| + } | 
| + | 
| + // Explicit targets appear in the output. | 
| + for (const Target* target : implicit_target_matches) { | 
| + if (all) | 
| + RecursivePrintTarget(dep_map, target, nullptr, 0); | 
| + else | 
| + RecursivePrintTarget(dep_map, target, &seen_targets, 0); | 
| + } | 
| +} | 
| + | 
| +void 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); | 
| +} | 
| + | 
| +void 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); | 
| +} | 
| + | 
| } // namespace | 
| const char kRefs[] = "refs"; | 
| const char kRefs_HelpShort[] = | 
| "refs: Find stuff referencing a target or file."; | 
| const char kRefs_Help[] = | 
| - "gn refs <out_dir> (<label_pattern>|<file>) [--files] [--tree] [--all]\n" | 
| - " [--all-toolchains]\n" | 
| + "gn refs <out_dir> (<label_pattern>|<label>|<file>)* [--files] [--tree]\n" | 
| 
 
scottmg
2015/02/19 21:52:01
--as, etc?
 
 | 
| + " [--all] [--all-toolchains]\n" | 
| "\n" | 
| " Finds reverse dependencies (which targets reference something). The\n" | 
| - " input is either a target label, a target label pattern, or a file\n" | 
| - " name.\n" | 
| + " input is a list containing:\n" | 
| + "\n" | 
| + " - Target label: The result will be which targets depend on it.\n" | 
| + "\n" | 
| + " - Config label: The result will be which targets list the given\n" | 
| + " config in its \"configs\" or \"public_configs\" list.\n" | 
| "\n" | 
| - " The <label_pattern> can take exact labels or patterns that match more\n" | 
| - " than one (although not general regular expressions).\n" | 
| - " See \"gn help label_pattern\" for details.\n" | 
| + " - Label pattern: The result will be which targets depend on any\n" | 
| + " target matching the given pattern. Patterns will not match\n" | 
| + " configs. These are not general regular expressions, see\n" | 
| + " \"gn help label_pattern\" for details.\n" | 
| "\n" | 
| - " If the input is a file name, the output will be the target(s)\n" | 
| - " referencing that file (potentially recursively if used with --tree\n" | 
| - " or --all). By default, only targets from the default toolchain that\n" | 
| - " reference the file will be listed.\n" | 
| + " - File name: The result will be which targets list the given file in\n" | 
| + " its \"inputs\", \"sources\", \"public\", or \"data\". Any input\n" | 
| + " that does not contain wildcards and does not match a target or a\n" | 
| + " config will be treated as a file.\n" | 
| + "\n" | 
| + "Options\n" | 
| "\n" | 
| " --all\n" | 
| " When used without --tree, will recurse and display all unique\n" | 
| - " dependencies of the given targets. When used with --tree, turns\n" | 
| - " off eliding to show a complete tree.\n" | 
| + " dependencies of the given targets. For example, if the input is\n" | 
| + " a target, this will output all targets that depend directly or\n" | 
| + " indirectly on the input. If the input is a file, this will output\n" | 
| + " all targets that depend directly or indirectly on that file.\n" | 
| + "\n" | 
| + " When used with --tree, turns off eliding to show a complete tree.\n" | 
| "\n" | 
| " --all-toolchains\n" | 
| - " For target patterns, make the label pattern match all toolchains.\n" | 
| - " If the label pattern does not specify an explicit toolchain,\n" | 
| - " labels from all toolchains will be matched (normally only the\n" | 
| - " default toolchain is matched when no toolchain is specified).\n" | 
| + " Normally only inputs in the default toolchain will be included.\n" | 
| + " This switch will turn on matching all toolchains.\n" | 
| + "\n" | 
| + " For example, a file is in a target might be compiled twice:\n" | 
| + " once in the default toolchain and once in a secondary one. Without\n" | 
| + " this flag, only the default toolchain one will be matched and\n" | 
| + " printed (potentially with its recursive dependencies, depending on\n" | 
| + " the other options). With this flag, both will be printed\n" | 
| + " (potentially with both of their recursive dependencies).\n" | 
| "\n" | 
| - " For filename inputs, lists targets from all toolchains that\n" | 
| - " include the file.\n" | 
| + TARGET_PRINTING_MODE_COMMAND_LINE_HELP | 
| "\n" | 
| - " --files\n" | 
| - " Output unique filenames referencing a matched target or config.\n" | 
| - " These will be relative to the source root directory such that they\n" | 
| - " are suitable for piping to other commands.\n" | 
| + TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP | 
| "\n" | 
| " --tree\n" | 
| " Outputs a reverse dependency tree from the given target.\n" | 
| " Duplicates will be elided. Combine with --all to see a full\n" | 
| " dependency tree.\n" | 
| "\n" | 
| + " Tree output can not be used with the filtering or output flags:\n" | 
| + " --as, --type, --testonly.\n" | 
| + "\n" | 
| + TARGET_TYPE_FILTER_COMMAND_LINE_HELP | 
| + "\n" | 
| "Examples (target input)\n" | 
| "\n" | 
| " gn refs out/Debug //tools/gn:gn\n" | 
| " Find all targets depending on the given exact target name.\n" | 
| "\n" | 
| - " gn refs out/Debug //base:i18n --files | xargs gvim\n" | 
| - " Edit all files containing references to //base:i18n\n" | 
| + " gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim\n" | 
| + " Edit all .gn files containing references to //base:i18n\n" | 
| "\n" | 
| " gn refs out/Debug //base --all\n" | 
| " List all targets depending directly or indirectly on //base:base.\n" | 
| @@ -269,15 +331,20 @@ const char kRefs_Help[] = | 
| "Examples (file input)\n" | 
| "\n" | 
| " gn refs out/Debug //base/macros.h\n" | 
| - " Print targets listing //base/macros.h as a source.\n" | 
| + " Print target(s) listing //base/macros.h as a source.\n" | 
| "\n" | 
| " gn refs out/Debug //base/macros.h --tree\n" | 
| " Display a reverse dependency tree to get to the given file. This\n" | 
| " will show how dependencies will reference that file.\n" | 
| "\n" | 
| - " gn refs out/Debug //base/macros.h --all\n" | 
| + " gn refs out/Debug //base/macros.h //base/basictypes.h --all\n" | 
| " Display all unique targets with some dependency path to a target\n" | 
| - " containing the given file as a source.\n"; | 
| + " containing either of the given files as a source.\n" | 
| + "\n" | 
| + " gn refs out/Debug //base/macros.h --testonly=true --type=executable\n" | 
| + " --all --as=output\n" | 
| + " Display the executable file names of all test executables\n" | 
| + " potentially affected by a change to the given file.\n"; | 
| int RunRefs(const std::vector<std::string>& args) { | 
| if (args.size() != 2) { | 
| @@ -291,95 +358,52 @@ int RunRefs(const std::vector<std::string>& args) { | 
| bool tree = cmdline->HasSwitch("tree"); | 
| bool all = cmdline->HasSwitch("all"); | 
| bool all_toolchains = cmdline->HasSwitch("all-toolchains"); | 
| - bool files = cmdline->HasSwitch("files"); | 
| Setup* setup = new Setup; | 
| setup->set_check_for_bad_items(false); | 
| if (!setup->DoSetup(args[0], false) || !setup->Run()) | 
| return 1; | 
| - // Figure out the target or targets that the user is querying. | 
| - bool is_file_input = false; | 
| - std::vector<const Target*> query; | 
| - if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains, | 
| - &query)) | 
| + // The inputs are everything but the first arg (which is the build dir). | 
| + std::vector<std::string> inputs(args.begin() + 1, args.end()); | 
| + | 
| + // 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; | 
| - if (query.empty()) { | 
| - // If it doesn't match any targets, assume input is file. | 
| - GetTargetsContainingFile(setup, args[1], all_toolchains, &query); | 
| - if (query.empty()) { | 
| - OutputString("\"" + args[1] + "\" matches no targets.\n"); | 
| - return 0; | 
| - } | 
| - is_file_input = true; | 
| + | 
| + // 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 (const auto& config : config_matches) { | 
| + GetTargetsReferencingConfig(setup, all_targets, config, all_toolchains, | 
| + &explicit_target_matches); | 
| } | 
| // Construct the reverse dependency tree. | 
| DepMap dep_map; | 
| FillDepMap(setup, &dep_map); | 
| - // When the input is a file, we want to print the targets in |query|, which | 
| - // are the things that directly reference the file, but when the input is a | 
| - // target, we skip that since the user is asking for what reference those. | 
| - if (tree) { | 
| - // Output dependency tree. | 
| - if (files) { | 
| - Err(nullptr, "--files option can't be used with --tree option.") | 
| - .PrintToStdout(); | 
| - return 1; | 
| - } | 
| - if (query.size() != 1) { | 
| - Err(nullptr, "Query matches more than one target.", | 
| - "--tree only supports a single target as input.").PrintToStdout(); | 
| - return 1; | 
| - } | 
| - if (all) { | 
| - // Recursively print all targets. | 
| - for (const auto& cur_query : query) { | 
| - if (is_file_input) | 
| - RecursivePrintTarget(dep_map, cur_query, nullptr, 0); | 
| - else | 
| - RecursivePrintTargetDeps(dep_map, cur_query, nullptr, 0); | 
| - } | 
| - } else { | 
| - // Recursively print unique targets. | 
| - TargetSet seen_targets; | 
| - for (const auto& cur_query : query) { | 
| - if (is_file_input) | 
| - RecursivePrintTarget(dep_map, cur_query, &seen_targets, 0); | 
| - else | 
| - RecursivePrintTargetDeps(dep_map, cur_query, &seen_targets, 0); | 
| - } | 
| - } | 
| - } else if (all) { | 
| - // Output recursive dependencies, uniquified and flattened. | 
| - TargetSet results; | 
| - for (const auto& cur_query : query) { | 
| - // File inputs also include the top level targets we found. | 
| - if (is_file_input) | 
| - results.insert(cur_query); | 
| - RecursiveCollectChildRefs(dep_map, cur_query, &results); | 
| - } | 
| - OutputResultSet(results, files); | 
| - } else { | 
| - TargetSet results; | 
| - for (const auto& cur_query : query) { | 
| - if (is_file_input) { | 
| - // When querying for a file, output the resolved list of targets only | 
| - // (don't need to track back any target dependencies). | 
| - results.insert(cur_query); | 
| - } else { | 
| - // When querying for a target, output direct references of everything | 
| - // in the query. | 
| - DepMap::const_iterator dep_begin = dep_map.lower_bound(cur_query); | 
| - DepMap::const_iterator dep_end = dep_map.upper_bound(cur_query); | 
| - for (DepMap::const_iterator cur_dep = dep_begin; | 
| - cur_dep != dep_end; cur_dep++) | 
| - results.insert(cur_dep->second); | 
| - } | 
| - } | 
| - OutputResultSet(results, files); | 
| - } | 
| + if (tree) | 
| + DoTreeOutput(dep_map, target_matches, explicit_target_matches, all); | 
| + else if (all) | 
| + DoAllListOutput(dep_map, target_matches, explicit_target_matches); | 
| + else | 
| + DoDirectListOutput(dep_map, target_matches, explicit_target_matches); | 
| return 0; | 
| } |