Index: tools/gn/command_desc.cc |
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc |
index ea1bab3d88b5bc4886a2366a48fc9c68902cf2a3..72525e97fd05304987f23e9e5280efb2f74dc2d0 100644 |
--- a/tools/gn/command_desc.cc |
+++ b/tools/gn/command_desc.cc |
@@ -9,18 +9,14 @@ |
#include <sstream> |
#include "base/command_line.h" |
-#include "build/build_config.h" |
+#include "base/json/json_writer.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/string_util.h" |
#include "tools/gn/commands.h" |
#include "tools/gn/config.h" |
-#include "tools/gn/config_values_extractors.h" |
-#include "tools/gn/deps_iterator.h" |
-#include "tools/gn/filesystem_utils.h" |
-#include "tools/gn/item.h" |
-#include "tools/gn/label.h" |
-#include "tools/gn/runtime_deps.h" |
+#include "tools/gn/desc_builder.h" |
#include "tools/gn/setup.h" |
#include "tools/gn/standard_out.h" |
-#include "tools/gn/substitution_writer.h" |
#include "tools/gn/switches.h" |
#include "tools/gn/target.h" |
#include "tools/gn/variables.h" |
@@ -32,725 +28,258 @@ namespace { |
// Desc-specific command line switches. |
const char kBlame[] = "blame"; |
const char kTree[] = "tree"; |
- |
-// Prints the given directory in a nice way for the user to view. |
-std::string FormatSourceDir(const SourceDir& dir) { |
-#if defined(OS_WIN) |
- // On Windows we fix up system absolute paths to look like native ones. |
- // Internally, they'll look like "/C:\foo\bar/" |
- if (dir.is_system_absolute()) { |
- std::string buf = dir.value(); |
- if (buf.size() > 3 && buf[2] == ':') { |
- buf.erase(buf.begin()); // Erase beginning slash. |
- return buf; |
+const char kAll[] = "all"; |
+ |
+// Prints value with specified indentation level |
+void PrintValue(const base::Value* value, int indentLevel) { |
+ std::string indent(indentLevel * 2, ' '); |
+ const base::ListValue* list_value = nullptr; |
+ const base::DictionaryValue* dict_value = nullptr; |
+ std::string string_value; |
+ bool bool_value = false; |
+ if (value->GetAsList(&list_value)) { |
+ for (const auto& v : *list_value) { |
+ PrintValue(v.get(), indentLevel); |
+ } |
+ } else if (value->GetAsString(&string_value)) { |
+ OutputString(indent); |
+ OutputString(string_value); |
+ OutputString("\n"); |
+ } else if (value->GetAsBoolean(&bool_value)) { |
+ OutputString(indent); |
+ OutputString(bool_value ? "true" : "false"); |
+ OutputString("\n"); |
+ } else if (value->GetAsDictionary(&dict_value)) { |
+ base::DictionaryValue::Iterator iter(*dict_value); |
+ while (!iter.IsAtEnd()) { |
+ OutputString(indent + iter.key() + "\n"); |
+ PrintValue(&iter.value(), indentLevel + 1); |
+ iter.Advance(); |
} |
+ } else if (value->IsType(base::Value::TYPE_NULL)) { |
+ OutputString(indent + "<null>\n"); |
} |
-#endif |
- return dir.value(); |
-} |
- |
-void RecursiveCollectChildDeps(const Target* target, |
- std::set<const Target*>* result); |
- |
-void RecursiveCollectDeps(const Target* target, |
- std::set<const Target*>* result) { |
- if (result->find(target) != result->end()) |
- return; // Already did this target. |
- result->insert(target); |
- |
- RecursiveCollectChildDeps(target, result); |
} |
-void RecursiveCollectChildDeps(const Target* target, |
- std::set<const Target*>* result) { |
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) |
- RecursiveCollectDeps(pair.ptr, result); |
+// Default handler for property |
+void DefaultHandler(const std::string& name, const base::Value* value) { |
+ OutputString("\n"); |
+ OutputString(name); |
+ OutputString("\n"); |
+ PrintValue(value, 1); |
} |
-// Prints dependencies of the given target (not the target itself). If the |
-// set is non-null, new targets encountered will be added to the set, and if |
-// a dependency is in the set already, it will not be recused into. When the |
-// set is null, all dependencies will be printed. |
-void RecursivePrintDeps(const Target* target, |
- const Label& default_toolchain, |
- std::set<const Target*>* seen_targets, |
- int indent_level) { |
- // Combine all deps into one sorted list. |
- std::vector<LabelTargetPair> sorted_deps; |
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) |
- sorted_deps.push_back(pair); |
- std::sort(sorted_deps.begin(), sorted_deps.end(), |
- LabelPtrLabelLess<Target>()); |
- |
- std::string indent(indent_level * 2, ' '); |
- for (const auto& pair : sorted_deps) { |
- const Target* cur_dep = pair.ptr; |
- |
- OutputString(indent + |
- cur_dep->label().GetUserVisibleName(default_toolchain)); |
- bool print_children = true; |
- if (seen_targets) { |
- if (seen_targets->find(cur_dep) == seen_targets->end()) { |
- // New target, mark it visited. |
- seen_targets->insert(cur_dep); |
- } else { |
- // Already seen. |
- print_children = false; |
- // Only print "..." if something is actually elided, which means that |
- // the current target has children. |
- if (!cur_dep->public_deps().empty() || |
- !cur_dep->private_deps().empty() || |
- !cur_dep->data_deps().empty()) |
- OutputString("..."); |
- } |
- } |
+// Specific handler for properties that need different treatment |
- OutputString("\n"); |
- if (print_children) { |
- RecursivePrintDeps(cur_dep, default_toolchain, seen_targets, |
- indent_level + 1); |
- } |
+// Prints label and property value on one line, capitalizing the label. |
+void LabelHandler(std::string name, const base::Value* value) { |
+ name[0] = base::ToUpperASCII(name[0]); |
+ std::string string_value; |
+ if (value->GetAsString(&string_value)) { |
+ OutputString(name + ": ", DECORATION_YELLOW); |
+ OutputString(string_value + "\n"); |
} |
} |
-void PrintDeps(const Target* target, bool display_header) { |
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
- Label toolchain_label = target->label().GetToolchainLabel(); |
- |
- // Tree mode is separate. |
- if (cmdline->HasSwitch(kTree)) { |
- if (display_header) |
- OutputString("\nDependency tree\n"); |
- |
- if (cmdline->HasSwitch("all")) { |
- // Show all tree deps with no eliding. |
- RecursivePrintDeps(target, toolchain_label, nullptr, 1); |
+void VisibilityHandler(const std::string& name, const base::Value* value) { |
+ const base::ListValue* list; |
+ if (value->GetAsList(&list)) { |
+ if (list->empty()) { |
+ base::StringValue str("(no visibility)"); |
+ DefaultHandler(name, &str); |
} else { |
- // Don't recurse into duplicates. |
- std::set<const Target*> seen_targets; |
- RecursivePrintDeps(target, toolchain_label, &seen_targets, 1); |
+ DefaultHandler(name, value); |
} |
- return; |
} |
- |
- // Collect the deps to display. |
- if (cmdline->HasSwitch("all")) { |
- // Show all dependencies. |
- if (display_header) |
- OutputString("\nAll recursive dependencies\n"); |
- |
- std::set<const Target*> all_deps; |
- RecursiveCollectChildDeps(target, &all_deps); |
- FilterAndPrintTargetSet(display_header, all_deps); |
- } else { |
- std::vector<const Target*> deps; |
- // Show direct dependencies only. |
- if (display_header) { |
- OutputString( |
- "\nDirect dependencies " |
- "(try also \"--all\", \"--tree\", or even \"--all --tree\")\n"); |
- } |
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) |
- deps.push_back(pair.ptr); |
- std::sort(deps.begin(), deps.end()); |
- FilterAndPrintTargets(display_header, &deps); |
- } |
-} |
- |
-// libs and lib_dirs are special in that they're inherited. We don't currently |
-// implement a blame feature for this since the bottom-up inheritance makes |
-// this difficult. |
-void PrintLibDirs(const Target* target, bool display_header) { |
- const OrderedSet<SourceDir>& lib_dirs = target->all_lib_dirs(); |
- if (lib_dirs.empty()) |
- return; |
- |
- if (display_header) |
- OutputString("\nlib_dirs\n"); |
- |
- for (size_t i = 0; i < lib_dirs.size(); i++) |
- OutputString(" " + FormatSourceDir(lib_dirs[i]) + "\n"); |
-} |
- |
-void PrintLibs(const Target* target, bool display_header) { |
- const OrderedSet<LibFile>& libs = target->all_libs(); |
- if (libs.empty()) |
- return; |
- |
- if (display_header) |
- OutputString("\nlibs\n"); |
- |
- for (size_t i = 0; i < libs.size(); i++) |
- OutputString(" " + libs[i].value() + "\n"); |
} |
-void PrintPublic(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\npublic\n"); |
- |
- if (target->all_headers_public()) { |
- OutputString(" [All headers listed in the sources are public.]\n"); |
- return; |
+void PublicHandler(const std::string& name, const base::Value* value) { |
+ std::string p; |
+ if (value->GetAsString(&p)) { |
+ if (p == "*") { |
+ base::StringValue str("[All headers listed in the sources are public.]"); |
+ DefaultHandler(name, &str); |
+ return; |
+ } |
} |
- |
- Target::FileList public_headers = target->public_headers(); |
- std::sort(public_headers.begin(), public_headers.end()); |
- for (const auto& hdr : public_headers) |
- OutputString(" " + hdr.value() + "\n"); |
-} |
- |
-void PrintCheckIncludes(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\ncheck_includes\n"); |
- |
- if (target->check_includes()) |
- OutputString(" true\n"); |
- else |
- OutputString(" false\n"); |
+ DefaultHandler(name, value); |
} |
-void PrintAllowCircularIncludesFrom(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\nallow_circular_includes_from\n"); |
- |
- Label toolchain_label = target->label().GetToolchainLabel(); |
- for (const auto& cur : target->allow_circular_includes_from()) |
- OutputString(" " + cur.GetUserVisibleName(toolchain_label) + "\n"); |
-} |
- |
-void PrintVisibility(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\nvisibility\n"); |
- |
- OutputString(target->visibility().Describe(2, false)); |
-} |
- |
-void PrintTestonly(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\ntestonly\n"); |
- |
- if (target->testonly()) |
- OutputString(" true\n"); |
+void ConfigsHandler(const std::string& name, const base::Value* value) { |
+ bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); |
+ if (tree) |
+ DefaultHandler(name + " tree (in order applying)", value); |
else |
- OutputString(" false\n"); |
+ DefaultHandler(name + " (in order applying, try also --tree)", value); |
} |
-// Recursively prints subconfigs of a config. |
-void PrintSubConfigs(const Config* config, int indent_level) { |
- if (config->configs().empty()) |
- return; |
- |
- std::string indent(indent_level * 2, ' '); |
- Label toolchain_label = config->label().GetToolchainLabel(); |
- for (const auto& pair : config->configs()) { |
- OutputString( |
- indent + pair.label.GetUserVisibleName(toolchain_label) + "\n"); |
- PrintSubConfigs(pair.ptr, indent_level + 1); |
- } |
-} |
- |
-// This allows configs stored as either std::vector<LabelConfigPair> or |
-// UniqueVector<LabelConfigPair> to be printed. |
-template <class VectorType> |
-void PrintConfigsVector(const Item* item, |
- const VectorType& configs, |
- const std::string& heading, |
- bool display_header) { |
- if (configs.empty()) |
- return; |
- |
+void DepsHandler(const std::string& name, const base::Value* value) { |
bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); |
- |
- // Don't sort since the order determines how things are processed. |
- if (display_header) { |
- if (tree) |
- OutputString("\n" + heading + " tree (in order applying)\n"); |
- else |
- OutputString("\n" + heading + " (in order applying, try also --tree)\n"); |
- } |
- |
- Label toolchain_label = item->label().GetToolchainLabel(); |
- for (const auto& config : configs) { |
- OutputString(" " + config.label.GetUserVisibleName(toolchain_label) + |
- "\n"); |
- if (tree) |
- PrintSubConfigs(config.ptr, 2); // 2 = start with double-indent. |
- } |
-} |
- |
-void PrintConfigs(const Target* target, bool display_header) { |
- PrintConfigsVector(target, target->configs().vector(), "configs", |
- display_header); |
-} |
- |
-void PrintConfigs(const Config* config, bool display_header) { |
- PrintConfigsVector(config, config->configs().vector(), "configs", |
- display_header); |
-} |
- |
-void PrintPublicConfigs(const Target* target, bool display_header) { |
- PrintConfigsVector(target, target->public_configs(), |
- "public_configs", display_header); |
-} |
- |
-void PrintAllDependentConfigs(const Target* target, bool display_header) { |
- PrintConfigsVector(target, target->all_dependent_configs(), |
- "all_dependent_configs", display_header); |
-} |
- |
-void PrintFileList(const Target::FileList& files, |
- const std::string& header, |
- bool indent_extra, |
- bool display_header) { |
- if (files.empty()) |
- return; |
- |
- if (display_header) |
- OutputString("\n" + header + "\n"); |
- |
- std::string indent = indent_extra ? " " : " "; |
- |
- Target::FileList sorted = files; |
- std::sort(sorted.begin(), sorted.end()); |
- for (const auto& elem : sorted) |
- OutputString(indent + elem.value() + "\n"); |
-} |
- |
-void PrintSources(const Target* target, bool display_header) { |
- PrintFileList(target->sources(), "sources", false, display_header); |
-} |
- |
-void PrintInputs(const Target* target, bool display_header) { |
- PrintFileList(target->inputs(), "inputs", false, display_header); |
-} |
- |
-void PrintOutputs(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\noutputs\n"); |
- |
- if (target->output_type() == Target::ACTION) { |
- // Action, print out outputs, don't apply sources to it. |
- for (const auto& elem : target->action_values().outputs().list()) { |
- OutputString(" " + elem.AsString() + "\n"); |
- } |
- } else if (target->output_type() == Target::CREATE_BUNDLE) { |
- std::vector<SourceFile> output_files; |
- target->bundle_data().GetOutputsAsSourceFiles(target->settings(), |
- &output_files); |
- PrintFileList(output_files, std::string(), true, false); |
- } else if (target->output_type() == Target::ACTION_FOREACH) { |
- const SubstitutionList& outputs = target->action_values().outputs(); |
- if (!outputs.required_types().empty()) { |
- // Display the pattern and resolved pattern separately, since there are |
- // subtitutions used. |
- OutputString(" Output pattern\n"); |
- for (const auto& elem : outputs.list()) |
- OutputString(" " + elem.AsString() + "\n"); |
- |
- // Now display what that resolves to given the sources. |
- OutputString("\n Resolved output file list\n"); |
- } |
- |
- // Resolved output list. |
- std::vector<SourceFile> output_files; |
- SubstitutionWriter::ApplyListToSources(target->settings(), outputs, |
- target->sources(), &output_files); |
- PrintFileList(output_files, std::string(), true, false); |
+ bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree); |
+ if (tree) { |
+ DefaultHandler("Dependency tree", value); |
} else { |
- DCHECK(target->IsBinary()); |
- const Tool* tool = target->toolchain()->GetToolForTargetFinalOutput(target); |
- |
- std::vector<OutputFile> output_files; |
- SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
- target, tool, tool->outputs(), &output_files); |
- |
- std::vector<SourceFile> output_files_as_source_file; |
- for (const OutputFile& output_file : output_files) { |
- output_files_as_source_file.push_back( |
- output_file.AsSourceFile(target->settings()->build_settings())); |
- } |
- |
- PrintFileList(output_files_as_source_file, std::string(), true, false); |
- } |
-} |
- |
-void PrintScript(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\nscript\n"); |
- OutputString(" " + target->action_values().script().value() + "\n"); |
-} |
- |
-void PrintArgs(const Target* target, bool display_header) { |
- if (display_header) |
- OutputString("\nargs\n"); |
- for (const auto& elem : target->action_values().args().list()) { |
- OutputString(" " + elem.AsString() + "\n"); |
- } |
-} |
- |
-void PrintDepfile(const Target* target, bool display_header) { |
- if (target->action_values().depfile().empty()) |
- return; |
- if (display_header) |
- OutputString("\ndepfile\n"); |
- OutputString(" " + target->action_values().depfile().AsString() + "\n"); |
-} |
- |
-// Attribute the origin for attributing from where a target came from. Does |
-// nothing if the input is null or it does not have a location. |
-void OutputSourceOfDep(const ParseNode* origin, std::ostream& out) { |
- if (!origin) |
- return; |
- Location location = origin->GetRange().begin(); |
- out << " (Added by " + location.file()->name().value() << ":" |
- << location.line_number() << ")\n"; |
-} |
- |
-// Templatized writer for writing out different config value types. |
-template<typename T> struct DescValueWriter {}; |
-template<> struct DescValueWriter<std::string> { |
- void operator()(const std::string& str, std::ostream& out) const { |
- out << " " << str << "\n"; |
- } |
-}; |
- |
-template<> struct DescValueWriter<SourceDir> { |
- void operator()(const SourceDir& dir, std::ostream& out) const { |
- out << " " << FormatSourceDir(dir) << "\n"; |
- } |
-}; |
- |
-template<> struct DescValueWriter<LibFile> { |
- void operator()(const LibFile& lib, std::ostream& out) const { |
- if (lib.is_source_file()) |
- out << " " << lib.source_file().value() << "\n"; |
- else |
- out << " " << lib.value() << "\n"; |
- } |
-}; |
- |
-// Writes a given config value type to the string, optionally with attribution. |
-// This should match RecursiveTargetConfigToStream in the order it traverses. |
-template<typename T> void OutputRecursiveTargetConfig( |
- const Target* target, |
- const char* header_name, |
- const std::vector<T>& (ConfigValues::* getter)() const) { |
- bool display_blame = |
- base::CommandLine::ForCurrentProcess()->HasSwitch(kBlame); |
- |
- DescValueWriter<T> writer; |
- std::ostringstream out; |
- |
- for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) { |
- if ((iter.cur().*getter)().empty()) |
- continue; |
- |
- // Optional blame sub-head. |
- if (display_blame) { |
- const Config* config = iter.GetCurrentConfig(); |
- if (config) { |
- // Source of this value is a config. |
- out << " From " << config->label().GetUserVisibleName(false) << "\n"; |
- OutputSourceOfDep(iter.origin(), out); |
- } else { |
- // Source of this value is the target itself. |
- out << " From " << target->label().GetUserVisibleName(false) << "\n"; |
- } |
+ if (!all) { |
+ DefaultHandler( |
+ "Direct dependencies " |
+ "(try also \"--all\", \"--tree\", or even \"--all --tree\")", |
+ value); |
+ } else { |
+ DefaultHandler("All recursive dependencies", value); |
} |
- |
- // Actual values. |
- ConfigValuesToStream(iter.cur(), getter, writer, out); |
- } |
- |
- std::string out_str = out.str(); |
- if (!out_str.empty()) { |
- if (header_name) |
- OutputString("\n" + std::string(header_name) + "\n"); |
- OutputString(out_str); |
} |
} |
-template<typename T> void OutputConfigValueArray( |
- const ConfigValues& values, |
- const char* header_name, |
- const std::vector<T>& (ConfigValues::* getter)() const) { |
- std::ostringstream out; |
- |
- DescValueWriter<T> writer; |
- for (const T& cur : (values.*getter)()) |
- writer(cur, out); |
- |
- std::string out_str = out.str(); |
- if (!out_str.empty()) { |
- if (header_name) |
- OutputString("\n" + std::string(header_name) + "\n"); |
- OutputString(out_str); |
- } |
-} |
+// Outputs need special processing when output patterns are present. |
+void ProcessOutputs(base::DictionaryValue* target) { |
+ base::ListValue* patterns = nullptr; |
+ base::ListValue* outputs = nullptr; |
+ target->GetList("output_patterns", &patterns); |
+ target->GetList(variables::kOutputs, &outputs); |
-void PrintRuntimeDeps(const Target* target) { |
- bool display_blame = |
- base::CommandLine::ForCurrentProcess()->HasSwitch(kBlame); |
- Label toolchain = target->label().GetToolchainLabel(); |
- |
- const Target* previous_from = NULL; |
- for (const auto& pair : ComputeRuntimeDeps(target)) { |
- if (display_blame) { |
- // Generally a target's runtime deps will be listed sequentially, so |
- // group them and don't duplicate the "from" label for two in a row. |
- if (previous_from == pair.second) { |
- OutputString(" "); // Just indent. |
- } else { |
- previous_from = pair.second; |
- OutputString("From "); |
- OutputString(pair.second->label().GetUserVisibleName(toolchain)); |
- OutputString("\n "); // Make the file name indented. |
- } |
+ if (outputs || patterns) { |
+ OutputString("\noutputs\n"); |
+ int indent = 1; |
+ if (patterns) { |
+ OutputString(" Output patterns\n"); |
+ indent = 2; |
+ PrintValue(patterns, indent); |
+ OutputString("\n Resolved output file list\n"); |
} |
- OutputString(pair.first.value()); |
- OutputString("\n"); |
- } |
-} |
+ if (outputs) |
+ PrintValue(outputs, indent); |
-// If "what" is empty, prints all PCH info. If "what" is nonempty, prints only |
-// the things that match (if any). Returns true if anything was printed. |
-bool PrintPrecompiledHeaderInfo(const ConfigValues& values, |
- const std::string& what, |
- bool display_headers) { |
- bool found_match = false; |
- if (what == variables::kPrecompiledHeader || what.empty()) { |
- if (!values.precompiled_header().empty()) { |
- if (display_headers) |
- OutputString("\nprecompiled_header\n"); |
- OutputString(values.precompiled_header() + "\n"); |
- } |
- found_match = true; |
- } |
- if (what == variables::kPrecompiledSource || what.empty()) { |
- if (!values.precompiled_source().is_null()) { |
- if (display_headers) |
- OutputString("\nprecompiled_source\n"); |
- OutputString(values.precompiled_source().value() + "\n"); |
- } |
- found_match = true; |
+ target->Remove("output_patterns", nullptr); |
+ target->Remove(variables::kOutputs, nullptr); |
} |
- return found_match; |
} |
bool PrintTarget(const Target* target, |
const std::string& what, |
- bool display_target_header) { |
- if (display_target_header) { |
- OutputString("Target: ", DECORATION_YELLOW); |
- OutputString(target->label().GetUserVisibleName(false) + "\n"); |
- OutputString("Type: ", DECORATION_YELLOW); |
- OutputString(std::string( |
- Target::GetStringForOutputType(target->output_type())) + "\n"); |
- OutputString("Toolchain: ", DECORATION_YELLOW); |
- OutputString( |
- target->label().GetToolchainLabel().GetUserVisibleName(false) + "\n"); |
- } |
- |
- // Display headers when outputting everything. |
- bool display_headers = what.empty(); |
- bool is_binary_output = target->IsBinary(); |
- |
- bool found_match = false; |
- |
- // General target meta variables. |
- if (what.empty() || what == variables::kVisibility) { |
- PrintVisibility(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kTestonly) { |
- PrintTestonly(target, display_headers); |
- found_match = true; |
- } |
- |
- // Binary target meta variables. |
- if (is_binary_output) { |
- if (what.empty() || what == variables::kCheckIncludes) { |
- PrintCheckIncludes(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kAllowCircularIncludesFrom) { |
- PrintAllowCircularIncludesFrom(target, display_headers); |
- found_match = true; |
- } |
- } |
- |
- // Sources and inputs. |
- if (what.empty() || what == variables::kSources) { |
- PrintSources(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kPublic) { |
- PrintPublic(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kInputs) { |
- PrintInputs(target, display_headers); |
- found_match = true; |
- } |
- |
- // Configs. Configs set directly on a target are only relevant for binary |
- // targets |
- if (is_binary_output && (what.empty() || what == variables::kConfigs)) { |
- PrintConfigs(target, display_headers); |
- found_match = true; |
- } |
- |
- // Dependent/public configs can be applied to anything. |
- if (what.empty() || what == variables::kPublicConfigs) { |
- PrintPublicConfigs(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kAllDependentConfigs) { |
- PrintAllDependentConfigs(target, display_headers); |
- found_match = true; |
- } |
- |
- // Action values. |
- if (target->output_type() == Target::ACTION || |
- target->output_type() == Target::ACTION_FOREACH) { |
- if (what.empty() || what == variables::kScript) { |
- PrintScript(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kArgs) { |
- PrintArgs(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kDepfile) { |
- PrintDepfile(target, display_headers); |
- found_match = true; |
- } |
- } |
- |
- // Outputs. |
- if (target->output_type() != Target::SOURCE_SET && |
- target->output_type() != Target::GROUP) { |
- if (what.empty() || what == variables::kOutputs) { |
- PrintOutputs(target, display_headers); |
- found_match = true; |
- } |
- } |
- |
- // Values from configs only apply to binary targets. |
- if (is_binary_output) { |
- #define CONFIG_VALUE_ARRAY_HANDLER(name, type) \ |
- if (what.empty() || what == #name) { \ |
- OutputRecursiveTargetConfig<type>( \ |
- target, display_headers ? #name : nullptr, &ConfigValues::name); \ |
- found_match = true; \ |
- } |
- |
- CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(defines, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir) |
- CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string) |
- // Libs and lib_dirs are handled specially below. |
- |
- #undef CONFIG_VALUE_ARRAY_HANDLER |
- |
- found_match |= PrintPrecompiledHeaderInfo(target->config_values(), |
- what, display_headers); |
- } |
- |
- // Deps |
- if (what.empty() || what == "deps") { |
- PrintDeps(target, display_headers); |
- found_match = true; |
- } |
- |
- // Runtime deps are special, print only when explicitly asked for and not in |
- // overview mode. |
- if (what == "runtime_deps") { |
- PrintRuntimeDeps(target); |
- found_match = true; |
- } |
- |
- // Libs can be part of any target and get recursively pushed up the chain, |
- // so display them regardless of target type. |
- if (what.empty() || what == variables::kLibs) { |
- PrintLibs(target, display_headers); |
- found_match = true; |
- } |
- if (what.empty() || what == variables::kLibDirs) { |
- PrintLibDirs(target, display_headers); |
- found_match = true; |
- } |
- |
- if (!found_match) { |
+ bool single_target, |
+ bool all, |
+ bool tree, |
+ bool blame) { |
+ std::unique_ptr<base::DictionaryValue> dict = |
+ DescBuilder::DescriptionForTarget(target, what, all, tree, blame); |
+ if (!what.empty() && dict->empty()) { |
OutputString("Don't know how to display \"" + what + "\" for \"" + |
- Target::GetStringForOutputType(target->output_type()) + "\".\n"); |
+ Target::GetStringForOutputType(target->output_type()) + |
+ "\".\n"); |
return false; |
} |
+ // Print single value, without any headers |
+ if (!what.empty() && dict->size() == 1 && single_target) { |
+ base::DictionaryValue::Iterator iter(*dict); |
+ PrintValue(&iter.value(), 0); |
+ return true; |
+ } |
+ |
+ OutputString("Target ", DECORATION_YELLOW); |
+ OutputString(target->label().GetUserVisibleName(false)); |
+ OutputString("\n"); |
+ |
+ std::unique_ptr<base::Value> v; |
+#define HANDLER(property, handler_name) \ |
+ if (dict->Remove(property, &v)) { \ |
+ handler_name(property, v.get()); \ |
+ } |
+ |
+ // Entries with DefaultHandler are present to enforce order |
+ HANDLER("type", LabelHandler); |
+ HANDLER("toolchain", LabelHandler); |
+ HANDLER(variables::kVisibility, VisibilityHandler); |
+ HANDLER(variables::kTestonly, DefaultHandler); |
+ HANDLER(variables::kCheckIncludes, DefaultHandler); |
+ HANDLER(variables::kAllowCircularIncludesFrom, DefaultHandler); |
+ HANDLER(variables::kSources, DefaultHandler); |
+ HANDLER(variables::kPublic, PublicHandler); |
+ HANDLER(variables::kInputs, DefaultHandler); |
+ HANDLER(variables::kConfigs, ConfigsHandler); |
+ HANDLER(variables::kPublicConfigs, ConfigsHandler); |
+ HANDLER(variables::kAllDependentConfigs, ConfigsHandler); |
+ HANDLER(variables::kScript, DefaultHandler); |
+ HANDLER(variables::kArgs, DefaultHandler); |
+ HANDLER(variables::kDepfile, DefaultHandler); |
+ ProcessOutputs(dict.get()); |
+ HANDLER("bundle_data", DefaultHandler); |
+ HANDLER(variables::kArflags, DefaultHandler); |
+ HANDLER(variables::kAsmflags, DefaultHandler); |
+ HANDLER(variables::kCflags, DefaultHandler); |
+ HANDLER(variables::kCflagsC, DefaultHandler); |
+ HANDLER(variables::kCflagsCC, DefaultHandler); |
+ HANDLER(variables::kCflagsObjC, DefaultHandler); |
+ HANDLER(variables::kCflagsObjCC, DefaultHandler); |
+ HANDLER(variables::kDefines, DefaultHandler); |
+ HANDLER(variables::kIncludeDirs, DefaultHandler); |
+ HANDLER(variables::kLdflags, DefaultHandler); |
+ HANDLER(variables::kPrecompiledHeader, DefaultHandler); |
+ HANDLER(variables::kPrecompiledSource, DefaultHandler); |
+ HANDLER(variables::kDeps, DepsHandler); |
+ HANDLER(variables::kLibs, DefaultHandler); |
+ HANDLER(variables::kLibDirs, DefaultHandler); |
+ |
+#undef HANDLER |
+ |
+ // Process the rest (if any) |
+ base::DictionaryValue::Iterator iter(*dict); |
+ while (!iter.IsAtEnd()) { |
+ DefaultHandler(iter.key(), &iter.value()); |
+ iter.Advance(); |
+ } |
+ |
return true; |
} |
bool PrintConfig(const Config* config, |
const std::string& what, |
- bool display_config_header) { |
- const ConfigValues& values = config->resolved_values(); |
- |
- if (display_config_header) { |
- OutputString("Config: ", DECORATION_YELLOW); |
- OutputString(config->label().GetUserVisibleName(false) + "\n"); |
- OutputString("Toolchain: ", DECORATION_YELLOW); |
- OutputString( |
- config->label().GetToolchainLabel().GetUserVisibleName(false) + "\n"); |
- if (what.empty() && !config->configs().empty()) { |
- OutputString( |
- "(This is a composite config, the values below are after the\n" |
- "expansion of the child configs.)\n"); |
- } |
+ bool single_config) { |
+ std::unique_ptr<base::DictionaryValue> dict = |
+ DescBuilder::DescriptionForConfig(config, what); |
+ if (!what.empty() && dict->empty()) { |
+ OutputString("Don't know how to display \"" + what + "\" for a config.\n"); |
+ return false; |
} |
- |
- // Display headers when outputting everything. |
- bool display_headers = what.empty(); |
- |
- if (what.empty() || what == variables::kConfigs) |
- PrintConfigs(config, display_headers); |
- |
-#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \ |
- if (what.empty() || what == #name) { \ |
- OutputConfigValueArray<type>(values, display_headers ? #name : nullptr, \ |
- &ConfigValues::name); \ |
- found_match = true; \ |
+ // Print single value, without any headers |
+ if (!what.empty() && dict->size() == 1 && single_config) { |
+ base::DictionaryValue::Iterator iter(*dict); |
+ PrintValue(&iter.value(), 0); |
+ return true; |
} |
- bool found_match = false; |
- |
- CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(defines, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir) |
- CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string) |
- CONFIG_VALUE_ARRAY_HANDLER(lib_dirs, SourceDir) |
- CONFIG_VALUE_ARRAY_HANDLER(libs, LibFile) |
+ OutputString("Config: ", DECORATION_YELLOW); |
+ OutputString(config->label().GetUserVisibleName(false)); |
+ OutputString("\n"); |
-#undef CONFIG_VALUE_ARRAY_HANDLER |
+ std::unique_ptr<base::Value> v; |
+#define HANDLER(property, handler_name) \ |
+ if (dict->Remove(property, &v)) { \ |
+ handler_name(property, v.get()); \ |
+ } |
- // Handles all PCH-related variables. |
- found_match |= PrintPrecompiledHeaderInfo(config->resolved_values(), |
- what, display_headers); |
+ HANDLER("toolchain", LabelHandler); |
+ if (!config->configs().empty()) { |
+ OutputString( |
+ "(This is a composite config, the values below are after the\n" |
+ "expansion of the child configs.)\n"); |
+ } |
+ HANDLER(variables::kArflags, DefaultHandler); |
+ HANDLER(variables::kAsmflags, DefaultHandler); |
+ HANDLER(variables::kCflags, DefaultHandler); |
+ HANDLER(variables::kCflagsC, DefaultHandler); |
+ HANDLER(variables::kCflagsCC, DefaultHandler); |
+ HANDLER(variables::kCflagsObjC, DefaultHandler); |
+ HANDLER(variables::kCflagsObjCC, DefaultHandler); |
+ HANDLER(variables::kDefines, DefaultHandler); |
+ HANDLER(variables::kIncludeDirs, DefaultHandler); |
+ HANDLER(variables::kLdflags, DefaultHandler); |
+ HANDLER(variables::kLibs, DefaultHandler); |
+ HANDLER(variables::kLibDirs, DefaultHandler); |
+ HANDLER(variables::kPrecompiledHeader, DefaultHandler); |
+ HANDLER(variables::kPrecompiledSource, DefaultHandler); |
+ |
+#undef HANDLER |
- if (!found_match) { |
- OutputString("Don't know how to display \"" + what + "\" for a config.\n"); |
- return false; |
- } |
return true; |
} |
@@ -762,7 +291,8 @@ const char kDesc[] = "desc"; |
const char kDesc_HelpShort[] = |
"desc: Show lots of insightful information about a target or config."; |
const char kDesc_Help[] = |
- "gn desc <out_dir> <label or pattern> [<what to show>] [--blame]\n" |
+ "gn desc <out_dir> <label or pattern> [<what to show>] [--blame] " |
+ "[--format=json]\n" |
"\n" |
" Displays information about a given target or config. The build\n" |
" build parameters will be taken for the build in the given <out_dir>.\n" |
@@ -814,6 +344,9 @@ const char kDesc_Help[] = |
"\n" |
ALL_TOOLCHAINS_SWITCH_HELP |
"\n" |
+ " --format=json\n" |
+ " Format the output as JSON instead of text.\n" |
+ "\n" |
"Target flags\n" |
"\n" |
" --blame\n" |
@@ -922,28 +455,52 @@ int RunDesc(const std::vector<std::string>& args) { |
if (args.size() == 3) |
what_to_print = args[2]; |
- bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1; |
- |
- // Display headers for each target when printing all values, or when printing |
- // multiple targets or configs. |
- bool display_item_header = multiple_outputs || what_to_print.empty(); |
- |
- bool printed_output = false; |
- for (const Target* target : target_matches) { |
- if (printed_output) |
- OutputString("\n\n"); |
- printed_output = true; |
- |
- if (!PrintTarget(target, what_to_print, display_item_header)) |
- return 1; |
- } |
- for (const Config* config : config_matches) { |
- if (printed_output) |
- OutputString("\n\n"); |
- printed_output = true; |
+ bool json = cmdline->GetSwitchValueASCII("format") == "json"; |
+ |
+ if (json) { |
+ // Convert all targets/configs to JSON, serialize and print them |
+ auto res = base::WrapUnique(new base::DictionaryValue()); |
+ if (!target_matches.empty()) { |
+ for (const auto* target : target_matches) { |
+ res->Set(target->label().GetUserVisibleName( |
+ target->settings()->default_toolchain_label()), |
+ DescBuilder::DescriptionForTarget( |
+ target, what_to_print, cmdline->HasSwitch(kAll), |
+ cmdline->HasSwitch(kTree), cmdline->HasSwitch(kBlame))); |
+ } |
+ } else if (!config_matches.empty()) { |
+ for (const auto* config : config_matches) { |
+ res->Set(config->label().GetUserVisibleName(false), |
+ DescBuilder::DescriptionForConfig(config, what_to_print)); |
+ } |
+ } |
+ std::string s; |
+ base::JSONWriter::WriteWithOptions( |
+ *res.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s); |
+ OutputString(s); |
+ } else { |
+ // Regular (non-json) formatted output |
+ bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1; |
+ |
+ bool printed_output = false; |
+ for (const Target* target : target_matches) { |
+ if (printed_output) |
+ OutputString("\n\n"); |
+ printed_output = true; |
+ |
+ if (!PrintTarget(target, what_to_print, !multiple_outputs, |
+ cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree), |
+ cmdline->HasSwitch(kBlame))) |
+ return 1; |
+ } |
+ for (const Config* config : config_matches) { |
+ if (printed_output) |
+ OutputString("\n\n"); |
+ printed_output = true; |
- if (!PrintConfig(config, what_to_print, display_item_header)) |
- return 1; |
+ if (!PrintConfig(config, what_to_print, !multiple_outputs)) |
+ return 1; |
+ } |
} |
return 0; |