Merge "Make apex modules support tagged dists"
diff --git a/build_test.bash b/build_test.bash
index a53a585..accca0f 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -43,14 +43,9 @@
     ;;
 esac
 
-function bazel_cleanup {
-  "${TOP}/tools/bazel" shutdown
-}
-trap bazel_cleanup EXIT
-
 echo
 echo "Running Bazel smoke test..."
-"${TOP}/tools/bazel" info
+"${TOP}/tools/bazel" --batch --max_idle_secs=1 info
 
 echo
 echo "Running Soong test..."
diff --git a/cc/cc.go b/cc/cc.go
index 1f31872..5e4faf2 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -592,6 +592,11 @@
 	return ok && ccLibDepTag.static()
 }
 
+func IsHeaderDepTag(depTag blueprint.DependencyTag) bool {
+	ccLibDepTag, ok := depTag.(libraryDependencyTag)
+	return ok && ccLibDepTag.header()
+}
+
 func IsRuntimeDepTag(depTag blueprint.DependencyTag) bool {
 	ccDepTag, ok := depTag.(dependencyTag)
 	return ok && ccDepTag == runtimeDepTag
diff --git a/cc/linkable.go b/cc/linkable.go
index 60ab6df..0609b28 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -64,6 +64,10 @@
 	return libraryDependencyTag{Kind: staticLibraryDependency}
 }
 
+func HeaderDepTag() blueprint.DependencyTag {
+	return libraryDependencyTag{Kind: headerLibraryDependency}
+}
+
 type SharedLibraryInfo struct {
 	SharedLibrary           android.Path
 	UnstrippedSharedLibrary android.Path
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 900eb7a..e706547 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -87,6 +87,7 @@
 
 	rule := android.NewRuleBuilder()
 	checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars").
+		Input(ctx.Config().HostToolPath(ctx, "dexdump")).
 		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
 
 	// If this is not an unbundled build and missing dependencies are not allowed
@@ -96,14 +97,9 @@
 	// Iterate over the module names on the boot classpath in order
 	for _, name := range android.SortedStringKeys(moduleToApex) {
 		if apexVariant, ok := nameToApexVariant[name]; ok {
-			if dep, ok := apexVariant.(Dependency); ok {
-				// Add the implementation jars for the module to be checked. This uses implementation
-				// and resources jar as that is what the previous make based check uses.
-				for _, jar := range dep.ImplementationAndResourcesJars() {
-					checkBootJars.Input(jar)
-				}
-			} else if _, ok := apexVariant.(*DexImport); ok {
-				// TODO(b/171479578): ignore deximport when doing package check until boot_jars.go can check dex jars.
+			if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok {
+				// Add the dex implementation jar for the module to be checked.
+				checkBootJars.Input(dep.DexJarBuildPath())
 			} else {
 				ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
 			}
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
diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
index cf4ef27..63fc9a9 100755
--- a/scripts/check_boot_jars/check_boot_jars.py
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -3,7 +3,7 @@
 """
 Check boot jars.
 
-Usage: check_boot_jars.py <package_allow_list_file> <jar1> <jar2> ...
+Usage: check_boot_jars.py <dexdump_path> <package_allow_list_file> <jar1> <jar2> ...
 """
 import logging
 import os.path
@@ -38,28 +38,44 @@
     return False
   return True
 
+# Pattern that matches the class descriptor in a "Class descriptor" line output
+# by dexdump and extracts the class name - with / instead of .
+CLASS_DESCRIPTOR_RE = re.compile("'L([^;]+);'")
 
-def CheckJar(allow_list_path, jar):
-  """Check a jar file.
+def CheckDexJar(dexdump_path, allow_list_path, jar):
+  """Check a dex jar file.
   """
-  # Get the list of files inside the jar file.
-  p = subprocess.Popen(args='jar tf %s' % jar,
+  # Get the class descriptor lines in the dexdump output. This filters out lines
+  # that do not contain class descriptors to reduce the size of the data read by
+  # this script.
+  p = subprocess.Popen(args='%s %s | grep "Class descriptor "' % (dexdump_path, jar),
       stdout=subprocess.PIPE, shell=True)
   stdout, _ = p.communicate()
   if p.returncode != 0:
     return False
-  items = stdout.split()
+  # Split the output into lines
+  lines = stdout.split('\n')
   classes = 0
-  for f in items:
-    if f.endswith('.class'):
-      classes += 1
-      package_name = os.path.dirname(f)
-      package_name = package_name.replace('/', '.')
-      if not package_name or not allow_list_re.match(package_name):
-        print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or'
-                              ' not in the allow list %s of packages allowed on the bootclasspath.'
-                              % (jar, f, package_name, allow_list_path))
-        return False
+  for line in lines:
+    # The last line will be empty
+    if line == '':
+      continue
+    # Try and find the descriptor on the line. Fail immediately if it cannot be found
+    # as the dexdump output has probably changed.
+    found = CLASS_DESCRIPTOR_RE.search(line)
+    if not found:
+      print >> sys.stderr, ('Could not find class descriptor in line `%s`' % line)
+      return False
+    # Extract the class name (using / instead of .) from the class descriptor line
+    f = found.group(1)
+    classes += 1
+    package_name = os.path.dirname(f)
+    package_name = package_name.replace('/', '.')
+    if not package_name or not allow_list_re.match(package_name):
+      print >> sys.stderr, ('Error: %s contains class file %s, whose package name "%s" is empty or'
+                            ' not in the allow list %s of packages allowed on the bootclasspath.'
+                            % (jar, f, package_name, allow_list_path))
+      return False
   if classes == 0:
     print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
     return False
@@ -67,17 +83,18 @@
 
 
 def main(argv):
-  if len(argv) < 2:
+  if len(argv) < 3:
     print __doc__
     return 1
-  allow_list_path = argv[0]
+  dexdump_path = argv[0]
+  allow_list_path = argv[1]
 
   if not LoadAllowList(allow_list_path):
     return 1
 
   passed = True
-  for jar in argv[1:]:
-    if not CheckJar(allow_list_path, jar):
+  for jar in argv[2:]:
+    if not CheckDexJar(dexdump_path, allow_list_path, jar):
       passed = False
   if not passed:
     return 1