Add logical_partition module type
logical_partition builds a partition image (which is usually called
'super.img') out of one or more filesystem images.
Bug: 180921702
Test: m microdroid_super
Change-Id: I659607647e3a5bc82c82b576a049e6c6f91cbddb
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
new file mode 100644
index 0000000..e547203
--- /dev/null
+++ b/filesystem/logical_partition.go
@@ -0,0 +1,210 @@
+// Copyright (C) 2021 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"
+ "strconv"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("logical_partition", logicalPartitionFactory)
+}
+
+type logicalPartition struct {
+ android.ModuleBase
+
+ properties logicalPartitionProperties
+
+ output android.OutputPath
+ installDir android.InstallPath
+}
+
+type logicalPartitionProperties struct {
+ // Set the name of the output. Defaults to <module_name>.img.
+ Stem *string
+
+ // Total size of the logical partition
+ Size *string
+
+ // List of groups. A group defines a fixed sized region. It can host one or more logical
+ // partitions and their total size is limited by the size of the group they are in.
+ Groups []groupProperties
+
+ // Whether the output is a sparse image or not. Default is false.
+ Sparse *bool
+}
+
+type groupProperties struct {
+ // Name of the partition group
+ Name *string
+
+ // Size of the partition group
+ Size *string
+
+ // List of logical partitions in this group
+ Partitions []partitionProperties
+}
+
+type partitionProperties struct {
+ // Name of the partition
+ Name *string
+
+ // Filesystem that is placed on the partition
+ Filesystem *string `android:"path"`
+}
+
+// logical_partition is a partition image which has one or more logical partitions in it.
+func logicalPartitionFactory() android.Module {
+ module := &logicalPartition{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ return module
+}
+
+func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // do nothing
+}
+
+func (l *logicalPartition) installFileName() string {
+ return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img")
+}
+
+func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ builder := android.NewRuleBuilder(pctx, ctx)
+
+ // Sparse the filesystem images and calculate their sizes
+ sparseImages := make(map[string]android.OutputPath)
+ sparseImageSizes := make(map[string]android.OutputPath)
+ for _, group := range l.properties.Groups {
+ for _, part := range group.Partitions {
+ sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
+ pName := proptools.String(part.Name)
+ sparseImages[pName] = sparseImg
+ sparseImageSizes[pName] = sizeTxt
+ }
+ }
+
+ cmd := builder.Command().BuiltTool("lpmake")
+
+ size := proptools.String(l.properties.Size)
+ if size == "" {
+ ctx.PropertyErrorf("size", "must be set")
+ }
+ if _, err := strconv.Atoi(size); err != nil {
+ ctx.PropertyErrorf("size", "must be a number")
+ }
+ cmd.FlagWithArg("--device-size=", size)
+
+ // TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots.
+ cmd.FlagWithArg("--metadata-slots=", "2")
+ cmd.FlagWithArg("--metadata-size=", "65536")
+
+ if proptools.Bool(l.properties.Sparse) {
+ cmd.Flag("--sparse")
+ }
+
+ groupNames := make(map[string]bool)
+ partitionNames := make(map[string]bool)
+
+ for _, group := range l.properties.Groups {
+ gName := proptools.String(group.Name)
+ if gName == "" {
+ ctx.PropertyErrorf("groups.name", "must be set")
+ }
+ if _, ok := groupNames[gName]; ok {
+ ctx.PropertyErrorf("group.name", "already exists")
+ } else {
+ groupNames[gName] = true
+ }
+ gSize := proptools.String(group.Size)
+ if gSize == "" {
+ ctx.PropertyErrorf("groups.size", "must be set")
+ }
+ if _, err := strconv.Atoi(gSize); err != nil {
+ ctx.PropertyErrorf("groups.size", "must be a number")
+ }
+ cmd.FlagWithArg("--group=", gName+":"+gSize)
+
+ for _, part := range group.Partitions {
+ pName := proptools.String(part.Name)
+ if pName == "" {
+ ctx.PropertyErrorf("groups.partitions.name", "must be set")
+ }
+ if _, ok := partitionNames[pName]; ok {
+ ctx.PropertyErrorf("groups.partitions.name", "already exists")
+ } else {
+ partitionNames[pName] = true
+ }
+ // Get size of the partition by reading the -size.txt file
+ pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+ cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
+ cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+ }
+ }
+
+ l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
+ cmd.FlagWithOutput("--output=", l.output)
+
+ builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName()))
+
+ l.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+}
+
+// Add a rule that converts the filesystem for the given partition to the given rule builder. The
+// path to the sparse file and the text file having the size of the partition are returned.
+func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) {
+ img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem))
+ name := proptools.String(p.Name)
+ sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath
+
+ builder.Temporary(sparseImg)
+ builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg)
+
+ sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath
+ builder.Temporary(sizeTxt)
+ builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg).
+ Text("| ").Text("tr").FlagWithArg("-d ", "'\n'").
+ Text("> ").Output(sizeTxt)
+
+ return sparseImg, sizeTxt
+}
+
+var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(l.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName())
+ },
+ },
+ }}
+}
+
+var _ Filesystem = (*logicalPartition)(nil)
+
+func (l *logicalPartition) OutputPath() android.Path {
+ return l.output
+}