[rust-project] support proc macros Rust IDEs (such as VSCode) nowadays have excellent support for expanding procedural macros, so long as they're fed the right information. gn's output of rust-project.json was lacking a couple of fields. Bug: chromium/1293933 Change-Id: I842ba5ad7a19f95a785d7c9e1517203bf9e88bb7 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13340 Reviewed-by: Tyler Mandry <tmandry@google.com> Reviewed-by: Brett Wilson <brettw@chromium.org> Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc index c11f967..4e66dcc 100644 --- a/src/gn/rust_project_writer.cc +++ b/src/gn/rust_project_writer.cc
@@ -320,6 +320,15 @@ } } + // If it's a proc macro, record its output location so IDEs can invoke it. + if (std::string_view(rust_tool->name()) == + std::string_view(RustTool::kRsToolMacro)) { + auto outputs = target->computed_outputs(); + if (outputs.size() > 0) { + crate.SetIsProcMacro(outputs[0]); + } + } + // Add the rest of the crate dependencies. for (const auto& dep : crate_deps) { auto idx = lookup[dep]; @@ -404,6 +413,15 @@ rust_project << " \"edition\": \"" << crate.edition() << "\"," NEWLINE; + auto proc_macro_target = crate.proc_macro_path(); + if (proc_macro_target.has_value()) { + rust_project << " \"is_proc_macro\": true," NEWLINE; + auto so_location = FilePathToUTF8(build_settings->GetFullPath( + proc_macro_target->AsSourceFile(build_settings))); + rust_project << " \"proc_macro_dylib_path\": \"" << so_location + << "\"," NEWLINE; + } + rust_project << " \"cfg\": ["; bool first_cfg = true; for (const auto& cfg : crate.configs()) {
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h index a63ded1..d53ff07 100644 --- a/src/gn/rust_project_writer_helpers.h +++ b/src/gn/rust_project_writer_helpers.h
@@ -55,6 +55,11 @@ // Set the compiler target ("e.g. x86_64-linux-kernel") void SetCompilerTarget(std::string target) { compiler_target_ = target; } + // Set that this is a proc macro with the path to the output .so/dylib/dll + void SetIsProcMacro(OutputFile proc_macro_dynamic_library) { + proc_macro_dynamic_library_ = proc_macro_dynamic_library; + } + // Returns the root file for the crate. SourceFile& root() { return root_; } @@ -81,6 +86,11 @@ return compiler_target_; } + // Returns whether this crate builds a proc macro .so + const std::optional<OutputFile>& proc_macro_path() { + return proc_macro_dynamic_library_; + } + private: SourceFile root_; CrateIndex index_; @@ -90,6 +100,7 @@ DependencyList deps_; std::optional<std::string> compiler_target_; std::vector<std::string> compiler_args_; + std::optional<OutputFile> proc_macro_dynamic_library_; }; using CrateList = std::vector<Crate>;
diff --git a/src/gn/rust_project_writer_unittest.cc b/src/gn/rust_project_writer_unittest.cc index 675f9a6..77a9d34 100644 --- a/src/gn/rust_project_writer_unittest.cc +++ b/src/gn/rust_project_writer_unittest.cc
@@ -521,4 +521,57 @@ "}\n"; ExpectEqOrShowDiff(expected_json, out); -} \ No newline at end of file +} + +TEST_F(RustProjectJSONWriter, OneRustProcMacroTarget) { + Err err; + TestWithScope setup; + setup.build_settings()->SetRootPath(UTF8ToFilePath("path")); + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_PROC_MACRO); + 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=feature=\"foo_enabled\""); + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + std::ostringstream stream; + std::vector<const Target*> targets; + targets.push_back(&target); + RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream); + std::string out = stream.str(); +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); +#endif + const char expected_json[] = + "{\n" + " \"roots\": [\n" + " \"path/foo/\"\n" + " ],\n" + " \"crates\": [\n" + " {\n" + " \"crate_id\": 0,\n" + " \"root_module\": \"path/foo/lib.rs\",\n" + " \"label\": \"//foo:bar\",\n" + " \"compiler_args\": [\"--cfg=feature=\\\"foo_enabled\\\"\"],\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2015\",\n" + " \"is_proc_macro\": true,\n" + " \"proc_macro_dylib_path\": \"path/out/Debug/obj/foo/libbar.so\",\n" + " \"cfg\": [\n" + " \"test\",\n" + " \"debug_assertions\",\n" + " \"feature=\\\"foo_enabled\\\"\"\n" + " ]\n" + " }\n" + " ]\n" + "}\n"; + + ExpectEqOrShowDiff(expected_json, out); +}