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:
+2
-7
@@ -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_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget"
|
||||
|
||||
private const val KOTLIN_ONLY_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinOnlyTarget"
|
||||
|
||||
internal val jvmPresetEntry = KotlinPresetEntry(
|
||||
"jvm",
|
||||
typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"),
|
||||
typeName(
|
||||
KOTLIN_ONLY_TARGET_CLASS_FQNAME,
|
||||
"$MPP_PACKAGE.KotlinJvmCompilation"
|
||||
)
|
||||
typeName("org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget")
|
||||
)
|
||||
|
||||
internal val jsPresetEntry = KotlinPresetEntry(
|
||||
"js",
|
||||
typeName("$MPP_PACKAGE.KotlinJsTargetPreset"),
|
||||
typeName(
|
||||
KOTLIN_ONLY_TARGET_CLASS_FQNAME,
|
||||
"$MPP_PACKAGE.KotlinOnlyTarget",
|
||||
"$MPP_PACKAGE.KotlinJsCompilation"
|
||||
)
|
||||
)
|
||||
|
||||
+119
-38
@@ -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))
|
||||
|
||||
+11
@@ -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'
|
||||
|
||||
+5
-1
@@ -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()")
|
||||
}
|
||||
+3
-3
@@ -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.KotlinJsCompilation
|
||||
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.KotlinNativeTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset
|
||||
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
|
||||
|
||||
@@ -19,8 +19,8 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP
|
||||
|
||||
fun jvm(
|
||||
name: String = "jvm",
|
||||
configure: KotlinOnlyTarget<KotlinJvmCompilation>.() -> Unit = { }
|
||||
): KotlinOnlyTarget<KotlinJvmCompilation> =
|
||||
configure: KotlinJvmTarget.() -> Unit = { }
|
||||
): KotlinJvmTarget =
|
||||
configureOrCreate(
|
||||
name,
|
||||
presets.getByName("jvm") as KotlinJvmTargetPreset,
|
||||
|
||||
+16
-9
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.BasePlugin
|
||||
import com.android.build.gradle.api.AndroidSourceSet
|
||||
import com.android.build.gradle.api.BaseVariant
|
||||
import com.android.builder.model.SourceProvider
|
||||
import groovy.lang.Closure
|
||||
@@ -430,8 +431,9 @@ internal abstract class AbstractKotlinPlugin(
|
||||
jarTask.dependsOn(inspectTask.getTaskOrProvider())
|
||||
}
|
||||
|
||||
private fun setUpJavaSourceSets(
|
||||
kotlinTarget: KotlinWithJavaTarget<*>
|
||||
internal fun setUpJavaSourceSets(
|
||||
kotlinTarget: KotlinTarget,
|
||||
duplicateJavaSourceSetsAsKotlinSourceSets: Boolean = true
|
||||
) {
|
||||
val project = kotlinTarget.project
|
||||
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:
|
||||
val compilationsUnderConstruction = mutableMapOf<String, KotlinWithJavaCompilation<*>>()
|
||||
val compilationsUnderConstruction = mutableMapOf<String, KotlinCompilation<*>>()
|
||||
|
||||
javaSourceSets.all { javaSourceSet ->
|
||||
val kotlinCompilation =
|
||||
compilationsUnderConstruction[javaSourceSet.name] ?: kotlinTarget.compilations.maybeCreate(javaSourceSet.name)
|
||||
kotlinCompilation.javaSourceSet = javaSourceSet
|
||||
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name)
|
||||
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet)
|
||||
kotlinSourceSet.kotlin.source(javaSourceSet.java)
|
||||
kotlinCompilation.source(kotlinSourceSet)
|
||||
(kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet = javaSourceSet
|
||||
|
||||
if (duplicateJavaSourceSetsAsKotlinSourceSets) {
|
||||
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name)
|
||||
kotlinSourceSet.kotlin.source(javaSourceSet.java)
|
||||
kotlinCompilation.source(kotlinSourceSet)
|
||||
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet)
|
||||
} else {
|
||||
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinCompilation.defaultSourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
kotlinTarget.compilations.all { kotlinCompilation ->
|
||||
val sourceSetName = kotlinCompilation.name
|
||||
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:
|
||||
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.defaultSourceSetName)
|
||||
|
||||
+3
@@ -301,6 +301,9 @@ internal val KotlinCompilation<*>.deprecatedCompileConfigurationName: String
|
||||
internal val KotlinCompilationToRunnableFiles<*>.deprecatedRuntimeConfigurationName: String
|
||||
get() = disambiguateName("runtime")
|
||||
|
||||
internal val KotlinTarget.testTaskName: String
|
||||
get() = lowerCamelCaseName(targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix)
|
||||
|
||||
abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>>(
|
||||
createDefaultSourceSets: Boolean,
|
||||
createTestCompilation: Boolean,
|
||||
|
||||
+10
-5
@@ -6,20 +6,25 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||
|
||||
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 kotlinPluginVersion: String
|
||||
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
|
||||
) : KotlinTargetPreset<R> {
|
||||
|
||||
protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T>
|
||||
|
||||
protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? =
|
||||
target.targetName
|
||||
|
||||
override fun createTarget(name: String): KotlinOnlyTarget<T> {
|
||||
val result = KotlinOnlyTarget<T>(project, platformType).apply {
|
||||
abstract protected fun instantiateTarget(): R
|
||||
|
||||
override fun createTarget(name: String): R {
|
||||
val result = instantiateTarget().apply {
|
||||
targetName = name
|
||||
disambiguationClassifier = provideTargetDisambiguationClassifier(this@apply)
|
||||
preset = this@KotlinOnlyTargetPreset
|
||||
|
||||
+3
-1
@@ -13,10 +13,12 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTargetConfigurator
|
||||
open class KotlinJsTargetPreset(
|
||||
project: Project,
|
||||
kotlinPluginVersion: String
|
||||
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
|
||||
) : KotlinOnlyTargetPreset<KotlinOnlyTarget<KotlinJsCompilation>, KotlinJsCompilation>(
|
||||
project,
|
||||
kotlinPluginVersion
|
||||
) {
|
||||
override fun instantiateTarget() = KotlinOnlyTarget<KotlinJsCompilation>(project, KotlinPlatformType.js)
|
||||
|
||||
override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator(kotlinPluginVersion)
|
||||
|
||||
override fun getName(): String = PRESET_NAME
|
||||
|
||||
+162
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -7,20 +7,19 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||
|
||||
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.KotlinSourceSetProcessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTargetConfigurator
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
||||
|
||||
class KotlinJvmTargetPreset(
|
||||
project: Project,
|
||||
kotlinPluginVersion: String
|
||||
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
|
||||
) : KotlinOnlyTargetPreset<KotlinJvmTarget, KotlinJvmCompilation>(
|
||||
project,
|
||||
kotlinPluginVersion
|
||||
) {
|
||||
override fun instantiateTarget(): KotlinJvmTarget = KotlinJvmTarget(project)
|
||||
|
||||
override fun getName(): String = PRESET_NAME
|
||||
|
||||
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
|
||||
|
||||
+19
@@ -10,7 +10,9 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
||||
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.utils.SingleWarningPerBuild
|
||||
|
||||
class KotlinJvmWithJavaTargetPreset(
|
||||
private val project: Project,
|
||||
@@ -20,6 +22,11 @@ class KotlinJvmWithJavaTargetPreset(
|
||||
override fun getName(): String = PRESET_NAME
|
||||
|
||||
override fun createTarget(name: String): KotlinWithJavaTarget<KotlinJvmOptions> {
|
||||
SingleWarningPerBuild.show(
|
||||
project,
|
||||
DEPRECATION_WARNING
|
||||
)
|
||||
|
||||
project.plugins.apply(JavaPlugin::class.java)
|
||||
|
||||
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, name).apply {
|
||||
@@ -50,5 +57,17 @@ class KotlinJvmWithJavaTargetPreset(
|
||||
|
||||
companion object {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -9,18 +9,22 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
|
||||
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.targets.metadata.KotlinMetadataTargetConfigurator
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
||||
|
||||
class KotlinMetadataTargetPreset(
|
||||
project: Project,
|
||||
kotlinPluginVersion: String
|
||||
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
|
||||
) : KotlinOnlyTargetPreset<KotlinOnlyTarget<KotlinCommonCompilation>, KotlinCommonCompilation>(
|
||||
project,
|
||||
kotlinPluginVersion
|
||||
) {
|
||||
override fun instantiateTarget() = KotlinOnlyTarget<KotlinCommonCompilation>(project, KotlinPlatformType.common)
|
||||
|
||||
override fun getName(): String = PRESET_NAME
|
||||
|
||||
override fun createCompilationFactory(
|
||||
|
||||
Reference in New Issue
Block a user