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()); +}