aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2015-03-30 17:20:39 -0700
committerColin Cross <ccross@android.com>2015-04-03 16:24:44 -0700
commit2fe6687847a137c6897b19afefa187a38a2a8b6e (patch)
treed710173c69915e0f8a1d22712b746e9f839a2c6c /java
parent35cec12a11e1b279960f463f53a74b5407de056a (diff)
downloadbuild_soong-2fe6687847a137c6897b19afefa187a38a2a8b6e.tar.gz
build_soong-2fe6687847a137c6897b19afefa187a38a2a8b6e.tar.bz2
build_soong-2fe6687847a137c6897b19afefa187a38a2a8b6e.zip
Support java libraries, binaries, and prebuilts
Add support for compiling java libraries (.jar files with or without .dex), java binaries (.jar files with a wrapper script to run them), and java prebuilts (for the SDK .jars) Change-Id: Id624da64c92cf20c6d9577c6bb06e5b212af0d1b
Diffstat (limited to 'java')
-rw-r--r--java/builder.go189
-rw-r--r--java/java.go403
-rw-r--r--java/resources.go45
3 files changed, 637 insertions, 0 deletions
diff --git a/java/builder.go b/java/builder.go
new file mode 100644
index 00000000..2f9d7018
--- /dev/null
+++ b/java/builder.go
@@ -0,0 +1,189 @@
+// 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 generates the final rules for compiling all Java. All properties related to
+// compiling should have been translated into javaBuilderFlags or another argument to the Transform*
+// functions.
+
+import (
+ "path/filepath"
+ "strings"
+
+ "android/soong/common"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/bootstrap"
+)
+
+var (
+ pctx = blueprint.NewPackageContext("android/soong/java")
+
+ // Compiling java is not conducive to proper dependency tracking. The path-matches-class-name
+ // requirement leads to unpredictable generated source file names, and a single .java file
+ // will get compiled into multiple .class files if it contains inner classes. To work around
+ // this, all java rules write into separate directories and then a post-processing step lists
+ // the files in the the directory into a list file that later rules depend on (and sometimes
+ // read from directly using @<listfile>)
+ cc = pctx.StaticRule("javac",
+ blueprint.RuleParams{
+ Command: `$javacCmd -encoding UTF-8 $javacFlags $bootClasspath $classpath ` +
+ `-extdirs "" -d $outDir @$out.rsp || ( rm -rf $outDir; exit 41 ) && ` +
+ `find $outDir -name "*.class" > $out`,
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ Description: "javac $outDir",
+ },
+ "javacCmd", "javacFlags", "bootClasspath", "classpath", "outDir")
+
+ jar = pctx.StaticRule("jar",
+ blueprint.RuleParams{
+ Command: `$jarCmd -o $out $jarArgs`,
+ Description: "jar $out",
+ },
+ "jarCmd", "jarArgs")
+
+ dx = pctx.StaticRule("dx",
+ blueprint.RuleParams{
+ Command: "$dxCmd --dex --output=$out $dxFlags $in",
+ Description: "dex $out",
+ },
+ "outDir", "dxFlags")
+)
+
+func init() {
+ pctx.StaticVariable("commonJdkFlags", "-source 1.7 -target 1.7 -Xmaxerrs 9999999")
+ pctx.StaticVariable("javacCmd", "javac -J-Xmx1024M $commonJdkFlags")
+ pctx.StaticVariable("jarCmd", filepath.Join(bootstrap.BinDir, "soong_jar"))
+ pctx.VariableFunc("dxCmd", func(c interface{}) (string, error) {
+ return c.(Config).HostBinTool("dx")
+ })
+}
+
+type javaBuilderFlags struct {
+ javacFlags string
+ dxFlags string
+ bootClasspath string
+ classpath string
+}
+
+type jarSpec struct {
+ fileList, dir string
+}
+
+func (j jarSpec) soongJarArgs() string {
+ return "-C " + j.dir + " -l " + j.fileList
+}
+
+func TransformJavaToClasses(ctx common.AndroidModuleContext, srcFiles []string,
+ flags javaBuilderFlags, deps []string) jarSpec {
+
+ classDir := filepath.Join(common.ModuleOutDir(ctx), "classes")
+ classFileList := filepath.Join(classDir, "classes.list")
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: cc,
+ Outputs: []string{classFileList},
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "javacFlags": flags.javacFlags,
+ "bootClasspath": flags.bootClasspath,
+ "classpath": flags.classpath,
+ "outDir": classDir,
+ },
+ })
+
+ return jarSpec{classFileList, classDir}
+}
+
+func TransformClassesToJar(ctx common.AndroidModuleContext, classes []jarSpec,
+ manifest string) string {
+
+ outputFile := filepath.Join(common.ModuleOutDir(ctx), "classes-full-debug.jar")
+
+ deps := []string{}
+ jarArgs := []string{}
+
+ for _, j := range classes {
+ deps = append(deps, j.fileList)
+ jarArgs = append(jarArgs, j.soongJarArgs())
+ }
+
+ if manifest != "" {
+ deps = append(deps, manifest)
+ jarArgs = append(jarArgs, "-m "+manifest)
+ }
+
+ deps = append(deps, "$jarCmd")
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: jar,
+ Outputs: []string{outputFile},
+ Implicits: deps,
+ Args: map[string]string{
+ "jarArgs": strings.Join(jarArgs, " "),
+ },
+ })
+
+ return outputFile
+}
+
+func TransformClassesJarToDex(ctx common.AndroidModuleContext, classesJar string,
+ flags javaBuilderFlags) string {
+
+ outputFile := filepath.Join(common.ModuleOutDir(ctx), "classes.dex")
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: dx,
+ Outputs: []string{outputFile},
+ Inputs: []string{classesJar},
+ Implicits: []string{"$dxCmd"},
+ Args: map[string]string{
+ "dxFlags": flags.dxFlags,
+ },
+ })
+
+ return outputFile
+}
+
+func TransformDexToJavaLib(ctx common.AndroidModuleContext, resources []jarSpec,
+ dexFile string) string {
+
+ outputFile := filepath.Join(common.ModuleOutDir(ctx), "javalib.jar")
+ var deps []string
+ var jarArgs []string
+
+ for _, j := range resources {
+ deps = append(deps, j.fileList)
+ jarArgs = append(jarArgs, j.soongJarArgs())
+ }
+
+ dexDir, _ := filepath.Split(dexFile)
+ jarArgs = append(jarArgs, "-C "+dexDir+" -f "+dexFile)
+
+ deps = append(deps, "$jarCmd", dexFile)
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: jar,
+ Outputs: []string{outputFile},
+ Implicits: deps,
+ Args: map[string]string{
+ "jarArgs": strings.Join(jarArgs, " "),
+ },
+ })
+
+ return outputFile
+}
diff --git a/java/java.go b/java/java.go
new file mode 100644
index 00000000..67bde045
--- /dev/null
+++ b/java/java.go
@@ -0,0 +1,403 @@
+// 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"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
+
+ "android/soong/common"
+)
+
+type Config interface {
+ SrcDir() string
+ PrebuiltOS() string
+ HostBinTool(string) (string, error)
+ Getenv(string) string
+}
+
+// TODO:
+// Autogenerated files:
+// AIDL
+// Proto
+// Renderscript
+// Post-jar passes:
+// Proguard
+// Emma
+// Jarjar
+// Dex
+// Rmtypedefs
+// Jack
+// DroidDoc
+// Findbugs
+
+// 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 struct {
+ // srcs: list of source files used to compile the Java module. May be .java, .logtags, .proto,
+ // or .aidl files.
+ Srcs []string `android:"arch_variant,arch_subtract"`
+
+ // resource_dirs: list of directories containing resources
+ Resource_dirs []string `android:"arch_variant"`
+
+ // no_standard_libraries: don't build against the default libraries (core-libart, core-junit,
+ // ext, and framework for device targets)
+ No_standard_libraries bool
+
+ // javacflags: list of module-specific flags that will be used for javac compiles
+ Javacflags []string `android:"arch_variant"`
+
+ // dxflags: list of module-specific flags that will be used for dex compiles
+ Dxflags []string `android:"arch_variant"`
+
+ // java_libs: list of of java libraries that will be in the classpath
+ Java_libs []string `android:"arch_variant"`
+
+ // java_static_libs: list of java libraries that will be compiled into the resulting jar
+ Java_static_libs []string `android:"arch_variant"`
+
+ // manifest: manifest file to be included in resulting jar
+ Manifest string
+
+ // sdk_version: 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"`
+ }
+
+ // output file suitable for inserting into the classpath of another compile
+ classpathFile string
+
+ // 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
+
+ // installed file for binary dependency
+ installFile string
+}
+
+type JavaModuleType interface {
+ GenerateJavaBuildActions(ctx common.AndroidModuleContext)
+}
+
+type JavaDependency interface {
+ ClasspathFile() string
+ ClassJarSpecs() []jarSpec
+ ResourceJarSpecs() []jarSpec
+}
+
+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
+ 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 ""
+ }
+ }
+}
+
+func (j *javaBase) AndroidDynamicDependencies(ctx common.AndroidDynamicDependerModuleContext) []string {
+ var deps []string
+
+ if !j.properties.No_standard_libraries {
+ bootClasspath := j.BootClasspath(ctx)
+ if bootClasspath != "" {
+ deps = append(deps, bootClasspath)
+ }
+ }
+ deps = append(deps, j.properties.Java_libs...)
+ deps = append(deps, j.properties.Java_static_libs...)
+
+ return deps
+}
+
+func (j *javaBase) collectDeps(ctx common.AndroidModuleContext) (classpath []string,
+ bootClasspath string, classJarSpecs, resourceJarSpecs []jarSpec) {
+
+ ctx.VisitDirectDeps(func(module blueprint.Module) {
+ otherName := ctx.OtherModuleName(module)
+ if javaDep, ok := module.(JavaDependency); ok {
+ 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 == j.BootClasspath(ctx) {
+ bootClasspath = javaDep.ClasspathFile()
+ } else {
+ panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
+ }
+ } else {
+ ctx.ModuleErrorf("unknown dependency module type for %q", otherName)
+ }
+ })
+
+ return classpath, bootClasspath, classJarSpecs, resourceJarSpecs
+}
+
+func (j *javaBase) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+ j.module.GenerateJavaBuildActions(ctx)
+}
+
+func (j *javaBase) GenerateJavaBuildActions(ctx common.AndroidModuleContext) {
+ flags := javaBuilderFlags{
+ javacFlags: strings.Join(j.properties.Javacflags, " "),
+ }
+
+ var javacDeps []string
+
+ srcFiles := j.properties.Srcs
+ srcFiles = pathtools.PrefixPaths(srcFiles, common.ModuleSrcDir(ctx))
+ srcFiles = common.ExpandGlobs(ctx, srcFiles)
+
+ classpath, bootClasspath, classJarSpecs, resourceJarSpecs := j.collectDeps(ctx)
+
+ if bootClasspath != "" {
+ flags.bootClasspath = "-bootclasspath " + bootClasspath
+ javacDeps = append(javacDeps, bootClasspath)
+ }
+
+ if len(classpath) > 0 {
+ flags.classpath = "-classpath " + strings.Join(classpath, ":")
+ javacDeps = append(javacDeps, classpath...)
+ }
+
+ // Compile java sources into .class files
+ classes := TransformJavaToClasses(ctx, srcFiles, flags, javacDeps)
+ if ctx.Failed() {
+ return
+ }
+
+ resourceJarSpecs = append(ResourceDirsToJarSpecs(ctx, j.properties.Resource_dirs), resourceJarSpecs...)
+ classJarSpecs = append([]jarSpec{classes}, classJarSpecs...)
+
+ manifest := j.properties.Manifest
+ if manifest != "" {
+ manifest = filepath.Join(common.ModuleSrcDir(ctx), 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
+ }
+ j.classJarSpecs = classJarSpecs
+ j.resourceJarSpecs = resourceJarSpecs
+ j.classpathFile = outputFile
+
+ if j.properties.Dex {
+ 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.Config().(Config).Getenv("NO_OPTIMIZE_DX") != "" {
+ dxFlags = append(dxFlags, "--no-optimize")
+ }
+
+ if ctx.Config().(Config).Getenv("GENERATE_DEX_DEBUG") != "" {
+ dxFlags = append(dxFlags,
+ "--debug",
+ "--verbose",
+ "--dump-to="+filepath.Join(common.ModuleOutDir(ctx), "classes.lst"),
+ "--dump-width=1000")
+ }
+
+ flags.dxFlags = strings.Join(dxFlags, " ")
+
+ // Compile classes.jar into classes.dex
+ dexFile := TransformClassesJarToDex(ctx, outputFile, flags)
+ if ctx.Failed() {
+ return
+ }
+
+ // Combine classes.dex + resources into javalib.jar
+ outputFile = TransformDexToJavaLib(ctx, resourceJarSpecs, dexFile)
+ }
+
+ j.installFile = ctx.InstallFileName("framework", ctx.ModuleName()+".jar", outputFile)
+}
+
+var _ JavaDependency = (*JavaLibrary)(nil)
+
+func (j *javaBase) ClasspathFile() string {
+ return j.classpathFile
+}
+
+func (j *javaBase) ClassJarSpecs() []jarSpec {
+ return j.classJarSpecs
+}
+
+func (j *javaBase) ResourceJarSpecs() []jarSpec {
+ return j.resourceJarSpecs
+}
+
+//
+// Java libraries (.jar file)
+//
+
+type JavaLibrary struct {
+ javaBase
+}
+
+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 JavaBinary struct {
+ JavaLibrary
+
+ binaryProperties struct {
+ // wrapper: installable script to execute the resulting jar
+ Wrapper string
+ }
+}
+
+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("bin", filepath.Join(common.ModuleSrcDir(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 JavaPrebuilt struct {
+ common.AndroidModuleBase
+
+ properties struct {
+ Srcs []string
+ }
+
+ classpathFile string
+}
+
+func (j *JavaPrebuilt) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+ if len(j.properties.Srcs) != 1 {
+ ctx.ModuleErrorf("expected exactly one jar in srcs")
+ return
+ }
+ j.classpathFile = filepath.Join(common.ModuleSrcDir(ctx), j.properties.Srcs[0])
+}
+
+var _ JavaDependency = (*JavaPrebuilt)(nil)
+
+func (j *JavaPrebuilt) ClasspathFile() string {
+ return j.classpathFile
+}
+
+func (j *JavaPrebuilt) ClassJarSpecs() []jarSpec {
+ return nil
+}
+
+func (j *JavaPrebuilt) ResourceJarSpecs() []jarSpec {
+ return nil
+}
+
+func JavaPrebuiltFactory() (blueprint.Module, []interface{}) {
+ module := &JavaPrebuilt{}
+
+ return common.InitAndroidArchModule(module, common.HostAndDeviceSupported,
+ common.MultilibCommon, &module.properties)
+}
+
+func inList(s string, l []string) bool {
+ for _, e := range l {
+ if e == s {
+ return true
+ }
+ }
+ return false
+}
diff --git a/java/resources.go b/java/resources.go
new file mode 100644
index 00000000..2fbf54bf
--- /dev/null
+++ b/java/resources.go
@@ -0,0 +1,45 @@
+// 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
+
+import (
+ "path/filepath"
+
+ "android/soong/common"
+)
+
+var resourceExcludes = []string{
+ "*.java",
+ "package.html",
+ "overview.html",
+ ".*.swp",
+ ".DS_Store",
+ "*~",
+}
+
+func ResourceDirsToJarSpecs(ctx common.AndroidModuleContext, dirs []string) []jarSpec {
+ jarSpecs := make([]jarSpec, len(dirs))
+
+ for i, dir := range dirs {
+ fileListFile := filepath.Join(common.ModuleOutDir(ctx), "res", dir, "resources.list")
+ depFile := fileListFile + ".d"
+ dir := filepath.Join(common.ModuleSrcDir(ctx), dir)
+ glob := filepath.Join(dir, "**/*")
+ common.GlobRule(ctx, glob, resourceExcludes, fileListFile, depFile)
+ jarSpecs[i] = jarSpec{fileListFile, dir}
+ }
+
+ return jarSpecs
+}