Create a new target type for Rust procedural macro

Rust procedural macros are special types, even though they're
technically shared libraries, they're not linked into targets and
their building follows different rules and requires different defaults
(e.g. they don't support sanitizers unlike other Rust targets). This
change introduces new rust_proc_macro target type that could be used for
building procedural macros rather than overloading shared_library.

Change-Id: I9ff176a5893681ba2c3d8ce8ea291603d253e991
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/6700
Commit-Queue: Petr Hosek <phosek@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index 8253dae..e574657 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -28,6 +28,7 @@
     *   [group: Declare a named group of targets.](#func_group)
     *   [loadable_module: Declare a loadable module target.](#func_loadable_module)
     *   [rust_library: Declare a Rust library target.](#func_rust_library)
+    *   [rust_proc_macro: Declare a Rust procedural macro target.](#func_rust_proc_macro)
     *   [shared_library: Declare a shared library target.](#func_shared_library)
     *   [source_set: Declare a source set target.](#func_source_set)
     *   [static_library: Declare a static library target.](#func_static_library)
@@ -1794,6 +1795,41 @@
            visibility
   Rust variables: aliased_deps, crate_root, crate_name
 ```
+### <a name="func_rust_proc_macro"></a>**rust_proc_macro**: Declare a Rust procedural macro target.
+
+```
+  A Rust procedural macro allows creating syntax extensions as execution of a
+  function. They are compiled as dynamic libraries and used by the compiler at
+  runtime.
+
+  Their use is the same as of other Rust libraries, but their build has some
+  additional restrictions in terms of supported flags.
+```
+
+#### **Language and compilation**
+
+```
+  The tools and commands used to create this target type will be
+  determined by the source files in its sources. Targets containing
+  multiple compiler-incompatible languages are not allowed (e.g. a
+  target containing both C and C++ sources is acceptable, but a
+  target containing C and Rust sources is not).
+```
+
+#### **Variables**
+
+```
+  Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+         asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+         libs, precompiled_header, precompiled_source, rustflags,
+         rustenv
+  Deps: data_deps, deps, public_deps
+  Dependent configs: all_dependent_configs, public_configs
+  General: check_includes, configs, data, friend, inputs, metadata,
+           output_name, output_extension, public, sources, testonly,
+           visibility
+  Rust variables: aliased_deps, crate_root, crate_name
+```
 ### <a name="func_shared_library"></a>**shared_library**: Declare a shared library target.
 
 ```
@@ -3307,7 +3343,7 @@
     rlib_output_extension [string, optional, rust tools only]
     dylib_output_extension [string, optional, rust tools only]
     cdylib_output_extension [string, optional, rust tools only]
-    proc_macro_output_extension [string, optional, rust tools only]
+    rust_proc_macro_output_extension [string, optional, rust tools only]
         Valid for: Rust tools
 
         These specify the default tool output for each of the crate types.
diff --git a/misc/emacs/gn-mode.el b/misc/emacs/gn-mode.el
index bd9e150..814be4b 100644
--- a/misc/emacs/gn-mode.el
+++ b/misc/emacs/gn-mode.el
@@ -63,7 +63,7 @@
 (defvar gn-font-lock-target-declaration-keywords
   '("action" "action_foreach" "bundle_data" "copy" "create_bundle" "executable"
     "group" "loadable_module" "shared_library" "source_set" "static_library"
-    "generated_file" "target"))
+    "generated_file" "target" "rust_library" "rust_proc_macro"))
 
 ;; pool() is handled specially since it's also a variable name
 (defvar gn-font-lock-buildfile-fun-keywords
diff --git a/misc/tm/GN.tmLanguage b/misc/tm/GN.tmLanguage
index 775b95e..81d6504 100644
--- a/misc/tm/GN.tmLanguage
+++ b/misc/tm/GN.tmLanguage
@@ -65,7 +65,7 @@
       <key>comment</key>
       <string>targets</string>
       <key>match</key>
-      <string>\b(?:action|action_foreach|copy|executable|group|loadable_module|shared_library|source_set|static_library|generated_file)\b</string>
+      <string>\b(?:action|action_foreach|copy|executable|group|loadable_module|shared_library|source_set|static_library|generated_file|rust_library|rust_proc_macro)\b</string>
       <key>name</key>
       <string>entity.name.tag.gn</string>
     </dict>
diff --git a/misc/vim/syntax/gn.vim b/misc/vim/syntax/gn.vim
index 6fe6d0b..25e4340 100644
--- a/misc/vim/syntax/gn.vim
+++ b/misc/vim/syntax/gn.vim
@@ -28,6 +28,7 @@
 syn keyword     gnTarget action action_foreach copy executable group
 syn keyword     gnTarget shared_library source_set static_library
 syn keyword     gnTarget loadable_module generated_file
+syn keyword     gnTarget rust_library rust_proc_macro
 hi def link     gnTarget            Type
 
 " Buildfile functions
diff --git a/src/gn/function_toolchain.cc b/src/gn/function_toolchain.cc
index 7fc95eb..fc9b0c1 100644
--- a/src/gn/function_toolchain.cc
+++ b/src/gn/function_toolchain.cc
@@ -380,7 +380,7 @@
     rlib_output_extension [string, optional, rust tools only]
     dylib_output_extension [string, optional, rust tools only]
     cdylib_output_extension [string, optional, rust tools only]
-    proc_macro_output_extension [string, optional, rust tools only]
+    rust_proc_macro_output_extension [string, optional, rust tools only]
         Valid for: Rust tools
 
         These specify the default tool output for each of the crate types.
diff --git a/src/gn/functions.cc b/src/gn/functions.cc
index 91723f5..640bcea 100644
--- a/src/gn/functions.cc
+++ b/src/gn/functions.cc
@@ -1445,6 +1445,7 @@
     INSERT_FUNCTION(Target, true)
     INSERT_FUNCTION(GeneratedFile, true)
     INSERT_FUNCTION(RustLibrary, true)
+    INSERT_FUNCTION(RustProcMacro, true)
 
     INSERT_FUNCTION(Assert, false)
     INSERT_FUNCTION(Config, false)
diff --git a/src/gn/functions.h b/src/gn/functions.h
index 8149db7..5027559 100644
--- a/src/gn/functions.h
+++ b/src/gn/functions.h
@@ -286,6 +286,15 @@
                      BlockNode* block,
                      Err* err);
 
+extern const char kRustProcMacro[];
+extern const char kRustProcMacro_HelpShort[];
+extern const char kRustProcMacro_Help[];
+Value RunRustProcMacro(Scope* scope,
+                   const FunctionCallNode* function,
+                   const std::vector<Value>& args,
+                   BlockNode* block,
+                   Err* err);
+
 extern const char kSetDefaults[];
 extern const char kSetDefaults_HelpShort[];
 extern const char kSetDefaults_Help[];
diff --git a/src/gn/functions_target.cc b/src/gn/functions_target.cc
index 0f9fc50..2c880c0 100644
--- a/src/gn/functions_target.cc
+++ b/src/gn/functions_target.cc
@@ -661,6 +661,38 @@
                               block, err);
 }
 
+// rust_proc_macro ----------------------------------------------------------------
+
+const char kRustProcMacro[] = "rust_proc_macro";
+const char kRustProcMacro_HelpShort[] =
+    "rust_proc_macro: Declare a Rust procedural macro target.";
+const char kRustProcMacro_Help[] =
+    R"(rust_proc_macro: Declare a Rust procedural macro target.
+
+  A Rust procedural macro allows creating syntax extensions as execution of a
+  function. They are compiled as dynamic libraries and used by the compiler at
+  runtime.
+
+  Their use is the same as of other Rust libraries, but their build has some
+  additional restrictions in terms of supported flags.
+
+Language and compilation
+)" LANGUAGE_HELP
+    R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+        RUST_VARS;
+Value RunRustProcMacro(Scope* scope,
+                   const FunctionCallNode* function,
+                   const std::vector<Value>& args,
+                   BlockNode* block,
+                   Err* err) {
+  return ExecuteGenericTarget(functions::kRustProcMacro, scope, function, args,
+                              block, err);
+}
+
 // shared_library --------------------------------------------------------------
 
 const char kSharedLibrary[] = "shared_library";
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index 2fba66c..f1d2fe5 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -459,7 +459,7 @@
         << target_->label().GetUserVisibleName(false);
 
     if (cur->output_type() == Target::RUST_LIBRARY ||
-        cur->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO)
+        cur->output_type() == Target::RUST_PROC_MACRO)
       continue;
 
     if (cur->dependency_output_file().value() !=
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index f23680d..d15fa48 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -55,6 +55,9 @@
         case Target::RUST_LIBRARY:
           crate_type = "rlib";
           break;
+        case Target::RUST_PROC_MACRO:
+          crate_type = "proc-macro";
+          break;
         default:
           NOTREACHED();
       }
@@ -186,7 +189,7 @@
   std::vector<const Target*> externs;
   for (const Target* target : deps) {
     if (target->output_type() == Target::RUST_LIBRARY ||
-        target->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO) {
+        target->output_type() == Target::RUST_PROC_MACRO) {
       externs.push_back(target);
     }
   }
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index b8a9532..781ee7b 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -437,12 +437,12 @@
   }
 }
 
-TEST_F(NinjaRustBinaryTargetWriterTest, ProcMacro) {
+TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
   Err err;
   TestWithScope setup;
 
   Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
-  procmacro.set_output_type(Target::LOADABLE_MODULE);
+  procmacro.set_output_type(Target::RUST_PROC_MACRO);
   procmacro.visibility().SetPublic();
   SourceFile barlib("//bar/lib.rs");
   procmacro.sources().push_back(SourceFile("//bar/mylib.rs"));
@@ -507,7 +507,7 @@
         "target_output_name = bar\n"
         "\n"
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
-        "../../foo/main.rs || obj/bar/libmymacro.so\n"
+        "../../foo/main.rs obj/bar/libmymacro.so\n"
         "  externs = --extern mymacro=obj/bar/libmymacro.so\n"
         "  rustdeps = -Ldependency=obj/bar\n";
     std::string out_str = out.str();
diff --git a/src/gn/rust_values_generator.cc b/src/gn/rust_values_generator.cc
index 241e6fd..38588b4 100644
--- a/src/gn/rust_values_generator.cc
+++ b/src/gn/rust_values_generator.cc
@@ -38,6 +38,7 @@
   if (target_->output_type() != Target::EXECUTABLE &&
       target_->output_type() != Target::SHARED_LIBRARY &&
       target_->output_type() != Target::RUST_LIBRARY &&
+      target_->output_type() != Target::RUST_PROC_MACRO &&
       target_->output_type() != Target::STATIC_LIBRARY &&
       target_->output_type() != Target::LOADABLE_MODULE) {
     // Only valid rust output types.
diff --git a/src/gn/target.cc b/src/gn/target.cc
index 9869138..5c2e721 100644
--- a/src/gn/target.cc
+++ b/src/gn/target.cc
@@ -313,6 +313,8 @@
       return functions::kGeneratedFile;
     case RUST_LIBRARY:
       return functions::kRustLibrary;
+    case RUST_PROC_MACRO:
+      return functions::kRustProcMacro;
     default:
       return "";
   }
@@ -400,19 +402,20 @@
 bool Target::IsBinary() const {
   return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
          output_type_ == LOADABLE_MODULE || output_type_ == STATIC_LIBRARY ||
-         output_type_ == SOURCE_SET || output_type_ == RUST_LIBRARY;
+         output_type_ == SOURCE_SET || output_type_ == RUST_LIBRARY ||
+         output_type_ == RUST_PROC_MACRO;
 }
 
 bool Target::IsLinkable() const {
   return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY ||
-         output_type_ == RUST_LIBRARY;
+         output_type_ == RUST_LIBRARY || output_type_ == RUST_PROC_MACRO;
 }
 
 bool Target::IsFinal() const {
   return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
          output_type_ == LOADABLE_MODULE || output_type_ == ACTION ||
          output_type_ == ACTION_FOREACH || output_type_ == COPY_FILES ||
-         output_type_ == CREATE_BUNDLE ||
+         output_type_ == CREATE_BUNDLE || output_type_ == RUST_PROC_MACRO ||
          (output_type_ == STATIC_LIBRARY && complete_static_lib_);
 }
 
@@ -520,7 +523,9 @@
   // Direct dependent libraries.
   if (dep->output_type() == STATIC_LIBRARY ||
       dep->output_type() == SHARED_LIBRARY ||
-      dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY)
+      dep->output_type() == SOURCE_SET ||
+      dep->output_type() == RUST_LIBRARY ||
+      dep->output_type() == RUST_PROC_MACRO)
     inherited_libraries_.Append(dep, is_public);
 
   if (dep->output_type() == SHARED_LIBRARY) {
@@ -672,6 +677,7 @@
           SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
               this, tool, tool->outputs().list()[0]);
       break;
+    case RUST_PROC_MACRO:
     case SHARED_LIBRARY:
       CHECK(tool->outputs().list().size() >= 1);
       check_tool_outputs = true;
diff --git a/src/gn/target.h b/src/gn/target.h
index 9666427..b86a893 100644
--- a/src/gn/target.h
+++ b/src/gn/target.h
@@ -49,6 +49,7 @@
     CREATE_BUNDLE,
     GENERATED_FILE,
     RUST_LIBRARY,
+    RUST_PROC_MACRO,
   };
 
   enum DepsIterationType {
diff --git a/src/gn/target_generator.cc b/src/gn/target_generator.cc
index 3936fd7..0111d72 100644
--- a/src/gn/target_generator.cc
+++ b/src/gn/target_generator.cc
@@ -148,6 +148,10 @@
     BinaryTargetGenerator generator(target.get(), scope, function_call,
                                     Target::RUST_LIBRARY, err);
     generator.Run();
+  } else if (output_type == functions::kRustProcMacro) {
+    BinaryTargetGenerator generator(target.get(), scope, function_call,
+                                    Target::RUST_PROC_MACRO, err);
+    generator.Run();
   } else {
     *err = Err(function_call, "Not a known target type",
                "I am very confused by the target type \"" + output_type + "\"");
diff --git a/src/gn/test_with_scope.cc b/src/gn/test_with_scope.cc
index c1ba4e8..06a8772 100644
--- a/src/gn/test_with_scope.cc
+++ b/src/gn/test_with_scope.cc
@@ -231,18 +231,18 @@
       "{{root_output_dir}}/{{target_output_name}}{{output_extension}}"));
   toolchain->SetTool(std::move(dylib_tool));
 
-  // PROC_MACRO
-  std::unique_ptr<Tool> proc_macro_tool = Tool::CreateTool(RustTool::kRsToolMacro);
+  // RUST_PROC_MACRO
+  std::unique_ptr<Tool> rust_proc_macro_tool = Tool::CreateTool(RustTool::kRsToolMacro);
   SetCommandForTool(
       "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
       "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
       "{{rustdeps}} {{externs}}",
-      proc_macro_tool.get());
-  proc_macro_tool->set_output_prefix("lib");
-  proc_macro_tool->set_default_output_extension(".so");
-  proc_macro_tool->set_outputs(SubstitutionList::MakeForTest(
+      rust_proc_macro_tool.get());
+  rust_proc_macro_tool->set_output_prefix("lib");
+  rust_proc_macro_tool->set_default_output_extension(".so");
+  rust_proc_macro_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
-  toolchain->SetTool(std::move(proc_macro_tool));
+  toolchain->SetTool(std::move(rust_proc_macro_tool));
 
   // RLIB
   std::unique_ptr<Tool> rlib_tool = Tool::CreateTool(RustTool::kRsToolRlib);
diff --git a/src/gn/tool.cc b/src/gn/tool.cc
index e43336c..6dc46e7 100644
--- a/src/gn/tool.cc
+++ b/src/gn/tool.cc
@@ -337,6 +337,8 @@
             return RustTool::kRsToolStaticlib;
           case Target::RUST_LIBRARY:
             return RustTool::kRsToolRlib;
+          case Target::RUST_PROC_MACRO:
+            return RustTool::kRsToolMacro;
           default:
             break;
         }