Enable kotlin java toolchain support for kapt task.
With this change 'KaptWithoutKotlincTask' will also support overriding default Gradle JDK to run kapt. In such case only 'in-process' kapt worker isolation mode is supported. If user does not provide custom JDK, kapt worker isolation mode will be 'no-isolation' as before. ^KT-45611 In Progress
This commit is contained in:
+107
@@ -11,6 +11,7 @@ import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
@@ -106,6 +107,112 @@ class KotlinJavaToolchainTest : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Kapt task should use only process worker isolation when kotlin java toolchain is set")
|
||||
@GradleTest
|
||||
internal fun kaptTasksShouldUseProcessWorkersIsolation(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "simpleWithKapt".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
) {
|
||||
useJdk9ToCompile()
|
||||
gradleProperties.append(
|
||||
"kapt.workers.isolation = none"
|
||||
)
|
||||
|
||||
build("assemble") {
|
||||
assertDaemonIsUsingJdk(getJdk9().javaExecutableRealPath)
|
||||
|
||||
assertOutputContains("Using workers PROCESS isolation mode to run kapt")
|
||||
assertOutputContains("Using non-default Kotlin java toolchain - 'kapt.workers.isolation == none' property is ignored!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Kapt task should use worker no-isolation mode when build is using Gradle JDK")
|
||||
@GradleTest
|
||||
internal fun kaptTasksShouldUseNoIsolationModeOnDefaultJvm(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
projectName = "simpleWithKapt".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
) {
|
||||
build("assemble") {
|
||||
assertOutputContains("Using workers NONE isolation mode to run kapt")
|
||||
assertOutputDoesNotContain("Using non-default Kotlin java toolchain - 'kapt.workers.isolation == none' property is ignored!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Kapt tasks with custom JDK should be cacheable")
|
||||
@GradleTest
|
||||
internal fun kaptTasksWithCustomJdkCacheable(gradleVersion: GradleVersion) {
|
||||
val buildCache = workingDir.resolve("custom-jdk-build-cache")
|
||||
project(
|
||||
projectName = "simpleWithKapt".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
projectPathAdditionalSuffix = "1/cache-test",
|
||||
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)
|
||||
) {
|
||||
enableLocalBuildCache(buildCache)
|
||||
useJdk9ToCompile()
|
||||
|
||||
build("assemble")
|
||||
}
|
||||
|
||||
project(
|
||||
projectName = "simpleWithKapt".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
projectPathAdditionalSuffix = "2/cache-test",
|
||||
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true),
|
||||
) {
|
||||
enableLocalBuildCache(buildCache)
|
||||
useJdk9ToCompile()
|
||||
|
||||
build("assemble") {
|
||||
assertTasksFromCache(
|
||||
":kaptGenerateStubsKotlin",
|
||||
":kaptKotlin",
|
||||
":compileKotlin"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Kapt tasks with default JDK and different isolation modes should be cacheable")
|
||||
@GradleTest
|
||||
internal fun kaptCacheableOnSwitchingIsolationModeAndDefaultJDK(gradleVersion: GradleVersion) {
|
||||
val buildCache = workingDir.resolve("custom-jdk-build-cache")
|
||||
project(
|
||||
projectName = "simpleWithKapt".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
projectPathAdditionalSuffix = "1/cache-test",
|
||||
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)
|
||||
) {
|
||||
enableLocalBuildCache(buildCache)
|
||||
|
||||
build("assemble")
|
||||
}
|
||||
|
||||
project(
|
||||
projectName = "simpleWithKapt".fullProjectName,
|
||||
gradleVersion = gradleVersion,
|
||||
projectPathAdditionalSuffix = "2/cache-test",
|
||||
buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true),
|
||||
) {
|
||||
enableLocalBuildCache(buildCache)
|
||||
gradleProperties.append(
|
||||
"kapt.workers.isolation = process"
|
||||
)
|
||||
|
||||
build("assemble") {
|
||||
assertTasksFromCache(
|
||||
":kaptGenerateStubsKotlin",
|
||||
":kaptKotlin",
|
||||
":compileKotlin"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BuildResult.assertDaemonIsUsingJdk(
|
||||
javaexecPath: String
|
||||
) = assertOutputContains("i: connected to the daemon. Daemon is using following 'java' executable to run itself: $javaexecPath")
|
||||
|
||||
+1
@@ -18,6 +18,7 @@ internal val DEFAULT_GROOVY_SETTINGS_FILE =
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "${'$'}kotlin_version"
|
||||
id "org.jetbrains.kotlin.kapt" version "${'$'}kotlin_version"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "org.jetbrains.kotlin.kapt"
|
||||
id "java"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
|
||||
kapt "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
compileKotlin.kotlinOptions.allWarningsAsErrors = true
|
||||
+1
@@ -0,0 +1 @@
|
||||
kapt.verbose=true
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package foo
|
||||
|
||||
internal class InternalDummy {
|
||||
internal val x = "InternalDummy.x"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package foo
|
||||
|
||||
internal class InternalDummyUser {
|
||||
internal fun use(dummy: InternalDummy) {
|
||||
if (dummy.x != "InternalDummy.x") throw AssertionError("dummy.x = ${dummy.x}")
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package foo
|
||||
|
||||
fun topLevelDummyFun() {}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package javaPackage;
|
||||
|
||||
public class JavaClass {
|
||||
|
||||
public void test() {
|
||||
System.out.println(example.TestClassGenerated.class.getName());
|
||||
System.out.println(example.SourceAnnotatedTestClassGenerated.class.getName());
|
||||
System.out.println(example.BinaryAnnotatedTestClassGenerated.class.getName());
|
||||
System.out.println(example.RuntimeAnnotatedTestClassGenerated.class.getName());
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package example
|
||||
|
||||
@example.ExampleAnnotation
|
||||
@example.ExampleSourceAnnotation
|
||||
@example.ExampleBinaryAnnotation
|
||||
@example.ExampleRuntimeAnnotation
|
||||
public class TestClass {
|
||||
|
||||
@example.ExampleAnnotation
|
||||
public val testVal: String = "text"
|
||||
|
||||
@example.ExampleAnnotation
|
||||
public fun testFunction(): TestClassGenerated = TestClassGenerated()
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package example;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JavaTest {
|
||||
@Test
|
||||
public void test() {
|
||||
TestClass testClass = new TestClass();
|
||||
Assert.assertEquals("text", testClass.getTestVal());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package foo
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
class InternalDummyTest {
|
||||
@Test
|
||||
fun testDummy() {
|
||||
val dummy = InternalDummy()
|
||||
val dummyUser = InternalDummyUser()
|
||||
dummyUser.use(dummy)
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -4,9 +4,11 @@ import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
||||
@@ -17,10 +19,11 @@ import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChan
|
||||
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.UnknownSnapshot
|
||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskConfigurator
|
||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.tasks.cacheOnlyIfEnabledForKotlin
|
||||
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
|
||||
import org.jetbrains.kotlin.gradle.utils.getValue
|
||||
import org.jetbrains.kotlin.gradle.utils.propertyWithNewInstance
|
||||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.jar.JarFile
|
||||
@@ -28,9 +31,10 @@ import javax.inject.Inject
|
||||
|
||||
@CacheableTask
|
||||
abstract class KaptTask @Inject constructor(
|
||||
@get:Internal
|
||||
protected val providerFactory: ProviderFactory
|
||||
): ConventionTask(), TaskWithLocalState {
|
||||
objectFactory: ObjectFactory
|
||||
) : ConventionTask(),
|
||||
TaskWithLocalState,
|
||||
UsesKotlinJavaToolchain {
|
||||
|
||||
open class Configurator<T: KaptTask>(protected val kotlinCompileTask: KotlinCompile) : TaskConfigurator<T> {
|
||||
override fun configure(task: T) {
|
||||
@@ -117,6 +121,9 @@ abstract class KaptTask @Inject constructor(
|
||||
@get:Internal
|
||||
abstract val classpath: ConfigurableFileCollection
|
||||
|
||||
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> =
|
||||
objectFactory.propertyWithNewInstance()
|
||||
|
||||
@Suppress("unused", "DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated(
|
||||
message = "Don't use directly. Used only for up-to-date checks",
|
||||
|
||||
+3
-10
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.internal
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
@@ -22,17 +21,14 @@ import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
||||
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
||||
import org.jetbrains.kotlin.gradle.report.ReportingSettings
|
||||
import org.jetbrains.kotlin.gradle.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class KaptWithKotlincTask @Inject constructor(
|
||||
objectFactory: ObjectFactory,
|
||||
providerFactory: ProviderFactory
|
||||
) : KaptTask(providerFactory),
|
||||
CompilerArgumentAwareWithInput<K2JVMCompilerArguments>,
|
||||
UsesKotlinJavaToolchain{
|
||||
objectFactory: ObjectFactory
|
||||
) : KaptTask(objectFactory),
|
||||
CompilerArgumentAwareWithInput<K2JVMCompilerArguments> {
|
||||
|
||||
class Configurator(kotlinCompileTask: KotlinCompile): KaptTask.Configurator<KaptWithKotlincTask>(kotlinCompileTask) {
|
||||
override fun configure(task: KaptWithKotlincTask) {
|
||||
@@ -56,9 +52,6 @@ abstract class KaptWithKotlincTask @Inject constructor(
|
||||
@get:Internal
|
||||
val taskProvider by lazy { GradleCompileTaskProvider(this) }
|
||||
|
||||
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchainProvider> =
|
||||
objectFactory.propertyWithNewInstance()
|
||||
|
||||
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
|
||||
|
||||
@get:Internal
|
||||
|
||||
+30
-17
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.gradle.internal
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.*
|
||||
@@ -23,7 +24,6 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
|
||||
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.findKotlinStdlibClasspath
|
||||
import org.jetbrains.kotlin.gradle.tasks.findToolsJar
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
@@ -34,9 +34,10 @@ import java.net.URLClassLoader
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class KaptWithoutKotlincTask @Inject constructor(
|
||||
providerFactory: ProviderFactory,
|
||||
objectFactory: ObjectFactory,
|
||||
private val providerFactory: ProviderFactory,
|
||||
private val workerExecutor: WorkerExecutor
|
||||
) : KaptTask(providerFactory) {
|
||||
) : KaptTask(objectFactory) {
|
||||
|
||||
class Configurator(kotlinCompileTask: KotlinCompile): KaptTask.Configurator<KaptWithoutKotlincTask>(kotlinCompileTask) {
|
||||
override fun configure(task: KaptWithoutKotlincTask) {
|
||||
@@ -157,15 +158,9 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
|
||||
}
|
||||
|
||||
val kaptClasspath = kaptJars.toList() + kotlinStdlibClasspath
|
||||
|
||||
//TODO for gradle < 6.5
|
||||
val isolationModeStr = getValue("kapt.workers.isolation") ?: "none"
|
||||
val isolationMode = when (isolationModeStr.toLowerCase()) {
|
||||
"process" -> IsolationMode.PROCESS
|
||||
"none" -> IsolationMode.NONE
|
||||
else -> IsolationMode.NONE
|
||||
}
|
||||
val toolsJarURLSpec = findToolsJar()?.toURI()?.toURL()?.toString().orEmpty()
|
||||
val isolationMode = getWorkerIsolationMode()
|
||||
logger.info("Using workers $isolationMode isolation mode to run kapt")
|
||||
val toolsJarURLSpec = kotlinJavaToolchainProvider.get().jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty()
|
||||
|
||||
submitWork(
|
||||
isolationMode,
|
||||
@@ -175,6 +170,25 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun getWorkerIsolationMode(): IsolationMode {
|
||||
val kotlinJavaToolchain = kotlinJavaToolchainProvider.get()
|
||||
val gradleJvm = kotlinJavaToolchain.currentJvm.get()
|
||||
// Ensuring Gradle build JDK is set to kotlin toolchain by also comparing javaExecutable paths,
|
||||
// as user may set JDK with same major Java version, but from different vendor
|
||||
val isRunningOnGradleJvm = gradleJvm.javaVersion == kotlinJavaToolchain.javaVersion.get() &&
|
||||
gradleJvm.javaExecutable.absolutePath == kotlinJavaToolchain.javaExecutable.get().asFile.absolutePath
|
||||
val isolationModeStr = getValue("kapt.workers.isolation")?.toLowerCase()
|
||||
return when {
|
||||
(isolationModeStr == null || isolationModeStr == "none") && isRunningOnGradleJvm -> IsolationMode.NONE
|
||||
else -> {
|
||||
if (isolationModeStr == "none") {
|
||||
logger.warn("Using non-default Kotlin java toolchain - 'kapt.workers.isolation == none' property is ignored!")
|
||||
}
|
||||
IsolationMode.PROCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun submitWork(
|
||||
isolationMode: IsolationMode,
|
||||
optionsForWorker: KaptOptionsForWorker,
|
||||
@@ -187,14 +201,13 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
|
||||
// for tests
|
||||
it.forkOptions.jvmArgs("-verbose:class")
|
||||
}
|
||||
logger.info("Kapt worker classpath: ${it.classpath}")
|
||||
}
|
||||
IsolationMode.CLASSLOADER -> workerExecutor.classLoaderIsolation() {
|
||||
it.forkOptions.executable = kotlinJavaToolchainProvider.get().javaExecutable.asFile.get().absolutePath
|
||||
logger.info("Kapt worker classpath: ${it.classpath}")
|
||||
}
|
||||
IsolationMode.NONE -> workerExecutor.noIsolation()
|
||||
IsolationMode.AUTO -> throw UnsupportedOperationException(
|
||||
"Kapt worker compilation does not support $isolationMode"
|
||||
IsolationMode.AUTO, IsolationMode.CLASSLOADER -> throw UnsupportedOperationException(
|
||||
"Kapt worker compilation does not support class loader isolation. " +
|
||||
"Please use either \"none\" or \"process\" in gradle.properties."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -24,7 +24,8 @@ abstract class KotlinJavaToolchainProvider @Inject constructor(
|
||||
objects: ObjectFactory,
|
||||
files: ProjectLayout
|
||||
) : KotlinJavaToolchain {
|
||||
private val currentJvm: Property<Jvm> = objects
|
||||
@get:Internal
|
||||
internal val currentJvm: Property<Jvm> = objects
|
||||
.property<Jvm>(Jvm.current())
|
||||
.chainedFinalizeValueOnRead()
|
||||
|
||||
|
||||
-16
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.ResolvedDependency
|
||||
@@ -104,21 +103,6 @@ internal fun findKotlinScriptRuntimeClasspath(project: Project): List<File> =
|
||||
internal fun findKotlinReflectClasspath(project: Project): List<File> =
|
||||
findKotlinModuleJar(project, KOTLIN_REFLECT_EXPECTED_CLASS, KOTLIN_REFLECT)
|
||||
|
||||
internal fun findToolsJar(): File? {
|
||||
val javacUtilContextClass =
|
||||
try {
|
||||
Class.forName("com.sun.tools.javac.util.Context")
|
||||
} catch (classNotFound: ClassNotFoundException) {
|
||||
val javaHome = System.getProperty("java.home") // current Java installation path
|
||||
throw GradleException(
|
||||
"Kotlin could not find the required JDK tools in the Java installation ${javaHome?.let { "'$it' " }.orEmpty()}" +
|
||||
"used by Gradle. Make sure Gradle is running on a JDK, not JRE.",
|
||||
classNotFound
|
||||
)
|
||||
}
|
||||
return javacUtilContextClass?.let(::findJarByClass)
|
||||
}
|
||||
|
||||
internal fun findJarByClass(klass: Class<*>): File? {
|
||||
val classFileName = klass.name.substringAfterLast(".") + ".class"
|
||||
val resource = klass.getResource(classFileName) ?: return null
|
||||
|
||||
Reference in New Issue
Block a user