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