rust_grpcio well known types support, default deps

The usage of the well known type Empty requires a hack in the module
above the grpc implementation, this is now the generated stem_mod.rs

This also adds additional implicit dependencies that are required by
the grpc protobuf generated code. This includes the addition of a
'header_libs' property for library dependencies which export include
paths required by protos.

We also now include both the protos and the grpcio in the library
variant via the mod_stem.rs.

Bug: 172592789
Bug: 171504899
Test: m nothing
Test: Example rust_grpcio module build command includes dependencies,
      include paths.
Change-Id: I187a13cd5cdea991828a1020314de16727e4f74e
diff --git a/rust/protobuf.go b/rust/protobuf.go
index ca40154..76fed30 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -15,6 +15,9 @@
 package rust
 
 import (
+	"fmt"
+	"strings"
+
 	"android/soong/android"
 )
 
@@ -22,6 +25,10 @@
 	defaultProtobufFlags = []string{""}
 )
 
+const (
+	grpcSuffix = "_grpc"
+)
+
 type PluginType int
 
 const (
@@ -44,6 +51,9 @@
 
 	// List of additional flags to pass to aprotoc
 	Proto_flags []string `android:"arch_variant"`
+
+	// List of libraries which export include paths required for this module
+	Header_libs []string `android:"arch_variant"`
 }
 
 type protobufDecorator struct {
@@ -72,6 +82,11 @@
 		ctx.PropertyErrorf("proto", "invalid path to proto file")
 	}
 
+	// Add exported dependency include paths
+	for _, include := range deps.depIncludePaths {
+		protoFlags.Flags = append(protoFlags.Flags, "-I"+include.String())
+	}
+
 	stem := proto.BaseSourceProvider.getStem(ctx)
 	// rust protobuf-codegen output <stem>.rs
 	stemFile := android.PathForModuleOut(ctx, stem+".rs")
@@ -79,17 +94,39 @@
 	modFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs")
 	// mod_<stem>.rs is the main/first output file to be included/compiled
 	outputs := android.WritablePaths{modFile, stemFile}
+	if proto.plugin == Grpc {
+		outputs = append(outputs, android.PathForModuleOut(ctx, stem+grpcSuffix+".rs"))
+	}
 	depFile := android.PathForModuleOut(ctx, "mod_"+stem+".d")
 
 	rule := android.NewRuleBuilder()
 	android.ProtoRule(ctx, rule, protoFile.Path(), protoFlags, protoFlags.Deps, outDir, depFile, outputs)
-	rule.Command().Text("printf '// @generated\\npub mod %s;\\n' '" + stem + "' >").Output(modFile)
+	rule.Command().Text("printf '" + proto.getModFileContents(ctx) + "' >").Output(modFile)
 	rule.Build(pctx, ctx, "protoc_"+protoFile.Path().Rel(), "protoc "+protoFile.Path().Rel())
 
 	proto.BaseSourceProvider.OutputFiles = android.Paths{modFile, stemFile}
 	return modFile
 }
 
+func (proto *protobufDecorator) getModFileContents(ctx ModuleContext) string {
+	stem := proto.BaseSourceProvider.getStem(ctx)
+	lines := []string{
+		"// @generated",
+		fmt.Sprintf("pub mod %s;", stem),
+	}
+
+	if proto.plugin == Grpc {
+		lines = append(lines, fmt.Sprintf("pub mod %s%s;", stem, grpcSuffix))
+		lines = append(
+			lines,
+			"pub mod empty {",
+			"    pub use protobuf::well_known_types::Empty;",
+			"}")
+	}
+
+	return strings.Join(lines, "\\n")
+}
+
 func (proto *protobufDecorator) setupPlugin(ctx ModuleContext, protoFlags android.ProtoFlags, outDir android.ModuleOutPath) (android.Paths, android.ProtoFlags) {
 	pluginPaths := []android.Path{}
 
@@ -118,6 +155,13 @@
 func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
+	deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
+
+	if proto.plugin == Grpc {
+		deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
+		deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
+	}
+
 	return deps
 }
 
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 7c39071..845911f 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -28,6 +28,16 @@
 			proto: "buf.proto",
 			crate_name: "rust_proto",
 			source_stem: "buf",
+			shared_libs: ["libfoo_shared"],
+			static_libs: ["libfoo_static"],
+		}
+		cc_library_shared {
+			name: "libfoo_shared",
+			export_include_dirs: ["shared_include"],
+		}
+		cc_library_static {
+			name: "libfoo_static",
+			export_include_dirs: ["static_include"],
 		}
 	`)
 	// Check that libprotobuf is added as a dependency.
@@ -43,6 +53,13 @@
 		t.Errorf("expected %q in %q", w, cmd)
 	}
 
+	// Check exported include directories
+	if w := "-Ishared_include"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
+	if w := "-Istatic_include"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
 }
 
 func TestRustGrpcio(t *testing.T) {
@@ -52,6 +69,16 @@
 			proto: "buf.proto",
 			crate_name: "rust_grpcio",
 			source_stem: "buf",
+			shared_libs: ["libfoo_shared"],
+			static_libs: ["libfoo_static"],
+		}
+		cc_library_shared {
+			name: "libfoo_shared",
+			export_include_dirs: ["shared_include"],
+		}
+		cc_library_static {
+			name: "libfoo_static",
+			export_include_dirs: ["static_include"],
 		}
 	`)
 
@@ -61,10 +88,33 @@
 		t.Errorf("libprotobuf dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
 	}
 
+	// Check that libgrpcio is added as a dependency.
+	if !android.InList("libgrpcio", librust_grpcio_module.Properties.AndroidMkDylibs) {
+		t.Errorf("libgrpcio dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
+	}
+
+	// Check that libfutures is added as a dependency.
+	if !android.InList("libfutures", librust_grpcio_module.Properties.AndroidMkDylibs) {
+		t.Errorf("libfutures dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
+	}
+
 	// Make sure the correct plugin is being used.
-	librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("buf.rs")
+	librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("buf_grpc.rs")
 	cmd := librust_grpcio_out.RuleParams.Command
 	if w := "protoc-gen-grpc"; !strings.Contains(cmd, w) {
 		t.Errorf("expected %q in %q", w, cmd)
 	}
+
+	// Check exported include directories
+	if w := "-Ishared_include"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
+	if w := "-Istatic_include"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
+
+	// Check that we're including the exported directory from libprotobuf-cpp-full
+	if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
 }
diff --git a/rust/rust.go b/rust/rust.go
index e65b90e..5b94045 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -229,6 +229,7 @@
 	ProcMacros []string
 	SharedLibs []string
 	StaticLibs []string
+	HeaderLibs []string
 
 	CrtBegin, CrtEnd string
 }
@@ -838,6 +839,11 @@
 				directSharedLibDeps = append(directSharedLibDeps, ccDep)
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
 				exportDep = true
+			case cc.IsHeaderDepTag(depTag):
+				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
+				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
+				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
+				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 			case depTag == cc.CrtBeginDepTag:
 				depPaths.CrtBegin = linkObject
 			case depTag == cc.CrtEndDepTag:
@@ -983,6 +989,8 @@
 		blueprint.Variation{Mutator: "link", Variation: "static"}),
 		cc.StaticDepTag(), deps.StaticLibs...)
 
+	actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
+
 	crtVariations := cc.GetCrtVariations(ctx, mod)
 	if deps.CrtBegin != "" {
 		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, deps.CrtBegin)
diff --git a/rust/testing.go b/rust/testing.go
index 4a1894c..4001566 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -79,6 +79,13 @@
 			nocrt: true,
 			system_shared_libs: [],
 		}
+		cc_library {
+			name: "libprotobuf-cpp-full",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			export_include_dirs: ["libprotobuf-cpp-full-includes"],
+		}
 		rust_library {
 			name: "libstd",
 			crate_name: "std",
@@ -103,6 +110,18 @@
 			srcs: ["foo.rs"],
 			host_supported: true,
 		}
+		rust_library {
+			name: "libgrpcio",
+			crate_name: "grpcio",
+			srcs: ["foo.rs"],
+			host_supported: true,
+		}
+		rust_library {
+			name: "libfutures",
+			crate_name: "futures",
+			srcs: ["foo.rs"],
+			host_supported: true,
+		}
 
 ` + cc.GatherRequiredDepsForTest(android.NoOsType)
 	return bp