summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rwxr-xr-xconfigure.py2
-rw-r--r--src/json.cc53
-rw-r--r--src/json.h26
-rw-r--r--src/json_test.cc40
-rw-r--r--src/ninja.cc18
6 files changed, 128 insertions, 13 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc02c4d..b49c5b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -103,6 +103,7 @@ add_library(libninja OBJECT
src/eval_env.cc
src/graph.cc
src/graphviz.cc
+ src/json.cc
src/line_printer.cc
src/manifest_parser.cc
src/metrics.cc
@@ -196,6 +197,7 @@ if(BUILD_TESTING)
src/dyndep_parser_test.cc
src/edit_distance_test.cc
src/graph_test.cc
+ src/json_test.cc
src/lexer_test.cc
src/manifest_parser_test.cc
src/missing_deps_test.cc
diff --git a/configure.py b/configure.py
index ffa75c7..4ca78fb 100755
--- a/configure.py
+++ b/configure.py
@@ -507,6 +507,7 @@ for name in ['build',
'eval_env',
'graph',
'graphviz',
+ 'json',
'lexer',
'line_printer',
'manifest_parser',
@@ -577,6 +578,7 @@ for name in ['build_log_test',
'disk_interface_test',
'edit_distance_test',
'graph_test',
+ 'json_test',
'lexer_test',
'manifest_parser_test',
'missing_deps_test',
diff --git a/src/json.cc b/src/json.cc
new file mode 100644
index 0000000..4bbf6e1
--- /dev/null
+++ b/src/json.cc
@@ -0,0 +1,53 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "json.h"
+
+#include <cstdio>
+#include <string>
+
+std::string EncodeJSONString(const std::string& in) {
+ static const char* hex_digits = "0123456789abcdef";
+ std::string out;
+ out.reserve(in.length() * 1.2);
+ for (std::string::const_iterator it = in.begin(); it != in.end(); ++it) {
+ char c = *it;
+ if (c == '\b')
+ out += "\\b";
+ else if (c == '\f')
+ out += "\\f";
+ else if (c == '\n')
+ out += "\\n";
+ else if (c == '\r')
+ out += "\\r";
+ else if (c == '\t')
+ out += "\\t";
+ else if (0x0 <= c && c < 0x20) {
+ out += "\\u00";
+ out += hex_digits[c >> 4];
+ out += hex_digits[c & 0xf];
+ } else if (c == '\\')
+ out += "\\\\";
+ else if (c == '\"')
+ out += "\\\"";
+ else
+ out += c;
+ }
+ return out;
+}
+
+void PrintJSONString(const std::string& in) {
+ std::string out = EncodeJSONString(in);
+ fwrite(out.c_str(), 1, out.length(), stdout);
+}
diff --git a/src/json.h b/src/json.h
new file mode 100644
index 0000000..f39c759
--- /dev/null
+++ b/src/json.h
@@ -0,0 +1,26 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NINJA_JSON_H_
+#define NINJA_JSON_H_
+
+#include <string>
+
+// Encode a string in JSON format without encolsing quotes
+std::string EncodeJSONString(const std::string& in);
+
+// Print a string in JSON format to stdout without enclosing quotes
+void PrintJSONString(const std::string& in);
+
+#endif
diff --git a/src/json_test.cc b/src/json_test.cc
new file mode 100644
index 0000000..b4afc73
--- /dev/null
+++ b/src/json_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "json.h"
+
+#include "test.h"
+
+TEST(JSONTest, RegularAscii) {
+ EXPECT_EQ(EncodeJSONString("foo bar"), "foo bar");
+}
+
+TEST(JSONTest, EscapedChars) {
+ EXPECT_EQ(EncodeJSONString("\"\\\b\f\n\r\t"),
+ "\\\""
+ "\\\\"
+ "\\b\\f\\n\\r\\t");
+}
+
+// codepoints between 0 and 0x1f should be escaped
+TEST(JSONTest, ControlChars) {
+ EXPECT_EQ(EncodeJSONString("\x01\x1f"), "\\u0001\\u001f");
+}
+
+// Leave them alone as JSON accepts unicode literals
+// out of control character range
+TEST(JSONTest, UTF8) {
+ const char* utf8str = "\xe4\xbd\xa0\xe5\xa5\xbd";
+ EXPECT_EQ(EncodeJSONString(utf8str), utf8str);
+}
diff --git a/src/ninja.cc b/src/ninja.cc
index 32cf00e..1d94442 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -41,6 +41,7 @@
#include "disk_interface.h"
#include "graph.h"
#include "graphviz.h"
+#include "json.h"
#include "manifest_parser.h"
#include "metrics.h"
#include "missing_deps.h"
@@ -773,15 +774,6 @@ int NinjaMain::ToolCleanDead(const Options* options, int argc, char* argv[]) {
return cleaner.CleanDead(build_log_.entries());
}
-void EncodeJSONString(const char *str) {
- while (*str) {
- if (*str == '"' || *str == '\\')
- putchar('\\');
- putchar(*str);
- str++;
- }
-}
-
enum EvaluateCommandMode {
ECM_NORMAL,
ECM_EXPAND_RSPFILE
@@ -814,13 +806,13 @@ std::string EvaluateCommandWithRspfile(const Edge* edge,
void printCompdb(const char* const directory, const Edge* const edge,
const EvaluateCommandMode eval_mode) {
printf("\n {\n \"directory\": \"");
- EncodeJSONString(directory);
+ PrintJSONString(directory);
printf("\",\n \"command\": \"");
- EncodeJSONString(EvaluateCommandWithRspfile(edge, eval_mode).c_str());
+ PrintJSONString(EvaluateCommandWithRspfile(edge, eval_mode));
printf("\",\n \"file\": \"");
- EncodeJSONString(edge->inputs_[0]->path().c_str());
+ PrintJSONString(edge->inputs_[0]->path());
printf("\",\n \"output\": \"");
- EncodeJSONString(edge->outputs_[0]->path().c_str());
+ PrintJSONString(edge->outputs_[0]->path());
printf("\"\n }");
}