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 IsToT bool 69 ImportsWithoutVersion []string 70 Stability *string 71 Lang string // target language [java|cpp|ndk|rust] 72 BaseName string 73 GenLog bool 74 Version string 75 GenTrace bool 76 Unstable *bool 77 Visibility []string 78 Flags []string 79} 80 81type aidlGenRule struct { 82 android.ModuleBase 83 84 properties aidlGenProperties 85 86 implicitInputs android.Paths 87 importFlags string 88 89 // TODO(b/149952131): always have a hash file 90 hashFile android.Path 91 92 genOutDir android.ModuleGenPath 93 genHeaderDir android.ModuleGenPath 94 genHeaderDeps android.Paths 95 genOutputs android.WritablePaths 96} 97 98var _ android.SourceFileProducer = (*aidlGenRule)(nil) 99var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil) 100 101func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 102 srcs, imports := getPaths(ctx, g.properties.Srcs, g.properties.AidlRoot) 103 104 if ctx.Failed() { 105 return 106 } 107 108 genDirTimestamp := android.PathForModuleGen(ctx, "timestamp") // $out/gen/timestamp 109 g.implicitInputs = append(g.implicitInputs, genDirTimestamp) 110 111 importPaths, implicits := getImportsFromDeps(ctx, g.properties.IsToT) 112 imports = append(imports, importPaths...) 113 114 g.importFlags = strings.Join(wrap("-I", imports, ""), " ") 115 116 g.genOutDir = android.PathForModuleGen(ctx) 117 g.genHeaderDir = android.PathForModuleGen(ctx, "include") 118 for _, src := range srcs { 119 outFile, headers := g.generateBuildActionsForSingleAidl(ctx, src) 120 g.genOutputs = append(g.genOutputs, outFile) 121 g.genHeaderDeps = append(g.genHeaderDeps, headers...) 122 } 123 124 // This is to clean genOutDir before generating any file 125 ctx.Build(pctx, android.BuildParams{ 126 Rule: aidlDirPrepareRule, 127 Implicits: implicits, 128 Inputs: srcs, 129 Output: genDirTimestamp, 130 Args: map[string]string{ 131 "outDir": g.genOutDir.String(), 132 }, 133 }) 134 135 // This is to trigger genrule alone 136 ctx.Build(pctx, android.BuildParams{ 137 Rule: android.Phony, 138 Output: android.PathForModuleOut(ctx, "timestamp"), // $out/timestamp 139 Inputs: g.genOutputs.Paths(), 140 }) 141} 142 143func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) { 144 baseDir := getBaseDir(ctx, src, android.PathForModuleSrc(ctx, g.properties.AidlRoot)) 145 146 var ext string 147 if g.properties.Lang == langJava { 148 ext = "java" 149 } else if g.properties.Lang == langRust { 150 ext = "rs" 151 } else { 152 ext = "cpp" 153 } 154 relPath, _ := filepath.Rel(baseDir, src.String()) 155 outFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension(relPath, ext)) 156 implicits := g.implicitInputs 157 158 optionalFlags := append([]string{}, g.properties.Flags...) 159 if g.properties.Version != "" { 160 optionalFlags = append(optionalFlags, "--version "+g.properties.Version) 161 162 hash := "notfrozen" 163 if !strings.HasPrefix(baseDir, ctx.Config().BuildDir()) { 164 hashFile := android.ExistentPathForSource(ctx, baseDir, ".hash") 165 if hashFile.Valid() { 166 hash = "$$(read -r <" + hashFile.Path().String() + " hash extra; printf '%s' \"$$hash\")" 167 implicits = append(implicits, hashFile.Path()) 168 169 g.hashFile = hashFile.Path() 170 } 171 } 172 optionalFlags = append(optionalFlags, "--hash "+hash) 173 } 174 if g.properties.GenTrace { 175 optionalFlags = append(optionalFlags, "-t") 176 } 177 if g.properties.Stability != nil { 178 optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability) 179 } 180 181 var headers android.WritablePaths 182 if g.properties.Lang == langJava { 183 ctx.Build(pctx, android.BuildParams{ 184 Rule: aidlJavaRule, 185 Input: src, 186 Implicits: implicits, 187 Output: outFile, 188 Args: map[string]string{ 189 "imports": g.importFlags, 190 "outDir": g.genOutDir.String(), 191 "optionalFlags": strings.Join(optionalFlags, " "), 192 }, 193 }) 194 } else if g.properties.Lang == langRust { 195 ctx.Build(pctx, android.BuildParams{ 196 Rule: aidlRustRule, 197 Input: src, 198 Implicits: implicits, 199 Output: outFile, 200 Args: map[string]string{ 201 "imports": g.importFlags, 202 "outDir": g.genOutDir.String(), 203 "optionalFlags": strings.Join(optionalFlags, " "), 204 }, 205 }) 206 } else { 207 typeName := strings.TrimSuffix(filepath.Base(relPath), ".aidl") 208 packagePath := filepath.Dir(relPath) 209 baseName := typeName 210 // TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if 211 // an interface name has a leading I. Those same heuristics have been 212 // moved here. 213 if len(baseName) >= 2 && baseName[0] == 'I' && 214 strings.ToUpper(baseName)[1] == baseName[1] { 215 baseName = strings.TrimPrefix(typeName, "I") 216 } 217 218 prefix := "" 219 if g.properties.Lang == langNdk || g.properties.Lang == langNdkPlatform { 220 prefix = "aidl" 221 } 222 223 headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, 224 typeName+".h")) 225 headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, 226 "Bp"+baseName+".h")) 227 headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, 228 "Bn"+baseName+".h")) 229 230 if g.properties.GenLog { 231 optionalFlags = append(optionalFlags, "--log") 232 } 233 234 aidlLang := g.properties.Lang 235 if aidlLang == langNdkPlatform { 236 aidlLang = "ndk" 237 } 238 239 ctx.Build(pctx, android.BuildParams{ 240 Rule: aidlCppRule, 241 Input: src, 242 Implicits: implicits, 243 Output: outFile, 244 ImplicitOutputs: headers, 245 Args: map[string]string{ 246 "imports": g.importFlags, 247 "lang": aidlLang, 248 "headerDir": g.genHeaderDir.String(), 249 "outDir": g.genOutDir.String(), 250 "optionalFlags": strings.Join(optionalFlags, " "), 251 }, 252 }) 253 } 254 255 return outFile, headers.Paths() 256} 257 258func (g *aidlGenRule) GeneratedSourceFiles() android.Paths { 259 return g.genOutputs.Paths() 260} 261 262func (g *aidlGenRule) Srcs() android.Paths { 263 return g.genOutputs.Paths() 264} 265 266func (g *aidlGenRule) GeneratedDeps() android.Paths { 267 return g.genHeaderDeps 268} 269 270func (g *aidlGenRule) GeneratedHeaderDirs() android.Paths { 271 return android.Paths{g.genHeaderDir} 272} 273 274func (g *aidlGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { 275 // original interface 276 ctx.AddDependency(ctx.Module(), interfaceDep, g.properties.BaseName+aidlInterfaceSuffix) 277 278 if !proptools.Bool(g.properties.Unstable) { 279 // for checkapi timestamps 280 ctx.AddDependency(ctx.Module(), apiDep, g.properties.BaseName+aidlApiSuffix) 281 } 282 283 // imported interfaces 284 ctx.AddDependency(ctx.Module(), importInterfaceDep, wrap("", g.properties.ImportsWithoutVersion, aidlInterfaceSuffix)...) 285 286 ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName) 287} 288 289func aidlGenFactory() android.Module { 290 g := &aidlGenRule{} 291 g.AddProperties(&g.properties) 292 android.InitAndroidModule(g) 293 return g 294} 295