Support Gradle instant execution for Kapt tasks

* In the Kotlin/JVM tasks, move the compiler arguments setup logic to
  a separate class, so that it can be reused by the Kapt tasks without
  directly referencing the Kotlin/JVM tasks

* In the Kapt tasks, carefully capture the values using the Provider API
  so that the task can be serialized for Instant Execution and then
  deserialized and executed without relying on the project model

Issue #KT-35181 Fixed
This commit is contained in:
Sergey Igushkin
2020-01-13 20:09:21 +03:00
parent 5111d1721a
commit f9acc0ab88
9 changed files with 268 additions and 92 deletions
@@ -15,7 +15,7 @@ import kotlin.test.fail
class InstantExecutionIT : BaseGradleIT() { class InstantExecutionIT : BaseGradleIT() {
private val androidGradlePluginVersion: AGPVersion private val androidGradlePluginVersion: AGPVersion
get() = AGPVersion.v4_0_ALPHA_1 get() = AGPVersion.v4_0_ALPHA_8
override fun defaultBuildOptions() = override fun defaultBuildOptions() =
super.defaultBuildOptions().copy( super.defaultBuildOptions().copy(
@@ -23,7 +23,7 @@ class InstantExecutionIT : BaseGradleIT() {
androidGradlePluginVersion = androidGradlePluginVersion androidGradlePluginVersion = androidGradlePluginVersion
) )
private val minimumGradleVersion = GradleVersionRequired.AtLeast("6.1-milestone-1") private val minimumGradleVersion = GradleVersionRequired.AtLeast("6.1-rc-3")
@Test @Test
fun testSimpleKotlinJvmProject() = with(Project("kotlinProject", minimumGradleVersion)) { fun testSimpleKotlinJvmProject() = with(Project("kotlinProject", minimumGradleVersion)) {
@@ -31,14 +31,37 @@ class InstantExecutionIT : BaseGradleIT() {
} }
@Test @Test
fun testSimpleKotlinAndroidProject() = with(Project("AndroidProject", minimumGradleVersion)) { fun testSimpleKotlinAndroidProject() = with(Project("android-dagger", minimumGradleVersion, "kapt2")) {
applyAndroidAndroid40Alpha4KotlinVersionWorkaround() applyAndroid40Alpha4KotlinVersionWorkaround()
testInstantExecutionOf(":Lib:compileFlavor1DebugKotlin", ":Android:compileFlavor1DebugKotlin") projectDir.resolve("gradle.properties").appendText("\nkapt.incremental.apt=false")
testInstantExecutionOf(":app:compileDebugKotlin", ":app:kaptDebugKotlin", ":app:kaptGenerateStubsDebugKotlin")
} }
private fun Project.testInstantExecutionOf(vararg taskNames: String) { @Test
fun testIncrementalKaptProject() = with(getIncrementalKaptProject()) {
testInstantExecutionOf(
":compileKotlin",
":kaptKotlin",
buildOptions = defaultBuildOptions().copy(
incremental = true,
kaptOptions = KaptOptions(
verbose = true,
useWorkers = true,
incrementalKapt = true,
includeCompileClasspath = false
)
)
)
}
private fun getIncrementalKaptProject() =
Project("kaptIncrementalCompilationProject", minimumGradleVersion).apply {
setupIncrementalAptProject("AGGREGATING")
}
private fun Project.testInstantExecutionOf(vararg taskNames: String, buildOptions: BuildOptions = defaultBuildOptions()) {
// First, run a build that serializes the tasks state for instant execution in further builds // First, run a build that serializes the tasks state for instant execution in further builds
instantExecutionOf(*taskNames) { instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful() assertSuccessful()
assertTasksExecuted(*taskNames) assertTasksExecuted(*taskNames)
checkInstantExecutionSucceeded() checkInstantExecutionSucceeded()
@@ -49,12 +72,12 @@ class InstantExecutionIT : BaseGradleIT() {
} }
// Then run a build where tasks states are deserialized to check that they work correctly in this mode // Then run a build where tasks states are deserialized to check that they work correctly in this mode
instantExecutionOf(*taskNames) { instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful() assertSuccessful()
assertTasksExecuted(*taskNames) assertTasksExecuted(*taskNames)
} }
instantExecutionOf(*taskNames) { instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful() assertSuccessful()
assertTasksUpToDate(*taskNames) assertTasksUpToDate(*taskNames)
} }
@@ -66,8 +89,12 @@ class InstantExecutionIT : BaseGradleIT() {
} }
} }
private fun Project.instantExecutionOf(vararg tasks: String, check: CompiledProject.() -> Unit) = private fun Project.instantExecutionOf(
build("-Dorg.gradle.unsafe.instant-execution=true", *tasks, check = check) vararg tasks: String,
buildOptions: BuildOptions = defaultBuildOptions(),
check: CompiledProject.() -> Unit
) =
build("-Dorg.gradle.unsafe.instant-execution=true", *tasks, options = buildOptions, check = check)
/** /**
* Copies all files from the directory containing the given [htmlReportFile] to a * Copies all files from the directory containing the given [htmlReportFile] to a
@@ -98,7 +125,7 @@ class InstantExecutionIT : BaseGradleIT() {
* test project's repositories, where there's no 'kotlin-eap' repo. * test project's repositories, where there's no 'kotlin-eap' repo.
* TODO remove this workaround once an Android Gradle plugin version is used that depends on the stable Kotlin version * TODO remove this workaround once an Android Gradle plugin version is used that depends on the stable Kotlin version
*/ */
private fun Project.applyAndroidAndroid40Alpha4KotlinVersionWorkaround() { private fun Project.applyAndroid40Alpha4KotlinVersionWorkaround() {
setupWorkingDir() setupWorkingDir()
val resolutionStrategyHack = """ val resolutionStrategyHack = """
@@ -22,6 +22,6 @@ class AGPVersion private constructor(private val versionNumber: VersionNumber) {
val v3_1_0 = fromString("3.1.0") val v3_1_0 = fromString("3.1.0")
val v3_2_0 = fromString("3.2.0") val v3_2_0 = fromString("3.2.0")
val v3_3_2 = fromString("3.3.2") val v3_3_2 = fromString("3.3.2")
val v4_0_ALPHA_1 = fromString("4.0.0-alpha01") val v4_0_ALPHA_8 = fromString("4.0.0-alpha08")
} }
} }
@@ -0,0 +1,126 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.TaskProvider
import org.jetbrains.kotlin.cli.common.arguments.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
internal interface CompilerArgumentsContributor<in T : CommonToolArguments> {
fun contributeArguments(
args: T,
flags: Collection<CompilerArgumentsConfigurationFlag>
)
}
internal interface CompilerArgumentsConfigurationFlag
internal object DefaultsOnly : CompilerArgumentsConfigurationFlag
internal object IgnoreClasspathResolutionErrors : CompilerArgumentsConfigurationFlag
internal fun compilerArgumentsConfigurationFlags(defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) =
mutableSetOf<CompilerArgumentsConfigurationFlag>().apply {
if (defaultsOnly) add(DefaultsOnly)
if (ignoreClasspathResolutionErrors) add(IgnoreClasspathResolutionErrors)
}
/** The primary purpose of this class is to encapsulate compiler arguments setup done by the AbstractKotlinCompiler tasks,
* but outside the tasks, so that this state & logic can be reused without referencing the task directly. */
internal open class AbstractKotlinCompileArgumentsContributor<T : CommonCompilerArguments>(
// Don't save this reference into a property! That would be hostile to Gradle instant execution
taskProvider: TaskProvider<out AbstractKotlinCompile<T>>
) : CompilerArgumentsContributor<T> {
private val coroutines by taskProvider.map { it.coroutines }
protected val logger by taskProvider.map { it.logger }
private val isMultiplatform by taskProvider.map { it.isMultiplatform }
private val pluginClasspath by taskProvider.map { it.pluginClasspath }
private val pluginOptions by taskProvider.map { it.pluginOptions }
override fun contributeArguments(
args: T,
flags: Collection<CompilerArgumentsConfigurationFlag>
) {
args.coroutinesState = when (coroutines) {
Coroutines.ENABLE -> CommonCompilerArguments.ENABLE
Coroutines.WARN -> CommonCompilerArguments.WARN
Coroutines.ERROR -> CommonCompilerArguments.ERROR
Coroutines.DEFAULT -> CommonCompilerArguments.DEFAULT
}
logger.kotlinDebug { "args.coroutinesState=${args.coroutinesState}" }
if (logger.isDebugEnabled) {
args.verbose = true
}
args.multiPlatform = isMultiplatform
setupPlugins(args)
}
internal fun setupPlugins(compilerArgs: T) {
compilerArgs.pluginClasspaths = pluginClasspath.toSortedPathsArray()
compilerArgs.pluginOptions = pluginOptions.arguments.toTypedArray()
}
}
internal open class KotlinJvmCompilerArgumentsContributor(
// Don't save this reference into a property! That would be hostile to Gradle instant execution. Only map it to the task properties.
taskProvider: TaskProvider<out KotlinCompile>
) : AbstractKotlinCompileArgumentsContributor<K2JVMCompilerArguments>(taskProvider) {
private val moduleName by taskProvider.map { it.moduleName }
private val friendPaths by taskProvider.map { it.friendPaths }
private val compileClasspath by taskProvider.map { it.compileClasspath }
private val destinationDir by taskProvider.map { it.destinationDir }
private val kotlinOptions by taskProvider.map {
listOfNotNull(
it.parentKotlinOptionsImpl as KotlinJvmOptionsImpl?,
it.kotlinOptions as KotlinJvmOptionsImpl
)
}
override fun contributeArguments(
args: K2JVMCompilerArguments,
flags: Collection<CompilerArgumentsConfigurationFlag>
) {
args.fillDefaultValues()
super.contributeArguments(args, flags)
args.moduleName = moduleName
logger.kotlinDebug { "args.moduleName = ${args.moduleName}" }
args.friendPaths = friendPaths
logger.kotlinDebug { "args.friendPaths = ${args.friendPaths?.joinToString() ?: "[]"}" }
if (DefaultsOnly in flags) return
args.allowNoSourceFiles = true
args.classpathAsList = try {
compileClasspath.toList().filter { it.exists() }
} catch (e: Exception) {
if (IgnoreClasspathResolutionErrors in flags) emptyList() else throw(e)
}
args.destinationAsFile = destinationDir
kotlinOptions.forEach { it.updateArguments(args) }
}
}
@@ -19,15 +19,12 @@ package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.gradle.incremental.ChangedFiles
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.FilteringSourceRootsContainer import org.jetbrains.kotlin.gradle.tasks.FilteringSourceRootsContainer
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.SourceRoots import org.jetbrains.kotlin.gradle.tasks.SourceRoots
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.isParentOf import org.jetbrains.kotlin.gradle.utils.isParentOf
import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo
import org.jetbrains.kotlin.incremental.classpathAsList import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile import org.jetbrains.kotlin.incremental.destinationAsFile
import java.io.File import java.io.File
@@ -37,6 +34,7 @@ open class KaptGenerateStubsTask : KotlinCompile() {
override val sourceRootsContainer = FilteringSourceRootsContainer(emptyList(), { isSourceRootAllowed(it) }) override val sourceRootsContainer = FilteringSourceRootsContainer(emptyList(), { isSourceRootAllowed(it) })
@get:Internal @get:Internal
@field:Transient // can't serialize task references in Gradle instant execution state
internal lateinit var kotlinCompileTask: KotlinCompile internal lateinit var kotlinCompileTask: KotlinCompile
@get:OutputDirectory @get:OutputDirectory
@@ -48,7 +46,7 @@ open class KaptGenerateStubsTask : KotlinCompile() {
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
val kaptClasspath: FileCollection val kaptClasspath: FileCollection
get() = project.files(*kaptClasspathConfigurations.toTypedArray()) get() = project.files(kaptClasspathConfigurations)
@get:Internal @get:Internal
internal lateinit var kaptClasspathConfigurations: List<Configuration> internal lateinit var kaptClasspathConfigurations: List<Configuration>
@@ -56,8 +54,9 @@ open class KaptGenerateStubsTask : KotlinCompile() {
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
@Suppress("unused") @Suppress("unused")
internal val kotlinTaskPluginClasspath internal val kotlinTaskPluginClasspath by project.provider {
get() = kotlinCompileTask.pluginClasspath kotlinCompileTask.pluginClasspath
}
@get:Input @get:Input
override var useModuleDetection: Boolean override var useModuleDetection: Boolean
@@ -66,11 +65,11 @@ open class KaptGenerateStubsTask : KotlinCompile() {
error("KaptGenerateStubsTask.useModuleDetection setter should not be called!") error("KaptGenerateStubsTask.useModuleDetection setter should not be called!")
} }
override fun source(vararg sources: Any?): SourceTask? { override fun source(vararg sources: Any): SourceTask {
return super.source(sourceRootsContainer.add(sources)) return super.source(sourceRootsContainer.add(sources))
} }
override fun setSource(sources: Any?) { override fun setSource(sources: Any) {
super.setSource(sourceRootsContainer.set(sources)) super.setSource(sourceRootsContainer.set(sources))
} }
@@ -79,8 +78,15 @@ open class KaptGenerateStubsTask : KotlinCompile() {
!stubsDir.isParentOf(source) && !stubsDir.isParentOf(source) &&
!generatedSourcesDir.isParentOf(source) !generatedSourcesDir.isParentOf(source)
private val compileKotlinArgumentsContributor by project.provider {
kotlinCompileTask.compilerArgumentsContributor
}
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
kotlinCompileTask.setupCompilerArgs(args, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors) compileKotlinArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags(
defaultsOnly,
ignoreClasspathResolutionErrors
))
val pluginOptionsWithKapt = pluginOptions.withWrappedKaptOptions(withApClasspath = kaptClasspath) val pluginOptionsWithKapt = pluginOptions.withWrappedKaptOptions(withApClasspath = kaptClasspath)
args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray() args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray()
@@ -90,10 +96,13 @@ open class KaptGenerateStubsTask : KotlinCompile() {
args.destinationAsFile = this.destinationDir args.destinationAsFile = this.destinationDir
} }
override fun getSourceRoots(): SourceRoots.ForJvm = private val sourceRoots by project.provider {
kotlinCompileTask.getSourceRoots().let { kotlinCompileTask.getSourceRoots().let {
val javaSourceRoots = it.javaSourceRoots.filterTo(HashSet()) { isSourceRootAllowed(it) } val javaSourceRoots = it.javaSourceRoots.filterTo(HashSet()) { isSourceRootAllowed(it) }
val kotlinSourceFiles = it.kotlinSourceFiles val kotlinSourceFiles = it.kotlinSourceFiles
SourceRoots.ForJvm(kotlinSourceFiles, javaSourceRoots) SourceRoots.ForJvm(kotlinSourceFiles, javaSourceRoots)
} }
}
override fun getSourceRoots(): SourceRoots.ForJvm = sourceRoots
} }
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.cacheOnlyIfEnabledForKotlin import org.jetbrains.kotlin.gradle.tasks.cacheOnlyIfEnabledForKotlin
import org.jetbrains.kotlin.gradle.tasks.clearLocalState import org.jetbrains.kotlin.gradle.tasks.clearLocalState
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.isJavaFile import org.jetbrains.kotlin.gradle.utils.isJavaFile
import java.io.File import java.io.File
import java.util.jar.JarFile import java.util.jar.JarFile
@@ -30,6 +31,7 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
override fun localStateDirectories(): FileCollection = project.files() override fun localStateDirectories(): FileCollection = project.files()
@get:Internal @get:Internal
@field:Transient
internal lateinit var kotlinCompileTask: KotlinCompile internal lateinit var kotlinCompileTask: KotlinCompile
@get:Internal @get:Internal
@@ -38,12 +40,13 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
val kaptClasspath: FileCollection val kaptClasspath: FileCollection
get() = project.files(*kaptClasspathConfigurations.toTypedArray()) get() = project.files(kaptClasspathConfigurations)
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
val compilerClasspath: List<File> val compilerClasspath: List<File> by project.provider {
get() = kotlinCompileTask.computedCompilerClasspath kotlinCompileTask.computedCompilerClasspath
}
@get:Internal @get:Internal
internal lateinit var kaptClasspathConfigurations: List<Configuration> internal lateinit var kaptClasspathConfigurations: List<Configuration>
@@ -82,8 +85,9 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
// @Internal because _abiClasspath and _nonAbiClasspath are used for actual checks // @Internal because _abiClasspath and _nonAbiClasspath are used for actual checks
@get:Internal @get:Internal
val classpath: FileCollection val classpath: FileCollection by project.provider {
get() = kotlinCompileTask.classpath kotlinCompileTask.classpath
}
@Suppress("unused", "DeprecatedCallableAddReplaceWith") @Suppress("unused", "DeprecatedCallableAddReplaceWith")
@Deprecated( @Deprecated(
@@ -92,8 +96,9 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
) )
@get:CompileClasspath @get:CompileClasspath
@get:InputFiles @get:InputFiles
internal val internalAbiClasspath: FileCollection internal val internalAbiClasspath: FileCollection by project.provider {
get() = if (includeCompileClasspath) project.files() else kotlinCompileTask.classpath if (includeCompileClasspath) project.files() else kotlinCompileTask.classpath
}
@Suppress("unused", "DeprecatedCallableAddReplaceWith") @Suppress("unused", "DeprecatedCallableAddReplaceWith")
@@ -103,8 +108,9 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
) )
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
internal val internalNonAbiClasspath: FileCollection internal val internalNonAbiClasspath: FileCollection by project.provider {
get() = if (includeCompileClasspath) kotlinCompileTask.classpath else project.files() if (includeCompileClasspath) kotlinCompileTask.classpath else project.files()
}
@get:Internal @get:Internal
var useBuildCache: Boolean = false var useBuildCache: Boolean = false
@@ -120,10 +126,14 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
return result return result
} }
// store the files before filtering, so that Gradle doesn't serialize the filtered collection for instant execution state and reuse it
private val unfilteredJavaSourceRoots by project.provider {
(kotlinCompileTask.sourceRootsContainer.sourceRoots + stubsDir)
}
@get:Internal @get:Internal
protected val javaSourceRoots: Set<File> protected val javaSourceRoots: Set<File>
get() = (kotlinCompileTask.sourceRootsContainer.sourceRoots + stubsDir) get() = unfilteredJavaSourceRoots.filterTo(HashSet(), ::isRootAllowed)
.filterTo(HashSet(), ::isRootAllowed)
private fun isRootAllowed(file: File): Boolean = private fun isRootAllowed(file: File): Boolean =
file.exists() && file.exists() &&
@@ -163,8 +173,10 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
} }
// TODO(gavra): Here we assume that kotlinc and javac output is available for incremental runs. We should insert some checks. // TODO(gavra): Here we assume that kotlinc and javac output is available for incremental runs. We should insert some checks.
@Internal @get:Internal
protected fun getCompiledSources() = listOfNotNull(kotlinCompileTask.destinationDir, kotlinCompileTask.javaOutputDir) internal val compiledSources by project.provider {
listOfNotNull(kotlinCompileTask.destinationDir, kotlinCompileTask.javaOutputDir)
}
protected fun getIncrementalChanges(inputs: IncrementalTaskInputs): KaptIncrementalChanges { protected fun getIncrementalChanges(inputs: IncrementalTaskInputs): KaptIncrementalChanges {
return if (isIncremental) { return if (isIncremental) {
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.optionalProvider
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
import java.io.File import java.io.File
@@ -43,8 +45,15 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
private val compileKotlinArgumentsContributor by project.provider {
kotlinCompileTask.compilerArgumentsContributor
}
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
kotlinCompileTask.setupCompilerArgs(args, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors) compileKotlinArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags(
defaultsOnly,
ignoreClasspathResolutionErrors
))
args.pluginClasspaths = pluginClasspath.toSortedPathsArray() args.pluginClasspaths = pluginClasspath.toSortedPathsArray()
@@ -52,7 +61,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
withApClasspath = kaptClasspath, withApClasspath = kaptClasspath,
changedFiles = changedFiles, changedFiles = changedFiles,
classpathChanges = classpathChanges, classpathChanges = classpathChanges,
compiledSourcesDir = getCompiledSources(), compiledSourcesDir = compiledSources,
processIncrementally = processIncrementally processIncrementally = processIncrementally
) )
@@ -69,6 +78,9 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
private var classpathChanges: List<String> = emptyList() private var classpathChanges: List<String> = emptyList()
private var processIncrementally = false private var processIncrementally = false
private val javaPackagePrefix by project.optionalProvider { kotlinCompileTask.javaPackagePrefix }
private val buildReportMode by project.optionalProvider { kotlinCompileTask.buildReportMode }
@TaskAction @TaskAction
fun compile(inputs: IncrementalTaskInputs) { fun compile(inputs: IncrementalTaskInputs) {
logger.debug("Running kapt annotation processing using the Kotlin compiler") logger.debug("Running kapt annotation processing using the Kotlin compiler")
@@ -87,7 +99,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val environment = GradleCompilerEnvironment( val environment = GradleCompilerEnvironment(
compilerClasspath, messageCollector, outputItemCollector, compilerClasspath, messageCollector, outputItemCollector,
buildReportMode = kotlinCompileTask.buildReportMode, buildReportMode = buildReportMode,
outputFiles = allOutputFiles() outputFiles = allOutputFiles()
) )
if (environment.toolsJar == null && !isAtLeastJava9) { if (environment.toolsJar == null && !isAtLeastJava9) {
@@ -99,7 +111,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
sourcesToCompile = emptyList(), sourcesToCompile = emptyList(),
commonSources = emptyList(), commonSources = emptyList(),
javaSourceRoots = javaSourceRoots, javaSourceRoots = javaSourceRoots,
javaPackagePrefix = kotlinCompileTask.javaPackagePrefix, javaPackagePrefix = javaPackagePrefix,
args = args, args = args,
environment = environment environment = environment
) )
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.findKotlinStdlibClasspath import org.jetbrains.kotlin.gradle.tasks.findKotlinStdlibClasspath
import org.jetbrains.kotlin.gradle.tasks.findToolsJar import org.jetbrains.kotlin.gradle.tasks.findToolsJar
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import java.io.File import java.io.File
import java.io.Serializable import java.io.Serializable
@@ -28,8 +29,9 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
@get:InputFiles @get:InputFiles
@get:Classpath @get:Classpath
@Suppress("unused") @Suppress("unused")
val kaptJars: Collection<File> val kaptJars: Collection<File> by project.provider {
get() = project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve() project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve()
}
@get:Input @get:Input
var isVerbose: Boolean = false var isVerbose: Boolean = false
@@ -85,7 +87,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
javaSourceRoots.toList(), javaSourceRoots.toList(),
changedFiles, changedFiles,
getCompiledSources(), compiledSources,
incAptCache, incAptCache,
classpathChanges.toList(), classpathChanges.toList(),
@@ -10,7 +10,6 @@ import org.gradle.api.Task
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.api.tasks.incremental.IncrementalTaskInputs
@@ -24,7 +23,7 @@ import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.incremental.ChangedFiles import org.jetbrains.kotlin.gradle.incremental.ChangedFiles
import org.jetbrains.kotlin.gradle.internal.CompilerArgumentAwareWithInput import org.jetbrains.kotlin.gradle.internal.*
import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
@@ -38,8 +37,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
import org.jetbrains.kotlin.gradle.report.BuildReportMode import org.jetbrains.kotlin.gradle.report.BuildReportMode
import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.incremental.ChangedFiles import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
import org.jetbrains.kotlin.utils.LibraryUtils import org.jetbrains.kotlin.utils.LibraryUtils
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@@ -174,12 +171,12 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
// Store this file collection before it is filtered by File::exists to ensure that Gradle Instant execution doesn't serialize the // Store this file collection before it is filtered by File::exists to ensure that Gradle Instant execution doesn't serialize the
// filtered files, losing those that don't exist yet and will only be created during build // filtered files, losing those that don't exist yet and will only be created during build
private val compileClasspathImpl by project.provider { private val compileClasspathImpl by project.provider {
classpath + additionalClasspath classpath + project.files(additionalClasspath)
} }
@get:Internal // classpath already participates in the checks @get:Internal // classpath already participates in the checks
internal val compileClasspath: Iterable<File> internal val compileClasspath: Iterable<File>
get() = compileClasspathImpl.filter { it.exists() } get() = compileClasspathImpl
@field:Transient @field:Transient
private val sourceFilesExtensionsSources: MutableList<Iterable<String>> = mutableListOf() private val sourceFilesExtensionsSources: MutableList<Iterable<String>> = mutableListOf()
@@ -218,7 +215,8 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
internal val coroutinesStr: String internal val coroutinesStr: String
get() = coroutines.name get() = coroutines.name
private val coroutines: Coroutines by project.provider { @get:Internal
internal val coroutines: Coroutines by project.provider {
kotlinExt.experimental.coroutines kotlinExt.experimental.coroutines
?: coroutinesFromGradleProperties ?: coroutinesFromGradleProperties
?: Coroutines.DEFAULT ?: Coroutines.DEFAULT
@@ -317,27 +315,16 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
*/ */
internal abstract fun callCompilerAsync(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles) internal abstract fun callCompilerAsync(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles)
private val isMultiplatform: Boolean by project.provider { @get:Internal
internal val isMultiplatform: Boolean by project.provider {
project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper } project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper }
} }
override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
args.coroutinesState = when (coroutines) { AbstractKotlinCompileArgumentsContributor(thisTaskProvider).contributeArguments(
Coroutines.ENABLE -> CommonCompilerArguments.ENABLE args,
Coroutines.WARN -> CommonCompilerArguments.WARN compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors)
Coroutines.ERROR -> CommonCompilerArguments.ERROR )
Coroutines.DEFAULT -> CommonCompilerArguments.DEFAULT
}
logger.kotlinDebug { "args.coroutinesState=${args.coroutinesState}" }
if (logger.isDebugEnabled) {
args.verbose = true
}
args.multiPlatform = isMultiplatform
setupPlugins(args)
} }
internal fun setupPlugins(compilerArgs: T) { internal fun setupPlugins(compilerArgs: T) {
@@ -351,6 +338,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
} }
} }
internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
get() = checkNotNull(project.locateTask<T>(name))
@CacheableTask @CacheableTask
open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), KotlinJvmCompile { open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), KotlinJvmCompile {
@get:Internal @get:Internal
@@ -390,28 +380,15 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
K2JVMCompilerArguments() K2JVMCompilerArguments()
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
args.apply { fillDefaultValues() } compilerArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags(
super.setupCompilerArgs(args, defaultsOnly = defaultsOnly, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors) defaultsOnly,
ignoreClasspathResolutionErrors
))
}
args.moduleName = moduleName @get:Internal
logger.kotlinDebug { "args.moduleName = ${args.moduleName}" } internal val compilerArgumentsContributor: CompilerArgumentsContributor<K2JVMCompilerArguments> by project.provider {
KotlinJvmCompilerArgumentsContributor(thisTaskProvider)
args.friendPaths = friendPaths
logger.kotlinDebug { "args.friendPaths = ${args.friendPaths?.joinToString() ?: "[]"}" }
if (defaultsOnly) return
args.allowNoSourceFiles = true
args.classpathAsList = try {
compileClasspath.toList()
} catch (e: Exception) {
if (ignoreClasspathResolutionErrors) emptyList() else throw(e)
}
args.destinationAsFile = destinationDir
parentKotlinOptionsImpl?.updateArguments(args)
kotlinOptionsImpl.updateArguments(args)
logger.kotlinDebug { "$name destinationDir = $destinationDir" }
} }
@Internal @Internal
@@ -488,13 +465,13 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
} }
// override setSource to track source directory sets and files (for generated android folders) // override setSource to track source directory sets and files (for generated android folders)
override fun setSource(sources: Any?) { override fun setSource(sources: Any) {
sourceRootsContainer.set(sources) sourceRootsContainer.set(sources)
super.setSource(sources) super.setSource(sources)
} }
// override source to track source directory sets and files (for generated android folders) // override source to track source directory sets and files (for generated android folders)
override fun source(vararg sources: Any?): SourceTask? { override fun source(vararg sources: Any): SourceTask {
sourceRootsContainer.add(*sources) sourceRootsContainer.add(*sources)
return super.source(*sources) return super.source(*sources)
} }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
internal operator fun <T> Provider<T>.getValue(thisRef: Any?, property: KProperty<*>) = get() internal operator fun <T> Provider<T>.getValue(thisRef: Any?, property: KProperty<*>) = get()
@@ -23,3 +24,13 @@ internal fun <T : Any> Project.newProperty(initialize: (() -> T)? = null): Prope
if (initialize != null) if (initialize != null)
set(provider(initialize)) set(provider(initialize))
} }
private class OptionalProviderDelegate<T>(private val provider: Provider<T?>) : ReadOnlyProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? =
if (provider.isPresent)
provider.get()
else null
}
internal fun <T> Project.optionalProvider(initialize: () -> T?): ReadOnlyProperty<Any?, T?> =
OptionalProviderDelegate(provider(initialize))