logical_partition.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright (C) 2021 The Android Open Source Project
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package filesystem
  15. import (
  16. "fmt"
  17. "strconv"
  18. "github.com/google/blueprint/proptools"
  19. "android/soong/android"
  20. )
  21. func init() {
  22. android.RegisterModuleType("logical_partition", logicalPartitionFactory)
  23. }
  24. type logicalPartition struct {
  25. android.ModuleBase
  26. properties logicalPartitionProperties
  27. output android.OutputPath
  28. installDir android.InstallPath
  29. }
  30. type logicalPartitionProperties struct {
  31. // Set the name of the output. Defaults to <module_name>.img.
  32. Stem *string
  33. // Total size of the logical partition. If set to "auto", total size is automatically
  34. // calcaulted as minimum.
  35. Size *string
  36. // List of partitions for default group. Default group has no size limit and automatically
  37. // minimized when creating an image.
  38. Default_group []partitionProperties
  39. // List of groups. A group defines a fixed sized region. It can host one or more logical
  40. // partitions and their total size is limited by the size of the group they are in.
  41. Groups []groupProperties
  42. // Whether the output is a sparse image or not. Default is false.
  43. Sparse *bool
  44. }
  45. type groupProperties struct {
  46. // Name of the partition group. Can't be "default"; use default_group instead.
  47. Name *string
  48. // Size of the partition group
  49. Size *string
  50. // List of logical partitions in this group
  51. Partitions []partitionProperties
  52. }
  53. type partitionProperties struct {
  54. // Name of the partition
  55. Name *string
  56. // Filesystem that is placed on the partition
  57. Filesystem *string `android:"path"`
  58. }
  59. // logical_partition is a partition image which has one or more logical partitions in it.
  60. func logicalPartitionFactory() android.Module {
  61. module := &logicalPartition{}
  62. module.AddProperties(&module.properties)
  63. android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
  64. return module
  65. }
  66. func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) {
  67. // do nothing
  68. }
  69. func (l *logicalPartition) installFileName() string {
  70. return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img")
  71. }
  72. func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext) {
  73. builder := android.NewRuleBuilder(pctx, ctx)
  74. // Sparse the filesystem images and calculate their sizes
  75. sparseImages := make(map[string]android.OutputPath)
  76. sparseImageSizes := make(map[string]android.OutputPath)
  77. sparsePartitions := func(partitions []partitionProperties) {
  78. for _, part := range partitions {
  79. sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
  80. pName := proptools.String(part.Name)
  81. sparseImages[pName] = sparseImg
  82. sparseImageSizes[pName] = sizeTxt
  83. }
  84. }
  85. for _, group := range l.properties.Groups {
  86. sparsePartitions(group.Partitions)
  87. }
  88. sparsePartitions(l.properties.Default_group)
  89. cmd := builder.Command().BuiltTool("lpmake")
  90. size := proptools.String(l.properties.Size)
  91. if size == "" {
  92. ctx.PropertyErrorf("size", "must be set")
  93. } else if _, err := strconv.Atoi(size); err != nil && size != "auto" {
  94. ctx.PropertyErrorf("size", `must be a number or "auto"`)
  95. }
  96. cmd.FlagWithArg("--device-size=", size)
  97. // TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots.
  98. cmd.FlagWithArg("--metadata-slots=", "2")
  99. cmd.FlagWithArg("--metadata-size=", "65536")
  100. if proptools.Bool(l.properties.Sparse) {
  101. cmd.Flag("--sparse")
  102. }
  103. groupNames := make(map[string]bool)
  104. partitionNames := make(map[string]bool)
  105. addPartitionsToGroup := func(partitions []partitionProperties, gName string) {
  106. for _, part := range partitions {
  107. pName := proptools.String(part.Name)
  108. if pName == "" {
  109. ctx.PropertyErrorf("groups.partitions.name", "must be set")
  110. }
  111. if _, ok := partitionNames[pName]; ok {
  112. ctx.PropertyErrorf("groups.partitions.name", "already exists")
  113. } else {
  114. partitionNames[pName] = true
  115. }
  116. // Get size of the partition by reading the -size.txt file
  117. pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
  118. cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
  119. cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
  120. }
  121. }
  122. addPartitionsToGroup(l.properties.Default_group, "default")
  123. for _, group := range l.properties.Groups {
  124. gName := proptools.String(group.Name)
  125. if gName == "" {
  126. ctx.PropertyErrorf("groups.name", "must be set")
  127. } else if gName == "default" {
  128. ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`)
  129. }
  130. if _, ok := groupNames[gName]; ok {
  131. ctx.PropertyErrorf("group.name", "already exists")
  132. } else {
  133. groupNames[gName] = true
  134. }
  135. gSize := proptools.String(group.Size)
  136. if gSize == "" {
  137. ctx.PropertyErrorf("groups.size", "must be set")
  138. }
  139. if _, err := strconv.Atoi(gSize); err != nil {
  140. ctx.PropertyErrorf("groups.size", "must be a number")
  141. }
  142. cmd.FlagWithArg("--group=", gName+":"+gSize)
  143. addPartitionsToGroup(group.Partitions, gName)
  144. }
  145. l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
  146. cmd.FlagWithOutput("--output=", l.output)
  147. builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName()))
  148. l.installDir = android.PathForModuleInstall(ctx, "etc")
  149. ctx.InstallFile(l.installDir, l.installFileName(), l.output)
  150. }
  151. // Add a rule that converts the filesystem for the given partition to the given rule builder. The
  152. // path to the sparse file and the text file having the size of the partition are returned.
  153. func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) {
  154. img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem))
  155. name := proptools.String(p.Name)
  156. sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath
  157. builder.Temporary(sparseImg)
  158. builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg)
  159. sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath
  160. builder.Temporary(sizeTxt)
  161. builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg).
  162. Text("| ").Text("tr").FlagWithArg("-d ", "'\n'").
  163. Text("> ").Output(sizeTxt)
  164. return sparseImg, sizeTxt
  165. }
  166. var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil)
  167. // Implements android.AndroidMkEntriesProvider
  168. func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries {
  169. return []android.AndroidMkEntries{android.AndroidMkEntries{
  170. Class: "ETC",
  171. OutputFile: android.OptionalPathForPath(l.output),
  172. ExtraEntries: []android.AndroidMkExtraEntriesFunc{
  173. func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
  174. entries.SetString("LOCAL_MODULE_PATH", l.installDir.String())
  175. entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName())
  176. },
  177. },
  178. }}
  179. }
  180. var _ Filesystem = (*logicalPartition)(nil)
  181. func (l *logicalPartition) OutputPath() android.Path {
  182. return l.output
  183. }
  184. func (l *logicalPartition) SignedOutputPath() android.Path {
  185. return nil // logical partition is not signed by itself
  186. }
  187. var _ android.OutputFileProducer = (*logicalPartition)(nil)
  188. // Implements android.OutputFileProducer
  189. func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) {
  190. if tag == "" {
  191. return []android.Path{l.output}, nil
  192. }
  193. return nil, fmt.Errorf("unsupported module reference tag %q", tag)
  194. }