[GN] Add JSON project writer
Output is a JSON file containing information about targets. The generator
can also optionally invoke a python script on generated file.
Example:
gn gen --ide=json ./out-json --json-ide-script=//scripts/custom-ide-generator.py \
--ison-ide-script-args="additional script arguments"
Also implements --format=json for gn desc as described in https://bugs.chromium.org/p/chromium/issues/detail?id=620132
BUG=
Review-Url: https://codereview.chromium.org/2064533002
Cr-Original-Commit-Position: refs/heads/master@{#406064}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: d2edeb9a743ad5a2046e655997277d3bb630db03
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
index 510c178..b77d2af 100644
--- a/tools/gn/BUILD.gn
+++ b/tools/gn/BUILD.gn
@@ -58,6 +58,8 @@
"create_bundle_target_generator.h",
"deps_iterator.cc",
"deps_iterator.h",
+ "desc_builder.cc",
+ "desc_builder.h",
"eclipse_writer.cc",
"eclipse_writer.h",
"err.cc",
@@ -101,6 +103,8 @@
"input_file_manager.h",
"item.cc",
"item.h",
+ "json_project_writer.cc",
+ "json_project_writer.h",
"label.cc",
"label.h",
"label_pattern.cc",
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index ea1bab3..72525e9 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 @@
// Desc-specific command line switches.
const char kBlame[] = "blame";
const char kTree[] = "tree";
+const char kAll[] = "all";
-// 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;
+// 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);
}
- }
-#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);
-}
-
-// 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("...");
- }
- }
-
+ } else if (value->GetAsString(&string_value)) {
+ OutputString(indent);
+ OutputString(string_value);
OutputString("\n");
- if (print_children) {
- RecursivePrintDeps(cur_dep, default_toolchain, seen_targets,
- indent_level + 1);
+ } 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");
}
}
-void PrintDeps(const Target* target, bool display_header) {
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- Label toolchain_label = target->label().GetToolchainLabel();
+// Default handler for property
+void DefaultHandler(const std::string& name, const base::Value* value) {
+ OutputString("\n");
+ OutputString(name);
+ OutputString("\n");
+ PrintValue(value, 1);
+}
- // Tree mode is separate.
- if (cmdline->HasSwitch(kTree)) {
- if (display_header)
- OutputString("\nDependency tree\n");
+// Specific handler for properties that need different treatment
- if (cmdline->HasSwitch("all")) {
- // Show all tree deps with no eliding.
- RecursivePrintDeps(target, toolchain_label, nullptr, 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 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");
+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;
}
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
- deps.push_back(pair.ptr);
- std::sort(deps.begin(), deps.end());
- FilterAndPrintTargets(display_header, &deps);
}
+ DefaultHandler(name, value);
}
-// 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;
- }
-
- 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");
-}
-
-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");
- else
- OutputString(" false\n");
-}
-
-// 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 ConfigsHandler(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.
- }
+ if (tree)
+ DefaultHandler(name + " tree (in order applying)", value);
+ else
+ DefaultHandler(name + " (in order applying, try also --tree)", value);
}
-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");
+void DepsHandler(const std::string& name, const base::Value* value) {
+ bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ if (tree) {
+ DefaultHandler("Dependency tree", value);
+ } else {
+ if (!all) {
+ DefaultHandler(
+ "Direct dependencies "
+ "(try also \"--all\", \"--tree\", or even \"--all --tree\")",
+ value);
+ } else {
+ DefaultHandler("All recursive dependencies", value);
}
- } 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.
+// 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);
+
+ 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");
}
+ if (outputs)
+ PrintValue(outputs, indent);
- // Resolved output list.
- std::vector<SourceFile> output_files;
- SubstitutionWriter::ApplyListToSources(target->settings(), outputs,
- target->sources(), &output_files);
- PrintFileList(output_files, std::string(), true, false);
- } 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);
+ target->Remove("output_patterns", nullptr);
+ target->Remove(variables::kOutputs, nullptr);
}
}
-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";
- }
- }
-
- // 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);
- }
-}
-
-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.
- }
- }
- OutputString(pair.first.value());
- OutputString("\n");
- }
-}
-
-// 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;
- }
- 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");
- }
- }
-
- // 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; \
- }
-
- 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)
-
-#undef CONFIG_VALUE_ARRAY_HANDLER
-
- // Handles all PCH-related variables.
- found_match |= PrintPrecompiledHeaderInfo(config->resolved_values(),
- what, display_headers);
-
- if (!found_match) {
+ 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;
}
+ // 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;
+ }
+
+ OutputString("Config: ", DECORATION_YELLOW);
+ OutputString(config->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()); \
+ }
+
+ 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
+
return true;
}
@@ -762,7 +291,8 @@
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 @@
"\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 @@
if (args.size() == 3)
what_to_print = args[2];
- bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1;
+ bool json = cmdline->GetSwitchValueASCII("format") == "json";
- // 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();
+ 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;
+ 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;
+ 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;
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index 3d44274..06b99ca 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -11,6 +11,7 @@
#include "tools/gn/build_settings.h"
#include "tools/gn/commands.h"
#include "tools/gn/eclipse_writer.h"
+#include "tools/gn/json_project_writer.h"
#include "tools/gn/ninja_target_writer.h"
#include "tools/gn/ninja_writer.h"
#include "tools/gn/qt_creator_writer.h"
@@ -36,10 +37,14 @@
const char kSwitchIdeValueVs2013[] = "vs2013";
const char kSwitchIdeValueVs2015[] = "vs2015";
const char kSwitchIdeValueXcode[] = "xcode";
+const char kSwitchIdeValueJson[] = "json";
const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
const char kSwitchRootTarget[] = "root-target";
const char kSwitchSln[] = "sln";
const char kSwitchWorkspace[] = "workspace";
+const char kSwitchJsonFileName[] = "json-file-name";
+const char kSwitchJsonIdeScript[] = "json-ide-script";
+const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
// Called on worker thread to write the ninja file.
void BackgroundDoWrite(const Target* target) {
@@ -113,7 +118,7 @@
if (generator) {
err += "but this file was not generated by any dependencies of the " +
- target_str + ". The target\nthat generates the file is:\n ";
+ target_str + ". The target\nthat generates the file is:\n ";
err += generator->label().GetUserVisibleName(show_toolchains);
} else {
err += "but no targets in the build generate that file.";
@@ -166,11 +171,12 @@
Err* err) {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
+ bool quiet = command_line->HasSwitch(switches::kQuiet);
base::ElapsedTimer timer;
if (ide == kSwitchIdeValueEclipse) {
bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err);
- if (res && !command_line->HasSwitch(switches::kQuiet)) {
+ if (res && !quiet) {
OutputString("Generating Eclipse settings took " +
base::Int64ToString(timer.Elapsed().InMilliseconds()) +
"ms\n");
@@ -189,7 +195,7 @@
filters = command_line->GetSwitchValueASCII(kSwitchFilters);
bool res = VisualStudioWriter::RunAndWriteFiles(
build_settings, builder, version, sln_name, filters, err);
- if (res && !command_line->HasSwitch(switches::kQuiet)) {
+ if (res && !quiet) {
OutputString("Generating Visual Studio projects took " +
base::Int64ToString(timer.Elapsed().InMilliseconds()) +
"ms\n");
@@ -202,7 +208,7 @@
command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs),
command_line->GetSwitchValueASCII(kSwitchFilters), build_settings,
builder, err);
- if (res && !command_line->HasSwitch(switches::kQuiet)) {
+ if (res && !quiet) {
OutputString("Generating Xcode projects took " +
base::Int64ToString(timer.Elapsed().InMilliseconds()) +
"ms\n");
@@ -214,12 +220,32 @@
root_target = command_line->GetSwitchValueASCII(kSwitchRootTarget);
bool res = QtCreatorWriter::RunAndWriteFile(build_settings, builder, err,
root_target);
- if (res && !command_line->HasSwitch(switches::kQuiet)) {
+ if (res && !quiet) {
OutputString("Generating QtCreator projects took " +
base::Int64ToString(timer.Elapsed().InMilliseconds()) +
"ms\n");
}
return res;
+ } else if (ide == kSwitchIdeValueJson) {
+ std::string file_name =
+ command_line->GetSwitchValueASCII(kSwitchJsonFileName);
+ if (file_name.empty())
+ file_name = "project.json";
+ std::string exec_script =
+ command_line->GetSwitchValueASCII(kSwitchJsonIdeScript);
+ std::string exec_script_extra_args =
+ command_line->GetSwitchValueASCII(kSwitchJsonIdeScriptArgs);
+ std::string filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+
+ bool res = JSONProjectWriter::RunAndWriteFiles(
+ build_settings, builder, file_name, exec_script, exec_script_extra_args,
+ filters, quiet, err);
+ if (res && !quiet) {
+ OutputString("Generating JSON projects took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
}
*err = Err(Location(), "Unknown IDE: " + ide);
@@ -229,8 +255,7 @@
} // namespace
const char kGen[] = "gen";
-const char kGen_HelpShort[] =
- "gen: Generate ninja files.";
+const char kGen_HelpShort[] = "gen: Generate ninja files.";
const char kGen_Help[] =
"gn gen: Generate ninja files.\n"
"\n"
@@ -259,12 +284,13 @@
" \"vs2015\" - Visual Studio 2015 project/solution files.\n"
" \"xcode\" - Xcode workspace/solution files.\n"
" \"qtcreator\" - QtCreator project files.\n"
+ " \"json\" - JSON file containing target information\n"
"\n"
" --filters=<path_prefixes>\n"
" Semicolon-separated list of label patterns used to limit the set\n"
" of generated projects (see \"gn help label_pattern\"). Only\n"
" matching targets and their dependencies will be included in the\n"
- " solution. Only used for Visual Studio and Xcode.\n"
+ " solution. Only used for Visual Studio, Xcode and JSON.\n"
"\n"
"Visual Studio Flags\n"
"\n"
@@ -305,7 +331,27 @@
" properly define includes/defines for each file individually.\n"
" Instead, one set of includes/defines is generated for the entire\n"
" project. This works fairly well but may still result in a few indexer\n"
- " issues here and there.\n";
+ " issues here and there.\n"
+ "\n"
+ "Generic JSON Output\n"
+ "\n"
+ " Dumps target information to JSON file and optionally invokes python\n"
+ " script on generated file. \n"
+ " See comments at the beginning of json_project_writer.cc and\n"
+ " desc_builder.cc for overview of JSON file format.\n"
+ "\n"
+ " --json-file-name=<json_file_name>\n"
+ " Overrides default file name (project.json) of generated JSON file.\n"
+ "\n"
+ " --json-ide-script=<path_to_python_script>\n"
+ " Executes python script after the JSON file is generated.\n"
+ " Path can be project absolute (//), system absolute (/) or\n"
+ " relative, in which case the output directory will be base.\n"
+ " Path to generated JSON file will be first argument when invoking\n"
+ " script.\n"
+ "\n"
+ " --json-ide-script-args=<argument>\n"
+ " Optional second argument that will passed to executed script.\n";
int RunGen(const std::vector<std::string>& args) {
base::ElapsedTimer timer;
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
index a646679..492b74f 100644
--- a/tools/gn/commands.cc
+++ b/tools/gn/commands.cc
@@ -6,6 +6,7 @@
#include "base/command_line.h"
#include "base/strings/string_split.h"
+#include "base/values.h"
#include "tools/gn/builder.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/item.h"
@@ -280,22 +281,20 @@
return item->defined_from()->GetRange().begin().file()->physical_name();
}
-void PrintTargetsAsBuildfiles(bool indent,
- const std::vector<const Target*>& targets) {
+void PrintTargetsAsBuildfiles(const std::vector<const Target*>& targets,
+ base::ListValue* out) {
// Output the set of unique source files.
std::set<std::string> unique_files;
for (const Target* target : targets)
unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
for (const std::string& file : unique_files) {
- if (indent)
- OutputString(" ");
- OutputString(file + "\n");
+ out->AppendString(file);
}
}
-void PrintTargetsAsLabels(bool indent,
- const std::vector<const Target*>& targets) {
+void PrintTargetsAsLabels(const std::vector<const Target*>& targets,
+ base::ListValue* out) {
// Putting the labels into a set automatically sorts them for us.
std::set<Label> unique_labels;
for (auto* target : targets)
@@ -307,16 +306,13 @@
for (const Label& label : unique_labels) {
// Print toolchain only for ones not in the default toolchain.
- if (indent)
- OutputString(" ");
- OutputString(label.GetUserVisibleName(
- label.GetToolchainLabel() != default_tc_label));
- OutputString("\n");
+ out->AppendString(label.GetUserVisibleName(label.GetToolchainLabel() !=
+ default_tc_label));
}
}
-void PrintTargetsAsOutputs(bool indent,
- const std::vector<const Target*>& targets) {
+void PrintTargetsAsOutputs(const std::vector<const Target*>& targets,
+ base::ListValue* out) {
if (targets.empty())
return;
@@ -336,10 +332,7 @@
std::string result = RebasePath(output_as_source.value(),
build_settings->build_dir(),
build_settings->root_path_utf8());
- if (indent)
- OutputString(" ");
- OutputString(result);
- OutputString("\n");
+ out->AppendString(result);
}
}
@@ -490,7 +483,8 @@
return true;
}
-void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
+void FilterAndPrintTargets(std::vector<const Target*>* targets,
+ base::ListValue* out) {
if (targets->empty())
return;
@@ -504,21 +498,40 @@
return;
switch (printing_mode) {
case TARGET_PRINT_BUILDFILE:
- PrintTargetsAsBuildfiles(indent, *targets);
+ PrintTargetsAsBuildfiles(*targets, out);
break;
case TARGET_PRINT_LABEL:
- PrintTargetsAsLabels(indent, *targets);
+ PrintTargetsAsLabels(*targets, out);
break;
case TARGET_PRINT_OUTPUT:
- PrintTargetsAsOutputs(indent, *targets);
+ PrintTargetsAsOutputs(*targets, out);
break;
}
}
+void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
+ base::ListValue tmp;
+ FilterAndPrintTargets(targets, &tmp);
+ for (const auto& value : tmp) {
+ std::string string;
+ value->GetAsString(&string);
+ if (indent)
+ OutputString(" ");
+ OutputString(string);
+ OutputString("\n");
+ }
+}
+
void FilterAndPrintTargetSet(bool indent,
const std::set<const Target*>& targets) {
std::vector<const Target*> target_vector(targets.begin(), targets.end());
FilterAndPrintTargets(indent, &target_vector);
}
+void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
+ base::ListValue* out) {
+ std::vector<const Target*> target_vector(targets.begin(), targets.end());
+ FilterAndPrintTargets(&target_vector, out);
+}
+
} // namespace commands
diff --git a/tools/gn/commands.h b/tools/gn/commands.h
index fdd10fc..01bf0ee 100644
--- a/tools/gn/commands.h
+++ b/tools/gn/commands.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/strings/string_piece.h"
+#include "base/values.h"
#include "tools/gn/target.h"
#include "tools/gn/unique_vector.h"
@@ -179,8 +180,13 @@
//
// The vector will be modified so that only the printed targets will remain.
void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets);
+void FilterAndPrintTargets(std::vector<const Target*>* targets,
+ base::ListValue* out);
+
void FilterAndPrintTargetSet(bool indent,
const std::set<const Target*>& targets);
+void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
+ base::ListValue* out);
// Extra help from command_check.cc
extern const char kNoGnCheck_Help[];
diff --git a/tools/gn/desc_builder.cc b/tools/gn/desc_builder.cc
new file mode 100644
index 0000000..bc8f02a
--- /dev/null
+++ b/tools/gn/desc_builder.cc
@@ -0,0 +1,694 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.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/desc_builder.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/runtime_deps.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/substitution_writer.h"
+#include "tools/gn/variables.h"
+
+// Example structure of Value for single target
+// (not applicable or empty fields will be ommitted depending on target type)
+//
+// target_properties = {
+// "type" : "output_type", // matching Target::GetStringForOutputType
+// "toolchain" : "toolchain_name",
+// "visibility" : [ list of visibility pattern descriptions ],
+// "test_only" : true or false,
+// "check_includes": true or false,
+// "allow_circular_includes_from": [ list of target names ],
+// "sources" : [ list of source files ],
+// "public" : either "*" or [ list of public headers],
+// "inputs" : [ list of inputs for target ],
+// "configs" : [ list of configs for this target ],
+// "public_configs" : [ list of public configs for this taget],
+// "all_dependent_configs", [ list of all dependent configs for this target],
+// "script" : "script for action targets",
+// "args" : [ argument list for action targets ],
+// "depfile : "file name for action input dependencies",
+// "outputs" : [ list of target outputs ],
+// "arflags", "asmflags", "cflags", "cflags_c",
+// "clfags_cc", "cflags_objc", "clfags_objcc" : [ list of flags],
+// "defines" : [ list of preprocessor definitions ],
+// "include_dirs" : [ list of include directories ],
+// "precompiled_header" : "name of precompiled header file",
+// "precompiled_source" : "path to precompiled source",
+// "deps : [ list of target dependencies ],
+// "libs" : [ list of libraries ],
+// "lib_dirs" : [ list of library directories ]
+// }
+//
+// Optionally, if "what" is specified while generating description, two other
+// properties can be requested that are not included by default
+//
+// "runtime_deps" : [list of computed runtime dependencies]
+// "source_outputs" : {
+// "source_file x" : [ list of outputs for source file x ]
+// "source_file y" : [ list of outputs for source file y ]
+// ...
+// }
+
+namespace {
+
+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;
+ }
+ }
+#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);
+}
+
+// Common functionality for target and config description builder
+class BaseDescBuilder {
+ public:
+ typedef std::unique_ptr<base::Value> ValuePtr;
+
+ BaseDescBuilder(const std::set<std::string>& what,
+ bool all,
+ bool tree,
+ bool blame)
+ : what_(what), all_(all), tree_(tree), blame_(blame) {}
+
+ protected:
+ virtual Label GetToolchainLabel() const = 0;
+
+ bool what(const std::string& w) const {
+ return what_.empty() || what_.find(w) != what_.end();
+ }
+
+ template <typename T>
+ ValuePtr RenderValue(const std::vector<T>& vector) {
+ auto res = base::WrapUnique(new base::ListValue());
+ for (const auto& v : vector)
+ res->Append(RenderValue(v));
+
+ return std::move(res);
+ }
+
+ ValuePtr RenderValue(const std::string& s, bool optional = false) {
+ return (s.empty() && optional) ? base::Value::CreateNullValue()
+ : ValuePtr(new base::StringValue(s));
+ }
+
+ ValuePtr RenderValue(const SourceDir& d) {
+ return d.is_null() ? base::Value::CreateNullValue()
+ : ValuePtr(new base::StringValue(FormatSourceDir(d)));
+ }
+
+ ValuePtr RenderValue(const SourceFile& f) {
+ return f.is_null() ? base::Value::CreateNullValue()
+ : ValuePtr(new base::StringValue(f.value()));
+ }
+
+ ValuePtr RenderValue(const LibFile& lib) {
+ if (lib.is_source_file())
+ return RenderValue(lib.source_file());
+ return RenderValue(lib.value());
+ }
+
+ template <class VectorType>
+ void FillInConfigVector(base::ListValue* out,
+ const VectorType& configs,
+ int indent = 0) {
+ for (const auto& config : configs) {
+ std::string name(indent * 2, ' ');
+ name.append(config.label.GetUserVisibleName(GetToolchainLabel()));
+ out->AppendString(name);
+ if (tree_)
+ FillInConfigVector(out, config.ptr->configs(), indent + 1);
+ }
+ }
+
+ void FillInPrecompiledHeader(base::DictionaryValue* out,
+ const ConfigValues& values) {
+ if (what(variables::kPrecompiledHeader) &&
+ !values.precompiled_header().empty()) {
+ out->Set(variables::kPrecompiledHeader,
+ RenderValue(values.precompiled_header(), true));
+ }
+ if (what(variables::kPrecompiledSource) &&
+ !values.precompiled_source().is_null()) {
+ out->Set(variables::kPrecompiledSource,
+ RenderValue(values.precompiled_source()));
+ }
+ }
+
+ std::set<std::string> what_;
+ bool all_;
+ bool tree_;
+ bool blame_;
+};
+
+class ConfigDescBuilder : public BaseDescBuilder {
+ public:
+ ConfigDescBuilder(const Config* config, const std::set<std::string>& what)
+ : BaseDescBuilder(what, false, false, false), config_(config) {}
+
+ std::unique_ptr<base::DictionaryValue> BuildDescription() {
+ auto res = base::WrapUnique(new base::DictionaryValue());
+ const ConfigValues& values = config_->resolved_values();
+
+ if (what_.empty())
+ res->SetString(
+ "toolchain",
+ config_->label().GetToolchainLabel().GetUserVisibleName(false));
+
+ if (what(variables::kConfigs) && !config_->configs().empty()) {
+ auto configs = base::WrapUnique(new base::ListValue());
+ FillInConfigVector(configs.get(), config_->configs().vector());
+ res->Set(variables::kConfigs, std::move(configs));
+ }
+
+#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
+ if (what(#name)) { \
+ ValuePtr ptr = \
+ render_config_value_array<type>(values, &ConfigValues::name); \
+ if (ptr) { \
+ res->Set(#name, std::move(ptr)); \
+ } \
+ }
+ 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)
+
+#undef CONFIG_VALUE_ARRAY_HANDLER
+
+ FillInPrecompiledHeader(res.get(), values);
+
+ return res;
+ }
+
+ protected:
+ Label GetToolchainLabel() const override {
+ return config_->label().GetToolchainLabel();
+ }
+
+ private:
+ template <typename T>
+ ValuePtr render_config_value_array(
+ const ConfigValues& values,
+ const std::vector<T>& (ConfigValues::*getter)() const) {
+ auto res = base::WrapUnique(new base::ListValue());
+
+ for (const T& cur : (values.*getter)())
+ res->Append(RenderValue(cur));
+
+ return res->empty() ? nullptr : std::move(res);
+ }
+
+ const Config* config_;
+};
+
+class TargetDescBuilder : public BaseDescBuilder {
+ public:
+ TargetDescBuilder(const Target* target,
+ const std::set<std::string>& what,
+ bool all,
+ bool tree,
+ bool blame)
+ : BaseDescBuilder(what, all, tree, blame), target_(target) {}
+
+ std::unique_ptr<base::DictionaryValue> BuildDescription() {
+ auto res = base::WrapUnique(new base::DictionaryValue());
+ bool is_binary_output = target_->IsBinary();
+
+ if (what_.empty()) {
+ res->SetString("type",
+ Target::GetStringForOutputType(target_->output_type()));
+ res->SetString(
+ "toolchain",
+ target_->label().GetToolchainLabel().GetUserVisibleName(false));
+ }
+
+ // General target meta variables.
+ if (what(variables::kVisibility))
+ res->Set(variables::kVisibility, target_->visibility().AsValue());
+
+ if (what(variables::kTestonly))
+ res->SetBoolean(variables::kTestonly, target_->testonly());
+
+ if (is_binary_output) {
+ if (what(variables::kCheckIncludes))
+ res->SetBoolean(variables::kCheckIncludes, target_->check_includes());
+
+ if (what(variables::kAllowCircularIncludesFrom)) {
+ auto labels = base::WrapUnique(new base::ListValue());
+ for (const auto& cur : target_->allow_circular_includes_from())
+ labels->AppendString(cur.GetUserVisibleName(GetToolchainLabel()));
+
+ res->Set(variables::kAllowCircularIncludesFrom, std::move(labels));
+ }
+ }
+
+ if (what(variables::kSources) && !target_->sources().empty())
+ res->Set(variables::kSources, RenderValue(target_->sources()));
+
+ if (what(variables::kOutputName) && !target_->output_name().empty())
+ res->SetString(variables::kOutputName, target_->output_name());
+
+ if (what(variables::kOutputDir) && !target_->output_dir().is_null())
+ res->Set(variables::kOutputDir, RenderValue(target_->output_dir()));
+
+ if (what(variables::kOutputExtension) && target_->output_extension_set())
+ res->SetString(variables::kOutputExtension, target_->output_extension());
+
+ if (what(variables::kPublic)) {
+ if (target_->all_headers_public())
+ res->SetString(variables::kPublic, "*");
+ else
+ res->Set(variables::kPublic, RenderValue(target_->public_headers()));
+ }
+
+ if (what(variables::kInputs) && !target_->inputs().empty())
+ res->Set(variables::kInputs, RenderValue(target_->inputs()));
+
+ if (is_binary_output && what(variables::kConfigs) &&
+ !target_->configs().empty()) {
+ auto configs = base::WrapUnique(new base::ListValue());
+ FillInConfigVector(configs.get(), target_->configs().vector());
+ res->Set(variables::kConfigs, std::move(configs));
+ }
+
+ if (what(variables::kPublicConfigs) && !target_->public_configs().empty()) {
+ auto configs = base::WrapUnique(new base::ListValue());
+ FillInConfigVector(configs.get(), target_->public_configs());
+ res->Set(variables::kPublicConfigs, std::move(configs));
+ }
+
+ if (what(variables::kAllDependentConfigs) &&
+ !target_->all_dependent_configs().empty()) {
+ auto configs = base::WrapUnique(new base::ListValue());
+ FillInConfigVector(configs.get(), target_->all_dependent_configs());
+ res->Set(variables::kAllDependentConfigs, std::move(configs));
+ }
+
+ // Action
+ if (target_->output_type() == Target::ACTION ||
+ target_->output_type() == Target::ACTION_FOREACH) {
+ if (what(variables::kScript))
+ res->SetString(variables::kScript,
+ target_->action_values().script().value());
+
+ if (what(variables::kArgs)) {
+ auto args = base::WrapUnique(new base::ListValue());
+ for (const auto& elem : target_->action_values().args().list())
+ args->AppendString(elem.AsString());
+
+ res->Set(variables::kArgs, std::move(args));
+ }
+ if (what(variables::kDepfile) &&
+ !target_->action_values().depfile().empty()) {
+ res->SetString(variables::kDepfile,
+ target_->action_values().depfile().AsString());
+ }
+ }
+
+ if (target_->output_type() != Target::SOURCE_SET &&
+ target_->output_type() != Target::GROUP &&
+ target_->output_type() != Target::BUNDLE_DATA) {
+ if (what(variables::kOutputs))
+ FillInOutputs(res.get());
+ }
+
+ // Source outputs are only included when specifically asked for it
+ if (what_.find("source_outputs") != what_.end())
+ FillInSourceOutputs(res.get());
+
+ if (target_->output_type() == Target::CREATE_BUNDLE && what("bundle_data"))
+ FillInBundle(res.get());
+
+ if (is_binary_output) {
+#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
+ if (what(#name)) { \
+ ValuePtr ptr = RenderConfigValues<type>(&ConfigValues::name); \
+ if (ptr) { \
+ res->Set(#name, std::move(ptr)); \
+ } \
+ }
+ 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)
+#undef CONFIG_VALUE_ARRAY_HANDLER
+
+ // Libs and lib_dirs are handled specially below.
+
+ FillInPrecompiledHeader(res.get(), target_->config_values());
+ }
+
+ if (what(variables::kDeps))
+ res->Set(variables::kDeps, RenderDeps());
+
+ // Runtime deps are special, print only when explicitly asked for and not in
+ // overview mode.
+ if (what_.find("runtime_deps") != what_.end())
+ res->Set("runtime_deps", RenderRuntimeDeps());
+
+ // 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.
+
+ // Libs can be part of any target and get recursively pushed up the chain,
+ // so display them regardless of target type.
+ if (what(variables::kLibs)) {
+ const OrderedSet<LibFile>& all_libs = target_->all_libs();
+ if (!all_libs.empty()) {
+ auto libs = base::WrapUnique(new base::ListValue());
+ for (size_t i = 0; i < all_libs.size(); i++)
+ libs->AppendString(all_libs[i].value());
+ res->Set(variables::kLibs, std::move(libs));
+ }
+ }
+
+ if (what(variables::kLibDirs)) {
+ const OrderedSet<SourceDir>& all_lib_dirs = target_->all_lib_dirs();
+ if (!all_lib_dirs.empty()) {
+ auto lib_dirs = base::WrapUnique(new base::ListValue());
+ for (size_t i = 0; i < all_lib_dirs.size(); i++)
+ lib_dirs->AppendString(FormatSourceDir(all_lib_dirs[i]));
+ res->Set(variables::kLibDirs, std::move(lib_dirs));
+ }
+ }
+
+ return res;
+ }
+
+ private:
+ // 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(base::ListValue* out,
+ const Target* target,
+ 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;
+ std::string str =
+ indent + cur_dep->label().GetUserVisibleName(GetToolchainLabel());
+
+ 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())
+ str += "...";
+ }
+ }
+
+ out->AppendString(str);
+
+ if (print_children)
+ RecursivePrintDeps(out, cur_dep, seen_targets, indent_level + 1);
+ }
+ }
+
+ ValuePtr RenderDeps() {
+ auto res = base::WrapUnique(new base::ListValue());
+
+ // Tree mode is separate.
+ if (tree_) {
+ if (all_) {
+ // Show all tree deps with no eliding.
+ RecursivePrintDeps(res.get(), target_, nullptr, 0);
+ } else {
+ // Don't recurse into duplicates.
+ std::set<const Target*> seen_targets;
+ RecursivePrintDeps(res.get(), target_, &seen_targets, 0);
+ }
+ } else { // not tree
+
+ // Collect the deps to display.
+ if (all_) {
+ // Show all dependencies.
+ std::set<const Target*> all_deps;
+ RecursiveCollectChildDeps(target_, &all_deps);
+ commands::FilterAndPrintTargetSet(all_deps, res.get());
+ } else {
+ // Show direct dependencies only.
+ std::vector<const Target*> deps;
+ for (const auto& pair : target_->GetDeps(Target::DEPS_ALL))
+ deps.push_back(pair.ptr);
+ std::sort(deps.begin(), deps.end());
+ commands::FilterAndPrintTargets(&deps, res.get());
+ }
+ }
+
+ return std::move(res);
+ }
+
+ ValuePtr RenderRuntimeDeps() {
+ auto res = base::WrapUnique(new base::ListValue());
+
+ const Target* previous_from = NULL;
+ for (const auto& pair : ComputeRuntimeDeps(target_)) {
+ std::string str;
+ if (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) {
+ str = " ";
+ } else {
+ previous_from = pair.second;
+ res->AppendString(
+ str + "From " +
+ pair.second->label().GetUserVisibleName(GetToolchainLabel()));
+ str = " ";
+ }
+ }
+
+ res->AppendString(str + pair.first.value());
+ }
+
+ return std::move(res);
+ }
+
+ void FillInSourceOutputs(base::DictionaryValue* res) {
+ auto dict = base::WrapUnique(new base::DictionaryValue());
+ for (const auto& source : target_->sources()) {
+ std::vector<OutputFile> outputs;
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (target_->GetOutputFilesForSource(source, &tool_type, &outputs)) {
+ auto list = base::WrapUnique(new base::ListValue());
+ for (const auto& output : outputs)
+ list->AppendString(output.value());
+
+ dict->SetWithoutPathExpansion(source.value(), std::move(list));
+ }
+ }
+ res->Set("source_outputs", std::move(dict));
+ }
+
+ void FillInBundle(base::DictionaryValue* res) {
+ auto data = base::WrapUnique(new base::DictionaryValue());
+ const BundleData& bundle_data = target_->bundle_data();
+ const Settings* settings = target_->settings();
+ BundleData::SourceFiles sources;
+ bundle_data.GetSourceFiles(&sources);
+ data->Set("source_files", RenderValue(sources));
+ data->SetString("root_dir_output",
+ bundle_data.GetBundleRootDirOutput(settings).value());
+ data->Set("root_dir", RenderValue(bundle_data.root_dir()));
+ data->Set("resources_dir", RenderValue(bundle_data.resources_dir()));
+ data->Set("executable_dir", RenderValue(bundle_data.executable_dir()));
+ data->Set("plugins_dir", RenderValue(bundle_data.plugins_dir()));
+ data->SetString("product_type", bundle_data.product_type());
+
+ auto deps = base::WrapUnique(new base::ListValue());
+ for (const auto* dep : bundle_data.bundle_deps())
+ deps->AppendString(dep->label().GetUserVisibleName(GetToolchainLabel()));
+
+ data->Set("deps", std::move(deps));
+ res->Set("bundle_data", std::move(data));
+ }
+
+ void FillInOutputs(base::DictionaryValue* res) {
+ if (target_->output_type() == Target::ACTION) {
+ auto list = base::WrapUnique(new base::ListValue());
+ for (const auto& elem : target_->action_values().outputs().list())
+ list->AppendString(elem.AsString());
+
+ res->Set(variables::kOutputs, std::move(list));
+ } else if (target_->output_type() == Target::CREATE_BUNDLE) {
+ std::vector<SourceFile> output_files;
+ target_->bundle_data().GetOutputsAsSourceFiles(target_->settings(),
+ &output_files);
+ res->Set(variables::kOutputs, RenderValue(output_files));
+ } else if (target_->output_type() == Target::ACTION_FOREACH ||
+ target_->output_type() == Target::COPY_FILES) {
+ const SubstitutionList& outputs = target_->action_values().outputs();
+ if (!outputs.required_types().empty()) {
+ auto patterns = base::WrapUnique(new base::ListValue());
+ for (const auto& elem : outputs.list())
+ patterns->AppendString(elem.AsString());
+
+ res->Set("output_patterns", std::move(patterns));
+ }
+ std::vector<SourceFile> output_files;
+ SubstitutionWriter::ApplyListToSources(target_->settings(), outputs,
+ target_->sources(), &output_files);
+ res->Set(variables::kOutputs, RenderValue(output_files));
+ } 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()));
+
+ res->Set(variables::kOutputs, RenderValue(output_files_as_source_file));
+ }
+ }
+
+ // Writes a given config value type to the string, optionally with
+ // attribution.
+ // This should match RecursiveTargetConfigToStream in the order it traverses.
+ template <class T>
+ ValuePtr RenderConfigValues(const std::vector<T>& (ConfigValues::*getter)()
+ const) {
+ auto res = base::WrapUnique(new base::ListValue());
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ const std::vector<T>& vec = (iter.cur().*getter)();
+
+ if (vec.empty())
+ continue;
+
+ if (blame_) {
+ const Config* config = iter.GetCurrentConfig();
+ if (config) {
+ // Source of this value is a config.
+ std::string from =
+ "From " + config->label().GetUserVisibleName(false);
+ res->AppendString(from);
+ if (iter.origin()) {
+ Location location = iter.origin()->GetRange().begin();
+ from = " (Added by " + location.file()->name().value() + ":" +
+ base::IntToString(location.line_number()) + ")";
+ res->AppendString(from);
+ }
+ } else {
+ // Source of this value is the target itself.
+ std::string from =
+ "From " + target_->label().GetUserVisibleName(false);
+ res->AppendString(from);
+ }
+ }
+
+ for (const T& val : vec) {
+ ValuePtr rendered = RenderValue(val);
+ std::string str;
+ // Indent string values in blame mode
+ if (blame_ && rendered->GetAsString(&str)) {
+ str = " " + str;
+ rendered = base::WrapUnique(new base::StringValue(str));
+ }
+ res->Append(std::move(rendered));
+ }
+ }
+ return res->empty() ? nullptr : std::move(res);
+ }
+
+ Label GetToolchainLabel() const override {
+ return target_->label().GetToolchainLabel();
+ }
+
+ const Target* target_;
+};
+
+} // namespace
+
+std::unique_ptr<base::DictionaryValue> DescBuilder::DescriptionForTarget(
+ const Target* target,
+ const std::string& what,
+ bool all,
+ bool tree,
+ bool blame) {
+ std::set<std::string> w;
+ if (!what.empty())
+ w.insert(what);
+ TargetDescBuilder b(target, w, all, tree, blame);
+ return b.BuildDescription();
+}
+
+std::unique_ptr<base::DictionaryValue> DescBuilder::DescriptionForConfig(
+ const Config* config,
+ const std::string& what) {
+ std::set<std::string> w;
+ if (!what.empty())
+ w.insert(what);
+ ConfigDescBuilder b(config, w);
+ return b.BuildDescription();
+}
diff --git a/tools/gn/desc_builder.h b/tools/gn/desc_builder.h
new file mode 100644
index 0000000..1f0c395
--- /dev/null
+++ b/tools/gn/desc_builder.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_DESC_BUILDER_H_
+#define TOOLS_GN_DESC_BUILDER_H_
+
+#include "base/values.h"
+#include "tools/gn/target.h"
+
+class DescBuilder {
+ public:
+ // Creates Dictionary representation for given target
+ static std::unique_ptr<base::DictionaryValue> DescriptionForTarget(
+ const Target* target,
+ const std::string& what,
+ bool all,
+ bool tree,
+ bool blame);
+
+ // Creates Dictionary representation for given config
+ static std::unique_ptr<base::DictionaryValue> DescriptionForConfig(
+ const Config* config,
+ const std::string& what);
+};
+
+#endif
diff --git a/tools/gn/json_project_writer.cc b/tools/gn/json_project_writer.cc
new file mode 100644
index 0000000..272a288
--- /dev/null
+++ b/tools/gn/json_project_writer.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/json_project_writer.h"
+
+#include <iostream>
+
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/deps_iterator.h"
+#include "tools/gn/desc_builder.h"
+#include "tools/gn/exec_process.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/settings.h"
+
+// Structure of JSON output file
+// {
+// "build_settings" = {
+// "root_path" : "absolute path of project root",
+// "build_dir" : "build directory (project relative)",
+// "default_toolchain" : "name of default toolchain"
+// }
+// "targets" = {
+// "target x name" : { target x properties },
+// "target y name" : { target y properties },
+// ...
+// }
+// }
+// See desc_builder.cc for overview of target properties
+
+namespace {
+
+void AddTargetDependencies(const Target* target,
+ std::set<const Target*>* deps) {
+ for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
+ if (deps->find(pair.ptr) == deps->end()) {
+ deps->insert(pair.ptr);
+ AddTargetDependencies(pair.ptr, deps);
+ }
+ }
+}
+
+// Filters targets according to filter string; Will also recursively
+// add dependent targets.
+bool FilterTargets(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets,
+ std::vector<const Target*>* targets,
+ const std::string& dir_filter_string,
+ Err* err) {
+ if (dir_filter_string.empty()) {
+ *targets = all_targets;
+ } else {
+ targets->reserve(all_targets.size());
+ std::vector<LabelPattern> filters;
+ if (!commands::FilterPatternsFromString(build_settings, dir_filter_string,
+ &filters, err)) {
+ return false;
+ }
+ commands::FilterTargetsByPatterns(all_targets, filters, targets);
+
+ std::set<const Target*> target_set(targets->begin(), targets->end());
+ for (const auto* target : *targets)
+ AddTargetDependencies(target, &target_set);
+
+ targets->clear();
+ targets->insert(targets->end(), target_set.begin(), target_set.end());
+ }
+
+ // Sort the list of targets per-label to get a consistent ordering of them
+ // in the generated project (and thus stability of the file generated).
+ std::sort(targets->begin(), targets->end(),
+ [](const Target* a, const Target* b) {
+ return a->label().name() < b->label().name();
+ });
+
+ return true;
+}
+
+std::string RenderJSON(const BuildSettings* build_settings,
+ const Builder* builder,
+ std::vector<const Target*>& all_targets) {
+ Label default_toolchain_label;
+
+ auto targets = base::WrapUnique(new base::DictionaryValue());
+ for (const auto* target : all_targets) {
+ if (default_toolchain_label.is_null())
+ default_toolchain_label = target->settings()->default_toolchain_label();
+ auto description =
+ DescBuilder::DescriptionForTarget(target, "", false, false, false);
+ // Outputs need to be asked for separately.
+ auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs",
+ false, false, false);
+ base::DictionaryValue* outputs_value = nullptr;
+ if (outputs->GetDictionary("source_outputs", &outputs_value) &&
+ !outputs_value->empty()) {
+ description->MergeDictionary(outputs.get());
+ }
+ targets->Set(target->label().GetUserVisibleName(default_toolchain_label),
+ std::move(description));
+ }
+
+ auto settings = base::WrapUnique(new base::DictionaryValue());
+ settings->SetString("root_path", build_settings->root_path_utf8());
+ settings->SetString("build_dir", build_settings->build_dir().value());
+ settings->SetString("default_toolchain",
+ default_toolchain_label.GetUserVisibleName(false));
+
+ auto output = base::WrapUnique(new base::DictionaryValue());
+ output->Set("targets", std::move(targets));
+ output->Set("build_settings", std::move(settings));
+
+ std::string s;
+ base::JSONWriter::WriteWithOptions(
+ *output.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
+ return s;
+}
+
+bool InvokePython(const BuildSettings* build_settings,
+ const base::FilePath& python_script_path,
+ const std::string& python_script_extra_args,
+ const base::FilePath& output_path,
+ bool quiet,
+ Err* err) {
+ const base::FilePath& python_path = build_settings->python_path();
+ base::CommandLine cmdline(python_path);
+ cmdline.AppendArg("--");
+ cmdline.AppendArgPath(python_script_path);
+ cmdline.AppendArgPath(output_path);
+ if (!python_script_extra_args.empty()) {
+ cmdline.AppendArg(python_script_extra_args);
+ }
+ base::FilePath startup_dir =
+ build_settings->GetFullPath(build_settings->build_dir());
+
+ std::string output;
+ std::string stderr_output;
+
+ int exit_code = 0;
+ if (!internal::ExecProcess(cmdline, startup_dir, &output, &stderr_output,
+ &exit_code)) {
+ *err =
+ Err(Location(), "Could not execute python.",
+ "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
+ return false;
+ }
+
+ if (!quiet) {
+ std::cout << output;
+ std::cerr << stderr_output;
+ }
+
+ if (exit_code != 0) {
+ *err = Err(Location(), "Python has quit with exit code " +
+ base::IntToString(exit_code) + ".");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool JSONProjectWriter::RunAndWriteFiles(
+ const BuildSettings* build_settings,
+ const Builder* builder,
+ const std::string& file_name,
+ const std::string& exec_script,
+ const std::string& exec_script_extra_args,
+ const std::string& dir_filter_string,
+ bool quiet,
+ Err* err) {
+ SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
+ Value(nullptr, file_name), err);
+ if (output_file.is_null()) {
+ return false;
+ }
+
+ base::FilePath output_path = build_settings->GetFullPath(output_file);
+
+ std::vector<const Target*> all_targets = builder->GetAllResolvedTargets();
+ std::vector<const Target*> targets;
+ if (!FilterTargets(build_settings, all_targets, &targets, dir_filter_string,
+ err)) {
+ return false;
+ }
+
+ std::string json = RenderJSON(build_settings, builder, targets);
+ if (!ContentsEqual(output_path, json)) {
+ if (!WriteFileIfChanged(output_path, json, err)) {
+ return false;
+ }
+
+ if (!exec_script.empty()) {
+ SourceFile script_file;
+ if (exec_script[0] != '/') {
+ // Relative path, assume the base is in build_dir.
+ script_file = build_settings->build_dir().ResolveRelativeFile(
+ Value(nullptr, exec_script), err);
+ if (script_file.is_null()) {
+ return false;
+ }
+ } else {
+ script_file = SourceFile(exec_script);
+ }
+ base::FilePath script_path = build_settings->GetFullPath(script_file);
+ return InvokePython(build_settings, script_path, exec_script_extra_args,
+ output_path, quiet, err);
+ }
+ }
+
+ return true;
+}
diff --git a/tools/gn/json_project_writer.h b/tools/gn/json_project_writer.h
new file mode 100644
index 0000000..e69dc3a
--- /dev/null
+++ b/tools/gn/json_project_writer.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_JSON_WRITER_H_
+#define TOOLS_GN_JSON_WRITER_H_
+
+#include "tools/gn/err.h"
+#include "tools/gn/target.h"
+
+class Builder;
+class BuildSettings;
+
+class JSONProjectWriter {
+ public:
+ static bool RunAndWriteFiles(const BuildSettings* build_setting,
+ const Builder* builder,
+ const std::string& file_name,
+ const std::string& exec_script,
+ const std::string& exec_script_extra_args,
+ const std::string& dir_filter_string,
+ bool quiet,
+ Err* err);
+};
+
+#endif
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index aa1fcf6..9851035 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -14,6 +14,7 @@
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/deps_iterator.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/source_file_type.h"
#include "tools/gn/substitution_writer.h"
@@ -215,29 +216,29 @@
const char* Target::GetStringForOutputType(OutputType type) {
switch (type) {
case UNKNOWN:
- return "Unknown";
+ return "unknown";
case GROUP:
- return "Group";
+ return functions::kGroup;
case EXECUTABLE:
- return "Executable";
+ return functions::kExecutable;
case LOADABLE_MODULE:
- return "Loadable module";
+ return functions::kLoadableModule;
case SHARED_LIBRARY:
- return "Shared library";
+ return functions::kSharedLibrary;
case STATIC_LIBRARY:
- return "Static library";
+ return functions::kStaticLibrary;
case SOURCE_SET:
- return "Source set";
+ return functions::kSourceSet;
case COPY_FILES:
- return "Copy";
+ return functions::kCopy;
case ACTION:
- return "Action";
+ return functions::kAction;
case ACTION_FOREACH:
- return "ActionForEach";
+ return functions::kActionForEach;
case BUNDLE_DATA:
- return "Bundle data";
+ return functions::kBundleData;
case CREATE_BUNDLE:
- return "Create bundle";
+ return functions::kCreateBundle;
default:
return "";
}
diff --git a/tools/gn/visibility.cc b/tools/gn/visibility.cc
index a61a5cd..b32586c 100644
--- a/tools/gn/visibility.cc
+++ b/tools/gn/visibility.cc
@@ -4,8 +4,10 @@
#include "tools/gn/visibility.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
+#include "base/values.h"
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/item.h"
@@ -83,6 +85,14 @@
return result;
}
+std::unique_ptr<base::Value> Visibility::AsValue() const {
+ auto* res = new base::ListValue();
+ for (const auto& pattern : patterns_)
+ res->AppendString(pattern.Describe());
+
+ return WrapUnique(res);
+}
+
// static
bool Visibility::CheckItemVisibility(const Item* from,
const Item* to,
@@ -93,7 +103,7 @@
"The item " + from->label().GetUserVisibleName(false) + "\n"
"can not depend on " + to_label + "\n"
"because it is not in " + to_label + "'s visibility list: " +
- to->visibility().Describe(0, true));
+ to->visibility().Describe(0, true));
return false;
}
return true;
diff --git a/tools/gn/visibility.h b/tools/gn/visibility.h
index d6d7f1f..4228f9d 100644
--- a/tools/gn/visibility.h
+++ b/tools/gn/visibility.h
@@ -5,12 +5,17 @@
#ifndef TOOLS_GN_VISIBILITY_H_
#define TOOLS_GN_VISIBILITY_H_
+#include <memory>
#include <vector>
#include "base/macros.h"
#include "tools/gn/label_pattern.h"
#include "tools/gn/source_dir.h"
+namespace base {
+class Value;
+}
+
class Err;
class Item;
class Label;
@@ -43,6 +48,9 @@
// result will end in a newline.
std::string Describe(int indent, bool include_brackets) const;
+ // Returns value representation of this visibility
+ std::unique_ptr<base::Value> AsValue() const;
+
// Helper function to check visibility between the given two items. If
// to is invisible to from, returns false and sets the error.
static bool CheckItemVisibility(const Item* from, const Item* to, Err* err);