| // Copyright (C) 2021 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package aidl |
| |
| import ( |
| "android/soong/android" |
| "android/soong/genrule" |
| |
| "path/filepath" |
| "strings" |
| |
| "github.com/google/blueprint" |
| "github.com/google/blueprint/pathtools" |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| var ( |
| aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{ |
| Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` + |
| `touch ${out} # ${in}`, |
| Description: "create ${out}", |
| }, "outDir") |
| |
| aidlCppRule = pctx.StaticRule("aidlCppRule", blueprint.RuleParams{ |
| Command: `mkdir -p "${headerDir}" && ` + |
| `${aidlCmd} --lang=${lang} ${optionalFlags} --structured --ninja -d ${out}.d ` + |
| `-h ${headerDir} -o ${outDir} ${imports} ${in}`, |
| Depfile: "${out}.d", |
| Deps: blueprint.DepsGCC, |
| CommandDeps: []string{"${aidlCmd}"}, |
| Description: "AIDL ${lang} ${in}", |
| }, "imports", "lang", "headerDir", "outDir", "optionalFlags") |
| |
| aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{ |
| Command: `${aidlCmd} --lang=java ${optionalFlags} --structured --ninja -d ${out}.d ` + |
| `-o ${outDir} ${imports} ${in}`, |
| Depfile: "${out}.d", |
| Deps: blueprint.DepsGCC, |
| CommandDeps: []string{"${aidlCmd}"}, |
| Description: "AIDL Java ${in}", |
| }, "imports", "outDir", "optionalFlags") |
| |
| aidlRustRule = pctx.StaticRule("aidlRustRule", blueprint.RuleParams{ |
| Command: `${aidlCmd} --lang=rust ${optionalFlags} --structured --ninja -d ${out}.d ` + |
| `-o ${outDir} ${imports} ${in}`, |
| Depfile: "${out}.d", |
| Deps: blueprint.DepsGCC, |
| CommandDeps: []string{"${aidlCmd}"}, |
| Description: "AIDL Rust ${in}", |
| }, "imports", "outDir", "optionalFlags") |
| ) |
| |
| type aidlGenProperties struct { |
| Srcs []string `android:"path"` |
| AidlRoot string // base directory for the input aidl file |
| Imports []string |
| Stability *string |
| Lang string // target language [java|cpp|ndk|rust] |
| BaseName string |
| GenLog bool |
| Version string |
| GenTrace bool |
| Unstable *bool |
| Visibility []string |
| Flags []string |
| } |
| |
| type aidlGenRule struct { |
| android.ModuleBase |
| |
| properties aidlGenProperties |
| |
| implicitInputs android.Paths |
| importFlags string |
| |
| // TODO(b/149952131): always have a hash file |
| hashFile android.Path |
| |
| genOutDir android.ModuleGenPath |
| genHeaderDir android.ModuleGenPath |
| genHeaderDeps android.Paths |
| genOutputs android.WritablePaths |
| } |
| |
| var _ android.SourceFileProducer = (*aidlGenRule)(nil) |
| var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil) |
| |
| func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| srcs, imports := getPaths(ctx, g.properties.Srcs) |
| |
| if ctx.Failed() { |
| return |
| } |
| |
| genDirTimestamp := android.PathForModuleGen(ctx, "timestamp") |
| g.implicitInputs = append(g.implicitInputs, genDirTimestamp) |
| |
| var importPaths []string |
| importPaths = append(importPaths, imports...) |
| ctx.VisitDirectDeps(func(dep android.Module) { |
| if importedAidl, ok := dep.(*aidlInterface); ok { |
| importPaths = append(importPaths, importedAidl.properties.Full_import_paths...) |
| } else if api, ok := dep.(*aidlApi); ok { |
| // When compiling an AIDL interface, also make sure that each |
| // version of the interface is compatible with its previous version |
| for _, path := range api.checkApiTimestamps { |
| g.implicitInputs = append(g.implicitInputs, path) |
| } |
| for _, path := range api.checkHashTimestamps { |
| g.implicitInputs = append(g.implicitInputs, path) |
| } |
| } |
| }) |
| g.importFlags = strings.Join(wrap("-I", importPaths, ""), " ") |
| |
| g.genOutDir = android.PathForModuleGen(ctx) |
| g.genHeaderDir = android.PathForModuleGen(ctx, "include") |
| for _, src := range srcs { |
| outFile, headers := g.generateBuildActionsForSingleAidl(ctx, src) |
| g.genOutputs = append(g.genOutputs, outFile) |
| g.genHeaderDeps = append(g.genHeaderDeps, headers...) |
| } |
| |
| // This is to clean genOutDir before generating any file |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: aidlDirPrepareRule, |
| Inputs: srcs, |
| Output: genDirTimestamp, |
| Args: map[string]string{ |
| "outDir": g.genOutDir.String(), |
| }, |
| }) |
| } |
| |
| func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) { |
| baseDir := getBaseDir(ctx, src, android.PathForModuleSrc(ctx, g.properties.AidlRoot)) |
| |
| var ext string |
| if g.properties.Lang == langJava { |
| ext = "java" |
| } else if g.properties.Lang == langRust { |
| ext = "rs" |
| } else { |
| ext = "cpp" |
| } |
| relPath, _ := filepath.Rel(baseDir, src.String()) |
| outFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension(relPath, ext)) |
| implicits := g.implicitInputs |
| |
| optionalFlags := append([]string{}, g.properties.Flags...) |
| if g.properties.Version != "" { |
| optionalFlags = append(optionalFlags, "--version "+g.properties.Version) |
| |
| hash := "notfrozen" |
| if !strings.HasPrefix(baseDir, ctx.Config().BuildDir()) { |
| hashFile := android.ExistentPathForSource(ctx, baseDir, ".hash") |
| if hashFile.Valid() { |
| hash = "$$(read -r <" + hashFile.Path().String() + " hash extra; printf '%s' \"$$hash\")" |
| implicits = append(implicits, hashFile.Path()) |
| |
| g.hashFile = hashFile.Path() |
| } |
| } |
| optionalFlags = append(optionalFlags, "--hash "+hash) |
| } |
| if g.properties.GenTrace { |
| optionalFlags = append(optionalFlags, "-t") |
| } |
| if g.properties.Stability != nil { |
| optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability) |
| } |
| |
| var headers android.WritablePaths |
| if g.properties.Lang == langJava { |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: aidlJavaRule, |
| Input: src, |
| Implicits: implicits, |
| Output: outFile, |
| Args: map[string]string{ |
| "imports": g.importFlags, |
| "outDir": g.genOutDir.String(), |
| "optionalFlags": strings.Join(optionalFlags, " "), |
| }, |
| }) |
| } else if g.properties.Lang == langRust { |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: aidlRustRule, |
| Input: src, |
| Implicits: implicits, |
| Output: outFile, |
| Args: map[string]string{ |
| "imports": g.importFlags, |
| "outDir": g.genOutDir.String(), |
| "optionalFlags": strings.Join(optionalFlags, " "), |
| }, |
| }) |
| } else { |
| typeName := strings.TrimSuffix(filepath.Base(relPath), ".aidl") |
| packagePath := filepath.Dir(relPath) |
| baseName := typeName |
| // TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if |
| // an interface name has a leading I. Those same heuristics have been |
| // moved here. |
| if len(baseName) >= 2 && baseName[0] == 'I' && |
| strings.ToUpper(baseName)[1] == baseName[1] { |
| baseName = strings.TrimPrefix(typeName, "I") |
| } |
| |
| prefix := "" |
| if g.properties.Lang == langNdk || g.properties.Lang == langNdkPlatform { |
| prefix = "aidl" |
| } |
| |
| headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, |
| typeName+".h")) |
| headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, |
| "Bp"+baseName+".h")) |
| headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, |
| "Bn"+baseName+".h")) |
| |
| if g.properties.GenLog { |
| optionalFlags = append(optionalFlags, "--log") |
| } |
| |
| aidlLang := g.properties.Lang |
| if aidlLang == langNdkPlatform { |
| aidlLang = "ndk" |
| } |
| |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: aidlCppRule, |
| Input: src, |
| Implicits: implicits, |
| Output: outFile, |
| ImplicitOutputs: headers, |
| Args: map[string]string{ |
| "imports": g.importFlags, |
| "lang": aidlLang, |
| "headerDir": g.genHeaderDir.String(), |
| "outDir": g.genOutDir.String(), |
| "optionalFlags": strings.Join(optionalFlags, " "), |
| }, |
| }) |
| } |
| |
| return outFile, headers.Paths() |
| } |
| |
| func (g *aidlGenRule) GeneratedSourceFiles() android.Paths { |
| return g.genOutputs.Paths() |
| } |
| |
| func (g *aidlGenRule) Srcs() android.Paths { |
| return g.genOutputs.Paths() |
| } |
| |
| func (g *aidlGenRule) GeneratedDeps() android.Paths { |
| return g.genHeaderDeps |
| } |
| |
| func (g *aidlGenRule) GeneratedHeaderDirs() android.Paths { |
| return android.Paths{g.genHeaderDir} |
| } |
| |
| func (g *aidlGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { |
| ctx.AddDependency(ctx.Module(), nil, wrap("", g.properties.Imports, aidlInterfaceSuffix)...) |
| if !proptools.Bool(g.properties.Unstable) { |
| ctx.AddDependency(ctx.Module(), nil, g.properties.BaseName+aidlApiSuffix) |
| } |
| |
| ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName) |
| } |
| |
| func aidlGenFactory() android.Module { |
| g := &aidlGenRule{} |
| g.AddProperties(&g.properties) |
| android.InitAndroidModule(g) |
| return g |
| } |