pw_protobuf: Add support for imports

This updates the protobuf codegen to support importing external .proto
files, and the GN proto build to set include directories for proto
dependencies.

Change-Id: I28f4884935a266b1b40de1935ab67af9cad44e11
diff --git a/pw_protobuf_compiler/proto.gni b/pw_protobuf_compiler/proto.gni
index 97ac763..ee2e2dd 100644
--- a/pw_protobuf_compiler/proto.gni
+++ b/pw_protobuf_compiler/proto.gni
@@ -1,4 +1,4 @@
-# Copyright 2019 The Pigweed Authors
+# Copyright 2020 The Pigweed Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 # use this file except in compliance with the License. You may obtain a copy of
@@ -26,9 +26,14 @@
 #  protos: List of input .proto files.
 template("_pw_pwpb_proto_library") {
   _proto_gen_dir = "$root_gen_dir/protos"
-  _outputs = process_file_template(
-          invoker.protos,
-          "$_proto_gen_dir/{{source_root_relative_dir}}/{{source_name_part}}.pwpb.h")
+  _module_path = get_path_info(".", "abspath")
+  _relative_proto_paths = rebase_path(invoker.protos, _module_path)
+
+  _outputs = []
+  foreach(_proto, _relative_proto_paths) {
+    _output = string_replace(_proto, ".proto", ".pwpb.h")
+    _outputs += [ "$_proto_gen_dir/$_output" ]
+  }
 
   _gen_target = "${target_name}_gen"
   pw_python_script(_gen_target) {
@@ -37,34 +42,33 @@
              "--language",
              "cc",
              "--module-path",
-             "//",
+             _module_path,
+             "--include-file",
+             invoker.include_file,
              "--out-dir",
              _proto_gen_dir,
            ] + get_path_info(invoker.protos, "abspath")
     inputs = invoker.protos
     outputs = _outputs
-
+    deps = invoker.deps
     if (defined(invoker.protoc_deps)) {
-      deps = invoker.protoc_deps
+      deps += invoker.protoc_deps
     }
   }
 
   # For C++ proto files, the generated proto directory is added as an include
   # path for the code. This requires using "all_dependent_configs" to force the
   # include on any code that transitively depends on the generated protos.
-  _include_root = rebase_path(get_path_info(".", "abspath"), "//")
   _include_config_target = "${target_name}_includes"
   config(_include_config_target) {
-    include_dirs = [ "$_proto_gen_dir/$_include_root" ]
+    include_dirs = [ "$_proto_gen_dir" ]
   }
 
   # Create a library with the generated source files.
-  # TODO(frolv): This currently only supports pw_protobuf, which is header-only.
-  # Figure out how to support .cc files.
   source_set(target_name) {
     all_dependent_configs = [ ":$_include_config_target" ]
-    deps = [ ":$_gen_target" ] + invoker.deps
-    public_deps = [ dir_pw_protobuf ]
+    deps = [ ":$_gen_target" ]
+    public_deps = [ dir_pw_protobuf ] + invoker.gen_deps
     sources = get_target_outputs(":$_gen_target")
     public = filter_include(sources, [ "*.pwpb.h" ])
   }
@@ -81,12 +85,18 @@
          "\$dir_third_party_nanopb must be set to compile nanopb protobufs")
 
   _proto_gen_dir = "$root_gen_dir/protos"
-  _outputs = process_file_template(
-          invoker.protos,
-          [
-            "$_proto_gen_dir/{{source_root_relative_dir}}/{{source_name_part}}.pb.h",
-            "$_proto_gen_dir/{{source_root_relative_dir}}/{{source_name_part}}.pb.c",
-          ])
+  _module_path = get_path_info(".", "abspath")
+  _relative_proto_paths = rebase_path(invoker.protos, _module_path)
+
+  _outputs = []
+  foreach(_proto, _relative_proto_paths) {
+    _output_h = string_replace(_proto, ".proto", ".pb.h")
+    _output_c = string_replace(_proto, ".proto", ".pb.c")
+    _outputs += [
+      "$_proto_gen_dir/$_output_h",
+      "$_proto_gen_dir/$_output_c",
+    ]
+  }
 
   _nanopb_plugin = "$dir_third_party_nanopb/generator/protoc-gen-nanopb"
   if (host_os == "win") {
@@ -102,9 +112,11 @@
              "--language",
              "nanopb",
              "--module-path",
-             "//",
+             _module_path,
              "--include-paths",
              "$dir_third_party_nanopb/generator/proto",
+             "--include-file",
+             invoker.include_file,
              "--out-dir",
              _proto_gen_dir,
              "--custom-plugin",
@@ -113,8 +125,9 @@
     inputs = invoker.protos
     outputs = _outputs
 
+    deps = invoker.deps
     if (defined(invoker.protoc_deps)) {
-      deps = invoker.protoc_deps
+      deps += invoker.protoc_deps
     }
   }
 
@@ -133,11 +146,8 @@
   # Create a library with the generated source files.
   source_set(target_name) {
     all_dependent_configs = [ ":$_include_config_target" ]
-    deps = invoker.deps
-    public_deps = [
-      ":$_gen_target",
-      dir_third_party_nanopb,
-    ]
+    deps = [ ":$_gen_target" ]
+    public_deps = [ dir_third_party_nanopb ] + invoker.gen_deps
     sources = get_target_outputs(":$_gen_target")
     public = filter_include(sources, [ "*.pb.h" ])
   }
@@ -167,11 +177,13 @@
              "go",
              "--module-path",
              "//",
+             "--include-file",
+             invoker.include_file,
              "--out-dir",
              _proto_gen_dir,
            ] + get_path_info(invoker.protos, "abspath")
     inputs = invoker.protos
-    deps = invoker.deps
+    deps = invoker.deps + invoker.gen_deps
     stamp = true
   }
 }
@@ -203,18 +215,40 @@
   assert(defined(invoker.sources) && invoker.sources != [],
          "pw_proto_codegen requires .proto source files")
 
-  foreach(_gen, pw_protobuf_generators) {
+  # For each proto target, create a file which collects the base directories of
+  # all of its dependencies to list as include paths to protoc.
+  _include_metadata_target = "${target_name}_include_paths"
+  _include_metadata_file = "${target_gen_dir}/${target_name}_includes.txt"
+  generated_file(_include_metadata_target) {
     if (defined(invoker.deps)) {
-      _gen_deps = process_file_template(invoker.deps, "{{source}}_${_gen}")
+      # Collect metadata from the include path files of each dependency.
+      deps = process_file_template(invoker.deps, "{{source}}_include_paths")
     } else {
-      _gen_deps = []
+      deps = []
     }
+    data_keys = [ "protoc_includes" ]
+    outputs = [ _include_metadata_file ]
+
+    # Indicate this library's base directory for its dependents.
+    metadata = {
+      protoc_includes = [ rebase_path(".", root_out_dir) ]
+    }
+  }
+
+  foreach(_gen, pw_protobuf_generators) {
     _lang_target = "${target_name}_${_gen}"
 
     if (_gen == "pwpb") {
+      _gen_deps = []
+      if (defined(invoker.deps)) {
+        _gen_deps = process_file_template(invoker.deps, "{{source}}_${_gen}")
+      }
+
       _pw_pwpb_proto_library(_lang_target) {
         protos = invoker.sources
-        deps = _gen_deps
+        deps = [ ":$_include_metadata_target" ]
+        include_file = _include_metadata_file
+        gen_deps = _gen_deps
 
         # List the pw_protobuf plugin's files as a dependency to recompile
         # generated code if they are modified.
@@ -223,12 +257,16 @@
     } else if (_gen == "nanopb") {
       _pw_nanopb_proto_library(_lang_target) {
         protos = invoker.sources
-        deps = _gen_deps
+        deps = [ ":$_include_metadata_target" ]
+        include_file = _include_metadata_file
+        gen_deps = _gen_deps
       }
     } else if (_gen == "go") {
       _pw_go_proto_library(_lang_target) {
         protos = invoker.sources
-        deps = _gen_deps
+        deps = [ ":$_include_metadata_target" ]
+        include_file = _include_metadata_file
+        gen_deps = _gen_deps
       }
     } else {
       assert(false,