1// Copyright (C) 2021 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 aidl 16 17import ( 18 "android/soong/android" 19 "android/soong/genrule" 20 21 "path/filepath" 22 "strings" 23 24 "github.com/google/blueprint" 25 "github.com/google/blueprint/pathtools" 26 "github.com/google/blueprint/proptools" 27) 28 29var ( 30 aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{ 31 Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` + 32 `touch ${out} # ${in}`, 33 Description: "create ${out}", 34 }, "outDir") 35 36 aidlCppRule = pctx.StaticRule("aidlCppRule", blueprint.RuleParams{ 37 Command: `mkdir -p "${headerDir}" && ` + 38 `${aidlCmd} --lang=${lang} ${optionalFlags} --structured --ninja -d ${out}.d ` + 39 `-h ${headerDir} -o ${outDir} ${imports} ${in}`, 40 Depfile: "${out}.d", 41 Deps: blueprint.DepsGCC, 42 CommandDeps: []string{"${aidlCmd}"}, 43 Description: "AIDL ${lang} ${in}", 44 }, "imports", "lang", "headerDir", "outDir", "optionalFlags") 45 46 aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{ 47 Command: `${aidlCmd} --lang=java ${optionalFlags} --structured --ninja -d ${out}.d ` + 48 `-o ${outDir} ${imports} ${in}`, 49 Depfile: "${out}.d", 50 Deps: blueprint.DepsGCC, 51 CommandDeps: []string{"${aidlCmd}"}, 52 Description: "AIDL Java ${in}", 53 }, "imports", "outDir", "optionalFlags") 54 55 aidlRustRule = pctx.StaticRule("aidlRustRule", blueprint.RuleParams{ 56 Command: `${aidlCmd} --lang=rust ${optionalFlags} --structured --ninja -d ${out}.d ` + 57 `-o ${outDir} ${imports} ${in}`, 58 Depfile: "${out}.d", 59 Deps: blueprint.DepsGCC, 60 CommandDeps: []string{"${aidlCmd}"}, 61 Description: "AIDL Rust ${in}", 62 }, "imports", "outDir", "optionalFlags") 63) 64 65type aidlGenProperties struct { 66 Srcs []string `android:"path"` 67 AidlRoot string // base directory for the input aidl file 68 Imports []string 69 Stability *string 70 Min_sdk_version *string 71 Platform_apis bool 72 Lang string // target language [java|cpp|ndk|rust] 73 BaseName string 74 GenLog bool 75 Version string 76 GenRpc bool 77 GenTrace bool 78 Unstable *bool 79 NotFrozen bool 80 Visibility []string 81 Flags []string 82} 83 84type aidlGenRule struct { 85 android.ModuleBase 86 87 properties aidlGenProperties 88 89 deps deps 90 implicitInputs android.Paths 91 importFlags string 92 93 // A frozen aidl_interface always have a hash file 94 hashFile android.Path 95 96 genOutDir android.ModuleGenPath 97 genHeaderDir android.ModuleGenPath 98 genHeaderDeps android.Paths 99 genOutputs android.WritablePaths 100} 101 102var _ android.SourceFileProducer = (*aidlGenRule)(nil) 103var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil) 104 105func (g *aidlGenRule) aidlInterface(ctx android.BaseModuleContext) *aidlInterface { 106 return ctx.GetDirectDepWithTag(g.properties.BaseName, interfaceDep).(*aidlInterface) 107} 108 109func (g *aidlGenRule) getImports(ctx android.ModuleContext) map[string]string { 110 iface := g.aidlInterface(ctx) 111 return iface.getImports(g.properties.Version) 112} 113 114func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 115 srcs, imports := getPaths(ctx, g.properties.Srcs, g.properties.AidlRoot) 116 117 if ctx.Failed() { 118 return 119 } 120 121 g.deps = getDeps(ctx, g.getImports(ctx)) 122 123 genDirTimestamp := android.PathForModuleGen(ctx, "timestamp") // $out/gen/timestamp 124 g.implicitInputs = append(g.implicitInputs, genDirTimestamp) 125 g.implicitInputs = append(g.implicitInputs, g.deps.implicits...) 126 g.implicitInputs = append(g.implicitInputs, g.deps.preprocessed...) 127 128 imports = append(imports, g.deps.imports...) 129 g.importFlags = strings.Join(wrap("-I", imports, ""), " ") 130 131 g.genOutDir = android.PathForModuleGen(ctx) 132 g.genHeaderDir = android.PathForModuleGen(ctx, "include") 133 for _, src := range srcs { 134 outFile, headers := g.generateBuildActionsForSingleAidl(ctx, src) 135 g.genOutputs = append(g.genOutputs, outFile) 136 g.genHeaderDeps = append(g.genHeaderDeps, headers...) 137 } 138 139 // This is to clean genOutDir before generating any file 140 ctx.Build(pctx, android.BuildParams{ 141 Rule: aidlDirPrepareRule, 142 Inputs: srcs, 143 Output: genDirTimestamp, 144 Args: map[string]string{ 145 "outDir": g.genOutDir.String(), 146 }, 147 }) 148 149 // This is to trigger genrule alone 150 ctx.Build(pctx, android.BuildParams{ 151 Rule: android.Phony, 152 Output: android.PathForModuleOut(ctx, "timestamp"), // $out/timestamp 153 Inputs: g.genOutputs.Paths(), 154 }) 155} 156 157func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) { 158 relPath := src.Rel() 159 baseDir := strings.TrimSuffix(strings.TrimSuffix(src.String(), relPath), "/") 160 161 var ext string 162 if g.properties.Lang == langJava { 163 ext = "java" 164 } else if g.properties.Lang == langRust { 165 ext = "rs" 166 } else { 167 ext = "cpp" 168 } 169 outFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension(relPath, ext)) 170 implicits := g.implicitInputs 171 172 optionalFlags := append([]string{}, g.properties.Flags...) 173 if g.properties.Version != "" { 174 optionalFlags = append(optionalFlags, "--version "+g.properties.Version) 175 176 hash := "notfrozen" 177 if !strings.HasPrefix(baseDir, ctx.Config().SoongOutDir()) { 178 hashFile := android.ExistentPathForSource(ctx, baseDir, ".hash") 179 if hashFile.Valid() { 180 hash = "$$(tail -1 '" + hashFile.Path().String() + "')" 181 implicits = append(implicits, hashFile.Path()) 182 183 g.hashFile = hashFile.Path() 184 } 185 } 186 optionalFlags = append(optionalFlags, "--hash "+hash) 187 } 188 if g.properties.GenRpc { 189 optionalFlags = append(optionalFlags, "--rpc") 190 } 191 if g.properties.GenTrace { 192 optionalFlags = append(optionalFlags, "-t") 193 } 194 if g.properties.Stability != nil { 195 optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability) 196 } 197 if g.properties.Platform_apis { 198 optionalFlags = append(optionalFlags, "--min_sdk_version platform_apis") 199 } else { 200 minSdkVer := proptools.StringDefault(g.properties.Min_sdk_version, "current") 201 optionalFlags = append(optionalFlags, "--min_sdk_version "+minSdkVer) 202 } 203 optionalFlags = append(optionalFlags, wrap("-p", g.deps.preprocessed.Strings(), "")...) 204 205 var headers android.WritablePaths 206 if g.properties.Lang == langJava { 207 ctx.Build(pctx, android.BuildParams{ 208 Rule: aidlJavaRule, 209 Input: src, 210 Implicits: implicits, 211 Output: outFile, 212 Args: map[string]string{ 213 "imports": g.importFlags, 214 "outDir": g.genOutDir.String(), 215 "optionalFlags": strings.Join(optionalFlags, " "), 216 }, 217 }) 218 } else if g.properties.Lang == langRust { 219 ctx.Build(pctx, android.BuildParams{ 220 Rule: aidlRustRule, 221 Input: src, 222 Implicits: implicits, 223 Output: outFile, 224 Args: map[string]string{ 225 "imports": g.importFlags, 226 "outDir": g.genOutDir.String(), 227 "optionalFlags": strings.Join(optionalFlags, " "), 228 }, 229 }) 230 } else { 231 typeName := strings.TrimSuffix(filepath.Base(relPath), ".aidl") 232 packagePath := filepath.Dir(relPath) 233 baseName := typeName 234 // TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if 235 // an interface name has a leading I. Those same heuristics have been 236 // moved here. 237 if len(baseName) >= 2 && baseName[0] == 'I' && 238 strings.ToUpper(baseName)[1] == baseName[1] { 239 baseName = strings.TrimPrefix(typeName, "I") 240 } 241 242 prefix := "" 243 if g.properties.Lang == langNdk || g.properties.Lang == langNdkPlatform { 244 prefix = "aidl" 245 } 246 247 headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, 248 typeName+".h")) 249 headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, 250 "Bp"+baseName+".h")) 251 headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, 252 "Bn"+baseName+".h")) 253 254 if g.properties.GenLog { 255 optionalFlags = append(optionalFlags, "--log") 256 } 257 258 aidlLang := g.properties.Lang 259 if aidlLang == langNdkPlatform { 260 aidlLang = "ndk" 261 } 262 263 ctx.Build(pctx, android.BuildParams{ 264 Rule: aidlCppRule, 265 Input: src, 266 Implicits: implicits, 267 Output: outFile, 268 ImplicitOutputs: headers, 269 Args: map[string]string{ 270 "imports": g.importFlags, 271 "lang": aidlLang, 272 "headerDir": g.genHeaderDir.String(), 273 "outDir": g.genOutDir.String(), 274 "optionalFlags": strings.Join(optionalFlags, " "), 275 }, 276 }) 277 } 278 279 return outFile, headers.Paths() 280} 281 282func (g *aidlGenRule) GeneratedSourceFiles() android.Paths { 283 return g.genOutputs.Paths() 284} 285 286func (g *aidlGenRule) Srcs() android.Paths { 287 return g.genOutputs.Paths() 288} 289 290func (g *aidlGenRule) GeneratedDeps() android.Paths { 291 return g.genHeaderDeps 292} 293 294func (g *aidlGenRule) GeneratedHeaderDirs() android.Paths { 295 return android.Paths{g.genHeaderDir} 296} 297 298func (g *aidlGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { 299 ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName) 300} 301 302func aidlGenFactory() android.Module { 303 g := &aidlGenRule{} 304 g.AddProperties(&g.properties) 305 android.InitAndroidModule(g) 306 return g 307} 308