xref: /aosp_15_r20/build/soong/fsgen/filesystem_creator.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package fsgen
16
17import (
18	"crypto/sha256"
19	"fmt"
20	"path/filepath"
21	"slices"
22	"strconv"
23	"strings"
24
25	"android/soong/android"
26	"android/soong/filesystem"
27	"android/soong/kernel"
28
29	"github.com/google/blueprint"
30	"github.com/google/blueprint/parser"
31	"github.com/google/blueprint/proptools"
32)
33
34var pctx = android.NewPackageContext("android/soong/fsgen")
35
36func init() {
37	registerBuildComponents(android.InitRegistrationContext)
38}
39
40func registerBuildComponents(ctx android.RegistrationContext) {
41	ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
42	ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
43}
44
45type filesystemCreatorProps struct {
46	Generated_partition_types   []string `blueprint:"mutated"`
47	Unsupported_partition_types []string `blueprint:"mutated"`
48
49	Vbmeta_module_names    []string `blueprint:"mutated"`
50	Vbmeta_partition_names []string `blueprint:"mutated"`
51
52	Boot_image        string `blueprint:"mutated" android:"path_device_first"`
53	Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
54	Init_boot_image   string `blueprint:"mutated" android:"path_device_first"`
55	Super_image       string `blueprint:"mutated" android:"path_device_first"`
56}
57
58type filesystemCreator struct {
59	android.ModuleBase
60
61	properties filesystemCreatorProps
62}
63
64func filesystemCreatorFactory() android.Module {
65	module := &filesystemCreator{}
66
67	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
68	module.AddProperties(&module.properties)
69	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
70		generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
71		avbpubkeyGenerated := createAvbpubkeyModule(ctx)
72		createFsGenState(ctx, generatedPrebuiltEtcModuleNames, avbpubkeyGenerated)
73		module.createAvbKeyFilegroups(ctx)
74		module.createMiscFilegroups(ctx)
75		module.createInternalModules(ctx)
76	})
77
78	return module
79}
80
81func generatedPartitions(ctx android.LoadHookContext) []string {
82	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
83	generatedPartitions := []string{"system"}
84	if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
85		generatedPartitions = append(generatedPartitions, "system_ext")
86	}
87	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
88		generatedPartitions = append(generatedPartitions, "vendor")
89	}
90	if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
91		generatedPartitions = append(generatedPartitions, "product")
92	}
93	if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
94		generatedPartitions = append(generatedPartitions, "odm")
95	}
96	if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
97		generatedPartitions = append(generatedPartitions, "userdata")
98	}
99	if partitionVars.BuildingSystemDlkmImage {
100		generatedPartitions = append(generatedPartitions, "system_dlkm")
101	}
102	if partitionVars.BuildingVendorDlkmImage {
103		generatedPartitions = append(generatedPartitions, "vendor_dlkm")
104	}
105	if partitionVars.BuildingOdmDlkmImage {
106		generatedPartitions = append(generatedPartitions, "odm_dlkm")
107	}
108	if partitionVars.BuildingRamdiskImage {
109		generatedPartitions = append(generatedPartitions, "ramdisk")
110	}
111	if buildingVendorBootImage(partitionVars) {
112		generatedPartitions = append(generatedPartitions, "vendor_ramdisk")
113	}
114	if ctx.DeviceConfig().BuildingRecoveryImage() && ctx.DeviceConfig().RecoveryPath() == "recovery" {
115		generatedPartitions = append(generatedPartitions, "recovery")
116	}
117	return generatedPartitions
118}
119
120func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
121	soongGeneratedPartitions := generatedPartitions(ctx)
122	finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
123	for _, partitionType := range soongGeneratedPartitions {
124		if f.createPartition(ctx, partitionType) {
125			f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
126			finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
127		} else {
128			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
129		}
130	}
131
132	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
133	dtbImg := createDtbImgFilegroup(ctx)
134
135	if buildingBootImage(partitionVars) {
136		if createBootImage(ctx, dtbImg) {
137			f.properties.Boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot")
138		} else {
139			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot")
140		}
141	}
142	if buildingVendorBootImage(partitionVars) {
143		if createVendorBootImage(ctx, dtbImg) {
144			f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
145		} else {
146			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot")
147		}
148	}
149	if buildingInitBootImage(partitionVars) {
150		if createInitBootImage(ctx) {
151			f.properties.Init_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "init_boot")
152		} else {
153			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "init_boot")
154		}
155	}
156
157	for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
158		f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
159		f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
160	}
161
162	if buildingSuperImage(partitionVars) {
163		createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
164		f.properties.Super_image = ":" + generatedModuleName(ctx.Config(), "super")
165	}
166
167	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
168	f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
169}
170
171func generatedModuleName(cfg android.Config, suffix string) string {
172	prefix := "soong"
173	if cfg.HasDeviceProduct() {
174		prefix = cfg.DeviceProduct()
175	}
176	return fmt.Sprintf("%s_generated_%s", prefix, suffix)
177}
178
179func generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
180	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
181}
182
183func (f *filesystemCreator) createDeviceModule(
184	ctx android.LoadHookContext,
185	generatedPartitionTypes []string,
186	vbmetaPartitions []string,
187) {
188	baseProps := &struct {
189		Name *string
190	}{
191		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
192	}
193
194	// Currently, only the system and system_ext partition module is created.
195	partitionProps := &filesystem.PartitionNameProperties{}
196	if android.InList("system", generatedPartitionTypes) {
197		partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
198	}
199	if android.InList("system_ext", generatedPartitionTypes) {
200		partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
201	}
202	if android.InList("vendor", generatedPartitionTypes) {
203		partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
204	}
205	if android.InList("product", generatedPartitionTypes) {
206		partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
207	}
208	if android.InList("odm", generatedPartitionTypes) {
209		partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
210	}
211	if android.InList("userdata", f.properties.Generated_partition_types) {
212		partitionProps.Userdata_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "userdata"))
213	}
214	partitionProps.Vbmeta_partitions = vbmetaPartitions
215
216	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
217}
218
219func partitionSpecificFsProps(ctx android.EarlyModuleContext, fsProps *filesystem.FilesystemProperties, partitionVars android.PartitionVariables, partitionType string) {
220	switch partitionType {
221	case "system":
222		fsProps.Build_logtags = proptools.BoolPtr(true)
223		// https://source.corp.google.com/h/googleplex-android/platform/build//639d79f5012a6542ab1f733b0697db45761ab0f3:core/packaging/flags.mk;l=21;drc=5ba8a8b77507f93aa48cc61c5ba3f31a4d0cbf37;bpv=1;bpt=0
224		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
225		// Identical to that of the aosp_shared_system_image
226		if partitionVars.ProductFsverityGenerateMetadata {
227			fsProps.Fsverity.Inputs = []string{
228				"etc/boot-image.prof",
229				"etc/dirty-image-objects",
230				"etc/preloaded-classes",
231				"etc/classpaths/*.pb",
232				"framework/*",
233				"framework/*/*",     // framework/{arch}
234				"framework/oat/*/*", // framework/oat/{arch}
235			}
236			fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
237		}
238		// Most of the symlinks and directories listed here originate from create_root_structure.mk,
239		// but the handwritten generic system image also recreates them:
240		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
241		// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
242		fsProps.Symlinks = []filesystem.SymlinkDefinition{
243			filesystem.SymlinkDefinition{
244				Target: proptools.StringPtr("/system/bin/init"),
245				Name:   proptools.StringPtr("init"),
246			},
247			filesystem.SymlinkDefinition{
248				Target: proptools.StringPtr("/system/etc"),
249				Name:   proptools.StringPtr("etc"),
250			},
251			filesystem.SymlinkDefinition{
252				Target: proptools.StringPtr("/system/bin"),
253				Name:   proptools.StringPtr("bin"),
254			},
255			filesystem.SymlinkDefinition{
256				Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
257				Name:   proptools.StringPtr("bugreports"),
258			},
259			filesystem.SymlinkDefinition{
260				Target: proptools.StringPtr("/sys/kernel/debug"),
261				Name:   proptools.StringPtr("d"),
262			},
263			filesystem.SymlinkDefinition{
264				Target: proptools.StringPtr("/storage/self/primary"),
265				Name:   proptools.StringPtr("sdcard"),
266			},
267			filesystem.SymlinkDefinition{
268				Target: proptools.StringPtr("/product/etc/security/adb_keys"),
269				Name:   proptools.StringPtr("adb_keys"),
270			},
271			filesystem.SymlinkDefinition{
272				Target: proptools.StringPtr("/vendor/odm/app"),
273				Name:   proptools.StringPtr("odm/app"),
274			},
275			filesystem.SymlinkDefinition{
276				Target: proptools.StringPtr("/vendor/odm/bin"),
277				Name:   proptools.StringPtr("odm/bin"),
278			},
279			filesystem.SymlinkDefinition{
280				Target: proptools.StringPtr("/vendor/odm/etc"),
281				Name:   proptools.StringPtr("odm/etc"),
282			},
283			filesystem.SymlinkDefinition{
284				Target: proptools.StringPtr("/vendor/odm/firmware"),
285				Name:   proptools.StringPtr("odm/firmware"),
286			},
287			filesystem.SymlinkDefinition{
288				Target: proptools.StringPtr("/vendor/odm/framework"),
289				Name:   proptools.StringPtr("odm/framework"),
290			},
291			filesystem.SymlinkDefinition{
292				Target: proptools.StringPtr("/vendor/odm/lib"),
293				Name:   proptools.StringPtr("odm/lib"),
294			},
295			filesystem.SymlinkDefinition{
296				Target: proptools.StringPtr("/vendor/odm/lib64"),
297				Name:   proptools.StringPtr("odm/lib64"),
298			},
299			filesystem.SymlinkDefinition{
300				Target: proptools.StringPtr("/vendor/odm/overlay"),
301				Name:   proptools.StringPtr("odm/overlay"),
302			},
303			filesystem.SymlinkDefinition{
304				Target: proptools.StringPtr("/vendor/odm/priv-app"),
305				Name:   proptools.StringPtr("odm/priv-app"),
306			},
307			filesystem.SymlinkDefinition{
308				Target: proptools.StringPtr("/vendor/odm/usr"),
309				Name:   proptools.StringPtr("odm/usr"),
310			},
311			filesystem.SymlinkDefinition{
312				Target: proptools.StringPtr("/product"),
313				Name:   proptools.StringPtr("system/product"),
314			},
315			filesystem.SymlinkDefinition{
316				Target: proptools.StringPtr("/system_ext"),
317				Name:   proptools.StringPtr("system/system_ext"),
318			},
319			filesystem.SymlinkDefinition{
320				Target: proptools.StringPtr("/vendor"),
321				Name:   proptools.StringPtr("system/vendor"),
322			},
323			filesystem.SymlinkDefinition{
324				Target: proptools.StringPtr("/system_dlkm/lib/modules"),
325				Name:   proptools.StringPtr("system/lib/modules"),
326			},
327			filesystem.SymlinkDefinition{
328				Target: proptools.StringPtr("/data/cache"),
329				Name:   proptools.StringPtr("cache"),
330			},
331			// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
332			// both devices with and without /odm_dlkm partition. Those symlinks are for
333			// devices without /odm_dlkm partition. For devices with /odm_dlkm
334			// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
335			// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
336			// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
337			filesystem.SymlinkDefinition{
338				Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
339				Name:   proptools.StringPtr("odm_dlkm/etc"),
340			},
341			filesystem.SymlinkDefinition{
342				Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
343				Name:   proptools.StringPtr("vendor_dlkm/etc"),
344			},
345		}
346		fsProps.Base_dir = proptools.StringPtr("system")
347		fsProps.Dirs = proptools.NewSimpleConfigurable([]string{
348			// From generic_rootdirs in build/make/target/product/generic/Android.bp
349			"acct",
350			"apex",
351			"bootstrap-apex",
352			"config",
353			"data",
354			"data_mirror",
355			"debug_ramdisk",
356			"dev",
357			"linkerconfig",
358			"metadata",
359			"mnt",
360			"odm",
361			"odm_dlkm",
362			"oem",
363			"postinstall",
364			"proc",
365			"second_stage_resources",
366			"storage",
367			"sys",
368			"system",
369			"system_dlkm",
370			"tmp",
371			"vendor",
372			"vendor_dlkm",
373
374			// from android_rootdirs in build/make/target/product/generic/Android.bp
375			"system_ext",
376			"product",
377		})
378	case "system_ext":
379		if partitionVars.ProductFsverityGenerateMetadata {
380			fsProps.Fsverity.Inputs = []string{
381				"framework/*",
382				"framework/*/*",     // framework/{arch}
383				"framework/oat/*/*", // framework/oat/{arch}
384			}
385			fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
386		}
387	case "product":
388		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
389		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
390		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
391			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
392		}
393	case "vendor":
394		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
395		fsProps.Symlinks = []filesystem.SymlinkDefinition{
396			filesystem.SymlinkDefinition{
397				Target: proptools.StringPtr("/odm"),
398				Name:   proptools.StringPtr("odm"),
399			},
400			filesystem.SymlinkDefinition{
401				Target: proptools.StringPtr("/vendor_dlkm/lib/modules"),
402				Name:   proptools.StringPtr("lib/modules"),
403			},
404		}
405		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
406		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
407			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
408		}
409	case "odm":
410		fsProps.Symlinks = []filesystem.SymlinkDefinition{
411			filesystem.SymlinkDefinition{
412				Target: proptools.StringPtr("/odm_dlkm/lib/modules"),
413				Name:   proptools.StringPtr("lib/modules"),
414			},
415		}
416	case "userdata":
417		fsProps.Base_dir = proptools.StringPtr("data")
418	case "ramdisk":
419		// Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
420		fsProps.Dirs = android.NewSimpleConfigurable([]string{
421			"debug_ramdisk",
422			"dev",
423			"metadata",
424			"mnt",
425			"proc",
426			"second_stage_resources",
427			"sys",
428		})
429		if partitionVars.BoardUsesGenericKernelImage {
430			fsProps.Dirs.AppendSimpleValue([]string{
431				"first_stage_ramdisk/debug_ramdisk",
432				"first_stage_ramdisk/dev",
433				"first_stage_ramdisk/metadata",
434				"first_stage_ramdisk/mnt",
435				"first_stage_ramdisk/proc",
436				"first_stage_ramdisk/second_stage_resources",
437				"first_stage_ramdisk/sys",
438			})
439		}
440	case "recovery":
441		// Following https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2826;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
442		fsProps.Dirs = android.NewSimpleConfigurable([]string{
443			"sdcard",
444			"tmp",
445		})
446		fsProps.Symlinks = []filesystem.SymlinkDefinition{
447			{
448				Target: proptools.StringPtr("/system/bin/init"),
449				Name:   proptools.StringPtr("init"),
450			},
451			{
452				Target: proptools.StringPtr("prop.default"),
453				Name:   proptools.StringPtr("default.prop"),
454			},
455		}
456		fsProps.Base_dir = proptools.StringPtr("recovery")
457	}
458}
459
460var (
461	dlkmPartitions = []string{
462		"system_dlkm",
463		"vendor_dlkm",
464		"odm_dlkm",
465	}
466)
467
468// Creates a soong module to build the given partition. Returns false if we can't support building
469// it.
470func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitionType string) bool {
471	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
472
473	fsProps, supported := generateFsProps(ctx, partitionType)
474	if !supported {
475		return false
476	}
477
478	if partitionType == "vendor" || partitionType == "product" || partitionType == "system" {
479		fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true)
480		if partitionType != "system" {
481			fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
482		}
483	}
484
485	if android.InList(partitionType, append(dlkmPartitions, "vendor_ramdisk")) {
486		f.createPrebuiltKernelModules(ctx, partitionType)
487	}
488
489	var module android.Module
490	if partitionType == "system" {
491		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
492	} else {
493		// Explicitly set the partition.
494		fsProps.Partition_type = proptools.StringPtr(partitionType)
495		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
496	}
497	module.HideFromMake()
498	if partitionType == "vendor" {
499		f.createVendorBuildProp(ctx)
500	}
501	return true
502}
503
504// Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
505func (f *filesystemCreator) createAvbKeyFilegroups(ctx android.LoadHookContext) {
506	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
507	var files []string
508
509	if len(partitionVars.BoardAvbKeyPath) > 0 {
510		files = append(files, partitionVars.BoardAvbKeyPath)
511	}
512	for _, partition := range android.SortedKeys(partitionVars.PartitionQualifiedVariables) {
513		specificPartitionVars := partitionVars.PartitionQualifiedVariables[partition]
514		if len(specificPartitionVars.BoardAvbKeyPath) > 0 {
515			files = append(files, specificPartitionVars.BoardAvbKeyPath)
516		}
517	}
518
519	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
520	for _, file := range files {
521		if _, ok := fsGenState.avbKeyFilegroups[file]; ok {
522			continue
523		}
524		if file == "external/avb/test/data/testkey_rsa4096.pem" {
525			// There already exists a checked-in filegroup for this commonly-used key, just use that
526			fsGenState.avbKeyFilegroups[file] = "avb_testkey_rsa4096"
527			continue
528		}
529		dir := filepath.Dir(file)
530		base := filepath.Base(file)
531		name := fmt.Sprintf("avb_key_%x", strings.ReplaceAll(file, "/", "_"))
532		ctx.CreateModuleInDirectory(
533			android.FileGroupFactory,
534			dir,
535			&struct {
536				Name       *string
537				Srcs       []string
538				Visibility []string
539			}{
540				Name:       proptools.StringPtr(name),
541				Srcs:       []string{base},
542				Visibility: []string{"//visibility:public"},
543			},
544		)
545		fsGenState.avbKeyFilegroups[file] = name
546	}
547}
548
549// Creates filegroups for miscellaneous other files
550func (f *filesystemCreator) createMiscFilegroups(ctx android.LoadHookContext) {
551	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
552
553	if partitionVars.BoardErofsCompressorHints != "" {
554		dir := filepath.Dir(partitionVars.BoardErofsCompressorHints)
555		base := filepath.Base(partitionVars.BoardErofsCompressorHints)
556		ctx.CreateModuleInDirectory(
557			android.FileGroupFactory,
558			dir,
559			&struct {
560				Name       *string
561				Srcs       []string
562				Visibility []string
563			}{
564				Name:       proptools.StringPtr("soong_generated_board_erofs_compress_hints_filegroup"),
565				Srcs:       []string{base},
566				Visibility: []string{"//visibility:public"},
567			},
568		)
569	}
570}
571
572// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
573// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency.
574// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view.
575func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string) {
576	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
577	name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType))
578	props := &struct {
579		Name                 *string
580		Srcs                 []string
581		System_deps          []string
582		System_dlkm_specific *bool
583		Vendor_dlkm_specific *bool
584		Odm_dlkm_specific    *bool
585		Vendor_ramdisk       *bool
586		Load_by_default      *bool
587		Blocklist_file       *string
588		Options_file         *string
589	}{
590		Name: proptools.StringPtr(name),
591	}
592	switch partitionType {
593	case "system_dlkm":
594		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules).Strings()
595		props.System_dlkm_specific = proptools.BoolPtr(true)
596		if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelLoadModules) == 0 {
597			// Create empty modules.load file for system
598			// https://source.corp.google.com/h/googleplex-android/platform/build/+/ef55daac9954896161b26db4f3ef1781b5a5694c:core/Makefile;l=695-700;drc=549fe2a5162548bd8b47867d35f907eb22332023;bpv=1;bpt=0
599			props.Load_by_default = proptools.BoolPtr(false)
600		}
601		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelBlocklistFile; blocklistFile != "" {
602			props.Blocklist_file = proptools.StringPtr(blocklistFile)
603		}
604	case "vendor_dlkm":
605		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelModules).Strings()
606		if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules) > 0 {
607			props.System_deps = []string{":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules}"}
608		}
609		props.Vendor_dlkm_specific = proptools.BoolPtr(true)
610		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelBlocklistFile; blocklistFile != "" {
611			props.Blocklist_file = proptools.StringPtr(blocklistFile)
612		}
613	case "odm_dlkm":
614		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelModules).Strings()
615		props.Odm_dlkm_specific = proptools.BoolPtr(true)
616		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelBlocklistFile; blocklistFile != "" {
617			props.Blocklist_file = proptools.StringPtr(blocklistFile)
618		}
619	case "vendor_ramdisk":
620		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelModules).Strings()
621		props.Vendor_ramdisk = proptools.BoolPtr(true)
622		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelBlocklistFile; blocklistFile != "" {
623			props.Blocklist_file = proptools.StringPtr(blocklistFile)
624		}
625		if optionsFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelOptionsFile; optionsFile != "" {
626			props.Options_file = proptools.StringPtr(optionsFile)
627		}
628
629	default:
630		ctx.ModuleErrorf("DLKM is not supported for %s\n", partitionType)
631	}
632
633	if len(props.Srcs) == 0 {
634		return // do not generate `prebuilt_kernel_modules` if there are no sources
635	}
636
637	kernelModule := ctx.CreateModuleInDirectory(
638		kernel.PrebuiltKernelModulesFactory,
639		".", // create in root directory for now
640		props,
641	)
642	kernelModule.HideFromMake()
643	// Add to deps
644	(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
645}
646
647// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
648func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
649	// Create a android_info for vendor
650	// The board info files might be in a directory outside the root soong namespace, so create
651	// the module in "."
652	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
653	androidInfoProps := &struct {
654		Name                  *string
655		Board_info_files      []string
656		Bootloader_board_name *string
657	}{
658		Name:             proptools.StringPtr(generatedModuleName(ctx.Config(), "android-info.prop")),
659		Board_info_files: partitionVars.BoardInfoFiles,
660	}
661	if len(androidInfoProps.Board_info_files) == 0 {
662		androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
663	}
664	androidInfoProp := ctx.CreateModuleInDirectory(
665		android.AndroidInfoFactory,
666		".",
667		androidInfoProps,
668	)
669	androidInfoProp.HideFromMake()
670	// Create a build prop for vendor
671	vendorBuildProps := &struct {
672		Name           *string
673		Vendor         *bool
674		Stem           *string
675		Product_config *string
676		Android_info   *string
677	}{
678		Name:           proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
679		Vendor:         proptools.BoolPtr(true),
680		Stem:           proptools.StringPtr("build.prop"),
681		Product_config: proptools.StringPtr(":product_config"),
682		Android_info:   proptools.StringPtr(":" + androidInfoProp.Name()),
683	}
684	vendorBuildProp := ctx.CreateModule(
685		android.BuildPropFactory,
686		vendorBuildProps,
687	)
688	vendorBuildProp.HideFromMake()
689}
690
691// createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
692// 1. vendor: Using PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS (space separated file list)
693// 1. product: Using PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS (space separated file list)
694// It creates a filegroup for each file in the fragment list
695// The filegroup modules are then added to `linker_config_srcs` of the autogenerated vendor `android_filesystem`.
696func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadHookContext, partitionType string) []string {
697	ret := []string{}
698	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
699	var linkerConfigSrcs []string
700	if partitionType == "vendor" {
701		linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.VendorLinkerConfigSrcs)
702	} else if partitionType == "product" {
703		linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.ProductLinkerConfigSrcs)
704	} else {
705		ctx.ModuleErrorf("linker.config.pb is only supported for vendor and product partitions. For system partition, use `android_system_image`")
706	}
707
708	if len(linkerConfigSrcs) > 0 {
709		// Create a filegroup, and add `:<filegroup_name>` to ret.
710		for index, linkerConfigSrc := range linkerConfigSrcs {
711			dir := filepath.Dir(linkerConfigSrc)
712			base := filepath.Base(linkerConfigSrc)
713			fgName := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-linker-config-src%s", partitionType, strconv.Itoa(index)))
714			srcs := []string{base}
715			fgProps := &struct {
716				Name *string
717				Srcs proptools.Configurable[[]string]
718			}{
719				Name: proptools.StringPtr(fgName),
720				Srcs: proptools.NewSimpleConfigurable(srcs),
721			}
722			ctx.CreateModuleInDirectory(
723				android.FileGroupFactory,
724				dir,
725				fgProps,
726			)
727			ret = append(ret, ":"+fgName)
728		}
729	}
730	return ret
731}
732
733type filesystemBaseProperty struct {
734	Name             *string
735	Compile_multilib *string
736	Visibility       []string
737}
738
739func generateBaseProps(namePtr *string) *filesystemBaseProperty {
740	return &filesystemBaseProperty{
741		Name:             namePtr,
742		Compile_multilib: proptools.StringPtr("both"),
743		// The vbmeta modules are currently in the root directory and depend on the partitions
744		Visibility: []string{"//.", "//build/soong:__subpackages__"},
745	}
746}
747
748func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*filesystem.FilesystemProperties, bool) {
749	fsProps := &filesystem.FilesystemProperties{}
750
751	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
752	var avbInfo avbInfo
753	var fsType string
754	if strings.Contains(partitionType, "ramdisk") {
755		fsType = "compressed_cpio"
756	} else {
757		specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
758		fsType = specificPartitionVars.BoardFileSystemType
759		avbInfo = getAvbInfo(ctx.Config(), partitionType)
760		if fsType == "" {
761			fsType = "ext4" //default
762		}
763	}
764
765	fsProps.Type = proptools.StringPtr(fsType)
766	if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() {
767		// Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
768		return nil, false
769	}
770
771	if *fsProps.Type == "erofs" {
772		if partitionVars.BoardErofsCompressor != "" {
773			fsProps.Erofs.Compressor = proptools.StringPtr(partitionVars.BoardErofsCompressor)
774		}
775		if partitionVars.BoardErofsCompressorHints != "" {
776			fsProps.Erofs.Compress_hints = proptools.StringPtr(":soong_generated_board_erofs_compress_hints_filegroup")
777		}
778	}
779
780	// Don't build this module on checkbuilds, the soong-built partitions are still in-progress
781	// and sometimes don't build.
782	fsProps.Unchecked_module = proptools.BoolPtr(true)
783
784	// BOARD_AVB_ENABLE
785	fsProps.Use_avb = avbInfo.avbEnable
786	// BOARD_AVB_KEY_PATH
787	fsProps.Avb_private_key = avbInfo.avbkeyFilegroup
788	// BOARD_AVB_ALGORITHM
789	fsProps.Avb_algorithm = avbInfo.avbAlgorithm
790	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX
791	fsProps.Rollback_index = avbInfo.avbRollbackIndex
792	fsProps.Avb_hash_algorithm = avbInfo.avbHashAlgorithm
793
794	fsProps.Partition_name = proptools.StringPtr(partitionType)
795
796	switch partitionType {
797	// The partitions that support file_contexts came from here:
798	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2270;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
799	case "system", "userdata", "cache", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm", "oem":
800		fsProps.Precompiled_file_contexts = proptools.StringPtr(":file_contexts_bin_gen")
801	}
802
803	fsProps.Is_auto_generated = proptools.BoolPtr(true)
804
805	partitionSpecificFsProps(ctx, fsProps, partitionVars, partitionType)
806
807	return fsProps, true
808}
809
810type avbInfo struct {
811	avbEnable        *bool
812	avbKeyPath       *string
813	avbkeyFilegroup  *string
814	avbAlgorithm     *string
815	avbRollbackIndex *int64
816	avbMode          *string
817	avbHashAlgorithm *string
818}
819
820func getAvbInfo(config android.Config, partitionType string) avbInfo {
821	partitionVars := config.ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
822	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
823	var result avbInfo
824	boardAvbEnable := partitionVars.BoardAvbEnable
825	if boardAvbEnable {
826		result.avbEnable = proptools.BoolPtr(true)
827		// There are "global" and "specific" copies of a lot of these variables. Sometimes they
828		// choose the specific and then fall back to the global one if it's not set, other times
829		// the global one actually only applies to the vbmeta partition.
830		if partitionType == "vbmeta" {
831			if partitionVars.BoardAvbKeyPath != "" {
832				result.avbKeyPath = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
833			}
834			if partitionVars.BoardAvbRollbackIndex != "" {
835				parsed, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 64)
836				if err != nil {
837					panic(fmt.Sprintf("Rollback index must be an int, got %s", partitionVars.BoardAvbRollbackIndex))
838				}
839				result.avbRollbackIndex = &parsed
840			}
841		}
842		if specificPartitionVars.BoardAvbKeyPath != "" {
843			result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
844		}
845		if specificPartitionVars.BoardAvbAlgorithm != "" {
846			result.avbAlgorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
847		} else if partitionVars.BoardAvbAlgorithm != "" {
848			result.avbAlgorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
849		}
850		if specificPartitionVars.BoardAvbRollbackIndex != "" {
851			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
852			if err != nil {
853				panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
854			}
855			result.avbRollbackIndex = &parsed
856		}
857		if specificPartitionVars.BoardAvbRollbackIndex != "" {
858			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
859			if err != nil {
860				panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
861			}
862			result.avbRollbackIndex = &parsed
863		}
864
865		// Make allows you to pass arbitrary arguments to avbtool via this variable, but in practice
866		// it's only used for --hash_algorithm. The soong module has a dedicated property for the
867		// hashtree algorithm, and doesn't allow custom arguments, so just extract the hashtree
868		// algorithm out of the arbitrary arguments.
869		addHashtreeFooterArgs := strings.Split(specificPartitionVars.BoardAvbAddHashtreeFooterArgs, " ")
870		if i := slices.Index(addHashtreeFooterArgs, "--hash_algorithm"); i >= 0 {
871			result.avbHashAlgorithm = &addHashtreeFooterArgs[i+1]
872		}
873
874		result.avbMode = proptools.StringPtr("make_legacy")
875	}
876	if result.avbKeyPath != nil {
877		fsGenState := config.Get(fsGenStateOnceKey).(*FsGenState)
878		filegroup := fsGenState.avbKeyFilegroups[*result.avbKeyPath]
879		result.avbkeyFilegroup = proptools.StringPtr(":" + filegroup)
880	}
881	return result
882}
883
884func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
885	partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
886	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
887	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
888	if !ok {
889		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
890	}
891	makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
892	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
893
894	builder := android.NewRuleBuilder(pctx, ctx)
895	builder.Command().BuiltTool("file_list_diff").
896		Input(makeFileList).
897		Input(filesystemInfo.FileListFile).
898		Text(partitionModuleName)
899	builder.Command().Text("touch").Output(diffTestResultFile)
900	builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
901	return diffTestResultFile
902}
903
904func createFailingCommand(ctx android.ModuleContext, message string) android.Path {
905	hasher := sha256.New()
906	hasher.Write([]byte(message))
907	filename := fmt.Sprintf("failing_command_%x.txt", hasher.Sum(nil))
908	file := android.PathForModuleOut(ctx, filename)
909	builder := android.NewRuleBuilder(pctx, ctx)
910	builder.Command().Textf("echo %s", proptools.NinjaAndShellEscape(message))
911	builder.Command().Text("exit 1 #").Output(file)
912	builder.Build("failing command "+filename, "failing command "+filename)
913	return file
914}
915
916func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
917	vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
918	outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
919	if !ok {
920		ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
921	}
922	if len(outputFilesProvider.DefaultOutputFiles) != 1 {
923		ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
924	}
925	soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
926	makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))
927
928	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
929	createDiffTest(ctx, diffTestResultFile, soongVbMetaFile, makeVbmetaFile)
930	return diffTestResultFile
931}
932
933func createDiffTest(ctx android.ModuleContext, diffTestResultFile android.WritablePath, file1 android.Path, file2 android.Path) {
934	builder := android.NewRuleBuilder(pctx, ctx)
935	builder.Command().Text("diff").
936		Input(file1).
937		Input(file2)
938	builder.Command().Text("touch").Output(diffTestResultFile)
939	builder.Build("diff test "+diffTestResultFile.String(), "diff test")
940}
941
942type systemImageDepTagType struct {
943	blueprint.BaseDependencyTag
944}
945
946var generatedFilesystemDepTag systemImageDepTagType
947var generatedVbmetaPartitionDepTag systemImageDepTagType
948
949func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
950	for _, partitionType := range f.properties.Generated_partition_types {
951		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
952	}
953	for _, vbmetaModule := range f.properties.Vbmeta_module_names {
954		ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
955	}
956}
957
958func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
959	if ctx.ModuleDir() != "build/soong/fsgen" {
960		ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
961	}
962	f.HideFromMake()
963
964	var content strings.Builder
965	generatedBp := android.PathForModuleOut(ctx, "soong_generated_product_config.bp")
966	for _, partition := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions {
967		content.WriteString(generateBpContent(ctx, partition))
968		content.WriteString("\n")
969	}
970	android.WriteFileRule(ctx, generatedBp, content.String())
971
972	ctx.Phony("product_config_to_bp", generatedBp)
973
974	var diffTestFiles []android.Path
975	for _, partitionType := range f.properties.Generated_partition_types {
976		diffTestFile := f.createFileListDiffTest(ctx, partitionType)
977		diffTestFiles = append(diffTestFiles, diffTestFile)
978		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
979	}
980	for _, partitionType := range f.properties.Unsupported_partition_types {
981		diffTestFile := createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType))
982		diffTestFiles = append(diffTestFiles, diffTestFile)
983		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
984	}
985	for i, vbmetaModule := range f.properties.Vbmeta_module_names {
986		diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
987		diffTestFiles = append(diffTestFiles, diffTestFile)
988		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
989	}
990	if f.properties.Boot_image != "" {
991		diffTestFile := android.PathForModuleOut(ctx, "boot_diff_test.txt")
992		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Boot_image)
993		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/boot.img", ctx.Config().DeviceName()))
994		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
995		diffTestFiles = append(diffTestFiles, diffTestFile)
996		ctx.Phony("soong_generated_boot_filesystem_test", diffTestFile)
997	}
998	if f.properties.Vendor_boot_image != "" {
999		diffTestFile := android.PathForModuleOut(ctx, "vendor_boot_diff_test.txt")
1000		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Vendor_boot_image)
1001		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/vendor_boot.img", ctx.Config().DeviceName()))
1002		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
1003		diffTestFiles = append(diffTestFiles, diffTestFile)
1004		ctx.Phony("soong_generated_vendor_boot_filesystem_test", diffTestFile)
1005	}
1006	if f.properties.Init_boot_image != "" {
1007		diffTestFile := android.PathForModuleOut(ctx, "init_boot_diff_test.txt")
1008		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Init_boot_image)
1009		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/init_boot.img", ctx.Config().DeviceName()))
1010		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
1011		diffTestFiles = append(diffTestFiles, diffTestFile)
1012		ctx.Phony("soong_generated_init_boot_filesystem_test", diffTestFile)
1013	}
1014	if f.properties.Super_image != "" {
1015		diffTestFile := android.PathForModuleOut(ctx, "super_diff_test.txt")
1016		soongSuperImg := android.PathForModuleSrc(ctx, f.properties.Super_image)
1017		makeSuperImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/super.img", ctx.Config().DeviceName()))
1018		createDiffTest(ctx, diffTestFile, soongSuperImg, makeSuperImage)
1019		diffTestFiles = append(diffTestFiles, diffTestFile)
1020		ctx.Phony("soong_generated_super_filesystem_test", diffTestFile)
1021	}
1022	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
1023}
1024
1025func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
1026	fsProps, fsTypeSupported := generateFsProps(ctx, partitionType)
1027	if !fsTypeSupported {
1028		return ""
1029	}
1030
1031	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
1032	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
1033	deps := fsGenState.fsDeps[partitionType]
1034	highPriorityDeps := fsGenState.generatedPrebuiltEtcModuleNames
1035	depProps := generateDepStruct(*deps, highPriorityDeps)
1036
1037	result, err := proptools.RepackProperties([]interface{}{baseProps, fsProps, depProps})
1038	if err != nil {
1039		ctx.ModuleErrorf("%s", err.Error())
1040		return ""
1041	}
1042
1043	moduleType := "android_filesystem"
1044	if partitionType == "system" {
1045		moduleType = "android_system_image"
1046	}
1047
1048	file := &parser.File{
1049		Defs: []parser.Definition{
1050			&parser.Module{
1051				Type: moduleType,
1052				Map:  *result,
1053			},
1054		},
1055	}
1056	bytes, err := parser.Print(file)
1057	if err != nil {
1058		ctx.ModuleErrorf(err.Error())
1059	}
1060	return strings.TrimSpace(string(bytes))
1061}
1062