From 5f6c5d795e098042f27a086177b4ce5f83ca0cec Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr@google.com>
Date: Fri, 26 Apr 2019 22:59:32 +0000
Subject: [PATCH] [zircon][gn] fidl_library() support for llcpp

This implements half the FIDL generation for LLCPP bindings.  It
can't be done fully the straightforward way until we can build the
fidlgen tool (Go).  Instead, this integrates it halfway by doing the
JSON generation part.  Then instead of running fidlgen, it produces
a build API module (JSON) describing how it would have run fidlgen,
and expects to find the fidlgen output files in the source tree.
Later integration will take that information and do the regeneration
and/or check that it doesn't need to be done, so that the
higher-level build can verify what the Zircon build alone can't yet.

Bug: BLD-442 #comment fidl_library() support for llcpp
Bug: BLD-353 #comment Switchback for FIDL LLCPP bindings before unification
Change-Id: Ied2e9972da1c1bb4957e29f98c721d96d8839d97
---
 zircon/BUILD.gn                 |  36 ++++++++
 zircon/public/gn/fidl.gni       |   6 +-
 zircon/public/gn/fidl/llcpp.gni | 152 ++++++++++++++++++++++++++++++++
 3 files changed, 193 insertions(+), 1 deletion(-)
 create mode 100644 zircon/public/gn/fidl/llcpp.gni

diff --git a/zircon/BUILD.gn b/zircon/BUILD.gn
index c65e65f7f4a..af4441dcce3 100644
--- a/zircon/BUILD.gn
+++ b/zircon/BUILD.gn
@@ -484,6 +484,7 @@ build_api_module("legacy_images") {
   }
 }
 
+
 # This describes all the generated source files in the build.
 #
 # The intent is that telling Ninja to build all these individual files
@@ -500,6 +501,41 @@ build_api_module("generated_sources") {
   data_keys = [ "generated_sources" ]
 }
 
+# This describes pre-generated FIDL bindings that are required by the build.
+#
+# TODO(BLD-441): This will go away when fidlgen is built in this build.
+# See $zx/public/gn/fidl/llcpp.gni, where the metadata is generated.
+#
+# Type: list(scope)
+#
+#   name
+#     Required: The FIDL library name as it appears in FIDL source (with dots).
+#     Type: string
+#
+#   label
+#     Required: The label of the fidl_library() target.
+#     Type: label_no_toolchain
+#
+#   json
+#     Required: Path to the fidlc --json output.
+#     Type: path relative to $root_build_dir
+#
+#   target_gen_dir
+#     Required: The place in the source tree where generated files go.
+#     Type: path relative to $root_build_dir
+#
+#   args
+#     Required: Argument list for `fidlgen` if run in $root_build_dir.
+#     Type: list(string)
+#
+build_api_module("fidl_gen") {
+  testonly = true
+  data_keys = [ "fidl_gen" ]
+  deps = [
+    ":all-cpu",
+  ]
+}
+
 # TODO(TC-303): ids.txt is deprecated and will be removed.
 if (current_toolchain == default_toolchain) {
   action("ids") {
diff --git a/zircon/public/gn/fidl.gni b/zircon/public/gn/fidl.gni
index 029cbc40e06..78102688e20 100644
--- a/zircon/public/gn/fidl.gni
+++ b/zircon/public/gn/fidl.gni
@@ -86,7 +86,10 @@ import("$zx/public/gn/fidl/fidlc.gni")
 # TODO(mcgrathr): Add more language generators.  For language support from
 # a different petal, add a build argument to contribute to this list via
 # default_overrides.
-fidl_support = [ "$zx/public/gn/fidl/c.gni" ]
+fidl_support = [
+  "$zx/public/gn/fidl/c.gni",
+  "$zx/public/gn/fidl/llcpp.gni",
+]
 
 # Each support module defines $fidl_support_fidlc and
 # $fidl_support_templates lists in its .gni file.
@@ -333,6 +336,7 @@ template("fidl_library") {
                                  "visibility",
                                  "testonly",
                                ])
+        fidl_sources = invoker.sources
 
         # The bindings-library template can map these to corresponding
         # bindings-library targets.
diff --git a/zircon/public/gn/fidl/llcpp.gni b/zircon/public/gn/fidl/llcpp.gni
new file mode 100644
index 00000000000..93b75462169
--- /dev/null
+++ b/zircon/public/gn/fidl/llcpp.gni
@@ -0,0 +1,152 @@
+# Copyright 2019 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("$zx/public/gn/subtarget_aliases.gni")
+
+# This is the $fidl_support module for "low-level" C++ bindings.
+# See fidl_library() for details.  This file should not normally be
+# imported by other code.
+
+# This tells fidl_library() to invoke fidl_llcpp_library().
+fidl_support_templates = [
+  {
+    import = "$zx/public/gn/fidl/llcpp.gni"
+    target = "fidl_llcpp_library"
+    fidlc = "json"
+  },
+]
+
+# This tells fidl_library() what fidlc outputs fidl_llcpp_library() requires.
+fidl_support_fidlc = [
+  {
+    name = "json"
+    files = [
+      {
+        switch = "--json"
+        path = "fidl.json"
+      },
+    ]
+  },
+]
+
+# Provide LLCPP bindings for fidl_library().  **Do not use directly!**
+#
+# This is never used directly, but only indirectly by fidl_library().
+# See there for details.
+template("fidl_llcpp_library") {
+  fidl_target = target_name
+  library_target = "$fidl_target.llcpp"
+  not_needed(invoker, "*")
+  if (current_toolchain != default_toolchain) {
+    # TODO(BLD-441): For now the bindings have to be generated and
+    # checked into the source tree.  So this is just a normal vanilla
+    # C++ library, except that its sources live in the gen/llcpp/
+    # subdirectory of the fidl_library() target's source directory and
+    # the public headers live in gen/llcpp/include/.  The Fuchsia GN
+    # build has code in //TBD to regenerate these files and check that
+    # the copies in the source tree are up to date.
+
+    config("_fidl_llcpp_library.config.$library_target") {
+      visibility = [
+        ":$library_target.headers",
+        ":$library_target.static",
+      ]
+      include_dirs = [ "gen/llcpp/include" ]
+    }
+
+    library(library_target) {
+      forward_variables_from(invoker,
+                             [
+                               "visibility",
+                               "testonly",
+                             ])
+
+      sources = [
+        "gen/llcpp/fidl.cc",
+      ]
+
+      configs += [ "$zx/public/gn/config:visibility_hidden" ]
+
+      # Users of the bindings library need the generated headers.
+      public_configs = [ ":_fidl_llcpp_library.config.$library_target" ]
+
+      deps = []
+      public_deps = []
+
+      # The generated headers of a dependent fidl_library() will #include the
+      # generated headers for its dependencies' bindings libraries, so those
+      # headers are needed in public_deps.  The generated bindings code may
+      # call into its dependencies' bindings code, so the libraries
+      # themselves are needed in deps too.
+      foreach(dep, invoker.fidl_deps) {
+        deps += [ "$dep.llcpp" ]
+        public_deps += [ "$dep.llcpp.headers" ]
+      }
+
+      # The generated code uses these.
+      public_deps += [ "$zx/system/ulib/fidl:fidl-llcpp.headers" ]
+      deps += [ "$zx/system/ulib/fidl:fidl-llcpp" ]
+
+      # TODO(BLD-441): Get the metadata below into the dependency graph.
+      # Putting the metadata here directly would duplicate the information
+      # across different toolchains that build the library.  So instead,
+      # use a dummy group() in $default_toolchain as a single node to hold
+      # the metadata.
+      deps += [ ":${fidl_target}.llcpp($default_toolchain)" ]
+    }
+
+    # Things normally depend on "fidl/foo:llcpp" rather than
+    # "fidl/foo:foo.llcpp".
+    subtarget_aliases(target_name) {
+      forward_variables_from(invoker,
+                             [
+                               "visibility",
+                               "testonly",
+                             ])
+      outputs = [
+        "llcpp",
+        "llcpp.headers",
+        "llcpp.static",
+      ]
+    }
+  } else {
+    # TODO(BLD-441): This exists only for the deps above.  This generates
+    # metadata for the build_api_module("fidl_gen") at top-level that
+    # informs the //tool/fidlgen_llcpp_zircon scripts what generated sources
+    # need to be updated.
+    # This will go away when the bindings generation is done directly here.
+    fidlc_outputs = invoker.fidlc_outputs
+    assert(fidlc_outputs == [ fidlc_outputs[0] ])
+    group(library_target) {
+      metadata = {
+        fidl_gen = [
+          {
+            label = get_label_info(":$target_name", "label_no_toolchain")
+            fidl_sources = rebase_path(invoker.fidl_sources, root_build_dir)
+            name = invoker.fidl_name
+            target_gen_dir = rebase_path("gen/llcpp", root_build_dir)
+            json = rebase_path(fidlc_outputs[0], root_build_dir)
+
+            # TODO(BLD-442): Could generate an individual response file
+            # here that could be used very simply to drive running fidlgen.
+            # Alternatively, give fidlgen as "JSON response file" feature:
+            # `fidlgen -json-args foo.json` reads a dictionary from the
+            # file.  Then we could write that JSON fragment right here in
+            # place of this args list.
+            args = [
+              "-json",
+              json,
+              "-include-base",
+              "$target_gen_dir/include",
+              "-header",
+              "$target_gen_dir/include/${invoker.fidl_path}/llcpp/fidl.h",
+              "-source",
+              "$target_gen_dir/fidl.cc",
+            ]
+          },
+        ]
+      }
+    }
+  }
+}
-- 
GitLab