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
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <set>
7#include <sstream>
8
9#include "base/command_line.h"
10#include "tools/gn/commands.h"
11#include "tools/gn/config.h"
12#include "tools/gn/config_values_extractors.h"
13#include "tools/gn/deps_iterator.h"
14#include "tools/gn/filesystem_utils.h"
15#include "tools/gn/item.h"
16#include "tools/gn/label.h"
17#include "tools/gn/setup.h"
18#include "tools/gn/standard_out.h"
19#include "tools/gn/substitution_writer.h"
20#include "tools/gn/target.h"
21#include "tools/gn/variables.h"
22
23namespace commands {
24
25namespace {
26
27// Prints the given directory in a nice way for the user to view.
28std::string FormatSourceDir(const SourceDir& dir) {
29#if defined(OS_WIN)
30  // On Windows we fix up system absolute paths to look like native ones.
31  // Internally, they'll look like "/C:\foo\bar/"
32  if (dir.is_system_absolute()) {
33    std::string buf = dir.value();
34    if (buf.size() > 3 && buf[2] == ':') {
35      buf.erase(buf.begin());  // Erase beginning slash.
36      return buf;
37    }
38  }
39#endif
40  return dir.value();
41}
42
43void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result);
44
45void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
46  if (result->find(target->label()) != result->end())
47    return;  // Already did this target.
48  result->insert(target->label());
49
50  RecursiveCollectChildDeps(target, result);
51}
52
53void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result) {
54  for (DepsIterator iter(target); !iter.done(); iter.Advance())
55    RecursiveCollectDeps(iter.target(), result);
56}
57
58// Prints dependencies of the given target (not the target itself). If the
59// set is non-null, new targets encountered will be added to the set, and if
60// a dependency is in the set already, it will not be recused into. When the
61// set is null, all dependencies will be printed.
62void RecursivePrintDeps(const Target* target,
63                        const Label& default_toolchain,
64                        std::set<const Target*>* seen_targets,
65                        int indent_level) {
66  // Combine all deps into one sorted list.
67  std::vector<LabelTargetPair> sorted_deps;
68  for (DepsIterator iter(target); !iter.done(); iter.Advance())
69    sorted_deps.push_back(iter.pair());
70  std::sort(sorted_deps.begin(), sorted_deps.end(),
71            LabelPtrLabelLess<Target>());
72
73  std::string indent(indent_level * 2, ' ');
74  for (size_t i = 0; i < sorted_deps.size(); i++) {
75    const Target* cur_dep = sorted_deps[i].ptr;
76
77    OutputString(indent +
78        cur_dep->label().GetUserVisibleName(default_toolchain));
79    bool print_children = true;
80    if (seen_targets) {
81      if (seen_targets->find(cur_dep) == seen_targets->end()) {
82        // New target, mark it visited.
83        seen_targets->insert(cur_dep);
84      } else {
85        // Already seen.
86        print_children = false;
87        // Only print "..." if something is actually elided, which means that
88        // the current target has children.
89        if (!cur_dep->public_deps().empty() ||
90            !cur_dep->private_deps().empty() ||
91            !cur_dep->data_deps().empty())
92          OutputString("...");
93      }
94    }
95
96    OutputString("\n");
97    if (print_children) {
98      RecursivePrintDeps(cur_dep, default_toolchain, seen_targets,
99                         indent_level + 1);
100    }
101  }
102}
103
104void PrintDeps(const Target* target, bool display_header) {
105  const CommandLine* cmdline = CommandLine::ForCurrentProcess();
106  Label toolchain_label = target->label().GetToolchainLabel();
107
108  // Tree mode is separate.
109  if (cmdline->HasSwitch("tree")) {
110    if (display_header)
111      OutputString("\nDependency tree:\n");
112
113    if (cmdline->HasSwitch("all")) {
114      // Show all tree deps with no eliding.
115      RecursivePrintDeps(target, toolchain_label, NULL, 1);
116    } else {
117      // Don't recurse into duplicates.
118      std::set<const Target*> seen_targets;
119      RecursivePrintDeps(target, toolchain_label, &seen_targets, 1);
120    }
121    return;
122  }
123
124  // Collect the deps to display.
125  std::vector<Label> deps;
126  if (cmdline->HasSwitch("all")) {
127    // Show all dependencies.
128    if (display_header)
129      OutputString("\nAll recursive dependencies:\n");
130
131    std::set<Label> all_deps;
132    RecursiveCollectChildDeps(target, &all_deps);
133    for (std::set<Label>::iterator i = all_deps.begin();
134         i != all_deps.end(); ++i)
135      deps.push_back(*i);
136  } else {
137    // Show direct dependencies only.
138    if (display_header) {
139      OutputString(
140          "\nDirect dependencies "
141          "(try also \"--all\", \"--tree\", or even \"--all --tree\"):\n");
142    }
143    for (DepsIterator iter(target); !iter.done(); iter.Advance())
144      deps.push_back(iter.label());
145  }
146
147  std::sort(deps.begin(), deps.end());
148  for (size_t i = 0; i < deps.size(); i++)
149    OutputString("  " + deps[i].GetUserVisibleName(toolchain_label) + "\n");
150}
151
152void PrintForwardDependentConfigsFrom(const Target* target,
153                                      bool display_header) {
154  if (target->forward_dependent_configs().empty())
155    return;
156
157  if (display_header)
158    OutputString("\nforward_dependent_configs_from:\n");
159
160  // Collect the sorted list of deps.
161  std::vector<Label> forward;
162  for (size_t i = 0; i < target->forward_dependent_configs().size(); i++)
163    forward.push_back(target->forward_dependent_configs()[i].label);
164  std::sort(forward.begin(), forward.end());
165
166  Label toolchain_label = target->label().GetToolchainLabel();
167  for (size_t i = 0; i < forward.size(); i++)
168    OutputString("  " + forward[i].GetUserVisibleName(toolchain_label) + "\n");
169}
170
171// libs and lib_dirs are special in that they're inherited. We don't currently
172// implement a blame feature for this since the bottom-up inheritance makes
173// this difficult.
174void PrintLibDirs(const Target* target, bool display_header) {
175  const OrderedSet<SourceDir>& lib_dirs = target->all_lib_dirs();
176  if (lib_dirs.empty())
177    return;
178
179  if (display_header)
180    OutputString("\nlib_dirs\n");
181
182  for (size_t i = 0; i < lib_dirs.size(); i++)
183    OutputString("    " + FormatSourceDir(lib_dirs[i]) + "\n");
184}
185
186void PrintLibs(const Target* target, bool display_header) {
187  const OrderedSet<std::string>& libs = target->all_libs();
188  if (libs.empty())
189    return;
190
191  if (display_header)
192    OutputString("\nlibs\n");
193
194  for (size_t i = 0; i < libs.size(); i++)
195    OutputString("    " + libs[i] + "\n");
196}
197
198void PrintPublic(const Target* target, bool display_header) {
199  if (display_header)
200    OutputString("\npublic:\n");
201
202  if (target->all_headers_public()) {
203    OutputString("  [All headers listed in the sources are public.]\n");
204    return;
205  }
206
207  Target::FileList public_headers = target->public_headers();
208  std::sort(public_headers.begin(), public_headers.end());
209  for (size_t i = 0; i < public_headers.size(); i++)
210    OutputString("  " + public_headers[i].value() + "\n");
211}
212
213void PrintCheckIncludes(const Target* target, bool display_header) {
214  if (display_header)
215    OutputString("\ncheck_includes:\n");
216
217  if (target->check_includes())
218    OutputString("  true\n");
219  else
220    OutputString("  false\n");
221}
222
223void PrintAllowCircularIncludesFrom(const Target* target, bool display_header) {
224  if (display_header)
225    OutputString("\nallow_circular_includes_from:\n");
226
227  Label toolchain_label = target->label().GetToolchainLabel();
228  const std::set<Label>& allow = target->allow_circular_includes_from();
229  for (std::set<Label>::const_iterator iter = allow.begin();
230       iter != allow.end(); ++iter)
231    OutputString("  " + iter->GetUserVisibleName(toolchain_label) + "\n");
232}
233
234void PrintVisibility(const Target* target, bool display_header) {
235  if (display_header)
236    OutputString("\nvisibility:\n");
237
238  OutputString(target->visibility().Describe(2, false));
239}
240
241void PrintTestonly(const Target* target, bool display_header) {
242  if (display_header)
243    OutputString("\ntestonly:\n");
244
245  if (target->testonly())
246    OutputString("  true\n");
247  else
248    OutputString("  false\n");
249}
250
251void PrintConfigsVector(const Target* target,
252                        const LabelConfigVector& configs,
253                        const std::string& heading,
254                        bool display_header) {
255  if (configs.empty())
256    return;
257
258  // Don't sort since the order determines how things are processed.
259  if (display_header)
260    OutputString("\n" + heading + " (in order applying):\n");
261
262  Label toolchain_label = target->label().GetToolchainLabel();
263  for (size_t i = 0; i < configs.size(); i++) {
264    OutputString("  " +
265        configs[i].label.GetUserVisibleName(toolchain_label) + "\n");
266  }
267}
268
269void PrintConfigsVector(const Target* target,
270                        const UniqueVector<LabelConfigPair>& configs,
271                        const std::string& heading,
272                        bool display_header) {
273  if (configs.empty())
274    return;
275
276  // Don't sort since the order determines how things are processed.
277  if (display_header)
278    OutputString("\n" + heading + " (in order applying):\n");
279
280  Label toolchain_label = target->label().GetToolchainLabel();
281  for (size_t i = 0; i < configs.size(); i++) {
282    OutputString("  " +
283        configs[i].label.GetUserVisibleName(toolchain_label) + "\n");
284  }
285}
286
287void PrintConfigs(const Target* target, bool display_header) {
288  PrintConfigsVector(target, target->configs().vector(), "configs",
289                     display_header);
290}
291
292void PrintPublicConfigs(const Target* target, bool display_header) {
293  PrintConfigsVector(target, target->public_configs(),
294                     "public_configs", display_header);
295}
296
297void PrintAllDependentConfigs(const Target* target, bool display_header) {
298  PrintConfigsVector(target, target->all_dependent_configs(),
299                     "all_dependent_configs", display_header);
300}
301
302void PrintFileList(const Target::FileList& files,
303                   const std::string& header,
304                   bool indent_extra,
305                   bool display_header) {
306  if (files.empty())
307    return;
308
309  if (display_header)
310    OutputString("\n" + header + ":\n");
311
312  std::string indent = indent_extra ? "    " : "  ";
313
314  Target::FileList sorted = files;
315  std::sort(sorted.begin(), sorted.end());
316  for (size_t i = 0; i < sorted.size(); i++)
317    OutputString(indent + sorted[i].value() + "\n");
318}
319
320void PrintSources(const Target* target, bool display_header) {
321  PrintFileList(target->sources(), "sources", false, display_header);
322}
323
324void PrintInputs(const Target* target, bool display_header) {
325  PrintFileList(target->inputs(), "inputs", false, display_header);
326}
327
328void PrintOutputs(const Target* target, bool display_header) {
329  if (display_header)
330    OutputString("\noutputs:\n");
331
332  if (target->output_type() == Target::ACTION) {
333    // Action, print out outputs, don't apply sources to it.
334    for (size_t i = 0; i < target->action_values().outputs().list().size();
335         i++) {
336      OutputString("  " +
337                   target->action_values().outputs().list()[i].AsString() +
338                   "\n");
339    }
340  } else {
341    const SubstitutionList& outputs = target->action_values().outputs();
342    if (!outputs.required_types().empty()) {
343      // Display the pattern and resolved pattern separately, since there are
344      // subtitutions used.
345      OutputString("  Output pattern:\n");
346      for (size_t i = 0; i < outputs.list().size(); i++)
347        OutputString("    " + outputs.list()[i].AsString() + "\n");
348
349      // Now display what that resolves to given the sources.
350      OutputString("\n  Resolved output file list:\n");
351    }
352
353    // Resolved output list.
354    std::vector<SourceFile> output_files;
355    SubstitutionWriter::ApplyListToSources(target->settings(), outputs,
356                                           target->sources(), &output_files);
357    PrintFileList(output_files, "", true, false);
358  }
359}
360
361void PrintScript(const Target* target, bool display_header) {
362  if (display_header)
363    OutputString("\nscript:\n");
364  OutputString("  " + target->action_values().script().value() + "\n");
365}
366
367void PrintArgs(const Target* target, bool display_header) {
368  if (display_header)
369    OutputString("\nargs:\n");
370  for (size_t i = 0; i < target->action_values().args().list().size(); i++) {
371    OutputString("  " +
372                 target->action_values().args().list()[i].AsString() + "\n");
373  }
374}
375
376void 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.
386void 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.
395template<typename T> struct DescValueWriter {};
396template<> struct DescValueWriter<std::string> {
397  void operator()(const std::string& str, std::ostream& out) const {
398    out << "    " << str << "\n";
399  }
400};
401template<> struct DescValueWriter<SourceDir> {
402  void operator()(const SourceDir& dir, std::ostream& out) const {
403    out << "    " << FormatSourceDir(dir) << "\n";
404  }
405};
406
407// Writes a given config value type to the string, optionally with attribution.
408// This should match RecursiveTargetConfigToStream in the order it traverses.
409template<typename T> void OutputRecursiveTargetConfig(
410    const Target* target,
411    const char* header_name,
412    const std::vector<T>& (ConfigValues::* getter)() const) {
413  bool display_blame = CommandLine::ForCurrentProcess()->HasSwitch("blame");
414
415  DescValueWriter<T> writer;
416  std::ostringstream out;
417
418  for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
419    if ((iter.cur().*getter)().empty())
420      continue;
421
422    // Optional blame sub-head.
423    if (display_blame) {
424      const Config* config = iter.GetCurrentConfig();
425      if (config) {
426        // Source of this value is a config.
427        out << "  From " << config->label().GetUserVisibleName(false) << "\n";
428        OutputSourceOfDep(iter.origin(), out);
429      } else {
430        // Source of this value is the target itself.
431        out << "  From " << target->label().GetUserVisibleName(false) << "\n";
432      }
433    }
434
435    // Actual values.
436    ConfigValuesToStream(iter.cur(), getter, writer, out);
437  }
438
439  std::string out_str = out.str();
440  if (!out_str.empty()) {
441    OutputString("\n" + std::string(header_name) + "\n");
442    OutputString(out_str);
443  }
444}
445
446}  // namespace
447
448// desc ------------------------------------------------------------------------
449
450const char kDesc[] = "desc";
451const char kDesc_HelpShort[] =
452    "desc: Show lots of insightful information about a target.";
453const char kDesc_Help[] =
454    "gn desc <out_dir> <target label> [<what to show>]\n"
455    "        [--blame] [--all | --tree]\n"
456    "\n"
457    "  Displays information about a given labeled target for the given build.\n"
458    "  The build parameters will be taken for the build in the given\n"
459    "  <out_dir>.\n"
460    "\n"
461    "Possibilities for <what to show>:\n"
462    "  (If unspecified an overall summary will be displayed.)\n"
463    "\n"
464    "  sources\n"
465    "      Source files.\n"
466    "\n"
467    "  inputs\n"
468    "      Additional input dependencies.\n"
469    "\n"
470    "  public\n"
471    "      Public header files.\n"
472    "\n"
473    "  check_includes\n"
474    "      Whether \"gn check\" checks this target for include usage.\n"
475    "\n"
476    "  allow_circular_includes_from\n"
477    "      Permit includes from these targets.\n"
478    "\n"
479    "  visibility\n"
480    "      Prints which targets can depend on this one.\n"
481    "\n"
482    "  testonly\n"
483    "      Whether this target may only be used in tests.\n"
484    "\n"
485    "  configs\n"
486    "      Shows configs applied to the given target, sorted in the order\n"
487    "      they're specified. This includes both configs specified in the\n"
488    "      \"configs\" variable, as well as configs pushed onto this target\n"
489    "      via dependencies specifying \"all\" or \"direct\" dependent\n"
490    "      configs.\n"
491    "\n"
492    "  deps [--all | --tree]\n"
493    "      Show immediate (or, when \"--all\" or \"--tree\" is specified,\n"
494    "      recursive) dependencies of the given target. \"--tree\" shows them\n"
495    "      in a tree format with duplicates elided (noted by \"...\").\n"
496    "      \"--all\" shows them sorted alphabetically. Using both flags will\n"
497    "      print a tree with no omissions. The \"deps\", \"public_deps\", and\n"
498    "      \"data_deps\" will all be included.\n"
499    "\n"
500    "  public_configs\n"
501    "  all_dependent_configs\n"
502    "      Shows the labels of configs applied to targets that depend on this\n"
503    "      one (either directly or all of them).\n"
504    "\n"
505    "  forward_dependent_configs_from\n"
506    "      Shows the labels of dependencies for which dependent configs will\n"
507    "      be pushed to targets depending on the current one.\n"
508    "\n"
509    "  script\n"
510    "  args\n"
511    "  depfile\n"
512    "      Actions only. The script and related values.\n"
513    "\n"
514    "  outputs\n"
515    "      Outputs for script and copy target types.\n"
516    "\n"
517    "  defines       [--blame]\n"
518    "  include_dirs  [--blame]\n"
519    "  cflags        [--blame]\n"
520    "  cflags_cc     [--blame]\n"
521    "  cflags_cxx    [--blame]\n"
522    "  ldflags       [--blame]\n"
523    "  lib_dirs\n"
524    "  libs\n"
525    "      Shows the given values taken from the target and all configs\n"
526    "      applying. See \"--blame\" below.\n"
527    "\n"
528    "  --blame\n"
529    "      Used with any value specified by a config, this will name\n"
530    "      the config that specified the value. This doesn't currently work\n"
531    "      for libs and lib_dirs because those are inherited and are more\n"
532    "      complicated to figure out the blame (patches welcome).\n"
533    "\n"
534    "Note:\n"
535    "  This command will show the full name of directories and source files,\n"
536    "  but when directories and source paths are written to the build file,\n"
537    "  they will be adjusted to be relative to the build directory. So the\n"
538    "  values for paths displayed by this command won't match (but should\n"
539    "  mean the same thing).\n"
540    "\n"
541    "Examples:\n"
542    "  gn desc out/Debug //base:base\n"
543    "      Summarizes the given target.\n"
544    "\n"
545    "  gn desc out/Foo :base_unittests deps --tree\n"
546    "      Shows a dependency tree of the \"base_unittests\" project in\n"
547    "      the current directory.\n"
548    "\n"
549    "  gn desc out/Debug //base defines --blame\n"
550    "      Shows defines set for the //base:base target, annotated by where\n"
551    "      each one was set from.\n";
552
553#define OUTPUT_CONFIG_VALUE(name, type) \
554    OutputRecursiveTargetConfig<type>(target, #name, &ConfigValues::name);
555
556int RunDesc(const std::vector<std::string>& args) {
557  if (args.size() != 2 && args.size() != 3) {
558    Err(Location(), "You're holding it wrong.",
559        "Usage: \"gn desc <out_dir> <target_name> [<what to display>]\"")
560        .PrintToStdout();
561    return 1;
562  }
563
564  // Deliberately leaked to avoid expensive process teardown.
565  Setup* setup = new Setup;
566  if (!setup->DoSetup(args[0], false))
567    return 1;
568  if (!setup->Run())
569    return 1;
570
571  const Target* target = ResolveTargetFromCommandLineString(setup, args[1]);
572  if (!target)
573    return 1;
574
575#define CONFIG_VALUE_HANDLER(name, type) \
576    } else if (what == #name) { OUTPUT_CONFIG_VALUE(name, type)
577
578  if (args.size() == 3) {
579    // User specified one thing to display.
580    const std::string& what = args[2];
581    if (what == variables::kConfigs) {
582      PrintConfigs(target, false);
583    } else if (what == variables::kPublicConfigs) {
584      PrintPublicConfigs(target, false);
585    } else if (what == variables::kAllDependentConfigs) {
586      PrintAllDependentConfigs(target, false);
587    } else if (what == variables::kForwardDependentConfigsFrom) {
588      PrintForwardDependentConfigsFrom(target, false);
589    } else if (what == variables::kSources) {
590      PrintSources(target, false);
591    } else if (what == variables::kPublic) {
592      PrintPublic(target, false);
593    } else if (what == variables::kCheckIncludes) {
594      PrintCheckIncludes(target, false);
595    } else if (what == variables::kAllowCircularIncludesFrom) {
596      PrintAllowCircularIncludesFrom(target, false);
597    } else if (what == variables::kVisibility) {
598      PrintVisibility(target, false);
599    } else if (what == variables::kTestonly) {
600      PrintTestonly(target, false);
601    } else if (what == variables::kInputs) {
602      PrintInputs(target, false);
603    } else if (what == variables::kScript) {
604      PrintScript(target, false);
605    } else if (what == variables::kArgs) {
606      PrintArgs(target, false);
607    } else if (what == variables::kDepfile) {
608      PrintDepfile(target, false);
609    } else if (what == variables::kOutputs) {
610      PrintOutputs(target, false);
611    } else if (what == variables::kDeps) {
612      PrintDeps(target, false);
613    } else if (what == variables::kLibDirs) {
614      PrintLibDirs(target, false);
615    } else if (what == variables::kLibs) {
616      PrintLibs(target, false);
617
618    CONFIG_VALUE_HANDLER(defines, std::string)
619    CONFIG_VALUE_HANDLER(include_dirs, SourceDir)
620    CONFIG_VALUE_HANDLER(cflags, std::string)
621    CONFIG_VALUE_HANDLER(cflags_c, std::string)
622    CONFIG_VALUE_HANDLER(cflags_cc, std::string)
623    CONFIG_VALUE_HANDLER(cflags_objc, std::string)
624    CONFIG_VALUE_HANDLER(cflags_objcc, std::string)
625    CONFIG_VALUE_HANDLER(ldflags, std::string)
626
627    } else {
628      OutputString("Don't know how to display \"" + what + "\".\n");
629      return 1;
630    }
631
632#undef CONFIG_VALUE_HANDLER
633    return 0;
634  }
635
636  // Display summary.
637
638  // Display this only applicable to binary targets.
639  bool is_binary_output =
640    target->output_type() != Target::GROUP &&
641    target->output_type() != Target::COPY_FILES &&
642    target->output_type() != Target::ACTION &&
643    target->output_type() != Target::ACTION_FOREACH;
644
645  // Generally we only want to display toolchains on labels when the toolchain
646  // is different than the default one for this target (which we always print
647  // in the header).
648  Label target_toolchain = target->label().GetToolchainLabel();
649
650  // Header.
651  OutputString("Target: ", DECORATION_YELLOW);
652  OutputString(target->label().GetUserVisibleName(false) + "\n");
653  OutputString("Type: ", DECORATION_YELLOW);
654  OutputString(std::string(
655      Target::GetStringForOutputType(target->output_type())) + "\n");
656  OutputString("Toolchain: ", DECORATION_YELLOW);
657  OutputString(target_toolchain.GetUserVisibleName(false) + "\n");
658
659  PrintSources(target, true);
660  if (is_binary_output) {
661    PrintPublic(target, true);
662    PrintCheckIncludes(target, true);
663    PrintAllowCircularIncludesFrom(target, true);
664  }
665  PrintVisibility(target, true);
666  if (is_binary_output) {
667    PrintTestonly(target, true);
668    PrintConfigs(target, true);
669  }
670
671  PrintPublicConfigs(target, true);
672  PrintAllDependentConfigs(target, true);
673  PrintForwardDependentConfigsFrom(target, true);
674
675  PrintInputs(target, true);
676
677  if (is_binary_output) {
678    OUTPUT_CONFIG_VALUE(defines, std::string)
679    OUTPUT_CONFIG_VALUE(include_dirs, SourceDir)
680    OUTPUT_CONFIG_VALUE(cflags, std::string)
681    OUTPUT_CONFIG_VALUE(cflags_c, std::string)
682    OUTPUT_CONFIG_VALUE(cflags_cc, std::string)
683    OUTPUT_CONFIG_VALUE(cflags_objc, std::string)
684    OUTPUT_CONFIG_VALUE(cflags_objcc, std::string)
685    OUTPUT_CONFIG_VALUE(ldflags, std::string)
686  }
687
688  if (target->output_type() == Target::ACTION ||
689      target->output_type() == Target::ACTION_FOREACH) {
690    PrintScript(target, true);
691    PrintArgs(target, true);
692    PrintDepfile(target, true);
693  }
694
695  if (target->output_type() == Target::ACTION ||
696      target->output_type() == Target::ACTION_FOREACH ||
697      target->output_type() == Target::COPY_FILES) {
698    PrintOutputs(target, true);
699  }
700
701  // Libs can be part of any target and get recursively pushed up the chain,
702  // so always display them, even for groups and such.
703  PrintLibs(target, true);
704  PrintLibDirs(target, true);
705
706  PrintDeps(target, true);
707
708  return 0;
709}
710
711}  // namespace commands
712