Merge "Move boot jars package check into platform_bootclasspath" am: 4aea00ec18

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1737753

Change-Id: I4d85e0f1abac976520b0127f314566348fbc33ee
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 7abda80..5ec9f71 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -18,37 +18,6 @@
 	"android/soong/android"
 )
 
-func init() {
-	android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory)
-}
-
-func bootJarsSingletonFactory() android.Singleton {
-	return &bootJarsSingleton{}
-}
-
-type bootJarsSingleton struct{}
-
-func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool {
-	for i := 0; i < list.Len(); i++ {
-		module := list.Jar(i)
-		// Ignore jacocoagent it is only added when instrumenting and so has no impact on
-		// app compatibility.
-		if module == "jacocoagent" {
-			continue
-		}
-		apex := list.Apex(i)
-		if existing, ok := moduleToApex[module]; ok {
-			ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)",
-				module, existing, apex)
-			return false
-		}
-
-		moduleToApex[module] = apex
-	}
-
-	return true
-}
-
 // isActiveModule returns true if the given module should be considered for boot
 // jars, i.e. if it's enabled and the preferred one in case of source and
 // prebuilt alternatives.
@@ -59,73 +28,17 @@
 	return android.IsModulePreferred(module)
 }
 
-func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	config := ctx.Config()
-	if config.SkipBootJarsCheck() {
-		return
-	}
-
-	// Populate a map from module name to APEX from the boot jars. If there is a
-	// problem such as duplicate modules then fail and return immediately. Note
-	// that both module and APEX names are tracked by base names here, so we need
-	// to be careful to remove "prebuilt_" prefixes when comparing them with
-	// actual modules and APEX bundles.
-	moduleToApex := make(map[string]string)
-	if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
-		!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
-		return
-	}
-
-	// Map from module name to the correct apex variant.
-	nameToApexVariant := make(map[string]android.Module)
-
-	// Scan all the modules looking for the module/apex variants corresponding to the
-	// boot jars.
-	ctx.VisitAllModules(func(module android.Module) {
-		if !isActiveModule(module) {
-			return
-		}
-
-		name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
-		if apex, ok := moduleToApex[name]; ok {
-			apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
-			if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexModule(apex) {
-				// The module name/apex variant should be unique in the system but double check
-				// just in case something has gone wrong.
-				if existing, ok := nameToApexVariant[name]; ok {
-					ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module)
-				}
-				nameToApexVariant[name] = module
-			}
-		}
-	})
-
+// buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package
+// check.
+func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) {
 	timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
 
 	rule := android.NewRuleBuilder(pctx, ctx)
-	checkBootJars := rule.Command().BuiltTool("check_boot_jars").
+	rule.Command().BuiltTool("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
-	// then all the boot jars listed must have been found.
-	strict := !config.UnbundledBuild() && !config.AllowMissingDependencies()
-
-	// 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.(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))
-			}
-		} else if strict {
-			ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name])
-		}
-	}
-
-	checkBootJars.Text("&& touch").Output(timestamp)
+		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")).
+		Inputs(bootDexJarByModule.bootDexJars()).
+		Text("&& touch").Output(timestamp)
 	rule.Build("boot_jars_package_check", "check boot jar packages")
 
 	// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 1ba463c..02343ad 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -189,7 +189,8 @@
 
 	b.generateClasspathProtoBuildActions(ctx)
 
-	b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
 	// Nothing to do if skipping the dexpreopt of boot image jars.
 	if SkipDexpreoptBootJars(ctx) {
@@ -258,7 +259,7 @@
 }
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
 
 	// Save the paths to the monolithic files for retrieval via OutputFiles().
 	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
@@ -276,7 +277,7 @@
 				Output: path,
 			})
 		}
-		return
+		return nil
 	}
 
 	monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments)
@@ -322,6 +323,8 @@
 	// jars.
 	indexCSV := hiddenAPISingletonPaths(ctx).index
 	buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV)
+
+	return bootDexJarByModule
 }
 
 // createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index f2ab6a1..1f28ed3 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -40,6 +40,7 @@
 			}
 		`, apex, fragment)),
 		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
 	)
 }
 
@@ -173,9 +174,29 @@
 .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
 `),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+
+		// Check the behavior of the snapshot without the source.
+		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+			// Make sure that the boot jars package check rule includes the dex jar retrieved from the prebuilt apex.
+			checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/mybootlib.jar")
+		}),
+
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
 	)
+
+	// Make sure that the boot jars package check rule includes the dex jar created from the source.
+	checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/mybootlib/android_common_apex10000/aligned/mybootlib.jar")
+}
+
+// checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
+// package check rule.
+func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModule string) {
+	platformBcp := result.ModuleForTests("platform-bootclasspath", "android_common")
+	bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
+	command := bootJarsCheckRule.RuleParams.Command
+	expectedCommandArgs := " out/soong/host/linux-x86/bin/dexdump build/soong/scripts/check_boot_jars/package_allowed_list.txt " + expectedModule + " &&"
+	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
 }
 
 func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {