From 6ade34f641173eed1ca309e63e1672ad31b622c9 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 15 Sep 2017 13:00:47 -0700 Subject: [PATCH] Initial device java support First pass at java support for the device. Adds desugar before dx, and passes javalib.jar to make. Test: m -j checkbuild Change-Id: I3138d943bc4867a52c3fce8fbdb597773793988d --- java/androidmk.go | 8 +- java/builder.go | 188 +++++++++++++++++++++++++++++++++--------- java/config/config.go | 1 + java/java.go | 114 +++++++++++++++---------- java/java_test.go | 10 +-- 5 files changed, 230 insertions(+), 91 deletions(-) diff --git a/java/androidmk.go b/java/androidmk.go index 680d8642d..89d7d51d6 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -25,13 +25,17 @@ import ( func (library *Library) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(library.outputFile), + OutputFile: android.OptionalPathForPath(library.classpathFile), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", Extra: []android.AndroidMkExtraFunc{ func(w io.Writer, outputFile android.Path) { if library.properties.Installable != nil && *library.properties.Installable == false { fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") } + if library.dexJarFile != nil { + fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String()) + } + fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.deviceProperties.Sdk_version) }, }, } @@ -53,7 +57,7 @@ func (prebuilt *Import) AndroidMk() android.AndroidMkData { func (binary *Binary) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(binary.outputFile), + OutputFile: android.OptionalPathForPath(binary.classpathFile), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { android.WriteAndroidMkData(w, data) diff --git a/java/builder.go b/java/builder.go index cf6ab6b59..b8332ad0a 100644 --- a/java/builder.go +++ b/java/builder.go @@ -83,12 +83,27 @@ var ( }, "jarArgs") + desugar = pctx.AndroidStaticRule("desugar", + blueprint.RuleParams{ + Command: `rm -rf $dumpDir && mkdir -p $dumpDir && ` + + `${config.JavaCmd} ` + + `-Djdk.internal.lambda.dumpProxyClasses=$$(cd $dumpDir && pwd) ` + + `$javaFlags ` + + `-jar ${config.DesugarJar} $classpathFlags $desugarFlags ` + + `-i $in -o $out`, + CommandDeps: []string{"${config.DesugarJar}"}, + }, + "javaFlags", "classpathFlags", "desugarFlags", "dumpDir") + dx = pctx.AndroidStaticRule("dx", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + - `${config.DxCmd} --dex --output=$outDir $dxFlags $in || ( rm -rf "$outDir"; exit 41 ) && ` + - `find "$outDir" -name "classes*.dex" | sort > $out`, - CommandDeps: []string{"${config.DxCmd}"}, + `${config.DxCmd} --dex --output=$outDir $dxFlags $in && ` + + `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`, + CommandDeps: []string{ + "${config.DxCmd}", + "${config.SoongZipCmd}", + }, }, "outDir", "dxFlags") @@ -107,8 +122,9 @@ func init() { type javaBuilderFlags struct { javacFlags string dxFlags string - bootClasspath string - classpath string + bootClasspath classpath + classpath classpath + desugarFlags string aidlFlags string javaVersion string } @@ -131,6 +147,8 @@ func TransformJavaToClasses(ctx android.ModuleContext, srcFiles, srcFileLists an javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@") deps = append(deps, srcFileLists...) + deps = append(deps, flags.bootClasspath...) + deps = append(deps, flags.classpath...) ctx.ModuleBuild(pctx, android.ModuleBuildParams{ Rule: javac, @@ -140,8 +158,8 @@ func TransformJavaToClasses(ctx android.ModuleContext, srcFiles, srcFileLists an Implicits: deps, Args: map[string]string{ "javacFlags": javacFlags, - "bootClasspath": flags.bootClasspath, - "classpath": flags.classpath, + "bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()), + "classpath": flags.classpath.JavaClasspath(), "outDir": classDir.String(), "annoDir": annoDir.String(), "javaVersion": flags.javaVersion, @@ -166,6 +184,8 @@ func RunErrorProne(ctx android.ModuleContext, srcFiles android.Paths, srcFileLis javacFlags := flags.javacFlags + android.JoinWithPrefix(srcFileLists.Strings(), "@") deps = append(deps, srcFileLists...) + deps = append(deps, flags.bootClasspath...) + deps = append(deps, flags.classpath...) ctx.ModuleBuild(pctx, android.ModuleBuildParams{ Rule: errorprone, @@ -175,8 +195,8 @@ func RunErrorProne(ctx android.ModuleContext, srcFiles android.Paths, srcFileLis Implicits: deps, Args: map[string]string{ "javacFlags": javacFlags, - "bootClasspath": flags.bootClasspath, - "classpath": flags.classpath, + "bootClasspath": flags.bootClasspath.JavaBootClasspath(ctx.Device()), + "classpath": flags.classpath.JavaClasspath(), "outDir": classDir.String(), "annoDir": annoDir.String(), "javaVersion": flags.javaVersion, @@ -246,11 +266,47 @@ func TransformJarsToJar(ctx android.ModuleContext, stem string, jars android.Pat return outputFile } -func TransformClassesJarToDex(ctx android.ModuleContext, classesJar android.Path, - flags javaBuilderFlags) jarSpec { +func TransformDesugar(ctx android.ModuleContext, classesJar android.Path, + flags javaBuilderFlags) android.Path { + + outputFile := android.PathForModuleOut(ctx, "classes-desugar.jar") + dumpDir := android.PathForModuleOut(ctx, "desugar_dumped_classes") + + javaFlags := "" + if ctx.AConfig().Getenv("EXPERIMENTAL_USE_OPENJDK9") != "" { + javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED" + } + + var desugarFlags []string + desugarFlags = append(desugarFlags, flags.bootClasspath.DesugarBootClasspath()...) + desugarFlags = append(desugarFlags, flags.classpath.DesugarClasspath()...) + + var deps android.Paths + deps = append(deps, flags.bootClasspath...) + deps = append(deps, flags.classpath...) + + ctx.ModuleBuild(pctx, android.ModuleBuildParams{ + Rule: desugar, + Description: "desugar", + Output: outputFile, + Input: classesJar, + Implicits: deps, + Args: map[string]string{ + "dumpDir": dumpDir.String(), + "javaFlags": javaFlags, + "classpathFlags": strings.Join(desugarFlags, " "), + "desugarFlags": flags.desugarFlags, + }, + }) + + return outputFile +} + +func TransformClassesJarToDexJar(ctx android.ModuleContext, classesJar android.Path, + flags javaBuilderFlags) android.Path { outDir := android.PathForModuleOut(ctx, "dex") - outputFile := android.PathForModuleOut(ctx, "dex.filelist") + outputFile := android.PathForModuleOut(ctx, "classes.dex.jar") ctx.ModuleBuild(pctx, android.ModuleBuildParams{ Rule: dx, @@ -263,34 +319,6 @@ func TransformClassesJarToDex(ctx android.ModuleContext, classesJar android.Path }, }) - return jarSpec{outputFile, outDir} -} - -func TransformDexToJavaLib(ctx android.ModuleContext, resources []jarSpec, - dexJarSpec jarSpec) android.Path { - - outputFile := android.PathForModuleOut(ctx, "javalib.jar") - var deps android.Paths - var jarArgs []string - - for _, j := range resources { - deps = append(deps, j.fileList) - jarArgs = append(jarArgs, j.soongJarArgs()) - } - - deps = append(deps, dexJarSpec.fileList) - jarArgs = append(jarArgs, dexJarSpec.soongJarArgs()) - - ctx.ModuleBuild(pctx, android.ModuleBuildParams{ - Rule: jar, - Description: "jar", - Output: outputFile, - Implicits: deps, - Args: map[string]string{ - "jarArgs": strings.Join(jarArgs, " "), - }, - }) - return outputFile } @@ -309,3 +337,83 @@ func TransformJarJar(ctx android.ModuleContext, classesJar android.Path, rulesFi return outputFile } + +type classpath []android.Path + +// Returns a -classpath argument in the form java or javac expects +func (x *classpath) JavaClasspath() string { + if len(*x) > 0 { + return "-classpath " + strings.Join(x.Strings(), ":") + } else { + return "" + } +} + +// Returns a -processorpath argument in the form java or javac expects +func (x *classpath) JavaProcessorpath() string { + if len(*x) > 0 { + return "-processorpath " + strings.Join(x.Strings(), ":") + } else { + return "" + } +} + +// Returns a -bootclasspath argument in the form java or javac expects. If forceEmpty is true, +// returns -bootclasspath "" if the bootclasspath is empty to ensure javac does not fall back to the +// default bootclasspath. +func (x *classpath) JavaBootClasspath(forceEmpty bool) string { + if len(*x) > 0 { + return "-bootclasspath " + strings.Join(x.Strings(), ":") + } else if forceEmpty { + return `-bootclasspath ""` + } else { + return "" + } +} + +func (x *classpath) DesugarBootClasspath() []string { + if x == nil || *x == nil { + return nil + } + flags := make([]string, len(*x)) + for i, v := range *x { + flags[i] = "--bootclasspath_entry " + v.String() + } + + return flags +} + +func (x *classpath) DesugarClasspath() []string { + if x == nil || *x == nil { + return nil + } + flags := make([]string, len(*x)) + for i, v := range *x { + flags[i] = "--classpath_entry " + v.String() + } + + return flags +} + +// Append an android.Paths to the end of the classpath list +func (x *classpath) AddPaths(paths android.Paths) { + for _, path := range paths { + *x = append(*x, path) + } +} + +// Convert a classpath to an android.Paths +func (x *classpath) Paths() android.Paths { + return append(android.Paths(nil), (*x)...) +} + +func (x *classpath) Strings() []string { + if x == nil { + return nil + } + ret := make([]string, len(*x)) + for i, path := range *x { + ret[i] = path.String() + } + return ret +} diff --git a/java/config/config.go b/java/config/config.go index 69d6fc395..3029a5a9c 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -68,6 +68,7 @@ func init() { pctx.StaticVariable("MergeZipsCmd", filepath.Join("${bootstrap.ToolDir}", "merge_zips")) pctx.HostBinToolVariable("DxCmd", "dx") pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar") + pctx.HostJavaToolVariable("DesugarJar", "desugar.jar") pctx.VariableFunc("JavacWrapper", func(config interface{}) (string, error) { if override := config.(android.Config).Getenv("JAVAC_WRAPPER"); override != "" { diff --git a/java/java.go b/java/java.go index d7b5847d5..41da3fc2e 100644 --- a/java/java.go +++ b/java/java.go @@ -137,6 +137,12 @@ type Module struct { // 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 @@ -154,6 +160,7 @@ type Module struct { type Dependency interface { ClasspathFiles() android.Paths + ResourceJarFiles() android.Paths AidlIncludeDirs() android.Paths } @@ -168,12 +175,11 @@ type dependencyTag struct { } var ( - staticLibTag = dependencyTag{name: "staticlib"} - libTag = dependencyTag{name: "javalib"} - bootClasspathTag = dependencyTag{name: "bootclasspath"} - frameworkResTag = dependencyTag{name: "framework-res"} - sdkDependencyTag = dependencyTag{name: "sdk"} - annotationProcessorTag = dependencyTag{name: "annotation processor"} + staticLibTag = dependencyTag{name: "staticlib"} + libTag = dependencyTag{name: "javalib"} + bootClasspathTag = dependencyTag{name: "bootclasspath"} + frameworkResTag = dependencyTag{name: "framework-res"} + sdkDependencyTag = dependencyTag{name: "sdk"} ) func (j *Module) deps(ctx android.BottomUpMutatorContext) { @@ -202,7 +208,7 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...) ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...) - ctx.AddDependency(ctx.Module(), annotationProcessorTag, j.properties.Annotation_processors...) + ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...) android.ExtractSourcesDeps(ctx, j.properties.Srcs) } @@ -230,13 +236,13 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt } type deps struct { - classpath android.Paths - bootClasspath android.Paths - staticJars android.Paths - aidlIncludeDirs android.Paths - srcFileLists android.Paths - annotationProcessors android.Paths - aidlPreprocess android.OptionalPath + 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 { @@ -264,8 +270,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case staticLibTag: deps.classpath = append(deps.classpath, dep.ClasspathFiles()...) deps.staticJars = append(deps.staticJars, dep.ClasspathFiles()...) - case annotationProcessorTag: - deps.annotationProcessors = append(deps.annotationProcessors, 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 @@ -306,21 +311,17 @@ func (j *Module) compile(ctx android.ModuleContext) { javacFlags = config.StripJavac9Flags(javacFlags) } - if len(deps.annotationProcessors) > 0 { - javacFlags = append(javacFlags, - "-processorpath "+strings.Join(deps.annotationProcessors.Strings(), ":")) - } - - for _, c := range j.properties.Annotation_processor_classes { - javacFlags = append(javacFlags, "-processor "+c) - } - if j.properties.Java_version != nil { flags.javaVersion = *j.properties.Java_version } else { flags.javaVersion = "${config.DefaultJavaVersion}" } + var extraDeps android.Paths + + flags.bootClasspath.AddPaths(deps.bootClasspath) + flags.classpath.AddPaths(deps.classpath) + if len(javacFlags) > 0 { ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " ")) flags.javacFlags = "$javacFlags" @@ -332,21 +333,6 @@ func (j *Module) compile(ctx android.ModuleContext) { flags.aidlFlags = "$aidlFlags" } - var extraDeps android.Paths - - if len(deps.bootClasspath) > 0 { - flags.bootClasspath = "-bootclasspath " + strings.Join(deps.bootClasspath.Strings(), ":") - extraDeps = append(extraDeps, deps.bootClasspath...) - } else if ctx.Device() { - // Explicitly clear the bootclasspath for device builds - flags.bootClasspath = `-bootclasspath ""` - } - - if len(deps.classpath) > 0 { - flags.classpath = "-classpath " + strings.Join(deps.classpath.Strings(), ":") - extraDeps = append(extraDeps, deps.classpath...) - } - srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs) srcFiles = j.genSources(ctx, srcFiles, flags) @@ -392,9 +378,15 @@ func (j *Module) compile(ctx android.ModuleContext) { 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) @@ -450,14 +442,39 @@ func (j *Module) compile(ctx android.ModuleContext) { flags.dxFlags = strings.Join(dxFlags, " ") - // Compile classes.jar into classes.dex - dexJarSpec := TransformClassesJarToDex(ctx, outputFile, flags) + 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 } - // Combine classes.dex + resources into javalib.jar - outputFile = TransformDexToJavaLib(ctx, resourceJarSpecs, dexJarSpec) + // 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 @@ -469,6 +486,10 @@ 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 } @@ -625,6 +646,11 @@ 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 } diff --git a/java/java_test.go b/java/java_test.go index ab5af0a2a..632ad7513 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -119,8 +119,8 @@ func TestSimple(t *testing.T) { t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) } - bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-compiled.jar") - baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-compiled.jar") + bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-desugar.jar") + baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-desugar.jar") if !strings.Contains(javac.Args["classpath"], bar) { t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar) @@ -182,7 +182,7 @@ func TestSdk(t *testing.T) { check := func(module string, depType depType, deps ...string) { for i := range deps { - deps[i] = filepath.Join(buildDir, ".intermediates", deps[i], "classes-compiled.jar") + deps[i] = filepath.Join(buildDir, ".intermediates", deps[i], "classes-desugar.jar") } dep := strings.Join(deps, ":") @@ -279,12 +279,12 @@ func TestDefaults(t *testing.T) { t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) } - bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-compiled.jar") + bar := filepath.Join(buildDir, ".intermediates", "bar", "classes-desugar.jar") if !strings.Contains(javac.Args["classpath"], bar) { t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar) } - baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-compiled.jar") + baz := filepath.Join(buildDir, ".intermediates", "baz", "classes-desugar.jar") if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz { t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz) }