Introduce Java support in JVM MPP targets (KT-26256)

Deprecate the `jvmWithJava` preset, add warning.

Add a function `withJava()` to JVM MPP targets which applies the 'java'
plugin and sets up the Kotlin & Java interoperation.

Issue #KT-26256 Fixed

Signed-off-by: Sergey Igushkin <hotkeytlt@gmail.com>
This commit is contained in:
Sergey Igushkin
2019-04-26 15:21:31 +03:00
parent 4625002efe
commit be05095df5
13 changed files with 364 additions and 72 deletions
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmWithJavaTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind
import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.util.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
@@ -299,52 +301,131 @@ class NewMultiplatformIT : BaseGradleIT() {
}
@Test
fun testJvmWithJavaEquivalence() = with(Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")) {
lateinit var classesWithoutJava: Set<String>
fun testJvmWithJavaEquivalence() = doTestJvmWithJava(testJavaSupportInJvmTargets = false)
fun getFilePathsSet(inDirectory: String): Set<String> {
val dir = projectDir.resolve(inDirectory)
return dir.walk().filter { it.isFile }.map { it.relativeTo(dir).path.replace('\\', '/') }.toSet()
}
@Test
fun testJavaSupportInJvmTargets() = doTestJvmWithJava(testJavaSupportInJvmTargets = true)
build("assemble") {
assertSuccessful()
classesWithoutJava = getFilePathsSet("build/classes")
}
private fun doTestJvmWithJava(testJavaSupportInJvmTargets: Boolean) =
with(Project("sample-lib", GradleVersionRequired.AtLeast("5.0"), "new-mpp-lib-and-app")) {
lateinit var classesWithoutJava: Set<String>
gradleBuildScript().modify {
it.replace("presets.jvm", "presets.jvmWithJava").replace("jvm(", "targetFromPreset(presets.jvmWithJava, ")
}
fun getFilePathsSet(inDirectory: String): Set<String> {
val dir = projectDir.resolve(inDirectory)
return dir.walk().filter { it.isFile }.map { it.relativeTo(dir).invariantSeparatorsPath }.toSet()
}
projectDir.resolve("src/main/java").apply {
mkdirs()
mkdir()
// Check that Java can access the dependencies (kotlin-stdlib):
resolve("JavaClassInJava.java").writeText("""
package com.example.lib;
import kotlin.sequences.Sequence;
class JavaClassInJava {
Sequence<String> makeSequence() { throw new UnsupportedOperationException(); }
build("assemble") {
assertSuccessful()
classesWithoutJava = getFilePathsSet("build/classes")
}
gradleBuildScript().modify {
if (testJavaSupportInJvmTargets) {
it + "\nkotlin.jvm(\"jvm6\").${KotlinJvmTarget::withJava.name}()"
} else {
it.replace("presets.jvm", "presets.jvmWithJava").replace("jvm(", "targetFromPreset(presets.jvmWithJava, ")
}.plus(
"\n" + """
buildscript {
repositories {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:5.0.0'
}
}
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'application'
mainClassName = 'com.example.lib.CommonKt'
""".trimIndent()
)
}
fun javaSourceRootForCompilation(compilationName: String) =
if (testJavaSupportInJvmTargets) "src/jvm6${compilationName.capitalize()}/java" else "src/$compilationName/java"
val javaMainSrcDir = javaSourceRootForCompilation("main")
val javaTestSrcDir = javaSourceRootForCompilation("test")
projectDir.resolve(javaMainSrcDir).apply {
mkdirs()
// Check that Java can access the dependencies (kotlin-stdlib):
resolve("JavaClassInJava.java").writeText(
"""
package com.example.lib;
import kotlin.sequences.Sequence;
class JavaClassInJava {
Sequence<String> makeSequence() { throw new UnsupportedOperationException(); }
}
""".trimIndent()
)
// Add a Kotlin source file in the Java source root and check that it is compiled:
resolve("KotlinClassInJava.kt").writeText(
"""
package com.example.lib
class KotlinClassInJava
""".trimIndent()
)
}
projectDir.resolve(javaTestSrcDir).apply {
mkdirs()
resolve("JavaTest.java").writeText(
"""
package com.example.lib;
import org.junit.*;
public class JavaTest {
@Test
public void testAccessKotlin() {
MainKt.expectedFun();
MainKt.x();
new KotlinClassInJava();
new JavaClassInJava();
}
}
""".trimIndent()
)
}
build("clean", "build", "run", "shadowJar") {
assertSuccessful()
val expectedMainClasses =
classesWithoutJava + setOf(
"kotlin/jvm6/main/com/example/lib/KotlinClassInJava.class",
"java/main/com/example/lib/JavaClassInJava.class",
"java/test/com/example/lib/JavaTest.class"
)
val actualClasses = getFilePathsSet("build/classes")
Assert.assertEquals(expectedMainClasses, actualClasses)
val jvmTestTaskName = if (testJavaSupportInJvmTargets) "jvm6Test" else "test"
assertTasksExecuted(":$jvmTestTaskName")
assertFileExists("build/reports/tests/$jvmTestTaskName/classes/com.example.lib.JavaTest.html")
if (testJavaSupportInJvmTargets) {
assertNotContains(KotlinJvmWithJavaTargetPreset.DEPRECATION_WARNING)
} else {
assertContains(KotlinJvmWithJavaTargetPreset.DEPRECATION_WARNING)
}
""".trimIndent())
// Add a Kotlin source file in the Java source root and check that it is compiled:
resolve("KotlinClassInJava.kt").writeText("""
package com.example.lib
class KotlinClassInJava
""".trimIndent())
}
assertTasksExecuted(":run")
assertContains(">>> Common.kt >>> main()")
build("clean", "assemble") {
assertSuccessful()
val expectedClasses =
classesWithoutJava +
"kotlin/jvm6/main/com/example/lib/KotlinClassInJava.class" +
"java/main/com/example/lib/JavaClassInJava.class"
val actualClasses = getFilePathsSet("build/classes")
Assert.assertEquals(expectedClasses, actualClasses)
assertTasksExecuted(":shadowJar")
val entries = ZipFile(projectDir.resolve("build/libs/sample-lib-1.0-all.jar")).use { zip ->
zip.entries().asSequence().map { it.name }.toSet()
}
assertTrue { "kotlin/Pair.class" in entries }
assertTrue { "com/example/lib/CommonKt.class" in entries }
assertTrue { "com/example/lib/MainKt.class" in entries }
assertTrue { "com/example/lib/JavaClassInJava.class" in entries }
assertTrue { "com/example/lib/KotlinClassInJava.class" in entries }
}
}
}
@Test
fun testLibWithTests() = doTestLibWithTests(Project("new-mpp-lib-with-tests", gradleVersion))
@@ -63,12 +63,23 @@ kotlin {
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
}
}
commonTest {
dependencies {
implementation kotlin("test-common")
implementation kotlin("test-annotations-common")
}
}
jvm6Main {
dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4'
}
}
jvm6Test {
dependencies {
implementation kotlin("test-junit")
}
}
nodeJsMain {
dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib-js'
@@ -2,4 +2,8 @@ package com.example.lib
fun <T> id(t: T): T = t
expect fun expectedFun(): Unit
expect fun expectedFun(): Unit
fun main() {
println(">>> Common.kt >>> main()")
}