[gn] Updating args --list to emit json
Updates the gn args --list command with an additional --json flag, which
emits the same information as the normal --list command but in json
format.
Intended to be used to generate structured documentation for GN build
arguments. The --markdown output is nice, but we want to do more
processing than that to get it into good shape to integrate with our docs.
Also updates the GetContextForValue function to get the file and the
line number separately, rather than as one string, and updates the
callsites with the new parameters.
Change-Id: Id1240c37f835a18a12a6e4f5be58b9f8c7a663d1
Reviewed-on: https://chromium-review.googlesource.com/978423
Commit-Queue: Julie Hockett <juliehockett@google.com>
Reviewed-by: Roland McGrath <mcgrathr@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#547759}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 8ca5c09bb15f1584155c9e295ff0725be5990d9a
diff --git a/tools/gn/command_args.cc b/tools/gn/command_args.cc
index bb40a6e..237aa0d 100644
--- a/tools/gn/command_args.cc
+++ b/tools/gn/command_args.cc
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -37,6 +38,7 @@
const char kSwitchList[] = "list";
const char kSwitchShort[] = "short";
const char kSwitchOverridesOnly[] = "overrides-only";
+const char kSwitchJson[] = "json";
bool DoesLineBeginWithComment(const base::StringPiece& line) {
// Skip whitespace.
@@ -76,14 +78,15 @@
// Tries to find the comment before the setting of the given value.
void GetContextForValue(const Value& value,
std::string* location_str,
+ int* line_no,
std::string* comment) {
Location location = value.origin()->GetRange().begin();
const InputFile* file = location.file();
if (!file)
return;
- *location_str = file->name().value() + ":" +
- base::IntToString(location.line_number());
+ *location_str = file->name().value();
+ *line_no = location.line_number();
const std::string& data = file->contents();
size_t line_off =
@@ -112,9 +115,11 @@
void PrintDefaultValueInfo(base::StringPiece name, const Value& value) {
OutputString(value.ToString(true) + "\n");
if (value.origin()) {
+ int line_no;
std::string location, comment;
- GetContextForValue(value, &location, &comment);
- OutputString(" From " + location + "\n");
+ GetContextForValue(value, &location, &line_no, &comment);
+ OutputString(" From " + location + ":" + base::IntToString(line_no) +
+ "\n");
if (!comment.empty())
OutputString("\n" + comment);
} else {
@@ -134,9 +139,11 @@
OutputString(" Current value = " + val.override_value.ToString(true) +
"\n");
if (val.override_value.origin()) {
+ int line_no;
std::string location, comment;
- GetContextForValue(val.override_value, &location, &comment);
- OutputString(" From " + location + "\n");
+ GetContextForValue(val.override_value, &location, &line_no, &comment);
+ OutputString(" From " + location + ":" + base::IntToString(line_no)
+ + "\n");
}
OutputString(" Overridden from the default = ");
PrintDefaultValueInfo(name, val.default_value);
@@ -147,6 +154,57 @@
}
}
+void BuildArgJson(base::Value& dict,
+ const base::StringPiece& name,
+ const Args::ValueWithOverride& arg,
+ bool short_only) {
+ assert(dict.is_dict());
+
+ // Fetch argument name.
+ dict.SetKey("name", base::Value(name));
+
+ // Fetch overridden value inforrmation (if present).
+ if (arg.has_override) {
+ base::DictionaryValue override_dict;
+ override_dict.SetKey("value",
+ base::Value(arg.override_value.ToString(true)));
+ if (arg.override_value.origin() && !short_only) {
+ int line_no;
+ std::string location, comment;
+ GetContextForValue(arg.override_value, &location, &line_no, &comment);
+ // Omit file and line if set with --args (i.e. no file)
+ if (!location.empty()) {
+ override_dict.SetKey("file", base::Value(location));
+ override_dict.SetKey("line", base::Value(line_no));
+ }
+ }
+ dict.SetKey("current", std::move(override_dict));
+ }
+
+ // Fetch default value information, and comment (if present).
+ base::DictionaryValue default_dict;
+ std::string comment;
+ default_dict.SetKey("value", base::Value(arg.default_value.ToString(true)));
+ if (arg.default_value.origin() && !short_only) {
+ int line_no;
+ std::string location;
+ GetContextForValue(arg.default_value, &location, &line_no, &comment);
+ // Only emit file and line if the value is overridden.
+ if (arg.has_override) {
+ default_dict.SetKey("file", base::Value(location));
+ default_dict.SetKey("line", base::Value(line_no));
+ }
+ }
+ dict.SetKey("default", std::move(default_dict));
+ if (!comment.empty() && !short_only)
+ dict.SetKey("comment", base::Value(comment));
+
+ std::string s;
+ base::JSONWriter::WriteWithOptions(
+ dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
+ OutputString(s);
+}
+
int ListArgs(const std::string& build_dir) {
Setup* setup = new Setup;
if (!setup->DoSetup(build_dir, false) || !setup->Run())
@@ -175,8 +233,26 @@
// Cache this to avoid looking it up for each |arg| in the loops below.
const bool overrides_only =
base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchOverridesOnly);
+ const bool short_only =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort);
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort)) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchJson)) {
+ // Convert all args to JSON, serialize and print them
+ auto list = std::make_unique<base::ListValue>();
+ for (const auto& arg : args) {
+ if (overrides_only && !arg.second.has_override)
+ continue;
+ list->GetList().emplace_back(base::DictionaryValue());
+ BuildArgJson(list->GetList().back(), arg.first, arg.second, short_only);
+ }
+ std::string s;
+ base::JSONWriter::WriteWithOptions(
+ *list.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
+ OutputString(s);
+ return 0;
+ }
+
+ if (short_only) {
// Short <key>=<current_value> output.
for (const auto& arg : args) {
if (overrides_only && !arg.second.has_override)
@@ -354,7 +430,7 @@
Note: you can edit the build args manually by editing the file "args.gn"
in the build directory and then running "gn gen <out_dir>".
- gn args <out_dir> --list[=<exact_arg>] [--short] [--overrides-only]
+ gn args <out_dir> --list[=<exact_arg>] [--short] [--overrides-only] [--json]
Lists all build arguments available in the current configuration, or, if
an exact_arg is specified for the list flag, just that one build
argument.
@@ -370,6 +446,25 @@
arguments that have been overridden (i.e. non-default arguments) will
be printed. Overrides come from the <out_dir>/args.gn file and //.gn
+ If --json is specified, the output will be emitted in json format.
+ JSON schema for output:
+ [
+ {
+ "name": variable_name,
+ "current": {
+ "value": overridden_value,
+ "file": file_name,
+ "line": line_no
+ },
+ "default": {
+ "value": default_value,
+ "file": file_name,
+ "line": line_no
+ },
+ "comment": comment_string
+ },
+ ...
+ ]
Examples