Add support escape JSON string to stream for export compile commands
gn gen --export-compile-commands does not produce valid JSON
The resulting file has "command" entries which are invalid JSON:
they are strings containing unescaped double quotation marks,
like "command": "... /showIncludes "..." "..." "..."".
Added new function EscapeJSONStringToStream that escape strings suitable
for JSON to generate compile_commands.json with valid JSON. This function
works the same as EscapeString but escape JSON string and writes the
results to the given stream, saving a copy.
Made EscapeJSONString return void and deleting the GetQuotedJSONString in
string_escape.*.
Added unit-test for EscapeJSONStringToStream function to escape_unittest.cc
Change-Id: I39c313314a186cb1de0280494cba3b546e5d1a1d
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8520
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/src/base/json/string_escape.cc b/src/base/json/string_escape.cc
index ea674f5..51a818c 100644
--- a/src/base/json/string_escape.cc
+++ b/src/base/json/string_escape.cc
@@ -116,30 +116,16 @@
} // namespace
-bool EscapeJSONString(std::string_view str,
+void EscapeJSONString(std::string_view str,
bool put_in_quotes,
std::string* dest) {
- return EscapeJSONStringImpl(str, put_in_quotes, dest);
+ EscapeJSONStringImpl(str, put_in_quotes, dest);
}
-bool EscapeJSONString(std::u16string_view str,
+void EscapeJSONString(std::u16string_view str,
bool put_in_quotes,
std::string* dest) {
- return EscapeJSONStringImpl(str, put_in_quotes, dest);
-}
-
-std::string GetQuotedJSONString(std::string_view str) {
- std::string dest;
- bool ok = EscapeJSONStringImpl(str, true, &dest);
- DCHECK(ok);
- return dest;
-}
-
-std::string GetQuotedJSONString(std::u16string_view str) {
- std::string dest;
- bool ok = EscapeJSONStringImpl(str, true, &dest);
- DCHECK(ok);
- return dest;
+ EscapeJSONStringImpl(str, put_in_quotes, dest);
}
std::string EscapeBytesAsInvalidJSONString(std::string_view str,
diff --git a/src/base/json/string_escape.h b/src/base/json/string_escape.h
index ada5179..14eecdd 100644
--- a/src/base/json/string_escape.h
+++ b/src/base/json/string_escape.h
@@ -24,22 +24,17 @@
//
// If |put_in_quotes| is true, then a leading and trailing double-quote mark
// will be appended to |dest| as well.
-bool EscapeJSONString(std::string_view str,
+void EscapeJSONString(std::string_view str,
bool put_in_quotes,
std::string* dest);
// Performs a similar function to the UTF-8 std::string_view version above,
// converting UTF-16 code units to UTF-8 code units and escaping non-printing
// control characters. On return, |dest| will contain a valid UTF-8 JSON string.
-bool EscapeJSONString(std::u16string_view str,
+void EscapeJSONString(std::u16string_view str,
bool put_in_quotes,
std::string* dest);
-// Helper functions that wrap the above two functions but return the value
-// instead of appending. |put_in_quotes| is always true.
-std::string GetQuotedJSONString(std::string_view str);
-std::string GetQuotedJSONString(std::u16string_view str);
-
// Given an arbitrary byte string |str|, this will escape all non-ASCII bytes
// as \uXXXX escape sequences. This function is *NOT* meant to be used with
// Unicode strings and does not validate |str| as one.
diff --git a/src/gn/compile_commands_writer.cc b/src/gn/compile_commands_writer.cc
index 8d6c7fb..d0367fc 100644
--- a/src/gn/compile_commands_writer.cc
+++ b/src/gn/compile_commands_writer.cc
@@ -159,7 +159,7 @@
for (const auto& range : tool->command().ranges()) {
// TODO: this is emitting a bonus space prior to each substitution.
if (range.type == &SubstitutionLiteral) {
- EscapeStringToStream(out, range.literal, no_quoting);
+ EscapeJSONStringToStream(out, range.literal, no_quoting);
} else if (range.type == &SubstitutionOutput) {
path_output.WriteFiles(out, tool_outputs);
} else if (range.type == &CSubstitutionDefines) {
diff --git a/src/gn/escape.cc b/src/gn/escape.cc
index 98f77d3..1874905 100644
--- a/src/gn/escape.cc
+++ b/src/gn/escape.cc
@@ -9,6 +9,7 @@
#include <memory>
#include "base/compiler_specific.h"
+#include "base/json/string_escape.h"
#include "base/logging.h"
#include "util/build_config.h"
@@ -266,3 +267,13 @@
StackOrHeapBuffer dest(str.size() * kMaxEscapedCharsPerChar);
out.write(dest, EscapeStringToString(str, options, dest, nullptr));
}
+
+void EscapeJSONStringToStream(std::ostream& out,
+ const std::string_view& str,
+ const EscapeOptions& options) {
+ std::string dest;
+ bool needed_quoting = !options.inhibit_quoting;
+ base::EscapeJSONString(str, needed_quoting, &dest);
+
+ EscapeStringToStream(out, dest, options);
+}
diff --git a/src/gn/escape.h b/src/gn/escape.h
index 28f31bf..78e6544 100644
--- a/src/gn/escape.h
+++ b/src/gn/escape.h
@@ -76,4 +76,10 @@
const std::string_view& str,
const EscapeOptions& options);
+// Same as EscapeString but escape JSON string and writes the results to the
+// given stream, saving a copy.
+void EscapeJSONStringToStream(std::ostream& out,
+ const std::string_view& str,
+ const EscapeOptions& options);
+
#endif // TOOLS_GN_ESCAPE_H_
diff --git a/src/gn/escape_unittest.cc b/src/gn/escape_unittest.cc
index ca42ac9..afc2bc4 100644
--- a/src/gn/escape_unittest.cc
+++ b/src/gn/escape_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "gn/escape.h"
+#include "gn/string_output_buffer.h"
#include "util/test/test.h"
TEST(Escape, Ninja) {
@@ -78,3 +79,25 @@
EXPECT_EQ("-VERSION=\"libsrtp2\\ 2.1.0-pre\"",
EscapeString("-VERSION=\"libsrtp2 2.1.0-pre\"", opts, nullptr));
}
+
+TEST(EscapeJSONString, NinjaPreformatted) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
+ opts.inhibit_quoting = true;
+
+ StringOutputBuffer buffer;
+ std::ostream out(&buffer);
+
+ EscapeJSONStringToStream(out, "foo\\\" bar", opts);
+ EXPECT_EQ("foo\\\\\\\" bar", buffer.str());
+
+ StringOutputBuffer buffer1;
+ std::ostream out1(&buffer1);
+ EscapeJSONStringToStream(out1, "foo bar\\\\", opts);
+ EXPECT_EQ("foo bar\\\\\\\\", buffer1.str());
+
+ StringOutputBuffer buffer2;
+ std::ostream out2(&buffer2);
+ EscapeJSONStringToStream(out2, "a: \"$\\b", opts);
+ EXPECT_EQ("a: \\\"$$\\\\b", buffer2.str());
+}