Fix quoting preprocessor defines for compilation database If a preprocessor define has a space character, which usually happens with double-quoted text, current compilation database writer cannot handle correctly. These spaces should be escaped. Tested that gn is generating correctly compile_commands.json on WebRTC with Linux. Bug: gn:109 Change-Id: I4ba2ed4c807e4cb9e078720df3b64834a4f9ec8e Reviewed-on: https://gn-review.googlesource.com/c/gn/+/6780 Reviewed-by: Brett Wilson <brettw@chromium.org> Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/AUTHORS b/AUTHORS index b78d6a5..0640d6c 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -9,6 +9,7 @@ # See python fnmatch module documentation for more information. Google Inc. <*@google.com> +HyperConnect Inc. <*@hpcnt.com> IBM Inc. <*@*.ibm.com> Loongson Technology Corporation Limited. <*@loongson.cn> MIPS Technologies, Inc. <*@mips.com>
diff --git a/src/gn/compile_commands_writer.cc b/src/gn/compile_commands_writer.cc index 10984cc..9ff67ca 100644 --- a/src/gn/compile_commands_writer.cc +++ b/src/gn/compile_commands_writer.cc
@@ -57,9 +57,9 @@ target->config_values().has_precompiled_headers(); std::ostringstream defines_out; - RecursiveTargetConfigToStream<std::string>( - target, &ConfigValues::defines, - DefineWriter(ESCAPE_NINJA_PREFORMATTED_COMMAND, true), defines_out); + RecursiveTargetConfigToStream<std::string>(target, &ConfigValues::defines, + DefineWriter(ESCAPE_SPACE, true), + defines_out); base::EscapeJSONString(defines_out.str(), false, &flags.defines); std::ostringstream includes_out;
diff --git a/src/gn/compile_commands_writer_unittest.cc b/src/gn/compile_commands_writer_unittest.cc index 96ef9eb..99dc55d 100644 --- a/src/gn/compile_commands_writer_unittest.cc +++ b/src/gn/compile_commands_writer_unittest.cc
@@ -239,7 +239,7 @@ target.sources().push_back(SourceFile("//foo/input.cc")); target.config_values().defines().push_back("BOOL_DEF"); target.config_values().defines().push_back("INT_DEF=123"); - target.config_values().defines().push_back("STR_DEF=\"ABCD-1\""); + target.config_values().defines().push_back("STR_DEF=\"ABCD 1\""); ASSERT_TRUE(target.OnResolved(&err)); targets.push_back(&target); @@ -248,7 +248,7 @@ writer.RenderJSON(build_settings(), targets, &out); const char expected[] = - "-DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\\\\\"ABCD-1\\\\\\\""; + "-DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\\\\\"ABCD\\\\ 1\\\\\\\""; EXPECT_TRUE(out.find(expected) != std::string::npos); }
diff --git a/src/gn/escape.cc b/src/gn/escape.cc index 9f567fb..98f77d3 100644 --- a/src/gn/escape.cc +++ b/src/gn/escape.cc
@@ -41,6 +41,19 @@ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; // clang-format on +size_t EscapeStringToString_Space(const std::string_view& str, + const EscapeOptions& options, + char* dest, + bool* needed_quoting) { + size_t i = 0; + for (const auto& elem : str) { + if (elem == ' ') + dest[i++] = '\\'; + dest[i++] = elem; + } + return i; +} + // Uses the stack if the space needed is small and the heap otherwise. class StackOrHeapBuffer { public: @@ -204,6 +217,8 @@ case ESCAPE_NONE: strncpy(dest, str.data(), str.size()); return str.size(); + case ESCAPE_SPACE: + return EscapeStringToString_Space(str, options, dest, needed_quoting); case ESCAPE_NINJA: return EscapeStringToString_Ninja(str, options, dest, needed_quoting); case ESCAPE_DEPFILE:
diff --git a/src/gn/escape.h b/src/gn/escape.h index 36a9c5c..28f31bf 100644 --- a/src/gn/escape.h +++ b/src/gn/escape.h
@@ -12,6 +12,9 @@ // No escaping. ESCAPE_NONE, + // Space only. + ESCAPE_SPACE, + // Ninja string escaping. ESCAPE_NINJA,
diff --git a/src/gn/escape_unittest.cc b/src/gn/escape_unittest.cc index 48f6941..ca42ac9 100644 --- a/src/gn/escape_unittest.cc +++ b/src/gn/escape_unittest.cc
@@ -69,3 +69,12 @@ // Only $ is escaped. EXPECT_EQ("a: \"$$\\b<;", EscapeString("a: \"$\\b<;", opts, nullptr)); } + +TEST(Escape, Space) { + EscapeOptions opts; + opts.mode = ESCAPE_SPACE; + + // ' ' is escaped. + EXPECT_EQ("-VERSION=\"libsrtp2\\ 2.1.0-pre\"", + EscapeString("-VERSION=\"libsrtp2 2.1.0-pre\"", opts, nullptr)); +}