blob: 118f9bdf9d3a49108dc8d902377cb51aa7a8ff0e [file] [log] [blame]
// 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 (
"path/filepath"
"strings"
"sync"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/cc"
"android/soong/genrule"
"android/soong/java"
"android/soong/phony"
)
var (
aidlInterfaceSuffix = "_interface"
pctx = android.NewPackageContext("android/aidl")
aidlCpp = pctx.HostBinToolVariable("aidlCpp", "aidl-cpp")
aidlCppRule = pctx.StaticRule("aidlCppRule", blueprint.RuleParams{
Command: "${aidlCpp} --structured ${imports} ${in} ${headerDir} ${cppFile}",
CommandDeps: []string{"${aidlCpp}"},
Description: "AIDL CPP ${in} => ${out}",
}, "imports", "headerDir", "cppFile")
aidlJava = pctx.HostBinToolVariable("aidlJava", "aidl")
aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
Command: "${aidlJava} --structured --ninja -d${out}.d ${imports} ${in} ${out}",
CommandDeps: []string{"${aidlJava}"},
Description: "AIDL Java ${in} => ${out}",
}, "imports")
)
func init() {
android.RegisterModuleType("aidl_interface", aidlInterfaceFactory)
}
// 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 {
Input string // a single aidl file
Outputs []string
Imports []string
CppFile *string // if specified, generates cpp
}
type aidlGenRule struct {
android.ModuleBase
properties aidlGenProperties
genHeaderDir android.Path
genInput android.Path
genOutputs android.WritablePaths
}
var _ android.SourceFileProducer = (*aidlGenRule)(nil)
var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil)
func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
g.genInput = android.PathForModuleSrc(ctx, g.properties.Input)
g.genHeaderDir = android.PathForModuleGen(ctx)
for _, output := range g.properties.Outputs {
g.genOutputs = append(g.genOutputs, android.PathForModuleGen(ctx, output))
}
var importPaths []string
ctx.VisitDirectDeps(func(dep android.Module) {
importPaths = append(importPaths, dep.(*aidlInterface).properties.Full_import_path)
})
imports := strings.Join(wrap("-I", importPaths, ""), " ")
if g.properties.CppFile == nil {
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlJavaRule,
Input: g.genInput,
Outputs: g.genOutputs,
Args: map[string]string{
"imports": imports,
},
})
} else {
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlCppRule,
Input: g.genInput,
Outputs: g.genOutputs,
Args: map[string]string{
"imports": imports,
"headerDir": g.genHeaderDir.String(),
"cppFile": android.PathForModuleGen(ctx, *g.properties.CppFile).String(),
},
})
}
}
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)...)
}
func aidlGenFactory() android.Module {
g := &aidlGenRule{}
g.AddProperties(&g.properties)
android.InitAndroidModule(g)
return g
}
type aidlInterfaceProperties struct {
// Vndk properties for interface library only.
cc.VndkProperties
// Whether the library can be installed on the vendor image.
Vendor_available *bool
// 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.
Srcs []string
Imports []string
// Whether to generate cpp.
// Default: true
Gen_cpp *bool
// Used by gen dependency to fill out aidl include path
Full_import_path string `blueprint:"mutated"`
}
type aidlInterface struct {
android.ModuleBase
properties aidlInterfaceProperties
// For a corresponding .aidl source, example: "IFoo"
types []string
// For a corresponding .aidl source, example: "some/package/path"
packagePaths []string
}
func (i *aidlInterface) shouldGenerateCpp() bool {
// explicitly true if not specified to give early warning to devs
return i.properties.Gen_cpp == nil || *i.properties.Gen_cpp
}
func (i *aidlInterface) checkAndUpdateSources(mctx android.LoadHookContext) {
if len(i.properties.Srcs) == 0 {
mctx.PropertyErrorf("srcs", "No sources provided.")
}
for _, source := range i.properties.Srcs {
if !strings.HasSuffix(source, ".aidl") {
mctx.PropertyErrorf("srcs", "Source must be a .aidl file: "+source)
continue
}
name := strings.TrimSuffix(source, ".aidl")
i.types = append(i.types, filepath.Base(name))
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)
}
i.packagePaths = append(i.packagePaths, filepath.Dir(relativePath))
}
}
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.shouldGenerateCpp() && !other.shouldGenerateCpp() {
mctx.PropertyErrorf("imports", "Import of gen C++ module must generate C++:"+anImport)
}
}
}
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)
}
i.properties.Full_import_path = filepath.Join(mctx.ModuleDir(), i.properties.Local_include_dir)
i.checkAndUpdateSources(mctx)
i.checkImports(mctx)
if mctx.Failed() {
return
}
var libs []string
if i.shouldGenerateCpp() {
libs = append(libs, addCppLibrary(mctx, i))
}
libs = append(libs, addJavaLibrary(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) string {
cppSourceGen := i.ModuleBase.Name() + "-cpp-gen"
cppHeaderGen := i.ModuleBase.Name() + "-cpp-gen-headers"
cppModuleGen := i.ModuleBase.Name() + "-cpp"
var cppGeneratedSources []string
var cppGeneratedHeaders []string
for idx, source := range i.properties.Srcs {
packagePath := i.packagePaths[idx]
typeName := i.types[idx]
// TODO(b/111362593): generate_cpp.cpp uses heuristics to figure out if
// an interface name has a leading I. Those same heuristics have been
// moved here.
baseName := typeName
if len(baseName) >= 2 && baseName[0] == 'I' && strings.ToUpper(baseName)[1] == baseName[1] {
baseName = strings.TrimPrefix(typeName, "I")
}
cppFile := filepath.Join(packagePath, typeName+".cpp")
headerFile := filepath.Join(packagePath, typeName+".h")
bpFile := filepath.Join(packagePath, "Bp"+baseName+".h")
bnFile := filepath.Join(packagePath, "Bn"+baseName+".h")
cppSourceGenName := cppSourceGen + "-" + typeName
mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
Name: proptools.StringPtr(cppSourceGenName),
}, &aidlGenProperties{
Input: source,
Outputs: []string{cppFile},
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
CppFile: proptools.StringPtr(cppFile),
})
cppGeneratedSources = append(cppGeneratedSources, cppSourceGenName)
cppHeaderGenName := cppHeaderGen + "-" + typeName
mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
Name: proptools.StringPtr(cppHeaderGenName),
}, &aidlGenProperties{
Input: source,
Outputs: []string{headerFile, bpFile, bnFile},
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
CppFile: proptools.StringPtr(cppFile),
})
cppGeneratedHeaders = append(cppGeneratedHeaders, cppHeaderGenName)
}
importExportDependencies := concat([]string{
"libbinder",
"libutils",
}, wrap("", i.properties.Imports, "-cpp"))
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: cppGeneratedSources,
Generated_headers: cppGeneratedHeaders,
Export_generated_headers: cppGeneratedHeaders,
Shared_libs: importExportDependencies,
Export_shared_lib_headers: importExportDependencies,
}, &i.properties.VndkProperties)
return cppModuleGen
}
func addJavaLibrary(mctx android.LoadHookContext, i *aidlInterface) string {
javaSourceGen := i.ModuleBase.Name() + "-java-gen"
javaModuleGen := i.ModuleBase.Name() + "-java"
var javaGeneratedSources []string
for idx, source := range i.properties.Srcs {
packagePath := i.packagePaths[idx]
typeName := i.types[idx]
javaFile := filepath.Join(packagePath, typeName+".java")
javaSourceGenName := javaSourceGen + "-" + typeName
mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
Name: proptools.StringPtr(javaSourceGenName),
}, &aidlGenProperties{
Input: source,
Outputs: []string{javaFile},
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
})
javaGeneratedSources = append(javaGeneratedSources, javaSourceGenName)
}
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("current"),
Static_libs: wrap("", i.properties.Imports, "-java"),
Srcs: wrap(":", javaGeneratedSources, ""),
})
return javaModuleGen
}
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
}