• 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	"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	// Information about the main soong-generated partitions
64	soongGeneratedPartitions allGeneratedPartitionData
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					"notice_xml_system":                         defaultDepCandidateProps(ctx.Config()),
109					"update_engine_sideload":                    defaultDepCandidateProps(ctx.Config()),
110					// keep-sorted end
111				},
112				"vendor": {
113					"fs_config_files_vendor":                               defaultDepCandidateProps(ctx.Config()),
114					"fs_config_dirs_vendor":                                defaultDepCandidateProps(ctx.Config()),
115					"notice_xml_vendor":                                    defaultDepCandidateProps(ctx.Config()),
116					generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
117				},
118				"odm": {
119					// fs_config_* files are automatically installed for all products with odm partitions.
120					// https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
121					"fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
122					"fs_config_dirs_odm":  defaultDepCandidateProps(ctx.Config()),
123					"notice_xml_odm":      defaultDepCandidateProps(ctx.Config()),
124				},
125				"product": {
126					"notice_xml_product": defaultDepCandidateProps(ctx.Config()),
127				},
128				"system_ext": {
129					// VNDK apexes are automatically included.
130					// This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
131					// https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
132					"com.android.vndk.v30":  defaultDepCandidateProps(ctx.Config()),
133					"com.android.vndk.v31":  defaultDepCandidateProps(ctx.Config()),
134					"com.android.vndk.v32":  defaultDepCandidateProps(ctx.Config()),
135					"com.android.vndk.v33":  defaultDepCandidateProps(ctx.Config()),
136					"com.android.vndk.v34":  defaultDepCandidateProps(ctx.Config()),
137					"notice_xml_system_ext": defaultDepCandidateProps(ctx.Config()),
138				},
139				"userdata": {},
140				"system_dlkm": {
141					// these are phony required deps of the phony fs_config_dirs_nonsystem
142					"fs_config_dirs_system_dlkm":  defaultDepCandidateProps(ctx.Config()),
143					"fs_config_files_system_dlkm": defaultDepCandidateProps(ctx.Config()),
144					"notice_xml_system_dlkm":      defaultDepCandidateProps(ctx.Config()),
145					// build props are automatically added to `ALL_DEFAULT_INSTALLED_MODULES`
146					"system_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
147				},
148				"vendor_dlkm": {
149					"fs_config_dirs_vendor_dlkm":  defaultDepCandidateProps(ctx.Config()),
150					"fs_config_files_vendor_dlkm": defaultDepCandidateProps(ctx.Config()),
151					"notice_xml_vendor_dlkm":      defaultDepCandidateProps(ctx.Config()),
152					"vendor_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
153				},
154				"odm_dlkm": {
155					"fs_config_dirs_odm_dlkm":  defaultDepCandidateProps(ctx.Config()),
156					"fs_config_files_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
157					"notice_xml_odm_dlkm":      defaultDepCandidateProps(ctx.Config()),
158					"odm_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
159				},
160				"ramdisk":        {},
161				"vendor_ramdisk": {},
162				"recovery": {
163					"sepolicy.recovery":                     defaultDepCandidateProps(ctx.Config()),
164					"plat_file_contexts.recovery":           defaultDepCandidateProps(ctx.Config()),
165					"plat_service_contexts.recovery":        defaultDepCandidateProps(ctx.Config()),
166					"plat_property_contexts.recovery":       defaultDepCandidateProps(ctx.Config()),
167					"system_ext_file_contexts.recovery":     defaultDepCandidateProps(ctx.Config()),
168					"system_ext_service_contexts.recovery":  defaultDepCandidateProps(ctx.Config()),
169					"system_ext_property_contexts.recovery": defaultDepCandidateProps(ctx.Config()),
170					"vendor_file_contexts.recovery":         defaultDepCandidateProps(ctx.Config()),
171					"vendor_service_contexts.recovery":      defaultDepCandidateProps(ctx.Config()),
172					"vendor_property_contexts.recovery":     defaultDepCandidateProps(ctx.Config()),
173					"odm_file_contexts.recovery":            defaultDepCandidateProps(ctx.Config()),
174					"odm_property_contexts.recovery":        defaultDepCandidateProps(ctx.Config()),
175					"product_file_contexts.recovery":        defaultDepCandidateProps(ctx.Config()),
176					"product_service_contexts.recovery":     defaultDepCandidateProps(ctx.Config()),
177					"product_property_contexts.recovery":    defaultDepCandidateProps(ctx.Config()),
178				},
179			},
180			fsDepsMutex:                     sync.Mutex{},
181			moduleToInstallationProps:       map[string]installationProperties{},
182			generatedPrebuiltEtcModuleNames: generatedPrebuiltEtcModuleNames,
183			avbKeyFilegroups:                map[string]string{},
184		}
185
186		if avbpubkeyGenerated {
187			(*fsGenState.fsDeps["product"])["system_other_avbpubkey"] = defaultDepCandidateProps(ctx.Config())
188		}
189
190		if len(ctx.Config().DeviceManifestFiles()) > 0 {
191			(*fsGenState.fsDeps["vendor"])["vendor_manifest.xml"] = defaultDepCandidateProps(ctx.Config())
192		}
193
194		// Add common resources `prebuilt_res` module as dep of recovery partition
195		(*fsGenState.fsDeps["recovery"])[fmt.Sprintf("recovery-resources-common-%s", getDpi(ctx))] = defaultDepCandidateProps(ctx.Config())
196		(*fsGenState.fsDeps["recovery"])[getRecoveryFontModuleName(ctx)] = defaultDepCandidateProps(ctx.Config())
197		(*fsGenState.fsDeps["recovery"])[createRecoveryBuildProp(ctx)] = defaultDepCandidateProps(ctx.Config())
198
199		return &fsGenState
200	}).(*FsGenState)
201}
202
203func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps multilibDeps, module string, partitionName string) {
204	otherNamespace := mctx.Namespace().Path
205	if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
206		mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
207	}
208}
209
210func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *multilibDeps, installPartition string) {
211	moduleName := mctx.ModuleName()
212	checkDepModuleInMultipleNamespaces(mctx, *deps, moduleName, installPartition)
213	if _, ok := (*deps)[moduleName]; ok {
214		// Prefer the namespace-specific module over the platform module
215		if mctx.Namespace().Path != "." {
216			(*deps)[moduleName].Namespace = mctx.Namespace().Path
217		}
218		(*deps)[moduleName].Arch = append((*deps)[moduleName].Arch, mctx.Module().Target().Arch.ArchType)
219	} else {
220		multilib, _ := mctx.Module().DecodeMultilib(mctx)
221		(*deps)[moduleName] = &depCandidateProps{
222			Namespace: mctx.Namespace().Path,
223			Multilib:  multilib,
224			Arch:      []android.ArchType{mctx.Module().Target().Arch.ArchType},
225		}
226	}
227}
228
229func collectDepsMutator(mctx android.BottomUpMutatorContext) {
230	m := mctx.Module()
231	if m.Target().Os.Class != android.Device {
232		return
233	}
234	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
235
236	fsGenState.fsDepsMutex.Lock()
237	defer fsGenState.fsDepsMutex.Unlock()
238
239	if slices.Contains(fsGenState.depCandidates, mctx.ModuleName()) {
240		installPartition := m.PartitionTag(mctx.DeviceConfig())
241		// Only add the module as dependency when:
242		// - its enabled
243		// - its namespace is included in PRODUCT_SOONG_NAMESPACES
244		if m.Enabled(mctx) && m.ExportedToMake() {
245			appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
246		}
247	}
248	// store the map of module to (required,overrides) even if the module is not in PRODUCT_PACKAGES.
249	// the module might be installed transitively.
250	if m.Enabled(mctx) && m.ExportedToMake() {
251		fsGenState.moduleToInstallationProps[m.Name()] = installationProperties{
252			Required:  m.RequiredModuleNames(mctx),
253			Overrides: m.Overrides(),
254		}
255	}
256}
257
258type depsStruct struct {
259	Deps []string
260}
261
262type multilibDepsStruct struct {
263	Common   depsStruct
264	Lib32    depsStruct
265	Lib64    depsStruct
266	Both     depsStruct
267	Prefer32 depsStruct
268}
269
270type packagingPropsStruct struct {
271	High_priority_deps []string
272	Deps               []string
273	Multilib           multilibDepsStruct
274}
275
276func fullyQualifiedModuleName(moduleName, namespace string) string {
277	if namespace == "." {
278		return moduleName
279	}
280	return fmt.Sprintf("//%s:%s", namespace, moduleName)
281}
282
283func getBitness(archTypes []android.ArchType) (ret []string) {
284	for _, archType := range archTypes {
285		if archType.Multilib == "" {
286			ret = append(ret, android.COMMON_VARIANT)
287		} else {
288			ret = append(ret, archType.Bitness())
289		}
290	}
291	return ret
292}
293
294func setDepsMutator(mctx android.BottomUpMutatorContext) {
295	removeOverriddenDeps(mctx)
296	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
297	fsDeps := fsGenState.fsDeps
298	m := mctx.Module()
299	if partition := fsGenState.soongGeneratedPartitions.typeForName(m.Name()); partition != "" {
300		if fsGenState.soongGeneratedPartitions.isHandwritten(m.Name()) {
301			// Handwritten image, don't modify it
302			return
303		}
304		depsStruct := generateDepStruct(*fsDeps[partition], fsGenState.generatedPrebuiltEtcModuleNames)
305		if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
306			mctx.ModuleErrorf(err.Error())
307		}
308	}
309}
310
311// removeOverriddenDeps collects PRODUCT_PACKAGES and (transitive) required deps.
312// it then removes any modules which appear in `overrides` of the above list.
313func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {
314	mctx.Config().Once(fsGenRemoveOverridesOnceKey, func() interface{} {
315		fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
316		fsDeps := fsGenState.fsDeps
317		overridden := map[string]bool{}
318		allDeps := []string{}
319
320		// Step 1: Initialization: Append PRODUCT_PACKAGES to the queue
321		for _, fsDep := range fsDeps {
322			for depName, _ := range *fsDep {
323				allDeps = append(allDeps, depName)
324			}
325		}
326
327		// Step 2: Process the queue, and add required modules to the queue.
328		i := 0
329		for {
330			if i == len(allDeps) {
331				break
332			}
333			depName := allDeps[i]
334			for _, overrides := range fsGenState.moduleToInstallationProps[depName].Overrides {
335				overridden[overrides] = true
336			}
337			// add required dep to the queue.
338			allDeps = append(allDeps, fsGenState.moduleToInstallationProps[depName].Required...)
339			i += 1
340		}
341
342		// Step 3: Delete all the overridden modules.
343		for overridden, _ := range overridden {
344			for partition, _ := range fsDeps {
345				delete(*fsDeps[partition], overridden)
346			}
347		}
348		return nil
349	})
350}
351
352var HighPriorityDeps = []string{}
353
354func isHighPriorityDep(depName string) bool {
355	for _, highPriorityDeps := range HighPriorityDeps {
356		if strings.HasPrefix(depName, highPriorityDeps) {
357			return true
358		}
359	}
360	return false
361}
362
363func generateDepStruct(deps map[string]*depCandidateProps, highPriorityDeps []string) *packagingPropsStruct {
364	depsStruct := packagingPropsStruct{}
365	for depName, depProps := range deps {
366		bitness := getBitness(depProps.Arch)
367		fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
368		if android.InList(depName, highPriorityDeps) {
369			depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
370		} else if android.InList("32", bitness) && android.InList("64", bitness) {
371			// If both 32 and 64 bit variants are enabled for this module
372			switch depProps.Multilib {
373			case string(android.MultilibBoth):
374				depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
375			case string(android.MultilibCommon), string(android.MultilibFirst):
376				depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
377			case "32":
378				depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
379			case "64", "darwin_universal":
380				depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
381			case "prefer32", "first_prefer32":
382				depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
383			default:
384				depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
385			}
386		} else if android.InList("64", bitness) {
387			// If only 64 bit variant is enabled
388			depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
389		} else if android.InList("32", bitness) {
390			// If only 32 bit variant is enabled
391			depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
392		} else {
393			// If only common variant is enabled
394			depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
395		}
396	}
397	depsStruct.Deps = android.SortedUniqueStrings(depsStruct.Deps)
398	depsStruct.Multilib.Lib32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib32.Deps)
399	depsStruct.Multilib.Lib64.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib64.Deps)
400	depsStruct.Multilib.Prefer32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Prefer32.Deps)
401	depsStruct.Multilib.Both.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Both.Deps)
402	depsStruct.Multilib.Common.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Common.Deps)
403	depsStruct.High_priority_deps = android.SortedUniqueStrings(depsStruct.High_priority_deps)
404
405	return &depsStruct
406}
407