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 codegen 16 17import ( 18 "android/soong/android" 19 "android/soong/cc" 20 21 "github.com/google/blueprint" 22 "github.com/google/blueprint/proptools" 23 24 "fmt" 25 "strconv" 26 "strings" 27) 28 29type ccDeclarationsTagType struct { 30 blueprint.BaseDependencyTag 31} 32 33var ccDeclarationsTag = ccDeclarationsTagType{} 34 35const baseLibDep = "server_configurable_flags" 36 37const libBaseDep = "libbase" 38const libLogDep = "liblog" 39const libAconfigStorageReadApiCcDep = "libaconfig_storage_read_api_cc" 40 41type CcAconfigLibraryProperties struct { 42 // name of the aconfig_declarations module to generate a library for 43 Aconfig_declarations string 44 45 // default mode is "production", the other accepted modes are: 46 // "test": to generate test mode version of the library 47 // "exported": to generate exported mode version of the library 48 // "force-read-only": to generate force-read-only mode version of the library 49 // an error will be thrown if the mode is not supported 50 Mode *string 51} 52 53type CcAconfigLibraryCallbacks struct { 54 properties *CcAconfigLibraryProperties 55 56 generatedDir android.WritablePath 57 headerDir android.WritablePath 58 generatedCpp android.WritablePath 59 generatedH android.WritablePath 60} 61 62func CcAconfigLibraryFactory() android.Module { 63 callbacks := &CcAconfigLibraryCallbacks{ 64 properties: &CcAconfigLibraryProperties{}, 65 } 66 return cc.GeneratedCcLibraryModuleFactory("cc_aconfig_library", callbacks) 67} 68 69func (this *CcAconfigLibraryCallbacks) GeneratorInit(ctx cc.BaseModuleContext) { 70} 71 72func (this *CcAconfigLibraryCallbacks) GeneratorProps() []interface{} { 73 return []interface{}{this.properties} 74} 75 76func (this *CcAconfigLibraryCallbacks) GeneratorDeps(ctx cc.DepsContext, deps cc.Deps) cc.Deps { 77 // Add a dependency for the declarations module 78 declarations := this.properties.Aconfig_declarations 79 if len(declarations) == 0 { 80 ctx.PropertyErrorf("aconfig_declarations", "aconfig_declarations property required") 81 } else { 82 ctx.AddDependency(ctx.Module(), ccDeclarationsTag, declarations) 83 } 84 85 mode := proptools.StringDefault(this.properties.Mode, "production") 86 87 // Add a dependency for the aconfig flags base library if it is not forced read only 88 if mode != "force-read-only" { 89 deps.SharedLibs = append(deps.SharedLibs, baseLibDep) 90 91 } 92 93 // TODO: after storage migration is over, don't add these in force-read-only-mode. 94 deps.SharedLibs = append(deps.SharedLibs, libAconfigStorageReadApiCcDep) 95 deps.SharedLibs = append(deps.SharedLibs, libBaseDep) 96 deps.SharedLibs = append(deps.SharedLibs, libLogDep) 97 98 // TODO: It'd be really nice if we could reexport this library and not make everyone do it. 99 100 return deps 101} 102 103func (this *CcAconfigLibraryCallbacks) GeneratorSources(ctx cc.ModuleContext) cc.GeneratedSource { 104 result := cc.GeneratedSource{} 105 106 // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag 107 declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag) 108 if len(declarationsModules) != 1 { 109 panic(fmt.Errorf("Exactly one aconfig_declarations property required")) 110 } 111 declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey) 112 113 // Figure out the generated file paths. This has to match aconfig's codegen_cpp.rs. 114 this.generatedDir = android.PathForModuleGen(ctx) 115 116 this.headerDir = android.PathForModuleGen(ctx, "include") 117 result.IncludeDirs = []android.Path{this.headerDir} 118 result.ReexportedDirs = []android.Path{this.headerDir} 119 120 basename := strings.ReplaceAll(declarations.Package, ".", "_") 121 122 this.generatedCpp = android.PathForModuleGen(ctx, basename+".cc") 123 result.Sources = []android.Path{this.generatedCpp} 124 125 this.generatedH = android.PathForModuleGen(ctx, "include", basename+".h") 126 result.Headers = []android.Path{this.generatedH} 127 128 return result 129} 130 131func (this *CcAconfigLibraryCallbacks) GeneratorFlags(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) cc.Flags { 132 return flags 133} 134 135func (this *CcAconfigLibraryCallbacks) GeneratorBuildActions(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) { 136 // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag 137 declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag) 138 if len(declarationsModules) != 1 { 139 panic(fmt.Errorf("Exactly one aconfig_declarations property required")) 140 } 141 declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey) 142 143 mode := proptools.StringDefault(this.properties.Mode, "production") 144 if !isModeSupported(mode) { 145 ctx.PropertyErrorf("mode", "%q is not a supported mode", mode) 146 } 147 148 ctx.Build(pctx, android.BuildParams{ 149 Rule: cppRule, 150 Input: declarations.IntermediateCacheOutputPath, 151 Outputs: []android.WritablePath{ 152 this.generatedCpp, 153 this.generatedH, 154 }, 155 Description: "cc_aconfig_library", 156 Args: map[string]string{ 157 "gendir": this.generatedDir.String(), 158 "mode": mode, 159 "debug": strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()), 160 }, 161 }) 162 163 android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{ 164 ModeInfos: map[string]android.ModeInfo{ 165 ctx.ModuleName(): { 166 Container: declarations.Container, 167 Mode: mode, 168 }}, 169 }) 170} 171