From 64ea16fdfcdf1d62a54506cbc9978dc07a89a21d Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Mon, 14 May 2018 16:37:43 +0300 Subject: [PATCH] Kapt: Allow running kapt using Workers API in Gradle --- .../gradle/internal/AndroidSubplugin.kt | 7 +- .../{ => kapt}/Kapt3KotlinGradleSubplugin.kt | 80 ++++++--- .../{ => kapt}/KaptGenerateStubsTask.kt | 0 .../kotlin/gradle/internal/kapt/KaptTask.kt | 84 +++++++++ .../KaptWithKotlincTask.kt} | 103 +++-------- .../internal/kapt/KaptWithoutKotlincTask.kt | 167 ++++++++++++++++++ .../gradle/plugin/KotlinPluginWrapper.kt | 9 + .../kotlin/gradle/tasks/CacheableTasks.kt | 3 + 8 files changed, 349 insertions(+), 104 deletions(-) rename libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/{ => kapt}/Kapt3KotlinGradleSubplugin.kt (87%) rename libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/{ => kapt}/KaptGenerateStubsTask.kt (100%) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt rename libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/{KaptTask.kt => kapt/KaptWithKotlincTask.kt} (52%) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt index 49ec59edfee..d1d7184fcf0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt @@ -46,12 +46,7 @@ class AndroidExtensionsSubpluginIndicator : Plugin { } private fun addAndroidExtensionsRuntimeIfNeeded(project: Project) { - val kotlinPluginWrapper = project.plugins.findPlugin(KotlinAndroidPluginWrapper::class.java) ?: run { - project.logger.error("'kotlin-android' plugin should be enabled before 'kotlin-android-extensions'") - return - } - - val kotlinPluginVersion = kotlinPluginWrapper.kotlinPluginVersion + val kotlinPluginVersion = project.getKotlinPluginVersion() ?: return project.configurations.all { configuration -> val name = configuration.name diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt similarity index 87% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt index cb7156fc1dc..cf1fe9af46b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt @@ -21,6 +21,7 @@ import org.gradle.process.CommandLineArgumentProvider import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedKotlinSourcesDir import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedSourcesDir +import org.jetbrains.kotlin.gradle.tasks.isWorkerAPISupported import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -71,6 +72,11 @@ abstract class KaptVariantData(val variantData: T) { class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { companion object { private val VERBOSE_OPTION_NAME = "kapt.verbose" + private val USE_WORKER_API = "kapt.use.worker.api" + + const val KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME = "kotlinKaptWorkerDependencies" + + private val KAPT_KOTLIN_GENERATED = "kapt.kotlin.generated" val MAIN_KAPT_CONFIGURATION_NAME = "kapt" @@ -88,6 +94,14 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { return project.configurations.findByName(getKaptConfigurationName(sourceSetName)) } + fun Project.isKaptVerbose(): Boolean { + return hasProperty(VERBOSE_OPTION_NAME) && property(VERBOSE_OPTION_NAME) == "true" + } + + fun Project.isUseWorkerApi(): Boolean { + return isWorkerAPISupported() && hasProperty(USE_WORKER_API) && property(USE_WORKER_API) == "true" + } + fun findMainKaptConfiguration(project: Project) = project.findKaptConfiguration(MAIN_KAPT_CONFIGURATION_NAME) fun createAptConfigurationIfNeeded(project: Project, sourceSetName: String): Configuration { @@ -183,7 +197,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { ) val kaptGenerateStubsTask = context.createKaptGenerateStubsTask() - val kaptTask = context.createKaptKotlinTask() + val kaptTask = context.createKaptKotlinTask(useWorkerApi = project.isUseWorkerApi()) kaptGenerateStubsTask.source(*kaptConfigurations.toTypedArray()) @@ -224,14 +238,26 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { pluginOptions += SubpluginOption("processors", annotationProcessors) } + kotlinSourcesOutputDir.mkdirs() + + val apOptions = getAPOptions() + + pluginOptions += CompositeSubpluginOption("apoptions", encodeList(apOptions.associate { it.key to it.value }), apOptions) + + pluginOptions += SubpluginOption("javacArguments", encodeList(kaptExtension.getJavacOptions())) + + addMiscOptions(pluginOptions) + + return pluginOptions + } + + private fun Kapt3SubpluginContext.getAPOptions(): List { val androidPlugin = kaptVariantData?.let { project.extensions.findByName("android") as? BaseExtension } val androidOptions = kaptVariantData?.annotationProcessorOptions ?: emptyMap() - kotlinSourcesOutputDir.mkdirs() - val apOptionsFromProviders = if (isGradleVersionAtLeast(4, 6)) kaptVariantData?.annotationProcessorOptionProviders @@ -245,16 +271,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { androidOptions.toList() + apOptionsFromProviders - val apOptions = apOptionsPairsList.map { SubpluginOption(it.first, it.second) } + - FilesSubpluginOption("kapt.kotlin.generated", listOf(kotlinSourcesOutputDir)) - - pluginOptions += CompositeSubpluginOption("apoptions", encodeList(apOptions.associate { it.key to it.value }), apOptions) - - pluginOptions += SubpluginOption("javacArguments", encodeList(kaptExtension.getJavacOptions())) - - addMiscOptions(pluginOptions) - - return pluginOptions + return apOptionsPairsList.map { SubpluginOption(it.first, it.second) } + + FilesSubpluginOption(KAPT_KOTLIN_GENERATED, listOf(kotlinSourcesOutputDir)) } private fun Kapt3SubpluginContext.buildAndAddOptionsTo(task: Task, container: CompilerPluginOptions, aptMode: String) { @@ -298,15 +316,14 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { pluginOptions += SubpluginOption("mapDiagnosticLocations", "${kaptExtension.mapDiagnosticLocations}") pluginOptions += FilesSubpluginOption("stubs", listOf(getKaptStubsDir())) - if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") { + if (project.isKaptVerbose()) { pluginOptions += SubpluginOption("verbose", "true") } } - private fun Kapt3SubpluginContext.createKaptKotlinTask(): KaptTask { - val kaptTask = project.tasks.create( - getKaptTaskName("kapt"), - KaptTask::class.java) + private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean): KaptTask { + val taskClass = if (useWorkerApi) KaptWithoutKotlincTask::class.java else KaptWithKotlincTask::class.java + val kaptTask = project.tasks.create(getKaptTaskName("kapt"), taskClass) kaptTask.useBuildCache = kaptExtension.useBuildCache @@ -333,11 +350,32 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations - kaptVariantData?.annotationProcessorOptionProviders?.let { - kaptTask.annotationProcessorOptionProviders.add(it) + if (kaptTask is KaptWithKotlincTask) { + kaptVariantData?.annotationProcessorOptionProviders?.let { + kaptTask.annotationProcessorOptionProviders.add(it) + } + + buildAndAddOptionsTo(kaptTask, kaptTask.pluginOptions, aptMode = "apt") } - buildAndAddOptionsTo(kaptTask, kaptTask.pluginOptions, aptMode = "apt") + if (kaptTask is KaptWithoutKotlincTask) { + project.configurations.create(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply { + project.getKotlinPluginVersion()?.let { kotlinPluginVersion -> + val kaptDependency = getPluginArtifact().run { "$groupId:$artifactId:$kotlinPluginVersion" } + dependencies.add(project.dependencies.create(kaptDependency)) + } ?: project.logger.error("Kotlin plugin should be enabled before 'kotlin-kapt'") + } + + with(kaptTask) { + projectDir = project.projectDir + + isVerbose = project.isKaptVerbose() + mapDiagnosticLocations = kaptExtension.mapDiagnosticLocations + annotationProcessorFqNames = kaptExtension.processors.split(',').filter { it.isNotEmpty() } + processorOptions = getAPOptions().map { it.key to it.value }.toMap() + javacOptions = kaptExtension.getJavacOptions() + } + } return kaptTask } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptGenerateStubsTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt similarity index 100% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptGenerateStubsTask.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt new file mode 100644 index 00000000000..8e83d0f3565 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptTask.kt @@ -0,0 +1,84 @@ +package org.jetbrains.kotlin.gradle.internal + +import org.gradle.api.GradleException +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.ConventionTask +import org.gradle.api.tasks.* +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment +import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner +import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl +import org.jetbrains.kotlin.gradle.plugin.compareVersionNumbers +import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.tasks.* +import org.jetbrains.kotlin.gradle.utils.isJavaFile +import org.jetbrains.kotlin.gradle.utils.isParentOf +import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray +import java.io.File + +@CacheableTask +open class KaptTask : ConventionTask() { + init { + cacheOnlyIfEnabledForKotlin() + + if (isBuildCacheSupported()) { + val reason = "Caching is disabled by default for kapt because of arbitrary behavior of external " + + "annotation processors. You can enable it by adding 'kapt.useBuildCache = true' to the build script." + outputs.cacheIf(reason) { useBuildCache } + } + } + + @get:Internal + internal lateinit var kotlinCompileTask: KotlinCompile + + @get:Internal + internal lateinit var stubsDir: File + + @get:Classpath @get:InputFiles + val kaptClasspath: FileCollection + get() = project.files(*kaptClasspathConfigurations.toTypedArray()) + + @get:Classpath @get:InputFiles + val compilerClasspath: List get() = kotlinCompileTask.computedCompilerClasspath + + @get:Internal + internal lateinit var kaptClasspathConfigurations: List + + @get:OutputDirectory + internal lateinit var classesDir: File + + @get:OutputDirectory + lateinit var destinationDir: File + + @get:OutputDirectory + lateinit var kotlinSourcesDestinationDir: File + + @get:Nested + internal val annotationProcessorOptionProviders: MutableList = mutableListOf() + + @get:Classpath @get:InputFiles + val classpath: FileCollection + get() = kotlinCompileTask.classpath + + @get:Internal + var useBuildCache: Boolean = false + + @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) + val source: Collection + get() { + val result = HashSet() + for (root in javaSourceRoots) { + root.walk().filterTo(result) { it.isJavaFile() } + } + return result + } + + protected val javaSourceRoots: Set + get() = (kotlinCompileTask.sourceRootsContainer.sourceRoots + stubsDir) + .filterTo(HashSet(), ::isRootAllowed) + + private fun isRootAllowed(file: File): Boolean = + !FileUtil.isAncestor(destinationDir, file, /* strict = */ false) && + !FileUtil.isAncestor(classesDir, file, /* strict = */ false) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt similarity index 52% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt index 15470493d46..fecec3d3e21 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt @@ -1,65 +1,41 @@ +/* + * Copyright 2010-2018 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.internal +import com.intellij.openapi.util.text.StringUtil import org.gradle.api.GradleException -import org.gradle.api.artifacts.Configuration import org.gradle.api.file.FileCollection -import org.gradle.api.internal.ConventionTask -import org.gradle.api.tasks.* +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl -import org.jetbrains.kotlin.gradle.plugin.compareVersionNumbers +import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions +import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector +import org.jetbrains.kotlin.gradle.tasks.clearOutputDirectories +import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME -import org.jetbrains.kotlin.gradle.tasks.* -import org.jetbrains.kotlin.gradle.utils.isJavaFile -import org.jetbrains.kotlin.gradle.utils.isParentOf import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray -import java.io.File - -@CacheableTask -open class KaptTask : ConventionTask(), CompilerArgumentAwareWithInput { - - init { - cacheOnlyIfEnabledForKotlin() - - if (isBuildCacheSupported()) { - val reason = "Caching is disabled by default for kapt because of arbitrary behavior of external " + - "annotation processors. You can enable it by adding 'kapt.useBuildCache = true' to the build script." - outputs.cacheIf(reason) { useBuildCache } - } - } +open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput { @get:Internal internal val pluginOptions = CompilerPluginOptions() - @get:Internal - internal lateinit var kotlinCompileTask: KotlinCompile + @get:Classpath + @get:InputFiles + @Suppress("unused") + internal val kotlinTaskPluginClasspaths get() = kotlinCompileTask.pluginClasspath - @get:Internal - internal lateinit var stubsDir: File - - @get:Classpath @get:InputFiles - val kaptClasspath: FileCollection - get() = project.files(*kaptClasspathConfigurations.toTypedArray()) - - @get:Classpath @get:InputFiles - val compilerClasspath: List get() = kotlinCompileTask.computedCompilerClasspath - - @get:Internal - internal lateinit var kaptClasspathConfigurations: List - - @get:OutputDirectory - internal lateinit var classesDir: File - - @get:OutputDirectory - lateinit var destinationDir: File - - @get:OutputDirectory - lateinit var kotlinSourcesDestinationDir: File - - @get:Nested - internal val annotationProcessorOptionProviders: MutableList = mutableListOf() + @get:Classpath + @get:InputFiles + val pluginClasspath: FileCollection + get() = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME) override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() @@ -74,37 +50,10 @@ open class KaptTask : ConventionTask(), CompilerArgumentAwareWithInput - get() { - val result = HashSet() - for (root in javaSourceRoots) { - root.walk().filterTo(result) { it.isJavaFile() } - } - return result - } - - private val javaSourceRoots: Set - get() = (kotlinCompileTask.sourceRootsContainer.sourceRoots + stubsDir) - .filterTo(HashSet(), ::isRootAllowed) - - private fun isRootAllowed(file: File): Boolean = - !destinationDir.isParentOf(file) && !classesDir.isParentOf(file) - @TaskAction fun compile() { + logger.debug("Running kapt annotation processing using the Kotlin compiler") + /** Delete everything inside generated sources and classes output directory * (annotation processing is not incremental) */ clearOutputDirectories() @@ -130,7 +79,7 @@ open class KaptTask : ConventionTask(), CompilerArgumentAwareWithInput= 0 + get() = StringUtil.compareVersionNumbers(getJavaRuntimeVersion(), "9") >= 0 private fun getJavaRuntimeVersion(): String { val rtVersion = System.getProperty("java.runtime.version") diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt new file mode 100644 index 00000000000..6517496f703 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2010-2018 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.internal + +import org.gradle.api.tasks.* +import org.gradle.api.file.FileCollection +import org.gradle.workers.IsolationMode +import org.gradle.workers.WorkerExecutor +import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.tasks.clearOutputDirectories +import org.jetbrains.kotlin.gradle.tasks.findKotlinStdlibClasspath +import org.jetbrains.kotlin.gradle.tasks.findToolsJar +import java.io.File +import java.io.Serializable +import java.net.URLClassLoader +import javax.inject.Inject + +open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor: WorkerExecutor) : KaptTask() { + @get:InputFiles + @get:Classpath + @Suppress("unused") + val kaptJars: Collection + get() = project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve() + + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + lateinit var projectDir: File + + @get:Input + var isVerbose: Boolean = false + + @get:Input + var mapDiagnosticLocations: Boolean = false + + @get:Input + lateinit var annotationProcessorFqNames: List + + @get:Input + lateinit var processorOptions: Map + + @get:Input + lateinit var javacOptions: Map + + @TaskAction + fun compile() { + logger.info("Running kapt annotation processing using the Gradle Worker API") + + clearOutputDirectories() + + val paths = KaptPathsForWorker( + projectDir, + classpath.files.toList(), + kaptClasspath.files.toList(), + javaSourceRoots.toList(), + destinationDir, + classesDir, + stubsDir + ) + + val options = KaptOptionsForWorker( + isVerbose, + mapDiagnosticLocations, + annotationProcessorFqNames, + processorOptions, + javacOptions + ) + + val kaptClasspath = kaptJars + findKotlinStdlibClasspath(project) + + workerExecutor.submit(KaptExecution::class.java) { config -> + config.isolationMode = IsolationMode.PROCESS + config.params(options, paths, findToolsJar(), kaptClasspath) + + logger.info("Kapt worker classpath: ${config.classpath}") + } + + workerExecutor.await() + } +} + +private class KaptExecution @Inject constructor( + val options: KaptOptionsForWorker, + val paths: KaptPathsForWorker, + val toolsJar: File?, + val kaptClasspath: List +) : Runnable { + private companion object { + private const val JAVAC_CONTEXT_CLASS = "com.sun.tools.javac.util.Context" + } + + override fun run(): Unit = with(options) { + val kaptClasspathUrls = kaptClasspath.map { it.toURI().toURL() }.toTypedArray() + + val rootClassLoader = findRootClassLoader() + + val classLoaderWithToolsJar = if (toolsJar != null && !javacIsAlreadyHere()) { + toolsJar.let { URLClassLoader(arrayOf(it.toURI().toURL()), rootClassLoader) } + } else { + rootClassLoader + } + + val kaptClassLoader = URLClassLoader(kaptClasspathUrls, classLoaderWithToolsJar) + + val kaptMethod = Class.forName("org.jetbrains.kotlin.kapt3.base.Kapt", true, kaptClassLoader) + .declaredMethods.single { it.name == "kapt" } + + kaptMethod.invoke( + null, + createKaptPaths(kaptClassLoader), + isVerbose, + mapDiagnosticLocations, + annotationProcessorFqNames, + processorOptions, + javacOptions + ) + } + + private fun javacIsAlreadyHere(): Boolean { + return try { + Class.forName(JAVAC_CONTEXT_CLASS, false, KaptExecution::class.java.classLoader) != null + } catch (e: Throwable) { + false + } + } + + private fun createKaptPaths(classLoader: ClassLoader) = with(paths) { + Class.forName("org.jetbrains.kotlin.kapt3.base.KaptPaths", true, classLoader).constructors.single().newInstance( + projectBaseDir, + compileClasspath, + annotationProcessingClasspath, + javaSourceRoots, + sourcesOutputDir, + classFilesOutputDir, + stubsOutputDir, + stubsOutputDir // sic! + ) + } + + private fun findRootClassLoader(): ClassLoader { + tailrec fun parentOrSelf(classLoader: ClassLoader): ClassLoader { + val parent = classLoader.parent ?: return classLoader + return parentOrSelf(parent) + } + return parentOrSelf(KaptExecution::class.java.classLoader) + } +} + +private data class KaptOptionsForWorker( + val isVerbose: Boolean, + val mapDiagnosticLocations: Boolean, + val annotationProcessorFqNames: List, + val processorOptions: Map, + val javacOptions: Map +) : Serializable + +private data class KaptPathsForWorker( + val projectBaseDir: File, + val compileClasspath: List, + val annotationProcessingClasspath: List, + val javaSourceRoots: List, + val sourcesOutputDir: File, + val classFilesOutputDir: File, + val stubsOutputDir: File +) : Serializable \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index c9bf554c00a..c449f226a3b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -82,6 +82,15 @@ open class Kotlin2JsPluginWrapper @Inject constructor(fileResolver: FileResolver Kotlin2JsPlugin(Kotlin2JsTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion) } +fun Project.getKotlinPluginVersion(): String? { + val kotlinPluginWrapper = plugins.findPlugin(KotlinAndroidPluginWrapper::class.java) ?: run { + project.logger.error("'kotlin-android' plugin should be enabled before 'kotlin-android-extensions'") + return null + } + + return kotlinPluginWrapper.kotlinPluginVersion +} + private fun Any.loadKotlinVersionFromResource(log: Logger): String { log.kotlinDebug("Loading version information") val props = Properties() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CacheableTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CacheableTasks.kt index 2c4ada39612..3b7c0d96d1f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CacheableTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CacheableTasks.kt @@ -23,6 +23,9 @@ import org.jetbrains.kotlin.gradle.utils.outputsCompatible internal fun isBuildCacheSupported(): Boolean = gradleVersion >= GradleVersion.version("4.3") +internal fun isWorkerAPISupported(): Boolean = + gradleVersion >= GradleVersion.version("4.3") + internal fun isBuildCacheEnabledForKotlin(): Boolean = isBuildCacheSupported() && System.getProperty(KOTLIN_CACHING_ENABLED_PROPERTY)?.toBoolean() ?: true