OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include <map> |
5 #include <set> | 6 #include <set> |
6 | 7 |
7 #include "base/command_line.h" | 8 #include "base/command_line.h" |
8 #include "tools/gn/commands.h" | 9 #include "tools/gn/commands.h" |
9 #include "tools/gn/filesystem_utils.h" | 10 #include "tools/gn/filesystem_utils.h" |
10 #include "tools/gn/input_file.h" | 11 #include "tools/gn/input_file.h" |
11 #include "tools/gn/item.h" | 12 #include "tools/gn/item.h" |
12 #include "tools/gn/pattern.h" | |
13 #include "tools/gn/setup.h" | 13 #include "tools/gn/setup.h" |
14 #include "tools/gn/standard_out.h" | 14 #include "tools/gn/standard_out.h" |
15 #include "tools/gn/target.h" | 15 #include "tools/gn/target.h" |
16 | 16 |
17 namespace commands { | 17 namespace commands { |
18 | 18 |
19 namespace { | 19 namespace { |
20 | 20 |
21 // Returns the file path generating this record. | 21 typedef std::set<const Target*> TargetSet; |
22 base::FilePath FilePathForRecord(const BuilderRecord* record) { | 22 typedef std::vector<const Target*> TargetVector; |
23 if (!record->item()) | 23 |
24 return base::FilePath(FILE_PATH_LITERAL("=UNRESOLVED DEPENDENCY=")); | 24 // Maps targets to the list of targets that depend on them. |
25 return record->item()->defined_from()->GetRange().begin().file() | 25 typedef std::multimap<const Target*, const Target*> DepMap; |
26 ->physical_name(); | 26 |
| 27 // Populates the reverse dependency map for the targets in the Setup. |
| 28 void FillDepMap(Setup* setup, DepMap* dep_map) { |
| 29 std::vector<const Target*> targets = |
| 30 setup->builder()->GetAllResolvedTargets(); |
| 31 |
| 32 for (size_t target_i = 0; target_i < targets.size(); target_i++) { |
| 33 const Target* target = targets[target_i]; |
| 34 |
| 35 // Add all deps to the map. |
| 36 const LabelTargetVector& deps = target->deps(); |
| 37 for (size_t dep_i = 0; dep_i < deps.size(); dep_i++) |
| 38 dep_map->insert(std::make_pair(deps[dep_i].ptr, target)); |
| 39 |
| 40 // Include data deps as well. |
| 41 const LabelTargetVector& datadeps = target->datadeps(); |
| 42 for (size_t dep_i = 0; dep_i < datadeps.size(); dep_i++) |
| 43 dep_map->insert(std::make_pair(datadeps[dep_i].ptr, target)); |
| 44 } |
| 45 } |
| 46 |
| 47 // Returns the file path generating this item. |
| 48 base::FilePath FilePathForItem(const Item* item) { |
| 49 return item->defined_from()->GetRange().begin().file()->physical_name(); |
| 50 } |
| 51 |
| 52 // Prints the targets which are the result of a query. This list is sorted |
| 53 // and, if as_files is set, the unique filenames matching those targets will |
| 54 // be used. |
| 55 void OutputResultSet(const TargetSet& results, bool as_files) { |
| 56 if (results.empty()) |
| 57 return; |
| 58 |
| 59 if (as_files) { |
| 60 // Output the set of unique source files. |
| 61 std::set<std::string> unique_files; |
| 62 for (TargetSet::const_iterator iter = results.begin(); |
| 63 iter != results.end(); ++iter) |
| 64 unique_files.insert(FilePathToUTF8(FilePathForItem(*iter))); |
| 65 |
| 66 for (std::set<std::string>::const_iterator iter = unique_files.begin(); |
| 67 iter != unique_files.end(); ++iter) { |
| 68 OutputString(*iter + "\n"); |
| 69 } |
| 70 } else { |
| 71 // Output sorted and uniquified list of labels. The set will sort the |
| 72 // labels. |
| 73 std::set<Label> unique_labels; |
| 74 for (TargetSet::const_iterator iter = results.begin(); |
| 75 iter != results.end(); ++iter) |
| 76 unique_labels.insert((*iter)->label()); |
| 77 |
| 78 // Grab the label of the default toolchain from a random target. |
| 79 Label default_tc_label = |
| 80 (*results.begin())->settings()->default_toolchain_label(); |
| 81 |
| 82 for (std::set<Label>::const_iterator iter = unique_labels.begin(); |
| 83 iter != unique_labels.end(); ++iter) { |
| 84 // Print toolchain only for ones not in the default toolchain. |
| 85 OutputString(iter->GetUserVisibleName( |
| 86 iter->GetToolchainLabel() != default_tc_label)); |
| 87 OutputString("\n"); |
| 88 } |
| 89 } |
| 90 } |
| 91 |
| 92 // Prints refs of the given target (not the target itself). If the set is |
| 93 // non-null, new targets encountered will be added to the set, and if a ref is |
| 94 // in the set already, it will not be recused into. When the set is null, all |
| 95 // refs will be printed. |
| 96 void RecursivePrintTree(const DepMap& dep_map, |
| 97 const Target* target, |
| 98 TargetSet* seen_targets, |
| 99 int indent_level) { |
| 100 std::string indent(indent_level * 2, ' '); |
| 101 |
| 102 DepMap::const_iterator dep_begin = dep_map.lower_bound(target); |
| 103 DepMap::const_iterator dep_end = dep_map.upper_bound(target); |
| 104 for (DepMap::const_iterator cur_dep = dep_begin; |
| 105 cur_dep != dep_end; cur_dep++) { |
| 106 const Target* cur_target = cur_dep->second; |
| 107 |
| 108 // Only print the toolchain for non-default-toolchain targets. |
| 109 OutputString(indent + cur_target->label().GetUserVisibleName( |
| 110 !cur_target->settings()->is_default())); |
| 111 |
| 112 bool print_children = true; |
| 113 if (seen_targets) { |
| 114 if (seen_targets->find(cur_target) == seen_targets->end()) { |
| 115 // New target, mark it visited. |
| 116 seen_targets->insert(cur_target); |
| 117 } else { |
| 118 // Already seen. |
| 119 print_children = false; |
| 120 // Only print "..." if something is actually elided, which means that |
| 121 // the current target has children. |
| 122 if (dep_map.lower_bound(cur_target) != dep_map.upper_bound(cur_target)) |
| 123 OutputString("..."); |
| 124 } |
| 125 } |
| 126 |
| 127 OutputString("\n"); |
| 128 if (print_children) |
| 129 RecursivePrintTree(dep_map, cur_target, seen_targets, indent_level + 1); |
| 130 } |
| 131 } |
| 132 |
| 133 void RecursiveCollectChildRefs(const DepMap& dep_map, |
| 134 const Target* target, |
| 135 TargetSet* results); |
| 136 |
| 137 // Recursively finds all targets that reference the given one, and additionally |
| 138 // adds the current one to the list. |
| 139 void RecursiveCollectRefs(const DepMap& dep_map, |
| 140 const Target* target, |
| 141 TargetSet* results) { |
| 142 if (results->find(target) != results->end()) |
| 143 return; // Already found this target. |
| 144 results->insert(target); |
| 145 RecursiveCollectChildRefs(dep_map, target, results); |
| 146 } |
| 147 |
| 148 // Recursively finds all targets that reference the given one. |
| 149 void RecursiveCollectChildRefs(const DepMap& dep_map, |
| 150 const Target* target, |
| 151 TargetSet* results) { |
| 152 DepMap::const_iterator dep_begin = dep_map.lower_bound(target); |
| 153 DepMap::const_iterator dep_end = dep_map.upper_bound(target); |
| 154 for (DepMap::const_iterator cur_dep = dep_begin; |
| 155 cur_dep != dep_end; cur_dep++) |
| 156 RecursiveCollectRefs(dep_map, cur_dep->second, results); |
27 } | 157 } |
28 | 158 |
29 } // namespace | 159 } // namespace |
30 | 160 |
31 const char kRefs[] = "refs"; | 161 const char kRefs[] = "refs"; |
32 const char kRefs_HelpShort[] = | 162 const char kRefs_HelpShort[] = |
33 "refs: Find stuff referencing a target, directory, or config."; | 163 "refs: Find stuff referencing a target, directory, or config."; |
34 const char kRefs_Help[] = | 164 const char kRefs_Help[] = |
35 "gn refs <label_pattern> [--files]\n" | 165 "gn refs <build_dir> <label_pattern> [--files] [--tree] [--all]\n" |
| 166 " [--all-toolchains]\n" |
36 "\n" | 167 "\n" |
37 " Finds code referencing a given label. The label can be a\n" | 168 " Finds which targets reference a given target or targets (reverse\n" |
38 " target or config name. Unlike most other commands, unresolved\n" | 169 " dependencies).\n" |
39 " dependencies will be tolerated. This allows you to use this command\n" | |
40 " to find references to targets you're in the process of moving.\n" | |
41 "\n" | 170 "\n" |
42 " By default, the mapping from source item to dest item (where the\n" | 171 " The <label_pattern> can take exact labels or patterns that match more\n" |
43 " pattern matches the dest item). See \"gn help pattern\" for\n" | 172 " than one (although not general regular expressions).\n" |
44 " information on pattern-matching rules.\n" | 173 " See \"gn help label_pattern\" for details.\n" |
45 "\n" | 174 "\n" |
46 "Option:\n" | 175 " --all\n" |
| 176 " When used without --tree, will recurse and display all unique\n" |
| 177 " dependencies of the given targets. When used with --tree, turns\n" |
| 178 " off eliding to show a complete tree.\n" |
| 179 "\n" |
| 180 " --all-toolchains\n" |
| 181 " Make the label pattern matche all toolchains. If the label pattern\n" |
| 182 " does not specify an explicit toolchain, labels from all toolchains\n" |
| 183 " will be matched (normally only the default toolchain is matched\n" |
| 184 " when no toolchain is specified).\n" |
| 185 "\n" |
47 " --files\n" | 186 " --files\n" |
48 " Output unique filenames referencing a matched target or config.\n" | 187 " Output unique filenames referencing a matched target or config.\n" |
| 188 " These will be relative to the source root directory such that they\n" |
| 189 " are suitable for piping to other commands.\n" |
49 "\n" | 190 "\n" |
50 "Examples:\n" | 191 " --tree\n" |
51 " gn refs \"//tools/gn/*\"\n" | 192 " Outputs a reverse dependency tree from the given target. The label\n" |
52 " Find all targets depending on any target or config in the\n" | 193 " pattern must match one target exactly. Duplicates will be elided.\n" |
53 " \"tools/gn\" directory.\n" | 194 " Combine with --all to see a full dependency tree.\n" |
54 "\n" | 195 "\n" |
55 " gn refs //tools/gn:gn\n" | 196 "Examples\n" |
| 197 "\n" |
| 198 " gn refs out/Debug //tools/gn:gn\n" |
56 " Find all targets depending on the given exact target name.\n" | 199 " Find all targets depending on the given exact target name.\n" |
57 "\n" | 200 "\n" |
58 " gn refs \"*gtk*\" --files\n" | 201 " gn refs out/Debug //base:i18n --files | xargs gvim\n" |
59 " Find all unique buildfiles with a dependency on a target that has\n" | 202 " Edit all files containing references to //base:i18n\n" |
60 " the substring \"gtk\" in the name.\n"; | 203 "\n" |
| 204 " gn refs out/Debug //base --all\n" |
| 205 " List all targets depending directly or indirectly on //base:base.\n" |
| 206 "\n" |
| 207 " gn refs out/Debug \"//base/*\"\n" |
| 208 " List all targets depending directly on any target in //base or\n" |
| 209 " its subdirectories.\n" |
| 210 "\n" |
| 211 " gn refs out/Debug \"//base:*\"\n" |
| 212 " List all targets depending directly on any target in\n" |
| 213 " //base/BUILD.gn.\n" |
| 214 "\n" |
| 215 " gn refs out/Debug //base --tree\n" |
| 216 " Print a reverse dependency tree of //base:base\n"; |
61 | 217 |
62 int RunRefs(const std::vector<std::string>& args) { | 218 int RunRefs(const std::vector<std::string>& args) { |
63 if (args.size() != 1 && args.size() != 2) { | 219 if (args.size() != 2) { |
64 Err(Location(), "You're holding it wrong.", | 220 Err(Location(), "You're holding it wrong.", |
65 "Usage: \"gn refs <label_pattern>\"").PrintToStdout(); | 221 "Usage: \"gn refs <build_dir> <label_pattern>\"").PrintToStdout(); |
66 return 1; | 222 return 1; |
67 } | 223 } |
68 | 224 |
69 Pattern pattern(args[0]); | 225 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); |
70 // Check for common errors on input. | 226 bool tree = cmdline->HasSwitch("tree"); |
71 if (args[0].find('*') == std::string::npos) { | 227 bool all = cmdline->HasSwitch("all"); |
72 // We need to begin with a "//" and have a colon if there's no "*" or it | 228 bool all_toolchains = cmdline->HasSwitch("all-toolchains"); |
73 // will be impossible to match anything. | 229 bool files = cmdline->HasSwitch("files"); |
74 if (args[0].size() < 2 || | |
75 (args[0][0] != '/' && args[0][1] != '/') || | |
76 args[0].find(':') == std::string::npos) { | |
77 OutputString("Assuming \"*" + args[0] + | |
78 "*\". See \"gn help refs\" for more information.\n", | |
79 DECORATION_YELLOW); | |
80 pattern = Pattern("*" + args[0] + "*"); | |
81 } | |
82 } | |
83 | 230 |
84 Setup* setup = new Setup; | 231 Setup* setup = new Setup; |
85 setup->set_check_for_bad_items(false); | 232 setup->set_check_for_bad_items(false); |
86 // TODO(brettw) bug 343726: Use a temporary directory instead of this | 233 if (!setup->DoSetup(args[0]) || !setup->Run()) |
87 // default one to avoid messing up any build that's in there. | |
88 if (!setup->DoSetup("//out/Default/") || !setup->Run()) | |
89 return 1; | 234 return 1; |
90 | 235 |
91 std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords(); | 236 // Figure out the target or targets that the user is querying. |
92 | 237 std::vector<const Target*> query; |
93 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); | 238 if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains, |
94 | 239 &query)) |
95 bool file_output = cmdline->HasSwitch("files"); | 240 return 1; |
96 std::set<std::string> unique_output; | 241 if (query.empty()) { |
97 | 242 OutputString("\"" + args[1] + "\" matches no targets.\n"); |
98 for (size_t record_index = 0; record_index < records.size(); record_index++) { | 243 return 0; |
99 const BuilderRecord* record = records[record_index]; | |
100 const BuilderRecord::BuilderRecordSet& deps = record->all_deps(); | |
101 for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin(); | |
102 d != deps.end(); ++d) { | |
103 std::string label = (*d)->label().GetUserVisibleName(false); | |
104 if (pattern.MatchesString(label)) { | |
105 // Got a match. | |
106 if (file_output) { | |
107 unique_output.insert(FilePathToUTF8(FilePathForRecord(record))); | |
108 break; // Found a match for this target's file, don't need more. | |
109 } else { | |
110 // We can get dupes when there are differnet toolchains involved, | |
111 // so we want to send all output through the de-duper. | |
112 unique_output.insert( | |
113 record->item()->label().GetUserVisibleName(false) + " -> " + | |
114 label); | |
115 } | |
116 } | |
117 } | |
118 } | 244 } |
119 | 245 |
120 for (std::set<std::string>::iterator i = unique_output.begin(); | 246 // Construct the reverse dependency tree. |
121 i != unique_output.end(); ++i) | 247 DepMap dep_map; |
122 OutputString(*i + "\n"); | 248 FillDepMap(setup, &dep_map); |
| 249 |
| 250 if (tree) { |
| 251 // Output dependency tree. |
| 252 if (files) { |
| 253 Err(NULL, "--files option can't be used with --tree option.") |
| 254 .PrintToStdout(); |
| 255 return 1; |
| 256 } |
| 257 if (query.size() != 1) { |
| 258 Err(NULL, "Query matches more than one target.", |
| 259 "--tree only supports a single target as input.").PrintToStdout(); |
| 260 return 1; |
| 261 } |
| 262 if (all) { |
| 263 // Recursively print all targets. |
| 264 RecursivePrintTree(dep_map, query[0], NULL, 0); |
| 265 } else { |
| 266 // Recursively print unique targets. |
| 267 TargetSet seen_targets; |
| 268 RecursivePrintTree(dep_map, query[0], &seen_targets, 0); |
| 269 } |
| 270 } else if (all) { |
| 271 // Output recursive dependencies, uniquified and flattened. |
| 272 TargetSet results; |
| 273 for (size_t query_i = 0; query_i < query.size(); query_i++) |
| 274 RecursiveCollectChildRefs(dep_map, query[query_i], &results); |
| 275 OutputResultSet(results, files); |
| 276 } else { |
| 277 // Output direct references of everything in the query. |
| 278 TargetSet results; |
| 279 for (size_t query_i = 0; query_i < query.size(); query_i++) { |
| 280 DepMap::const_iterator dep_begin = dep_map.lower_bound(query[query_i]); |
| 281 DepMap::const_iterator dep_end = dep_map.upper_bound(query[query_i]); |
| 282 for (DepMap::const_iterator cur_dep = dep_begin; |
| 283 cur_dep != dep_end; cur_dep++) |
| 284 results.insert(cur_dep->second); |
| 285 } |
| 286 OutputResultSet(results, files); |
| 287 } |
123 | 288 |
124 return 0; | 289 return 0; |
125 } | 290 } |
126 | 291 |
127 } // namespace commands | 292 } // namespace commands |
OLD | NEW |