platform_build_soong/java/java.go
Colin Cross c99deeb961 Use blueprint DependencyTags
Blueprint now requres DependencyTags to be passed to AddDependency
calls.  Use the tags to avoid setting member variables on modules, which
will be lost after the mutators have been called.

Change-Id: I8c1d9ed1db85a300e14394b911a516d361ba9f75
2016-04-12 15:24:02 -07:00

604 lines
18 KiB
Go

// Copyright 2015 Google Inc. All rights reserved.
//
// 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 java
// This file contains the module types for compiling Java for Android, and converts the properties
// into the flags and filenames necessary to pass to the compiler. The final creation of the rules
// is handled in builder.go
import (
"fmt"
"strings"
"github.com/google/blueprint"
"android/soong"
"android/soong/common"
"android/soong/genrule"
)
func init() {
soong.RegisterModuleType("java_library", JavaLibraryFactory)
soong.RegisterModuleType("java_library_static", JavaLibraryFactory)
soong.RegisterModuleType("java_library_host", JavaLibraryHostFactory)
soong.RegisterModuleType("java_binary", JavaBinaryFactory)
soong.RegisterModuleType("java_binary_host", JavaBinaryHostFactory)
soong.RegisterModuleType("prebuilt_java_library", JavaPrebuiltFactory)
soong.RegisterModuleType("prebuilt_sdk", SdkPrebuiltFactory)
soong.RegisterModuleType("android_app", AndroidAppFactory)
soong.RegisterSingletonType("logtags", LogtagsSingleton)
}
// TODO:
// Autogenerated files:
// Proto
// Renderscript
// Post-jar passes:
// Proguard
// Emma
// Jarjar
// Dex
// Rmtypedefs
// Jack
// DroidDoc
// Findbugs
type javaBaseProperties struct {
// list of source files used to compile the Java module. May be .java, .logtags, .proto,
// or .aidl files.
Srcs []string `android:"arch_variant"`
// list of source files that should not be used to build the Java module.
// This is most useful in the arch/multilib variants to remove non-common files
Exclude_srcs []string `android:"arch_variant"`
// list of directories containing Java resources
Java_resource_dirs []string `android:"arch_variant"`
// list of directories that should be excluded from java_resource_dirs
Exclude_java_resource_dirs []string `android:"arch_variant"`
// don't build against the default libraries (core-libart, core-junit,
// ext, and framework for device targets)
No_standard_libraries bool
// list of module-specific flags that will be used for javac compiles
Javacflags []string `android:"arch_variant"`
// list of module-specific flags that will be used for jack compiles
Jack_flags []string `android:"arch_variant"`
// list of module-specific flags that will be used for dex compiles
Dxflags []string `android:"arch_variant"`
// list of of java libraries that will be in the classpath
Java_libs []string `android:"arch_variant"`
// list of java libraries that will be compiled into the resulting jar
Java_static_libs []string `android:"arch_variant"`
// manifest file to be included in resulting jar
Manifest *string
// if not blank, set to the version of the sdk to compile against
Sdk_version string
// Set for device java libraries, and for host versions of device java libraries
// built for testing
Dex bool `blueprint:"mutated"`
// if not blank, run jarjar using the specified rules file
Jarjar_rules *string
// directories to pass to aidl tool
Aidl_includes []string
// directories that should be added as include directories
// for any aidl sources of modules that depend on this module
Export_aidl_include_dirs []string
}
// javaBase contains the properties and members used by all java module types, and implements
// the blueprint.Module interface.
type javaBase struct {
common.AndroidModuleBase
module JavaModuleType
properties javaBaseProperties
// output file suitable for inserting into the classpath of another compile
classpathFile common.Path
// output file suitable for installing or running
outputFile common.Path
// jarSpecs suitable for inserting classes from a static library into another jar
classJarSpecs []jarSpec
// jarSpecs suitable for inserting resources from a static library into another jar
resourceJarSpecs []jarSpec
exportAidlIncludeDirs common.Paths
logtagsSrcs common.Paths
// filelists of extra source files that should be included in the javac command line,
// for example R.java generated by aapt for android apps
ExtraSrcLists common.Paths
// installed file for binary dependency
installFile common.Path
}
type AndroidJavaModuleContext common.AndroidBaseContext
type JavaModuleType interface {
GenerateJavaBuildActions(ctx common.AndroidModuleContext)
JavaDependencies(ctx AndroidJavaModuleContext) []string
}
type JavaDependency interface {
ClasspathFile() common.Path
ClassJarSpecs() []jarSpec
ResourceJarSpecs() []jarSpec
AidlIncludeDirs() common.Paths
}
func NewJavaBase(base *javaBase, module JavaModuleType, hod common.HostOrDeviceSupported,
props ...interface{}) (blueprint.Module, []interface{}) {
base.module = module
props = append(props, &base.properties)
return common.InitAndroidArchModule(base, hod, common.MultilibCommon, props...)
}
func (j *javaBase) BootClasspath(ctx common.AndroidBaseContext) string {
if ctx.Device() {
if j.properties.Sdk_version == "" {
return "core-libart"
} else if j.properties.Sdk_version == "current" {
// TODO: !TARGET_BUILD_APPS
// TODO: export preprocessed framework.aidl from android_stubs_current
return "android_stubs_current"
} else if j.properties.Sdk_version == "system_current" {
return "android_system_stubs_current"
} else {
return "sdk_v" + j.properties.Sdk_version
}
} else {
if j.properties.Dex {
return "core-libart"
} else {
return ""
}
}
}
var defaultJavaLibraries = []string{"core-libart", "core-junit", "ext", "framework"}
func javaDepsMutator(ctx common.AndroidBottomUpMutatorContext) {
if j, ok := ctx.Module().(JavaModuleType); ok {
ctx.AddDependency(ctx.Module(), nil, j.JavaDependencies(ctx)...)
}
}
func (j *javaBase) JavaDependencies(ctx AndroidJavaModuleContext) []string {
var deps []string
if !j.properties.No_standard_libraries {
bootClasspath := j.BootClasspath(ctx)
if bootClasspath != "" {
deps = append(deps, bootClasspath)
}
if ctx.Device() && j.properties.Sdk_version == "" {
deps = append(deps, defaultJavaLibraries...)
}
}
deps = append(deps, j.properties.Java_libs...)
deps = append(deps, j.properties.Java_static_libs...)
return deps
}
func (j *javaBase) aidlFlags(ctx common.AndroidModuleContext, aidlPreprocess common.OptionalPath,
aidlIncludeDirs common.Paths) []string {
localAidlIncludes := common.PathsForModuleSrc(ctx, j.properties.Aidl_includes)
var flags []string
if aidlPreprocess.Valid() {
flags = append(flags, "-p"+aidlPreprocess.String())
} else {
flags = append(flags, common.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
}
flags = append(flags, common.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
flags = append(flags, common.JoinWithPrefix(localAidlIncludes.Strings(), "-I"))
flags = append(flags, "-I"+common.PathForModuleSrc(ctx).String())
flags = append(flags, "-I"+common.PathForModuleSrc(ctx, "src").String())
return flags
}
func (j *javaBase) collectDeps(ctx common.AndroidModuleContext) (classpath common.Paths,
bootClasspath common.OptionalPath, classJarSpecs, resourceJarSpecs []jarSpec, aidlPreprocess common.OptionalPath,
aidlIncludeDirs common.Paths, srcFileLists common.Paths) {
ctx.VisitDirectDeps(func(module blueprint.Module) {
otherName := ctx.OtherModuleName(module)
if javaDep, ok := module.(JavaDependency); ok {
if otherName == j.BootClasspath(ctx) {
bootClasspath = common.OptionalPathForPath(javaDep.ClasspathFile())
} else if inList(otherName, defaultJavaLibraries) {
classpath = append(classpath, javaDep.ClasspathFile())
} else if inList(otherName, j.properties.Java_libs) {
classpath = append(classpath, javaDep.ClasspathFile())
} else if inList(otherName, j.properties.Java_static_libs) {
classpath = append(classpath, javaDep.ClasspathFile())
classJarSpecs = append(classJarSpecs, javaDep.ClassJarSpecs()...)
resourceJarSpecs = append(resourceJarSpecs, javaDep.ResourceJarSpecs()...)
} else if otherName == "framework-res" {
if ctx.ModuleName() == "framework" {
// framework.jar has a one-off dependency on the R.java and Manifest.java files
// generated by framework-res.apk
srcFileLists = append(srcFileLists, module.(*javaBase).module.(*AndroidApp).aaptJavaFileList)
}
} else {
panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
}
aidlIncludeDirs = append(aidlIncludeDirs, javaDep.AidlIncludeDirs()...)
if sdkDep, ok := module.(sdkDependency); ok {
if sdkDep.AidlPreprocessed().Valid() {
if aidlPreprocess.Valid() {
ctx.ModuleErrorf("multiple dependencies with preprocessed aidls:\n %q\n %q",
aidlPreprocess, sdkDep.AidlPreprocessed())
} else {
aidlPreprocess = sdkDep.AidlPreprocessed()
}
}
}
}
})
return classpath, bootClasspath, classJarSpecs, resourceJarSpecs, aidlPreprocess,
aidlIncludeDirs, srcFileLists
}
func (j *javaBase) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
j.module.GenerateJavaBuildActions(ctx)
}
func (j *javaBase) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
j.exportAidlIncludeDirs = common.PathsForModuleSrc(ctx, j.properties.Export_aidl_include_dirs)
classpath, bootClasspath, classJarSpecs, resourceJarSpecs, aidlPreprocess,
aidlIncludeDirs, srcFileLists := j.collectDeps(ctx)
var flags javaBuilderFlags
javacFlags := j.properties.Javacflags
if len(javacFlags) > 0 {
ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
flags.javacFlags = "$javacFlags"
}
aidlFlags := j.aidlFlags(ctx, aidlPreprocess, aidlIncludeDirs)
if len(aidlFlags) > 0 {
ctx.Variable(pctx, "aidlFlags", strings.Join(aidlFlags, " "))
flags.aidlFlags = "$aidlFlags"
}
var javacDeps common.Paths
if bootClasspath.Valid() {
flags.bootClasspath = "-bootclasspath " + bootClasspath.String()
javacDeps = append(javacDeps, bootClasspath.Path())
}
if len(classpath) > 0 {
flags.classpath = "-classpath " + strings.Join(classpath.Strings(), ":")
javacDeps = append(javacDeps, classpath...)
}
srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
srcFiles = j.genSources(ctx, srcFiles, flags)
ctx.VisitDirectDeps(func(module blueprint.Module) {
if gen, ok := module.(genrule.SourceFileGenerator); ok {
srcFiles = append(srcFiles, gen.GeneratedSourceFiles()...)
}
})
srcFileLists = append(srcFileLists, j.ExtraSrcLists...)
if len(srcFiles) > 0 {
// Compile java sources into .class files
classes := TransformJavaToClasses(ctx, srcFiles, srcFileLists, flags, javacDeps)
if ctx.Failed() {
return
}
classJarSpecs = append([]jarSpec{classes}, classJarSpecs...)
}
resourceJarSpecs = append(ResourceDirsToJarSpecs(ctx, j.properties.Java_resource_dirs, j.properties.Exclude_java_resource_dirs),
resourceJarSpecs...)
manifest := common.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
allJarSpecs := append([]jarSpec(nil), classJarSpecs...)
allJarSpecs = append(allJarSpecs, resourceJarSpecs...)
// Combine classes + resources into classes-full-debug.jar
outputFile := TransformClassesToJar(ctx, allJarSpecs, manifest)
if ctx.Failed() {
return
}
if j.properties.Jarjar_rules != nil {
jarjar_rules := common.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
// Transform classes-full-debug.jar into classes-jarjar.jar
outputFile = TransformJarJar(ctx, outputFile, jarjar_rules)
if ctx.Failed() {
return
}
classes, _ := TransformPrebuiltJarToClasses(ctx, outputFile)
classJarSpecs = []jarSpec{classes}
}
j.resourceJarSpecs = resourceJarSpecs
j.classJarSpecs = classJarSpecs
j.classpathFile = outputFile
if j.properties.Dex && len(srcFiles) > 0 {
dxFlags := j.properties.Dxflags
if false /* emma enabled */ {
// If you instrument class files that have local variable debug information in
// them emma does not correctly maintain the local variable table.
// This will cause an error when you try to convert the class files for Android.
// The workaround here is to build different dex file here based on emma switch
// then later copy into classes.dex. When emma is on, dx is run with --no-locals
// option to remove local variable information
dxFlags = append(dxFlags, "--no-locals")
}
if ctx.AConfig().Getenv("NO_OPTIMIZE_DX") != "" {
dxFlags = append(dxFlags, "--no-optimize")
}
if ctx.AConfig().Getenv("GENERATE_DEX_DEBUG") != "" {
dxFlags = append(dxFlags,
"--debug",
"--verbose",
"--dump-to="+common.PathForModuleOut(ctx, "classes.lst").String(),
"--dump-width=1000")
}
flags.dxFlags = strings.Join(dxFlags, " ")
// Compile classes.jar into classes.dex
dexJarSpec := TransformClassesJarToDex(ctx, outputFile, flags)
if ctx.Failed() {
return
}
// Combine classes.dex + resources into javalib.jar
outputFile = TransformDexToJavaLib(ctx, resourceJarSpecs, dexJarSpec)
}
ctx.CheckbuildFile(outputFile)
j.outputFile = outputFile
}
var _ JavaDependency = (*JavaLibrary)(nil)
func (j *javaBase) ClasspathFile() common.Path {
return j.classpathFile
}
func (j *javaBase) ClassJarSpecs() []jarSpec {
return j.classJarSpecs
}
func (j *javaBase) ResourceJarSpecs() []jarSpec {
return j.resourceJarSpecs
}
func (j *javaBase) AidlIncludeDirs() common.Paths {
return j.exportAidlIncludeDirs
}
var _ logtagsProducer = (*javaBase)(nil)
func (j *javaBase) logtags() common.Paths {
return j.logtagsSrcs
}
//
// Java libraries (.jar file)
//
type JavaLibrary struct {
javaBase
}
func (j *JavaLibrary) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
j.javaBase.GenerateJavaBuildActions(ctx)
j.installFile = ctx.InstallFileName(common.PathForModuleInstall(ctx, "framework"), ctx.ModuleName()+".jar", j.outputFile)
}
func JavaLibraryFactory() (blueprint.Module, []interface{}) {
module := &JavaLibrary{}
module.properties.Dex = true
return NewJavaBase(&module.javaBase, module, common.HostAndDeviceSupported)
}
func JavaLibraryHostFactory() (blueprint.Module, []interface{}) {
module := &JavaLibrary{}
return NewJavaBase(&module.javaBase, module, common.HostSupported)
}
//
// Java Binaries (.jar file plus wrapper script)
//
type javaBinaryProperties struct {
// installable script to execute the resulting jar
Wrapper string
}
type JavaBinary struct {
JavaLibrary
binaryProperties javaBinaryProperties
}
func (j *JavaBinary) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
j.JavaLibrary.GenerateJavaBuildActions(ctx)
// Depend on the installed jar (j.installFile) so that the wrapper doesn't get executed by
// another build rule before the jar has been installed.
ctx.InstallFile(common.PathForModuleInstall(ctx, "bin"), common.PathForModuleSrc(ctx, j.binaryProperties.Wrapper),
j.installFile)
}
func JavaBinaryFactory() (blueprint.Module, []interface{}) {
module := &JavaBinary{}
module.properties.Dex = true
return NewJavaBase(&module.javaBase, module, common.HostAndDeviceSupported, &module.binaryProperties)
}
func JavaBinaryHostFactory() (blueprint.Module, []interface{}) {
module := &JavaBinary{}
return NewJavaBase(&module.javaBase, module, common.HostSupported, &module.binaryProperties)
}
//
// Java prebuilts
//
type javaPrebuiltProperties struct {
Srcs []string
}
type JavaPrebuilt struct {
common.AndroidModuleBase
properties javaPrebuiltProperties
classpathFile common.Path
classJarSpecs, resourceJarSpecs []jarSpec
}
func (j *JavaPrebuilt) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
if len(j.properties.Srcs) != 1 {
ctx.ModuleErrorf("expected exactly one jar in srcs")
return
}
prebuilt := common.PathForModuleSrc(ctx, j.properties.Srcs[0])
classJarSpec, resourceJarSpec := TransformPrebuiltJarToClasses(ctx, prebuilt)
j.classpathFile = prebuilt
j.classJarSpecs = []jarSpec{classJarSpec}
j.resourceJarSpecs = []jarSpec{resourceJarSpec}
ctx.InstallFileName(common.PathForModuleInstall(ctx, "framework"), ctx.ModuleName()+".jar", j.classpathFile)
}
var _ JavaDependency = (*JavaPrebuilt)(nil)
func (j *JavaPrebuilt) ClasspathFile() common.Path {
return j.classpathFile
}
func (j *JavaPrebuilt) ClassJarSpecs() []jarSpec {
return j.classJarSpecs
}
func (j *JavaPrebuilt) ResourceJarSpecs() []jarSpec {
return j.resourceJarSpecs
}
func (j *JavaPrebuilt) AidlIncludeDirs() common.Paths {
return nil
}
func JavaPrebuiltFactory() (blueprint.Module, []interface{}) {
module := &JavaPrebuilt{}
return common.InitAndroidArchModule(module, common.HostAndDeviceSupported,
common.MultilibCommon, &module.properties)
}
//
// SDK java prebuilts (.jar containing resources plus framework.aidl)
//
type sdkDependency interface {
JavaDependency
AidlPreprocessed() common.OptionalPath
}
var _ sdkDependency = (*sdkPrebuilt)(nil)
type sdkPrebuiltProperties struct {
Aidl_preprocessed *string
}
type sdkPrebuilt struct {
JavaPrebuilt
sdkProperties sdkPrebuiltProperties
aidlPreprocessed common.OptionalPath
}
func (j *sdkPrebuilt) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
j.JavaPrebuilt.GenerateAndroidBuildActions(ctx)
j.aidlPreprocessed = common.OptionalPathForModuleSrc(ctx, j.sdkProperties.Aidl_preprocessed)
}
func (j *sdkPrebuilt) AidlPreprocessed() common.OptionalPath {
return j.aidlPreprocessed
}
func SdkPrebuiltFactory() (blueprint.Module, []interface{}) {
module := &sdkPrebuilt{}
return common.InitAndroidArchModule(module, common.HostAndDeviceSupported,
common.MultilibCommon, &module.properties, &module.sdkProperties)
}
func inList(s string, l []string) bool {
for _, e := range l {
if e == s {
return true
}
}
return false
}