Include Rust proc-macros as externs

Depending on Rust targets with type crate_type = "proc-macro" should
cause these to be included as externs.

Bug: crbug.com/gn/104
Change-Id: I4595f2bb01a39574001830d22b3b435310e28314
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/5800
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Petr Hosek <phosek@google.com>
diff --git a/tools/gn/ninja_rust_binary_target_writer.cc b/tools/gn/ninja_rust_binary_target_writer.cc
index 142e542..e1e762c 100644
--- a/tools/gn/ninja_rust_binary_target_writer.cc
+++ b/tools/gn/ninja_rust_binary_target_writer.cc
@@ -188,7 +188,8 @@
 void NinjaRustBinaryTargetWriter::WriteExterns() {
   std::vector<const Target*> externs;
   for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
-    if (pair.ptr->output_type() == Target::RUST_LIBRARY) {
+    if (pair.ptr->output_type() == Target::RUST_LIBRARY ||
+        pair.ptr->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO) {
       externs.push_back(pair.ptr);
     }
   }
diff --git a/tools/gn/ninja_rust_binary_target_writer_unittest.cc b/tools/gn/ninja_rust_binary_target_writer_unittest.cc
index 9cac3e2..e247f3d 100644
--- a/tools/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -5,6 +5,7 @@
 #include "tools/gn/ninja_rust_binary_target_writer.h"
 
 #include "tools/gn/config.h"
+#include "tools/gn/rust_values.h"
 #include "tools/gn/scheduler.h"
 #include "tools/gn/target.h"
 #include "tools/gn/test_with_scheduler.h"
@@ -369,3 +370,83 @@
     EXPECT_EQ(expected, out_str) << out_str;
   }
 }
+
+TEST_F(NinjaRustBinaryTargetWriterTest, ProcMacro) {
+  Err err;
+  TestWithScope setup;
+
+  Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
+  procmacro.set_output_type(Target::LOADABLE_MODULE);
+  procmacro.visibility().SetPublic();
+  SourceFile barlib("//bar/lib.rs");
+  procmacro.sources().push_back(SourceFile("//bar/mylib.rs"));
+  procmacro.sources().push_back(barlib);
+  procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+  procmacro.rust_values().set_crate_root(barlib);
+  procmacro.rust_values().crate_name() = "mymacro";
+  procmacro.rust_values().edition() = "2018";
+  procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+  procmacro.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(procmacro.OnResolved(&err));
+
+  {
+    std::ostringstream out;
+    NinjaRustBinaryTargetWriter writer(&procmacro, out);
+    writer.Run();
+
+    const char expected[] =
+        "crate_name = mymacro\n"
+        "crate_type = proc-macro\n"
+        "output_dir = \n"
+        "rustc_output_extension = .so\n"
+        "rustflags =\n"
+        "rustenv =\n"
+        "root_out_dir = .\n"
+        "target_out_dir = obj/bar\n"
+        "target_output_name = mymacro\n"
+        "\n"
+        "build obj/bar/libmymacro.so: rustc ../../bar/lib.rs | "
+        "../../bar/mylib.rs ../../bar/lib.rs\n"
+        "  edition = 2018\n";
+    std::string out_str = out.str();
+    EXPECT_EQ(expected, out_str) << out_str;
+  }
+
+  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  target.set_output_type(Target::EXECUTABLE);
+  target.visibility().SetPublic();
+  SourceFile main("//foo/main.rs");
+  target.sources().push_back(SourceFile("//foo/source.rs"));
+  target.sources().push_back(main);
+  target.source_types_used().Set(SourceFile::SOURCE_RS);
+  target.rust_values().set_crate_root(main);
+  target.rust_values().crate_name() = "foo_bar";
+  target.rust_values().edition() = "2018";
+  target.private_deps().push_back(LabelTargetPair(&procmacro));
+  target.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  {
+    std::ostringstream out;
+    NinjaRustBinaryTargetWriter writer(&target, out);
+    writer.Run();
+
+    const char expected[] =
+        "crate_name = foo_bar\n"
+        "crate_type = bin\n"
+        "output_dir = \n"
+        "rustc_output_extension = \n"
+        "rustflags =\n"
+        "rustenv =\n"
+        "root_out_dir = .\n"
+        "target_out_dir = obj/foo\n"
+        "target_output_name = bar\n"
+        "\n"
+        "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
+        "../../foo/main.rs || obj/bar/libmymacro.so\n"
+        "  externs = --extern mymacro=obj/bar/libmymacro.so\n"
+        "  edition = 2018\n";
+    std::string out_str = out.str();
+    EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+  }
+}