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 <stddef.h> | 5 #include <stddef.h> |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <set> | 8 #include <set> |
9 #include <sstream> | 9 #include <sstream> |
10 | 10 |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "build/build_config.h" | 12 #include "base/json/json_writer.h" |
13 #include "base/memory/ptr_util.h" | |
13 #include "tools/gn/commands.h" | 14 #include "tools/gn/commands.h" |
14 #include "tools/gn/config.h" | 15 #include "tools/gn/config.h" |
15 #include "tools/gn/config_values_extractors.h" | 16 #include "tools/gn/desc_builder.h" |
16 #include "tools/gn/deps_iterator.h" | |
17 #include "tools/gn/filesystem_utils.h" | |
18 #include "tools/gn/item.h" | |
19 #include "tools/gn/label.h" | |
20 #include "tools/gn/runtime_deps.h" | |
21 #include "tools/gn/setup.h" | 17 #include "tools/gn/setup.h" |
22 #include "tools/gn/standard_out.h" | 18 #include "tools/gn/standard_out.h" |
23 #include "tools/gn/substitution_writer.h" | |
24 #include "tools/gn/switches.h" | 19 #include "tools/gn/switches.h" |
25 #include "tools/gn/target.h" | 20 #include "tools/gn/target.h" |
26 #include "tools/gn/variables.h" | 21 #include "tools/gn/variables.h" |
27 | 22 |
28 namespace commands { | 23 namespace commands { |
29 | 24 |
30 namespace { | 25 namespace { |
31 | 26 |
32 // Desc-specific command line switches. | 27 // Desc-specific command line switches. |
33 const char kBlame[] = "blame"; | 28 const char kBlame[] = "blame"; |
34 const char kTree[] = "tree"; | 29 const char kTree[] = "tree"; |
35 | 30 const char kAll[] = "all"; |
36 // Prints the given directory in a nice way for the user to view. | 31 |
37 std::string FormatSourceDir(const SourceDir& dir) { | 32 // Prints value with specified indentation level |
38 #if defined(OS_WIN) | 33 void PrintValue(const base::Value* value, int indentLevel) { |
39 // On Windows we fix up system absolute paths to look like native ones. | 34 std::string indent(indentLevel * 2, ' '); |
40 // Internally, they'll look like "/C:\foo\bar/" | 35 const base::ListValue* list = nullptr; |
41 if (dir.is_system_absolute()) { | 36 const base::DictionaryValue* dict = nullptr; |
42 std::string buf = dir.value(); | 37 std::string string; |
43 if (buf.size() > 3 && buf[2] == ':') { | 38 bool b(false); |
brettw
2016/07/01 23:34:17
Can you do
bool bool_value = false;
(the paren-s
| |
44 buf.erase(buf.begin()); // Erase beginning slash. | 39 if (value->GetAsList(&list)) { |
45 return buf; | 40 for (const auto& v : *list) { |
46 } | 41 PrintValue(v.get(), indentLevel); |
47 } | 42 } |
48 #endif | 43 } else if (value->GetAsString(&string)) { |
49 return dir.value(); | 44 OutputString(indent); |
50 } | 45 OutputString(string); |
51 | |
52 void RecursiveCollectChildDeps(const Target* target, | |
53 std::set<const Target*>* result); | |
54 | |
55 void RecursiveCollectDeps(const Target* target, | |
56 std::set<const Target*>* result) { | |
57 if (result->find(target) != result->end()) | |
58 return; // Already did this target. | |
59 result->insert(target); | |
60 | |
61 RecursiveCollectChildDeps(target, result); | |
62 } | |
63 | |
64 void RecursiveCollectChildDeps(const Target* target, | |
65 std::set<const Target*>* result) { | |
66 for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) | |
67 RecursiveCollectDeps(pair.ptr, result); | |
68 } | |
69 | |
70 // Prints dependencies of the given target (not the target itself). If the | |
71 // set is non-null, new targets encountered will be added to the set, and if | |
72 // a dependency is in the set already, it will not be recused into. When the | |
73 // set is null, all dependencies will be printed. | |
74 void RecursivePrintDeps(const Target* target, | |
75 const Label& default_toolchain, | |
76 std::set<const Target*>* seen_targets, | |
77 int indent_level) { | |
78 // Combine all deps into one sorted list. | |
79 std::vector<LabelTargetPair> sorted_deps; | |
80 for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) | |
81 sorted_deps.push_back(pair); | |
82 std::sort(sorted_deps.begin(), sorted_deps.end(), | |
83 LabelPtrLabelLess<Target>()); | |
84 | |
85 std::string indent(indent_level * 2, ' '); | |
86 for (const auto& pair : sorted_deps) { | |
87 const Target* cur_dep = pair.ptr; | |
88 | |
89 OutputString(indent + | |
90 cur_dep->label().GetUserVisibleName(default_toolchain)); | |
91 bool print_children = true; | |
92 if (seen_targets) { | |
93 if (seen_targets->find(cur_dep) == seen_targets->end()) { | |
94 // New target, mark it visited. | |
95 seen_targets->insert(cur_dep); | |
96 } else { | |
97 // Already seen. | |
98 print_children = false; | |
99 // Only print "..." if something is actually elided, which means that | |
100 // the current target has children. | |
101 if (!cur_dep->public_deps().empty() || | |
102 !cur_dep->private_deps().empty() || | |
103 !cur_dep->data_deps().empty()) | |
104 OutputString("..."); | |
105 } | |
106 } | |
107 | |
108 OutputString("\n"); | 46 OutputString("\n"); |
109 if (print_children) { | 47 } else if (value->GetAsBoolean(&b)) { |
110 RecursivePrintDeps(cur_dep, default_toolchain, seen_targets, | 48 OutputString(indent); |
111 indent_level + 1); | 49 OutputString(b ? "true" : "false"); |
112 } | 50 OutputString("\n"); |
113 } | 51 } else if (value->GetAsDictionary(&dict)) { |
114 } | 52 base::DictionaryValue::Iterator iter(*dict); |
115 | 53 while (!iter.IsAtEnd()) { |
116 void PrintDeps(const Target* target, bool display_header) { | 54 OutputString(indent + iter.key() + "\n"); |
117 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | 55 PrintValue(&iter.value(), indentLevel + 1); |
118 Label toolchain_label = target->label().GetToolchainLabel(); | 56 iter.Advance(); |
119 | 57 } |
120 // Tree mode is separate. | 58 } else if (value->IsType(base::Value::TYPE_NULL)) { |
121 if (cmdline->HasSwitch(kTree)) { | 59 OutputString(indent + "<null>\n"); |
122 if (display_header) | 60 } |
123 OutputString("\nDependency tree\n"); | 61 } |
124 | 62 |
125 if (cmdline->HasSwitch("all")) { | 63 // Default handler for property |
126 // Show all tree deps with no eliding. | 64 void DefaultHandler(const std::string& name, const base::Value* value) { |
127 RecursivePrintDeps(target, toolchain_label, nullptr, 1); | 65 OutputString("\n"); |
66 OutputString(name); | |
67 OutputString("\n"); | |
68 PrintValue(value, 1); | |
69 } | |
70 | |
71 // Specific handler for properties that needs different treatment | |
72 | |
73 void TypeHandler(const std::string& name, const base::Value* value) { | |
74 std::string type; | |
75 if (value->GetAsString(&type)) { | |
76 auto t = DescBuilder::GetOutputTypeForString(type); | |
77 OutputString("Type: ", DECORATION_YELLOW); | |
78 OutputString(Target::GetStringForOutputType(t)); | |
79 OutputString("\n"); | |
80 } | |
81 } | |
82 | |
83 void ToolchainHandler(const std::string& name, const base::Value* value) { | |
84 std::string toolchain; | |
85 if (value->GetAsString(&toolchain)) { | |
86 OutputString("Toolchain: ", DECORATION_YELLOW); | |
87 OutputString(toolchain + "\n"); | |
88 } | |
89 } | |
90 | |
91 void VisibilityHandler(const std::string& name, const base::Value* value) { | |
92 const base::ListValue* list; | |
93 if (value->GetAsList(&list)) { | |
94 if (list->empty()) { | |
95 base::StringValue str("(no visibility)"); | |
96 DefaultHandler(name, &str); | |
128 } else { | 97 } else { |
129 // Don't recurse into duplicates. | 98 DefaultHandler(name, value); |
130 std::set<const Target*> seen_targets; | 99 } |
131 RecursivePrintDeps(target, toolchain_label, &seen_targets, 1); | 100 } |
132 } | 101 } |
133 return; | 102 |
134 } | 103 void PublicHandler(const std::string& name, const base::Value* value) { |
135 | 104 std::string p; |
136 // Collect the deps to display. | 105 if (value->GetAsString(&p)) { |
137 if (cmdline->HasSwitch("all")) { | 106 if (p == "*") { |
138 // Show all dependencies. | 107 base::StringValue str("[All headers listed in the sources are public.]"); |
139 if (display_header) | 108 DefaultHandler(name, &str); |
140 OutputString("\nAll recursive dependencies\n"); | 109 return; |
141 | 110 } |
142 std::set<const Target*> all_deps; | 111 } |
143 RecursiveCollectChildDeps(target, &all_deps); | 112 DefaultHandler(name, value); |
144 FilterAndPrintTargetSet(display_header, all_deps); | 113 } |
114 | |
115 void ConfigsHandler(const std::string& name, const base::Value* value) { | |
116 bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | |
117 if (tree) | |
118 DefaultHandler(name + " tree (in order applying)", value); | |
119 else | |
120 DefaultHandler(name + " (in order applying, try also --tree)", value); | |
121 } | |
122 | |
123 void DepsHandler(const std::string& name, const base::Value* value) { | |
124 bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | |
125 bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | |
126 if (tree) { | |
127 DefaultHandler("Dependency tree", value); | |
145 } else { | 128 } else { |
146 std::vector<const Target*> deps; | 129 if (!all) { |
147 // Show direct dependencies only. | 130 DefaultHandler( |
148 if (display_header) { | 131 "Direct dependencies " |
149 OutputString( | 132 "(try also \"--all\", \"--tree\", or even \"--all --tree\")", |
150 "\nDirect dependencies " | 133 value); |
151 "(try also \"--all\", \"--tree\", or even \"--all --tree\")\n"); | 134 } else { |
152 } | 135 DefaultHandler("All recursive dependencies", value); |
153 for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) | 136 } |
154 deps.push_back(pair.ptr); | 137 } |
155 std::sort(deps.begin(), deps.end()); | 138 } |
156 FilterAndPrintTargets(display_header, &deps); | 139 |
157 } | 140 // Outputs need special processing when output patterns are present |
158 } | 141 void ProcessOutputs(base::DictionaryValue* target) { |
159 | 142 base::ListValue* patterns = nullptr; |
160 // libs and lib_dirs are special in that they're inherited. We don't currently | 143 base::ListValue* outputs = nullptr; |
161 // implement a blame feature for this since the bottom-up inheritance makes | 144 target->GetList("output_patterns", &patterns); |
162 // this difficult. | 145 target->GetList(variables::kOutputs, &outputs); |
163 void PrintLibDirs(const Target* target, bool display_header) { | 146 |
164 const OrderedSet<SourceDir>& lib_dirs = target->all_lib_dirs(); | 147 if (outputs || patterns) { |
165 if (lib_dirs.empty()) | |
166 return; | |
167 | |
168 if (display_header) | |
169 OutputString("\nlib_dirs\n"); | |
170 | |
171 for (size_t i = 0; i < lib_dirs.size(); i++) | |
172 OutputString(" " + FormatSourceDir(lib_dirs[i]) + "\n"); | |
173 } | |
174 | |
175 void PrintLibs(const Target* target, bool display_header) { | |
176 const OrderedSet<LibFile>& libs = target->all_libs(); | |
177 if (libs.empty()) | |
178 return; | |
179 | |
180 if (display_header) | |
181 OutputString("\nlibs\n"); | |
182 | |
183 for (size_t i = 0; i < libs.size(); i++) | |
184 OutputString(" " + libs[i].value() + "\n"); | |
185 } | |
186 | |
187 void PrintPublic(const Target* target, bool display_header) { | |
188 if (display_header) | |
189 OutputString("\npublic\n"); | |
190 | |
191 if (target->all_headers_public()) { | |
192 OutputString(" [All headers listed in the sources are public.]\n"); | |
193 return; | |
194 } | |
195 | |
196 Target::FileList public_headers = target->public_headers(); | |
197 std::sort(public_headers.begin(), public_headers.end()); | |
198 for (const auto& hdr : public_headers) | |
199 OutputString(" " + hdr.value() + "\n"); | |
200 } | |
201 | |
202 void PrintCheckIncludes(const Target* target, bool display_header) { | |
203 if (display_header) | |
204 OutputString("\ncheck_includes\n"); | |
205 | |
206 if (target->check_includes()) | |
207 OutputString(" true\n"); | |
208 else | |
209 OutputString(" false\n"); | |
210 } | |
211 | |
212 void PrintAllowCircularIncludesFrom(const Target* target, bool display_header) { | |
213 if (display_header) | |
214 OutputString("\nallow_circular_includes_from\n"); | |
215 | |
216 Label toolchain_label = target->label().GetToolchainLabel(); | |
217 for (const auto& cur : target->allow_circular_includes_from()) | |
218 OutputString(" " + cur.GetUserVisibleName(toolchain_label) + "\n"); | |
219 } | |
220 | |
221 void PrintVisibility(const Target* target, bool display_header) { | |
222 if (display_header) | |
223 OutputString("\nvisibility\n"); | |
224 | |
225 OutputString(target->visibility().Describe(2, false)); | |
226 } | |
227 | |
228 void PrintTestonly(const Target* target, bool display_header) { | |
229 if (display_header) | |
230 OutputString("\ntestonly\n"); | |
231 | |
232 if (target->testonly()) | |
233 OutputString(" true\n"); | |
234 else | |
235 OutputString(" false\n"); | |
236 } | |
237 | |
238 // Recursively prints subconfigs of a config. | |
239 void PrintSubConfigs(const Config* config, int indent_level) { | |
240 if (config->configs().empty()) | |
241 return; | |
242 | |
243 std::string indent(indent_level * 2, ' '); | |
244 Label toolchain_label = config->label().GetToolchainLabel(); | |
245 for (const auto& pair : config->configs()) { | |
246 OutputString( | |
247 indent + pair.label.GetUserVisibleName(toolchain_label) + "\n"); | |
248 PrintSubConfigs(pair.ptr, indent_level + 1); | |
249 } | |
250 } | |
251 | |
252 // This allows configs stored as either std::vector<LabelConfigPair> or | |
253 // UniqueVector<LabelConfigPair> to be printed. | |
254 template <class VectorType> | |
255 void PrintConfigsVector(const Item* item, | |
256 const VectorType& configs, | |
257 const std::string& heading, | |
258 bool display_header) { | |
259 if (configs.empty()) | |
260 return; | |
261 | |
262 bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); | |
263 | |
264 // Don't sort since the order determines how things are processed. | |
265 if (display_header) { | |
266 if (tree) | |
267 OutputString("\n" + heading + " tree (in order applying)\n"); | |
268 else | |
269 OutputString("\n" + heading + " (in order applying, try also --tree)\n"); | |
270 } | |
271 | |
272 Label toolchain_label = item->label().GetToolchainLabel(); | |
273 for (const auto& config : configs) { | |
274 OutputString(" " + config.label.GetUserVisibleName(toolchain_label) + | |
275 "\n"); | |
276 if (tree) | |
277 PrintSubConfigs(config.ptr, 2); // 2 = start with double-indent. | |
278 } | |
279 } | |
280 | |
281 void PrintConfigs(const Target* target, bool display_header) { | |
282 PrintConfigsVector(target, target->configs().vector(), "configs", | |
283 display_header); | |
284 } | |
285 | |
286 void PrintConfigs(const Config* config, bool display_header) { | |
287 PrintConfigsVector(config, config->configs().vector(), "configs", | |
288 display_header); | |
289 } | |
290 | |
291 void PrintPublicConfigs(const Target* target, bool display_header) { | |
292 PrintConfigsVector(target, target->public_configs(), | |
293 "public_configs", display_header); | |
294 } | |
295 | |
296 void PrintAllDependentConfigs(const Target* target, bool display_header) { | |
297 PrintConfigsVector(target, target->all_dependent_configs(), | |
298 "all_dependent_configs", display_header); | |
299 } | |
300 | |
301 void PrintFileList(const Target::FileList& files, | |
302 const std::string& header, | |
303 bool indent_extra, | |
304 bool display_header) { | |
305 if (files.empty()) | |
306 return; | |
307 | |
308 if (display_header) | |
309 OutputString("\n" + header + "\n"); | |
310 | |
311 std::string indent = indent_extra ? " " : " "; | |
312 | |
313 Target::FileList sorted = files; | |
314 std::sort(sorted.begin(), sorted.end()); | |
315 for (const auto& elem : sorted) | |
316 OutputString(indent + elem.value() + "\n"); | |
317 } | |
318 | |
319 void PrintSources(const Target* target, bool display_header) { | |
320 PrintFileList(target->sources(), "sources", false, display_header); | |
321 } | |
322 | |
323 void PrintInputs(const Target* target, bool display_header) { | |
324 PrintFileList(target->inputs(), "inputs", false, display_header); | |
325 } | |
326 | |
327 void PrintOutputs(const Target* target, bool display_header) { | |
328 if (display_header) | |
329 OutputString("\noutputs\n"); | 148 OutputString("\noutputs\n"); |
330 | 149 int indent = 1; |
331 if (target->output_type() == Target::ACTION) { | 150 if (patterns) { |
332 // Action, print out outputs, don't apply sources to it. | 151 OutputString(" Output patterns\n"); |
333 for (const auto& elem : target->action_values().outputs().list()) { | 152 indent = 2; |
334 OutputString(" " + elem.AsString() + "\n"); | 153 PrintValue(patterns, indent); |
335 } | |
336 } else if (target->output_type() == Target::CREATE_BUNDLE) { | |
337 std::vector<SourceFile> output_files; | |
338 target->bundle_data().GetOutputsAsSourceFiles(target->settings(), | |
339 &output_files); | |
340 PrintFileList(output_files, std::string(), true, false); | |
341 } else { | |
342 const SubstitutionList& outputs = target->action_values().outputs(); | |
343 if (!outputs.required_types().empty()) { | |
344 // Display the pattern and resolved pattern separately, since there are | |
345 // subtitutions used. | |
346 OutputString(" Output pattern\n"); | |
347 for (const auto& elem : outputs.list()) | |
348 OutputString(" " + elem.AsString() + "\n"); | |
349 | |
350 // Now display what that resolves to given the sources. | |
351 OutputString("\n Resolved output file list\n"); | 154 OutputString("\n Resolved output file list\n"); |
352 } | 155 } |
353 | 156 if (outputs) |
354 // Resolved output list. | 157 PrintValue(outputs, indent); |
355 std::vector<SourceFile> output_files; | 158 |
356 SubstitutionWriter::ApplyListToSources(target->settings(), outputs, | 159 target->Remove("output_patterns", nullptr); |
357 target->sources(), &output_files); | 160 target->Remove(variables::kOutputs, nullptr); |
358 PrintFileList(output_files, std::string(), true, false); | 161 } |
359 } | |
360 } | |
361 | |
362 void PrintScript(const Target* target, bool display_header) { | |
363 if (display_header) | |
364 OutputString("\nscript\n"); | |
365 OutputString(" " + target->action_values().script().value() + "\n"); | |
366 } | |
367 | |
368 void PrintArgs(const Target* target, bool display_header) { | |
369 if (display_header) | |
370 OutputString("\nargs\n"); | |
371 for (const auto& elem : target->action_values().args().list()) { | |
372 OutputString(" " + elem.AsString() + "\n"); | |
373 } | |
374 } | |
375 | |
376 void PrintDepfile(const Target* target, bool display_header) { | |
377 if (target->action_values().depfile().empty()) | |
378 return; | |
379 if (display_header) | |
380 OutputString("\ndepfile\n"); | |
381 OutputString(" " + target->action_values().depfile().AsString() + "\n"); | |
382 } | |
383 | |
384 // Attribute the origin for attributing from where a target came from. Does | |
385 // nothing if the input is null or it does not have a location. | |
386 void OutputSourceOfDep(const ParseNode* origin, std::ostream& out) { | |
387 if (!origin) | |
388 return; | |
389 Location location = origin->GetRange().begin(); | |
390 out << " (Added by " + location.file()->name().value() << ":" | |
391 << location.line_number() << ")\n"; | |
392 } | |
393 | |
394 // Templatized writer for writing out different config value types. | |
395 template<typename T> struct DescValueWriter {}; | |
396 template<> struct DescValueWriter<std::string> { | |
397 void operator()(const std::string& str, std::ostream& out) const { | |
398 out << " " << str << "\n"; | |
399 } | |
400 }; | |
401 | |
402 template<> struct DescValueWriter<SourceDir> { | |
403 void operator()(const SourceDir& dir, std::ostream& out) const { | |
404 out << " " << FormatSourceDir(dir) << "\n"; | |
405 } | |
406 }; | |
407 | |
408 template<> struct DescValueWriter<LibFile> { | |
409 void operator()(const LibFile& lib, std::ostream& out) const { | |
410 if (lib.is_source_file()) | |
411 out << " " << lib.source_file().value() << "\n"; | |
412 else | |
413 out << " " << lib.value() << "\n"; | |
414 } | |
415 }; | |
416 | |
417 // Writes a given config value type to the string, optionally with attribution. | |
418 // This should match RecursiveTargetConfigToStream in the order it traverses. | |
419 template<typename T> void OutputRecursiveTargetConfig( | |
420 const Target* target, | |
421 const char* header_name, | |
422 const std::vector<T>& (ConfigValues::* getter)() const) { | |
423 bool display_blame = | |
424 base::CommandLine::ForCurrentProcess()->HasSwitch(kBlame); | |
425 | |
426 DescValueWriter<T> writer; | |
427 std::ostringstream out; | |
428 | |
429 for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) { | |
430 if ((iter.cur().*getter)().empty()) | |
431 continue; | |
432 | |
433 // Optional blame sub-head. | |
434 if (display_blame) { | |
435 const Config* config = iter.GetCurrentConfig(); | |
436 if (config) { | |
437 // Source of this value is a config. | |
438 out << " From " << config->label().GetUserVisibleName(false) << "\n"; | |
439 OutputSourceOfDep(iter.origin(), out); | |
440 } else { | |
441 // Source of this value is the target itself. | |
442 out << " From " << target->label().GetUserVisibleName(false) << "\n"; | |
443 } | |
444 } | |
445 | |
446 // Actual values. | |
447 ConfigValuesToStream(iter.cur(), getter, writer, out); | |
448 } | |
449 | |
450 std::string out_str = out.str(); | |
451 if (!out_str.empty()) { | |
452 if (header_name) | |
453 OutputString("\n" + std::string(header_name) + "\n"); | |
454 OutputString(out_str); | |
455 } | |
456 } | |
457 | |
458 template<typename T> void OutputConfigValueArray( | |
459 const ConfigValues& values, | |
460 const char* header_name, | |
461 const std::vector<T>& (ConfigValues::* getter)() const) { | |
462 std::ostringstream out; | |
463 | |
464 DescValueWriter<T> writer; | |
465 for (const T& cur : (values.*getter)()) | |
466 writer(cur, out); | |
467 | |
468 std::string out_str = out.str(); | |
469 if (!out_str.empty()) { | |
470 if (header_name) | |
471 OutputString("\n" + std::string(header_name) + "\n"); | |
472 OutputString(out_str); | |
473 } | |
474 } | |
475 | |
476 void PrintRuntimeDeps(const Target* target) { | |
477 bool display_blame = | |
478 base::CommandLine::ForCurrentProcess()->HasSwitch(kBlame); | |
479 Label toolchain = target->label().GetToolchainLabel(); | |
480 | |
481 const Target* previous_from = NULL; | |
482 for (const auto& pair : ComputeRuntimeDeps(target)) { | |
483 if (display_blame) { | |
484 // Generally a target's runtime deps will be listed sequentially, so | |
485 // group them and don't duplicate the "from" label for two in a row. | |
486 if (previous_from == pair.second) { | |
487 OutputString(" "); // Just indent. | |
488 } else { | |
489 previous_from = pair.second; | |
490 OutputString("From "); | |
491 OutputString(pair.second->label().GetUserVisibleName(toolchain)); | |
492 OutputString("\n "); // Make the file name indented. | |
493 } | |
494 } | |
495 OutputString(pair.first.value()); | |
496 OutputString("\n"); | |
497 } | |
498 } | |
499 | |
500 // If "what" is empty, prints all PCH info. If "what" is nonempty, prints only | |
501 // the things that match (if any). Returns true if anything was printed. | |
502 bool PrintPrecompiledHeaderInfo(const ConfigValues& values, | |
503 const std::string& what, | |
504 bool display_headers) { | |
505 bool found_match = false; | |
506 if (what == variables::kPrecompiledHeader || what.empty()) { | |
507 if (!values.precompiled_header().empty()) { | |
508 if (display_headers) | |
509 OutputString("\nprecompiled_header\n"); | |
510 OutputString(values.precompiled_header() + "\n"); | |
511 } | |
512 found_match = true; | |
513 } | |
514 if (what == variables::kPrecompiledSource || what.empty()) { | |
515 if (!values.precompiled_source().is_null()) { | |
516 if (display_headers) | |
517 OutputString("\nprecompiled_source\n"); | |
518 OutputString(values.precompiled_source().value() + "\n"); | |
519 } | |
520 found_match = true; | |
521 } | |
522 return found_match; | |
523 } | 162 } |
524 | 163 |
525 bool PrintTarget(const Target* target, | 164 bool PrintTarget(const Target* target, |
526 const std::string& what, | 165 const std::string& what, |
527 bool display_target_header) { | 166 bool single_target, |
528 if (display_target_header) { | 167 bool all, |
529 OutputString("Target: ", DECORATION_YELLOW); | 168 bool tree, |
530 OutputString(target->label().GetUserVisibleName(false) + "\n"); | 169 bool blame) { |
531 OutputString("Type: ", DECORATION_YELLOW); | 170 std::unique_ptr<base::DictionaryValue> dict = |
532 OutputString(std::string( | 171 DescBuilder::DescriptionForTarget(target, what, all, tree, blame); |
533 Target::GetStringForOutputType(target->output_type())) + "\n"); | 172 if (!what.empty() && dict->empty()) { |
534 OutputString("Toolchain: ", DECORATION_YELLOW); | |
535 OutputString( | |
536 target->label().GetToolchainLabel().GetUserVisibleName(false) + "\n"); | |
537 } | |
538 | |
539 // Display headers when outputting everything. | |
540 bool display_headers = what.empty(); | |
541 bool is_binary_output = target->IsBinary(); | |
542 | |
543 bool found_match = false; | |
544 | |
545 // General target meta variables. | |
546 if (what.empty() || what == variables::kVisibility) { | |
547 PrintVisibility(target, display_headers); | |
548 found_match = true; | |
549 } | |
550 if (what.empty() || what == variables::kTestonly) { | |
551 PrintTestonly(target, display_headers); | |
552 found_match = true; | |
553 } | |
554 | |
555 // Binary target meta variables. | |
556 if (is_binary_output) { | |
557 if (what.empty() || what == variables::kCheckIncludes) { | |
558 PrintCheckIncludes(target, display_headers); | |
559 found_match = true; | |
560 } | |
561 if (what.empty() || what == variables::kAllowCircularIncludesFrom) { | |
562 PrintAllowCircularIncludesFrom(target, display_headers); | |
563 found_match = true; | |
564 } | |
565 } | |
566 | |
567 // Sources and inputs. | |
568 if (what.empty() || what == variables::kSources) { | |
569 PrintSources(target, display_headers); | |
570 found_match = true; | |
571 } | |
572 if (what.empty() || what == variables::kPublic) { | |
573 PrintPublic(target, display_headers); | |
574 found_match = true; | |
575 } | |
576 if (what.empty() || what == variables::kInputs) { | |
577 PrintInputs(target, display_headers); | |
578 found_match = true; | |
579 } | |
580 | |
581 // Configs. Configs set directly on a target are only relevant for binary | |
582 // targets | |
583 if (is_binary_output && (what.empty() || what == variables::kConfigs)) { | |
584 PrintConfigs(target, display_headers); | |
585 found_match = true; | |
586 } | |
587 | |
588 // Dependent/public configs can be applied to anything. | |
589 if (what.empty() || what == variables::kPublicConfigs) { | |
590 PrintPublicConfigs(target, display_headers); | |
591 found_match = true; | |
592 } | |
593 if (what.empty() || what == variables::kAllDependentConfigs) { | |
594 PrintAllDependentConfigs(target, display_headers); | |
595 found_match = true; | |
596 } | |
597 | |
598 // Action values. | |
599 if (target->output_type() == Target::ACTION || | |
600 target->output_type() == Target::ACTION_FOREACH) { | |
601 if (what.empty() || what == variables::kScript) { | |
602 PrintScript(target, display_headers); | |
603 found_match = true; | |
604 } | |
605 if (what.empty() || what == variables::kArgs) { | |
606 PrintArgs(target, display_headers); | |
607 found_match = true; | |
608 } | |
609 if (what.empty() || what == variables::kDepfile) { | |
610 PrintDepfile(target, display_headers); | |
611 found_match = true; | |
612 } | |
613 } | |
614 | |
615 // Outputs. | |
616 if (target->output_type() == Target::ACTION || | |
617 target->output_type() == Target::ACTION_FOREACH || | |
618 target->output_type() == Target::COPY_FILES || | |
619 target->output_type() == Target::CREATE_BUNDLE) { | |
620 if (what.empty() || what == variables::kOutputs) { | |
621 PrintOutputs(target, display_headers); | |
622 found_match = true; | |
623 } | |
624 } | |
625 | |
626 // Values from configs only apply to binary targets. | |
627 if (is_binary_output) { | |
628 #define CONFIG_VALUE_ARRAY_HANDLER(name, type) \ | |
629 if (what.empty() || what == #name) { \ | |
630 OutputRecursiveTargetConfig<type>( \ | |
631 target, display_headers ? #name : nullptr, &ConfigValues::name); \ | |
632 found_match = true; \ | |
633 } | |
634 | |
635 CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string) | |
636 CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string) | |
637 CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string) | |
638 CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string) | |
639 CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string) | |
640 CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string) | |
641 CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string) | |
642 CONFIG_VALUE_ARRAY_HANDLER(defines, std::string) | |
643 CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir) | |
644 CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string) | |
645 // Libs and lib_dirs are handled specially below. | |
646 | |
647 #undef CONFIG_VALUE_ARRAY_HANDLER | |
648 | |
649 found_match |= PrintPrecompiledHeaderInfo(target->config_values(), | |
650 what, display_headers); | |
651 } | |
652 | |
653 // Deps | |
654 if (what.empty() || what == "deps") { | |
655 PrintDeps(target, display_headers); | |
656 found_match = true; | |
657 } | |
658 | |
659 // Runtime deps are special, print only when explicitly asked for and not in | |
660 // overview mode. | |
661 if (what == "runtime_deps") { | |
662 PrintRuntimeDeps(target); | |
663 found_match = true; | |
664 } | |
665 | |
666 // Libs can be part of any target and get recursively pushed up the chain, | |
667 // so display them regardless of target type. | |
668 if (what.empty() || what == variables::kLibs) { | |
669 PrintLibs(target, display_headers); | |
670 found_match = true; | |
671 } | |
672 if (what.empty() || what == variables::kLibDirs) { | |
673 PrintLibDirs(target, display_headers); | |
674 found_match = true; | |
675 } | |
676 | |
677 if (!found_match) { | |
678 OutputString("Don't know how to display \"" + what + "\" for \"" + | 173 OutputString("Don't know how to display \"" + what + "\" for \"" + |
679 Target::GetStringForOutputType(target->output_type()) + "\".\n"); | 174 Target::GetStringForOutputType(target->output_type()) + |
175 "\".\n"); | |
680 return false; | 176 return false; |
681 } | 177 } |
178 // Print single value, without any headers | |
179 if (!what.empty() && dict->size() == 1 && single_target) { | |
180 base::DictionaryValue::Iterator iter(*dict); | |
181 PrintValue(&iter.value(), 0); | |
182 return true; | |
183 } | |
184 | |
185 OutputString("Target ", DECORATION_YELLOW); | |
186 OutputString(target->label().GetUserVisibleName(false)); | |
187 OutputString("\n"); | |
188 | |
189 std::unique_ptr<base::Value> v; | |
190 #define HANDLER(property, handler_name) \ | |
191 if (dict->Remove(property, &v)) { \ | |
192 handler_name(property, v.get()); \ | |
193 } | |
194 | |
195 // Entries with DefaultHandler are present to enforce order | |
196 HANDLER("type", TypeHandler); | |
197 HANDLER("toolchain", ToolchainHandler); | |
198 HANDLER(variables::kVisibility, VisibilityHandler); | |
199 HANDLER(variables::kTestonly, DefaultHandler); | |
200 HANDLER(variables::kCheckIncludes, DefaultHandler); | |
201 HANDLER(variables::kAllowCircularIncludesFrom, DefaultHandler); | |
202 HANDLER(variables::kSources, DefaultHandler); | |
203 HANDLER(variables::kPublic, PublicHandler); | |
204 HANDLER(variables::kInputs, DefaultHandler); | |
205 HANDLER(variables::kConfigs, ConfigsHandler); | |
206 HANDLER(variables::kPublicConfigs, ConfigsHandler); | |
207 HANDLER(variables::kAllDependentConfigs, ConfigsHandler); | |
208 HANDLER(variables::kScript, DefaultHandler); | |
209 HANDLER(variables::kArgs, DefaultHandler); | |
210 HANDLER(variables::kDepfile, DefaultHandler); | |
211 ProcessOutputs(dict.get()); | |
212 HANDLER("bundle_data", DefaultHandler); | |
213 HANDLER(variables::kArflags, DefaultHandler); | |
214 HANDLER(variables::kAsmflags, DefaultHandler); | |
215 HANDLER(variables::kCflags, DefaultHandler); | |
216 HANDLER(variables::kCflagsC, DefaultHandler); | |
217 HANDLER(variables::kCflagsCC, DefaultHandler); | |
218 HANDLER(variables::kCflagsObjC, DefaultHandler); | |
219 HANDLER(variables::kCflagsObjCC, DefaultHandler); | |
220 HANDLER(variables::kDefines, DefaultHandler); | |
221 HANDLER(variables::kIncludeDirs, DefaultHandler); | |
222 HANDLER(variables::kLdflags, DefaultHandler); | |
223 HANDLER(variables::kPrecompiledHeader, DefaultHandler); | |
224 HANDLER(variables::kPrecompiledSource, DefaultHandler); | |
225 HANDLER(variables::kDeps, DepsHandler); | |
226 HANDLER(variables::kLibs, DefaultHandler); | |
227 HANDLER(variables::kLibDirs, DefaultHandler); | |
228 | |
229 #undef HANDLER | |
230 | |
231 // Process the rest (if any) | |
232 base::DictionaryValue::Iterator iter(*dict); | |
233 while (!iter.IsAtEnd()) { | |
234 DefaultHandler(iter.key(), &iter.value()); | |
235 iter.Advance(); | |
236 } | |
237 | |
682 return true; | 238 return true; |
683 } | 239 } |
684 | 240 |
685 bool PrintConfig(const Config* config, | 241 bool PrintConfig(const Config* config, |
686 const std::string& what, | 242 const std::string& what, |
687 bool display_config_header) { | 243 bool single_config) { |
688 const ConfigValues& values = config->resolved_values(); | 244 std::unique_ptr<base::DictionaryValue> dict = |
689 | 245 DescBuilder::DescriptionForConfig(config, what); |
690 if (display_config_header) { | 246 if (!what.empty() && dict->empty()) { |
691 OutputString("Config: ", DECORATION_YELLOW); | |
692 OutputString(config->label().GetUserVisibleName(false) + "\n"); | |
693 OutputString("Toolchain: ", DECORATION_YELLOW); | |
694 OutputString( | |
695 config->label().GetToolchainLabel().GetUserVisibleName(false) + "\n"); | |
696 if (what.empty() && !config->configs().empty()) { | |
697 OutputString( | |
698 "(This is a composite config, the values below are after the\n" | |
699 "expansion of the child configs.)\n"); | |
700 } | |
701 } | |
702 | |
703 // Display headers when outputting everything. | |
704 bool display_headers = what.empty(); | |
705 | |
706 if (what.empty() || what == variables::kConfigs) | |
707 PrintConfigs(config, display_headers); | |
708 | |
709 #define CONFIG_VALUE_ARRAY_HANDLER(name, type) \ | |
710 if (what.empty() || what == #name) { \ | |
711 OutputConfigValueArray<type>(values, display_headers ? #name : nullptr, \ | |
712 &ConfigValues::name); \ | |
713 found_match = true; \ | |
714 } | |
715 | |
716 bool found_match = false; | |
717 | |
718 CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string) | |
719 CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string) | |
720 CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string) | |
721 CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string) | |
722 CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string) | |
723 CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string) | |
724 CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string) | |
725 CONFIG_VALUE_ARRAY_HANDLER(defines, std::string) | |
726 CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir) | |
727 CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string) | |
728 CONFIG_VALUE_ARRAY_HANDLER(lib_dirs, SourceDir) | |
729 CONFIG_VALUE_ARRAY_HANDLER(libs, LibFile) | |
730 | |
731 #undef CONFIG_VALUE_ARRAY_HANDLER | |
732 | |
733 // Handles all PCH-related variables. | |
734 found_match |= PrintPrecompiledHeaderInfo(config->resolved_values(), | |
735 what, display_headers); | |
736 | |
737 if (!found_match) { | |
738 OutputString("Don't know how to display \"" + what + "\" for a config.\n"); | 247 OutputString("Don't know how to display \"" + what + "\" for a config.\n"); |
739 return false; | 248 return false; |
740 } | 249 } |
250 // Print single value, without any headers | |
251 if (!what.empty() && dict->size() == 1 && single_config) { | |
252 base::DictionaryValue::Iterator iter(*dict); | |
253 PrintValue(&iter.value(), 0); | |
254 return true; | |
255 } | |
256 | |
257 OutputString("Config: ", DECORATION_YELLOW); | |
258 OutputString(config->label().GetUserVisibleName(false)); | |
259 OutputString("\n"); | |
260 | |
261 std::unique_ptr<base::Value> v; | |
262 #define HANDLER(property, handler_name) \ | |
263 if (dict->Remove(property, &v)) { \ | |
264 handler_name(property, v.get()); \ | |
265 } | |
266 | |
267 HANDLER("toolchain", ToolchainHandler); | |
268 if (!config->configs().empty()) { | |
269 OutputString( | |
270 "(This is a composite config, the values below are after the\n" | |
271 "expansion of the child configs.)\n"); | |
272 } | |
273 HANDLER(variables::kArflags, DefaultHandler); | |
274 HANDLER(variables::kAsmflags, DefaultHandler); | |
275 HANDLER(variables::kCflags, DefaultHandler); | |
276 HANDLER(variables::kCflagsC, DefaultHandler); | |
277 HANDLER(variables::kCflagsCC, DefaultHandler); | |
278 HANDLER(variables::kCflagsObjC, DefaultHandler); | |
279 HANDLER(variables::kCflagsObjCC, DefaultHandler); | |
280 HANDLER(variables::kDefines, DefaultHandler); | |
281 HANDLER(variables::kIncludeDirs, DefaultHandler); | |
282 HANDLER(variables::kLdflags, DefaultHandler); | |
283 HANDLER(variables::kLibs, DefaultHandler); | |
284 HANDLER(variables::kLibDirs, DefaultHandler); | |
285 HANDLER(variables::kPrecompiledHeader, DefaultHandler); | |
286 HANDLER(variables::kPrecompiledSource, DefaultHandler); | |
287 | |
288 #undef HANDLER | |
289 | |
741 return true; | 290 return true; |
742 } | 291 } |
743 | 292 |
744 } // namespace | 293 } // namespace |
745 | 294 |
746 // desc ------------------------------------------------------------------------ | 295 // desc ------------------------------------------------------------------------ |
747 | 296 |
748 const char kDesc[] = "desc"; | 297 const char kDesc[] = "desc"; |
749 const char kDesc_HelpShort[] = | 298 const char kDesc_HelpShort[] = |
750 "desc: Show lots of insightful information about a target or config."; | 299 "desc: Show lots of insightful information about a target or config."; |
751 const char kDesc_Help[] = | 300 const char kDesc_Help[] = |
752 "gn desc <out_dir> <label or pattern> [<what to show>] [--blame]\n" | 301 "gn desc <out_dir> <label or pattern> [<what to show>] [--blame] " |
302 "[--format=json]\n" | |
753 "\n" | 303 "\n" |
754 " Displays information about a given target or config. The build\n" | 304 " Displays information about a given target or config. The build\n" |
755 " build parameters will be taken for the build in the given <out_dir>.\n" | 305 " build parameters will be taken for the build in the given <out_dir>.\n" |
756 "\n" | 306 "\n" |
757 " The <label or pattern> can be a target label, a config label, or a\n" | 307 " The <label or pattern> can be a target label, a config label, or a\n" |
758 " label pattern (see \"gn help label_pattern\"). A label pattern will\n" | 308 " label pattern (see \"gn help label_pattern\"). A label pattern will\n" |
759 " only match targets.\n" | 309 " only match targets.\n" |
760 "\n" | 310 "\n" |
761 "Possibilities for <what to show>\n" | 311 "Possibilities for <what to show>\n" |
762 "\n" | 312 "\n" |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
812 "\n" | 362 "\n" |
813 "Configs\n" | 363 "Configs\n" |
814 "\n" | 364 "\n" |
815 " The \"configs\" section will list all configs that apply. For targets\n" | 365 " The \"configs\" section will list all configs that apply. For targets\n" |
816 " this will include configs specified in the \"configs\" variable of\n" | 366 " this will include configs specified in the \"configs\" variable of\n" |
817 " the target, and also configs pushed onto this target via public\n" | 367 " the target, and also configs pushed onto this target via public\n" |
818 " or \"all dependent\" configs.\n" | 368 " or \"all dependent\" configs.\n" |
819 "\n" | 369 "\n" |
820 " Configs can have child configs. Specifying --tree will show the\n" | 370 " Configs can have child configs. Specifying --tree will show the\n" |
821 " hierarchy.\n" | 371 " hierarchy.\n" |
822 "\n" | 372 "\n" |
823 "Printing deps\n" | 373 "Printing deps\n" |
824 "\n" | 374 "\n" |
825 " Deps will include all public, private, and data deps (TODO this could\n" | 375 " Deps will include all public, private, and data deps (TODO this could\n" |
826 " be clarified and enhanced) sorted in order applying. The following\n" | 376 " be clarified and enhanced) sorted in order applying. The following\n" |
827 " may be used:\n" | 377 " may be used:\n" |
828 "\n" | 378 "\n" |
829 " --all\n" | 379 " --all\n" |
830 " Collects all recursive dependencies and prints a sorted flat list.\n" | 380 " Collects all recursive dependencies and prints a sorted flat list.\n" |
831 " Also usable with --tree (see below).\n" | 381 " Also usable with --tree (see below).\n" |
832 "\n" | 382 "\n" |
833 TARGET_PRINTING_MODE_COMMAND_LINE_HELP | 383 TARGET_PRINTING_MODE_COMMAND_LINE_HELP |
834 "\n" | 384 "\n" |
835 TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP | 385 TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP |
836 "\n" | 386 "\n" |
837 " --tree\n" | 387 " --tree\n" |
838 " Print a dependency tree. By default, duplicates will be elided\n" | 388 " Print a dependency tree. By default, duplicates will be elided\n" |
839 " with \"...\" but when --all and -tree are used together, no\n" | 389 " with \"...\" but when --all and -tree are used together, no\n" |
840 " eliding will be performed.\n" | 390 " eliding will be performed.\n" |
841 "\n" | 391 "\n" |
842 " The \"deps\", \"public_deps\", and \"data_deps\" will all be\n" | 392 " The \"deps\", \"public_deps\", and \"data_deps\" will all be\n" |
843 " included in the tree.\n" | 393 " included in the tree.\n" |
844 "\n" | 394 "\n" |
845 " Tree output can not be used with the filtering or output flags:\n" | 395 " Tree output can not be used with the filtering or output flags:\n" |
846 " --as, --type, --testonly.\n" | 396 " --as, --type, --testonly.\n" |
847 "\n" | 397 "\n" |
848 TARGET_TYPE_FILTER_COMMAND_LINE_HELP | 398 TARGET_TYPE_FILTER_COMMAND_LINE_HELP |
849 "\n" | 399 "\n" |
400 "Format\n" | |
401 "\n" | |
402 " Optional --format=json flag can be specified to switch output to JSON.\n" | |
403 "\n" | |
850 "Note\n" | 404 "Note\n" |
851 "\n" | 405 "\n" |
852 " This command will show the full name of directories and source files,\n" | 406 " This command will show the full name of directories and source files,\n" |
853 " but when directories and source paths are written to the build file,\n" | 407 " but when directories and source paths are written to the build file,\n" |
854 " they will be adjusted to be relative to the build directory. So the\n" | 408 " they will be adjusted to be relative to the build directory. So the\n" |
855 " values for paths displayed by this command won't match (but should\n" | 409 " values for paths displayed by this command won't match (but should\n" |
856 " mean the same thing).\n" | 410 " mean the same thing).\n" |
857 "\n" | 411 "\n" |
858 "Examples\n" | 412 "Examples\n" |
859 "\n" | 413 "\n" |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
896 | 450 |
897 if (!ResolveFromCommandLineInput( | 451 if (!ResolveFromCommandLineInput( |
898 setup, target_list, cmdline->HasSwitch(switches::kAllToolchains), | 452 setup, target_list, cmdline->HasSwitch(switches::kAllToolchains), |
899 &target_matches, &config_matches, &toolchain_matches, &file_matches)) | 453 &target_matches, &config_matches, &toolchain_matches, &file_matches)) |
900 return 1; | 454 return 1; |
901 | 455 |
902 std::string what_to_print; | 456 std::string what_to_print; |
903 if (args.size() == 3) | 457 if (args.size() == 3) |
904 what_to_print = args[2]; | 458 what_to_print = args[2]; |
905 | 459 |
906 bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1; | 460 bool json = cmdline->GetSwitchValueASCII("format") == "json"; |
907 | 461 |
908 // Display headers for each target when printing all values, or when printing | 462 if (json) { |
909 // multiple targets or configs. | 463 // Convert all targets/configs to JSON, serialize and print them |
910 bool display_item_header = multiple_outputs || what_to_print.empty(); | 464 auto res = base::WrapUnique(new base::DictionaryValue()); |
465 if (!target_matches.empty()) { | |
466 for (const auto* target : target_matches) { | |
467 res->Set(target->label().GetUserVisibleName( | |
468 target->settings()->default_toolchain_label()), | |
469 DescBuilder::DescriptionForTarget( | |
470 target, what_to_print, cmdline->HasSwitch(kAll), | |
471 cmdline->HasSwitch(kTree), cmdline->HasSwitch(kBlame))); | |
472 } | |
473 } else if (!config_matches.empty()) { | |
474 for (const auto* config : config_matches) { | |
475 res->Set(config->label().GetUserVisibleName(false), | |
476 DescBuilder::DescriptionForConfig(config, what_to_print)); | |
477 } | |
478 } | |
479 std::string s; | |
480 base::JSONWriter::WriteWithOptions( | |
481 *res.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s); | |
482 OutputString(s); | |
483 } else { | |
484 // Regular (non-json) formatted output | |
485 bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1; | |
911 | 486 |
912 bool printed_output = false; | 487 bool printed_output = false; |
913 for (const Target* target : target_matches) { | 488 for (const Target* target : target_matches) { |
914 if (printed_output) | 489 if (printed_output) |
915 OutputString("\n\n"); | 490 OutputString("\n\n"); |
916 printed_output = true; | 491 printed_output = true; |
917 | 492 |
918 if (!PrintTarget(target, what_to_print, display_item_header)) | 493 if (!PrintTarget(target, what_to_print, !multiple_outputs, |
919 return 1; | 494 cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree), |
920 } | 495 cmdline->HasSwitch(kBlame))) |
921 for (const Config* config : config_matches) { | 496 return 1; |
922 if (printed_output) | 497 } |
923 OutputString("\n\n"); | 498 for (const Config* config : config_matches) { |
924 printed_output = true; | 499 if (printed_output) |
500 OutputString("\n\n"); | |
501 printed_output = true; | |
925 | 502 |
926 if (!PrintConfig(config, what_to_print, display_item_header)) | 503 if (!PrintConfig(config, what_to_print, !multiple_outputs)) |
927 return 1; | 504 return 1; |
505 } | |
928 } | 506 } |
929 | 507 |
930 return 0; | 508 return 0; |
931 } | 509 } |
932 | 510 |
933 } // namespace commands | 511 } // namespace commands |
OLD | NEW |