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