diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt index 66dd6f979e0..67339962752 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt @@ -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" ) ) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index 479ee2472d3..20d9020b7d6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -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 + fun testJvmWithJavaEquivalence() = doTestJvmWithJava(testJavaSupportInJvmTargets = false) - fun getFilePathsSet(inDirectory: String): Set { - 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 - gradleBuildScript().modify { - it.replace("presets.jvm", "presets.jvmWithJava").replace("jvm(", "targetFromPreset(presets.jvmWithJava, ") - } + fun getFilePathsSet(inDirectory: String): Set { + 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 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 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)) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/build.gradle index 34281d104d8..d22c1a562d7 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/build.gradle @@ -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' diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/src/commonMain/kotlin/Common.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/src/commonMain/kotlin/Common.kt index 140f108c9a9..a5a482b1e47 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/src/commonMain/kotlin/Common.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib/src/commonMain/kotlin/Common.kt @@ -2,4 +2,8 @@ package com.example.lib fun id(t: T): T = t -expect fun expectedFun(): Unit \ No newline at end of file +expect fun expectedFun(): Unit + +fun main() { + println(">>> Common.kt >>> main()") +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt index 5872e5478db..5e8f9e9fa06 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt @@ -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.() -> Unit = { } - ): KotlinOnlyTarget = + configure: KotlinJvmTarget.() -> Unit = { } + ): KotlinJvmTarget = configureOrCreate( name, presets.getByName("jvm") as KotlinJvmTargetPreset, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 97ac8d10120..ec1011925b4 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -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>() + val compilationsUnderConstruction = mutableMapOf>() 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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index 5907d8103c0..75664049051 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -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>( createDefaultSourceSets: Boolean, createTestCompilation: Boolean, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index 0fa4b278e5e..83d8b73ecf1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -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>( +abstract class KotlinOnlyTargetPreset, T : KotlinCompilation<*>>( protected val project: Project, protected val kotlinPluginVersion: String -) : KotlinTargetPreset> { +) : KotlinTargetPreset { protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget): String? = target.targetName - override fun createTarget(name: String): KotlinOnlyTarget { - val result = KotlinOnlyTarget(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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt index 7ab45364308..bf130a23766 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt @@ -13,10 +13,12 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTargetConfigurator open class KotlinJsTargetPreset( project: Project, kotlinPluginVersion: String -) : KotlinOnlyTargetPreset( +) : KotlinOnlyTargetPreset, KotlinJsCompilation>( project, kotlinPluginVersion ) { + override fun instantiateTarget() = KotlinOnlyTarget(project, KotlinPlatformType.js) + override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator(kotlinPluginVersion) override fun getName(): String = PRESET_NAME diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt new file mode 100644 index 00000000000..6983df3215b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt @@ -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( + 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) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetPreset.kt index f0fbbd4c37a..ac0d27267c5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetPreset.kt @@ -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( +) : KotlinOnlyTargetPreset( project, kotlinPluginVersion ) { + override fun instantiateTarget(): KotlinJvmTarget = KotlinJvmTarget(project) + override fun getName(): String = PRESET_NAME override fun createCompilationFactory(forTarget: KotlinOnlyTarget): KotlinCompilationFactory = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmWithJavaTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmWithJavaTargetPreset.kt index 04a2c7941af..478f1d73ee3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmWithJavaTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmWithJavaTargetPreset.kt @@ -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 { + SingleWarningPerBuild.show( + project, + DEPRECATION_WARNING + ) + project.plugins.apply(JavaPlugin::class.java) val target = KotlinWithJavaTarget(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" } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt index 4db84f1de96..2dfee29df80 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt @@ -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( +) : KotlinOnlyTargetPreset, KotlinCommonCompilation>( project, kotlinPluginVersion ) { + override fun instantiateTarget() = KotlinOnlyTarget(project, KotlinPlatformType.common) + override fun getName(): String = PRESET_NAME override fun createCompilationFactory(