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