Merge "Add super_image module type and create super image module in fsgen" into main am: a242492bb7 am: db55b7aaf4

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

Change-Id: Iec678109d2e55a40d0877ce6b3ae7e2e030c6f3b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/android/variable.go b/android/variable.go
index 3829f48..e012103 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -582,6 +582,11 @@
 	BoardAvbRollbackIndexLocation string `json:",omitempty"`
 }
 
+type BoardSuperPartitionGroupProps struct {
+	GroupSize     string   `json:",omitempty"`
+	PartitionList []string `json:",omitempty"`
+}
+
 type ChainedAvbPartitionProps struct {
 	Partitions            []string `json:",omitempty"`
 	Key                   string   `json:",omitempty"`
@@ -635,6 +640,18 @@
 	InternalBootconfig              []string `json:",omitempty"`
 	InternalBootconfigFile          string   `json:",omitempty"`
 
+	// Super image stuff
+	ProductUseDynamicPartitions       bool                                     `json:",omitempty"`
+	ProductRetrofitDynamicPartitions  bool                                     `json:",omitempty"`
+	ProductBuildSuperPartition        bool                                     `json:",omitempty"`
+	BoardSuperPartitionSize           string                                   `json:",omitempty"`
+	BoardSuperPartitionMetadataDevice string                                   `json:",omitempty"`
+	BoardSuperPartitionBlockDevices   []string                                 `json:",omitempty"`
+	BoardSuperPartitionGroups         map[string]BoardSuperPartitionGroupProps `json:",omitempty"`
+	ProductVirtualAbOta               bool                                     `json:",omitempty"`
+	ProductVirtualAbOtaRetrofit       bool                                     `json:",omitempty"`
+	AbOtaUpdater                      bool                                     `json:",omitempty"`
+
 	// Avb (android verified boot) stuff
 	BoardAvbEnable          bool                                `json:",omitempty"`
 	BoardAvbAlgorithm       string                              `json:",omitempty"`
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index bbb3ea7..127faa7 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -25,6 +25,7 @@
         "fsverity_metadata.go",
         "logical_partition.go",
         "raw_binary.go",
+        "super_image.go",
         "system_image.go",
         "vbmeta.go",
         "testing.go",
diff --git a/filesystem/super_image.go b/filesystem/super_image.go
new file mode 100644
index 0000000..1583c0b
--- /dev/null
+++ b/filesystem/super_image.go
@@ -0,0 +1,234 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+	"fmt"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	android.RegisterModuleType("super_image", SuperImageFactory)
+}
+
+type superImage struct {
+	android.ModuleBase
+
+	properties     SuperImageProperties
+	partitionProps SuperImagePartitionNameProperties
+
+	installDir android.InstallPath
+}
+
+type SuperImageProperties struct {
+	// the size of the super partition
+	Size *int64
+	// the block device where metadata for dynamic partitions is stored
+	Metadata_device *string
+	// the super partition block device list
+	Block_devices *string
+	// whether A/B updater is used
+	Ab_update *bool
+	// whether dynamic partitions is enabled on devices that were launched without this support
+	Retrofit *bool
+	// whether virtual A/B seamless update is enabled
+	Virtual_ab *bool
+	// whether retrofitting virtual A/B seamless update is enabled
+	Virtual_ab_retrofit *bool
+	// whether the output is a sparse image
+	Sparse *bool
+	// information about how partitions within the super partition are grouped together
+	Partition_groups []PartitionGroupsInfo
+	// whether dynamic partitions is used
+	Use_dynamic_partitions *bool
+}
+
+type PartitionGroupsInfo struct {
+	Name          string
+	GroupSize     string
+	PartitionList []string
+}
+
+type SuperImagePartitionNameProperties struct {
+	// Name of the System partition filesystem module
+	System_partition *string
+	// Name of the System_ext partition filesystem module
+	System_ext_partition *string
+	// Name of the System_dlkm partition filesystem module
+	System_dlkm_partition *string
+	// Name of the System_other partition filesystem module
+	System_other_partition *string
+	// Name of the Product partition filesystem module
+	Product_partition *string
+	// Name of the Vendor partition filesystem module
+	Vendor_partition *string
+	// Name of the Vendor_dlkm partition filesystem module
+	Vendor_dlkm_partition *string
+	// Name of the Odm partition filesystem module
+	Odm_partition *string
+	// Name of the Odm_dlkm partition filesystem module
+	Odm_dlkm_partition *string
+}
+
+func SuperImageFactory() android.Module {
+	module := &superImage{}
+	module.AddProperties(&module.properties, &module.partitionProps)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type superImageDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var superImageDepTag superImageDepTagType
+
+func (s *superImage) DepsMutator(ctx android.BottomUpMutatorContext) {
+	addDependencyIfDefined := func(dep *string) {
+		if dep != nil {
+			ctx.AddDependency(ctx.Module(), superImageDepTag, proptools.String(dep))
+		}
+	}
+
+	addDependencyIfDefined(s.partitionProps.System_partition)
+	addDependencyIfDefined(s.partitionProps.System_ext_partition)
+	addDependencyIfDefined(s.partitionProps.System_dlkm_partition)
+	addDependencyIfDefined(s.partitionProps.System_other_partition)
+	addDependencyIfDefined(s.partitionProps.Product_partition)
+	addDependencyIfDefined(s.partitionProps.Vendor_partition)
+	addDependencyIfDefined(s.partitionProps.Vendor_dlkm_partition)
+	addDependencyIfDefined(s.partitionProps.Odm_partition)
+	addDependencyIfDefined(s.partitionProps.Odm_dlkm_partition)
+}
+
+func (s *superImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	miscInfo, deps := s.buildMiscInfo(ctx)
+	builder := android.NewRuleBuilder(pctx, ctx)
+	output := android.PathForModuleOut(ctx, s.installFileName())
+	lpMake := ctx.Config().HostToolPath(ctx, "lpmake")
+	lpMakeDir := filepath.Dir(lpMake.String())
+	deps = append(deps, lpMake)
+	builder.Command().Textf("PATH=%s:\\$PATH", lpMakeDir).
+		BuiltTool("build_super_image").
+		Text("-v").
+		Input(miscInfo).
+		Implicits(deps).
+		Output(output)
+	builder.Build("build_super_image", fmt.Sprintf("Creating super image %s", s.BaseModuleName()))
+	ctx.SetOutputFiles([]android.Path{output}, "")
+}
+
+func (s *superImage) installFileName() string {
+	return s.BaseModuleName() + ".img"
+}
+
+func (s *superImage) buildMiscInfo(ctx android.ModuleContext) (android.Path, android.Paths) {
+	var miscInfoString strings.Builder
+	addStr := func(name string, value string) {
+		miscInfoString.WriteString(name)
+		miscInfoString.WriteRune('=')
+		miscInfoString.WriteString(value)
+		miscInfoString.WriteRune('\n')
+	}
+
+	addStr("use_dynamic_partitions", strconv.FormatBool(proptools.Bool(s.properties.Use_dynamic_partitions)))
+	addStr("dynamic_partition_retrofit", strconv.FormatBool(proptools.Bool(s.properties.Retrofit)))
+	addStr("lpmake", "lpmake")
+	addStr("super_metadata_device", proptools.String(s.properties.Metadata_device))
+	addStr("super_block_devices", proptools.String(s.properties.Block_devices))
+	addStr("super_super_device_size", strconv.Itoa(proptools.Int(s.properties.Size)))
+	var groups, partitionList []string
+	for _, groupInfo := range s.properties.Partition_groups {
+		groups = append(groups, groupInfo.Name)
+		partitionList = append(partitionList, groupInfo.PartitionList...)
+		addStr("super_"+groupInfo.Name+"_group_size", groupInfo.GroupSize)
+		addStr("super_"+groupInfo.Name+"_partition_list", strings.Join(groupInfo.PartitionList, " "))
+	}
+	addStr("super_partition_groups", strings.Join(groups, " "))
+	addStr("dynamic_partition_list", strings.Join(partitionList, " "))
+
+	addStr("virtual_ab", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab)))
+	addStr("virtual_ab_retrofit", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab_retrofit)))
+	addStr("ab_update", strconv.FormatBool(proptools.Bool(s.properties.Ab_update)))
+	addStr("build_non_sparse_super_partition", strconv.FormatBool(!proptools.Bool(s.properties.Sparse)))
+
+	partitionToImagePath := make(map[string]string)
+	nameToPartition := make(map[string]string)
+	var systemOtherPartitionNameNeeded string
+	addEntryToPartitionToName := func(p string, s *string) {
+		if proptools.String(s) != "" {
+			nameToPartition[*s] = p
+		}
+	}
+
+	// Build partitionToImagePath, because system partition may need system_other
+	// partition image path
+	for _, p := range partitionList {
+		if _, ok := nameToPartition[p]; ok {
+			continue
+		}
+		switch p {
+		case "system":
+			addEntryToPartitionToName(p, s.partitionProps.System_partition)
+			systemOtherPartitionNameNeeded = proptools.String(s.partitionProps.System_other_partition)
+		case "system_dlkm":
+			addEntryToPartitionToName(p, s.partitionProps.System_dlkm_partition)
+		case "system_ext":
+			addEntryToPartitionToName(p, s.partitionProps.System_ext_partition)
+		case "product":
+			addEntryToPartitionToName(p, s.partitionProps.Product_partition)
+		case "vendor":
+			addEntryToPartitionToName(p, s.partitionProps.Vendor_partition)
+		case "vendor_dlkm":
+			addEntryToPartitionToName(p, s.partitionProps.Vendor_dlkm_partition)
+		case "odm":
+			addEntryToPartitionToName(p, s.partitionProps.Odm_partition)
+		case "odm_dlkm":
+			addEntryToPartitionToName(p, s.partitionProps.Odm_dlkm_partition)
+		default:
+			ctx.ModuleErrorf("current partition %s not a super image supported partition", p)
+		}
+	}
+
+	var deps android.Paths
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if p, ok := nameToPartition[m.Name()]; ok {
+			if output, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
+				partitionToImagePath[p] = output.DefaultOutputFiles[0].String()
+				deps = append(deps, output.DefaultOutputFiles[0])
+			}
+		} else if systemOtherPartitionNameNeeded != "" && m.Name() == systemOtherPartitionNameNeeded {
+			if output, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
+				partitionToImagePath["system_other"] = output.DefaultOutputFiles[0].String()
+				// TODO: add system_other to deps after it can be generated
+				// deps = append(deps, output.DefaultOutputFiles[0])
+			}
+		}
+	})
+
+	for _, p := range android.SortedKeys(partitionToImagePath) {
+		addStr(p+"_image", partitionToImagePath[p])
+	}
+
+	miscInfo := android.PathForModuleOut(ctx, "misc_info.txt")
+	android.WriteFileRule(ctx, miscInfo, miscInfoString.String())
+	return miscInfo, deps
+}
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index e8065f3..365d954 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -17,6 +17,7 @@
         "filesystem_creator.go",
         "fsgen_mutators.go",
         "prebuilt_etc_modules_gen.go",
+        "super_img.go",
         "util.go",
         "vbmeta_partitions.go",
     ],
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 6ded3aa..8d355dd 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -52,6 +52,7 @@
 	Boot_image        string `blueprint:"mutated" android:"path_device_first"`
 	Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
 	Init_boot_image   string `blueprint:"mutated" android:"path_device_first"`
+	Super_image       string `blueprint:"mutated" android:"path_device_first"`
 }
 
 type filesystemCreator struct {
@@ -158,6 +159,11 @@
 		f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
 	}
 
+	if buildingSuperImage(partitionVars) {
+		createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
+		f.properties.Super_image = ":" + generatedModuleName(ctx.Config(), "super")
+	}
+
 	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
 	f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
 }
@@ -998,6 +1004,14 @@
 		diffTestFiles = append(diffTestFiles, diffTestFile)
 		ctx.Phony("soong_generated_init_boot_filesystem_test", diffTestFile)
 	}
+	if f.properties.Super_image != "" {
+		diffTestFile := android.PathForModuleOut(ctx, "super_diff_test.txt")
+		soongSuperImg := android.PathForModuleSrc(ctx, f.properties.Super_image)
+		makeSuperImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/super.img", ctx.Config().DeviceName()))
+		createDiffTest(ctx, diffTestFile, soongSuperImg, makeSuperImage)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony("soong_generated_super_filesystem_test", diffTestFile)
+	}
 	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
 }
 
diff --git a/fsgen/super_img.go b/fsgen/super_img.go
new file mode 100644
index 0000000..4569896
--- /dev/null
+++ b/fsgen/super_img.go
@@ -0,0 +1,91 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+	"strconv"
+
+	"android/soong/android"
+	"android/soong/filesystem"
+	"github.com/google/blueprint/proptools"
+)
+
+func buildingSuperImage(partitionVars android.PartitionVariables) bool {
+	return partitionVars.ProductBuildSuperPartition
+}
+
+func createSuperImage(ctx android.LoadHookContext, partitions []string, partitionVars android.PartitionVariables) {
+	baseProps := &struct {
+		Name *string
+	}{
+		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "super")),
+	}
+
+	superImageProps := &filesystem.SuperImageProperties{
+		Metadata_device:        proptools.StringPtr(partitionVars.BoardSuperPartitionMetadataDevice),
+		Block_devices:          proptools.StringPtr(partitionVars.BoardSuperPartitionBlockDevices[0]),
+		Ab_update:              proptools.BoolPtr(partitionVars.AbOtaUpdater),
+		Retrofit:               proptools.BoolPtr(partitionVars.ProductRetrofitDynamicPartitions),
+		Virtual_ab:             proptools.BoolPtr(partitionVars.ProductVirtualAbOta),
+		Virtual_ab_retrofit:    proptools.BoolPtr(partitionVars.ProductVirtualAbOtaRetrofit),
+		Use_dynamic_partitions: proptools.BoolPtr(partitionVars.ProductUseDynamicPartitions),
+	}
+	size, _ := strconv.ParseInt(partitionVars.BoardSuperPartitionSize, 10, 64)
+	superImageProps.Size = proptools.Int64Ptr(size)
+	sparse := !partitionVars.TargetUserimagesSparseExtDisabled && !partitionVars.TargetUserimagesSparseF2fsDisabled
+	superImageProps.Sparse = proptools.BoolPtr(sparse)
+
+	var partitionGroupsInfo []filesystem.PartitionGroupsInfo
+	for _, groupName := range android.SortedKeys(partitionVars.BoardSuperPartitionGroups) {
+		info := filesystem.PartitionGroupsInfo{
+			Name:          groupName,
+			GroupSize:     partitionVars.BoardSuperPartitionGroups[groupName].GroupSize,
+			PartitionList: partitionVars.BoardSuperPartitionGroups[groupName].PartitionList,
+		}
+		partitionGroupsInfo = append(partitionGroupsInfo, info)
+	}
+	superImageProps.Partition_groups = partitionGroupsInfo
+
+	partitionNameProps := &filesystem.SuperImagePartitionNameProperties{}
+	if android.InList("system", partitions) {
+		partitionNameProps.System_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	}
+	if android.InList("system_ext", partitions) {
+		partitionNameProps.System_ext_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+	}
+	if android.InList("system_dlkm", partitions) {
+		partitionNameProps.System_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_dlkm"))
+	}
+	if android.InList("system_other", partitions) {
+		partitionNameProps.System_other_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_other"))
+	}
+	if android.InList("product", partitions) {
+		partitionNameProps.Product_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
+	}
+	if android.InList("vendor", partitions) {
+		partitionNameProps.Vendor_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
+	}
+	if android.InList("vendor_dlkm", partitions) {
+		partitionNameProps.Vendor_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_dlkm"))
+	}
+	if android.InList("odm", partitions) {
+		partitionNameProps.Odm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
+	}
+	if android.InList("odm_dlkm", partitions) {
+		partitionNameProps.Odm_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm_dlkm"))
+	}
+
+	ctx.CreateModule(filesystem.SuperImageFactory, baseProps, superImageProps, partitionNameProps)
+}