Files
build_soong/java/java.go
Colin Cross d689143f1d Move errorprone dependency to compiled classes jar
The resources jar and the combined jar are not always used, so
add the errorprone dependency to the compiled jar.

Test: m -j checkbuild
Change-Id: Iaa10a04347758c676bb704969b86f9442e6c3175
2017-09-29 14:02:22 -07:00

777 lines
22 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 Module. The final creation of the rules
// is handled in builder.go
import (
"fmt"
"path/filepath"
"strconv"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/genrule"
"android/soong/java/config"
)
func init() {
android.RegisterModuleType("java_defaults", defaultsFactory)
android.RegisterModuleType("java_library", LibraryFactory)
android.RegisterModuleType("java_library_static", LibraryFactory)
android.RegisterModuleType("java_library_host", LibraryHostFactory)
android.RegisterModuleType("java_binary", BinaryFactory)
android.RegisterModuleType("java_binary_host", BinaryHostFactory)
android.RegisterModuleType("java_import", ImportFactory)
android.RegisterModuleType("java_import_host", ImportFactoryHost)
android.RegisterModuleType("android_app", AndroidAppFactory)
android.RegisterSingletonType("logtags", LogtagsSingleton)
}
// TODO:
// Autogenerated files:
// Proto
// Renderscript
// Post-jar passes:
// Proguard
// Jacoco
// Jarjar
// Dex
// Rmtypedefs
// DroidDoc
// Findbugs
type CompilerProperties 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 (legacy-test, core-junit,
// ext, and framework for device targets)
No_standard_libs *bool
// list of module-specific flags that will be used for javac compiles
Javacflags []string `android:"arch_variant"`
// list of of java libraries that will be in the classpath
Libs []string `android:"arch_variant"`
// list of java libraries that will be compiled into the resulting jar
Static_libs []string `android:"arch_variant"`
// manifest file to be included in resulting jar
Manifest *string
// if not blank, run jarjar using the specified rules file
Jarjar_rules *string
// If not blank, set the java version passed to javac as -source and -target
Java_version *string
// If set to false, don't allow this module to be installed. Defaults to true.
Installable *bool
// List of modules to use as annotation processors
Annotation_processors []string
// List of classes to pass to javac to use as annotation processors
Annotation_processor_classes []string
}
type CompilerDeviceProperties struct {
// list of module-specific flags that will be used for dex compiles
Dxflags []string `android:"arch_variant"`
// 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"`
// 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
}
// Module contains the properties and members used by all java module types
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
properties CompilerProperties
deviceProperties CompilerDeviceProperties
// output file suitable for inserting into the classpath of another compile
classpathFile android.Path
// output file containing classes.dex
dexJarFile android.Path
// output files containing resources
resourceJarFiles android.Paths
// output file suitable for installing or running
outputFile android.Path
exportAidlIncludeDirs android.Paths
logtagsSrcs android.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 android.Paths
// installed file for binary dependency
installFile android.Path
}
type Dependency interface {
ClasspathFiles() android.Paths
ResourceJarFiles() android.Paths
AidlIncludeDirs() android.Paths
}
func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
android.InitAndroidArchModule(module, hod, android.MultilibCommon)
android.InitDefaultableModule(module)
}
type dependencyTag struct {
blueprint.BaseDependencyTag
name string
}
var (
staticLibTag = dependencyTag{name: "staticlib"}
libTag = dependencyTag{name: "javalib"}
bootClasspathTag = dependencyTag{name: "bootclasspath"}
frameworkResTag = dependencyTag{name: "framework-res"}
)
type sdkDep struct {
useModule, useFiles, useDefaultLibs bool
module string
jar android.Path
aidl android.Path
}
func decodeSdkDep(ctx android.BaseContext, v string) sdkDep {
switch v {
case "", "current", "system_current", "test_current":
// OK
default:
if _, err := strconv.Atoi(v); err != nil {
ctx.PropertyErrorf("sdk_version", "invalid sdk version")
return sdkDep{}
}
}
toFile := func(v string) sdkDep {
dir := filepath.Join("prebuilts/sdk", v)
jar := filepath.Join(dir, "android.jar")
aidl := filepath.Join(dir, "framework.aidl")
jarPath := android.ExistentPathForSource(ctx, "sdkdir", jar)
aidlPath := android.ExistentPathForSource(ctx, "sdkdir", aidl)
if !jarPath.Valid() {
ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, jar)
return sdkDep{}
}
if !aidlPath.Valid() {
ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, aidl)
return sdkDep{}
}
return sdkDep{
useFiles: true,
jar: jarPath.Path(),
aidl: aidlPath.Path(),
}
}
toModule := func(m string) sdkDep {
return sdkDep{
useModule: true,
module: m,
}
}
if ctx.AConfig().UnbundledBuild() {
if v == "" {
if ctx, ok := ctx.(android.ModuleContext); ok {
ctx.AddMissingDependencies([]string{"sdk_version_must_be_set_for_modules_used_in_unbundled_builds"})
}
return sdkDep{}
}
return toFile(v)
}
switch v {
case "":
return sdkDep{
useDefaultLibs: true,
}
case "current":
return toModule("android_stubs_current")
case "system_current":
return toModule("android_system_stubs_current")
case "test_current":
return toModule("android_test_stubs_current")
default:
return toFile(v)
}
}
func (j *Module) deps(ctx android.BottomUpMutatorContext) {
if !proptools.Bool(j.properties.No_standard_libs) {
if ctx.Device() {
sdkDep := decodeSdkDep(ctx, j.deviceProperties.Sdk_version)
if sdkDep.useDefaultLibs {
ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
ctx.AddDependency(ctx.Module(), libTag, config.DefaultLibraries...)
}
if sdkDep.useModule {
ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
}
} else {
if j.deviceProperties.Dex {
ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
}
}
}
ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
android.ExtractSourcesDeps(ctx, j.properties.Srcs)
}
func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
aidlIncludeDirs android.Paths) []string {
localAidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl_includes)
var flags []string
if aidlPreprocess.Valid() {
flags = append(flags, "-p"+aidlPreprocess.String())
} else {
flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
}
flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
flags = append(flags, android.JoinWithPrefix(localAidlIncludes.Strings(), "-I"))
flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
if src := android.ExistentPathForSource(ctx, "", "src"); src.Valid() {
flags = append(flags, "-I"+src.String())
}
return flags
}
type deps struct {
classpath android.Paths
bootClasspath android.Paths
staticJars android.Paths
staticJarResources android.Paths
aidlIncludeDirs android.Paths
srcFileLists android.Paths
aidlPreprocess android.OptionalPath
}
func (j *Module) collectDeps(ctx android.ModuleContext) deps {
var deps deps
sdkDep := decodeSdkDep(ctx, j.deviceProperties.Sdk_version)
if sdkDep.useFiles {
deps.classpath = append(deps.classpath, sdkDep.jar)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, sdkDep.aidl)
}
ctx.VisitDirectDeps(func(module blueprint.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
dep, _ := module.(Dependency)
if dep == nil {
switch tag {
case android.DefaultsDepTag, android.SourceDepTag:
// Nothing to do
default:
ctx.ModuleErrorf("depends on non-java module %q", otherName)
}
return
}
switch tag {
case bootClasspathTag:
deps.bootClasspath = append(deps.bootClasspath, dep.ClasspathFiles()...)
case libTag:
deps.classpath = append(deps.classpath, dep.ClasspathFiles()...)
case staticLibTag:
deps.classpath = append(deps.classpath, dep.ClasspathFiles()...)
deps.staticJars = append(deps.staticJars, dep.ClasspathFiles()...)
deps.staticJarResources = append(deps.staticJarResources, dep.ResourceJarFiles()...)
case frameworkResTag:
if ctx.ModuleName() == "framework" {
// framework.jar has a one-off dependency on the R.java and Manifest.java files
// generated by framework-res.apk
deps.srcFileLists = append(deps.srcFileLists, module.(*AndroidApp).aaptJavaFileList)
}
default:
panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
}
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
})
return deps
}
func (j *Module) compile(ctx android.ModuleContext) {
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Export_aidl_include_dirs)
deps := j.collectDeps(ctx)
var flags javaBuilderFlags
javacFlags := j.properties.Javacflags
if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") == "" {
javacFlags = config.StripJavac9Flags(javacFlags)
}
if j.properties.Java_version != nil {
flags.javaVersion = *j.properties.Java_version
} else {
flags.javaVersion = "${config.DefaultJavaVersion}"
}
flags.bootClasspath.AddPaths(deps.bootClasspath)
flags.classpath.AddPaths(deps.classpath)
if len(javacFlags) > 0 {
ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
flags.javacFlags = "$javacFlags"
}
aidlFlags := j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
if len(aidlFlags) > 0 {
ctx.Variable(pctx, "aidlFlags", strings.Join(aidlFlags, " "))
flags.aidlFlags = "$aidlFlags"
}
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()...)
}
})
deps.srcFileLists = append(deps.srcFileLists, j.ExtraSrcLists...)
var jars android.Paths
if len(srcFiles) > 0 {
var extraJarDeps android.Paths
if ctx.AConfig().IsEnvTrue("RUN_ERROR_PRONE") {
// If error-prone is enabled, add an additional rule to compile the java files into
// a separate set of classes (so that they don't overwrite the normal ones and require
// a rebuild when error-prone is turned off).
// TODO(ccross): Once we always compile with javac9 we may be able to conditionally
// enable error-prone without affecting the output class files.
errorprone := RunErrorProne(ctx, srcFiles, deps.srcFileLists, flags, nil)
extraJarDeps = append(extraJarDeps, errorprone)
}
// Compile java sources into .class files
classes := TransformJavaToClasses(ctx, srcFiles, deps.srcFileLists, flags, extraJarDeps)
if ctx.Failed() {
return
}
jars = append(jars, classes)
}
resourceJarSpecs := ResourceDirsToJarSpecs(ctx, j.properties.Java_resource_dirs, j.properties.Exclude_java_resource_dirs)
if len(resourceJarSpecs) > 0 {
// Combine classes + resources into classes-full-debug.jar
resourceJar := TransformResourcesToJar(ctx, resourceJarSpecs, extraJarDeps)
if ctx.Failed() {
return
}
j.resourceJarFiles = append(j.resourceJarFiles, resourceJar)
jars = append(jars, resourceJar)
}
// Propagate the resources from the transitive closure of static dependencies for copying
// into dex jars
j.resourceJarFiles = append(j.resourceJarFiles, deps.staticJarResources...)
// static classpath jars have the resources in them, so the resource jars aren't necessary here
jars = append(jars, deps.staticJars...)
manifest := android.OptionalPathForModuleSrc(ctx, j.properties.Manifest)
// Combine the classes built from sources, any manifests, and any static libraries into
// classes-combined.jar. If there is only one input jar this step will be skipped.
outputFile := TransformJarsToJar(ctx, "classes.jar", jars, manifest, false)
if j.properties.Jarjar_rules != nil {
jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
// Transform classes-combined.jar into classes-jarjar.jar
outputFile = TransformJarJar(ctx, outputFile, jarjar_rules)
if ctx.Failed() {
return
}
}
j.classpathFile = outputFile
// TODO(ccross): handle hostdex
if ctx.Device() && len(srcFiles) > 0 {
dxFlags := j.deviceProperties.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="+android.PathForModuleOut(ctx, "classes.lst").String(),
"--dump-width=1000")
}
var minSdkVersion string
switch j.deviceProperties.Sdk_version {
case "", "current", "test_current", "system_current":
minSdkVersion = strconv.Itoa(ctx.AConfig().DefaultAppTargetSdkInt())
default:
minSdkVersion = j.deviceProperties.Sdk_version
}
dxFlags = append(dxFlags, "--min-sdk-version="+minSdkVersion)
flags.dxFlags = strings.Join(dxFlags, " ")
desugarFlags := []string{
"--min_sdk_version " + minSdkVersion,
"--desugar_try_with_resources_if_needed=false",
"--allow_empty_bootclasspath",
}
if inList("--core-library", dxFlags) {
desugarFlags = append(desugarFlags, "--core_library")
}
flags.desugarFlags = strings.Join(desugarFlags, " ")
desugarJar := TransformDesugar(ctx, outputFile, flags)
if ctx.Failed() {
return
}
// TODO(ccross): For now, use the desugared jar as the classpath file. Eventually this
// might cause problems because desugar wants non-desugared jars in its class path.
j.classpathFile = desugarJar
// Compile classes.jar into classes.dex
dexJarFile := TransformClassesJarToDexJar(ctx, desugarJar, flags)
if ctx.Failed() {
return
}
jars := android.Paths{dexJarFile}
jars = append(jars, j.resourceJarFiles...)
outputFile = TransformJarsToJar(ctx, "javalib.jar", jars, android.OptionalPath{}, true)
j.dexJarFile = outputFile
}
ctx.CheckbuildFile(outputFile)
j.outputFile = outputFile
}
var _ Dependency = (*Library)(nil)
func (j *Module) ClasspathFiles() android.Paths {
return android.Paths{j.classpathFile}
}
func (j *Module) ResourceJarFiles() android.Paths {
return j.resourceJarFiles
}
func (j *Module) AidlIncludeDirs() android.Paths {
return j.exportAidlIncludeDirs
}
var _ logtagsProducer = (*Module)(nil)
func (j *Module) logtags() android.Paths {
return j.logtagsSrcs
}
//
// Java libraries (.jar file)
//
type Library struct {
Module
}
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.compile(ctx)
if j.properties.Installable == nil || *j.properties.Installable == true {
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
ctx.ModuleName()+".jar", j.outputFile)
}
}
func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
j.deps(ctx)
}
func LibraryFactory() android.Module {
module := &Library{}
module.deviceProperties.Dex = true
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
func LibraryHostFactory() android.Module {
module := &Library{}
module.AddProperties(&module.Module.properties)
InitJavaModule(module, android.HostSupported)
return module
}
//
// Java Binaries (.jar file plus wrapper script)
//
type binaryProperties struct {
// installable script to execute the resulting jar
Wrapper string
}
type Binary struct {
Library
binaryProperties binaryProperties
wrapperFile android.ModuleSrcPath
binaryFile android.OutputPath
}
func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.Library.GenerateAndroidBuildActions(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.
j.wrapperFile = android.PathForModuleSrc(ctx, j.binaryProperties.Wrapper)
j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
ctx.ModuleName(), j.wrapperFile, j.installFile)
}
func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
j.deps(ctx)
}
func BinaryFactory() android.Module {
module := &Binary{}
module.deviceProperties.Dex = true
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
&module.binaryProperties)
InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
func BinaryHostFactory() android.Module {
module := &Binary{}
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
&module.binaryProperties)
InitJavaModule(module, android.HostSupported)
return module
}
//
// Java prebuilts
//
type ImportProperties struct {
Jars []string
}
type Import struct {
android.ModuleBase
prebuilt android.Prebuilt
properties ImportProperties
classpathFiles android.Paths
combinedClasspathFile android.Path
}
func (j *Import) Prebuilt() *android.Prebuilt {
return &j.prebuilt
}
func (j *Import) PrebuiltSrcs() []string {
return j.properties.Jars
}
func (j *Import) Name() string {
return j.prebuilt.Name(j.ModuleBase.Name())
}
func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
}
func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
j.combinedClasspathFile = TransformJarsToJar(ctx, "classes.jar", j.classpathFiles, android.OptionalPath{}, false)
}
var _ Dependency = (*Import)(nil)
func (j *Import) ClasspathFiles() android.Paths {
return j.classpathFiles
}
func (j *Import) ResourceJarFiles() android.Paths {
// resources are in the ClasspathFiles
return nil
}
func (j *Import) AidlIncludeDirs() android.Paths {
return nil
}
var _ android.PrebuiltInterface = (*Import)(nil)
func ImportFactory() android.Module {
module := &Import{}
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Jars)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
return module
}
func ImportFactoryHost() android.Module {
module := &Import{}
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Jars)
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
return module
}
func inList(s string, l []string) bool {
for _, e := range l {
if e == s {
return true
}
}
return false
}
//
// Defaults
//
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
}
func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
func (d *Defaults) DepsMutator(ctx android.BottomUpMutatorContext) {
}
func defaultsFactory() android.Module {
return DefaultsFactory()
}
func DefaultsFactory(props ...interface{}) android.Module {
module := &Defaults{}
module.AddProperties(props...)
module.AddProperties(
&CompilerProperties{},
&CompilerDeviceProperties{},
)
android.InitDefaultsModule(module)
return module
}