// Copyright (C) 2018 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/cc"
"android/soong/genrule"
"android/soong/java"
"android/soong/phony"
"fmt"
"io"
"path/filepath"
"strconv"
"strings"
"sync"
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
var (
aidlInterfaceSuffix = "_interface"
aidlApiSuffix = "-api"
langCpp = "cpp"
langJava = "java"
langNdk = "ndk"
langNdkPlatform = "ndk_platform"
futureVersion = "10000"
pctx = android.NewPackageContext("android/aidl")
aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{
Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
`touch ${out}`,
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")
aidlDumpApiRule = pctx.StaticRule("aidlDumpApiRule", blueprint.RuleParams{
Command: `rm -rf "${out}" && mkdir -p "${out}" && ` +
`${aidlCmd} --dumpapi --structured ${imports} --out ${out} ${in}`,
CommandDeps: []string{"${aidlCmd}"},
}, "imports")
aidlDumpMappingsRule = pctx.StaticRule("aidlDumpMappingsRule", blueprint.RuleParams{
Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
`${aidlCmd} --apimapping ${outDir}/intermediate.txt ${in} ${imports} && ` +
`${aidlToJniCmd} ${outDir}/intermediate.txt ${out}`,
CommandDeps: []string{"${aidlCmd}"},
}, "imports", "outDir")
aidlFreezeApiRule = pctx.AndroidStaticRule("aidlFreezeApiRule",
blueprint.RuleParams{
Command: `mkdir -p ${to} && rm -rf ${to}/* && ` +
`${bpmodifyCmd} -w -m ${name} -parameter versions -a ${version} ${bp} && ` +
`cp -rf ${in}/* ${to} && ` +
`find ${to} -type f -exec bash -c ` +
`"cat ${apiPreamble} {} > {}.temp; mv {}.temp {}" \; && ` +
`touch ${out}`,
CommandDeps: []string{"${bpmodifyCmd}"},
}, "to", "name", "version", "bp", "apiPreamble")
aidlCheckApiRule = pctx.StaticRule("aidlCheckApiRule", blueprint.RuleParams{
Command: `(${aidlCmd} --checkapi ${old} ${new} && touch ${out}) || ` +
`(cat ${messageFile} && exit 1)`,
CommandDeps: []string{"${aidlCmd}"},
Description: "AIDL CHECK API: ${new} against ${old}",
}, "old", "new", "messageFile")
aidlDiffApiRule = pctx.StaticRule("aidlDiffApiRule", blueprint.RuleParams{
Command: `(diff -r -B -I '//.*' ${old} ${new} && touch ${out}) || ` +
`(cat ${messageFile} && exit 1)`,
Description: "Check equality of ${new} and ${old}",
}, "old", "new", "messageFile")
)
func init() {
pctx.HostBinToolVariable("aidlCmd", "aidl")
pctx.HostBinToolVariable("bpmodifyCmd", "bpmodify")
pctx.SourcePathVariable("aidlToJniCmd", "system/tools/aidl/build/aidl_to_jni.py")
android.RegisterModuleType("aidl_interface", aidlInterfaceFactory)
android.RegisterModuleType("aidl_mapping", aidlMappingFactory)
}
// wrap(p, a, s) = [p + v + s for v in a]
func wrap(prefix string, strs []string, suffix string) []string {
ret := make([]string, len(strs))
for i, v := range strs {
ret[i] = prefix + v + suffix
}
return ret
}
// concat(a...) = sum((i for i in a), [])
func concat(sstrs ...[]string) []string {
var ret []string
for _, v := range sstrs {
ret = append(ret, v...)
}
return ret
}
func isRelativePath(path string) bool {
if path == "" {
return true
}
return filepath.Clean(path) == path && path != ".." &&
!strings.HasPrefix(path, "../") && !strings.HasPrefix(path, "/")
}
type aidlGenProperties struct {
Srcs []string
AidlRoot string // base directory for the input aidl file
Imports []string
Lang string // target language [java|cpp|ndk]
BaseName string
GenLog bool
Version string
}
type aidlGenRule struct {
android.ModuleBase
properties aidlGenProperties
implicitInputs android.Paths
importFlags string
genOutDir android.ModuleGenPath
genHeaderDir android.ModuleGenPath
genOutputs android.WritablePaths
}
var _ android.SourceFileProducer = (*aidlGenRule)(nil)
var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil)
func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
genDirTimestamp := android.PathForModuleGen(ctx, "timestamp")
g.implicitInputs = append(g.implicitInputs, genDirTimestamp)
var importPaths []string
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)
}
}
})
g.importFlags = strings.Join(wrap("-I", importPaths, ""), " ")
srcs := android.PathsWithModuleSrcSubDir(ctx, android.PathsForModuleSrc(ctx, g.properties.Srcs), g.properties.AidlRoot)
g.genOutDir = android.PathForModuleGen(ctx)
g.genHeaderDir = android.PathForModuleGen(ctx, "include")
for _, src := range srcs {
g.genOutputs = append(g.genOutputs, g.generateBuildActionsForSingleAidl(ctx, src))
}
// This is to clean genOutDir before generating any file
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlDirPrepareRule,
Implicits: srcs,
Output: genDirTimestamp,
Args: map[string]string{
"outDir": g.genOutDir.String(),
},
})
}
func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) android.WritablePath {
var outFile android.WritablePath
if g.properties.Lang == langJava {
outFile = android.PathForModuleGen(ctx, pathtools.ReplaceExtension(src.Rel(), "java"))
} else {
outFile = android.PathForModuleGen(ctx, pathtools.ReplaceExtension(src.Rel(), "cpp"))
}
var optionalFlags []string
if g.properties.Version != "" {
optionalFlags = append(optionalFlags, "--version "+g.properties.Version)
}
if g.properties.Lang == langJava {
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlJavaRule,
Input: src,
Implicits: g.implicitInputs,
Output: outFile,
Args: map[string]string{
"imports": g.importFlags,
"outDir": g.genOutDir.String(),
"optionalFlags": strings.Join(optionalFlags, " "),
},
})
} else {
typeName := strings.TrimSuffix(filepath.Base(src.Rel()), ".aidl")
packagePath := filepath.Dir(src.Rel())
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"
}
var headers android.WritablePaths
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.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlCppRule,
Input: src,
Implicits: g.implicitInputs,
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
}
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.genOutputs.Paths()
}
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)...)
ctx.AddDependency(ctx.Module(), nil, g.properties.BaseName+aidlApiSuffix)
}
func aidlGenFactory() android.Module {
g := &aidlGenRule{}
g.AddProperties(&g.properties)
android.InitAndroidModule(g)
return g
}
type aidlApiProperties struct {
BaseName string
Inputs []string
Imports []string
Api_dir *string
Versions []string
AidlRoot string // base directory for the input aidl file
}
type aidlApi struct {
android.ModuleBase
properties aidlApiProperties
// for triggering api check for version X against version X-1
checkApiTimestamps android.WritablePaths
// for triggering freezing API as the new version
freezeApiTimestamp android.WritablePath
}
func (m *aidlApi) apiDir() string {
if m.properties.Api_dir != nil {
return *(m.properties.Api_dir)
} else {
return "api"
}
}
// Version of the interface at ToT if it is frozen
func (m *aidlApi) validateCurrentVersion(ctx android.ModuleContext) string {
if len(m.properties.Versions) == 0 {
return "1"
} else {
latestVersion := m.properties.Versions[len(m.properties.Versions)-1]
i, err := strconv.ParseInt(latestVersion, 10, 64)
if err != nil {
ctx.PropertyErrorf("versions", "must be integers")
return ""
}
return strconv.FormatInt(i+1, 10)
}
}
func (m *aidlApi) createApiDumpFromSource(ctx android.ModuleContext) (apiDir android.WritablePath, apiFiles android.WritablePaths) {
var importPaths []string
ctx.VisitDirectDeps(func(dep android.Module) {
if importedAidl, ok := dep.(*aidlInterface); ok {
importPaths = append(importPaths, importedAidl.properties.Full_import_paths...)
}
})
var srcs android.Paths
for _, input := range m.properties.Inputs {
path := android.PathForModuleSrc(ctx, input)
path = android.PathWithModuleSrcSubDir(ctx, path, m.properties.AidlRoot)
srcs = append(srcs, path)
}
apiDir = android.PathForModuleOut(ctx, "dump")
for _, src := range srcs {
apiFiles = append(apiFiles, android.PathForModuleOut(ctx, "dump", src.Rel()))
}
imports := strings.Join(wrap("-I", importPaths, ""), " ")
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlDumpApiRule,
Inputs: srcs,
Output: apiDir,
ImplicitOutputs: apiFiles,
Args: map[string]string{
"imports": imports,
},
})
return apiDir, apiFiles
}
func (m *aidlApi) freezeApiDumpAsVersion(ctx android.ModuleContext, apiDumpDir android.Path, apiFiles android.Paths, version string) android.WritablePath {
timestampFile := android.PathForModuleOut(ctx, "freezeapi_"+version+".timestamp")
modulePath := android.PathForModuleSrc(ctx).String()
var implicits android.Paths
implicits = append(implicits, apiFiles...)
apiPreamble := android.PathForSource(ctx, "system/tools/aidl/build/api_preamble.txt")
implicits = append(implicits, apiPreamble)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlFreezeApiRule,
Description: "Freezing AIDL API of " + m.properties.BaseName + " as version " + version,
Implicits: implicits,
Output: timestampFile,
Args: map[string]string{
"to": filepath.Join(modulePath, m.apiDir(), version),
"name": m.properties.BaseName,
"version": version,
"bp": android.PathForModuleSrc(ctx, "Android.bp").String(),
"apiPreamble": apiPreamble.String(),
},
})
return timestampFile
}
func (m *aidlApi) checkCompatibility(ctx android.ModuleContext, oldApiDir android.Path, oldApiFiles android.Paths, newApiDir android.Path, newApiFiles android.Paths) android.WritablePath {
newVersion := newApiDir.Base()
timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_compatibility.txt")
var implicits android.Paths
implicits = append(implicits, oldApiFiles...)
implicits = append(implicits, newApiFiles...)
implicits = append(implicits, messageFile)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlCheckApiRule,
Implicits: implicits,
Output: timestampFile,
Args: map[string]string{
"old": oldApiDir.String(),
"new": newApiDir.String(),
"messageFile": messageFile.String(),
},
})
return timestampFile
}
func (m *aidlApi) checkEquality(ctx android.ModuleContext, oldApiDir android.Path, oldApiFiles android.Paths, newApiDir android.Path, newApiFiles android.Paths) android.WritablePath {
newVersion := newApiDir.Base()
timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_equality.txt")
var implicits android.Paths
implicits = append(implicits, oldApiFiles...)
implicits = append(implicits, newApiFiles...)
implicits = append(implicits, messageFile)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlDiffApiRule,
Implicits: implicits,
Output: timestampFile,
Args: map[string]string{
"old": oldApiDir.String(),
"new": newApiDir.String(),
"messageFile": messageFile.String(),
},
})
return timestampFile
}
func (m *aidlApi) GenerateAndroidBuildActions(ctx android.ModuleContext) {
currentVersion := m.validateCurrentVersion(ctx)
if ctx.Failed() {
return
}
currentDumpDir, currentApiFiles := m.createApiDumpFromSource(ctx)
m.freezeApiTimestamp = m.freezeApiDumpAsVersion(ctx, currentDumpDir, currentApiFiles.Paths(), currentVersion)
apiDirs := make(map[string]android.Path)
apiFiles := make(map[string]android.Paths)
for _, ver := range m.properties.Versions {
apiDir := android.PathForModuleSrc(ctx, m.apiDir(), ver)
apiDirs[ver] = apiDir
apiFiles[ver] = ctx.Glob(filepath.Join(apiDir.String(), "**/*.aidl"), nil)
}
apiDirs[currentVersion] = currentDumpDir
apiFiles[currentVersion] = currentApiFiles.Paths()
// Check that version X is backward compatible with version X-1
for i, newVersion := range m.properties.Versions {
if i != 0 {
oldVersion := m.properties.Versions[i-1]
checkApiTimestamp := m.checkCompatibility(ctx, apiDirs[oldVersion], apiFiles[oldVersion], apiDirs[newVersion], apiFiles[newVersion])
m.checkApiTimestamps = append(m.checkApiTimestamps, checkApiTimestamp)
}
}
// ... and that the currentVersion (ToT) is backwards compatible with or
// equal to the latest frozen version
if len(m.properties.Versions) >= 1 {
latestVersion := m.properties.Versions[len(m.properties.Versions)-1]
var checkApiTimestamp android.WritablePath
if ctx.Config().DefaultAppTargetSdkInt() != android.FutureApiLevel {
// If API is frozen, don't allow any change to the API
checkApiTimestamp = m.checkEquality(ctx, apiDirs[latestVersion], apiFiles[latestVersion], apiDirs[currentVersion], apiFiles[currentVersion])
} else {
// If not, allow backwards compatible changes to the API
checkApiTimestamp = m.checkCompatibility(ctx, apiDirs[latestVersion], apiFiles[latestVersion], apiDirs[currentVersion], apiFiles[currentVersion])
}
m.checkApiTimestamps = append(m.checkApiTimestamps, checkApiTimestamp)
}
}
func (m *aidlApi) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
targetName := m.properties.BaseName + "-freeze-api"
fmt.Fprintln(w, ".PHONY:", targetName)
fmt.Fprintln(w, targetName+":", m.freezeApiTimestamp.String())
},
}
}
func (m *aidlApi) DepsMutator(ctx android.BottomUpMutatorContext) {
ctx.AddDependency(ctx.Module(), nil, wrap("", m.properties.Imports, aidlInterfaceSuffix)...)
}
func aidlApiFactory() android.Module {
m := &aidlApi{}
m.AddProperties(&m.properties)
android.InitAndroidModule(m)
return m
}
type aidlInterfaceProperties struct {
// Vndk properties for interface library only.
cc.VndkProperties
// Whether the library can be installed on the vendor image.
Vendor_available *bool
// Top level directories for includes.
// TODO(b/128940869): remove it if aidl_interface can depend on framework.aidl
Include_dirs []string
// Relative path for includes. By default assumes AIDL path is relative to current directory.
// TODO(b/111117220): automatically compute by letting AIDL parse multiple files simultaneously
Local_include_dir string
// The owner of the module
Owner *string
// List of .aidl files which compose this interface. These may be globbed.
Srcs []string
Imports []string
// Used by gen dependency to fill out aidl include path
Full_import_paths []string `blueprint:"mutated"`
// Directory where API dumps are. Default is "api".
Api_dir *string
// Previous API versions that are now frozen. The version that is last in
// the list is considered as the most recent version.
Versions []string
Backend struct {
Java struct {
// Whether to generate Java code using Java binder APIs
// Default: true
Enabled *bool
// Set to the version of the sdk to compile against
// Default: system_current
Sdk_version *string
}
Cpp struct {
// Whether to generate C++ code using C++ binder APIs
// Default: true
Enabled *bool
// Whether to generate additional code for gathering information
// about the transactions
// Default: false
Gen_log *bool
}
Ndk struct {
// Whether to generate C++ code using NDK binder APIs
// Default: true
Enabled *bool
// Whether to generate additional code for gathering information
// about the transactions
// Default: false
Gen_log *bool
}
}
}
type aidlInterface struct {
android.ModuleBase
properties aidlInterfaceProperties
// Unglobbed sources
rawSrcs []string
}
func (i *aidlInterface) shouldGenerateJavaBackend() bool {
// explicitly true if not specified to give early warning to devs
return i.properties.Backend.Java.Enabled == nil || *i.properties.Backend.Java.Enabled
}
func (i *aidlInterface) shouldGenerateCppBackend() bool {
// explicitly true if not specified to give early warning to devs
return i.properties.Backend.Cpp.Enabled == nil || *i.properties.Backend.Cpp.Enabled
}
func (i *aidlInterface) shouldGenerateNdkBackend() bool {
// explicitly true if not specified to give early warning to devs
return i.properties.Backend.Ndk.Enabled == nil || *i.properties.Backend.Ndk.Enabled
}
func (i *aidlInterface) checkAndUpdateSources(mctx android.LoadHookContext) {
prefix := mctx.ModuleDir()
for _, source := range i.properties.Srcs {
if pathtools.IsGlob(source) {
globbedSrcFiles, err := mctx.GlobWithDeps(filepath.Join(prefix, source), nil)
if err != nil {
mctx.ModuleErrorf("glob: %s", err.Error())
}
for _, globbedSrc := range globbedSrcFiles {
relativeGlobbedSrc, err := filepath.Rel(prefix, globbedSrc)
if err != nil {
panic(err)
}
i.rawSrcs = append(i.rawSrcs, relativeGlobbedSrc)
}
} else {
i.rawSrcs = append(i.rawSrcs, source)
}
}
if len(i.rawSrcs) == 0 {
mctx.PropertyErrorf("srcs", "No sources provided.")
}
for _, source := range i.rawSrcs {
if !strings.HasSuffix(source, ".aidl") {
mctx.PropertyErrorf("srcs", "Source must be a .aidl file: "+source)
continue
}
relativePath, err := filepath.Rel(i.properties.Local_include_dir, source)
if err != nil || !isRelativePath(relativePath) {
mctx.PropertyErrorf("srcs", "Source is not in local_include_dir: "+source)
}
}
}
func (i *aidlInterface) checkImports(mctx android.LoadHookContext) {
for _, anImport := range i.properties.Imports {
other := lookupInterface(anImport)
if other == nil {
mctx.PropertyErrorf("imports", "Import does not exist: "+anImport)
}
if i.shouldGenerateCppBackend() && !other.shouldGenerateCppBackend() {
mctx.PropertyErrorf("backend.cpp.enabled",
"C++ backend not enabled in the imported AIDL interface %q", anImport)
}
if i.shouldGenerateNdkBackend() && !other.shouldGenerateNdkBackend() {
mctx.PropertyErrorf("backend.ndk.enabled",
"NDK backend not enabled in the imported AIDL interface %q", anImport)
}
}
}
func (i *aidlInterface) versionedName(version string) string {
name := i.ModuleBase.Name()
if version != futureVersion && version != "" {
name = name + "-V" + version
}
return name
}
func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, base string) {
if version == futureVersion || version == "" {
return i.rawSrcs, i.properties.Local_include_dir
} else {
var apiDir string
if i.properties.Api_dir != nil {
apiDir = *(i.properties.Api_dir)
} else {
apiDir = "api"
}
base = filepath.Join(apiDir, version)
full_paths, err := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), base, "**/*.aidl"), nil)
if err != nil {
panic(err)
}
for _, path := range full_paths {
// Here, we need path local to the module
srcs = append(srcs, strings.TrimPrefix(path, mctx.ModuleDir()+"/"))
}
return srcs, base
}
}
func aidlInterfaceHook(mctx android.LoadHookContext, i *aidlInterface) {
if !isRelativePath(i.properties.Local_include_dir) {
mctx.PropertyErrorf("local_include_dir", "must be relative path: "+i.properties.Local_include_dir)
}
var importPaths []string
importPaths = append(importPaths, filepath.Join(mctx.ModuleDir(), i.properties.Local_include_dir))
importPaths = append(importPaths, i.properties.Include_dirs...)
i.properties.Full_import_paths = importPaths
i.checkAndUpdateSources(mctx)
i.checkImports(mctx)
if mctx.Failed() {
return
}
var libs []string
currentVersion := ""
if len(i.properties.Versions) > 0 {
currentVersion = futureVersion
}
if i.shouldGenerateCppBackend() {
libs = append(libs, addCppLibrary(mctx, i, currentVersion, langCpp))
for _, version := range i.properties.Versions {
addCppLibrary(mctx, i, version, langCpp)
}
}
if i.shouldGenerateNdkBackend() {
// TODO(b/119771576): inherit properties and export 'is vendor' computation from cc.go
if !proptools.Bool(i.properties.Vendor_available) {
libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdk))
for _, version := range i.properties.Versions {
addCppLibrary(mctx, i, version, langNdk)
}
}
// TODO(b/121157555): combine with '-ndk' variant
libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdkPlatform))
for _, version := range i.properties.Versions {
addCppLibrary(mctx, i, version, langNdkPlatform)
}
}
libs = append(libs, addJavaLibrary(mctx, i, currentVersion))
for _, version := range i.properties.Versions {
addJavaLibrary(mctx, i, version)
}
addApiModule(mctx, i)
// Reserve this module name for future use
mctx.CreateModule(android.ModuleFactoryAdaptor(phony.PhonyFactory), &phonyProperties{
Name: proptools.StringPtr(i.ModuleBase.Name()),
Required: libs,
})
}
func addCppLibrary(mctx android.LoadHookContext, i *aidlInterface, version string, lang string) string {
cppSourceGen := i.versionedName(version) + "-" + lang + "-source"
cppModuleGen := i.versionedName(version) + "-" + lang
srcs, base := i.srcsForVersion(mctx, version)
if len(srcs) == 0 {
// This can happen when the version is about to be frozen; the version
// directory is created but API dump hasn't been copied there.
// Don't create a library for the yet-to-be-frozen version.
return ""
}
genLog := false
if lang == langCpp {
genLog = proptools.Bool(i.properties.Backend.Cpp.Gen_log)
} else if lang == langNdk || lang == langNdkPlatform {
genLog = proptools.Bool(i.properties.Backend.Ndk.Gen_log)
}
mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
Name: proptools.StringPtr(cppSourceGen),
}, &aidlGenProperties{
Srcs: srcs,
AidlRoot: base,
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
Lang: lang,
BaseName: i.ModuleBase.Name(),
GenLog: genLog,
Version: version,
})
importExportDependencies := wrap("", i.properties.Imports, "-"+lang)
var libJSONCppDependency []string
var staticLibDependency []string
var sdkVersion *string
var stl *string
var cpp_std *string
if lang == langCpp {
importExportDependencies = append(importExportDependencies, "libbinder", "libutils")
if genLog {
libJSONCppDependency = []string{"libjsoncpp"}
}
sdkVersion = nil
stl = nil
cpp_std = nil
} else if lang == langNdk {
importExportDependencies = append(importExportDependencies, "libbinder_ndk")
if genLog {
staticLibDependency = []string{"libjsoncpp_ndk"}
}
sdkVersion = proptools.StringPtr("current")
stl = proptools.StringPtr("c++_shared")
} else if lang == langNdkPlatform {
importExportDependencies = append(importExportDependencies, "libbinder_ndk")
if genLog {
libJSONCppDependency = []string{"libjsoncpp"}
}
} else {
panic("Unrecognized language: " + lang)
}
mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
Name: proptools.StringPtr(cppModuleGen),
Owner: i.properties.Owner,
Vendor_available: i.properties.Vendor_available,
Defaults: []string{"aidl-cpp-module-defaults"},
Generated_sources: []string{cppSourceGen},
Generated_headers: []string{cppSourceGen},
Export_generated_headers: []string{cppSourceGen},
Static: staticLib{Whole_static_libs: libJSONCppDependency},
Shared: sharedLib{Shared_libs: libJSONCppDependency, Export_shared_lib_headers: libJSONCppDependency},
Static_libs: staticLibDependency,
Shared_libs: importExportDependencies,
Export_shared_lib_headers: importExportDependencies,
Sdk_version: sdkVersion,
Stl: stl,
Cpp_std: cpp_std,
Cflags: []string{"-Wextra", "-Wall", "-Werror"},
}, &i.properties.VndkProperties)
return cppModuleGen
}
func addJavaLibrary(mctx android.LoadHookContext, i *aidlInterface, version string) string {
javaSourceGen := i.versionedName(version) + "-java-source"
javaModuleGen := i.versionedName(version) + "-java"
srcs, base := i.srcsForVersion(mctx, version)
if len(srcs) == 0 {
// This can happen when the version is about to be frozen; the version
// directory is created but API dump hasn't been copied there.
// Don't create a library for the yet-to-be-frozen version.
return ""
}
sdkVersion := proptools.StringDefault(i.properties.Backend.Java.Sdk_version, "system_current")
mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
Name: proptools.StringPtr(javaSourceGen),
}, &aidlGenProperties{
Srcs: srcs,
AidlRoot: base,
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
Lang: langJava,
BaseName: i.ModuleBase.Name(),
Version: version,
})
mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory), &javaProperties{
Name: proptools.StringPtr(javaModuleGen),
Owner: i.properties.Owner,
Installable: proptools.BoolPtr(true),
Defaults: []string{"aidl-java-module-defaults"},
No_framework_libs: proptools.BoolPtr(true),
Sdk_version: proptools.StringPtr(sdkVersion),
Static_libs: wrap("", i.properties.Imports, "-java"),
Srcs: []string{":" + javaSourceGen},
})
return javaModuleGen
}
func addApiModule(mctx android.LoadHookContext, i *aidlInterface) string {
apiModule := i.ModuleBase.Name() + aidlApiSuffix
mctx.CreateModule(android.ModuleFactoryAdaptor(aidlApiFactory), &nameProperties{
Name: proptools.StringPtr(apiModule),
}, &aidlApiProperties{
BaseName: i.ModuleBase.Name(),
Inputs: i.rawSrcs,
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
Api_dir: i.properties.Api_dir,
AidlRoot: i.properties.Local_include_dir,
Versions: i.properties.Versions,
})
return apiModule
}
func (i *aidlInterface) Name() string {
return i.ModuleBase.Name() + aidlInterfaceSuffix
}
func (i *aidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
func (i *aidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) {
}
var aidlInterfaceMutex sync.Mutex
var aidlInterfaces []*aidlInterface
func aidlInterfaceFactory() android.Module {
i := &aidlInterface{}
i.AddProperties(&i.properties)
android.InitAndroidModule(i)
android.AddLoadHook(i, func(ctx android.LoadHookContext) { aidlInterfaceHook(ctx, i) })
aidlInterfaceMutex.Lock()
aidlInterfaces = append(aidlInterfaces, i)
aidlInterfaceMutex.Unlock()
return i
}
func lookupInterface(name string) *aidlInterface {
for _, i := range aidlInterfaces {
if i.ModuleBase.Name() == name {
return i
}
}
return nil
}
type aidlMappingProperties struct {
// Source file of this prebuilt.
Srcs []string `android:"arch_variant"`
Output string
}
type aidlMapping struct {
android.ModuleBase
properties aidlMappingProperties
outputFilePath android.WritablePath
}
func (s *aidlMapping) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ExtractSourcesDeps(ctx, s.properties.Srcs)
}
func addItemsToMap(dest map[string]bool, src []string) {
for _, item := range src {
dest[item] = true
}
}
func (s *aidlMapping) GenerateAndroidBuildActions(ctx android.ModuleContext) {
var srcs android.Paths
var all_import_dirs map[string]bool = make(map[string]bool)
ctx.VisitDirectDeps(func(module android.Module) {
for _, property := range module.GetProperties() {
if jproperty, ok := property.(*java.CompilerProperties); ok {
for _, src := range jproperty.Srcs {
if strings.HasSuffix(src, ".aidl") {
full_path := android.PathForModuleSrc(ctx, src)
srcs = append(srcs, full_path)
all_import_dirs[filepath.Dir(full_path.String())] = true
} else if pathtools.IsGlob(src) {
globbedSrcFiles, err := ctx.GlobWithDeps(src, nil)
if err == nil {
for _, globbedSrc := range globbedSrcFiles {
full_path := android.PathForModuleSrc(ctx, globbedSrc)
all_import_dirs[full_path.String()] = true
}
}
}
}
} else if jproperty, ok := property.(*java.CompilerDeviceProperties); ok {
addItemsToMap(all_import_dirs, jproperty.Aidl.Include_dirs)
for _, include_dir := range jproperty.Aidl.Export_include_dirs {
var full_path = filepath.Join(ctx.ModuleDir(), include_dir)
all_import_dirs[full_path] = true
}
for _, include_dir := range jproperty.Aidl.Local_include_dirs {
var full_path = filepath.Join(ctx.ModuleSubDir(), include_dir)
all_import_dirs[full_path] = true
}
}
}
})
var import_dirs []string
for dir := range all_import_dirs {
import_dirs = append(import_dirs, dir)
}
imports := strings.Join(wrap("-I", import_dirs, ""), " ")
s.outputFilePath = android.PathForModuleOut(ctx, s.properties.Output)
outDir := android.PathForModuleGen(ctx)
ctx.Build(pctx, android.BuildParams{
Rule: aidlDumpMappingsRule,
Inputs: srcs,
Output: s.outputFilePath,
Args: map[string]string{
"imports": imports,
"outDir": outDir.String(),
},
})
}
func InitAidlMappingModule(s *aidlMapping) {
s.AddProperties(&s.properties)
}
func aidlMappingFactory() android.Module {
module := &aidlMapping{}
InitAidlMappingModule(module)
android.InitAndroidModule(module)
return module
}
func (m *aidlMapping) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
targetName := m.Name()
fmt.Fprintln(w, ".PHONY:", targetName)
fmt.Fprintln(w, targetName+":", m.outputFilePath.String())
},
}
}