Separate mutators and modules creation into a separate file in fsgen
filesystem_creator.go file is getting too large; this change separates
the filesystem_module definition and the fsgen package mutators and
modules creation logic.
Test: m nothing --no-skip-soong-tests
Change-Id: I832d4369984f3e45eb92278ea3a091f8c47f467e
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
new file mode 100644
index 0000000..096b132
--- /dev/null
+++ b/fsgen/fsgen_mutators.go
@@ -0,0 +1,342 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+ "android/soong/android"
+ "fmt"
+ "slices"
+ "sync"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
+ ctx.BottomUp("fs_set_deps", setDepsMutator)
+}
+
+var fsGenStateOnceKey = android.NewOnceKey("FsGenState")
+var fsGenRemoveOverridesOnceKey = android.NewOnceKey("FsGenRemoveOverrides")
+
+// Map of partition module name to its partition that may be generated by Soong.
+// Note that it is not guaranteed that all modules returned by this function are successfully
+// created.
+func getAllSoongGeneratedPartitionNames(config android.Config, partitions []string) map[string]string {
+ ret := map[string]string{}
+ for _, partition := range partitions {
+ ret[generatedModuleNameForPartition(config, partition)] = partition
+ }
+ return ret
+}
+
+type depCandidateProps struct {
+ Namespace string
+ Multilib string
+ Arch []android.ArchType
+}
+
+// Map of module name to depCandidateProps
+type multilibDeps map[string]*depCandidateProps
+
+// Information necessary to generate the filesystem modules, including details about their
+// dependencies
+type FsGenState struct {
+ // List of modules in `PRODUCT_PACKAGES` and `PRODUCT_PACKAGES_DEBUG`
+ depCandidates []string
+ // Map of names of partition to the information of modules to be added as deps
+ fsDeps map[string]*multilibDeps
+ // List of name of partitions to be generated by the filesystem_creator module
+ soongGeneratedPartitions []string
+ // Mutex to protect the fsDeps
+ fsDepsMutex sync.Mutex
+ // Map of _all_ soong module names to their corresponding installation properties
+ moduleToInstallationProps map[string]installationProperties
+}
+
+type installationProperties struct {
+ Required []string
+ Overrides []string
+}
+
+func defaultDepCandidateProps(config android.Config) *depCandidateProps {
+ return &depCandidateProps{
+ Namespace: ".",
+ Arch: []android.ArchType{config.BuildArch},
+ }
+}
+
+func generatedPartitions(ctx android.LoadHookContext) []string {
+ generatedPartitions := []string{"system"}
+ if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+ generatedPartitions = append(generatedPartitions, "system_ext")
+ }
+ if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
+ generatedPartitions = append(generatedPartitions, "vendor")
+ }
+ if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
+ generatedPartitions = append(generatedPartitions, "product")
+ }
+ if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
+ generatedPartitions = append(generatedPartitions, "odm")
+ }
+ if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingSystemDlkmImage {
+ generatedPartitions = append(generatedPartitions, "system_dlkm")
+ }
+ return generatedPartitions
+}
+
+func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNames []string) *FsGenState {
+ return ctx.Config().Once(fsGenStateOnceKey, func() interface{} {
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ candidates := android.FirstUniqueStrings(android.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug))
+ candidates = android.Concat(candidates, generatedPrebuiltEtcModuleNames)
+
+ return &FsGenState{
+ depCandidates: candidates,
+ fsDeps: map[string]*multilibDeps{
+ // These additional deps are added according to the cuttlefish system image bp.
+ "system": {
+ "com.android.apex.cts.shim.v1_prebuilt": defaultDepCandidateProps(ctx.Config()),
+ "dex_bootjars": defaultDepCandidateProps(ctx.Config()),
+ "framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
+ "init.environ.rc-soong": defaultDepCandidateProps(ctx.Config()),
+ "libclang_rt.asan": defaultDepCandidateProps(ctx.Config()),
+ "libcompiler_rt": defaultDepCandidateProps(ctx.Config()),
+ "libdmabufheap": defaultDepCandidateProps(ctx.Config()),
+ "libgsi": defaultDepCandidateProps(ctx.Config()),
+ "llndk.libraries.txt": defaultDepCandidateProps(ctx.Config()),
+ "logpersist.start": defaultDepCandidateProps(ctx.Config()),
+ "update_engine_sideload": defaultDepCandidateProps(ctx.Config()),
+ },
+ "vendor": {
+ "fs_config_files_vendor": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_dirs_vendor": defaultDepCandidateProps(ctx.Config()),
+ generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
+ },
+ "odm": {
+ // fs_config_* files are automatically installed for all products with odm partitions.
+ // https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
+ "fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_dirs_odm": defaultDepCandidateProps(ctx.Config()),
+ },
+ "product": {},
+ "system_ext": {
+ // VNDK apexes are automatically included.
+ // This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
+ // https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
+ "com.android.vndk.v30": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v31": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v32": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
+ },
+ "system_dlkm": {},
+ },
+ soongGeneratedPartitions: generatedPartitions(ctx),
+ fsDepsMutex: sync.Mutex{},
+ moduleToInstallationProps: map[string]installationProperties{},
+ }
+ }).(*FsGenState)
+}
+
+func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps multilibDeps, module string, partitionName string) {
+ otherNamespace := mctx.Namespace().Path
+ if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
+ mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
+ }
+}
+
+func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *multilibDeps, installPartition string) {
+ checkDepModuleInMultipleNamespaces(mctx, *deps, mctx.Module().Name(), installPartition)
+ if _, ok := (*deps)[mctx.Module().Name()]; ok {
+ // Prefer the namespace-specific module over the platform module
+ if mctx.Namespace().Path != "." {
+ (*deps)[mctx.Module().Name()].Namespace = mctx.Namespace().Path
+ }
+ (*deps)[mctx.Module().Name()].Arch = append((*deps)[mctx.Module().Name()].Arch, mctx.Module().Target().Arch.ArchType)
+ } else {
+ multilib, _ := mctx.Module().DecodeMultilib(mctx)
+ (*deps)[mctx.Module().Name()] = &depCandidateProps{
+ Namespace: mctx.Namespace().Path,
+ Multilib: multilib,
+ Arch: []android.ArchType{mctx.Module().Target().Arch.ArchType},
+ }
+ }
+}
+
+func collectDepsMutator(mctx android.BottomUpMutatorContext) {
+ fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+
+ m := mctx.Module()
+ if m.Target().Os.Class == android.Device && slices.Contains(fsGenState.depCandidates, m.Name()) {
+ installPartition := m.PartitionTag(mctx.DeviceConfig())
+ fsGenState.fsDepsMutex.Lock()
+ // Only add the module as dependency when:
+ // - its enabled
+ // - its namespace is included in PRODUCT_SOONG_NAMESPACES
+ if m.Enabled(mctx) && m.ExportedToMake() {
+ appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
+ }
+ fsGenState.fsDepsMutex.Unlock()
+ }
+ // store the map of module to (required,overrides) even if the module is not in PRODUCT_PACKAGES.
+ // the module might be installed transitively.
+ if m.Target().Os.Class == android.Device && m.Enabled(mctx) && m.ExportedToMake() {
+ fsGenState.fsDepsMutex.Lock()
+ fsGenState.moduleToInstallationProps[m.Name()] = installationProperties{
+ Required: m.RequiredModuleNames(mctx),
+ Overrides: m.Overrides(),
+ }
+ fsGenState.fsDepsMutex.Unlock()
+ }
+}
+
+type depsStruct struct {
+ Deps []string
+}
+
+type multilibDepsStruct struct {
+ Common depsStruct
+ Lib32 depsStruct
+ Lib64 depsStruct
+ Both depsStruct
+ Prefer32 depsStruct
+}
+
+type packagingPropsStruct struct {
+ High_priority_deps []string
+ Deps []string
+ Multilib multilibDepsStruct
+}
+
+func fullyQualifiedModuleName(moduleName, namespace string) string {
+ if namespace == "." {
+ return moduleName
+ }
+ return fmt.Sprintf("//%s:%s", namespace, moduleName)
+}
+
+func getBitness(archTypes []android.ArchType) (ret []string) {
+ for _, archType := range archTypes {
+ if archType.Multilib == "" {
+ ret = append(ret, android.COMMON_VARIANT)
+ } else {
+ ret = append(ret, archType.Bitness())
+ }
+ }
+ return ret
+}
+
+func setDepsMutator(mctx android.BottomUpMutatorContext) {
+ removeOverriddenDeps(mctx)
+ fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+ fsDeps := fsGenState.fsDeps
+ soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
+ m := mctx.Module()
+ if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
+ depsStruct := generateDepStruct(*fsDeps[partition])
+ if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
+ mctx.ModuleErrorf(err.Error())
+ }
+ }
+}
+
+// removeOverriddenDeps collects PRODUCT_PACKAGES and (transitive) required deps.
+// it then removes any modules which appear in `overrides` of the above list.
+func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {
+ mctx.Config().Once(fsGenRemoveOverridesOnceKey, func() interface{} {
+ fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+ fsDeps := fsGenState.fsDeps
+ overridden := map[string]bool{}
+ allDeps := []string{}
+
+ // Step 1: Initialization: Append PRODUCT_PACKAGES to the queue
+ for _, fsDep := range fsDeps {
+ for depName, _ := range *fsDep {
+ allDeps = append(allDeps, depName)
+ }
+ }
+
+ // Step 2: Process the queue, and add required modules to the queue.
+ i := 0
+ for {
+ if i == len(allDeps) {
+ break
+ }
+ depName := allDeps[i]
+ for _, overrides := range fsGenState.moduleToInstallationProps[depName].Overrides {
+ overridden[overrides] = true
+ }
+ // add required dep to the queue.
+ allDeps = append(allDeps, fsGenState.moduleToInstallationProps[depName].Required...)
+ i += 1
+ }
+
+ // Step 3: Delete all the overridden modules.
+ for overridden, _ := range overridden {
+ for partition, _ := range fsDeps {
+ delete(*fsDeps[partition], overridden)
+ }
+ }
+ return nil
+ })
+}
+
+var HighPriorityDeps = []string{}
+
+func generateDepStruct(deps map[string]*depCandidateProps) *packagingPropsStruct {
+ depsStruct := packagingPropsStruct{}
+ for depName, depProps := range deps {
+ bitness := getBitness(depProps.Arch)
+ fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
+ if android.InList(depName, HighPriorityDeps) {
+ depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
+ } else if android.InList("32", bitness) && android.InList("64", bitness) {
+ // If both 32 and 64 bit variants are enabled for this module
+ switch depProps.Multilib {
+ case string(android.MultilibBoth):
+ depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+ case string(android.MultilibCommon), string(android.MultilibFirst):
+ depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
+ case "32":
+ depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+ case "64", "darwin_universal":
+ depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+ case "prefer32", "first_prefer32":
+ depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
+ default:
+ depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+ }
+ } else if android.InList("64", bitness) {
+ // If only 64 bit variant is enabled
+ depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+ } else if android.InList("32", bitness) {
+ // If only 32 bit variant is enabled
+ depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+ } else {
+ // If only common variant is enabled
+ depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
+ }
+ }
+ depsStruct.Deps = android.SortedUniqueStrings(depsStruct.Deps)
+ depsStruct.Multilib.Lib32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib32.Deps)
+ depsStruct.Multilib.Lib64.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib64.Deps)
+ depsStruct.Multilib.Prefer32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Prefer32.Deps)
+ depsStruct.Multilib.Both.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Both.Deps)
+ depsStruct.Multilib.Common.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Common.Deps)
+
+ return &depsStruct
+}