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 ModeInfo struct { 47 Container string 48 Mode string 49} 50type CodegenInfo struct { 51 // AconfigDeclarations is the name of the aconfig_declarations modules that 52 // the codegen module is associated with 53 AconfigDeclarations []string 54 55 // Paths to the cache files of the associated aconfig_declaration modules 56 IntermediateCacheOutputPaths Paths 57 58 // Paths to the srcjar files generated from the java_aconfig_library modules 59 Srcjars Paths 60 61 ModeInfos map[string]ModeInfo 62} 63 64var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]() 65 66func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]ModeInfo) { 67 if len(from) > 0 { 68 depTag := ctx.OtherModuleDependencyTag(module) 69 if tag, ok := depTag.(PropagateAconfigValidationDependencyTag); ok && tag.PropagateAconfigValidation() { 70 maps.Copy(to, from) 71 } 72 } 73} 74 75type aconfigPropagatingDeclarationsInfo struct { 76 AconfigFiles map[string]Paths 77 ModeInfos map[string]ModeInfo 78} 79 80var AconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]() 81 82func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) { 83 if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { 84 for k, v := range dep.ModeInfos { 85 msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n", 86 module.Name(), container, k, v.Container, v.Mode) 87 if v.Container != container && v.Mode != "exported" && v.Mode != "force-read-only" { 88 if asError { 89 ctx.ModuleErrorf(msg) 90 } else { 91 fmt.Printf("WARNING: " + msg) 92 } 93 } else { 94 if !asError { 95 fmt.Printf("PASSED: " + msg) 96 } 97 } 98 } 99 } 100} 101 102func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { 103 mergedAconfigFiles := make(map[string]Paths) 104 mergedModeInfos := make(map[string]ModeInfo) 105 106 ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) { 107 if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 { 108 maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos) 109 } 110 111 // If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them. 112 if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok { 113 mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath) 114 } 115 if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { 116 for container, v := range dep.AconfigFiles { 117 mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) 118 } 119 propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos) 120 } 121 }) 122 // We only need to set the provider if we have aconfig files. 123 if len(mergedAconfigFiles) > 0 { 124 for _, container := range SortedKeys(mergedAconfigFiles) { 125 aconfigFiles := mergedAconfigFiles[container] 126 mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true) 127 } 128 129 SetProvider(ctx, AconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{ 130 AconfigFiles: mergedAconfigFiles, 131 ModeInfos: mergedModeInfos, 132 }) 133 ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles) 134 } 135} 136 137func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) { 138 info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey) 139 // If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done. 140 if !ok || len(info.AconfigFiles) == 0 { 141 return 142 } 143 data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) { 144 AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles).Strings()) 145 }) 146 // If there is a Custom writer, it needs to support this provider. 147 if data.Custom != nil { 148 switch reflect.TypeOf(mod).String() { 149 case "*aidl.aidlApi": // writes non-custom before adding .phony 150 case "*android_sdk.sdkRepoHost": // doesn't go through base_rules 151 case "*apex.apexBundle": // aconfig_file properties written 152 case "*bpf.bpf": // properties written (both for module and objs) 153 case "*genrule.Module": // writes non-custom before adding .phony 154 case "*java.SystemModules": // doesn't go through base_rules 155 case "*phony.phony": // properties written 156 case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY` 157 case "*sysprop.syspropLibrary": // properties written 158 default: 159 panic(fmt.Errorf("custom make rules do not handle aconfig files for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), mod)) 160 } 161 } 162} 163 164func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries *[]AndroidMkEntries) { 165 // If there are no entries, then we can ignore this module, even if it has aconfig files. 166 if len(*entries) == 0 { 167 return 168 } 169 info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey) 170 if !ok || len(info.AconfigFiles) == 0 { 171 return 172 } 173 // All of the files in the module potentially depend on the aconfig flag values. 174 for idx, _ := range *entries { 175 (*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries, 176 func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) { 177 entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles)) 178 }, 179 ) 180 181 } 182} 183 184func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths { 185 inputs = SortedUniquePaths(inputs) 186 if len(inputs) == 1 { 187 return Paths{inputs[0]} 188 } 189 190 output := PathForModuleOut(ctx, container, "aconfig_merged.pb") 191 192 if generateRule { 193 ctx.Build(pctx, BuildParams{ 194 Rule: mergeAconfigFilesRule, 195 Description: "merge aconfig files", 196 Inputs: inputs, 197 Output: output, 198 Args: map[string]string{ 199 "flags": JoinWithPrefix(inputs.Strings(), "--cache "), 200 }, 201 }) 202 } 203 204 return Paths{output} 205} 206 207func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) { 208 // TODO(b/311155208): The default container here should be system. 209 container := "system" 210 211 if m.SocSpecific() { 212 container = "vendor" 213 } else if m.ProductSpecific() { 214 container = "product" 215 } else if m.SystemExtSpecific() { 216 container = "system_ext" 217 } 218 219 paths = append(paths, aconfigFiles[container]...) 220 if container == "system" { 221 // TODO(b/311155208): Once the default container is system, we can drop this. 222 paths = append(paths, aconfigFiles[""]...) 223 } 224 if container != "system" { 225 if len(aconfigFiles[container]) == 0 && len(aconfigFiles[""]) > 0 { 226 // TODO(b/308625757): Either we guessed the container wrong, or the flag is misdeclared. 227 // For now, just include the system (aka "") container if we get here. 228 //fmt.Printf("container_mismatch: module=%v container=%v files=%v\n", m, container, aconfigFiles) 229 } 230 paths = append(paths, aconfigFiles[""]...) 231 } 232 return 233} 234