blob: 11dfac0db89a7227d73002b35d22668aacdbb4d5 [file] [log] [blame] [edit]
// 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/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 filesystemCreatorProps struct {
Generated_partition_types []string `blueprint:"mutated"`
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"`
Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
Init_boot_image string `blueprint:"mutated" android:"path_device_first"`
Super_image string `blueprint:"mutated" android:"path_device_first"`
}
type filesystemCreator struct {
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) {
generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
avbpubkeyGenerated := createAvbpubkeyModule(ctx)
createFsGenState(ctx, generatedPrebuiltEtcModuleNames, avbpubkeyGenerated)
module.createAvbKeyFilegroups(ctx)
module.createMiscFilegroups(ctx)
module.createInternalModules(ctx)
})
return module
}
func generatedPartitions(ctx android.LoadHookContext) []string {
partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
generatedPartitions := []string{"system"}
if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
generatedPartitions = append(generatedPartitions, "system_ext")
}
if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
generatedPartitions = append(generatedPartitions, "vendor")
}
if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
generatedPartitions = append(generatedPartitions, "product")
}
if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
generatedPartitions = append(generatedPartitions, "odm")
}
if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
generatedPartitions = append(generatedPartitions, "userdata")
}
if partitionVars.BuildingSystemDlkmImage {
generatedPartitions = append(generatedPartitions, "system_dlkm")
}
if partitionVars.BuildingVendorDlkmImage {
generatedPartitions = append(generatedPartitions, "vendor_dlkm")
}
if partitionVars.BuildingOdmDlkmImage {
generatedPartitions = append(generatedPartitions, "odm_dlkm")
}
if partitionVars.BuildingRamdiskImage {
generatedPartitions = append(generatedPartitions, "ramdisk")
}
if buildingVendorBootImage(partitionVars) {
generatedPartitions = append(generatedPartitions, "vendor_ramdisk")
}
if ctx.DeviceConfig().BuildingRecoveryImage() && ctx.DeviceConfig().RecoveryPath() == "recovery" {
generatedPartitions = append(generatedPartitions, "recovery")
}
return generatedPartitions
}
func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
soongGeneratedPartitions := generatedPartitions(ctx)
finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
for _, partitionType := range soongGeneratedPartitions {
if f.createPartition(ctx, partitionType) {
f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
} else {
f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
}
}
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")
}
}
if buildingVendorBootImage(partitionVars) {
if createVendorBootImage(ctx, dtbImg) {
f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
} else {
f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_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")
}
}
for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
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)
}
if buildingSuperImage(partitionVars) {
createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
f.properties.Super_image = ":" + generatedModuleName(ctx.Config(), "super")
}
ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
}
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 (f *filesystemCreator) createDeviceModule(
ctx android.LoadHookContext,
generatedPartitionTypes []string,
vbmetaPartitions []string,
) {
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{}
if android.InList("system", generatedPartitionTypes) {
partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
}
if android.InList("system_ext", generatedPartitionTypes) {
partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
}
if android.InList("vendor", generatedPartitionTypes) {
partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
}
if android.InList("product", generatedPartitionTypes) {
partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
}
if android.InList("odm", generatedPartitionTypes) {
partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
}
if android.InList("userdata", f.properties.Generated_partition_types) {
partitionProps.Userdata_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "userdata"))
}
partitionProps.Vbmeta_partitions = vbmetaPartitions
ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
}
func partitionSpecificFsProps(ctx android.EarlyModuleContext, fsProps *filesystem.FilesystemProperties, partitionVars android.PartitionVariables, partitionType string) {
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)
// 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}"})
}
// Most of the symlinks and directories listed here originate from create_root_structure.mk,
// but the handwritten generic system image also recreates them:
// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
fsProps.Symlinks = []filesystem.SymlinkDefinition{
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/system/bin/init"),
Name: proptools.StringPtr("init"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/system/etc"),
Name: proptools.StringPtr("etc"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/system/bin"),
Name: proptools.StringPtr("bin"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
Name: proptools.StringPtr("bugreports"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/sys/kernel/debug"),
Name: proptools.StringPtr("d"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/storage/self/primary"),
Name: proptools.StringPtr("sdcard"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/product/etc/security/adb_keys"),
Name: proptools.StringPtr("adb_keys"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/app"),
Name: proptools.StringPtr("odm/app"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/bin"),
Name: proptools.StringPtr("odm/bin"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/etc"),
Name: proptools.StringPtr("odm/etc"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/firmware"),
Name: proptools.StringPtr("odm/firmware"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/framework"),
Name: proptools.StringPtr("odm/framework"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/lib"),
Name: proptools.StringPtr("odm/lib"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/lib64"),
Name: proptools.StringPtr("odm/lib64"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/overlay"),
Name: proptools.StringPtr("odm/overlay"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/priv-app"),
Name: proptools.StringPtr("odm/priv-app"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/odm/usr"),
Name: proptools.StringPtr("odm/usr"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/product"),
Name: proptools.StringPtr("system/product"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/system_ext"),
Name: proptools.StringPtr("system/system_ext"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor"),
Name: proptools.StringPtr("system/vendor"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/system_dlkm/lib/modules"),
Name: proptools.StringPtr("system/lib/modules"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/data/cache"),
Name: proptools.StringPtr("cache"),
},
// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
// both devices with and without /odm_dlkm partition. Those symlinks are for
// devices without /odm_dlkm partition. For devices with /odm_dlkm
// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
Name: proptools.StringPtr("odm_dlkm/etc"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
Name: proptools.StringPtr("vendor_dlkm/etc"),
},
}
fsProps.Base_dir = proptools.StringPtr("system")
fsProps.Dirs = proptools.NewSimpleConfigurable([]string{
// From generic_rootdirs in build/make/target/product/generic/Android.bp
"acct",
"apex",
"bootstrap-apex",
"config",
"data",
"data_mirror",
"debug_ramdisk",
"dev",
"linkerconfig",
"metadata",
"mnt",
"odm",
"odm_dlkm",
"oem",
"postinstall",
"proc",
"second_stage_resources",
"storage",
"sys",
"system",
"system_dlkm",
"tmp",
"vendor",
"vendor_dlkm",
// from android_rootdirs in build/make/target/product/generic/Android.bp
"system_ext",
"product",
})
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}"})
}
case "product":
fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
}
case "vendor":
fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
fsProps.Symlinks = []filesystem.SymlinkDefinition{
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/odm"),
Name: proptools.StringPtr("odm"),
},
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/vendor_dlkm/lib/modules"),
Name: proptools.StringPtr("lib/modules"),
},
}
fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
}
case "odm":
fsProps.Symlinks = []filesystem.SymlinkDefinition{
filesystem.SymlinkDefinition{
Target: proptools.StringPtr("/odm_dlkm/lib/modules"),
Name: proptools.StringPtr("lib/modules"),
},
}
case "userdata":
fsProps.Base_dir = proptools.StringPtr("data")
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",
})
}
case "recovery":
// Following https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2826;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
fsProps.Dirs = android.NewSimpleConfigurable([]string{
"sdcard",
"tmp",
})
fsProps.Symlinks = []filesystem.SymlinkDefinition{
{
Target: proptools.StringPtr("/system/bin/init"),
Name: proptools.StringPtr("init"),
},
{
Target: proptools.StringPtr("prop.default"),
Name: proptools.StringPtr("default.prop"),
},
}
fsProps.Base_dir = proptools.StringPtr("recovery")
}
}
var (
dlkmPartitions = []string{
"system_dlkm",
"vendor_dlkm",
"odm_dlkm",
}
)
// Creates a soong module to build the given partition. Returns false if we can't support building
// it.
func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitionType string) bool {
baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
fsProps, supported := generateFsProps(ctx, partitionType)
if !supported {
return false
}
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, append(dlkmPartitions, "vendor_ramdisk")) {
f.createPrebuiltKernelModules(ctx, partitionType)
}
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)
}
return true
}
// 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
}
}
// 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"},
},
)
}
}
// 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))
props := &struct {
Name *string
Srcs []string
System_deps []string
System_dlkm_specific *bool
Vendor_dlkm_specific *bool
Odm_dlkm_specific *bool
Vendor_ramdisk *bool
Load_by_default *bool
Blocklist_file *string
Options_file *string
}{
Name: proptools.StringPtr(name),
}
switch partitionType {
case "system_dlkm":
props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules).Strings()
props.System_dlkm_specific = proptools.BoolPtr(true)
if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.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 := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelBlocklistFile; blocklistFile != "" {
props.Blocklist_file = proptools.StringPtr(blocklistFile)
}
case "vendor_dlkm":
props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelModules).Strings()
if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules) > 0 {
props.System_deps = []string{":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules}"}
}
props.Vendor_dlkm_specific = proptools.BoolPtr(true)
if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelBlocklistFile; blocklistFile != "" {
props.Blocklist_file = proptools.StringPtr(blocklistFile)
}
case "odm_dlkm":
props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelModules).Strings()
props.Odm_dlkm_specific = proptools.BoolPtr(true)
if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelBlocklistFile; blocklistFile != "" {
props.Blocklist_file = proptools.StringPtr(blocklistFile)
}
case "vendor_ramdisk":
props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelModules).Strings()
props.Vendor_ramdisk = proptools.BoolPtr(true)
if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelBlocklistFile; blocklistFile != "" {
props.Blocklist_file = proptools.StringPtr(blocklistFile)
}
if optionsFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelOptionsFile; optionsFile != "" {
props.Options_file = proptools.StringPtr(optionsFile)
}
default:
ctx.ModuleErrorf("DLKM is not supported for %s\n", partitionType)
}
if len(props.Srcs) == 0 {
return // do not generate `prebuilt_kernel_modules` if there are no sources
}
kernelModule := ctx.CreateModuleInDirectory(
kernel.PrebuiltKernelModulesFactory,
".", // create in root directory for now
props,
)
kernelModule.HideFromMake()
// Add to deps
(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
}
// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
func (f *filesystemCreator) createVendorBuildProp(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 := &struct {
Name *string
Board_info_files []string
Bootloader_board_name *string
}{
Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "android-info.prop")),
Board_info_files: partitionVars.BoardInfoFiles,
}
if len(androidInfoProps.Board_info_files) == 0 {
androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
}
androidInfoProp := ctx.CreateModuleInDirectory(
android.AndroidInfoFactory,
".",
androidInfoProps,
)
androidInfoProp.HideFromMake()
// Create a build prop for vendor
vendorBuildProps := &struct {
Name *string
Vendor *bool
Stem *string
Product_config *string
Android_info *string
}{
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(":" + androidInfoProp.Name()),
}
vendorBuildProp := ctx.CreateModule(
android.BuildPropFactory,
vendorBuildProps,
)
vendorBuildProp.HideFromMake()
}
// 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
Visibility []string
}
func generateBaseProps(namePtr *string) *filesystemBaseProperty {
return &filesystemBaseProperty{
Name: namePtr,
Compile_multilib: proptools.StringPtr("both"),
// The vbmeta modules are currently in the root directory and depend on the partitions
Visibility: []string{"//.", "//build/soong:__subpackages__"},
}
}
func generateFsProps(ctx android.EarlyModuleContext, 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.StringPtr(fsType)
if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() {
// Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
return nil, false
}
if *fsProps.Type == "erofs" {
if partitionVars.BoardErofsCompressor != "" {
fsProps.Erofs.Compressor = proptools.StringPtr(partitionVars.BoardErofsCompressor)
}
if partitionVars.BoardErofsCompressorHints != "" {
fsProps.Erofs.Compress_hints = proptools.StringPtr(":soong_generated_board_erofs_compress_hints_filegroup")
}
}
// Don't build this module on checkbuilds, the soong-built partitions are still in-progress
// and sometimes don't build.
fsProps.Unchecked_module = proptools.BoolPtr(true)
// 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
fsProps.Avb_hash_algorithm = avbInfo.avbHashAlgorithm
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)
partitionSpecificFsProps(ctx, fsProps, partitionVars, partitionType)
return fsProps, true
}
type avbInfo struct {
avbEnable *bool
avbKeyPath *string
avbkeyFilegroup *string
avbAlgorithm *string
avbRollbackIndex *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, 10, 64)
if err != nil {
panic(fmt.Sprintf("Rollback index must be an int, got %s", partitionVars.BoardAvbRollbackIndex))
}
result.avbRollbackIndex = &parsed
}
}
if specificPartitionVars.BoardAvbKeyPath != "" {
result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
}
if specificPartitionVars.BoardAvbAlgorithm != "" {
result.avbAlgorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
} else if partitionVars.BoardAvbAlgorithm != "" {
result.avbAlgorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
}
if specificPartitionVars.BoardAvbRollbackIndex != "" {
parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
if err != nil {
panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
}
result.avbRollbackIndex = &parsed
}
if specificPartitionVars.BoardAvbRollbackIndex != "" {
parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
if err != nil {
panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
}
result.avbRollbackIndex = &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) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
if !ok {
ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
}
makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().BuiltTool("file_list_diff").
Input(makeFileList).
Input(filesystemInfo.FileListFile).
Text(partitionModuleName)
builder.Command().Text("touch").Output(diffTestResultFile)
builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
return diffTestResultFile
}
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)
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.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
if !ok {
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)
builder.Command().Text("diff").
Input(file1).
Input(file2)
builder.Command().Text("touch").Output(diffTestResultFile)
builder.Build("diff test "+diffTestResultFile.String(), "diff test")
}
type systemImageDepTagType struct {
blueprint.BaseDependencyTag
}
var generatedFilesystemDepTag systemImageDepTagType
var generatedVbmetaPartitionDepTag systemImageDepTagType
func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
for _, partitionType := range f.properties.Generated_partition_types {
ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
}
for _, vbmetaModule := range f.properties.Vbmeta_module_names {
ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
}
}
func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if ctx.ModuleDir() != "build/soong/fsgen" {
ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
}
f.HideFromMake()
var content strings.Builder
generatedBp := android.PathForModuleOut(ctx, "soong_generated_product_config.bp")
for _, partition := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions {
content.WriteString(generateBpContent(ctx, partition))
content.WriteString("\n")
}
android.WriteFileRule(ctx, generatedBp, content.String())
ctx.Phony("product_config_to_bp", generatedBp)
var diffTestFiles []android.Path
for _, partitionType := range f.properties.Generated_partition_types {
diffTestFile := f.createFileListDiffTest(ctx, partitionType)
diffTestFiles = append(diffTestFiles, diffTestFile)
ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
}
for _, partitionType := range 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 {
fsProps, fsTypeSupported := generateFsProps(ctx, partitionType)
if !fsTypeSupported {
return ""
}
baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
deps := fsGenState.fsDeps[partitionType]
highPriorityDeps := fsGenState.generatedPrebuiltEtcModuleNames
depProps := generateDepStruct(*deps, highPriorityDeps)
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))
}