| // 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" | 
 | 	"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"` | 
 | } | 
 |  | 
 | 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) | 
 | 	} | 
 |  | 
 | 	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 = []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 = []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"), | 
 | 			}, | 
 | 		} | 
 | 		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 = []string{ | 
 | 				"framework/*", | 
 | 				"framework/*/*",     // framework/{arch} | 
 | 				"framework/oat/*/*", // framework/oat/{arch} | 
 | 			} | 
 | 			fsProps.Fsverity.Libs = []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("vendor/odm"), | 
 | 			}, | 
 | 			filesystem.SymlinkDefinition{ | 
 | 				Target: proptools.StringPtr("/vendor_dlkm/lib/modules"), | 
 | 				Name:   proptools.StringPtr("vendor/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("odm/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", | 
 | 			}) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | 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.Partition_name = proptools.StringPtr(partitionType) | 
 |  | 
 | 	if !strings.Contains(partitionType, "ramdisk") { | 
 | 		fsProps.Base_dir = proptools.StringPtr(partitionType) | 
 | 	} | 
 |  | 
 | 	fsProps.Is_auto_generated = proptools.BoolPtr(true) | 
 |  | 
 | 	partitionSpecificFsProps(ctx, fsProps, partitionVars, partitionType) | 
 |  | 
 | 	// system_image properties that are not set: | 
 | 	// - filesystemProperties.Avb_hash_algorithm | 
 | 	// - filesystemProperties.File_contexts | 
 | 	// - filesystemProperties.Dirs | 
 | 	// - filesystemProperties.Symlinks | 
 | 	// - filesystemProperties.Fake_timestamp | 
 | 	// - filesystemProperties.Uuid | 
 | 	// - filesystemProperties.Mount_point | 
 | 	// - filesystemProperties.Include_make_built_files | 
 | 	// - filesystemProperties.Build_logtags | 
 | 	// - systemImageProperties.Linker_config_src | 
 |  | 
 | 	return fsProps, true | 
 | } | 
 |  | 
 | type avbInfo struct { | 
 | 	avbEnable        *bool | 
 | 	avbKeyPath       *string | 
 | 	avbkeyFilegroup  *string | 
 | 	avbAlgorithm     *string | 
 | 	avbRollbackIndex *int64 | 
 | 	avbMode          *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) | 
 | 		if specificPartitionVars.BoardAvbKeyPath != "" { | 
 | 			result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath) | 
 | 		} else if partitionVars.BoardAvbKeyPath != "" { | 
 | 			result.avbKeyPath = proptools.StringPtr(partitionVars.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 | 
 | 		} else 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 | 
 | 		} | 
 | 		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) | 
 | 	} | 
 | 	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)) | 
 | } |