• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 generatedPartitionData struct {
46	partitionType string
47	moduleName    string
48	// supported is true if the module was created successfully, false if there was some problem
49	// and the module couldn't be created.
50	supported   bool
51	handwritten bool
52}
53
54type allGeneratedPartitionData []generatedPartitionData
55
56func (d allGeneratedPartitionData) moduleNames() []string {
57	var result []string
58	for _, data := range d {
59		if data.supported {
60			result = append(result, data.moduleName)
61		}
62	}
63	return result
64}
65
66func (d allGeneratedPartitionData) types() []string {
67	var result []string
68	for _, data := range d {
69		if data.supported {
70			result = append(result, data.partitionType)
71		}
72	}
73	return result
74}
75
76func (d allGeneratedPartitionData) unsupportedTypes() []string {
77	var result []string
78	for _, data := range d {
79		if !data.supported {
80			result = append(result, data.partitionType)
81		}
82	}
83	return result
84}
85
86func (d allGeneratedPartitionData) names() []string {
87	var result []string
88	for _, data := range d {
89		if data.supported {
90			result = append(result, data.moduleName)
91		}
92	}
93	return result
94}
95
96func (d allGeneratedPartitionData) nameForType(ty string) string {
97	for _, data := range d {
98		if data.supported && data.partitionType == ty {
99			return data.moduleName
100		}
101	}
102	return ""
103}
104
105func (d allGeneratedPartitionData) typeForName(name string) string {
106	for _, data := range d {
107		if data.supported && data.moduleName == name {
108			return data.partitionType
109		}
110	}
111	return ""
112}
113
114func (d allGeneratedPartitionData) isHandwritten(name string) bool {
115	for _, data := range d {
116		if data.supported && data.moduleName == name {
117			return data.handwritten
118		}
119	}
120	return false
121}
122
123type filesystemCreatorProps struct {
124	Unsupported_partition_types []string `blueprint:"mutated"`
125
126	Vbmeta_module_names    []string `blueprint:"mutated"`
127	Vbmeta_partition_names []string `blueprint:"mutated"`
128
129	Boot_image        string `blueprint:"mutated" android:"path_device_first"`
130	Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
131	Init_boot_image   string `blueprint:"mutated" android:"path_device_first"`
132	Super_image       string `blueprint:"mutated" android:"path_device_first"`
133}
134
135type filesystemCreator struct {
136	android.ModuleBase
137
138	properties filesystemCreatorProps
139}
140
141func filesystemCreatorFactory() android.Module {
142	module := &filesystemCreator{}
143
144	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
145	module.AddProperties(&module.properties)
146	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
147		generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
148		avbpubkeyGenerated := createAvbpubkeyModule(ctx)
149		createFsGenState(ctx, generatedPrebuiltEtcModuleNames, avbpubkeyGenerated)
150		module.createAvbKeyFilegroups(ctx)
151		module.createMiscFilegroups(ctx)
152		module.createInternalModules(ctx)
153	})
154
155	return module
156}
157
158func generatedPartitions(ctx android.EarlyModuleContext) allGeneratedPartitionData {
159	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
160
161	var result allGeneratedPartitionData
162	addGenerated := func(ty string) {
163		result = append(result, generatedPartitionData{
164			partitionType: ty,
165			moduleName:    generatedModuleNameForPartition(ctx.Config(), ty),
166			supported:     true,
167		})
168	}
169
170	if ctx.Config().UseSoongSystemImage() {
171		if ctx.Config().SoongDefinedSystemImage() == "" {
172			panic("PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true")
173		}
174		result = append(result, generatedPartitionData{
175			partitionType: "system",
176			moduleName:    ctx.Config().SoongDefinedSystemImage(),
177			supported:     true,
178			handwritten:   true,
179		})
180	} else {
181		addGenerated("system")
182	}
183	if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
184		addGenerated("system_ext")
185	}
186	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
187		addGenerated("vendor")
188	}
189	if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
190		addGenerated("product")
191	}
192	if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
193		addGenerated("odm")
194	}
195	if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
196		addGenerated("userdata")
197	}
198	if partitionVars.BuildingSystemDlkmImage {
199		addGenerated("system_dlkm")
200	}
201	if partitionVars.BuildingVendorDlkmImage {
202		addGenerated("vendor_dlkm")
203	}
204	if partitionVars.BuildingOdmDlkmImage {
205		addGenerated("odm_dlkm")
206	}
207	if partitionVars.BuildingRamdiskImage {
208		addGenerated("ramdisk")
209	}
210	if buildingVendorBootImage(partitionVars) {
211		addGenerated("vendor_ramdisk")
212	}
213	if ctx.DeviceConfig().BuildingRecoveryImage() && ctx.DeviceConfig().RecoveryPath() == "recovery" {
214		addGenerated("recovery")
215	}
216	return result
217}
218
219func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
220	partitions := generatedPartitions(ctx)
221	for i := range partitions {
222		f.createPartition(ctx, partitions, &partitions[i])
223	}
224	// Create android_info.prop
225	f.createAndroidInfo(ctx)
226
227	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
228	dtbImg := createDtbImgFilegroup(ctx)
229
230	if buildingBootImage(partitionVars) {
231		if createBootImage(ctx, dtbImg) {
232			f.properties.Boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot")
233		} else {
234			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot")
235		}
236	}
237	if buildingVendorBootImage(partitionVars) {
238		if createVendorBootImage(ctx, dtbImg) {
239			f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
240		} else {
241			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot")
242		}
243	}
244	if buildingInitBootImage(partitionVars) {
245		if createInitBootImage(ctx) {
246			f.properties.Init_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "init_boot")
247		} else {
248			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "init_boot")
249		}
250	}
251
252	var systemOtherImageName string
253	if buildingSystemOtherImage(partitionVars) {
254		systemModule := partitions.nameForType("system")
255		systemOtherImageName = generatedModuleNameForPartition(ctx.Config(), "system_other")
256		ctx.CreateModule(
257			filesystem.SystemOtherImageFactory,
258			&filesystem.SystemOtherImageProperties{
259				System_image:                    &systemModule,
260				Preinstall_dexpreopt_files_from: partitions.moduleNames(),
261			},
262			&struct {
263				Name *string
264			}{
265				Name: proptools.StringPtr(systemOtherImageName),
266			},
267		)
268	}
269
270	for _, x := range f.createVbmetaPartitions(ctx, partitions) {
271		f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
272		f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
273	}
274
275	var superImageSubpartitions []string
276	if buildingSuperImage(partitionVars) {
277		superImageSubpartitions = createSuperImage(ctx, partitions, partitionVars, systemOtherImageName)
278		f.properties.Super_image = ":" + generatedModuleNameForPartition(ctx.Config(), "super")
279	}
280
281	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = partitions
282	f.createDeviceModule(ctx, partitions, f.properties.Vbmeta_module_names, superImageSubpartitions)
283}
284
285func generatedModuleName(cfg android.Config, suffix string) string {
286	prefix := "soong"
287	if cfg.HasDeviceProduct() {
288		prefix = cfg.DeviceProduct()
289	}
290	return fmt.Sprintf("%s_generated_%s", prefix, suffix)
291}
292
293func generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
294	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
295}
296
297func buildingSystemOtherImage(partitionVars android.PartitionVariables) bool {
298	// TODO: Recreate this logic from make instead of just depending on the final result variable:
299	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=429;drc=15a0df840e7093f65518003ab80cf24a3d9e8e6a
300	return partitionVars.BuildingSystemOtherImage
301}
302
303func (f *filesystemCreator) createBootloaderFilegroup(ctx android.LoadHookContext) (string, bool) {
304	bootloaderPath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.PrebuiltBootloader
305	if len(bootloaderPath) == 0 {
306		return "", false
307	}
308
309	bootloaderFilegroupName := generatedModuleName(ctx.Config(), "bootloader")
310	filegroupProps := &struct {
311		Name       *string
312		Srcs       []string
313		Visibility []string
314	}{
315		Name:       proptools.StringPtr(bootloaderFilegroupName),
316		Srcs:       []string{bootloaderPath},
317		Visibility: []string{"//visibility:public"},
318	}
319	ctx.CreateModuleInDirectory(android.FileGroupFactory, ".", filegroupProps)
320	return bootloaderFilegroupName, true
321}
322
323func (f *filesystemCreator) createReleaseToolsFilegroup(ctx android.LoadHookContext) (string, bool) {
324	releaseToolsDir := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ReleaseToolsExtensionDir
325	if releaseToolsDir == "" {
326		return "", false
327	}
328
329	releaseToolsFilegroupName := generatedModuleName(ctx.Config(), "releasetools")
330	filegroupProps := &struct {
331		Name       *string
332		Srcs       []string
333		Visibility []string
334	}{
335		Name:       proptools.StringPtr(releaseToolsFilegroupName),
336		Srcs:       []string{"releasetools.py"},
337		Visibility: []string{"//visibility:public"},
338	}
339	ctx.CreateModuleInDirectory(android.FileGroupFactory, releaseToolsDir, filegroupProps)
340	return releaseToolsFilegroupName, true
341}
342
343func (f *filesystemCreator) createFastbootInfoFilegroup(ctx android.LoadHookContext) (string, bool) {
344	fastbootInfoFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardFastbootInfoFile
345	if fastbootInfoFile == "" {
346		return "", false
347	}
348
349	fastbootInfoFilegroupName := generatedModuleName(ctx.Config(), "fastboot")
350	filegroupProps := &struct {
351		Name       *string
352		Srcs       []string
353		Visibility []string
354	}{
355		Name:       proptools.StringPtr(fastbootInfoFilegroupName),
356		Srcs:       []string{fastbootInfoFile},
357		Visibility: []string{"//visibility:public"},
358	}
359	ctx.CreateModuleInDirectory(android.FileGroupFactory, ".", filegroupProps)
360	return fastbootInfoFilegroupName, true
361}
362
363func (f *filesystemCreator) createDeviceModule(
364	ctx android.LoadHookContext,
365	partitions allGeneratedPartitionData,
366	vbmetaPartitions []string,
367	superImageSubPartitions []string,
368) {
369	baseProps := &struct {
370		Name *string
371	}{
372		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
373	}
374
375	// Currently, only the system and system_ext partition module is created.
376	partitionProps := &filesystem.PartitionNameProperties{}
377	if f.properties.Super_image != "" {
378		partitionProps.Super_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super"))
379	}
380	if modName := partitions.nameForType("system"); modName != "" && !android.InList("system", superImageSubPartitions) {
381		partitionProps.System_partition_name = proptools.StringPtr(modName)
382	}
383	if modName := partitions.nameForType("system_ext"); modName != "" && !android.InList("system_ext", superImageSubPartitions) {
384		partitionProps.System_ext_partition_name = proptools.StringPtr(modName)
385	}
386	if modName := partitions.nameForType("vendor"); modName != "" && !android.InList("vendor", superImageSubPartitions) {
387		partitionProps.Vendor_partition_name = proptools.StringPtr(modName)
388	}
389	if modName := partitions.nameForType("product"); modName != "" && !android.InList("product", superImageSubPartitions) {
390		partitionProps.Product_partition_name = proptools.StringPtr(modName)
391	}
392	if modName := partitions.nameForType("odm"); modName != "" && !android.InList("odm", superImageSubPartitions) {
393		partitionProps.Odm_partition_name = proptools.StringPtr(modName)
394	}
395	if modName := partitions.nameForType("userdata"); modName != "" {
396		partitionProps.Userdata_partition_name = proptools.StringPtr(modName)
397	}
398	if modName := partitions.nameForType("recovery"); modName != "" && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
399		partitionProps.Recovery_partition_name = proptools.StringPtr(modName)
400	}
401	if modName := partitions.nameForType("system_dlkm"); modName != "" && !android.InList("system_dlkm", superImageSubPartitions) {
402		partitionProps.System_dlkm_partition_name = proptools.StringPtr(modName)
403	}
404	if modName := partitions.nameForType("vendor_dlkm"); modName != "" && !android.InList("vendor_dlkm", superImageSubPartitions) {
405		partitionProps.Vendor_dlkm_partition_name = proptools.StringPtr(modName)
406	}
407	if modName := partitions.nameForType("odm_dlkm"); modName != "" && !android.InList("odm_dlkm", superImageSubPartitions) {
408		partitionProps.Odm_dlkm_partition_name = proptools.StringPtr(modName)
409	}
410	if f.properties.Boot_image != "" {
411		partitionProps.Boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "boot"))
412	}
413	if f.properties.Vendor_boot_image != "" {
414		partitionProps.Vendor_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_boot"))
415	}
416	if f.properties.Init_boot_image != "" {
417		partitionProps.Init_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "init_boot"))
418	}
419	partitionProps.Vbmeta_partitions = vbmetaPartitions
420
421	deviceProps := &filesystem.DeviceProperties{
422		Main_device:                   proptools.BoolPtr(true),
423		Ab_ota_updater:                proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaUpdater),
424		Ab_ota_partitions:             ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPartitions,
425		Ab_ota_postinstall_config:     ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPostInstallConfig,
426		Ramdisk_node_list:             proptools.StringPtr(":ramdisk_node_list"),
427		Android_info:                  proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop{.txt}")),
428		Kernel_version:                ctx.Config().ProductVariables().BoardKernelVersion,
429		Partial_ota_update_partitions: ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardPartialOtaUpdatePartitionsList,
430		Flash_block_size:              proptools.StringPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardFlashBlockSize),
431		Bootloader_in_update_package:  proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BootloaderInUpdatePackage),
432	}
433
434	if bootloader, ok := f.createBootloaderFilegroup(ctx); ok {
435		deviceProps.Bootloader = proptools.StringPtr(":" + bootloader)
436	}
437	if releaseTools, ok := f.createReleaseToolsFilegroup(ctx); ok {
438		deviceProps.Releasetools_extension = proptools.StringPtr(":" + releaseTools)
439	}
440	if fastbootInfo, ok := f.createFastbootInfoFilegroup(ctx); ok {
441		deviceProps.FastbootInfo = proptools.StringPtr(":" + fastbootInfo)
442	}
443
444	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps, deviceProps)
445}
446
447func partitionSpecificFsProps(ctx android.EarlyModuleContext, partitions allGeneratedPartitionData, fsProps *filesystem.FilesystemProperties, partitionType string) {
448	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
449	switch partitionType {
450	case "system":
451		fsProps.Build_logtags = proptools.BoolPtr(true)
452		// https://source.corp.google.com/h/googleplex-android/platform/build//639d79f5012a6542ab1f733b0697db45761ab0f3:core/packaging/flags.mk;l=21;drc=5ba8a8b77507f93aa48cc61c5ba3f31a4d0cbf37;bpv=1;bpt=0
453		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
454		// Identical to that of the aosp_shared_system_image
455		if partitionVars.ProductFsverityGenerateMetadata {
456			fsProps.Fsverity.Inputs = proptools.NewSimpleConfigurable([]string{
457				"etc/boot-image.prof",
458				"etc/dirty-image-objects",
459				"etc/preloaded-classes",
460				"etc/classpaths/*.pb",
461				"framework/*",
462				"framework/*/*",     // framework/{arch}
463				"framework/oat/*/*", // framework/oat/{arch}
464			})
465			fsProps.Fsverity.Libs = proptools.NewSimpleConfigurable([]string{":framework-res{.export-package.apk}"})
466		}
467		fsProps.Symlinks = commonSymlinksFromRoot
468		fsProps.Symlinks = append(fsProps.Symlinks,
469			[]filesystem.SymlinkDefinition{
470				{
471					Target: proptools.StringPtr("/data/cache"),
472					Name:   proptools.StringPtr("cache"),
473				},
474				{
475					Target: proptools.StringPtr("/storage/self/primary"),
476					Name:   proptools.StringPtr("sdcard"),
477				},
478				{
479					Target: proptools.StringPtr("/system_dlkm/lib/modules"),
480					Name:   proptools.StringPtr("system/lib/modules"),
481				},
482				{
483					Target: proptools.StringPtr("/product"),
484					Name:   proptools.StringPtr("system/product"),
485				},
486				{
487					Target: proptools.StringPtr("/system_ext"),
488					Name:   proptools.StringPtr("system/system_ext"),
489				},
490				{
491					Target: proptools.StringPtr("/vendor"),
492					Name:   proptools.StringPtr("system/vendor"),
493				},
494			}...,
495		)
496		fsProps.Base_dir = proptools.StringPtr("system")
497		fsProps.Dirs = proptools.NewSimpleConfigurable(commonPartitionDirs)
498		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
499		fsProps.Stem = proptools.StringPtr("system.img")
500	case "system_ext":
501		if partitionVars.ProductFsverityGenerateMetadata {
502			fsProps.Fsverity.Inputs = proptools.NewSimpleConfigurable([]string{
503				"framework/*",
504				"framework/*/*",     // framework/{arch}
505				"framework/oat/*/*", // framework/oat/{arch}
506			})
507			fsProps.Fsverity.Libs = proptools.NewSimpleConfigurable([]string{":framework-res{.export-package.apk}"})
508		}
509		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
510		fsProps.Stem = proptools.StringPtr("system_ext.img")
511		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
512	case "product":
513		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
514		fsProps.Android_filesystem_deps.System = proptools.StringPtr(partitions.nameForType("system"))
515		if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
516			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(systemExtName)
517		}
518		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
519		fsProps.Stem = proptools.StringPtr("product.img")
520	case "vendor":
521		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
522		fsProps.Symlinks = []filesystem.SymlinkDefinition{
523			filesystem.SymlinkDefinition{
524				Target: proptools.StringPtr("/odm"),
525				Name:   proptools.StringPtr("odm"),
526			},
527			filesystem.SymlinkDefinition{
528				Target: proptools.StringPtr("/vendor_dlkm/lib/modules"),
529				Name:   proptools.StringPtr("lib/modules"),
530			},
531		}
532		fsProps.Android_filesystem_deps.System = proptools.StringPtr(partitions.nameForType("system"))
533		if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
534			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(systemExtName)
535		}
536		fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorSecurityPatch)
537		fsProps.Stem = proptools.StringPtr("vendor.img")
538	case "odm":
539		fsProps.Symlinks = []filesystem.SymlinkDefinition{
540			filesystem.SymlinkDefinition{
541				Target: proptools.StringPtr("/odm_dlkm/lib/modules"),
542				Name:   proptools.StringPtr("lib/modules"),
543			},
544		}
545		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmSecurityPatch)
546		fsProps.Stem = proptools.StringPtr("odm.img")
547	case "userdata":
548		fsProps.Stem = proptools.StringPtr("userdata.img")
549		if vars, ok := partitionVars.PartitionQualifiedVariables["userdata"]; ok {
550			parsed, err := strconv.ParseInt(vars.BoardPartitionSize, 10, 64)
551			if err != nil {
552				panic(fmt.Sprintf("Partition size must be an int, got %s", vars.BoardPartitionSize))
553			}
554			fsProps.Partition_size = &parsed
555			// Disable avb for userdata partition
556			fsProps.Use_avb = nil
557		}
558		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2265;drc=7f50a123045520f2c5e18e9eb4e83f92244a1459
559		if s, err := strconv.ParseBool(partitionVars.ProductFsCasefold); err == nil {
560			fsProps.Support_casefolding = proptools.BoolPtr(s)
561		} else if len(partitionVars.ProductFsCasefold) > 0 {
562			ctx.ModuleErrorf("Unrecognized PRODUCT_FS_CASEFOLD value %s", partitionVars.ProductFsCasefold)
563		}
564		if s, err := strconv.ParseBool(partitionVars.ProductQuotaProjid); err == nil {
565			fsProps.Support_project_quota = proptools.BoolPtr(s)
566		} else if len(partitionVars.ProductQuotaProjid) > 0 {
567			ctx.ModuleErrorf("Unrecognized PRODUCT_QUOTA_PROJID value %s", partitionVars.ProductQuotaProjid)
568		}
569		if s, err := strconv.ParseBool(partitionVars.ProductFsCompression); err == nil {
570			fsProps.Enable_compression = proptools.BoolPtr(s)
571		} else if len(partitionVars.ProductFsCompression) > 0 {
572			ctx.ModuleErrorf("Unrecognized PRODUCT_FS_COMPRESSION value %s", partitionVars.ProductFsCompression)
573		}
574
575	case "ramdisk":
576		// Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
577		fsProps.Dirs = android.NewSimpleConfigurable([]string{
578			"debug_ramdisk",
579			"dev",
580			"metadata",
581			"mnt",
582			"proc",
583			"second_stage_resources",
584			"sys",
585		})
586		if partitionVars.BoardUsesGenericKernelImage {
587			fsProps.Dirs.AppendSimpleValue([]string{
588				"first_stage_ramdisk/debug_ramdisk",
589				"first_stage_ramdisk/dev",
590				"first_stage_ramdisk/metadata",
591				"first_stage_ramdisk/mnt",
592				"first_stage_ramdisk/proc",
593				"first_stage_ramdisk/second_stage_resources",
594				"first_stage_ramdisk/sys",
595			})
596		}
597		fsProps.Stem = proptools.StringPtr("ramdisk.img")
598	case "recovery":
599		dirs := append(commonPartitionDirs, []string{
600			"sdcard",
601		}...)
602
603		dirsWithRoot := make([]string, len(dirs))
604		for i, dir := range dirs {
605			dirsWithRoot[i] = filepath.Join("root", dir)
606		}
607
608		fsProps.Dirs = proptools.NewSimpleConfigurable(dirsWithRoot)
609		fsProps.Symlinks = symlinksWithNamePrefix(append(commonSymlinksFromRoot, filesystem.SymlinkDefinition{
610			Target: proptools.StringPtr("prop.default"),
611			Name:   proptools.StringPtr("default.prop"),
612		}), "root")
613		fsProps.Stem = proptools.StringPtr("recovery.img")
614	case "system_dlkm":
615		fsProps.Security_patch = proptools.StringPtr(partitionVars.SystemDlkmSecurityPatch)
616		fsProps.Stem = proptools.StringPtr("system_dlkm.img")
617	case "vendor_dlkm":
618		fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorDlkmSecurityPatch)
619		fsProps.Stem = proptools.StringPtr("vendor_dlkm.img")
620	case "odm_dlkm":
621		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmDlkmSecurityPatch)
622		fsProps.Stem = proptools.StringPtr("odm_dlkm.img")
623	case "vendor_ramdisk":
624		if recoveryName := partitions.nameForType("recovery"); recoveryName != "" {
625			fsProps.Include_files_of = []string{recoveryName}
626		}
627		fsProps.Stem = proptools.StringPtr("vendor_ramdisk.img")
628	}
629}
630
631var (
632	dlkmPartitions = []string{
633		"system_dlkm",
634		"vendor_dlkm",
635		"odm_dlkm",
636	}
637)
638
639// Creates a soong module to build the given partition.
640func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitions allGeneratedPartitionData, partition *generatedPartitionData) {
641	// Nextgen team's handwritten soong system image, don't need to create anything ourselves
642	if partition.partitionType == "system" && ctx.Config().UseSoongSystemImage() {
643		return
644	}
645
646	baseProps := generateBaseProps(proptools.StringPtr(partition.moduleName))
647
648	fsProps, supported := generateFsProps(ctx, partitions, partition.partitionType)
649	if !supported {
650		partition.supported = false
651		return
652	}
653
654	partitionType := partition.partitionType
655	if partitionType == "vendor" || partitionType == "product" || partitionType == "system" {
656		fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true)
657		if partitionType != "system" {
658			fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
659		}
660	}
661
662	if android.InList(partitionType, append(dlkmPartitions, "vendor_ramdisk")) {
663		f.createPrebuiltKernelModules(ctx, partitionType)
664	}
665
666	var module android.Module
667	if partitionType == "system" {
668		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
669	} else {
670		// Explicitly set the partition.
671		fsProps.Partition_type = proptools.StringPtr(partitionType)
672		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
673	}
674	module.HideFromMake()
675	if partitionType == "vendor" {
676		f.createVendorBuildProp(ctx)
677	}
678}
679
680// Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
681func (f *filesystemCreator) createAvbKeyFilegroups(ctx android.LoadHookContext) {
682	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
683	var files []string
684
685	if len(partitionVars.BoardAvbKeyPath) > 0 {
686		files = append(files, partitionVars.BoardAvbKeyPath)
687	}
688	for _, partition := range android.SortedKeys(partitionVars.PartitionQualifiedVariables) {
689		specificPartitionVars := partitionVars.PartitionQualifiedVariables[partition]
690		if len(specificPartitionVars.BoardAvbKeyPath) > 0 {
691			files = append(files, specificPartitionVars.BoardAvbKeyPath)
692		}
693	}
694
695	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
696	for _, file := range files {
697		if _, ok := fsGenState.avbKeyFilegroups[file]; ok {
698			continue
699		}
700		if file == "external/avb/test/data/testkey_rsa4096.pem" {
701			// There already exists a checked-in filegroup for this commonly-used key, just use that
702			fsGenState.avbKeyFilegroups[file] = "avb_testkey_rsa4096"
703			continue
704		}
705		dir := filepath.Dir(file)
706		base := filepath.Base(file)
707		name := fmt.Sprintf("avb_key_%x", strings.ReplaceAll(file, "/", "_"))
708		ctx.CreateModuleInDirectory(
709			android.FileGroupFactory,
710			dir,
711			&struct {
712				Name       *string
713				Srcs       []string
714				Visibility []string
715			}{
716				Name:       proptools.StringPtr(name),
717				Srcs:       []string{base},
718				Visibility: []string{"//visibility:public"},
719			},
720		)
721		fsGenState.avbKeyFilegroups[file] = name
722	}
723}
724
725// Creates filegroups for miscellaneous other files
726func (f *filesystemCreator) createMiscFilegroups(ctx android.LoadHookContext) {
727	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
728
729	if partitionVars.BoardErofsCompressorHints != "" {
730		dir := filepath.Dir(partitionVars.BoardErofsCompressorHints)
731		base := filepath.Base(partitionVars.BoardErofsCompressorHints)
732		ctx.CreateModuleInDirectory(
733			android.FileGroupFactory,
734			dir,
735			&struct {
736				Name       *string
737				Srcs       []string
738				Visibility []string
739			}{
740				Name:       proptools.StringPtr("soong_generated_board_erofs_compress_hints_filegroup"),
741				Srcs:       []string{base},
742				Visibility: []string{"//visibility:public"},
743			},
744		)
745	}
746}
747
748// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
749// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency.
750// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view.
751func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string) {
752	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
753	name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType))
754	props := &struct {
755		Name                 *string
756		Srcs                 []string
757		System_deps          []string
758		System_dlkm_specific *bool
759		Vendor_dlkm_specific *bool
760		Odm_dlkm_specific    *bool
761		Vendor_ramdisk       *bool
762		Load_by_default      *bool
763		Blocklist_file       *string
764		Options_file         *string
765		Strip_debug_symbols  *bool
766	}{
767		Name:                proptools.StringPtr(name),
768		Strip_debug_symbols: proptools.BoolPtr(false),
769	}
770	switch partitionType {
771	case "system_dlkm":
772		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules).Strings()
773		props.System_dlkm_specific = proptools.BoolPtr(true)
774		if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelLoadModules) == 0 {
775			// Create empty modules.load file for system
776			// https://source.corp.google.com/h/googleplex-android/platform/build/+/ef55daac9954896161b26db4f3ef1781b5a5694c:core/Makefile;l=695-700;drc=549fe2a5162548bd8b47867d35f907eb22332023;bpv=1;bpt=0
777			props.Load_by_default = proptools.BoolPtr(false)
778		}
779		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelBlocklistFile; blocklistFile != "" {
780			props.Blocklist_file = proptools.StringPtr(blocklistFile)
781		}
782	case "vendor_dlkm":
783		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelModules).Strings()
784		if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules) > 0 {
785			props.System_deps = []string{":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules}"}
786		}
787		props.Vendor_dlkm_specific = proptools.BoolPtr(true)
788		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelBlocklistFile; blocklistFile != "" {
789			props.Blocklist_file = proptools.StringPtr(blocklistFile)
790		}
791	case "odm_dlkm":
792		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelModules).Strings()
793		props.Odm_dlkm_specific = proptools.BoolPtr(true)
794		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelBlocklistFile; blocklistFile != "" {
795			props.Blocklist_file = proptools.StringPtr(blocklistFile)
796		}
797	case "vendor_ramdisk":
798		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelModules).Strings()
799		props.Vendor_ramdisk = proptools.BoolPtr(true)
800		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelBlocklistFile; blocklistFile != "" {
801			props.Blocklist_file = proptools.StringPtr(blocklistFile)
802		}
803		if optionsFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelOptionsFile; optionsFile != "" {
804			props.Options_file = proptools.StringPtr(optionsFile)
805		}
806
807	default:
808		ctx.ModuleErrorf("DLKM is not supported for %s\n", partitionType)
809	}
810
811	if len(props.Srcs) == 0 {
812		return // do not generate `prebuilt_kernel_modules` if there are no sources
813	}
814
815	kernelModule := ctx.CreateModuleInDirectory(
816		kernel.PrebuiltKernelModulesFactory,
817		".", // create in root directory for now
818		props,
819	)
820	kernelModule.HideFromMake()
821	// Add to deps
822	(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
823}
824
825// Create an android_info module. This will be used to create /vendor/build.prop
826func (f *filesystemCreator) createAndroidInfo(ctx android.LoadHookContext) {
827	// Create a android_info for vendor
828	// The board info files might be in a directory outside the root soong namespace, so create
829	// the module in "."
830	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
831	androidInfoProps := &struct {
832		Name                  *string
833		Board_info_files      []string
834		Bootloader_board_name *string
835		Stem                  *string
836	}{
837		Name:             proptools.StringPtr(generatedModuleName(ctx.Config(), "android_info.prop")),
838		Board_info_files: partitionVars.BoardInfoFiles,
839		Stem:             proptools.StringPtr("android-info.txt"),
840	}
841	if len(androidInfoProps.Board_info_files) == 0 {
842		androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
843	}
844	androidInfoProp := ctx.CreateModuleInDirectory(
845		android.AndroidInfoFactory,
846		".",
847		androidInfoProps,
848	)
849	androidInfoProp.HideFromMake()
850}
851
852func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
853	vendorBuildProps := &struct {
854		Name           *string
855		Vendor         *bool
856		Stem           *string
857		Product_config *string
858		Android_info   *string
859		Licenses       []string
860		Dist           android.Dist
861	}{
862		Name:           proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
863		Vendor:         proptools.BoolPtr(true),
864		Stem:           proptools.StringPtr("build.prop"),
865		Product_config: proptools.StringPtr(":product_config"),
866		Android_info:   proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop")),
867		Dist: android.Dist{
868			Targets: []string{"droidcore-unbundled"},
869			Dest:    proptools.StringPtr("build.prop-vendor"),
870		},
871		Licenses: []string{"Android-Apache-2.0"},
872	}
873	vendorBuildProp := ctx.CreateModule(
874		android.BuildPropFactory,
875		vendorBuildProps,
876	)
877	// We don't want this to conflict with the make-built vendor build.prop, but unfortunately
878	// calling HideFromMake() prevents disting files, even in soong-only mode. So only call
879	// HideFromMake() on soong+make builds.
880	if ctx.Config().KatiEnabled() {
881		vendorBuildProp.HideFromMake()
882	}
883}
884
885func createRecoveryBuildProp(ctx android.LoadHookContext) string {
886	moduleName := generatedModuleName(ctx.Config(), "recovery-prop.default")
887
888	var vendorBuildProp *string
889	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
890		vendorBuildProp = proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "vendor-build.prop"))
891	}
892
893	recoveryBuildProps := &struct {
894		Name                  *string
895		System_build_prop     *string
896		Vendor_build_prop     *string
897		Odm_build_prop        *string
898		Product_build_prop    *string
899		System_ext_build_prop *string
900
901		Recovery        *bool
902		No_full_install *bool
903		Visibility      []string
904	}{
905		Name:                  proptools.StringPtr(moduleName),
906		System_build_prop:     proptools.StringPtr(":system-build.prop"),
907		Vendor_build_prop:     vendorBuildProp,
908		Odm_build_prop:        proptools.StringPtr(":odm-build.prop"),
909		Product_build_prop:    proptools.StringPtr(":product-build.prop"),
910		System_ext_build_prop: proptools.StringPtr(":system_ext-build.prop"),
911
912		Recovery:        proptools.BoolPtr(true),
913		No_full_install: proptools.BoolPtr(true),
914		Visibility:      []string{"//visibility:public"},
915	}
916
917	ctx.CreateModule(android.RecoveryBuildPropModuleFactory, recoveryBuildProps)
918
919	return moduleName
920}
921
922// createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
923// 1. vendor: Using PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS (space separated file list)
924// 1. product: Using PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS (space separated file list)
925// It creates a filegroup for each file in the fragment list
926// The filegroup modules are then added to `linker_config_srcs` of the autogenerated vendor `android_filesystem`.
927func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadHookContext, partitionType string) []string {
928	ret := []string{}
929	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
930	var linkerConfigSrcs []string
931	if partitionType == "vendor" {
932		linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.VendorLinkerConfigSrcs)
933	} else if partitionType == "product" {
934		linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.ProductLinkerConfigSrcs)
935	} else {
936		ctx.ModuleErrorf("linker.config.pb is only supported for vendor and product partitions. For system partition, use `android_system_image`")
937	}
938
939	if len(linkerConfigSrcs) > 0 {
940		// Create a filegroup, and add `:<filegroup_name>` to ret.
941		for index, linkerConfigSrc := range linkerConfigSrcs {
942			dir := filepath.Dir(linkerConfigSrc)
943			base := filepath.Base(linkerConfigSrc)
944			fgName := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-linker-config-src%s", partitionType, strconv.Itoa(index)))
945			srcs := []string{base}
946			fgProps := &struct {
947				Name *string
948				Srcs proptools.Configurable[[]string]
949			}{
950				Name: proptools.StringPtr(fgName),
951				Srcs: proptools.NewSimpleConfigurable(srcs),
952			}
953			ctx.CreateModuleInDirectory(
954				android.FileGroupFactory,
955				dir,
956				fgProps,
957			)
958			ret = append(ret, ":"+fgName)
959		}
960	}
961	return ret
962}
963
964type filesystemBaseProperty struct {
965	Name             *string
966	Compile_multilib *string
967	Visibility       []string
968}
969
970func generateBaseProps(namePtr *string) *filesystemBaseProperty {
971	return &filesystemBaseProperty{
972		Name:             namePtr,
973		Compile_multilib: proptools.StringPtr("both"),
974		// The vbmeta modules are currently in the root directory and depend on the partitions
975		Visibility: []string{"//.", "//build/soong:__subpackages__"},
976	}
977}
978
979func generateFsProps(ctx android.EarlyModuleContext, partitions allGeneratedPartitionData, partitionType string) (*filesystem.FilesystemProperties, bool) {
980	fsProps := &filesystem.FilesystemProperties{}
981
982	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
983	var avbInfo avbInfo
984	var fsType string
985	if strings.Contains(partitionType, "ramdisk") {
986		fsType = "compressed_cpio"
987	} else {
988		specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
989		fsType = specificPartitionVars.BoardFileSystemType
990		avbInfo = getAvbInfo(ctx.Config(), partitionType)
991		if fsType == "" {
992			fsType = "ext4" //default
993		}
994	}
995
996	fsProps.Type = proptools.StringPtr(fsType)
997	if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() {
998		// Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
999		return nil, false
1000	}
1001
1002	if *fsProps.Type == "erofs" {
1003		if partitionVars.BoardErofsCompressor != "" {
1004			fsProps.Erofs.Compressor = proptools.StringPtr(partitionVars.BoardErofsCompressor)
1005		}
1006		if partitionVars.BoardErofsCompressorHints != "" {
1007			fsProps.Erofs.Compress_hints = proptools.StringPtr(":soong_generated_board_erofs_compress_hints_filegroup")
1008		}
1009	}
1010
1011	// Don't build this module on checkbuilds, the soong-built partitions are still in-progress
1012	// and sometimes don't build.
1013	fsProps.Unchecked_module = proptools.BoolPtr(true)
1014
1015	// BOARD_AVB_ENABLE
1016	fsProps.Use_avb = avbInfo.avbEnable
1017	// BOARD_AVB_KEY_PATH
1018	fsProps.Avb_private_key = avbInfo.avbkeyFilegroup
1019	// BOARD_AVB_ALGORITHM
1020	fsProps.Avb_algorithm = avbInfo.avbAlgorithm
1021	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX
1022	fsProps.Rollback_index = avbInfo.avbRollbackIndex
1023	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION
1024	fsProps.Rollback_index_location = avbInfo.avbRollbackIndexLocation
1025	fsProps.Avb_hash_algorithm = avbInfo.avbHashAlgorithm
1026
1027	fsProps.Partition_name = proptools.StringPtr(partitionType)
1028
1029	switch partitionType {
1030	// The partitions that support file_contexts came from here:
1031	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2270;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
1032	case "system", "userdata", "cache", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm", "oem":
1033		fsProps.Precompiled_file_contexts = proptools.StringPtr(":file_contexts_bin_gen")
1034	}
1035
1036	fsProps.Is_auto_generated = proptools.BoolPtr(true)
1037	if partitionType != "system" {
1038		mountPoint := proptools.StringPtr(partitionType)
1039		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/tools/releasetools/build_image.py;l=1012;drc=3f576a753594bad3fc838ccb8b1b72f7efac1d50
1040		if partitionType == "userdata" {
1041			mountPoint = proptools.StringPtr("data")
1042		}
1043		fsProps.Mount_point = mountPoint
1044
1045	}
1046
1047	partitionSpecificFsProps(ctx, partitions, fsProps, partitionType)
1048
1049	return fsProps, true
1050}
1051
1052type avbInfo struct {
1053	avbEnable                *bool
1054	avbKeyPath               *string
1055	avbkeyFilegroup          *string
1056	avbAlgorithm             *string
1057	avbRollbackIndex         *int64
1058	avbRollbackIndexLocation *int64
1059	avbMode                  *string
1060	avbHashAlgorithm         *string
1061}
1062
1063func getAvbInfo(config android.Config, partitionType string) avbInfo {
1064	partitionVars := config.ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
1065	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
1066	var result avbInfo
1067	boardAvbEnable := partitionVars.BoardAvbEnable
1068	if boardAvbEnable {
1069		result.avbEnable = proptools.BoolPtr(true)
1070		// There are "global" and "specific" copies of a lot of these variables. Sometimes they
1071		// choose the specific and then fall back to the global one if it's not set, other times
1072		// the global one actually only applies to the vbmeta partition.
1073		if partitionType == "vbmeta" {
1074			if partitionVars.BoardAvbKeyPath != "" {
1075				result.avbKeyPath = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
1076			}
1077			if partitionVars.BoardAvbRollbackIndex != "" {
1078				parsed, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 64)
1079				if err != nil {
1080					panic(fmt.Sprintf("Rollback index must be an int, got %s", partitionVars.BoardAvbRollbackIndex))
1081				}
1082				result.avbRollbackIndex = &parsed
1083			}
1084		}
1085		if specificPartitionVars.BoardAvbKeyPath != "" {
1086			result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
1087		}
1088		if specificPartitionVars.BoardAvbAlgorithm != "" {
1089			result.avbAlgorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
1090		} else if partitionVars.BoardAvbAlgorithm != "" {
1091			result.avbAlgorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
1092		}
1093		if specificPartitionVars.BoardAvbRollbackIndex != "" {
1094			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
1095			if err != nil {
1096				panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
1097			}
1098			result.avbRollbackIndex = &parsed
1099		}
1100		if specificPartitionVars.BoardAvbRollbackIndexLocation != "" {
1101			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndexLocation, 10, 64)
1102			if err != nil {
1103				panic(fmt.Sprintf("Rollback index location must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndexLocation))
1104			}
1105			result.avbRollbackIndexLocation = &parsed
1106		}
1107
1108		// Make allows you to pass arbitrary arguments to avbtool via this variable, but in practice
1109		// it's only used for --hash_algorithm. The soong module has a dedicated property for the
1110		// hashtree algorithm, and doesn't allow custom arguments, so just extract the hashtree
1111		// algorithm out of the arbitrary arguments.
1112		addHashtreeFooterArgs := strings.Split(specificPartitionVars.BoardAvbAddHashtreeFooterArgs, " ")
1113		if i := slices.Index(addHashtreeFooterArgs, "--hash_algorithm"); i >= 0 {
1114			result.avbHashAlgorithm = &addHashtreeFooterArgs[i+1]
1115		}
1116
1117		result.avbMode = proptools.StringPtr("make_legacy")
1118	}
1119	if result.avbKeyPath != nil {
1120		fsGenState := config.Get(fsGenStateOnceKey).(*FsGenState)
1121		filegroup := fsGenState.avbKeyFilegroups[*result.avbKeyPath]
1122		result.avbkeyFilegroup = proptools.StringPtr(":" + filegroup)
1123	}
1124	return result
1125}
1126
1127func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string, partitionModuleName string) android.Path {
1128	partitionImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
1129	filesystemInfo, ok := android.OtherModuleProvider(ctx, partitionImage, filesystem.FilesystemProvider)
1130	if !ok {
1131		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
1132		return nil
1133	}
1134	makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
1135	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
1136
1137	builder := android.NewRuleBuilder(pctx, ctx)
1138	builder.Command().BuiltTool("file_list_diff").
1139		Input(makeFileList).
1140		Input(filesystemInfo.FileListFile).
1141		Text(partitionModuleName)
1142	builder.Command().Text("touch").Output(diffTestResultFile)
1143	builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
1144	return diffTestResultFile
1145}
1146
1147func createFailingCommand(ctx android.ModuleContext, message string) android.Path {
1148	hasher := sha256.New()
1149	hasher.Write([]byte(message))
1150	filename := fmt.Sprintf("failing_command_%x.txt", hasher.Sum(nil))
1151	file := android.PathForModuleOut(ctx, filename)
1152	builder := android.NewRuleBuilder(pctx, ctx)
1153	builder.Command().Textf("echo %s", proptools.NinjaAndShellEscape(message))
1154	builder.Command().Text("exit 1 #").Output(file)
1155	builder.Build("failing command "+filename, "failing command "+filename)
1156	return file
1157}
1158
1159func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
1160	vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
1161	outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
1162	if !ok {
1163		ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
1164	}
1165	if len(outputFilesProvider.DefaultOutputFiles) != 1 {
1166		ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
1167	}
1168	soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
1169	makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))
1170
1171	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
1172	createDiffTest(ctx, diffTestResultFile, soongVbMetaFile, makeVbmetaFile)
1173	return diffTestResultFile
1174}
1175
1176func createDiffTest(ctx android.ModuleContext, diffTestResultFile android.WritablePath, file1 android.Path, file2 android.Path) {
1177	builder := android.NewRuleBuilder(pctx, ctx)
1178	builder.Command().Text("diff").
1179		Input(file1).
1180		Input(file2)
1181	builder.Command().Text("touch").Output(diffTestResultFile)
1182	builder.Build("diff test "+diffTestResultFile.String(), "diff test")
1183}
1184
1185type imageDepTagType struct {
1186	blueprint.BaseDependencyTag
1187}
1188
1189var generatedFilesystemDepTag imageDepTagType
1190var generatedVbmetaPartitionDepTag imageDepTagType
1191
1192func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
1193	for _, name := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions.names() {
1194		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, name)
1195	}
1196	for _, vbmetaModule := range f.properties.Vbmeta_module_names {
1197		ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
1198	}
1199}
1200
1201func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1202	if ctx.ModuleDir() != "build/soong/fsgen" {
1203		ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
1204	}
1205	f.HideFromMake()
1206
1207	partitions := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions
1208
1209	var content strings.Builder
1210	generatedBp := android.PathForModuleOut(ctx, "soong_generated_product_config.bp")
1211	for _, partition := range partitions.types() {
1212		content.WriteString(generateBpContent(ctx, partition))
1213		content.WriteString("\n")
1214	}
1215	android.WriteFileRule(ctx, generatedBp, content.String())
1216
1217	ctx.Phony("product_config_to_bp", generatedBp)
1218
1219	if !ctx.Config().KatiEnabled() {
1220		// Cannot diff since the kati packaging rules will not be created.
1221		return
1222	}
1223	var diffTestFiles []android.Path
1224	for _, partitionType := range partitions.types() {
1225		diffTestFile := f.createFileListDiffTest(ctx, partitionType, partitions.nameForType(partitionType))
1226		diffTestFiles = append(diffTestFiles, diffTestFile)
1227		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
1228	}
1229	for _, partitionType := range slices.Concat(partitions.unsupportedTypes(), f.properties.Unsupported_partition_types) {
1230		diffTestFile := createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType))
1231		diffTestFiles = append(diffTestFiles, diffTestFile)
1232		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
1233	}
1234	for i, vbmetaModule := range f.properties.Vbmeta_module_names {
1235		diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
1236		diffTestFiles = append(diffTestFiles, diffTestFile)
1237		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
1238	}
1239	if f.properties.Boot_image != "" {
1240		diffTestFile := android.PathForModuleOut(ctx, "boot_diff_test.txt")
1241		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Boot_image)
1242		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/boot.img", ctx.Config().DeviceName()))
1243		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
1244		diffTestFiles = append(diffTestFiles, diffTestFile)
1245		ctx.Phony("soong_generated_boot_filesystem_test", diffTestFile)
1246	}
1247	if f.properties.Vendor_boot_image != "" {
1248		diffTestFile := android.PathForModuleOut(ctx, "vendor_boot_diff_test.txt")
1249		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Vendor_boot_image)
1250		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/vendor_boot.img", ctx.Config().DeviceName()))
1251		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
1252		diffTestFiles = append(diffTestFiles, diffTestFile)
1253		ctx.Phony("soong_generated_vendor_boot_filesystem_test", diffTestFile)
1254	}
1255	if f.properties.Init_boot_image != "" {
1256		diffTestFile := android.PathForModuleOut(ctx, "init_boot_diff_test.txt")
1257		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Init_boot_image)
1258		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/init_boot.img", ctx.Config().DeviceName()))
1259		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
1260		diffTestFiles = append(diffTestFiles, diffTestFile)
1261		ctx.Phony("soong_generated_init_boot_filesystem_test", diffTestFile)
1262	}
1263	if f.properties.Super_image != "" {
1264		diffTestFile := android.PathForModuleOut(ctx, "super_diff_test.txt")
1265		soongSuperImg := android.PathForModuleSrc(ctx, f.properties.Super_image)
1266		makeSuperImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/super.img", ctx.Config().DeviceName()))
1267		createDiffTest(ctx, diffTestFile, soongSuperImg, makeSuperImage)
1268		diffTestFiles = append(diffTestFiles, diffTestFile)
1269		ctx.Phony("soong_generated_super_filesystem_test", diffTestFile)
1270	}
1271	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
1272}
1273
1274func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
1275	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
1276	fsProps, fsTypeSupported := generateFsProps(ctx, fsGenState.soongGeneratedPartitions, partitionType)
1277	if !fsTypeSupported {
1278		return ""
1279	}
1280
1281	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
1282	deps := fsGenState.fsDeps[partitionType]
1283	highPriorityDeps := fsGenState.generatedPrebuiltEtcModuleNames
1284	depProps := generateDepStruct(*deps, highPriorityDeps)
1285
1286	result, err := proptools.RepackProperties([]interface{}{baseProps, fsProps, depProps})
1287	if err != nil {
1288		ctx.ModuleErrorf("%s", err.Error())
1289		return ""
1290	}
1291
1292	moduleType := "android_filesystem"
1293	if partitionType == "system" {
1294		moduleType = "android_system_image"
1295	}
1296
1297	file := &parser.File{
1298		Defs: []parser.Definition{
1299			&parser.Module{
1300				Type: moduleType,
1301				Map:  *result,
1302			},
1303		},
1304	}
1305	bytes, err := parser.Print(file)
1306	if err != nil {
1307		ctx.ModuleErrorf(err.Error())
1308	}
1309	return strings.TrimSpace(string(bytes))
1310}
1311