xref: /aosp_15_r20/build/soong/fsgen/fsgen_mutators.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	"fmt"
19	"slices"
20	"strings"
21	"sync"
22
23	"android/soong/android"
24
25	"github.com/google/blueprint/proptools"
26)
27
28func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
29	ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
30	ctx.BottomUp("fs_set_deps", setDepsMutator)
31}
32
33var fsGenStateOnceKey = android.NewOnceKey("FsGenState")
34var fsGenRemoveOverridesOnceKey = android.NewOnceKey("FsGenRemoveOverrides")
35
36// Map of partition module name to its partition that may be generated by Soong.
37// Note that it is not guaranteed that all modules returned by this function are successfully
38// created.
39func getAllSoongGeneratedPartitionNames(config android.Config, partitions []string) map[string]string {
40	ret := map[string]string{}
41	for _, partition := range partitions {
42		ret[generatedModuleNameForPartition(config, partition)] = partition
43	}
44	return ret
45}
46
47type depCandidateProps struct {
48	Namespace string
49	Multilib  string
50	Arch      []android.ArchType
51}
52
53// Map of module name to depCandidateProps
54type multilibDeps map[string]*depCandidateProps
55
56// Information necessary to generate the filesystem modules, including details about their
57// dependencies
58type FsGenState struct {
59	// List of modules in `PRODUCT_PACKAGES` and `PRODUCT_PACKAGES_DEBUG`
60	depCandidates []string
61	// Map of names of partition to the information of modules to be added as deps
62	fsDeps map[string]*multilibDeps
63	// List of name of partitions to be generated by the filesystem_creator module
64	soongGeneratedPartitions []string
65	// Mutex to protect the fsDeps
66	fsDepsMutex sync.Mutex
67	// Map of _all_ soong module names to their corresponding installation properties
68	moduleToInstallationProps map[string]installationProperties
69	// List of prebuilt_* modules that are autogenerated.
70	generatedPrebuiltEtcModuleNames []string
71	// Mapping from a path to an avb key to the name of a filegroup module that contains it
72	avbKeyFilegroups map[string]string
73}
74
75type installationProperties struct {
76	Required  []string
77	Overrides []string
78}
79
80func defaultDepCandidateProps(config android.Config) *depCandidateProps {
81	return &depCandidateProps{
82		Namespace: ".",
83		Arch:      []android.ArchType{config.BuildArch},
84	}
85}
86
87func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNames []string, avbpubkeyGenerated bool) *FsGenState {
88	return ctx.Config().Once(fsGenStateOnceKey, func() interface{} {
89		partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
90		candidates := android.FirstUniqueStrings(android.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug))
91		candidates = android.Concat(candidates, generatedPrebuiltEtcModuleNames)
92
93		fsGenState := FsGenState{
94			depCandidates: candidates,
95			fsDeps: map[string]*multilibDeps{
96				// These additional deps are added according to the cuttlefish system image bp.
97				"system": {
98					// keep-sorted start
99					"com.android.apex.cts.shim.v1_prebuilt":     defaultDepCandidateProps(ctx.Config()),
100					"dex_bootjars":                              defaultDepCandidateProps(ctx.Config()),
101					"framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
102					"init.environ.rc-soong":                     defaultDepCandidateProps(ctx.Config()),
103					"libcompiler_rt":                            defaultDepCandidateProps(ctx.Config()),
104					"libdmabufheap":                             defaultDepCandidateProps(ctx.Config()),
105					"libgsi":                                    defaultDepCandidateProps(ctx.Config()),
106					"llndk.libraries.txt":                       defaultDepCandidateProps(ctx.Config()),
107					"logpersist.start":                          defaultDepCandidateProps(ctx.Config()),
108					"update_engine_sideload":                    defaultDepCandidateProps(ctx.Config()),
109					// keep-sorted end
110				},
111				"vendor": {
112					"fs_config_files_vendor":                               defaultDepCandidateProps(ctx.Config()),
113					"fs_config_dirs_vendor":                                defaultDepCandidateProps(ctx.Config()),
114					generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
115				},
116				"odm": {
117					// fs_config_* files are automatically installed for all products with odm partitions.
118					// https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
119					"fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
120					"fs_config_dirs_odm":  defaultDepCandidateProps(ctx.Config()),
121				},
122				"product": {},
123				"system_ext": {
124					// VNDK apexes are automatically included.
125					// This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
126					// https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
127					"com.android.vndk.v30": defaultDepCandidateProps(ctx.Config()),
128					"com.android.vndk.v31": defaultDepCandidateProps(ctx.Config()),
129					"com.android.vndk.v32": defaultDepCandidateProps(ctx.Config()),
130					"com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
131					"com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
132				},
133				"userdata": {},
134				"system_dlkm": {
135					// these are phony required deps of the phony fs_config_dirs_nonsystem
136					"fs_config_dirs_system_dlkm":  defaultDepCandidateProps(ctx.Config()),
137					"fs_config_files_system_dlkm": defaultDepCandidateProps(ctx.Config()),
138					// build props are automatically added to `ALL_DEFAULT_INSTALLED_MODULES`
139					"system_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
140				},
141				"vendor_dlkm": {
142					"fs_config_dirs_vendor_dlkm":  defaultDepCandidateProps(ctx.Config()),
143					"fs_config_files_vendor_dlkm": defaultDepCandidateProps(ctx.Config()),
144					"vendor_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
145				},
146				"odm_dlkm": {
147					"fs_config_dirs_odm_dlkm":  defaultDepCandidateProps(ctx.Config()),
148					"fs_config_files_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
149					"odm_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
150				},
151				"ramdisk":        {},
152				"vendor_ramdisk": {},
153				"recovery":       {},
154			},
155			fsDepsMutex:                     sync.Mutex{},
156			moduleToInstallationProps:       map[string]installationProperties{},
157			generatedPrebuiltEtcModuleNames: generatedPrebuiltEtcModuleNames,
158			avbKeyFilegroups:                map[string]string{},
159		}
160
161		if avbpubkeyGenerated {
162			(*fsGenState.fsDeps["product"])["system_other_avbpubkey"] = defaultDepCandidateProps(ctx.Config())
163		}
164
165		// Add common resources `prebuilt_res` module as dep of recovery partition
166		(*fsGenState.fsDeps["recovery"])[fmt.Sprintf("recovery-resources-common-%s", getDpi(ctx))] = defaultDepCandidateProps(ctx.Config())
167
168		return &fsGenState
169	}).(*FsGenState)
170}
171
172func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps multilibDeps, module string, partitionName string) {
173	otherNamespace := mctx.Namespace().Path
174	if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
175		mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
176	}
177}
178
179func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *multilibDeps, installPartition string) {
180	moduleName := mctx.ModuleName()
181	checkDepModuleInMultipleNamespaces(mctx, *deps, moduleName, installPartition)
182	if _, ok := (*deps)[moduleName]; ok {
183		// Prefer the namespace-specific module over the platform module
184		if mctx.Namespace().Path != "." {
185			(*deps)[moduleName].Namespace = mctx.Namespace().Path
186		}
187		(*deps)[moduleName].Arch = append((*deps)[moduleName].Arch, mctx.Module().Target().Arch.ArchType)
188	} else {
189		multilib, _ := mctx.Module().DecodeMultilib(mctx)
190		(*deps)[moduleName] = &depCandidateProps{
191			Namespace: mctx.Namespace().Path,
192			Multilib:  multilib,
193			Arch:      []android.ArchType{mctx.Module().Target().Arch.ArchType},
194		}
195	}
196}
197
198func collectDepsMutator(mctx android.BottomUpMutatorContext) {
199	m := mctx.Module()
200	if m.Target().Os.Class != android.Device {
201		return
202	}
203	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
204
205	fsGenState.fsDepsMutex.Lock()
206	defer fsGenState.fsDepsMutex.Unlock()
207
208	if slices.Contains(fsGenState.depCandidates, mctx.ModuleName()) {
209		installPartition := m.PartitionTag(mctx.DeviceConfig())
210		// Only add the module as dependency when:
211		// - its enabled
212		// - its namespace is included in PRODUCT_SOONG_NAMESPACES
213		if m.Enabled(mctx) && m.ExportedToMake() {
214			appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
215		}
216	}
217	// store the map of module to (required,overrides) even if the module is not in PRODUCT_PACKAGES.
218	// the module might be installed transitively.
219	if m.Enabled(mctx) && m.ExportedToMake() {
220		fsGenState.moduleToInstallationProps[m.Name()] = installationProperties{
221			Required:  m.RequiredModuleNames(mctx),
222			Overrides: m.Overrides(),
223		}
224	}
225}
226
227type depsStruct struct {
228	Deps []string
229}
230
231type multilibDepsStruct struct {
232	Common   depsStruct
233	Lib32    depsStruct
234	Lib64    depsStruct
235	Both     depsStruct
236	Prefer32 depsStruct
237}
238
239type packagingPropsStruct struct {
240	High_priority_deps []string
241	Deps               []string
242	Multilib           multilibDepsStruct
243}
244
245func fullyQualifiedModuleName(moduleName, namespace string) string {
246	if namespace == "." {
247		return moduleName
248	}
249	return fmt.Sprintf("//%s:%s", namespace, moduleName)
250}
251
252func getBitness(archTypes []android.ArchType) (ret []string) {
253	for _, archType := range archTypes {
254		if archType.Multilib == "" {
255			ret = append(ret, android.COMMON_VARIANT)
256		} else {
257			ret = append(ret, archType.Bitness())
258		}
259	}
260	return ret
261}
262
263func setDepsMutator(mctx android.BottomUpMutatorContext) {
264	removeOverriddenDeps(mctx)
265	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
266	fsDeps := fsGenState.fsDeps
267	soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
268	m := mctx.Module()
269	if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
270		depsStruct := generateDepStruct(*fsDeps[partition], fsGenState.generatedPrebuiltEtcModuleNames)
271		if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
272			mctx.ModuleErrorf(err.Error())
273		}
274	}
275}
276
277// removeOverriddenDeps collects PRODUCT_PACKAGES and (transitive) required deps.
278// it then removes any modules which appear in `overrides` of the above list.
279func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {
280	mctx.Config().Once(fsGenRemoveOverridesOnceKey, func() interface{} {
281		fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
282		fsDeps := fsGenState.fsDeps
283		overridden := map[string]bool{}
284		allDeps := []string{}
285
286		// Step 1: Initialization: Append PRODUCT_PACKAGES to the queue
287		for _, fsDep := range fsDeps {
288			for depName, _ := range *fsDep {
289				allDeps = append(allDeps, depName)
290			}
291		}
292
293		// Step 2: Process the queue, and add required modules to the queue.
294		i := 0
295		for {
296			if i == len(allDeps) {
297				break
298			}
299			depName := allDeps[i]
300			for _, overrides := range fsGenState.moduleToInstallationProps[depName].Overrides {
301				overridden[overrides] = true
302			}
303			// add required dep to the queue.
304			allDeps = append(allDeps, fsGenState.moduleToInstallationProps[depName].Required...)
305			i += 1
306		}
307
308		// Step 3: Delete all the overridden modules.
309		for overridden, _ := range overridden {
310			for partition, _ := range fsDeps {
311				delete(*fsDeps[partition], overridden)
312			}
313		}
314		return nil
315	})
316}
317
318var HighPriorityDeps = []string{}
319
320func isHighPriorityDep(depName string) bool {
321	for _, highPriorityDeps := range HighPriorityDeps {
322		if strings.HasPrefix(depName, highPriorityDeps) {
323			return true
324		}
325	}
326	return false
327}
328
329func generateDepStruct(deps map[string]*depCandidateProps, highPriorityDeps []string) *packagingPropsStruct {
330	depsStruct := packagingPropsStruct{}
331	for depName, depProps := range deps {
332		bitness := getBitness(depProps.Arch)
333		fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
334		if android.InList(depName, highPriorityDeps) {
335			depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
336		} else if android.InList("32", bitness) && android.InList("64", bitness) {
337			// If both 32 and 64 bit variants are enabled for this module
338			switch depProps.Multilib {
339			case string(android.MultilibBoth):
340				depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
341			case string(android.MultilibCommon), string(android.MultilibFirst):
342				depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
343			case "32":
344				depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
345			case "64", "darwin_universal":
346				depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
347			case "prefer32", "first_prefer32":
348				depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
349			default:
350				depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
351			}
352		} else if android.InList("64", bitness) {
353			// If only 64 bit variant is enabled
354			depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
355		} else if android.InList("32", bitness) {
356			// If only 32 bit variant is enabled
357			depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
358		} else {
359			// If only common variant is enabled
360			depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
361		}
362	}
363	depsStruct.Deps = android.SortedUniqueStrings(depsStruct.Deps)
364	depsStruct.Multilib.Lib32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib32.Deps)
365	depsStruct.Multilib.Lib64.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib64.Deps)
366	depsStruct.Multilib.Prefer32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Prefer32.Deps)
367	depsStruct.Multilib.Both.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Both.Deps)
368	depsStruct.Multilib.Common.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Common.Deps)
369	depsStruct.High_priority_deps = android.SortedUniqueStrings(depsStruct.High_priority_deps)
370
371	return &depsStruct
372}
373