1// Copyright 2023 Google Inc. All rights reserved. 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 android 16 17import ( 18 "fmt" 19 "io" 20 "maps" 21 "reflect" 22 23 "github.com/google/blueprint" 24) 25 26var ( 27 mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule", 28 blueprint.RuleParams{ 29 Command: `${aconfig} dump --dedup --format protobuf --out $out $flags`, 30 CommandDeps: []string{"${aconfig}"}, 31 }, "flags") 32 _ = pctx.HostBinToolVariable("aconfig", "aconfig") 33) 34 35// Provider published by aconfig_value_set 36type AconfigDeclarationsProviderData struct { 37 Package string 38 Container string 39 Exportable bool 40 IntermediateCacheOutputPath WritablePath 41 IntermediateDumpOutputPath WritablePath 42} 43 44var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]() 45 46type AconfigReleaseDeclarationsProviderData map[string]AconfigDeclarationsProviderData 47 48var AconfigReleaseDeclarationsProviderKey = blueprint.NewProvider[AconfigReleaseDeclarationsProviderData]() 49 50type ModeInfo struct { 51 Container string 52 Mode string 53} 54type CodegenInfo struct { 55 // AconfigDeclarations is the name of the aconfig_declarations modules that 56 // the codegen module is associated with 57 AconfigDeclarations []string 58 59 // Paths to the cache files of the associated aconfig_declaration modules 60 IntermediateCacheOutputPaths Paths 61 62 // Paths to the srcjar files generated from the java_aconfig_library modules 63 Srcjars Paths 64 65 ModeInfos map[string]ModeInfo 66} 67 68var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]() 69 70func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]ModeInfo) { 71 if len(from) > 0 { 72 depTag := ctx.OtherModuleDependencyTag(module) 73 if tag, ok := depTag.(PropagateAconfigValidationDependencyTag); ok && tag.PropagateAconfigValidation() { 74 maps.Copy(to, from) 75 } 76 } 77} 78 79type aconfigPropagatingDeclarationsInfo struct { 80 AconfigFiles map[string]Paths 81 ModeInfos map[string]ModeInfo 82} 83 84var AconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]() 85 86func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) { 87 if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { 88 for k, v := range dep.ModeInfos { 89 msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n", 90 module.Name(), container, k, v.Container, v.Mode) 91 if v.Container != container && v.Mode != "exported" && v.Mode != "force-read-only" { 92 if asError { 93 ctx.ModuleErrorf(msg) 94 } else { 95 fmt.Print("WARNING: " + msg) 96 } 97 } else { 98 if !asError { 99 fmt.Print("PASSED: " + msg) 100 } 101 } 102 } 103 } 104} 105 106func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { 107 mergedAconfigFiles := make(map[string]Paths) 108 mergedModeInfos := make(map[string]ModeInfo) 109 110 ctx.VisitDirectDepsProxy(func(module ModuleProxy) { 111 if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 { 112 maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos) 113 } 114 115 // If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them. 116 if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok { 117 mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath) 118 } 119 // If we were generating on-device artifacts for other release configs, we would need to add code here to propagate 120 // those artifacts as well. See also b/298444886. 121 if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { 122 for container, v := range dep.AconfigFiles { 123 mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) 124 } 125 propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos) 126 } 127 }) 128 // We only need to set the provider if we have aconfig files. 129 if len(mergedAconfigFiles) > 0 { 130 for _, container := range SortedKeys(mergedAconfigFiles) { 131 aconfigFiles := mergedAconfigFiles[container] 132 mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true) 133 } 134 135 SetProvider(ctx, AconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{ 136 AconfigFiles: mergedAconfigFiles, 137 ModeInfos: mergedModeInfos, 138 }) 139 ctx.setAconfigPaths(getAconfigFilePaths(getContainer(ctx.Module()), mergedAconfigFiles)) 140 } 141} 142 143func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) { 144 info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey) 145 // If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done. 146 if !ok || len(info.AconfigFiles) == 0 { 147 return 148 } 149 data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) { 150 AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths( 151 getContainerUsingProviders(ctx, mod), info.AconfigFiles).Strings()) 152 }) 153 // If there is a Custom writer, it needs to support this provider. 154 if data.Custom != nil { 155 switch reflect.TypeOf(mod).String() { 156 case "*aidl.aidlApi": // writes non-custom before adding .phony 157 case "*android_sdk.sdkRepoHost": // doesn't go through base_rules 158 case "*apex.apexBundle": // aconfig_file properties written 159 case "*bpf.bpf": // properties written (both for module and objs) 160 case "*genrule.Module": // writes non-custom before adding .phony 161 case "*java.SystemModules": // doesn't go through base_rules 162 case "*phony.phony": // properties written 163 case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY` 164 case "*sysprop.syspropLibrary": // properties written 165 default: 166 panic(fmt.Errorf("custom make rules do not handle aconfig files for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), mod)) 167 } 168 } 169} 170 171func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries *[]AndroidMkEntries) { 172 // If there are no entries, then we can ignore this module, even if it has aconfig files. 173 if len(*entries) == 0 { 174 return 175 } 176 info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey) 177 if !ok || len(info.AconfigFiles) == 0 { 178 return 179 } 180 // All of the files in the module potentially depend on the aconfig flag values. 181 for idx, _ := range *entries { 182 (*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries, 183 func(_ AndroidMkExtraEntriesContext, entries *AndroidMkEntries) { 184 entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths( 185 getContainerUsingProviders(ctx, mod), info.AconfigFiles)) 186 }, 187 ) 188 189 } 190} 191 192// TODO(b/397766191): Change the signature to take ModuleProxy 193// Please only access the module's internal data through providers. 194func aconfigUpdateAndroidMkInfos(ctx fillInEntriesContext, mod Module, infos *AndroidMkProviderInfo) { 195 info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey) 196 if !ok || len(info.AconfigFiles) == 0 { 197 return 198 } 199 // All of the files in the module potentially depend on the aconfig flag values. 200 infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths( 201 getContainerUsingProviders(ctx, mod), info.AconfigFiles)) 202 if len(infos.ExtraInfo) > 0 { 203 for _, ei := range (*infos).ExtraInfo { 204 ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths( 205 getContainerUsingProviders(ctx, mod), info.AconfigFiles)) 206 } 207 } 208} 209 210func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths { 211 inputs = SortedUniquePaths(inputs) 212 if len(inputs) == 1 { 213 return Paths{inputs[0]} 214 } 215 216 output := PathForModuleOut(ctx, container, "aconfig_merged.pb") 217 218 if generateRule { 219 ctx.Build(pctx, BuildParams{ 220 Rule: mergeAconfigFilesRule, 221 Description: "merge aconfig files", 222 Inputs: inputs, 223 Output: output, 224 Args: map[string]string{ 225 "flags": JoinWithPrefix(inputs.Strings(), "--cache "), 226 }, 227 }) 228 } 229 230 return Paths{output} 231} 232 233func getContainer(m Module) string { 234 container := "system" 235 base := m.base() 236 if base.SocSpecific() { 237 container = "vendor" 238 } else if base.ProductSpecific() { 239 container = "product" 240 } else if base.SystemExtSpecific() { 241 container = "system_ext" 242 } 243 244 return container 245} 246 247// TODO(b/397766191): Change the signature to take ModuleProxy 248// Please only access the module's internal data through providers. 249func getContainerUsingProviders(ctx OtherModuleProviderContext, m Module) string { 250 container := "system" 251 commonInfo := OtherModulePointerProviderOrDefault(ctx, m, CommonModuleInfoProvider) 252 if commonInfo.Vendor || commonInfo.Proprietary || commonInfo.SocSpecific { 253 container = "vendor" 254 } else if commonInfo.ProductSpecific { 255 container = "product" 256 } else if commonInfo.SystemExtSpecific { 257 container = "system_ext" 258 } 259 260 return container 261} 262 263func getAconfigFilePaths(container string, aconfigFiles map[string]Paths) (paths Paths) { 264 paths = append(paths, aconfigFiles[container]...) 265 if container == "system" { 266 // TODO(b/311155208): Once the default container is system, we can drop this. 267 paths = append(paths, aconfigFiles[""]...) 268 } 269 if container != "system" { 270 if len(aconfigFiles[container]) == 0 && len(aconfigFiles[""]) > 0 { 271 // TODO(b/308625757): Either we guessed the container wrong, or the flag is misdeclared. 272 // For now, just include the system (aka "") container if we get here. 273 //fmt.Printf("container_mismatch: module=%v container=%v files=%v\n", m, container, aconfigFiles) 274 } 275 paths = append(paths, aconfigFiles[""]...) 276 } 277 return 278} 279