diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rwxr-xr-x | configure.py | 2 | ||||
-rw-r--r-- | src/json.cc | 53 | ||||
-rw-r--r-- | src/json.h | 26 | ||||
-rw-r--r-- | src/json_test.cc | 40 | ||||
-rw-r--r-- | src/ninja.cc | 18 |
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 }"); } |