Add dependencies from platform_bootclasspath to contents

Adds a FinalDeps mutator to add dependencies from the
platform_bootclasspath to the configured boot jars which can be from
either the platform or any apex. It adds dependencies for every
configured boot jar, whether in ArtApexJars, BootJars or
UpdatableBootJars.

At the moment the dependencies are only used for testing purposes
but following changes will make more use of them.

Bug: 177892522
Test: m nothing
Change-Id: I981305bf45bc20539a3d36987252f490e2b885cc
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 5507077..5272eaf 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/dexpreopt"
+	"github.com/google/blueprint"
 )
 
 func init() {
@@ -25,10 +26,38 @@
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
+
+	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("platform_bootclasspath_deps", platformBootclasspathDepsMutator)
+	})
 }
 
+type platformBootclasspathDependencyTag struct {
+	blueprint.BaseDependencyTag
+
+	name string
+}
+
+// Avoid having to make platform bootclasspath content visible to the platform bootclasspath.
+//
+// This is a temporary workaround to make it easier to migrate to platform bootclasspath with proper
+// dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (t platformBootclasspathDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// The tag used for the dependency between the platform bootclasspath and any configured boot jars.
+var platformBootclasspathModuleDepTag = platformBootclasspathDependencyTag{name: "module"}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathDependencyTag{}
+
 type platformBootclasspathModule struct {
 	android.ModuleBase
+
+	// The apex:module pairs obtained from the configured modules.
+	//
+	// Currently only for testing.
+	configuredModules []android.Module
 }
 
 func platformBootclasspathFactory() android.Module {
@@ -47,7 +76,71 @@
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
+func platformBootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
+	m := ctx.Module()
+	if p, ok := m.(*platformBootclasspathModule); ok {
+		// Add dependencies on all the modules configured in the "art" boot image.
+		artImageConfig := genBootImageConfigs(ctx)[artBootImageName]
+		addDependenciesOntoBootImageModules(ctx, artImageConfig.modules)
+
+		// Add dependencies on all the modules configured in the "boot" boot image. That does not
+		// include modules configured in the "art" boot image.
+		bootImageConfig := p.getImageConfig(ctx)
+		addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules)
+
+		// Add dependencies on all the updatable modules.
+		updatableModules := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
+		addDependenciesOntoBootImageModules(ctx, updatableModules)
+	}
+}
+
+func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) {
+	var variations []blueprint.Variation
+	if apex != "platform" {
+		// Pick the correct apex variant.
+		variations = []blueprint.Variation{
+			{Mutator: "apex", Variation: apex},
+		}
+	}
+
+	addedDep := false
+	if ctx.OtherModuleDependencyVariantExists(variations, name) {
+		ctx.AddFarVariationDependencies(variations, tag, name)
+		addedDep = true
+	}
+
+	// Add a dependency on the prebuilt module if it exists.
+	prebuiltName := android.PrebuiltNameFromSource(name)
+	if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) {
+		ctx.AddVariationDependencies(variations, tag, prebuiltName)
+		addedDep = true
+	}
+
+	// If no appropriate variant existing for this, so no dependency could be added, then it is an
+	// error, unless missing dependencies are allowed. The simplest way to handle that is to add a
+	// dependency that will not be satisfied and the default behavior will handle it.
+	if !addedDep {
+		ctx.AddFarVariationDependencies(variations, tag, name)
+	}
+}
+
+func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList) {
+	for i := 0; i < modules.Len(); i++ {
+		apex := modules.Apex(i)
+		name := modules.Jar(i)
+
+		addDependencyOntoApexModulePair(ctx, apex, name, platformBootclasspathModuleDepTag)
+	}
+}
+
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+		tag := ctx.OtherModuleDependencyTag(module)
+		if tag == platformBootclasspathModuleDepTag {
+			b.configuredModules = append(b.configuredModules, module)
+		}
+	})
+
 	// Nothing to do if skipping the dexpreopt of boot image jars.
 	if SkipDexpreoptBootJars(ctx) {
 		return