Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/fsgen/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 78 kB image not shown  

Quelle  filesystem_creator.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// 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 (
 "crypto/sha256"
 "fmt"
 "path/filepath"
 "slices"
 "strconv"
 "strings"

 "android/soong/android"
 "android/soong/filesystem"
 "android/soong/genrule"
 "android/soong/kernel"

 "github.com/google/blueprint"
 "github.com/google/blueprint/parser"
 "github.com/google/blueprint/proptools"
)

var pctx = android.NewPackageContext("android/soong/fsgen")

func init() {
 registerBuildComponents(android.InitRegistrationContext)
}

func registerBuildComponents(ctx android.RegistrationContext) {
 ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
 ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
}

type generatedPartitionData struct {
 partitionType string
 moduleName    string
 // supported is true if the module was created successfully, false if there was some problem
 // and the module couldn't be created.
 supported   bool
 handwritten bool
 // diffRef is true if the module does not build an image but used only for the file list diff
 // reference.
 diffRef bool
}

type allGeneratedPartitionData []generatedPartitionData

func (d allGeneratedPartitionData) moduleNames() []string {
 var result []string
 for _, data := range d {
  if data.supported && !data.diffRef {
   result = append(result, data.moduleName)
  }
 }
 return result
}

func (d allGeneratedPartitionData) types() []string {
 var result []string
 for _, data := range d {
  if data.supported && !data.diffRef {
   result = append(result, data.partitionType)
  }
 }
 return result
}

func (d allGeneratedPartitionData) unsupportedTypes() []string {
 var result []string
 for _, data := range d {
  if !data.supported && !data.diffRef {
   result = append(result, data.partitionType)
  }
 }
 return result
}

func (d allGeneratedPartitionData) names() []string {
 var result []string
 for _, data := range d {
  if data.supported {
   result = append(result, data.moduleName)
  }
 }
 return result
}

func (d allGeneratedPartitionData) nameForType(ty string) string {
 for _, data := range d {
  if data.supported && !data.diffRef && data.partitionType == ty {
   return data.moduleName
  }
 }
 return ""
}

func (d allGeneratedPartitionData) typeForName(name string) string {
 for _, data := range d {
  if data.supported && data.moduleName == name {
   return data.partitionType
  }
 }
 return ""
}

func (d allGeneratedPartitionData) isHandwritten(name string) bool {
 for _, data := range d {
  if data.supported && data.moduleName == name {
   return data.handwritten
  }
 }
 return false
}

type filesystemCreatorProps struct {
 Unsupported_partition_types []string `blueprint:"mutated"`

 Vbmeta_module_names    []string `blueprint:"mutated"`
 Vbmeta_partition_names []string `blueprint:"mutated"`

 Boot_image                     string `blueprint:"mutated" android:"path_device_first"`
 Boot_16k_image                 string `blueprint:"mutated" android:"path_device_first"`
 Vendor_boot_image              string `blueprint:"mutated" android:"path_device_first"`
 Vendor_boot_debug_image        string `blueprint:"mutated" android:"path_device_first"`
 Vendor_boot_test_harness_image string `blueprint:"mutated" android:"path_device_first"`
 Vendor_kernel_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"`
 Radio_image                    string `blueprint:"mutated" android:"path_device_first"`
 Bootloader                     string `blueprint:"mutated" android:"path_device_first"`
 Tzsw                           string `blueprint:"mutated" android:"path_device_first"`
}

type filesystemCreator struct {
 android.ModuleBase

 properties filesystemCreatorProps
}

func filesystemCreatorFactory() android.Module {
 module := &filesystemCreator{}

 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 module.AddProperties(&module.properties)
 android.AddLoadHook(module, func(ctx android.LoadHookContext) {
  if !shouldEnableFilesystemCreator(ctx) {
   return
  }
  generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
  avbpubkeyGenerated := createAvbpubkeyModule(ctx)
  createFsGenState(ctx, generatedPrebuiltEtcModuleNames, avbpubkeyGenerated)
  module.createAvbKeyFilegroups(ctx)
  module.createMiscFilegroups(ctx)
  module.createProductConfigDistGenrules(ctx)
  module.createInternalModules(ctx)
  module.createBackgroundPicturesForRecovery(ctx)
 })

 return module
}

func shouldEnableFilesystemCreator(ctx android.ConfigContext) bool {
 if ctx.Config().HasUnbundledBuildApps() || ctx.Config().UnbundledBuild() {
  // unbundled builds don't build a device. The android_device's dist artifacts
  // would conflict with the dist artifacts from the unbundled singleton.
  return false
 }
 // We create the filsystem modules even if soong-only mode isn't enabled, so we at least
 // get analysis time checks running everywhere for more real-world coverage.
 return true
}

// This is a build process. It must read all configuration values.
func (f *filesystemCreator) UseGenericConfig() bool {
 return false
}

func generatedPartitions(ctx android.EarlyModuleContext) allGeneratedPartitionData {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse

 var result allGeneratedPartitionData
 addGenerated := func(ty string) {
  result = append(result, generatedPartitionData{
   partitionType: ty,
   moduleName:    generatedModuleNameForPartition(ctx.Config(), ty),
   supported:     true,
  })
 }
 // Always create system and system_ext partition due to still need it for finding deps for vendor or product partition.
 if ctx.Config().UseSoongSystemImage() {
  if ctx.Config().SoongDefinedSystemImage() == "" {
   panic("PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true")
  }
  result = append(result, generatedPartitionData{
   partitionType: "system",
   moduleName:    ctx.Config().SoongDefinedSystemImage(),
   supported:     true,
   handwritten:   true,
  })
  // Add a separate generated module for file list comparison, with the "system" partition type.
  // This does not create an image, but provides the list of installed files from PRODUCT_PACKAGES.
  result = append(result, generatedPartitionData{
   partitionType: "system",
   moduleName:    generatedModuleName(ctx.Config(), "system_filelist_check_image"),
   supported:     true,
   handwritten:   false,
   diffRef:       true,
  })
 } else {
  addGenerated("system")
 }
 // Auto generate system_ext filesystem if SystemExtPath() is 'system_ext' the partition will not needed if its value is 'system/system_ext'.
 if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
  addGenerated("system_ext")
 }
 if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
  addGenerated("vendor")
 }
 // Auto generate product filesystem even if BuildingProductimage is false.
 // The autogenerated partition will be an info only dep for apexkeys.txt
 // generation in android_device.
 if ctx.DeviceConfig().ProductPath() == "product" {
  addGenerated("product")
 }
 if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
  addGenerated("odm")
 }
 if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
  addGenerated("userdata")
 }
 if partitionVars.BuildingSystemDlkmImage {
  addGenerated("system_dlkm")
 }
 if partitionVars.BuildingVendorDlkmImage {
  addGenerated("vendor_dlkm")
 }
 if partitionVars.BuildingOdmDlkmImage {
  addGenerated("odm_dlkm")
 }
 if partitionVars.BuildingRamdiskImage {
  addGenerated("ramdisk")
 }
 if buildingVendorBootImage(partitionVars) {
  addGenerated("vendor_ramdisk")
 }
 if buildingVendorRamdiskFragmentDlkm(ctx, partitionVars) {
  addGenerated("vendor_ramdisk_fragment_dlkm")
 }
 if buildingDebugVendorBootImage(partitionVars) {
  addGenerated("vendor_ramdisk-debug")
  addGenerated("vendor_ramdisk-test-harness")
 }
 if buildingDebugRamdiskImage(partitionVars) {
  addGenerated("debug_ramdisk")
  addGenerated("test_harness_ramdisk")
 }
 if buildingVendorKernelBootImage(partitionVars) {
  addGenerated("vendor_kernel_ramdisk")
 }

 if ctx.DeviceConfig().BuildingRecoveryImage() && ctx.DeviceConfig().RecoveryPath() == "recovery" {
  addGenerated("recovery")
 }
 return result
}

func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
 partitions := generatedPartitions(ctx)
 for i := range partitions {
  f.createPartition(ctx, partitions, &partitions[i])
 }

 // Filter out file list diff reference from the partitions list for other image creations
 // to avoid it being included in system_other, vbmeta, super image, etc.
 var validPartitions allGeneratedPartitionData
 for _, p := range partitions {
  if !p.diffRef {
   validPartitions = append(validPartitions, p)
  }
 }

 // Create android_info.prop
 f.createAndroidInfo(ctx)

 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 dtbImg := createDtbImgFilegroup(ctx)

 if buildingBootImage(partitionVars) {
  if createBootImage(ctx, dtbImg) {
   f.properties.Boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot")
  } else {
   f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot")
  }
 }
 var vendorRamdiskFragmentModules []string
 if buildingVendorBootImage(partitionVars) {
  if exists, fragments := createVendorBootImage(ctx, dtbImg); exists {
   f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
   vendorRamdiskFragmentModules = fragments
  } else {
   f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot")
  }
 }
 if buildingDebugVendorBootImage(partitionVars) {
  if createVendorBootDebugImage(ctx, dtbImg, vendorRamdiskFragmentModules) {
   f.properties.Vendor_boot_debug_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot-debug")
  } else {
   f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot-debug")
  }
  if createVendorBootTestHarnessImage(ctx, dtbImg, vendorRamdiskFragmentModules) {
   f.properties.Vendor_boot_test_harness_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot-test-harness")
  } else {
   f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot-test-harness")
  }

 }

 if buildingVendorKernelBootImage(partitionVars) {
  createVendorKernelBootImage(ctx, dtbImg)
  f.properties.Vendor_kernel_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_kernel_boot")
 } else {
  f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_kernel_boot")
 }

 if buildingInitBootImage(partitionVars) {
  if createInitBootImage(ctx) {
   f.properties.Init_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "init_boot")
  } else {
   f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "init_boot")
  }
 }
 if partitionVars.BoardKernelPath16k != "" {
  if createBootImage16k(ctx) {
   f.properties.Boot_16k_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot_16k")
  } else {
   f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot_16k")
  }
 }

 var systemOtherImageName string
 if buildingSystemOtherImage(partitionVars) {
  systemModule := validPartitions.nameForType("system")
  systemOtherImageName = generatedModuleNameForPartition(ctx.Config(), "system_other")
  ctx.CreateModule(
   filesystem.SystemOtherImageFactory,
   &filesystem.SystemOtherImageProperties{
    System_image:                    &systemModule,
    Preinstall_dexpreopt_files_from: validPartitions.moduleNames(),
   },
   &struct {
    Name *string
   }{
    Name: proptools.StringPtr(systemOtherImageName),
   },
  )
 }

 if pvmfwProperties := getPvmfwProperties(ctx); pvmfwProperties != nil {
  // Add this to soongGeneratedPartitions. fsgen uses this to create the deps of vbmeta images.
  // vbmeta_system of some products chain pvmfw, and adding this
  // here ensures that this chain will be present in the autogenerated vbmeta images.
  validPartitions = append(validPartitions,
   generatedPartitionData{
    partitionType: "pvmfw",
    moduleName:    strings.TrimPrefix(*pvmfwProperties.Image, ":"),
    supported:     true,
    handwritten:   true,
   })
 }
 if tzsw, ok := f.createTzsw(ctx); ok {
  // Add this to soongGeneratedPartitions. fsgen uses this to create the deps of vbmeta images.
  // vbmeta_system of some products chain pvmfw, and adding this
  // here ensures that this chain will be present in the autogenerated vbmeta images.
  validPartitions = append(validPartitions,
   generatedPartitionData{
    partitionType: "tzsw",
    moduleName:    tzsw,
    supported:     true,
    handwritten:   false,
   })
  f.properties.Tzsw = tzsw
 }

 if radioImgModuleName := createRadioImg(ctx); radioImgModuleName != "" {
  f.properties.Radio_image = radioImgModuleName
 }
 if bootloader, ok := f.createBootloader(ctx); ok {
  f.properties.Bootloader = bootloader
 }

 for _, x := range f.createVbmetaPartitions(ctx, validPartitions) {
  f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
  f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
 }

 var superImageSubpartitions []string
 if buildingSuperImage(partitionVars) {
  superImageSubpartitions = createSuperImage(ctx, validPartitions, partitionVars, systemOtherImageName)
  f.properties.Super_image = ":" + generatedModuleNameForPartition(ctx.Config(), "super")
 } else if partitionVars.ProductUseDynamicPartitions {
  createSuperImage(ctx, validPartitions, partitionVars, systemOtherImageName)
 }

 ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = partitions
 f.createDeviceModule(ctx, validPartitions, f.properties.Vbmeta_module_names, superImageSubpartitions)
}

func generatedModuleName(cfg android.Config, suffix string) string {
 prefix := "soong"
 if cfg.HasDeviceProduct() {
  prefix = cfg.DeviceProduct()
 }
 return fmt.Sprintf("%s_generated_%s", prefix, suffix)
}

func generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
 return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
}

func buildingSystemImage(partitionVars android.PartitionVariables) bool {
 return partitionVars.PartitionQualifiedVariables["system"].BuildingImage
}

func buildingSystemExtImage(partitionVars android.PartitionVariables) bool {
 return partitionVars.PartitionQualifiedVariables["system_ext"].BuildingImage
}

func buildingProductImage(partitionVars android.PartitionVariables) bool {
 return partitionVars.PartitionQualifiedVariables["product"].BuildingImage
}

func buildingSystemOtherImage(partitionVars android.PartitionVariables) bool {
 // TODO: Recreate this logic from make instead of just depending on the final result variable:
 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=429;drc=15a0df840e7093f65518003ab80cf24a3d9e8e6a
 return partitionVars.BuildingSystemOtherImage
}

func (f *filesystemCreator) createBootloader(ctx android.LoadHookContext) (string, bool) {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 var bootloaderSrc string
 path := partitionVars.BootloaderFilePath

 if android.SrcIsModule(path) != "" {
  bootloaderSrc = path
 } else if filePath := android.ExistentPathForSource(ctx, path, "bootloader.img"); filePath.Valid() {
  bootloaderSrc = filePath.String()
 }
 if bootloaderSrc == "" {
  return "", false
 }

 boardPlatform := proptools.String(ctx.Config().ProductVariables().BoardPlatform)

 bootloaderModuleName := generatedModuleName(ctx.Config(), "bootloader")
 bootloaderProps := filesystem.PrebuiltBootloaderProperties{
  Src:               &bootloaderSrc,
  Ab_ota_partitions: partitionVars.AbOtaBootloaderPartitions,
  Unpack_tool:       proptools.StringPtr(fmt.Sprintf("vendor/google_devices/%s/prebuilts/misc_bins/fbimg/fbpacktool.py", boardPlatform)),
  Unpack_tool_deps:  []string{fmt.Sprintf("vendor/google_devices/%s/prebuilts/misc_bins/fbimg/*.py", boardPlatform)},
 }

 ctx.CreateModuleInDirectory(
  filesystem.PrebuiltBootloaderFactory,
  ".",
  &struct {
   Name *string
  }{
   Name: &bootloaderModuleName,
  },
  &bootloaderProps,
 )
 return bootloaderModuleName, true
}

func (f *filesystemCreator) createTzsw(ctx android.LoadHookContext) (string, bool) {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 if len(partitionVars.BoardPrebuiltTzswImagePath) == 0 {
  return "", false
 }

 tzswModuleName := generatedModuleName(ctx.Config(), "tzsw")
 props := filesystem.PrebuiltTzswProperties{
  Src: proptools.StringPtr(partitionVars.BoardPrebuiltTzswImagePath),
 }
 ctx.CreateModuleInDirectory(filesystem.PrebuiltTzswFactory, ".",
  &struct {
   Name *string
  }{
   Name: &tzswModuleName,
  },
  &props)
 return tzswModuleName, true
}

func (f *filesystemCreator) createReleaseToolsFilegroup(ctx android.LoadHookContext) (string, bool) {
 releaseToolsDir := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ReleaseToolsExtensionDir
 if releaseToolsDir == "" {
  return "", false
 }

 releaseToolsFilegroupName := generatedModuleName(ctx.Config(), "releasetools")
 filegroupProps := &struct {
  Name       *string
  Srcs       []string
  Visibility []string
 }{
  Name:       proptools.StringPtr(releaseToolsFilegroupName),
  Srcs:       []string{"releasetools.py"},
  Visibility: []string{"//visibility:public"},
 }
 ctx.CreateModuleInDirectory(android.FileGroupFactory, releaseToolsDir, filegroupProps)
 return releaseToolsFilegroupName, true
}

func (f *filesystemCreator) createFastbootInfoFilegroup(ctx android.LoadHookContext) (string, bool) {
 fastbootInfoFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardFastbootInfoFile
 if fastbootInfoFile == "" {
  return "", false
 }

 fastbootInfoFilegroupName := generatedModuleName(ctx.Config(), "fastboot")
 filegroupProps := &struct {
  Name       *string
  Srcs       []string
  Visibility []string
 }{
  Name:       proptools.StringPtr(fastbootInfoFilegroupName),
  Srcs:       []string{fastbootInfoFile},
  Visibility: []string{"//visibility:public"},
 }
 ctx.CreateModuleInDirectory(android.FileGroupFactory, ".", filegroupProps)
 return fastbootInfoFilegroupName, true
}

func (f *filesystemCreator) createDeviceModule(
 ctx android.LoadHookContext,
 partitions allGeneratedPartitionData,
 vbmetaPartitions []string,
 superImageSubPartitions []string,
) {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse

 var vendorBlobsLicenseProp *string
 if partitionVars.VendorBlobsLicense != "" {
  vendorBlobsLicenceFilegroupName := generatedModuleName(ctx.Config(), "vendor_blobs_license")
  filegroupProps := &struct {
   Name       *string
   Srcs       []string
   Visibility []string
  }{
   Name:       proptools.StringPtr(vendorBlobsLicenceFilegroupName),
   Srcs:       []string{filepath.Base(partitionVars.VendorBlobsLicense)},
   Visibility: []string{"//build/soong/fsgen:__subpackages__"},
  }
  ctx.CreateModuleInDirectory(android.FileGroupFactory, filepath.Dir(partitionVars.VendorBlobsLicense), filegroupProps)
  vendorBlobsLicenseProp = proptools.StringPtr(":" + vendorBlobsLicenceFilegroupName)
 }

 baseProps := &struct {
  Name *string
 }{
  Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
 }

 // Currently, only the system and system_ext partition module is created.
 partitionProps := &filesystem.PartitionNameProperties{}
 infoPartitionProps := &filesystem.PartitionNameProperties{}
 if f.properties.Super_image != "" {
  partitionProps.Super_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super"))
 } else if partitionVars.ProductUseDynamicPartitions {
  infoPartitionProps.Super_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super"))
 }
 if modName := partitions.nameForType("system"); modName != "" && !android.InList("system", superImageSubPartitions) {
  if buildingSystemImage(partitionVars) {
   partitionProps.System_partition_name = proptools.StringPtr(modName)
  } else {
   infoPartitionProps.System_partition_name = proptools.StringPtr(modName)
  }
 }
 if modName := partitions.nameForType("system_ext"); modName != "" && !android.InList("system_ext", superImageSubPartitions) {
  if buildingSystemExtImage(partitionVars) {
   partitionProps.System_ext_partition_name = proptools.StringPtr(modName)
  } else {
   infoPartitionProps.System_ext_partition_name = proptools.StringPtr(modName)
  }
 }
 if modName := partitions.nameForType("vendor"); modName != "" && !android.InList("vendor", superImageSubPartitions) {
  partitionProps.Vendor_partition_name = proptools.StringPtr(modName)
 }
 if modName := partitions.nameForType("product"); modName != "" && !android.InList("product", superImageSubPartitions) {
  if buildingProductImage(partitionVars) {
   partitionProps.Product_partition_name = proptools.StringPtr(modName)
  } else {
   infoPartitionProps.Product_partition_name = proptools.StringPtr(modName)
  }
 }
 if modName := partitions.nameForType("odm"); modName != "" && !android.InList("odm", superImageSubPartitions) {
  partitionProps.Odm_partition_name = proptools.StringPtr(modName)
 }
 if modName := partitions.nameForType("userdata"); modName != "" {
  partitionProps.Userdata_partition_name = proptools.StringPtr(modName)
 }
 if modName := partitions.nameForType("recovery"); modName != "" && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
  partitionProps.Recovery_partition_name = proptools.StringPtr(modName)
 }
 if modName := partitions.nameForType("system_dlkm"); modName != "" && !android.InList("system_dlkm", superImageSubPartitions) {
  partitionProps.System_dlkm_partition_name = proptools.StringPtr(modName)
 }
 if modName := partitions.nameForType("vendor_dlkm"); modName != "" && !android.InList("vendor_dlkm", superImageSubPartitions) {
  partitionProps.Vendor_dlkm_partition_name = proptools.StringPtr(modName)
 }
 if modName := partitions.nameForType("odm_dlkm"); modName != "" && !android.InList("odm_dlkm", superImageSubPartitions) {
  partitionProps.Odm_dlkm_partition_name = proptools.StringPtr(modName)
 }
 if f.properties.Boot_image != "" {
  partitionProps.Boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "boot"))
 }
 if f.properties.Boot_16k_image != "" {
  partitionProps.Boot_16k_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "boot_16k"))
 }
 if f.properties.Vendor_boot_image != "" {
  partitionProps.Vendor_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_boot"))
 }
 if f.properties.Vendor_boot_debug_image != "" {
  partitionProps.Vendor_boot_debug_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_boot-debug"))
 }
 if f.properties.Vendor_boot_test_harness_image != "" {
  partitionProps.Vendor_boot_test_harness_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_boot-test-harness"))
 }
 if f.properties.Init_boot_image != "" {
  partitionProps.Init_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "init_boot"))
 } else if partitionVars.BuildingRamdiskImage {
  partitionProps.Ramdisk_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk"))
 }

 if f.properties.Vendor_kernel_boot_image != "" {
  partitionProps.Vendor_kernel_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_kernel_boot"))
 }
 if modName := partitions.nameForType("vendor_kernel_ramdisk"); modName != "" {
  partitionProps.Vendor_kernel_ramdisk_partition_name = proptools.StringPtr(modName)
 }

 // This property is only set in Soong-only builds as not all custom partitions has been
 // converted to Soong. Unconditionally setting this property will lead to missing
 // dependencies error.
 if !ctx.Config().KatiEnabled() {
  partitionProps.Custom_partitions = partitionVars.CustomImagesPartitions
 }

 partitionProps.Vbmeta_partitions = vbmetaPartitions

 deviceProps := &filesystem.DeviceProperties{
  Main_device:                          proptools.BoolPtr(true),
  Ab_ota_updater:                       proptools.BoolPtr(partitionVars.AbOtaUpdater),
  Ab_ota_partitions:                    partitionVars.AbOtaPartitions,
  Ab_ota_postinstall_config:            partitionVars.AbOtaPostInstallConfig,
  Android_info:                         proptools.StringPtr(generatedModuleName(ctx.Config(), "android_info.prop")),
  Kernel_version:                       ctx.Config().ProductVariables().BoardKernelVersion,
  Partial_ota_update_partitions:        partitionVars.BoardPartialOtaUpdatePartitionsList,
  Flash_block_size:                     proptools.StringPtr(partitionVars.BoardFlashBlockSize),
  Bootloader_in_update_package:         proptools.BoolPtr(partitionVars.BootloaderInUpdatePackage),
  Precompiled_sepolicy_without_vendor:  proptools.StringPtr(":precompiled_sepolicy_without_vendor"),
  Vendor_blobs_license:                 vendorBlobsLicenseProp,
  InfoPartitionProps:                   *infoPartitionProps,
  Minimal_font_footprint:               proptools.BoolPtr(partitionVars.MinimalFontFootprint),
  Stage_device_files:                   getstageDeviceFileProps(ctx),
  Product_restrict_vendor_files:        proptools.StringPtr(partitionVars.ProductRestrictVendorFiles),
  Vendor_product_restrict_vendor_files: proptools.StringPtr(partitionVars.VendorProductRestrictVendorFiles),
  Vendor_exception_paths:               partitionVars.VendorExceptionPaths,
  Vendor_exception_modules:             partitionVars.VendorExceptionModules,
 }

 if buildingInitBootImage(partitionVars) {
  // https://cs.android.com/android/_/android/platform/build/+/045a3d6a3e359633a14853a5a5e1e4f2a11cbdae:core/Makefile;l=6869-6873;drc=a951ebf0198006f7fd38073a05c442d0eb92f97b;bpv=1;bpt=0
  deviceProps.Ramdisk_node_list = proptools.StringPtr(":ramdisk_node_list")
 }

 if f.properties.Bootloader != "" {
  deviceProps.Bootloader = &f.properties.Bootloader
 }
 if releaseTools, ok := f.createReleaseToolsFilegroup(ctx); ok {
  deviceProps.Releasetools_extension = proptools.StringPtr(":" + releaseTools)
 }
 if fastbootInfo, ok := f.createFastbootInfoFilegroup(ctx); ok {
  deviceProps.FastbootInfo = proptools.StringPtr(":" + fastbootInfo)
 }
 if dtboModuleName := getDtboModuleName(ctx); dtboModuleName != "" {
  deviceProps.Dtbo_image = proptools.StringPtr(dtboModuleName)
 }
 if dtbo16kModuleName := getDtbo16kModuleName(ctx); dtbo16kModuleName != "" {
  deviceProps.Dtbo_image_16k = proptools.StringPtr(dtbo16kModuleName)
 }
 if pvmfwProperties := getPvmfwProperties(ctx); pvmfwProperties != nil {
  deviceProps.Pvmfw = *pvmfwProperties
 }
 if f.properties.Tzsw != "" {
  deviceProps.Tzsw = proptools.StringPtr(f.properties.Tzsw)
 }

 if f.properties.Radio_image != "" {
  deviceProps.Radio_partition_name = &f.properties.Radio_image
 }

 if ctx.Config().SoongDefinedSystemImage() != "" {
  deviceProps.System_filelist_diff_ref = proptools.StringPtr(generatedModuleName(ctx.Config(), "system_filelist_check_image"))
 }

 ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps, deviceProps)
}

func createRadioImg(ctx android.LoadHookContext) string {
 radioFilePath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardRadioImagePath
 if radioFilePath == "" {
  return ""
 }
 if path := android.ExistentPathForSource(ctx, radioFilePath); !path.Valid() {
  return ""
 }
 name := generatedModuleNameForPartition(ctx.Config(), "radio")
 radioImgProps := filesystem.PrebuiltRadioImgProperties{
  Src:               proptools.StringPtr(radioFilePath),
  Ab_ota_partitions: ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaRadioPartitions,
  Unpack_tool:       proptools.StringPtr(fmt.Sprintf("vendor/google_devices/%s/prebuilts/misc_bins/unpack.py", proptools.String(ctx.Config().ProductVariables().BoardPlatform))),
 }

 ctx.CreateModuleInDirectory(
  filesystem.PrebuiltRadioImgFactory,
  ".",
  &struct {
   Name *string
  }{
   Name: &name,
  },
  &radioImgProps,
 )
 return name
}

// Returns pvfmw properties if the product uses pvmfw.
func getPvmfwProperties(ctx android.LoadHookContext) *filesystem.PvmfwProperties {
 if !ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardUsesPvmfwImage {
  return nil
 }
 var partitionSize *int64
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 boardPvmfwPartitionSize := partitionVars.BoardPvmfwPartitionSize
 if boardPvmfwPartitionSize != "" {
  size, err := strconv.ParseInt(boardPvmfwPartitionSize, 064)
  if err != nil {
   ctx.ModuleErrorf("Error parsing BoardPvmfwPartitionSize %s", err)
  }
  partitionSize = proptools.Int64Ptr(size)
 }
 image := "pvmfw_img"
 if partitionVars.BoardPvmfwImagePrebuilt != "" {
  image = partitionVars.BoardPvmfwImagePrebuilt
 }
 bin := "pvmfw_bin"
 if partitionVars.BoardPvmfwBinPrebuilt != "" {
  bin = partitionVars.BoardPvmfwBinPrebuilt
 }
 avbkey := "pvmfw_embedded_key_pub_bin"
 if partitionVars.BoardPvmfwEmbeddedAvbkeyPrebuilt != "" {
  avbkey = partitionVars.BoardPvmfwEmbeddedAvbkeyPrebuilt
 }

 return &filesystem.PvmfwProperties{
  Image:          proptools.StringPtr(":" + image),
  Binary_name:    proptools.StringPtr(bin),
  Avbkey:         proptools.StringPtr(":" + avbkey),
  Partition_size: partitionSize,
 }
}

func createRamdisk16k(ctx android.LoadHookContext) string {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 kernelPath := partitionVars.BoardKernelPath16k
 if kernelPath == "" {
  return ""
 }

 name := generatedModuleNameForPartition(ctx.Config(), "ramdisk_16k")
 props := filesystem.Ramdisk16kImgProperties{
  System_dep: proptools.StringPtr(fmt.Sprintf(":%s{.modules.zip}", generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules"))),
  Kernel:     proptools.StringPtr(kernelPath),
 }

 if partitionVars.BoardKernelModulesZip != "" {
  props.Zip.Src = &partitionVars.BoardKernelModulesZip
  props.Zip.Extra_blocked_modules = partitionVars.BoardKernelModulesZipExtraBlocked16kModules
 } else {
  props.Srcs = partitionVars.BoardKernelModules16K
  props.Load = partitionVars.BoardKernelModulesLoad16K
 }

 ctx.CreateModuleInDirectory(
  filesystem.Ramdisk16kImgFactory,
  ".",
  &struct {
   Name *string
  }{
   Name: &name,
  },
  &props,
 )
 return name
}

func partitionSpecificFsProps(ctx android.EarlyModuleContext, partitions allGeneratedPartitionData, fsProps *filesystem.FilesystemProperties, partitionType string) {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 switch partitionType {
 case "system":
  fsProps.Build_logtags = proptools.BoolPtr(true)
  // https://source.corp.google.com/h/googleplex-android/platform/build//639d79f5012a6542ab1f733b0697db45761ab0f3:core/packaging/flags.mk;l=21;drc=5ba8a8b77507f93aa48cc61c5ba3f31a4d0cbf37;bpv=1;bpt=0
  fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
  fsProps.Check_vintf = proptools.BoolPtr(true)
  // Identical to that of the aosp_shared_system_image
  if partitionVars.ProductFsverityGenerateMetadata {
   fsProps.Fsverity.Inputs = proptools.NewSimpleConfigurable([]string{
    "etc/boot-image.prof",
    "etc/dirty-image-objects",
    "etc/preloaded-classes",
    "etc/classpaths/*.pb",
    "framework/*",
    "framework/*/*",     // framework/{arch}
    "framework/oat/*/*", // framework/oat/{arch}
   })
   fsProps.Fsverity.Libs = proptools.NewSimpleConfigurable([]string{":framework-res{.export-package.apk}"})
  }
  fsProps.Symlinks = commonSymlinksFromRoot
  fsProps.Symlinks = append(fsProps.Symlinks,
   []filesystem.SymlinkDefinition{
    {
     Target: proptools.StringPtr("/data/cache"),
     Name:   proptools.StringPtr("cache"),
    },
    {
     Target: proptools.StringPtr("/storage/self/primary"),
     Name:   proptools.StringPtr("sdcard"),
    },
   }...,
  )
  if ctx.DeviceConfig().VendorPath() == "vendor" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/vendor"),
     Name:   proptools.StringPtr("system/vendor"),
    },
   )
  }
  if ctx.DeviceConfig().ProductPath() == "product" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/product"),
     Name:   proptools.StringPtr("system/product"),
    },
   )
  }
  if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/system_ext"),
     Name:   proptools.StringPtr("system/system_ext"),
    },
   )
  }
  if ctx.DeviceConfig().SystemDlkmPath() == "system_dlkm" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/system_dlkm/lib/modules"),
     Name:   proptools.StringPtr("system/lib/modules"),
    },
   )
  }

  fsProps.Base_dir = proptools.StringPtr("system")
  fsProps.Dirs = proptools.NewSimpleConfigurable(commonPartitionDirs)
  fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
  fsProps.Stem = proptools.StringPtr("system.img")
 case "system_ext":
  if partitionVars.ProductFsverityGenerateMetadata {
   fsProps.Fsverity.Inputs = proptools.NewSimpleConfigurable([]string{
    "framework/*",
    "framework/*/*",     // framework/{arch}
    "framework/oat/*/*", // framework/oat/{arch}
   })
   fsProps.Fsverity.Libs = proptools.NewSimpleConfigurable([]string{":framework-res{.export-package.apk}"})
  }
  fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
  fsProps.Stem = proptools.StringPtr("system_ext.img")
  fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
 case "product":
  fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
  if systemName := partitions.nameForType("system"); systemName != "" {
   fsProps.Android_filesystem_deps.System = proptools.StringPtr(systemName)
  }
  if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
   fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(systemExtName)
  }
  fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
  fsProps.Stem = proptools.StringPtr("product.img")
 case "vendor":
  fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
  fsProps.Check_vintf = proptools.BoolPtr(true)
  if ctx.DeviceConfig().OdmPath() == "odm" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/odm"),
     Name:   proptools.StringPtr("odm"),
    },
   )
  }
  if ctx.DeviceConfig().VendorDlkmPath() == "vendor_dlkm" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/vendor_dlkm/lib/modules"),
     Name:   proptools.StringPtr("lib/modules"),
    },
   )
  }
  if systemName := partitions.nameForType("system"); systemName != "" {
   fsProps.Android_filesystem_deps.System = proptools.StringPtr(systemName)
  }
  if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
   fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(systemExtName)
  }
  if productName := partitions.nameForType("product"); productName != "" {
   fsProps.Android_filesystem_deps.Product = proptools.StringPtr(productName)
  }
  fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorSecurityPatch)
  fsProps.Stem = proptools.StringPtr("vendor.img")
 case "odm":
  if ctx.DeviceConfig().OdmDlkmPath() == "odm_dlkm" {
   fsProps.Symlinks = append(fsProps.Symlinks,
    filesystem.SymlinkDefinition{
     Target: proptools.StringPtr("/odm_dlkm/lib/modules"),
     Name:   proptools.StringPtr("lib/modules"),
    },
   )
  }
  fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmSecurityPatch)
  fsProps.Stem = proptools.StringPtr("odm.img")
 case "userdata":
  fsProps.Stem = proptools.StringPtr("userdata.img")
  if vars, ok := partitionVars.PartitionQualifiedVariables["userdata"]; ok {
   parsed, err := strconv.ParseInt(vars.BoardPartitionSize, 1064)
   if err != nil {
    panic(fmt.Sprintf("Partition size must be an int, got %s", vars.BoardPartitionSize))
   }
   fsProps.Partition_size = &parsed
   // Disable avb for userdata partition
   fsProps.Use_avb = nil
  }
  // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2265;drc=7f50a123045520f2c5e18e9eb4e83f92244a1459
  if s, err := strconv.ParseBool(partitionVars.ProductFsCasefold); err == nil {
   fsProps.Support_casefolding = proptools.BoolPtr(s)
  } else if len(partitionVars.ProductFsCasefold) > 0 {
   ctx.ModuleErrorf("Unrecognized PRODUCT_FS_CASEFOLD value %s", partitionVars.ProductFsCasefold)
  }
  if s, err := strconv.ParseBool(partitionVars.ProductQuotaProjid); err == nil {
   fsProps.Support_project_quota = proptools.BoolPtr(s)
  } else if len(partitionVars.ProductQuotaProjid) > 0 {
   ctx.ModuleErrorf("Unrecognized PRODUCT_QUOTA_PROJID value %s", partitionVars.ProductQuotaProjid)
  }
  if s, err := strconv.ParseBool(partitionVars.ProductFsCompression); err == nil {
   fsProps.Enable_compression = proptools.BoolPtr(s)
  } else if len(partitionVars.ProductFsCompression) > 0 {
   ctx.ModuleErrorf("Unrecognized PRODUCT_FS_COMPRESSION value %s", partitionVars.ProductFsCompression)
  }
  if s, err := strconv.ParseInt(partitionVars.BoardF2fsBlockSize, 1064); err == nil {
   fsProps.F2fs_blocksize = proptools.Int64Ptr(s)
  } else if len(partitionVars.BoardF2fsBlockSize) > 0 {
   ctx.ModuleErrorf("Unrecognized BOARD_F2FS_BLOCKSIZE value %s", partitionVars.BoardF2fsBlockSize)
  }
  if s, err := strconv.ParseBool(partitionVars.BoardF2fsPackedSsa); err == nil {
   fsProps.Support_f2fs_packedssa = proptools.BoolPtr(s)
  } else if len(partitionVars.BoardF2fsPackedSsa) > 0 {
   ctx.ModuleErrorf("Unrecognized BOARD_F2FS_PACKED_SSA value %s", partitionVars.BoardF2fsPackedSsa)
  }

 case "ramdisk":
  // Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
  fsProps.Dirs = android.NewSimpleConfigurable([]string{
   "debug_ramdisk",
   "dev",
   "metadata",
   "mnt",
   "proc",
   "second_stage_resources",
   "sys",
  })
  if partitionVars.BoardUsesGenericKernelImage {
   fsProps.Dirs.AppendSimpleValue([]string{
    "first_stage_ramdisk/debug_ramdisk",
    "first_stage_ramdisk/dev",
    "first_stage_ramdisk/metadata",
    "first_stage_ramdisk/mnt",
    "first_stage_ramdisk/proc",
    "first_stage_ramdisk/second_stage_resources",
    "first_stage_ramdisk/sys",
   })
  }
  fsProps.Stem = proptools.StringPtr("ramdisk.img")
 case "recovery":
  dirs := append(commonPartitionDirs, []string{
   "sdcard",
  }...)

  dirsWithRoot := make([]string, len(dirs))
  for i, dir := range dirs {
   dirsWithRoot[i] = filepath.Join("root", dir)
  }

  fsProps.Dirs = proptools.NewSimpleConfigurable(dirsWithRoot)
  fsProps.Symlinks = symlinksWithNamePrefix(append(commonSymlinksFromRoot, filesystem.SymlinkDefinition{
   Target: proptools.StringPtr("prop.default"),
   Name:   proptools.StringPtr("default.prop"),
  }), "root")
  fsProps.Stem = proptools.StringPtr("recovery.img")
 case "system_dlkm":
  fsProps.Security_patch = proptools.StringPtr(partitionVars.SystemDlkmSecurityPatch)
  fsProps.Stem = proptools.StringPtr("system_dlkm.img")
 case "vendor_dlkm":
  fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorDlkmSecurityPatch)
  fsProps.Stem = proptools.StringPtr("vendor_dlkm.img")
 case "odm_dlkm":
  fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmDlkmSecurityPatch)
  fsProps.Stem = proptools.StringPtr("odm_dlkm.img")
 case "vendor_ramdisk":
  if recoveryName := partitions.nameForType("recovery"); recoveryName != "" {
   fsProps.Include_files_of = []string{recoveryName}
  }
  fsProps.Stem = proptools.StringPtr("vendor_ramdisk.img")
 case "vendor_ramdisk-debug":
  fsProps.Include_files_of = append(
   fsProps.Include_files_of,
   generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk"),
  )
  if recoveryName := partitions.nameForType("recovery"); recoveryName != "" {
   fsProps.Include_files_of = append(
    fsProps.Include_files_of,
    recoveryName,
   )
  }
  fsProps.Include_files_of = append(
   fsProps.Include_files_of,
   generatedModuleNameForPartition(ctx.Config(), "debug_ramdisk"),
  )
  fsProps.Stem = proptools.StringPtr("vendor_ramdisk-debug.img")
 case "vendor_ramdisk-test-harness":
  fsProps.Include_files_of = append(
   fsProps.Include_files_of,
   generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk"),
  )
  if recoveryName := partitions.nameForType("recovery"); recoveryName != "" {
   fsProps.Include_files_of = append(
    fsProps.Include_files_of,
    recoveryName,
   )
  }
  fsProps.Include_files_of = append(
   fsProps.Include_files_of,
   generatedModuleNameForPartition(ctx.Config(), "debug_ramdisk"),
   generatedModuleNameForPartition(ctx.Config(), "test_harness_ramdisk"),
  )
  fsProps.Stem = proptools.StringPtr("vendor_ramdisk-test-harness.img")
 case "vendor_kernel_ramdisk":
  fsProps.Stem = proptools.StringPtr("vendor_kernel_ramdisk.img")
 case "vendor_ramdisk_fragment_dlkm":
  fsProps.Ramdisk_fragment_name = proptools.StringPtr("dlkm")
 }
}

var (
 partitionsWithKernelModules = []string{
  "system_dlkm",
  "vendor_dlkm",
  "odm_dlkm",
  "vendor_ramdisk",
  "vendor_ramdisk-debug",
  "vendor_ramdisk-test-harness",
  "vendor_kernel_ramdisk",
  "vendor_ramdisk_fragment_dlkm",
 }
)

// Creates a soong module to build the given partition.
func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitions allGeneratedPartitionData, partition *generatedPartitionData) {
 // Handwritten images, don't need to create anything
 if partition.handwritten {
  return
 }

 baseProps := generateBaseProps(proptools.StringPtr(partition.moduleName), ctx.Config())

 fsProps, supported := generateFsProps(ctx, partitions, partition.partitionType)
 if !supported {
  partition.supported = false
  return
 }

 partitionType := partition.partitionType
 if partitionType == "vendor" || partitionType == "product" || partitionType == "system" {
  fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true)
  if partitionType != "system" {
   fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
  }
 }

 if android.InList(partitionType, partitionsWithKernelModules) {
  f.createPrebuiltKernelModules(ctx, partitionType)
 }
 if partitionType == "vendor_ramdisk_fragment_dlkm" {
  partitionType = "vendor_ramdisk"
 }

 var module android.Module
 if partitionType == "system" {
  module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
 } else {
  // Explicitly set the partition.
  fsProps.Partition_type = proptools.StringPtr(partitionType)
  module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
 }
 module.HideFromMake()
 if partitionType == "vendor" {
  f.createVendorBuildProp(ctx)
 }
}

// Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
func (f *filesystemCreator) createAvbKeyFilegroups(ctx android.LoadHookContext) {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 var files []string

 if len(partitionVars.BoardAvbKeyPath) > 0 {
  files = append(files, partitionVars.BoardAvbKeyPath)
 }
 for _, partition := range android.SortedKeys(partitionVars.PartitionQualifiedVariables) {
  specificPartitionVars := partitionVars.PartitionQualifiedVariables[partition]
  if len(specificPartitionVars.BoardAvbKeyPath) > 0 {
   files = append(files, specificPartitionVars.BoardAvbKeyPath)
  }
 }

 fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
 for _, file := range files {
  if _, ok := fsGenState.avbKeyFilegroups[file]; ok {
   continue
  }
  if file == "external/avb/test/data/testkey_rsa4096.pem" {
   // There already exists a checked-in filegroup for this commonly-used key, just use that
   fsGenState.avbKeyFilegroups[file] = "avb_testkey_rsa4096"
   continue
  }
  dir := filepath.Dir(file)
  base := filepath.Base(file)
  name := fmt.Sprintf("avb_key_%x", strings.ReplaceAll(file, "/", "_"))
  ctx.CreateModuleInDirectory(
   android.FileGroupFactory,
   dir,
   &struct {
    Name       *string
    Srcs       []string
    Visibility []string
   }{
    Name:       proptools.StringPtr(name),
    Srcs:       []string{base},
    Visibility: []string{"//visibility:public"},
   },
  )
  fsGenState.avbKeyFilegroups[file] = name
 }
}

func (f *filesystemCreator) createBackgroundPicturesForRecovery(ctx android.LoadHookContext) {
 name, width := getRecoveryBackgroundPicturesGeneratorModuleName(ctx)
 if name == "" {
  return
 }
 ctx.CreateModule(
  filesystem.RecoveryBackgroundPicturesFactory,
  &struct {
   Name        *string
   Image_width *int64
   Fonts       []string
   Resources   []string
   Recovery    *bool
  }{
   Name:        proptools.StringPtr(name),
   Image_width: proptools.Int64Ptr(width),
   Fonts:       []string{":recovery_noto-fonts_dep", ":recovery_roboto-fonts_dep"},
   Resources:   []string{":bootable_recovery_resources"},
   Recovery:    proptools.BoolPtr(true),
  },
 )

}

// Creates filegroups for miscellaneous other files
func (f *filesystemCreator) createMiscFilegroups(ctx android.LoadHookContext) {
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse

 if partitionVars.BoardErofsCompressorHints != "" {
  dir := filepath.Dir(partitionVars.BoardErofsCompressorHints)
  base := filepath.Base(partitionVars.BoardErofsCompressorHints)
  ctx.CreateModuleInDirectory(
   android.FileGroupFactory,
   dir,
   &struct {
    Name       *string
    Srcs       []string
    Visibility []string
   }{
    Name:       proptools.StringPtr("soong_generated_board_erofs_compress_hints_filegroup"),
    Srcs:       []string{base},
    Visibility: []string{"//visibility:public"},
   },
  )
 }
}

// Creates genrules for dist rules defined in product config
// One genrule will be created per goal.
func (f *filesystemCreator) createProductConfigDistGenrules(ctx android.LoadHookContext) {
 if ctx.Config().KatiEnabled() {
  // Skip in soong+make builds to prevent duplicate dist rules.
  return
 }
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse

 dstToSrcs := map[string]string{}
 for _, entry := range partitionVars.AllDistSrcDstPairs {
  split := strings.Split(entry, ":")
  src, dst := split[0], split[1]
  dstToSrcs[dst] = src
 }
 goalToDst := map[string][]string{}
 goalToSrcs := map[string][]string{}
 for _, entry := range partitionVars.AllDistGoalOutputPairs {
  split := strings.Split(entry, ":")
  goal, dst := split[0], split[1]
  src := dstToSrcs[dst]
  if !strings.HasPrefix(src, ctx.Config().OutDir()) {
   // check src file is not under out/.
   // Disting generated files is not supported at the moment (it will cause missing dependency errors).
   goalToDst[goal] = append(goalToDst[goal], dst)
   goalToSrcs[goal] = append(goalToSrcs[goal], dstToSrcs[dst])
  }
 }

 for goal, srcs := range goalToSrcs {
  ctx.CreateModuleInDirectory(
   genrule.GenRuleFactory,
   ".",
   &struct {
    Name       *string
    Srcs       []string
    Out        []string
    Cmd        *string
    Dist       android.Dist
    Visibility []string
   }{
    Name:       proptools.StringPtr("soong_generated_dist_genrule_for_goal_" + goal),
    Srcs:       srcs,
    Out:        goalToDst[goal],
    Cmd:        proptools.StringPtr("in_files=($(in)); out_files=($(out)); for i in $${!in_files[@]}; do cp $${in_files[i]} $${out_files[i]}; done"),
    Dist:       android.Dist{Targets: []string{goal}},
    Visibility: []string{"//visibility:public"},
   },
  )
 }
}

// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency.
// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view.
func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string) {
 fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
 name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType))
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 commonProps := &struct {
  Name                  *string
  System_dlkm_specific  *bool
  Vendor_dlkm_specific  *bool
  Odm_dlkm_specific     *bool
  Vendor_ramdisk        *bool
  Vendor_kernel_ramdisk *bool
 }{
  Name: proptools.StringPtr(name),
 }
 props := &kernel.PrebuiltKernelModulesProperties{}

 hasSrcs := []bool{false}
 setSrcs := func(srcs []string) {
  if len(srcs) > 0 {
   props.Srcs = android.NewSimpleConfigurable(srcs)
   hasSrcs[0] = true
  }
 }

 if partitionVars.BoardKernelModulesZip != "" {
  // This can be either a source file from the top of the tree, or a module reference.
  // The source file case happens to work because we create these modules in the root
  // directory.
  props.Zip.Src = &partitionVars.BoardKernelModulesZip
  hasSrcs[0] = true
 }

 switch partitionType {
 case "system_dlkm":
  commonProps.System_dlkm_specific = proptools.BoolPtr(true)
  props.Strip_debug_symbols = proptools.BoolPtr(false)

  if partitionVars.BoardKernelModulesZip != "" {
   props.Zip.Load_file = proptools.StringPtr("system_dlkm.modules.load")
   props.Zip.Blocklist_file = proptools.StringPtr("system_dlkm.modules.blocklist")
  } else {
   setSrcs(android.ExistentPathsForSources(ctx, partitionVars.SystemKernelModules).Strings())
   if len(partitionVars.SystemKernelLoadModules) == 0 {
    // Create empty modules.load file for system
    // https://source.corp.google.com/h/googleplex-android/platform/build/+/ef55daac9954896161b26db4f3ef1781b5a5694c:core/Makefile;l=695-700;drc=549fe2a5162548bd8b47867d35f907eb22332023;bpv=1;bpt=0
    props.Load_by_default = proptools.BoolPtr(false)
   }
   if blocklistFile := partitionVars.SystemKernelBlocklistFile; blocklistFile != "" {
    props.Blocklist_file = proptools.StringPtr(blocklistFile)
   }
  }
 case "vendor_dlkm":
  commonProps.Vendor_dlkm_specific = proptools.BoolPtr(true)
  if partitionVars.DoNotStripVendorModules {
   props.Strip_debug_symbols = proptools.BoolPtr(false)
  }

  if partitionVars.BoardKernelModulesZip != "" {
   setSrcs(android.ExistentPathsForSources(ctx, partitionVars.BoardKernelModulesZipExtraVendorKernelModules).Strings())
   props.Zip.Load_file = proptools.StringPtr("vendor_dlkm.modules.load")
   props.Zip.Blocklist_file = proptools.StringPtr("vendor_dlkm.modules.blocklist")
   // TODO: Should probably remove the device name from this file
   props.Zip.Srcs_16k_cfg_file = proptools.StringPtr(fmt.Sprintf("init.insmod.%s.cfg", ctx.Config().DeviceName()))
   // unconditionally add this dep when using a zip, as we don't know if there are actually
   // kernel modules for the system or not until execution time.
   props.System_dep = proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules.zip}")
  } else {
   setSrcs(android.ExistentPathsForSources(ctx, partitionVars.VendorKernelModules).Strings())
   props.Src_filenames_to_load = partitionVars.VendorKernelModulesLoad
   props.Srcs_16k = android.ExistentPathsForSources(ctx, partitionVars.VendorKernelModules2ndStage16kbMode).Strings()
   if len(partitionVars.SystemKernelModules) > 0 {
    props.System_dep = proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules.zip}")
   }
   if blocklistFile := partitionVars.VendorKernelBlocklistFile; blocklistFile != "" {
    props.Blocklist_file = proptools.StringPtr(blocklistFile)
   }
  }
 case "odm_dlkm":
  commonProps.Odm_dlkm_specific = proptools.BoolPtr(true)
  props.Strip_debug_symbols = proptools.BoolPtr(false)

  if partitionVars.BoardKernelModulesZip != "" {
   props.Zip.Load_file = proptools.StringPtr("odm_dlkm.modules.load")
   props.Zip.Blocklist_file = proptools.StringPtr("odm_dlkm.modules.blocklist")
  } else {
   setSrcs(android.ExistentPathsForSources(ctx, partitionVars.OdmKernelModules).Strings())
   if blocklistFile := partitionVars.OdmKernelBlocklistFile; blocklistFile != "" {
    props.Blocklist_file = proptools.StringPtr(blocklistFile)
   }
  }
 case "vendor_ramdisk", "vendor_ramdisk-debug", "vendor_ramdisk-test-harness", "vendor_ramdisk_fragment_dlkm":
  commonProps.Vendor_ramdisk = proptools.BoolPtr(true)

  if partitionType == "vendor_ramdisk" && buildingVendorRamdiskFragmentDlkm(ctx, partitionVars) {
   // Skip including the kernel modules in vendor_ramdisk.
   // The kernel modules will come from the dlkm ramdisk fragment.
   return
  }

  if partitionVars.BoardKernelModulesZip != "" {
   // Pixels don't use BOARD_VENDOR_RAMDISK_KERNEL_MODULES
   return
  }

  setSrcs(android.ExistentPathsForSources(ctx, partitionVars.VendorRamdiskKernelModules).Strings())

  if blocklistFile := partitionVars.VendorRamdiskKernelBlocklistFile; blocklistFile != "" {
   props.Blocklist_file = proptools.StringPtr(blocklistFile)
  }
  if optionsFile := partitionVars.VendorRamdiskKernelOptionsFile; optionsFile != "" {
   props.Options_file = proptools.StringPtr(optionsFile)
  }
  if partitionVars.DoNotStripVendorRamdiskModules {
   props.Strip_debug_symbols = proptools.BoolPtr(false)
  }
 case "vendor_kernel_ramdisk":
  commonProps.Vendor_kernel_ramdisk = proptools.BoolPtr(true)
  props.Strip_debug_symbols = proptools.BoolPtr(false)
  if partitionVars.BoardKernelModulesZip != "" {
   props.Zip.Load_file = proptools.StringPtr("vendor_kernel_boot.modules.load")
   props.Zip.Extra_loads = partitionVars.BoardKernelModulesZipExtraVendorKernelRamdiskLoads
  } else {
   setSrcs(android.ExistentPathsForSources(ctx, partitionVars.VendorKernelRamdiskKernelModules).Strings())
  }
 default:
  ctx.ModuleErrorf("DLKM is not supported for %s\n", partitionType)
 }

 if !hasSrcs[0] {
  return // do not generate `prebuilt_kernel_modules` if there are no sources
 }

 kernelModule := ctx.CreateModuleInDirectory(
  kernel.PrebuiltKernelModulesFactory,
  ".", // create in root directory for now
  commonProps,
  props,
 )
 kernelModule.HideFromMake()
 // Add to deps
 (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
}

// Create an android_info module. This will be used to create /vendor/build.prop
func (f *filesystemCreator) createAndroidInfo(ctx android.LoadHookContext) {
 // Create a android_info for vendor
 // The board info files might be in a directory outside the root soong namespace, so create
 // the module in "."
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 androidInfoProps := &android.AndroidInfoProperties{

  Board_info_files: partitionVars.BoardInfoFiles,
  Stem:             proptools.StringPtr("android-info.txt"),
 }
 if len(androidInfoProps.Board_info_files) == 0 {
  androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
 }
 androidInfoProp := ctx.CreateModuleInDirectory(
  android.AndroidInfoFactory,
  ".",
  &struct {
   Name *string
  }{
   Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "android_info.prop")),
  },
  androidInfoProps,
 )
 androidInfoProp.HideFromMake()
}

func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
 vendorBuildProps := &struct {
  Name           *string
  Vendor         *bool
  Stem           *string
  Product_config *string
  Android_info   *string
  Licenses       []string
  Dist           android.Dist
 }{
  Name:           proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
  Vendor:         proptools.BoolPtr(true),
  Stem:           proptools.StringPtr("build.prop"),
  Product_config: proptools.StringPtr(":product_config"),
  Android_info:   proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop")),
  Dist: android.Dist{
   Targets: []string{"droidcore-unbundled"},
   Dest:    proptools.StringPtr("build.prop-vendor"),
  },
  Licenses: []string{"Android-Apache-2.0"},
 }
 vendorBuildProp := ctx.CreateModule(
  android.BuildPropFactory,
  vendorBuildProps,
 )
 // We don't want this to conflict with the make-built vendor build.prop, but unfortunately
 // calling HideFromMake() prevents disting files, even in soong-only mode. So only call
 // HideFromMake() on soong+make builds.
 if ctx.Config().KatiEnabled() {
  vendorBuildProp.HideFromMake()
 }
}

func createRecoveryBuildProp(ctx android.LoadHookContext) string {
 moduleName := generatedModuleName(ctx.Config(), "recovery-prop.default")

 var vendorBuildProp *string
 if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
  vendorBuildProp = proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "vendor-build.prop"))
 }

 recoveryBuildProps := &struct {
  Name                  *string
  System_build_prop     *string
  Vendor_build_prop     *string
  Odm_build_prop        *string
  Product_build_prop    *string
  System_ext_build_prop *string

  Recovery        *bool
  No_full_install *bool
  Visibility      []string
 }{
  Name:                  proptools.StringPtr(moduleName),
  System_build_prop:     proptools.StringPtr(":system-build.prop"),
  Vendor_build_prop:     vendorBuildProp,
  Odm_build_prop:        proptools.StringPtr(":odm-build.prop"),
  Product_build_prop:    proptools.StringPtr(":product-build.prop"),
  System_ext_build_prop: proptools.StringPtr(":system_ext-build.prop"),

  Recovery:        proptools.BoolPtr(true),
  No_full_install: proptools.BoolPtr(true),
  Visibility:      []string{"//visibility:public"},
 }

 ctx.CreateModule(android.RecoveryBuildPropModuleFactory, recoveryBuildProps)

 return moduleName
}

// createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
// 1. vendor: Using PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS (space separated file list)
// 1. product: Using PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS (space separated file list)
// It creates a filegroup for each file in the fragment list
// The filegroup modules are then added to `linker_config_srcs` of the autogenerated vendor `android_filesystem`.
func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadHookContext, partitionType string) []string {
 ret := []string{}
 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 var linkerConfigSrcs []string
 if partitionType == "vendor" {
  linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.VendorLinkerConfigSrcs)
 } else if partitionType == "product" {
  linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.ProductLinkerConfigSrcs)
 } else {
  ctx.ModuleErrorf("linker.config.pb is only supported for vendor and product partitions. For system partition, use `android_system_image`")
 }

 if len(linkerConfigSrcs) > 0 {
  // Create a filegroup, and add `:<filegroup_name>` to ret.
  for index, linkerConfigSrc := range linkerConfigSrcs {
   dir := filepath.Dir(linkerConfigSrc)
   base := filepath.Base(linkerConfigSrc)
   fgName := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-linker-config-src%s", partitionType, strconv.Itoa(index)))
   srcs := []string{base}
   fgProps := &struct {
    Name *string
    Srcs proptools.Configurable[[]string]
   }{
    Name: proptools.StringPtr(fgName),
    Srcs: proptools.NewSimpleConfigurable(srcs),
   }
   ctx.CreateModuleInDirectory(
    android.FileGroupFactory,
    dir,
    fgProps,
   )
   ret = append(ret, ":"+fgName)
  }
 }
 return ret
}

type filesystemBaseProperty struct {
 Name                    *string
 Compile_multilib        *string
 Native_bridge_supported *bool
 Visibility              []string
 Unchecked_module        *bool
}

func generateBaseProps(namePtr *string, config android.Config) *filesystemBaseProperty {
 return &filesystemBaseProperty{
  Name:                    namePtr,
  Compile_multilib:        proptools.StringPtr("both"),
  Native_bridge_supported: proptools.BoolPtr(config.ProductVariables().NativeBridgeArch != nil),
  // The vbmeta modules are currently in the root directory and depend on the partitions
  Visibility: []string{"//.", "//build/soong:__subpackages__"},
  // Don't build this module on checkbuilds, the soong-built partitions are still in-progress
  // and sometimes don't build.
  Unchecked_module: proptools.BoolPtr(true),
 }
}

func generateFsProps(ctx android.EarlyModuleContext, partitions allGeneratedPartitionData, partitionType string) (*filesystem.FilesystemProperties, bool) {
 fsProps := &filesystem.FilesystemProperties{}

 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 var avbInfo avbInfo
 var fsType string
 if strings.Contains(partitionType, "ramdisk") {
  fsType = "compressed_cpio"
 } else {
  specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
  fsType = specificPartitionVars.BoardFileSystemType
  avbInfo = getAvbInfo(ctx.Config(), partitionType)
  if fsType == "" {
   fsType = "ext4" //default
  }
 }

 fsProps.Type = proptools.NewSimpleConfigurable(fsType)
 if filesystem.GetFsTypeFromString(ctx, fsType).IsUnknown() {
  // Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
  return nil, false
 }

 switch fsType {
 case "erofs":
  if partitionVars.BoardErofsCompressor != "" {
   fsProps.Erofs.Compressor = proptools.NewSimpleConfigurable(partitionVars.BoardErofsCompressor)
  }
  if partitionVars.BoardErofsCompressorHints != "" {
   fsProps.Erofs.Compress_hints = proptools.NewSimpleConfigurable(":soong_generated_board_erofs_compress_hints_filegroup")
  }
  if s, err := strconv.ParseBool(partitionVars.BoardErofsShareDupBlocks); err == nil {
   fsProps.Share_dup_blocks = proptools.BoolPtr(s)
  }
  if partitionVars.BoardErofsEnableDedupe != "" {
   s, err := strconv.ParseBool(partitionVars.BoardErofsEnableDedupe)
   if err != nil {
    panic(fmt.Sprintf("erofs enable dedupe must be a bool, got %s", partitionVars.BoardErofsEnableDedupe))
   }
   fsProps.Erofs.Enable_dedupe = proptools.BoolPtr(s)
  }
  if len(partitionVars.BoardErofsPclusterSize) > 0 {
   parsed, err := strconv.ParseInt(partitionVars.BoardErofsPclusterSize, 1064)
   if err != nil {
    panic(fmt.Sprintf("erofs pcluster size must be an int, got %s", partitionVars.BoardErofsPclusterSize))
   }
   fsProps.Erofs.Pcluster_size = &parsed
  }
  // BOARD_*IMAGE_PCLUSTER_SIZE overrides BOARD_EROFS_PCLUSTER_SIZE
  specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
  if len(specificPartitionVars.BoardErofsPclusterSize) > 0 {
   parsed, err := strconv.ParseInt(specificPartitionVars.BoardErofsPclusterSize, 1064)
   if err != nil {
    panic(fmt.Sprintf("%s erofs pcluster size must be an int, got %s", partitionType, specificPartitionVars.BoardErofsPclusterSize))
   }
   fsProps.Erofs.Pcluster_size = &parsed
  }
  if len(partitionVars.BoardErofsBlockSize) > 0 {
   parsed, err := strconv.ParseInt(partitionVars.BoardErofsBlockSize, 1064)
   if err != nil {
    panic(fmt.Sprintf("erofs pcluster size must be an int, got %s", partitionVars.BoardErofsBlockSize))
   }
   fsProps.Erofs.Block_size = &parsed
  }
  // BOARD_*IMAGE_EROFS_BLOCKSIZE overrides BOARD_EROFS_BLOCKSIZE
  if len(specificPartitionVars.BoardErofsBlockSize) > 0 {
   parsed, err := strconv.ParseInt(specificPartitionVars.BoardErofsBlockSize, 1064)
   if err != nil {
    panic(fmt.Sprintf("%s erofs block size must be an int, got %s", partitionType, specificPartitionVars.BoardErofsBlockSize))
   }
   fsProps.Erofs.Block_size = &parsed
  }
  if specificPartitionVars.BoardErofsEnableDedupe != "" {
   s, err := strconv.ParseBool(specificPartitionVars.BoardErofsEnableDedupe)
   if err != nil {
    panic(fmt.Sprintf("%s erofs enable dedupe must be a bool, got %s", partitionType, specificPartitionVars.BoardErofsEnableDedupe))
   }
   fsProps.Erofs.Enable_dedupe = proptools.BoolPtr(s)
  }
 case "ext4":
  if s, err := strconv.ParseBool(partitionVars.BoardExt4ShareDupBlocks); err == nil {
   fsProps.Share_dup_blocks = proptools.BoolPtr(s)
  }
 }

 // BOARD_AVB_ENABLE
 fsProps.Use_avb = avbInfo.avbEnable
 // BOARD_AVB_KEY_PATH
 fsProps.Avb_private_key = avbInfo.avbkeyFilegroup
 // BOARD_AVB_ALGORITHM
 fsProps.Avb_algorithm = avbInfo.avbAlgorithm
 // BOARD_AVB_SYSTEM_ROLLBACK_INDEX
 fsProps.Rollback_index = avbInfo.avbRollbackIndex
 // BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION
 fsProps.Rollback_index_location = avbInfo.avbRollbackIndexLocation
 fsProps.Avb_hash_algorithm = avbInfo.avbHashAlgorithm

 // Do not use a fixed timestamp.
 // This prevents a full push on the first adb sync.
 fsProps.No_use_fixed_timestamp = proptools.BoolPtr(true)

 fsProps.Partition_name = proptools.StringPtr(partitionType)

 switch partitionType {
 // The partitions that support file_contexts came from here:
 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2270;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
 case "system", "userdata", "cache", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm", "oem":
  fsProps.Precompiled_file_contexts = proptools.StringPtr(":file_contexts_bin_gen")
 }

 fsProps.Is_auto_generated = proptools.BoolPtr(true)
 if partitionType != "system" {
  mountPoint := proptools.StringPtr(partitionType)
  // https://cs.android.com/android/platform/superproject/main/+/main:build/make/tools/releasetools/build_image.py;l=1012;drc=3f576a753594bad3fc838ccb8b1b72f7efac1d50
  if partitionType == "userdata" {
   mountPoint = proptools.StringPtr("data")
  }
  fsProps.Mount_point = mountPoint

 }

 fsProps.Enable_host_init_verifier_check = proptools.BoolPtr(false)

 partitionSpecificFsProps(ctx, partitions, fsProps, partitionType)

 return fsProps, true
}

type avbInfo struct {
 avbEnable                *bool
 avbKeyPath               *string
 avbkeyFilegroup          *string
 avbAlgorithm             *string
 avbRollbackIndex         *int64
 avbRollbackIndexLocation *int64
 avbMode                  *string
 avbHashAlgorithm         *string
}

func getAvbInfo(config android.Config, partitionType string) avbInfo {
 partitionVars := config.ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
 var result avbInfo
 boardAvbEnable := partitionVars.BoardAvbEnable
 if boardAvbEnable {
  result.avbEnable = proptools.BoolPtr(true)
  // There are "global" and "specific" copies of a lot of these variables. Sometimes they
  // choose the specific and then fall back to the global one if it's not set, other times
  // the global one actually only applies to the vbmeta partition.
  if partitionType == "vbmeta" {
   if partitionVars.BoardAvbKeyPath != "" {
    result.avbKeyPath = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
   }
   if partitionVars.BoardAvbRollbackIndex != "" {
    parsed, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 1064)
    if err != nil {
     panic(fmt.Sprintf("Rollback index must be an int, got %s", partitionVars.BoardAvbRollbackIndex))
    }
    result.avbRollbackIndex = &parsed
   }
   // The global BoardAvbAlgorithm should only apply to vbmeta.
   if partitionVars.BoardAvbAlgorithm != "" {
    result.avbAlgorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
   }
  }
  if specificPartitionVars.BoardAvbKeyPath != "" {
   result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
  }
  if specificPartitionVars.BoardAvbAlgorithm != "" {
   result.avbAlgorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
  }
  if specificPartitionVars.BoardAvbRollbackIndex != "" {
   parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 1064)
   if err != nil {
    panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
   }
   result.avbRollbackIndex = &parsed
  }
  if specificPartitionVars.BoardAvbRollbackIndexLocation != "" {
   parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndexLocation1064)
   if err != nil {
    panic(fmt.Sprintf("Rollback index location must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndexLocation))
   }
   result.avbRollbackIndexLocation = &parsed
  }

  // Make allows you to pass arbitrary arguments to avbtool via this variable, but in practice
  // it's only used for --hash_algorithm. The soong module has a dedicated property for the
  // hashtree algorithm, and doesn't allow custom arguments, so just extract the hashtree
  // algorithm out of the arbitrary arguments.
  addHashtreeFooterArgs := strings.Split(specificPartitionVars.BoardAvbAddHashtreeFooterArgs, " ")
  if i := slices.Index(addHashtreeFooterArgs, "--hash_algorithm"); i >= 0 {
   result.avbHashAlgorithm = &addHashtreeFooterArgs[i+1]
  }

  result.avbMode = proptools.StringPtr("make_legacy")
 }
 if result.avbKeyPath != nil {
  fsGenState := config.Get(fsGenStateOnceKey).(*FsGenState)
  filegroup := fsGenState.avbKeyFilegroups[*result.avbKeyPath]
  result.avbkeyFilegroup = proptools.StringPtr(":" + filegroup)
 }
 return result
}

func (f *filesystemCreator) createSoongMakeFileListDiffTest(ctx android.ModuleContext, partitionType string, partitionModuleName string) android.Path {
 fileListFile := filesystem.GetFileListFile(ctx, partitionModuleName, generatedFilesystemDepTag)
 makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
 diffTestTimestamp := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.timestamp", partitionModuleName))
 return filesystem.CreateFileListDiffTest(ctx, diffTestTimestamp, makeFileList, fileListFile, partitionModuleName)
}

func createFailingCommand(ctx android.ModuleContext, message string) android.Path {
 hasher := sha256.New()
 hasher.Write([]byte(message))
 filename := fmt.Sprintf("failing_command_%x.txt", hasher.Sum(nil))
 file := android.PathForModuleOut(ctx, filename)
 builder := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 builder.Command().Textf("echo %s", proptools.NinjaAndShellEscape(message))
 builder.Command().Text("exit 1 #").Output(file)
 builder.Build("failing command "+filename, "failing command "+filename)
 return file
}

func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
 vbmetaModule := ctx.GetDirectDepProxyWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
 outputFilesProvider := android.GetOutputFiles(ctx, vbmetaModule)
 if outputFilesProvider == nil {
  ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
 }
 if len(outputFilesProvider.DefaultOutputFiles) != 1 {
  ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
 }
 soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
 makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))

 diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
 createDiffTest(ctx, diffTestResultFile, soongVbMetaFile, makeVbmetaFile)
 return diffTestResultFile
}

func createDiffTest(ctx android.ModuleContext, diffTestResultFile android.WritablePath, file1 android.Path, file2 android.Path) {
 builder := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 builder.Command().Text("diff").
  Input(file1).
  Input(file2)
 builder.Command().Text("touch").Output(diffTestResultFile)
 builder.Build("diff test "+diffTestResultFile.String(), "diff test")
}

type imageDepTagType struct {
 blueprint.BaseDependencyTag
}

var generatedFilesystemDepTag imageDepTagType
var generatedVbmetaPartitionDepTag imageDepTagType

func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
 if !shouldEnableFilesystemCreator(ctx) {
  return
 }
 for _, name := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions.names() {
  if android.InList(ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions.typeForName(name), []string{"pvmfw", "tzsw"}) {
   ctx.AddFarVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), generatedFilesystemDepTag, name)
  } else {
   ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, name)
  }
 }
 for _, vbmetaModule := range f.properties.Vbmeta_module_names {
  ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
 }
}

func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 if !shouldEnableFilesystemCreator(ctx) {
  return
 }
 if ctx.ModuleDir() != "build/soong/fsgen" {
  ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
 }
 f.HideFromMake()

 partitions := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions

 var content strings.Builder
 generatedBp := android.PathForModuleOut(ctx, "soong_generated_product_config.bp")
 for _, partition := range partitions {
  if partition.handwritten {
   continue // no need to create a module in the autogenerated Android.bp file
  }
  content.WriteString(generateBpContent(ctx, partition.partitionType))
  content.WriteString("\n")
 }
 android.WriteFileRule(ctx, generatedBp, content.String())

 ctx.Phony("product_config_to_bp", generatedBp)

 if !ctx.Config().KatiEnabled() {
  // Cannot diff since the kati packaging rules will not be created.
  return
 }
 var diffTestFiles []android.Path
 for _, partitionType := range partitions.types() {
  if android.InList(partitionType, []string{
   "debug_ramdisk",
   "test_harness_ramdisk",
   "vendor_ramdisk-debug",
   "vendor_ramdisk-test-harness",
   "vendor_kernel_ramdisk",
   "pvmfw",
  }) {
   continue // Make packaging does not create a filter file for this partition.
  }
  diffTestFile := f.createSoongMakeFileListDiffTest(ctx, partitionType, partitions.nameForType(partitionType))
  diffTestFiles = append(diffTestFiles, diffTestFile)
  ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
 }
 for _, partitionType := range slices.Concat(partitions.unsupportedTypes(), f.properties.Unsupported_partition_types) {
  diffTestFile := createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType))
  diffTestFiles = append(diffTestFiles, diffTestFile)
  ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
 }
 for i, vbmetaModule := range f.properties.Vbmeta_module_names {
  diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
  diffTestFiles = append(diffTestFiles, diffTestFile)
  ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
 }
 if f.properties.Boot_image != "" {
  diffTestFile := android.PathForModuleOut(ctx, "boot_diff_test.txt")
  soongBootImg := android.PathForModuleSrc(ctx, f.properties.Boot_image)
  makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/boot.img", ctx.Config().DeviceName()))
  createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
  diffTestFiles = append(diffTestFiles, diffTestFile)
  ctx.Phony("soong_generated_boot_filesystem_test", diffTestFile)
 }
 if f.properties.Vendor_boot_image != "" {
  diffTestFile := android.PathForModuleOut(ctx, "vendor_boot_diff_test.txt")
  soongBootImg := android.PathForModuleSrc(ctx, f.properties.Vendor_boot_image)
  makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/vendor_boot.img", ctx.Config().DeviceName()))
  createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
  diffTestFiles = append(diffTestFiles, diffTestFile)
  ctx.Phony("soong_generated_vendor_boot_filesystem_test", diffTestFile)
 }
 if f.properties.Init_boot_image != "" {
  diffTestFile := android.PathForModuleOut(ctx, "init_boot_diff_test.txt")
  soongBootImg := android.PathForModuleSrc(ctx, f.properties.Init_boot_image)
  makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/init_boot.img", ctx.Config().DeviceName()))
  createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
  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...)
}

func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
 fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
 fsProps, fsTypeSupported := generateFsProps(ctx, fsGenState.soongGeneratedPartitions, partitionType)
 if !fsTypeSupported {
  return ""
 }
 if partitionType == "tzsw" {
  return "" // TODO: Add support for autogenerating an Android.bp file for tzsw
 }

 baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)), ctx.Config())
 deps := fsGenState.fsDeps[partitionType]
 highPriorityDeps := fsGenState.generatedPrebuiltEtcModuleNames
 var overriddenDeps []string
 if deps, ok := fsGenState.overriddenModuleNames[partitionType]; ok {
  overriddenDeps = deps
 }
 depProps := generateDepStruct(*deps, highPriorityDeps, overriddenDeps)

 result, err := proptools.RepackProperties([]interface{}{baseProps, fsProps, depProps})
 if err != nil {
  ctx.ModuleErrorf("%s", err.Error())
  return ""
 }

 moduleType := "android_filesystem"
 if partitionType == "system" {
  moduleType = "android_system_image"
 }

 file := &parser.File{
  Defs: []parser.Definition{
   &parser.Module{
    Type: moduleType,
    Map:  *result,
   },
  },
 }
 bytes, err := parser.Print(file)
 if err != nil {
  ctx.ModuleErrorf(err.Error())
 }
 return strings.TrimSpace(string(bytes))
}

[Dauer der Verarbeitung: 0.43 Sekunden, vorverarbeitet 2026-06-28]