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() {
private val androidGradlePluginVersion: AGPVersion
get() = AGPVersion.v4_0_ALPHA_1
get() = AGPVersion.v4_0_ALPHA_8
override fun defaultBuildOptions() =
super.defaultBuildOptions().copy(
@@ -23,7 +23,7 @@ class InstantExecutionIT : BaseGradleIT() {
androidGradlePluginVersion = androidGradlePluginVersion
)
private val minimumGradleVersion = GradleVersionRequired.AtLeast("6.1-milestone-1")
private val minimumGradleVersion = GradleVersionRequired.AtLeast("6.1-rc-3")
@Test
fun testSimpleKotlinJvmProject() = with(Project("kotlinProject", minimumGradleVersion)) {
@@ -31,14 +31,37 @@ class InstantExecutionIT : BaseGradleIT() {
}
@Test
fun testSimpleKotlinAndroidProject() = with(Project("AndroidProject", minimumGradleVersion)) {
applyAndroidAndroid40Alpha4KotlinVersionWorkaround()
testInstantExecutionOf(":Lib:compileFlavor1DebugKotlin", ":Android:compileFlavor1DebugKotlin")
fun testSimpleKotlinAndroidProject() = with(Project("android-dagger", minimumGradleVersion, "kapt2")) {
applyAndroid40Alpha4KotlinVersionWorkaround()
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
instantExecutionOf(*taskNames) {
instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful()
assertTasksExecuted(*taskNames)
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
instantExecutionOf(*taskNames) {
instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful()
assertTasksExecuted(*taskNames)
}
instantExecutionOf(*taskNames) {
instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful()
assertTasksUpToDate(*taskNames)
}
@@ -66,8 +89,12 @@ class InstantExecutionIT : BaseGradleIT() {
}
}
private fun Project.instantExecutionOf(vararg tasks: String, check: CompiledProject.() -> Unit) =
build("-Dorg.gradle.unsafe.instant-execution=true", *tasks, check = check)
private fun Project.instantExecutionOf(
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
@@ -98,7 +125,7 @@ class InstantExecutionIT : BaseGradleIT() {
* 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
*/
private fun Project.applyAndroidAndroid40Alpha4KotlinVersionWorkaround() {
private fun Project.applyAndroid40Alpha4KotlinVersionWorkaround() {
setupWorkingDir()
val resolutionStrategyHack = """
@@ -22,6 +22,6 @@ class AGPVersion private constructor(private val versionNumber: VersionNumber) {
val v3_1_0 = fromString("3.1.0")
val v3_2_0 = fromString("3.2.0")
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.file.FileCollection
import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
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.KotlinCompile
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.pathsAsStringRelativeTo
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
import java.io.File
@@ -37,6 +34,7 @@ open class KaptGenerateStubsTask : KotlinCompile() {
override val sourceRootsContainer = FilteringSourceRootsContainer(emptyList(), { isSourceRootAllowed(it) })
@get:Internal
@field:Transient // can't serialize task references in Gradle instant execution state
internal lateinit var kotlinCompileTask: KotlinCompile
@get:OutputDirectory
@@ -48,7 +46,7 @@ open class KaptGenerateStubsTask : KotlinCompile() {
@get:Classpath
@get:InputFiles
val kaptClasspath: FileCollection
get() = project.files(*kaptClasspathConfigurations.toTypedArray())
get() = project.files(kaptClasspathConfigurations)
@get:Internal
internal lateinit var kaptClasspathConfigurations: List<Configuration>
@@ -56,8 +54,9 @@ open class KaptGenerateStubsTask : KotlinCompile() {
@get:Classpath
@get:InputFiles
@Suppress("unused")
internal val kotlinTaskPluginClasspath
get() = kotlinCompileTask.pluginClasspath
internal val kotlinTaskPluginClasspath by project.provider {
kotlinCompileTask.pluginClasspath
}
@get:Input
override var useModuleDetection: Boolean
@@ -66,11 +65,11 @@ open class KaptGenerateStubsTask : KotlinCompile() {
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))
}
override fun setSource(sources: Any?) {
override fun setSource(sources: Any) {
super.setSource(sourceRootsContainer.set(sources))
}
@@ -79,8 +78,15 @@ open class KaptGenerateStubsTask : KotlinCompile() {
!stubsDir.isParentOf(source) &&
!generatedSourcesDir.isParentOf(source)
private val compileKotlinArgumentsContributor by project.provider {
kotlinCompileTask.compilerArgumentsContributor
}
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)
args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray()
@@ -90,10 +96,13 @@ open class KaptGenerateStubsTask : KotlinCompile() {
args.destinationAsFile = this.destinationDir
}
override fun getSourceRoots(): SourceRoots.ForJvm =
private val sourceRoots by project.provider {
kotlinCompileTask.getSourceRoots().let {
val javaSourceRoots = it.javaSourceRoots.filterTo(HashSet()) { isSourceRootAllowed(it) }
val kotlinSourceFiles = it.kotlinSourceFiles
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.cacheOnlyIfEnabledForKotlin
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.isJavaFile
import java.io.File
import java.util.jar.JarFile
@@ -30,6 +31,7 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
override fun localStateDirectories(): FileCollection = project.files()
@get:Internal
@field:Transient
internal lateinit var kotlinCompileTask: KotlinCompile
@get:Internal
@@ -38,12 +40,13 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
@get:Classpath
@get:InputFiles
val kaptClasspath: FileCollection
get() = project.files(*kaptClasspathConfigurations.toTypedArray())
get() = project.files(kaptClasspathConfigurations)
@get:Classpath
@get:InputFiles
val compilerClasspath: List<File>
get() = kotlinCompileTask.computedCompilerClasspath
val compilerClasspath: List<File> by project.provider {
kotlinCompileTask.computedCompilerClasspath
}
@get:Internal
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
@get:Internal
val classpath: FileCollection
get() = kotlinCompileTask.classpath
val classpath: FileCollection by project.provider {
kotlinCompileTask.classpath
}
@Suppress("unused", "DeprecatedCallableAddReplaceWith")
@Deprecated(
@@ -92,8 +96,9 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
)
@get:CompileClasspath
@get:InputFiles
internal val internalAbiClasspath: FileCollection
get() = if (includeCompileClasspath) project.files() else kotlinCompileTask.classpath
internal val internalAbiClasspath: FileCollection by project.provider {
if (includeCompileClasspath) project.files() else kotlinCompileTask.classpath
}
@Suppress("unused", "DeprecatedCallableAddReplaceWith")
@@ -103,8 +108,9 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
)
@get:Classpath
@get:InputFiles
internal val internalNonAbiClasspath: FileCollection
get() = if (includeCompileClasspath) kotlinCompileTask.classpath else project.files()
internal val internalNonAbiClasspath: FileCollection by project.provider {
if (includeCompileClasspath) kotlinCompileTask.classpath else project.files()
}
@get:Internal
var useBuildCache: Boolean = false
@@ -120,10 +126,14 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
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
protected val javaSourceRoots: Set<File>
get() = (kotlinCompileTask.sourceRootsContainer.sourceRoots + stubsDir)
.filterTo(HashSet(), ::isRootAllowed)
get() = unfilteredJavaSourceRoots.filterTo(HashSet(), ::isRootAllowed)
private fun isRootAllowed(file: File): Boolean =
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.
@Internal
protected fun getCompiledSources() = listOfNotNull(kotlinCompileTask.destinationDir, kotlinCompileTask.javaOutputDir)
@get:Internal
internal val compiledSources by project.provider {
listOfNotNull(kotlinCompileTask.destinationDir, kotlinCompileTask.javaOutputDir)
}
protected fun getIncrementalChanges(inputs: IncrementalTaskInputs): KaptIncrementalChanges {
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.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
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 java.io.File
@@ -43,8 +45,15 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
private val compileKotlinArgumentsContributor by project.provider {
kotlinCompileTask.compilerArgumentsContributor
}
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
kotlinCompileTask.setupCompilerArgs(args, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors)
compileKotlinArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags(
defaultsOnly,
ignoreClasspathResolutionErrors
))
args.pluginClasspaths = pluginClasspath.toSortedPathsArray()
@@ -52,7 +61,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
withApClasspath = kaptClasspath,
changedFiles = changedFiles,
classpathChanges = classpathChanges,
compiledSourcesDir = getCompiledSources(),
compiledSourcesDir = compiledSources,
processIncrementally = processIncrementally
)
@@ -69,6 +78,9 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
private var classpathChanges: List<String> = emptyList()
private var processIncrementally = false
private val javaPackagePrefix by project.optionalProvider { kotlinCompileTask.javaPackagePrefix }
private val buildReportMode by project.optionalProvider { kotlinCompileTask.buildReportMode }
@TaskAction
fun compile(inputs: IncrementalTaskInputs) {
logger.debug("Running kapt annotation processing using the Kotlin compiler")
@@ -87,7 +99,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
val outputItemCollector = OutputItemsCollectorImpl()
val environment = GradleCompilerEnvironment(
compilerClasspath, messageCollector, outputItemCollector,
buildReportMode = kotlinCompileTask.buildReportMode,
buildReportMode = buildReportMode,
outputFiles = allOutputFiles()
)
if (environment.toolsJar == null && !isAtLeastJava9) {
@@ -99,7 +111,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
sourcesToCompile = emptyList(),
commonSources = emptyList(),
javaSourceRoots = javaSourceRoots,
javaPackagePrefix = kotlinCompileTask.javaPackagePrefix,
javaPackagePrefix = javaPackagePrefix,
args = args,
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.findKotlinStdlibClasspath
import org.jetbrains.kotlin.gradle.tasks.findToolsJar
import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.io.Serializable
@@ -28,8 +29,9 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
@get:InputFiles
@get:Classpath
@Suppress("unused")
val kaptJars: Collection<File>
get() = project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve()
val kaptJars: Collection<File> by project.provider {
project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve()
}
@get:Input
var isVerbose: Boolean = false
@@ -85,7 +87,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
javaSourceRoots.toList(),
changedFiles,
getCompiledSources(),
compiledSources,
incAptCache,
classpathChanges.toList(),
@@ -10,7 +10,6 @@ import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
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.gradle.dsl.*
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.tasks.TaskWithLocalState
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.utils.*
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 java.io.File
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
// filtered files, losing those that don't exist yet and will only be created during build
private val compileClasspathImpl by project.provider {
classpath + additionalClasspath
classpath + project.files(additionalClasspath)
}
@get:Internal // classpath already participates in the checks
internal val compileClasspath: Iterable<File>
get() = compileClasspathImpl.filter { it.exists() }
get() = compileClasspathImpl
@field:Transient
private val sourceFilesExtensionsSources: MutableList<Iterable<String>> = mutableListOf()
@@ -218,7 +215,8 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
internal val coroutinesStr: String
get() = coroutines.name
private val coroutines: Coroutines by project.provider {
@get:Internal
internal val coroutines: Coroutines by project.provider {
kotlinExt.experimental.coroutines
?: coroutinesFromGradleProperties
?: Coroutines.DEFAULT
@@ -317,27 +315,16 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
*/
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 }
}
override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
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)
AbstractKotlinCompileArgumentsContributor(thisTaskProvider).contributeArguments(
args,
compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors)
)
}
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
open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), KotlinJvmCompile {
@get:Internal
@@ -390,28 +380,15 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
K2JVMCompilerArguments()
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
args.apply { fillDefaultValues() }
super.setupCompilerArgs(args, defaultsOnly = defaultsOnly, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors)
compilerArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags(
defaultsOnly,
ignoreClasspathResolutionErrors
))
}
args.moduleName = moduleName
logger.kotlinDebug { "args.moduleName = ${args.moduleName}" }
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" }
@get:Internal
internal val compilerArgumentsContributor: CompilerArgumentsContributor<K2JVMCompilerArguments> by project.provider {
KotlinJvmCompilerArgumentsContributor(thisTaskProvider)
}
@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 fun setSource(sources: Any?) {
override fun setSource(sources: Any) {
sourceRootsContainer.set(sources)
super.setSource(sources)
}
// 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)
return super.source(*sources)
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
internal operator fun <T> Provider<T>.getValue(thisRef: Any?, property: KProperty<*>) = get()
@@ -22,4 +23,14 @@ internal fun <T : Any> Project.newProperty(initialize: (() -> T)? = null): Prope
(project.objects.property(Any::class.java) as Property<T>).apply {
if (initialize != null)
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))