Reject newlines in string config values (defines, cflags, etc.)
A literal newline in a define or flag value would be written verbatim
into ninja files, breaking ninja's line-based parsing. Rather than
trying to escape newlines (which would work on POSIX via $$'\n' but
has no equivalent for cmd.exe on Windows), reject them with a clear
error at GN time. A newline in a compiler flag is almost certainly
a mistake anyway.
The validation is in GetStringList() so it covers all string config
values: defines, cflags, cflags_c, cflags_cc, cflags_objc,
cflags_objcc, asmflags, arflags, ldflags, rustflags, rustenv, and
swiftflags.
Before, without the pkg-config.py change in the linked bug, things
failed at build time:
```
% autoninja -C out/gnlinux base_unittests
offline mode
ninja: Entering directory `out/gnlinux'
0.20s load build.ninja failed:
1.47s Error: failed to load build.ninja: toolchain.ninja: line:61052: unexpected indent: " include_dirs =
```
Now, the fail at `gn gen` time instead:
```
% ~/src/gn/out/gn gen out/gnlinux
//build/config/linux/pkg-config.py ["-s", "../../build/linux/debian_bullseye_amd64-sysroot", "-a", "x64"] []
ERROR at //build/config/linux/atk/BUILD.gn:33:5: Newlines in defines values are not supported.
"ATK_LIB_DIR=\"$atk_lib_dir\"",
^-----------------------------
The value `ATK_LIB_DIR="[[],[],[],[],[]]
"` contains a newline.
See //build/config/linux/atk/BUILD.gn:19:1: whence it was called.
pkg_config("atk") {
^------------------
See //ui/accessibility/BUILD.gn:465:20: which caused the file to be included.
configs += [ "//build/config/linux/atk" ]
^-------------------------
```
Bug: 40176116
Change-Id: I870d625552087430f5a679b8668a1929662e7b4a
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/21460
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: Nico Weber <thakis@chromium.org>
Reviewed-by: Nico Weber <thakis@google.com>
diff --git a/src/gn/config_values_extractors_unittest.cc b/src/gn/config_values_extractors_unittest.cc
index 3db4bff..d187dab 100644
--- a/src/gn/config_values_extractors_unittest.cc
+++ b/src/gn/config_values_extractors_unittest.cc
@@ -6,6 +6,7 @@
#include "gn/config.h"
#include "gn/config_values_extractors.h"
+#include "gn/config_values_generator.h"
#include "gn/target.h"
#include "gn/test_with_scope.h"
#include "util/test/test.h"
@@ -147,3 +148,38 @@
"//target/ //target/config/ //target/all/ //target/direct/ "
"//dep1/all/ //dep2/all/ //dep1/direct/ ");
}
+
+TEST(ConfigValuesGenerator, DefinesWithNewlineError) {
+ TestWithScope setup;
+ Err err;
+
+ // Set up a scope with a defines list containing a newline.
+ Value defines_value(nullptr, Value::LIST);
+ defines_value.list_value().push_back(Value(nullptr, "GOOD"));
+ defines_value.list_value().push_back(Value(nullptr, "BAD=a\nb"));
+ setup.scope()->SetValue("defines", defines_value, nullptr);
+
+ ConfigValues config_values;
+ ConfigValuesGenerator gen(&config_values, setup.scope(),
+ SourceDir("//foo/"), &err);
+ gen.Run();
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.message(), "Newlines in defines values are not supported.");
+ EXPECT_EQ(err.help_text(), "The value `BAD=a\nb` contains a newline.");
+}
+
+TEST(ConfigValuesGenerator, CflagsWithNewlineError) {
+ TestWithScope setup;
+ Err err;
+
+ Value cflags_value(nullptr, Value::LIST);
+ cflags_value.list_value().push_back(Value(nullptr, "-Dfoo\nbar"));
+ setup.scope()->SetValue("cflags", cflags_value, nullptr);
+
+ ConfigValues config_values;
+ ConfigValuesGenerator gen(&config_values, setup.scope(),
+ SourceDir("//foo/"), &err);
+ gen.Run();
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.message(), "Newlines in cflags values are not supported.");
+}
diff --git a/src/gn/config_values_generator.cc b/src/gn/config_values_generator.cc
index cf91a8c..3d95f46 100644
--- a/src/gn/config_values_generator.cc
+++ b/src/gn/config_values_generator.cc
@@ -27,6 +27,19 @@
return; // No value, empty input and succeed.
ExtractListOfStringValues(*value, &(config_values->*accessor)(), err);
+ if (err->has_error())
+ return;
+
+ const auto& strings = (config_values->*accessor)();
+ for (size_t i = 0; i < strings.size(); i++) {
+ if (strings[i].find('\n') != std::string::npos) {
+ *err = Err(value->list_value()[i],
+ "Newlines in " + std::string(var_name) + " values are not "
+ "supported.",
+ "The value `" + strings[i] + "` contains a newline.");
+ return;
+ }
+ }
}
void GetDirList(Scope* scope,