[rust-project] Support space-separated --cfg arguments When GN exports `rust-project.json` (via `--export-rust-project`), it attempts to extract `cfg` arguments from a target's `rustflags` to populate the `cfg` array in the JSON file. This allows language servers like `rust-analyzer` to correctly resolve conditional compilation blocks (e.g. `#[gtest]`). Prior to this commit, GN only successfully extracts these elements if they are specified using the single-string format (e.g., `rustflags = [ "--cfg=foo" ]`). If the build configuration provides the flag and the argument separately (e.g., `rustflags = [ "--cfg", "foo" ]`), GN ignores the argument entirely. This results in `rust-analyzer` being unable to resolve `cfg`-gated code in projects using the space-separated format. Examples of using the space-separated syntax can be found for example in Chromium in `build/rust/BUILD.gn`. Change-Id: I2c99c0645f6052c16c98f557b26ef47c6a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/23740 Reviewed-by: Andrew Grieve <agrieve@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Zgroza (Luke) Klimek <zgroza@chromium.org>
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc index 181d50f..eb2d4e2 100644 --- a/src/gn/rust_project_writer.cc +++ b/src/gn/rust_project_writer.cc
@@ -137,6 +137,22 @@ return std::nullopt; } +std::vector<std::string> FindAllArgValues( + const char* arg, + const std::vector<std::string>& args) { + std::vector<std::string> values; + for (auto it = args.begin(); it != args.end(); ++it) { + if (*it == arg) { + ++it; + if (it == args.end()) { + break; + } + values.push_back(*it); + } + } + return values; +} + std::optional<std::string> FindArgValueAfterPrefix( const std::string& prefix, const std::vector<std::string>& args) { @@ -231,6 +247,9 @@ crate.SetCompilerTarget(compiler_target.value()); ConfigList cfgs = FindAllArgValuesAfterPrefix("--cfg=", compiler_args); + auto exact_cfgs = FindAllArgValues("--cfg", compiler_args); + cfgs.insert(cfgs.end(), std::make_move_iterator(exact_cfgs.begin()), + std::make_move_iterator(exact_cfgs.end())); crate.AddConfigItem("test"); crate.AddConfigItem("debug_assertions");
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h index 4feff94..f11de8c 100644 --- a/src/gn/rust_project_writer_helpers.h +++ b/src/gn/rust_project_writer_helpers.h
@@ -143,6 +143,11 @@ const std::string& prefix, const std::vector<std::string>& args); +// Find all arguments that match the given exact string, returning the value +// after. +std::vector<std::string> FindAllArgValues(const char* arg, + const std::vector<std::string>& args); + // Find all arguments that match the given prefix, returning the value after // the prefix for each one. e.g. "--cfg=value" is returned as "value" if the // prefix "--cfg=" is used.
diff --git a/src/gn/rust_project_writer_helpers_unittest.cc b/src/gn/rust_project_writer_helpers_unittest.cc index c37f215..082e92e 100644 --- a/src/gn/rust_project_writer_helpers_unittest.cc +++ b/src/gn/rust_project_writer_helpers_unittest.cc
@@ -252,3 +252,28 @@ "feature=\"bar_enabled\""}; EXPECT_EQ(expected, result); } + +TEST_F(RustProjectWriterHelper, FindAllArgValues) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--cfg"); + target.config_values().rustflags().push_back("feature=\"foo_enabled\""); + target.config_values().rustflags().push_back("--edition=2018"); + target.config_values().rustflags().push_back("--cfg"); + target.config_values().rustflags().push_back("feature=\"bar_enabled\""); + target.config_values().rustflags().push_back("--target"); + + auto args = ExtractCompilerArgs(&target); + std::vector<std::string> result = FindAllArgValues("--cfg", args); + std::vector<std::string> expected = {"feature=\"foo_enabled\"", + "feature=\"bar_enabled\""}; + EXPECT_EQ(expected, result); +}