Merge "Generate boot zip file from prebuilt_bootclasspath_fragment"
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 6d935d8..ebd22cf 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -162,11 +162,12 @@
 	// versioned sdk.
 	produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
 
-	// produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the
-	// required android.ArchType values in the returned map.
+	// produceBootImageFiles will attempt to produce rules to create the boot image files at the paths
+	// predefined in the bootImageConfig.
 	//
-	// It must return nil if the boot image files cannot be produced for whatever reason.
-	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch
+	// If it could not create the files then it will return nil. Otherwise, it will return a map from
+	// android.ArchType to the predefined paths of the boot image files.
+	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
@@ -454,9 +455,13 @@
 		if imageConfig != nil {
 			// Delegate the production of the boot image files to a module type specific method.
 			common := ctx.Module().(commonBootclasspathFragment)
-			bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents)
+			bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig)
 
 			if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+				// Zip the boot image files up, if available. This will generate the zip file in a
+				// predefined location.
+				buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFilesByArch)
+
 				// Copy the dex jars of this fragment's content modules to their predefined locations.
 				copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
 			}
@@ -664,7 +669,7 @@
 }
 
 // produceBootImageFiles builds the boot image files from the source if it is required.
-func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	if SkipDexpreoptBootJars(ctx) {
 		return nil
 	}
@@ -674,45 +679,34 @@
 	dexpreopt.GetGlobalSoongConfig(ctx)
 
 	// Only generate the boot image if the configuration does not skip it.
-	if !b.generateBootImageBuildActions(ctx, contents, imageConfig) {
-		return nil
-	}
-
-	// Only make the files available to an apex if they were actually generated.
-	files := bootImageFilesByArch{}
-	for _, variant := range imageConfig.apexVariants() {
-		files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
-	}
-
-	return files
+	return b.generateBootImageBuildActions(ctx, imageConfig)
 }
 
 // generateBootImageBuildActions generates ninja rules to create the boot image if required for this
 // module.
 //
-// Returns true if the boot image is created, false otherwise.
-func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, contents []android.Module, imageConfig *bootImageConfig) bool {
+// If it could not create the files then it will return nil. Otherwise, it will return a map from
+// android.ArchType to the predefined paths of the boot image files.
+func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	global := dexpreopt.GetGlobalConfig(ctx)
 	if !shouldBuildBootImages(ctx.Config(), global) {
-		return false
+		return nil
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce a boot image.
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if apexInfo.IsForPlatform() {
-		return false
+		return nil
 	}
 
 	// Bootclasspath fragment modules that are versioned do not produce a boot image.
 	if android.IsModuleInVersionedSdk(ctx.Module()) {
-		return false
+		return nil
 	}
 
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
-	buildBootImage(ctx, imageConfig, profile)
-
-	return true
+	return buildBootImage(ctx, imageConfig, profile)
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
@@ -940,7 +934,7 @@
 }
 
 // produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
 		return nil
 	}
@@ -962,6 +956,7 @@
 	}
 
 	di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+	files := bootImageFilesByArch{}
 	for _, variant := range imageConfig.apexVariants() {
 		arch := variant.target.Arch.ArchType
 		for _, toPath := range variant.imagesDeps {
@@ -969,6 +964,10 @@
 			// Get the path to the file that the deapexer extracted from the prebuilt apex file.
 			fromPath := di.PrebuiltExportPath(apexRelativePath)
 
+			// Return the toPath as the calling code expects the paths in the returned map to be the
+			// paths predefined in the bootImageConfig.
+			files[arch] = append(files[arch], toPath)
+
 			// Copy the file to the predefined location.
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
@@ -978,10 +977,7 @@
 		}
 	}
 
-	// The returned files will be made available to APEXes that include a bootclasspath_fragment.
-	// However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point
-	// in returning any files.
-	return nil
+	return files
 }
 
 var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 03769fa..cb5b6be 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -503,29 +503,47 @@
 }
 
 // buildBootImage takes a bootImageConfig, and creates rules to build it.
-func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
-	var zipFiles android.Paths
+//
+// It returns a map from android.ArchType to the predefined paths of the boot image files.
+func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch {
+	filesByArch := bootImageFilesByArch{}
 	for _, variant := range image.variants {
-		files := buildBootImageVariant(ctx, variant, profile)
+		buildBootImageVariant(ctx, variant, profile)
 		if variant.target.Os == android.Android {
-			zipFiles = append(zipFiles, files.Paths()...)
+			filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
 		}
 	}
 
-	if image.zip != nil {
-		rule := android.NewRuleBuilder(pctx, ctx)
-		rule.Command().
-			BuiltTool("soong_zip").
-			FlagWithOutput("-o ", image.zip).
-			FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
-			FlagWithInputList("-f ", zipFiles, " -f ")
+	return filesByArch
+}
 
-		rule.Build("zip_"+image.name, "zip "+image.name+" image")
+// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files.
+//
+// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it
+// is a map from android.ArchType to the predefined locations.
+func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) {
+	if filesByArch == nil {
+		return
 	}
+
+	// Compute the list of files from all the architectures.
+	zipFiles := android.Paths{}
+	for _, archType := range android.ArchTypeList() {
+		zipFiles = append(zipFiles, filesByArch[archType]...)
+	}
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", image.zip).
+		FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
+		FlagWithInputList("-f ", zipFiles, " -f ")
+
+	rule.Build("zip_"+image.name, "zip "+image.name+" image")
 }
 
 // Generate boot image build rules for a specific target.
-func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) android.WritablePaths {
+func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) {
 
 	globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
@@ -641,11 +659,8 @@
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
 
-	var zipFiles android.WritablePaths
-
 	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
 		cmd.ImplicitOutput(artOrOat)
-		zipFiles = append(zipFiles, artOrOat)
 
 		// Install the .oat and .art files
 		rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base()))
@@ -653,7 +668,6 @@
 
 	for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") {
 		cmd.ImplicitOutput(vdex)
-		zipFiles = append(zipFiles, vdex)
 
 		// Note that the vdex files are identical between architectures.
 		// Make rules will create symlinks to share them between architectures.
@@ -675,8 +689,6 @@
 	image.installs = rule.Installs()
 	image.vdexInstalls = vdexInstalls
 	image.unstrippedInstalls = unstrippedInstalls
-
-	return zipFiles
 }
 
 const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index d72cee0..3160a27 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -437,7 +437,10 @@
 
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
-	buildBootImage(ctx, imageConfig, profile)
+	bootImageFilesByArch := buildBootImage(ctx, imageConfig, profile)
+
+	// Zip the boot image files up.
+	buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFilesByArch)
 
 	dumpOatRules(ctx, imageConfig)
 }