diff options
author | Colin Cross <ccross@android.com> | 2015-03-30 17:20:39 -0700 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2015-04-03 16:24:44 -0700 |
commit | 2fe6687847a137c6897b19afefa187a38a2a8b6e (patch) | |
tree | d710173c69915e0f8a1d22712b746e9f839a2c6c /java | |
parent | 35cec12a11e1b279960f463f53a74b5407de056a (diff) | |
download | build_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.go | 189 | ||||
-rw-r--r-- | java/java.go | 403 | ||||
-rw-r--r-- | java/resources.go | 45 |
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 +} |