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
@@ -21,22 +21,17 @@ internal const val MPP_PACKAGE = "org.jetbrains.kotlin.gradle.plugin.mpp"
internal const val KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset" internal const val KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset"
internal const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget" internal const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget"
private const val KOTLIN_ONLY_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinOnlyTarget"
internal val jvmPresetEntry = KotlinPresetEntry( internal val jvmPresetEntry = KotlinPresetEntry(
"jvm", "jvm",
typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"), typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"),
typeName( typeName("org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget")
KOTLIN_ONLY_TARGET_CLASS_FQNAME,
"$MPP_PACKAGE.KotlinJvmCompilation"
)
) )
internal val jsPresetEntry = KotlinPresetEntry( internal val jsPresetEntry = KotlinPresetEntry(
"js", "js",
typeName("$MPP_PACKAGE.KotlinJsTargetPreset"), typeName("$MPP_PACKAGE.KotlinJsTargetPreset"),
typeName( typeName(
KOTLIN_ONLY_TARGET_CLASS_FQNAME, "$MPP_PACKAGE.KotlinOnlyTarget",
"$MPP_PACKAGE.KotlinJsCompilation" "$MPP_PACKAGE.KotlinJsCompilation"
) )
) )
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.gradle package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations 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.KotlinMultiplatformPlugin
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind
import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker 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.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks 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.gradle.util.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
@@ -299,52 +301,131 @@ class NewMultiplatformIT : BaseGradleIT() {
} }
@Test @Test
fun testJvmWithJavaEquivalence() = with(Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")) { fun testJvmWithJavaEquivalence() = doTestJvmWithJava(testJavaSupportInJvmTargets = false)
lateinit var classesWithoutJava: Set<String>
fun getFilePathsSet(inDirectory: String): Set<String> { @Test
val dir = projectDir.resolve(inDirectory) fun testJavaSupportInJvmTargets() = doTestJvmWithJava(testJavaSupportInJvmTargets = true)
return dir.walk().filter { it.isFile }.map { it.relativeTo(dir).path.replace('\\', '/') }.toSet()
}
build("assemble") { private fun doTestJvmWithJava(testJavaSupportInJvmTargets: Boolean) =
assertSuccessful() with(Project("sample-lib", GradleVersionRequired.AtLeast("5.0"), "new-mpp-lib-and-app")) {
classesWithoutJava = getFilePathsSet("build/classes") lateinit var classesWithoutJava: Set<String>
}
gradleBuildScript().modify { fun getFilePathsSet(inDirectory: String): Set<String> {
it.replace("presets.jvm", "presets.jvmWithJava").replace("jvm(", "targetFromPreset(presets.jvmWithJava, ") val dir = projectDir.resolve(inDirectory)
} return dir.walk().filter { it.isFile }.map { it.relativeTo(dir).invariantSeparatorsPath }.toSet()
}
projectDir.resolve("src/main/java").apply { build("assemble") {
mkdirs() assertSuccessful()
mkdir() classesWithoutJava = getFilePathsSet("build/classes")
// Check that Java can access the dependencies (kotlin-stdlib): }
resolve("JavaClassInJava.java").writeText("""
package com.example.lib; gradleBuildScript().modify {
import kotlin.sequences.Sequence; if (testJavaSupportInJvmTargets) {
class JavaClassInJava { it + "\nkotlin.jvm(\"jvm6\").${KotlinJvmTarget::withJava.name}()"
Sequence<String> makeSequence() { throw new UnsupportedOperationException(); } } 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: assertTasksExecuted(":run")
resolve("KotlinClassInJava.kt").writeText(""" assertContains(">>> Common.kt >>> main()")
package com.example.lib
class KotlinClassInJava
""".trimIndent())
}
build("clean", "assemble") { assertTasksExecuted(":shadowJar")
assertSuccessful() val entries = ZipFile(projectDir.resolve("build/libs/sample-lib-1.0-all.jar")).use { zip ->
val expectedClasses = zip.entries().asSequence().map { it.name }.toSet()
classesWithoutJava + }
"kotlin/jvm6/main/com/example/lib/KotlinClassInJava.class" + assertTrue { "kotlin/Pair.class" in entries }
"java/main/com/example/lib/JavaClassInJava.class" assertTrue { "com/example/lib/CommonKt.class" in entries }
val actualClasses = getFilePathsSet("build/classes") assertTrue { "com/example/lib/MainKt.class" in entries }
Assert.assertEquals(expectedClasses, actualClasses) assertTrue { "com/example/lib/JavaClassInJava.class" in entries }
assertTrue { "com/example/lib/KotlinClassInJava.class" in entries }
}
} }
}
@Test @Test
fun testLibWithTests() = doTestLibWithTests(Project("new-mpp-lib-with-tests", gradleVersion)) fun testLibWithTests() = doTestLibWithTests(Project("new-mpp-lib-with-tests", gradleVersion))
@@ -63,12 +63,23 @@ kotlin {
api 'org.jetbrains.kotlin:kotlin-stdlib-common' api 'org.jetbrains.kotlin:kotlin-stdlib-common'
} }
} }
commonTest {
dependencies {
implementation kotlin("test-common")
implementation kotlin("test-annotations-common")
}
}
jvm6Main { jvm6Main {
dependencies { dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib' api 'org.jetbrains.kotlin:kotlin-stdlib'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4'
} }
} }
jvm6Test {
dependencies {
implementation kotlin("test-junit")
}
}
nodeJsMain { nodeJsMain {
dependencies { dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib-js' api 'org.jetbrains.kotlin:kotlin-stdlib-js'
@@ -3,3 +3,7 @@ package com.example.lib
fun <T> id(t: T): T = t fun <T> id(t: T): T = t
expect fun expectedFun(): Unit expect fun expectedFun(): Unit
fun main() {
println(">>> Common.kt >>> main()")
}
@@ -7,11 +7,11 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
// DO NOT EDIT MANUALLY! Generated by org.jetbrains.kotlin.generators.gradle.dsl.MppPresetFunctionsCodegenKt // DO NOT EDIT MANUALLY! Generated by org.jetbrains.kotlin.generators.gradle.dsl.MppPresetFunctionsCodegenKt
@@ -19,8 +19,8 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP
fun jvm( fun jvm(
name: String = "jvm", name: String = "jvm",
configure: KotlinOnlyTarget<KotlinJvmCompilation>.() -> Unit = { } configure: KotlinJvmTarget.() -> Unit = { }
): KotlinOnlyTarget<KotlinJvmCompilation> = ): KotlinJvmTarget =
configureOrCreate( configureOrCreate(
name, name,
presets.getByName("jvm") as KotlinJvmTargetPreset, presets.getByName("jvm") as KotlinJvmTargetPreset,
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.BaseExtension import com.android.build.gradle.BaseExtension
import com.android.build.gradle.BasePlugin import com.android.build.gradle.BasePlugin
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.BaseVariant
import com.android.builder.model.SourceProvider import com.android.builder.model.SourceProvider
import groovy.lang.Closure import groovy.lang.Closure
@@ -430,8 +431,9 @@ internal abstract class AbstractKotlinPlugin(
jarTask.dependsOn(inspectTask.getTaskOrProvider()) jarTask.dependsOn(inspectTask.getTaskOrProvider())
} }
private fun setUpJavaSourceSets( internal fun setUpJavaSourceSets(
kotlinTarget: KotlinWithJavaTarget<*> kotlinTarget: KotlinTarget,
duplicateJavaSourceSetsAsKotlinSourceSets: Boolean = true
) { ) {
val project = kotlinTarget.project val project = kotlinTarget.project
val javaSourceSets = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets val javaSourceSets = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
@@ -442,22 +444,27 @@ internal abstract class AbstractKotlinPlugin(
} }
// Workaround for indirect mutual recursion between the two `all { ... }` handlers: // Workaround for indirect mutual recursion between the two `all { ... }` handlers:
val compilationsUnderConstruction = mutableMapOf<String, KotlinWithJavaCompilation<*>>() val compilationsUnderConstruction = mutableMapOf<String, KotlinCompilation<*>>()
javaSourceSets.all { javaSourceSet -> javaSourceSets.all { javaSourceSet ->
val kotlinCompilation = val kotlinCompilation =
compilationsUnderConstruction[javaSourceSet.name] ?: kotlinTarget.compilations.maybeCreate(javaSourceSet.name) compilationsUnderConstruction[javaSourceSet.name] ?: kotlinTarget.compilations.maybeCreate(javaSourceSet.name)
kotlinCompilation.javaSourceSet = javaSourceSet (kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet = javaSourceSet
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name)
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet) if (duplicateJavaSourceSetsAsKotlinSourceSets) {
kotlinSourceSet.kotlin.source(javaSourceSet.java) val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name)
kotlinCompilation.source(kotlinSourceSet) kotlinSourceSet.kotlin.source(javaSourceSet.java)
kotlinCompilation.source(kotlinSourceSet)
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet)
} else {
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinCompilation.defaultSourceSet)
}
} }
kotlinTarget.compilations.all { kotlinCompilation -> kotlinTarget.compilations.all { kotlinCompilation ->
val sourceSetName = kotlinCompilation.name val sourceSetName = kotlinCompilation.name
compilationsUnderConstruction[sourceSetName] = kotlinCompilation compilationsUnderConstruction[sourceSetName] = kotlinCompilation
kotlinCompilation.javaSourceSet = javaSourceSets.maybeCreate(sourceSetName) (kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet = javaSourceSets.maybeCreate(sourceSetName)
// Another Kotlin source set following the other convention, named according to the compilation, not the Java source set: // Another Kotlin source set following the other convention, named according to the compilation, not the Java source set:
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.defaultSourceSetName) val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.defaultSourceSetName)
@@ -301,6 +301,9 @@ internal val KotlinCompilation<*>.deprecatedCompileConfigurationName: String
internal val KotlinCompilationToRunnableFiles<*>.deprecatedRuntimeConfigurationName: String internal val KotlinCompilationToRunnableFiles<*>.deprecatedRuntimeConfigurationName: String
get() = disambiguateName("runtime") get() = disambiguateName("runtime")
internal val KotlinTarget.testTaskName: String
get() = lowerCamelCaseName(targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix)
abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>>( abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>>(
createDefaultSourceSets: Boolean, createDefaultSourceSets: Boolean,
createTestCompilation: Boolean, createTestCompilation: Boolean,
@@ -6,20 +6,25 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>( abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompilation<*>>(
protected val project: Project, protected val project: Project,
protected val kotlinPluginVersion: String protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> { ) : KotlinTargetPreset<R> {
protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T>
protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? = protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? =
target.targetName target.targetName
override fun createTarget(name: String): KotlinOnlyTarget<T> { abstract protected fun instantiateTarget(): R
val result = KotlinOnlyTarget<T>(project, platformType).apply {
override fun createTarget(name: String): R {
val result = instantiateTarget().apply {
targetName = name targetName = name
disambiguationClassifier = provideTargetDisambiguationClassifier(this@apply) disambiguationClassifier = provideTargetDisambiguationClassifier(this@apply)
preset = this@KotlinOnlyTargetPreset preset = this@KotlinOnlyTargetPreset
@@ -13,10 +13,12 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTargetConfigurator
open class KotlinJsTargetPreset( open class KotlinJsTargetPreset(
project: Project, project: Project,
kotlinPluginVersion: String kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>( ) : KotlinOnlyTargetPreset<KotlinOnlyTarget<KotlinJsCompilation>, KotlinJsCompilation>(
project, project,
kotlinPluginVersion kotlinPluginVersion
) { ) {
override fun instantiateTarget() = KotlinOnlyTarget<KotlinJsCompilation>(project, KotlinPlatformType.js)
override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator(kotlinPluginVersion) override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator(kotlinPluginVersion)
override fun getName(): String = PRESET_NAME override fun getName(): String = PRESET_NAME
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.jvm
import org.gradle.api.InvalidUserCodeException
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.testing.Test
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import java.util.concurrent.Callable
class KotlinJvmTarget(
project: Project
) : KotlinOnlyTarget<KotlinJvmCompilation>(
project, KotlinPlatformType.jvm
) {
var withJavaEnabled = false
private set
@Suppress("unused") // user DSL
fun withJava() {
project.multiplatformExtension.targets.find { it is KotlinJvmTarget && it.withJavaEnabled }
?.let { existingJavaTarget ->
throw InvalidUserCodeException(
"Only one of the JVM targets can be configured to work with Java. The target '${existingJavaTarget.name}' is " +
"already set up to work with Java; cannot setup another target '$targetName'"
)
}
withJavaEnabled = true
project.plugins.apply(JavaPlugin::class.java)
val javaPluginConvention = project.convention.getPlugin(JavaPluginConvention::class.java)
AbstractKotlinPlugin.setUpJavaSourceSets(this, duplicateJavaSourceSetsAsKotlinSourceSets = false)
// Below, some effort is made to ensure that a user or 3rd-party plugin that inspects or interacts
// with the entities created by the Java plugin, not knowing of the existence of the Kotlin plugin,
// sees 'the right picture' of the inputs and outputs, for example:
// * the relevant dependencies for Java and Kotlin are in sync,
// * the Java outputs contain the outputs produced by Kotlin as well
javaPluginConvention.sourceSets.all { javaSourceSet ->
val compilation = compilations.getByName(javaSourceSet.name)
val compileJavaTask = project.tasks.getByName(javaSourceSet.compileJavaTaskName) as AbstractCompile
configureJavaTask(compilation.compileKotlinTask, compileJavaTask, project.logger)
setupJavaSourceSetSourcesAndResources(javaSourceSet, compilation)
val javaClasses = project.files(Callable { compileJavaTask.destinationDir }).builtBy(compileJavaTask)
compilation.output.classesDirs.from(javaClasses)
(javaSourceSet.output.classesDirs as? ConfigurableFileCollection)?.from(
compilation.output.classesDirs.minus(javaClasses)
)
javaSourceSet.output.setResourcesDir(Callable { compilation.output.resourcesDirProvider })
setupDependenciesCrossInclusionForJava(compilation, javaSourceSet)
}
// Eliminate the Java output configurations from dependency resolution to avoid ambiguity between them and
// the equivalent configurations created for the target:
listOf(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME)
.forEach { outputConfigurationName ->
project.configurations.findByName(outputConfigurationName)?.isCanBeConsumed = false
}
disableJavaPluginTasks(javaPluginConvention)
}
private fun setupJavaSourceSetSourcesAndResources(
javaSourceSet: SourceSet,
compilation: KotlinJvmCompilation
) {
javaSourceSet.java.setSrcDirs(listOf("src/${compilation.defaultSourceSet.name}/java"))
compilation.defaultSourceSet.kotlin.srcDirs(javaSourceSet.java.sourceDirectories)
// To avoid confusion in the sources layout, remove the default Java source directories
// (like src/main/java, src/test/java) and instead add sibling directories to those where the Kotlin
// sources are placed (i.e. src/jvmMain/java, src/jvmTest/java):
javaSourceSet.resources.setSrcDirs(compilation.defaultSourceSet.resources.sourceDirectories)
compilation.defaultSourceSet.resources.srcDirs(javaSourceSet.resources.sourceDirectories)
// Resources processing is done with the Kotlin resource processing task:
val processJavaResourcesTask = project.tasks.getByName(javaSourceSet.processResourcesTaskName)
processJavaResourcesTask.dependsOn(project.tasks.getByName(compilation.processResourcesTaskName))
processJavaResourcesTask.enabled = false
}
private fun disableJavaPluginTasks(javaPluginConvention: JavaPluginConvention) {
// A 'normal' build should not do redundant job like running the tests twice or building two JARs,
// so disable some tasks and just make them depend on the others:
val targetJar = project.tasks.getByName(artifactsTaskName) as Jar
val javaJar = project.tasks.getByName(javaPluginConvention.sourceSets.getByName("main").jarTaskName) as Jar
(javaJar.source as? ConfigurableFileCollection)?.setFrom(targetJar.source)
javaJar.conventionMapping("archiveName") { targetJar.archiveName }
javaJar.dependsOn(targetJar)
javaJar.enabled = false
val javaTestTask = project.tasks.getByName(JavaPlugin.TEST_TASK_NAME) as Test
javaTestTask.dependsOn(project.tasks.getByName(testTaskName) as Test)
javaTestTask.enabled = false
}
private fun setupDependenciesCrossInclusionForJava(
compilation: KotlinJvmCompilation,
javaSourceSet: SourceSet
) {
// Make sure Kotlin compilation dependencies appear in the Java source set classpaths:
listOf(
compilation.apiConfigurationName,
compilation.implementationConfigurationName,
compilation.compileOnlyConfigurationName,
compilation.deprecatedCompileConfigurationName
).forEach { configurationName ->
project.addExtendsFromRelation(javaSourceSet.compileClasspathConfigurationName, configurationName)
}
listOf(
compilation.apiConfigurationName,
compilation.implementationConfigurationName,
compilation.runtimeOnlyConfigurationName,
compilation.deprecatedRuntimeConfigurationName
).forEach { configurationName ->
project.addExtendsFromRelation(javaSourceSet.runtimeClasspathConfigurationName, configurationName)
}
// Add the Java source set dependencies to the Kotlin compilation compile & runtime configurations:
listOfNotNull(
javaSourceSet.compileConfigurationName,
javaSourceSet.compileOnlyConfigurationName,
javaSourceSet.apiConfigurationName.takeIf { project.configurations.findByName(it) != null },
javaSourceSet.implementationConfigurationName
).forEach { configurationName ->
project.addExtendsFromRelation(compilation.compileDependencyConfigurationName, configurationName)
}
listOfNotNull(
javaSourceSet.runtimeConfigurationName,
javaSourceSet.runtimeOnlyConfigurationName,
javaSourceSet.apiConfigurationName.takeIf { project.configurations.findByName(it) != null },
javaSourceSet.implementationConfigurationName
).forEach { configurationName ->
project.addExtendsFromRelation(compilation.runtimeDependencyConfigurationName, configurationName)
}
}
}
@@ -7,20 +7,19 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTargetConfigurator import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTargetConfigurator
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
class KotlinJvmTargetPreset( class KotlinJvmTargetPreset(
project: Project, project: Project,
kotlinPluginVersion: String kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>( ) : KotlinOnlyTargetPreset<KotlinJvmTarget, KotlinJvmCompilation>(
project, project,
kotlinPluginVersion kotlinPluginVersion
) { ) {
override fun instantiateTarget(): KotlinJvmTarget = KotlinJvmTarget(project)
override fun getName(): String = PRESET_NAME override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> = override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
@@ -10,7 +10,9 @@ import org.gradle.api.Project
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
class KotlinJvmWithJavaTargetPreset( class KotlinJvmWithJavaTargetPreset(
private val project: Project, private val project: Project,
@@ -20,6 +22,11 @@ class KotlinJvmWithJavaTargetPreset(
override fun getName(): String = PRESET_NAME override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> { override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
SingleWarningPerBuild.show(
project,
DEPRECATION_WARNING
)
project.plugins.apply(JavaPlugin::class.java) project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply { val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
@@ -50,5 +57,17 @@ class KotlinJvmWithJavaTargetPreset(
companion object { companion object {
const val PRESET_NAME = "jvmWithJava" const val PRESET_NAME = "jvmWithJava"
val DEPRECATION_WARNING = "\nThe 'jvmWithJava' preset is deprecated and will be removed soon. " +
"Please use an ordinary JVM target with Java support: \n\n" +
" kotlin { \n" +
" jvm { \n" +
" ${KotlinJvmTarget::withJava.name}() \n" +
" } \n" +
" }\n\n" +
"After this change, please move the Java sources to the Kotlin source set directories. " +
"For example, if the JVM target is given the default name 'jvm':\n" +
" * instead of 'src/main/java', use 'src/jvmMain/java'\n" +
" * instead of 'src/test/java', use 'src/jvmTest/java'\n"
} }
} }
@@ -9,18 +9,22 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
class KotlinMetadataTargetPreset( class KotlinMetadataTargetPreset(
project: Project, project: Project,
kotlinPluginVersion: String kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>( ) : KotlinOnlyTargetPreset<KotlinOnlyTarget<KotlinCommonCompilation>, KotlinCommonCompilation>(
project, project,
kotlinPluginVersion kotlinPluginVersion
) { ) {
override fun instantiateTarget() = KotlinOnlyTarget<KotlinCommonCompilation>(project, KotlinPlatformType.common)
override fun getName(): String = PRESET_NAME override fun getName(): String = PRESET_NAME
override fun createCompilationFactory( override fun createCompilationFactory(