Merge "Add prebuilt ABI checker support to soong"
diff --git a/Android.bp b/Android.bp
index 97f786e..2c400cf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -248,6 +248,7 @@
"java/java.go",
"java/jdeps.go",
"java/java_resources.go",
+ "java/kotlin.go",
"java/prebuilt_apis.go",
"java/proto.go",
"java/sdk.go",
@@ -260,6 +261,7 @@
"java/dexpreopt_test.go",
"java/java_test.go",
"java/jdeps_test.go",
+ "java/kotlin_test.go",
"java/sdk_test.go",
],
pluginFor: ["soong_build"],
diff --git a/android/config.go b/android/config.go
index 393382b..956c4af 100644
--- a/android/config.go
+++ b/android/config.go
@@ -241,6 +241,7 @@
}
config.BuildOsVariant = config.Targets[BuildOs][0].String()
+ config.BuildOsCommonVariant = getCommonTargets(config.Targets[BuildOs])[0].String()
return testConfig
}
diff --git a/java/androidmk.go b/java/androidmk.go
index b9e4298..ccb5109 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -33,9 +33,6 @@
} else {
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationAndResourcesJar.String())
}
- if library.installFile == nil {
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
- }
if library.dexJarFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
}
diff --git a/java/app.go b/java/app.go
index e44737e..335d9fc 100644
--- a/java/app.go
+++ b/java/app.go
@@ -79,8 +79,6 @@
appProperties appProperties
- extraLinkFlags []string
-
installJniLibs []jniLib
bundleFile android.Path
@@ -155,29 +153,29 @@
}
func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
- linkFlags := append([]string(nil), a.extraLinkFlags...)
+ aaptLinkFlags := []string{}
+ // Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided.
hasProduct := false
for _, f := range a.aaptProperties.Aaptflags {
if strings.HasPrefix(f, "--product") {
hasProduct = true
+ break
}
}
-
- // Product characteristics
if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
- linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
+ aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
}
if !Bool(a.aaptProperties.Aapt_include_all_resources) {
// Product AAPT config
for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
- linkFlags = append(linkFlags, "-c", aaptConfig)
+ aaptLinkFlags = append(aaptLinkFlags, "-c", aaptConfig)
}
// Product AAPT preferred config
if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
- linkFlags = append(linkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
+ aaptLinkFlags = append(aaptLinkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
}
}
@@ -186,10 +184,10 @@
manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
if overridden {
- linkFlags = append(linkFlags, "--rename-manifest-package "+manifestPackageName)
+ aaptLinkFlags = append(aaptLinkFlags, "--rename-manifest-package "+manifestPackageName)
}
- a.aapt.buildActions(ctx, sdkContext(a), linkFlags...)
+ a.aapt.buildActions(ctx, sdkContext(a), aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
diff --git a/java/builder.go b/java/builder.go
index 8615664..40f72e1 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -60,26 +60,6 @@
"javacFlags", "bootClasspath", "classpath", "processorpath", "srcJars", "srcJarDir",
"outDir", "annoDir", "javaVersion")
- kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
- blueprint.RuleParams{
- Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" && mkdir -p "$classesDir" "$srcJarDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.GenKotlinBuildFileCmd} $classpath $classesDir $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
- `${config.KotlincCmd} $kotlincFlags ` +
- `-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile && ` +
- `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir`,
- CommandDeps: []string{
- "${config.KotlincCmd}",
- "${config.KotlinCompilerJar}",
- "${config.GenKotlinBuildFileCmd}",
- "${config.SoongZipCmd}",
- "${config.ZipSyncCmd}",
- },
- Rspfile: "$out.rsp",
- RspfileContent: `$in`,
- },
- "kotlincFlags", "classpath", "srcJars", "srcJarDir", "classesDir", "kotlinJvmTarget", "kotlinBuildFile")
-
turbine = pctx.AndroidStaticRule("turbine",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -177,35 +157,6 @@
protoRoot bool
}
-func TransformKotlinToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
- srcFiles, srcJars android.Paths,
- flags javaBuilderFlags) {
-
- inputs := append(android.Paths(nil), srcFiles...)
-
- var deps android.Paths
- deps = append(deps, flags.kotlincClasspath...)
- deps = append(deps, srcJars...)
-
- ctx.Build(pctx, android.BuildParams{
- Rule: kotlinc,
- Description: "kotlinc",
- Output: outputFile,
- Inputs: inputs,
- Implicits: deps,
- Args: map[string]string{
- "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
- "kotlincFlags": flags.kotlincFlags,
- "srcJars": strings.Join(srcJars.Strings(), " "),
- "classesDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
- "kotlinBuildFile": android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
- // http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
- "kotlinJvmTarget": "1.8",
- },
- })
-}
-
func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) {
@@ -249,7 +200,7 @@
// ensure java does not fall back to the default bootclasspath.
bootClasspath = `--bootclasspath ""`
} else {
- bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath"), " ")
+ bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath "), " ")
}
ctx.Build(pctx, android.BuildParams{
@@ -262,7 +213,7 @@
"javacFlags": flags.javacFlags,
"bootClasspath": bootClasspath,
"srcJars": strings.Join(srcJars.Strings(), " "),
- "classpath": strings.Join(flags.classpath.FormTurbineClasspath("--classpath"), " "),
+ "classpath": strings.Join(flags.classpath.FormTurbineClasspath("--classpath "), " "),
"outDir": android.PathForModuleOut(ctx, "turbine", "classes").String(),
"javaVersion": flags.javaVersion,
},
@@ -464,7 +415,7 @@
}
flags := make([]string, len(*x))
for i, v := range *x {
- flags[i] = optName + " " + v.String()
+ flags[i] = optName + v.String()
}
return flags
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 432840e..2af7b3c 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -14,6 +14,8 @@
package config
+import "strings"
+
var (
KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
KotlincIllegalFlags = []string{
@@ -25,5 +27,14 @@
func init() {
pctx.SourcePathVariable("KotlincCmd", "external/kotlinc/bin/kotlinc")
pctx.SourcePathVariable("KotlinCompilerJar", "external/kotlinc/lib/kotlin-compiler.jar")
+ pctx.SourcePathVariable("KotlinKaptJar", "external/kotlinc/lib/kotlin-annotation-processing.jar")
pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
+
+ // These flags silence "Illegal reflective access" warnings when running kotlinc in OpenJDK9
+ pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
+ "-J--add-opens=java.base/sun.net.www.protocol.jar=ALL-UNNAMED",
+ }, " "))
}
diff --git a/java/dex.go b/java/dex.go
index a6d486a..913eee6 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -85,8 +85,8 @@
func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
d8Flags := j.dexCommonFlags(ctx)
- d8Flags = append(d8Flags, flags.bootClasspath.FormTurbineClasspath("--lib")...)
- d8Flags = append(d8Flags, flags.classpath.FormTurbineClasspath("--lib")...)
+ d8Flags = append(d8Flags, flags.bootClasspath.FormTurbineClasspath("--lib ")...)
+ d8Flags = append(d8Flags, flags.classpath.FormTurbineClasspath("--lib ")...)
var d8Deps android.Paths
d8Deps = append(d8Deps, flags.bootClasspath...)
@@ -215,6 +215,11 @@
},
})
}
+ if j.deviceProperties.UncompressDex {
+ alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
+ TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
+ javalibJar = alignedJavalibJar
+ }
return javalibJar
}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 6a603cf..a06a666 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -85,23 +85,31 @@
// The encode dex rule requires unzipping and rezipping the classes.dex files, ensure that if it was uncompressed
// in the input it stays uncompressed in the output.
soongZipFlags := ""
+ tmpOutput := output
+ tmpDir := android.PathForModuleOut(ctx, "hiddenapi", "dex")
if uncompressDex {
soongZipFlags = "-L 0"
+ tmpOutput = android.PathForModuleOut(ctx, "hiddenapi", "unaligned", "unaligned.jar")
+ tmpDir = android.PathForModuleOut(ctx, "hiddenapi", "unaligned")
}
ctx.Build(pctx, android.BuildParams{
Rule: hiddenAPIEncodeDexRule,
Description: "hiddenapi encode dex",
Input: dexInput,
- Output: output,
+ Output: tmpOutput,
Implicit: flags,
Args: map[string]string{
"flags": flags.String(),
- "tmpDir": android.PathForModuleOut(ctx, "hiddenapi", "dex").String(),
+ "tmpDir": tmpDir.String(),
"soongZipFlags": soongZipFlags,
},
})
+ if uncompressDex {
+ TransformZipAlign(ctx, output, tmpOutput)
+ }
+
hiddenAPISaveDexInputs(ctx, dexInput)
}
diff --git a/java/java.go b/java/java.go
index 9c4bd86..1e5c628 100644
--- a/java/java.go
+++ b/java/java.go
@@ -379,6 +379,7 @@
frameworkResTag = dependencyTag{name: "framework-res"}
frameworkApkTag = dependencyTag{name: "framework-apk"}
kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
+ kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"}
proguardRaiseTag = dependencyTag{name: "proguard-raise"}
certificateTag = dependencyTag{name: "certificate"}
instrumentationForTag = dependencyTag{name: "instrumentation_for"}
@@ -483,6 +484,9 @@
// TODO(ccross): move this to a mutator pass that can tell if generated sources contain
// Kotlin files
ctx.AddVariationDependencies(nil, kotlinStdlibTag, "kotlin-stdlib")
+ if len(j.properties.Annotation_processors) > 0 {
+ ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
+ }
}
if j.shouldInstrumentStatic(ctx) {
@@ -564,6 +568,7 @@
systemModules android.Path
aidlPreprocess android.OptionalPath
kotlinStdlib android.Paths
+ kotlinAnnotations android.Paths
}
func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
@@ -723,6 +728,8 @@
}
case kotlinStdlibTag:
deps.kotlinStdlib = dep.HeaderJars()
+ case kotlinAnnotationsTag:
+ deps.kotlinAnnotations = dep.HeaderJars()
}
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
@@ -969,18 +976,28 @@
kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
- flags.kotlincClasspath = append(flags.kotlincClasspath, deps.bootClasspath...)
- flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
- flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
+ flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
+ flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
+
+ flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
+ flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
+
+ if len(flags.processorPath) > 0 {
+ // Use kapt for annotation processing
+ kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
+ kotlinKapt(ctx, kaptSrcJar, kotlinSrcFiles, srcJars, flags)
+ srcJars = append(srcJars, kaptSrcJar)
+ // Disable annotation processing in javac, it's already been handled by kapt
+ flags.processorPath = nil
+ }
kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
- TransformKotlinToClasses(ctx, kotlinJar, kotlinSrcFiles, srcJars, flags)
+ kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, srcJars, flags)
if ctx.Failed() {
return
}
// Make javac rule depend on the kotlinc rule
- flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
flags.classpath = append(flags.classpath, kotlinJar)
// Jar kotlin classes into the final jar after javac
@@ -1395,13 +1412,7 @@
j.deviceProperties.UncompressDex = j.shouldUncompressDex(ctx)
j.compile(ctx)
- if Bool(j.properties.Installable) || ctx.Host() {
- if j.deviceProperties.UncompressDex {
- alignedOutputFile := android.PathForModuleOut(ctx, "aligned", ctx.ModuleName()+".jar")
- TransformZipAlign(ctx, alignedOutputFile, j.outputFile)
- j.outputFile = alignedOutputFile
- }
-
+ if (Bool(j.properties.Installable) || ctx.Host()) && !android.DirectlyInAnyApex(ctx, ctx.ModuleName()) {
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
ctx.ModuleName()+".jar", j.outputFile)
}
diff --git a/java/java_test.go b/java/java_test.go
index 6437c0c..9f805bb 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -121,6 +121,7 @@
"core.current.stubs",
"core.platform.api.stubs",
"kotlin-stdlib",
+ "kotlin-annotations",
}
for _, extra := range extraModules {
@@ -571,68 +572,6 @@
}
}
-func TestKotlin(t *testing.T) {
- ctx := testJava(t, `
- java_library {
- name: "foo",
- srcs: ["a.java", "b.kt"],
- }
-
- java_library {
- name: "bar",
- srcs: ["b.kt"],
- libs: ["foo"],
- static_libs: ["baz"],
- }
-
- java_library {
- name: "baz",
- srcs: ["c.java"],
- }
- `)
-
- fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
- fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
-
- if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
- fooKotlinc.Inputs[1].String() != "b.kt" {
- t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
- }
-
- if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
- t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
- }
-
- if !strings.Contains(fooJavac.Args["classpath"], fooKotlinc.Output.String()) {
- t.Errorf("foo classpath %v does not contain %q",
- fooJavac.Args["classpath"], fooKotlinc.Output.String())
- }
-
- if !inList(fooKotlinc.Output.String(), fooJar.Inputs.Strings()) {
- t.Errorf("foo jar inputs %v does not contain %q",
- fooJar.Inputs.Strings(), fooKotlinc.Output.String())
- }
-
- fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
- bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
- barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
-
- if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
- t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
- }
-
- if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
- t.Errorf(`expected %q in bar implicits %v`,
- fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
- }
-
- if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
- t.Errorf(`expected %q in bar implicits %v`,
- bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
- }
-}
-
func TestTurbine(t *testing.T) {
ctx := testJava(t, `
java_library {
diff --git a/java/kotlin.go b/java/kotlin.go
new file mode 100644
index 0000000..c72f2de
--- /dev/null
+++ b/java/kotlin.go
@@ -0,0 +1,168 @@
+// Copyright 2019 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 (
+ "bytes"
+ "encoding/base64"
+ "encoding/binary"
+ "strings"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+var kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
+ blueprint.RuleParams{
+ Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" && mkdir -p "$classesDir" "$srcJarDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.GenKotlinBuildFileCmd} $classpath $classesDir $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
+ `${config.KotlincCmd} ${config.JavacHeapFlags} $kotlincFlags ` +
+ `-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir`,
+ CommandDeps: []string{
+ "${config.KotlincCmd}",
+ "${config.KotlinCompilerJar}",
+ "${config.GenKotlinBuildFileCmd}",
+ "${config.SoongZipCmd}",
+ "${config.ZipSyncCmd}",
+ },
+ Rspfile: "$out.rsp",
+ RspfileContent: `$in`,
+ },
+ "kotlincFlags", "classpath", "srcJars", "srcJarDir", "classesDir", "kotlinJvmTarget", "kotlinBuildFile")
+
+// kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile.
+func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars android.Paths,
+ flags javaBuilderFlags) {
+
+ var deps android.Paths
+ deps = append(deps, flags.kotlincClasspath...)
+ deps = append(deps, srcJars...)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: kotlinc,
+ Description: "kotlinc",
+ Output: outputFile,
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
+ "kotlincFlags": flags.kotlincFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "classesDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
+ "kotlinBuildFile": android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
+ // http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
+ "kotlinJvmTarget": "1.8",
+ },
+ })
+}
+
+var kapt = pctx.AndroidGomaStaticRule("kapt",
+ blueprint.RuleParams{
+ Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && mkdir -p "$srcJarDir" "$kaptDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.GenKotlinBuildFileCmd} $classpath "" $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
+ `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} $kotlincFlags ` +
+ `-Xplugin=${config.KotlinKaptJar} ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptDir/stubs ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:javacArguments=$encodedJavacFlags ` +
+ `$kaptProcessorPath ` +
+ `-Xbuild-file=$kotlinBuildFile && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources`,
+ CommandDeps: []string{
+ "${config.KotlincCmd}",
+ "${config.KotlinCompilerJar}",
+ "${config.KotlinKaptJar}",
+ "${config.GenKotlinBuildFileCmd}",
+ "${config.SoongZipCmd}",
+ "${config.ZipSyncCmd}",
+ },
+ Rspfile: "$out.rsp",
+ RspfileContent: `$in`,
+ },
+ "kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "classpath", "srcJars", "srcJarDir", "kaptDir", "kotlinJvmTarget", "kotlinBuildFile")
+
+// kotlinKapt performs Kotlin-compatible annotation processing. It takes .kt and .java sources and srcjars, and runs
+// annotation processors over all of them, producing a srcjar of generated code in outputFile. The srcjar should be
+// added as an additional input to kotlinc and javac rules, and the javac rule should have annotation processing
+// disabled.
+func kotlinKapt(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars android.Paths,
+ flags javaBuilderFlags) {
+
+ var deps android.Paths
+ deps = append(deps, flags.kotlincClasspath...)
+ deps = append(deps, srcJars...)
+ deps = append(deps, flags.processorPath...)
+
+ kaptProcessorPath := flags.processorPath.FormTurbineClasspath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
+
+ encodedJavacFlags := kaptEncodeFlags([][2]string{
+ {"-source", flags.javaVersion},
+ {"-target", flags.javaVersion},
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: kapt,
+ Description: "kapt",
+ Output: outputFile,
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
+ "kotlincFlags": flags.kotlincFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "srcJarDir": android.PathForModuleOut(ctx, "kapt", "srcJars").String(),
+ "kotlinBuildFile": android.PathForModuleOut(ctx, "kapt", "build.xml").String(),
+ "kaptProcessorPath": strings.Join(kaptProcessorPath, " "),
+ "kaptDir": android.PathForModuleOut(ctx, "kapt/gen").String(),
+ "encodedJavacFlags": encodedJavacFlags,
+ },
+ })
+}
+
+// kapt converts a list of key, value pairs into a base64 encoded Java serialization, which is what kapt expects.
+func kaptEncodeFlags(options [][2]string) string {
+ buf := &bytes.Buffer{}
+
+ binary.Write(buf, binary.BigEndian, uint32(len(options)))
+ for _, option := range options {
+ binary.Write(buf, binary.BigEndian, uint16(len(option[0])))
+ buf.WriteString(option[0])
+ binary.Write(buf, binary.BigEndian, uint16(len(option[1])))
+ buf.WriteString(option[1])
+ }
+
+ header := &bytes.Buffer{}
+ header.Write([]byte{0xac, 0xed, 0x00, 0x05}) // java serialization header
+
+ if buf.Len() < 256 {
+ header.WriteByte(0x77) // blockdata
+ header.WriteByte(byte(buf.Len()))
+ } else {
+ header.WriteByte(0x7a) // blockdatalong
+ binary.Write(header, binary.BigEndian, uint32(buf.Len()))
+ }
+
+ return base64.StdEncoding.EncodeToString(append(header.Bytes(), buf.Bytes()...))
+}
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
new file mode 100644
index 0000000..1069f42
--- /dev/null
+++ b/java/kotlin_test.go
@@ -0,0 +1,189 @@
+// Copyright 2017 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 (
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestKotlin(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.kt"],
+ libs: ["foo"],
+ static_libs: ["baz"],
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["c.java"],
+ }
+ `)
+
+ fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+ fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
+
+ if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
+ fooKotlinc.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
+ }
+
+ if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
+ }
+
+ if !strings.Contains(fooJavac.Args["classpath"], fooKotlinc.Output.String()) {
+ t.Errorf("foo classpath %v does not contain %q",
+ fooJavac.Args["classpath"], fooKotlinc.Output.String())
+ }
+
+ if !inList(fooKotlinc.Output.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %q",
+ fooJar.Inputs.Strings(), fooKotlinc.Output.String())
+ }
+
+ fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
+ bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
+ barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
+
+ if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
+ t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
+ }
+
+ if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
+ t.Errorf(`expected %q in bar implicits %v`,
+ fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+ }
+
+ if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
+ t.Errorf(`expected %q in bar implicits %v`,
+ bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+ }
+}
+
+func TestKapt(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ annotation_processors: ["bar"],
+ }
+
+ java_library_host {
+ name: "bar",
+ }
+ `)
+
+ kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+ kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+ javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+
+ // Test that the kotlin and java sources are passed to kapt and kotlinc
+ if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs)
+ }
+ if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
+ }
+
+ // Test that only the java sources are passed to javac
+ if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+ }
+
+ // Test that the kapt srcjar is a dependency of kotlinc and javac rules
+ if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) {
+ t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings())
+ }
+ if !inList(kapt.Output.String(), javac.Implicits.Strings()) {
+ t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings())
+ }
+
+ // Test that the kapt srcjar is extracted by the kotlinc and javac rules
+ if kotlinc.Args["srcJars"] != kapt.Output.String() {
+ t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ }
+ if javac.Args["srcJars"] != kapt.Output.String() {
+ t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ }
+}
+
+func TestKaptEncodeFlags(t *testing.T) {
+ // Compares the kaptEncodeFlags against the results of the example implementation at
+ // https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding
+ tests := []struct {
+ in [][2]string
+ out string
+ }{
+ {
+ // empty input
+ in: [][2]string{},
+ out: "rO0ABXcEAAAAAA==",
+ },
+ {
+ // common input
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ },
+ out: "rO0ABXcgAAAAAgAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjg=",
+ },
+ {
+ // input that serializes to a 255 byte block
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ {"a", strings.Repeat("b", 218)},
+ },
+ out: "rO0ABXf/AAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA2mJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJi",
+ },
+ {
+ // input that serializes to a 256 byte block
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ {"a", strings.Repeat("b", 219)},
+ },
+ out: "rO0ABXoAAAEAAAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA22JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYg==",
+ },
+ {
+ // input that serializes to a 257 byte block
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ {"a", strings.Repeat("b", 220)},
+ },
+ out: "rO0ABXoAAAEBAAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA3GJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI=",
+ },
+ }
+
+ for i, test := range tests {
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ got := kaptEncodeFlags(test.in)
+ if got != test.out {
+ t.Errorf("\nwant %q\n got %q", test.out, got)
+ }
+ })
+ }
+}