[banjo] Wire up fidlgen_banjo in the build.

- Create new template to augment the existing `banjo` template by
  adding targets which handle the Banjo sources as FIDL sources;
- Generate "C bindings" (placeholders for now) with fidlgen_banjo;
- Add a build step to ensure bindings generated respectively by the
  banjo and fidlgen_banjo tools remain identical.

Bug: 68157
Change-Id: I233889f558c68c8166bf2d8ee0d6e5487328b4e5
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/476317
Commit-Queue: P.Y. Laligand <pylaligand@google.com>
Reviewed-by: David Gilhooley <dgilhooley@google.com>
diff --git a/build/banjo/banjo.gni b/build/banjo/banjo.gni
index e13b56d..31dcaf5 100644
--- a/build/banjo/banjo.gni
+++ b/build/banjo/banjo.gni
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/banjo/fidl_adapter.gni")
 import("//build/banjo/toolchain.gni")
 import("//build/rust/toolchain.gni")
 
@@ -30,6 +31,12 @@
 #   sdk_category (optional)
 #     Publication level of the library in SDKs.
 #     See //build/sdk/sdk_atom.gni.
+#
+#   translate_to_fidl (optional)
+#     Whether to add extra targets presenting this library as a FIDL library.
+#     This is part of the Banjo deprecation effort and should not be used in
+#     any other context.
+#     Defaults to false.
 
 template("banjo") {
   if (defined(invoker.sdk_category)) {
@@ -39,11 +46,13 @@
     not_needed(invoker, [ "needs_composite" ])
   }
 
+  excluded_variables = [ "translate_to_fidl" ]
+
   if (current_toolchain == banjo_toolchain) {
     import("//build/banjo/banjo_library.gni")
 
     banjo_library(target_name) {
-      forward_variables_from(invoker, "*")
+      forward_variables_from(invoker, "*", excluded_variables)
       if (!defined(deps)) {
         deps = []
       }
@@ -53,16 +62,25 @@
     import("//build/rust/banjo_rust_library.gni")
 
     banjo_rust_library(target_name) {
-      forward_variables_from(invoker, "*")
+      forward_variables_from(invoker, "*", excluded_variables)
     }
 
     banjo_c_target(target_name) {
-      forward_variables_from(invoker, "*")
+      forward_variables_from(invoker, "*", excluded_variables)
     }
   } else {
     assert(false,
            "Unable to process BANJO target in toolchain $current_toolchain.")
   }
+
+  if (defined(invoker.translate_to_fidl) && invoker.translate_to_fidl) {
+    if (is_fuchsia) {
+      # Add an extra set of targets handling Banjo sources as a FIDL library.
+      fidl_adapter(target_name) {
+        forward_variables_from(invoker, "*", excluded_variables)
+      }
+    }
+  }
 }
 
 template("banjo_dummy") {
diff --git a/build/banjo/fidl_adapter.gni b/build/banjo/fidl_adapter.gni
new file mode 100644
index 0000000..7b7927e
--- /dev/null
+++ b/build/banjo/fidl_adapter.gni
@@ -0,0 +1,114 @@
+# Copyright 2021 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/banjo/toolchain.gni")
+import("//build/compiled_action.gni")
+import("//build/fidl/fidl_library.gni")
+import("//build/fidl/toolchain.gni")
+import("//build/testing/golden_file.gni")
+
+# Exposes a Banjo library as a FIDL library.
+#
+# This template is used in the Banjo deprecation project (fxb/67196).
+# Banjo sources are processed as FIDL sources, generating a FIDL IR file. That
+# file is then fed to fidlgen_banjo which generates bindings identical to the
+# ones generated by the legacy banjo tool (as verified by a build step).
+#
+# This template accepts the same parameters as the `banjo` template, but only
+# uses `name` and `sources`.
+
+template("fidl_adapter") {
+  not_needed(invoker,
+             [
+               "needs_composite",
+               "sdk_category",
+             ])
+
+  main_target_name = "$target_name.fidl"
+  library_name = target_name
+  if (defined(invoker.name)) {
+    library_name = invoker.name
+  }
+
+  if (current_toolchain == fidl_toolchain) {
+    fidl_library(main_target_name) {
+      name = library_name
+      sources = invoker.sources
+    }
+  } else if (is_fuchsia) {
+    not_needed(invoker, "*")
+
+    c_target_name = "${library_name}_banjo_c"
+    fidlgen_target_name = "$c_target_name.gen"
+    config_target_name = "$c_target_name.config"
+    verification_target_name = "${library_name}.verify"
+
+    ir_dep = ":$main_target_name($fidl_toolchain)"
+    ir_gen_dir = get_label_info(ir_dep, "target_gen_dir")
+    ir_file = "$ir_gen_dir/$main_target_name.fidl.json"
+
+    header_path = string_replace(library_name, ".", "/")
+    header_path = string_replace(header_path, "_", "-")
+    header_include_base = "$target_gen_dir/$c_target_name"
+    header_file = "$header_include_base/$header_path/c/banjo.h"
+
+    # Run fidlgen_banjo on the IR and generate C code.
+    compiled_action(fidlgen_target_name) {
+      tool = "//src/devices/tools/fidlgen_banjo:bin"
+      tool_output_name = "fidlgen_banjo"
+
+      inputs = [ ir_file ]
+
+      outputs = [ header_file ]
+
+      deps = [ ir_dep ]
+
+      args = [
+        "--ir",
+        rebase_path(ir_file, root_build_dir),
+        "--output",
+        rebase_path(header_file, root_build_dir),
+        "--backend",
+        "dummy_c",
+      ]
+    }
+
+    banjo_dep = ":${target_name}_c_compile($banjo_toolchain)"
+    banjo_gen_dir = get_label_info(banjo_dep, "root_gen_dir")
+    banjo_header_file = "$banjo_gen_dir/$header_path/c/banjo.h"
+
+    # Verify that the new-style and old-style bindings are identical.
+    golden_file(verification_target_name) {
+      golden = banjo_header_file
+
+      current = header_file
+
+      deps = [
+        ":$fidlgen_target_name",
+        banjo_dep,
+      ]
+    }
+
+    # This config ensures dependents can find the generated header within the
+    # output directory.
+    config(config_target_name) {
+      include_dirs = [ header_include_base ]
+    }
+
+    # Exposes the bindings as C sources to the rest of the build.
+    # Eventually this target will be surfaced via the `fidl` template.
+    source_set(c_target_name) {
+      public = [ header_file ]
+
+      public_configs = [ ":$config_target_name" ]
+
+      deps = [ ":$fidlgen_target_name" ]
+
+      deps += [ ":$verification_target_name" ]
+    }
+  } else {
+    assert(false,
+           "Unable to translate Banjo target to FIDL in $current_toolchain.")
+  }
+}