[Gradle] Add APIs that allow AGP to configure Kotlin compilation

Add APIs that describe tasks, so that AGP can configure them. This
change all adds support to *Config objects to support configuring
tasks when there is no access to internal KGP objects.

^KT-50869 In Progress
This commit is contained in:
Ivan Gavrilovic
2022-03-23 18:14:47 +00:00
committed by Space
parent 46cb4902e1
commit ca193a89d2
25 changed files with 1125 additions and 365 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import java.io.File
@@ -73,6 +74,44 @@ open class CompilerPluginConfig {
fun addPluginArgument(pluginId: String, option: SubpluginOption) {
optionsByPluginId.getOrPut(pluginId) { mutableListOf() }.add(option)
}
@Input
fun getAsTaskInputArgs(): Map<String, String> {
val result = mutableMapOf<String, String>()
optionsByPluginId.forEach { (id, subpluginOptions) ->
result += computeForSubpluginId(id, subpluginOptions)
}
return result
}
private fun computeForSubpluginId(subpluginId: String, subpluginOptions: List<SubpluginOption>): Map<String, String> {
// There might be several options with the same key. We group them together
// and add an index to the Gradle input property name to resolve possible duplication:
val result = mutableMapOf<String, String>()
val pluginOptionsGrouped = subpluginOptions.groupBy { it.key }
for ((optionKey, optionsGroup) in pluginOptionsGrouped) {
optionsGroup.forEachIndexed { index, option ->
val indexSuffix = if (optionsGroup.size > 1) ".$index" else ""
when (option) {
is InternalSubpluginOption -> return@forEachIndexed
is CompositeSubpluginOption -> {
val subpluginIdWithWrapperKey = "$subpluginId.$optionKey$indexSuffix"
result += computeForSubpluginId(subpluginIdWithWrapperKey, option.originalOptions)
}
is FilesSubpluginOption -> when (option.kind) {
FilesOptionKind.INTERNAL -> Unit
}.run { /* exhaustive when */ }
else -> {
result["$subpluginId." + option.key + indexSuffix] = option.value
}
}
}
}
return result
}
}
/**
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2021 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.plugin
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KaptExtensionConfig
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtensionConfig
import org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs
import org.jetbrains.kotlin.gradle.tasks.Kapt
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
/** An API used by third-party plugins to integration with the Kotlin Gradle plugin. */
interface KotlinJvmFactory {
/** Instance of DSL object that should be used to configure KAPT stub generation and annotation processing tasks.*/
val kaptExtension: KaptExtensionConfig
/** Instance of DSL object that should be used to configure Kotlin compilation pipeline. */
val kotlinExtension: KotlinTopLevelExtensionConfig
/** Gets the current version of the Kotlin Gradle plugin. */
val pluginVersion: String
/** Creates instance of DSL object that should be used to configure JVM/android specific compilation. */
fun createKotlinJvmOptions(): KotlinJvmOptions
/** Creates a Kotlin compile task. */
fun registerKotlinJvmCompileTask(taskName: String): TaskProvider<out KotlinJvmCompile>
/** Creates a stub generation task which creates Java sources stubs from Kotlin sources. */
fun registerKaptGenerateStubsTask(taskName: String): TaskProvider<out KaptGenerateStubs>
/** Creates a KAPT task which runs annotation processing. */
fun registerKaptTask(taskName: String): TaskProvider<out Kapt>
/** Adds a compiler plugin dependency to this project. This can be e.g a Maven coordinate or a project included in the build. */
fun addCompilerPluginDependency(dependency: Provider<Any>)
/** Returns a [FileCollection] that contains all compiler plugins classpath for this project. */
fun getCompilerPlugins(): FileCollection
}
@@ -0,0 +1,160 @@
/*
* Copyright 2010-2021 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.tasks
import org.gradle.api.Task
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.api.tasks.util.PatternFilterable
import org.gradle.work.Incremental
import org.gradle.work.NormalizeLineEndings
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig
interface KotlinCompileTool : PatternFilterable, Task {
@get:InputFiles
@get:SkipWhenEmpty
@get:NormalizeLineEndings
@get:IgnoreEmptyDirectories
@get:PathSensitive(PathSensitivity.RELATIVE)
val sources: FileCollection
/**
* Sets sources for this task.
* The given sources object is evaluated as per [org.gradle.api.Project.files].
*/
fun source(vararg sources: Any)
/**
* Sets sources for this task.
* The given sources object is evaluated as per [org.gradle.api.Project.files].
*/
fun setSource(vararg sources: Any)
@get:Classpath
@get:NormalizeLineEndings
@get:Incremental
val libraries: ConfigurableFileCollection
@get:OutputDirectory
val destinationDirectory: DirectoryProperty
}
interface BaseKotlinCompile : KotlinCompileTool {
@get:Internal
val friendPaths: ConfigurableFileCollection
@get:NormalizeLineEndings
@get:Classpath
val pluginClasspath: ConfigurableFileCollection
@get:Input
val moduleName: Property<String>
@get:Internal
val sourceSetName: Property<String>
@get:Input
val multiPlatformEnabled: Property<Boolean>
@get:Input
val useModuleDetection: Property<Boolean>
@get:Nested
val pluginOptions: ListProperty<CompilerPluginConfig>
}
interface KotlinJvmCompile : BaseKotlinCompile, KotlinCompile<KotlinJvmOptions> {
// JVM specific
@get:Internal("Takes part in compiler args.")
val parentKotlinOptions: Property<KotlinJvmOptions>
}
interface KaptGenerateStubs : KotlinJvmCompile {
@get:OutputDirectory
val stubsDir: DirectoryProperty
@get:Internal("Not an input, just passed as kapt args. ")
val kaptClasspath: ConfigurableFileCollection
}
interface BaseKapt : Task {
//part of kaptClasspath consisting from external artifacts only
//basically kaptClasspath = kaptExternalClasspath + artifacts built locally
@get:NormalizeLineEndings
@get:Classpath
val kaptExternalClasspath: ConfigurableFileCollection
@get:Internal
val kaptClasspathConfigurationNames: ListProperty<String>
/**
* Output directory that contains caches necessary to support incremental annotation processing.
*/
@get:LocalState
val incAptCache: DirectoryProperty
@get:OutputDirectory
val classesDir: DirectoryProperty
@get:OutputDirectory
val destinationDir: DirectoryProperty
/** Used in the model builder only. */
@get:OutputDirectory
val kotlinSourcesDestinationDir: DirectoryProperty
@get:Nested
val annotationProcessorOptionProviders: MutableList<Any>
@get:Internal
val stubsDir: DirectoryProperty
@get:NormalizeLineEndings
@get:Classpath
val kaptClasspath: ConfigurableFileCollection
@get:Internal
val compiledSources: ConfigurableFileCollection
@get:Internal("Task implementation adds correct input annotation.")
val classpath: ConfigurableFileCollection
/** Needed for the model builder. */
@get:Internal
val sourceSetName: Property<String>
@get:InputFiles
@get:NormalizeLineEndings
@get:IgnoreEmptyDirectories
@get:Incremental
@get:PathSensitive(PathSensitivity.RELATIVE)
val source: ConfigurableFileCollection
@get:Input
val includeCompileClasspath: Property<Boolean>
@get:Internal("Used to compute javac option.")
val defaultJavaSourceCompatibility: Property<String>
}
interface Kapt : BaseKapt {
@get:Input
val addJdkClassesToClasspath: Property<Boolean>
@get:NormalizeLineEndings
@get:Classpath
val kaptJars: ConfigurableFileCollection
}
@@ -53,6 +53,7 @@ class UpToDateIT : KGPBaseTest() {
OptionMutation("compileKotlin.kotlinOptions.freeCompilerArgs", "[]", "['-Xallow-kotlin-package']"),
OptionMutation("archivesBaseName", "'someName'", "'otherName'"),
subpluginOptionMutation,
subpluginOptionMutationWithKapt,
externalOutputMutation,
compilerClasspathMutation
)
@@ -137,7 +138,9 @@ class UpToDateIT : KGPBaseTest() {
}
override fun checkAfterRebuild(buildResult: BuildResult) = with(buildResult) {
assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin", ":kaptKotlin")
assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin")
// KAPT with workers is not impacted by compiler classpath changes.
assertTasksUpToDate(":kaptKotlin")
}
}
@@ -163,6 +166,29 @@ class UpToDateIT : KGPBaseTest() {
}
}
private val subpluginOptionMutationWithKapt = object : ProjectMutation {
override val name: String get() = "subpluginOptionMutationWithKapt"
override fun initProject(project: TestProject) = with(project) {
buildGradle.appendText(
"\n" + """
apply plugin: 'kotlin-kapt'
plugins.apply("org.jetbrains.kotlin.plugin.allopen")
allOpen { annotation("allopen.Foo"); annotation("allopen.Bar") }
""".trimIndent()
)
}
override fun mutateProject(project: TestProject) = with(project) {
buildGradle.modify { it.replace("allopen.Foo", "allopen.Baz") }
}
override fun checkAfterRebuild(buildResult: BuildResult) = with(buildResult) {
assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin")
assertTasksUpToDate(":kaptKotlin")
}
}
private val externalOutputMutation = object : ProjectMutation {
override val name = "externalOutputMutation"
@@ -0,0 +1,120 @@
/*
* Copyright 2010-2022 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.tasks
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import kotlin.io.path.createDirectories
import kotlin.io.path.writeText
@DisplayName("JVM API validation")
class KotlinJvmApiTest : KGPBaseTest() {
@DisplayName("Kotlin compilation can be set up using APIs")
@JvmGradlePluginTests
@GradleTest
internal fun kotlinCompilationShouldRunIfSetUpWithApi(gradleVersion: GradleVersion) {
project(
projectName = "kotlinJavaProject",
gradleVersion = gradleVersion
) {
projectPath.resolve("src").let {
it.deleteRecursively()
it.resolve("main").createDirectories()
it.resolve("main/foo.kt").writeText(
"""
class Foo
""".trimIndent()
)
}
buildGradle.modify {
it.replace("id 'org.jetbrains.kotlin.jvm'", "id 'org.jetbrains.kotlin.jvm' apply false") +
"""
import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
KotlinBaseApiPlugin apiPlugin = plugins.apply(KotlinBaseApiPlugin.class)
apiPlugin.registerKotlinJvmCompileTask("foo").configure {
it.source("src/main")
it.multiPlatformEnabled.set(false)
it.moduleName.set("main")
it.sourceSetName.set("main")
it.useModuleDetection.set(false)
it.destinationDirectory.fileValue(new File(project.buildDir, "fooOutput"))
}
""".trimIndent()
}
val expectedOutput = projectPath.resolve("build/fooOutput/Foo.class")
build("foo") {
assertFileExists(expectedOutput)
}
}
}
@DisplayName("KAPT can be set up using APIs")
@OtherGradlePluginTests
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0)
@GradleTest
internal fun kaptShouldRunIfSetUpWithApi(gradleVersion: GradleVersion) {
project(
projectName = "kotlinJavaProject",
gradleVersion = gradleVersion
) {
projectPath.resolve("src").let {
it.deleteRecursively()
it.resolve("main").createDirectories()
it.resolve("main/foo.kt").writeText(
"""
class Foo
""".trimIndent()
)
}
buildGradle.modify {
it.replace("id 'org.jetbrains.kotlin.jvm'", "id 'org.jetbrains.kotlin.jvm' apply false") +
"""
import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
KotlinBaseApiPlugin apiPlugin = plugins.apply(KotlinBaseApiPlugin.class)
File kaptFakeJar = new File(project.projectDir, "kapt.jar")
kaptFakeJar.createNewFile()
apiPlugin.addCompilerPluginDependency(
project.provider {
"org.jetbrains.kotlin:kotlin-annotation-processing-gradle:${"$"}kotlin_version"
}
)
apiPlugin.registerKaptGenerateStubsTask("foo").configure {
it.source("src/main")
it.multiPlatformEnabled.set(false)
it.moduleName.set("main")
it.sourceSetName.set("main")
it.useModuleDetection.set(false)
it.destinationDirectory.fileValue(new File(project.buildDir, "fooOutput"))
it.stubsDir.fileValue(new File(project.buildDir, "fooOutputStubs"))
it.kaptClasspath.from(kaptFakeJar)
it.pluginClasspath.from(apiPlugin.getCompilerPlugins())
}
""".trimIndent()
}
val expectedOutputClass = projectPath.resolve("build/fooOutput/Foo.class")
val expectedOutputStubs = listOf(
projectPath.resolve("build/fooOutputStubs/Foo.java"),
projectPath.resolve("build/fooOutputStubs/Foo.kapt_metadata"),
projectPath.resolve("build/fooOutputStubs/error/NonExistentClass.java")
)
build("foo") {
assertFileExists(expectedOutputClass)
expectedOutputStubs.forEach { assertFileExists(it) }
}
}
}
}
@@ -23,6 +23,10 @@ import org.gradle.api.tasks.Internal
interface KotlinJsCompile : KotlinCompile<KotlinJsOptions>
@Deprecated(
message = "Moved into API artifact",
replaceWith = ReplaceWith("KotlinJvmCompile", "org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile")
)
interface KotlinJvmCompile : KotlinCompile<KotlinJvmOptions>
interface KotlinCommonCompile : KotlinCompile<KotlinMultiplatformCommonOptions>
@@ -29,8 +29,7 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.configuration.*
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
@@ -386,9 +385,9 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
val androidOptions = androidVariantData?.annotationProcessorOptions ?: emptyMap()
val androidSubpluginOptions = androidOptions.toList().map { SubpluginOption(it.first, it.second) }
androidSubpluginOptions + kaptExtension.getAdditionalArguments(project, androidVariantData, androidExtension).toList()
.map { SubpluginOption(it.first, it.second) } +
FilesSubpluginOption(KAPT_KOTLIN_GENERATED, listOf(kotlinSourcesOutputDir))
androidSubpluginOptions + getNonAndroidDslApOptions(
kaptExtension, project, listOf(kotlinSourcesOutputDir), androidVariantData, androidExtension
).get()
}
private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean): TaskProvider<out KaptTask> {
@@ -396,9 +395,9 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
val taskName = getKaptTaskName("kapt")
val taskConfigAction = if (taskClass == KaptWithoutKotlincTask::class.java ) {
KaptWithoutKotlincConfigAction(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension)
KaptWithoutKotlincConfig(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension)
} else {
KaptWithKotlincConfigAction(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension)
KaptWithKotlincConfig(kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile, kaptExtension)
}
val kaptClasspathConfiguration = project.configurations.create("kaptClasspath_$taskName")
@@ -452,22 +451,18 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
} else {
check(taskClass == KaptWithoutKotlincTask::class.java)
getDslKaptApOptions()
}.map {
val res = CompilerPluginOptions()
it.forEach { res.addPluginArgument(KAPT_SUBPLUGIN_ID, it) }
return@map res
}
}.toCompilerPluginOptions()
task.kaptPluginOptions.add(pluginOptions)
}
return if (taskClass == KaptWithoutKotlincTask::class.java) {
taskConfigAction as KaptWithoutKotlincConfigAction
taskConfigAction as KaptWithoutKotlincConfig
project.registerTask(taskName, KaptWithoutKotlincTask::class.java, emptyList()).also {
taskConfigAction.execute(it)
}
} else {
taskConfigAction as KaptWithKotlincConfigAction
taskConfigAction as KaptWithKotlincConfig
project.registerTask(taskName, KaptWithKotlincTask::class.java, emptyList()).also {
taskConfigAction.execute(it)
}
@@ -593,6 +588,21 @@ internal fun buildKaptSubpluginOptions(
return pluginOptions
}
/* Returns AP options from KAPT static DSL. */
internal fun getNonAndroidDslApOptions(
kaptExtension: KaptExtension,
project: Project,
kotlinSourcesOutputDir: Iterable<File>,
variantData: BaseVariant?,
androidExtension: BaseExtension?
): Provider<List<SubpluginOption>> {
return project.provider {
kaptExtension.getAdditionalArguments(project, variantData, androidExtension).toList()
.map { SubpluginOption(it.first, it.second) } +
FilesSubpluginOption(Kapt3GradleSubplugin.KAPT_KOTLIN_GENERATED, kotlinSourcesOutputDir)
}
}
private fun encodeList(options: Map<String, String>): String {
val os = ByteArrayOutputStream()
val oos = ObjectOutputStream(os)
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.file.*
import org.gradle.api.model.ObjectFactory
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.work.Incremental
@@ -27,9 +26,10 @@ import org.gradle.work.NormalizeLineEndings
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.utils.isParentOf
import org.jetbrains.kotlin.gradle.tasks.toSingleCompilerPluginOptions
import org.jetbrains.kotlin.gradle.utils.isParentOf
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
import java.io.File
@@ -43,13 +43,12 @@ abstract class KaptGenerateStubsTask @Inject constructor(
KotlinJvmOptionsImpl(),
workerExecutor,
objectFactory
) {
), KaptGenerateStubs {
@get:OutputDirectory
abstract val stubsDir: DirectoryProperty
@get:Internal("Not an input, just passed as kapt args. ")
abstract val kaptClasspath: ConfigurableFileCollection
// Bug in Gradle - without this override Gradle complains @Internal is not
// compatible with @Classpath and @Incremental annotations
@get:Internal
abstract override val libraries: ConfigurableFileCollection
/* Used as input as empty kapt classpath should not trigger stub generation, but a non-empty one should. */
@Input
@@ -88,10 +87,10 @@ abstract class KaptGenerateStubsTask @Inject constructor(
}
@get:Internal
abstract override val scriptSources: FileCollection
override val scriptSources: FileCollection = objectFactory.fileCollection()
@get:Internal
abstract override val androidLayoutResources: FileCollection
override val androidLayoutResources: FileCollection = objectFactory.fileCollection()
override val incrementalProps: List<FileCollection>
get() = listOf(
@@ -3,7 +3,6 @@ package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
@@ -20,6 +19,7 @@ import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChange
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.UnknownSnapshot
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.utils.addToStdlib.cast
@@ -32,7 +32,8 @@ abstract class KaptTask @Inject constructor(
objectFactory: ObjectFactory
) : DefaultTask(),
TaskWithLocalState,
UsesKotlinJavaToolchain {
UsesKotlinJavaToolchain,
BaseKapt {
init {
cacheOnlyIfEnabledForKotlin()
@@ -41,26 +42,10 @@ abstract class KaptTask @Inject constructor(
outputs.cacheIf(reason) { useBuildCache }
}
@get:Internal
internal abstract val stubsDir: DirectoryProperty
@get:NormalizeLineEndings
@get:Classpath
abstract val kaptClasspath: ConfigurableFileCollection
//part of kaptClasspath consisting from external artifacts only
//basically kaptClasspath = kaptExternalClasspath + artifacts built locally
@get:NormalizeLineEndings
@get:Classpath
abstract val kaptExternalClasspath: ConfigurableFileCollection
@get:NormalizeLineEndings
@get:Classpath
abstract val compilerClasspath: ConfigurableFileCollection
@get:Internal
internal abstract val kaptClasspathConfigurationNames: ListProperty<String>
@get:PathSensitive(PathSensitivity.NONE)
@get:Incremental
@get:NormalizeLineEndings
@@ -70,39 +55,19 @@ abstract class KaptTask @Inject constructor(
abstract val classpathStructure: ConfigurableFileCollection
@get:Internal
abstract val kaptPluginOptions: ListProperty<CompilerPluginOptions>
/**
* Output directory that contains caches necessary to support incremental annotation processing.
*/
@get:LocalState
abstract val incAptCache: DirectoryProperty
@get:OutputDirectory
internal abstract val classesDir: DirectoryProperty
@get:OutputDirectory
abstract val destinationDir: DirectoryProperty
/** Used in the model builder only. */
@get:OutputDirectory
abstract val kotlinSourcesDestinationDir: DirectoryProperty
abstract val kaptPluginOptions: ListProperty<CompilerPluginConfig>
@get:Nested
internal val annotationProcessorOptionProviders: MutableList<Any> = mutableListOf()
override val annotationProcessorOptionProviders: MutableList<Any> = mutableListOf()
@get:Input
internal val includeCompileClasspath: Property<Boolean> = objectFactory
override val includeCompileClasspath: Property<Boolean> = objectFactory
.propertyWithConvention(true)
.chainedFinalizeValueOnRead()
@get:Input
internal var isIncremental = true
// @Internal because _abiClasspath and _nonAbiClasspath are used for actual checks
@get:Internal
abstract val classpath: ConfigurableFileCollection
@get:Internal
internal val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory
.propertyWithNewInstance({ null })
@@ -136,17 +101,6 @@ abstract class KaptTask @Inject constructor(
@get:Internal
var useBuildCache: Boolean = false
/** Needed for the model builder. */
@get:Internal
abstract val sourceSetName: Property<String>
@get:InputFiles
@get:IgnoreEmptyDirectories
@get:NormalizeLineEndings
@get:Incremental
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val source: ConfigurableFileCollection
@get:Internal
override val metrics: Property<BuildMetricsReporter> = objectFactory
.property(BuildMetricsReporterImpl())
@@ -154,9 +108,6 @@ abstract class KaptTask @Inject constructor(
@get:Input
abstract val verbose: Property<Boolean>
@get:Internal
abstract val defaultJavaSourceCompatibility: Property<String>
protected fun checkAnnotationProcessorClasspath() {
if (!includeCompileClasspath.get()) return
@@ -186,10 +137,6 @@ abstract class KaptTask @Inject constructor(
}
}
// TODO(gavra): Here we assume that kotlinc and javac output is available for incremental runs. We should insert some checks.
@get:Internal
internal abstract val compiledSources: ConfigurableFileCollection
protected fun getIncrementalChanges(inputChanges: InputChanges): KaptIncrementalChanges {
return if (isIncremental) {
findClasspathChanges(inputChanges)
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChan
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig
import org.jetbrains.kotlin.gradle.report.ReportingSettings
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.newInstance
@@ -46,7 +47,7 @@ abstract class KaptWithKotlincTask @Inject constructor(
/** Used only as task input, actual values come from [compileKotlinArgumentsContributor]. */
@get:Nested
internal abstract val additionalPluginOptionsAsInputs: ListProperty<CompilerPluginOptions>
internal abstract val additionalPluginOptionsAsInputs: ListProperty<CompilerPluginConfig>
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
@@ -16,7 +16,6 @@ import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.listProperty
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.work.InputChanges
import org.gradle.work.NormalizeLineEndings
import org.gradle.workers.IsolationMode
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
@@ -24,8 +23,7 @@ import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.internal.kapt.classloaders.ClassLoadersCache
import org.jetbrains.kotlin.gradle.internal.kapt.classloaders.rootOrSelf
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinAndroidPluginWrapper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.Kapt
import org.jetbrains.kotlin.gradle.tasks.toSingleCompilerPluginOptions
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.utils.PathUtil
@@ -40,11 +38,7 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
objectFactory: ObjectFactory,
private val providerFactory: ProviderFactory,
private val workerExecutor: WorkerExecutor
) : KaptTask(objectFactory) {
@get:NormalizeLineEndings
@get:Classpath
abstract val kaptJars: ConfigurableFileCollection
) : KaptTask(objectFactory), Kapt {
@get:Input
var classLoadersCacheSize: Int = 0
@@ -61,9 +55,6 @@ abstract class KaptWithoutKotlincTask @Inject constructor(
@get:Input
abstract val javacOptions: MapProperty<String, String>
@get:Input
abstract val addJdkClassesToClasspath: Property<Boolean>
@get:Internal
internal val projectDir = project.projectDir
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2021 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.plugin
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask
import org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.configuration.KaptGenerateStubsConfig
import org.jetbrains.kotlin.gradle.tasks.configuration.KaptWithoutKotlincConfig
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
/** Plugin that can be used by third-party plugins to create Kotlin-specific DSL and tasks (compilation and KAPT) for JVM platform. */
abstract class KotlinBaseApiPlugin : KotlinBasePlugin(), KotlinJvmFactory {
private val logger = Logging.getLogger(KotlinBaseApiPlugin::class.java)
override val pluginVersion = getKotlinPluginVersion(logger)
private lateinit var myProject: Project
private val taskCreator = KotlinTasksProvider()
override fun apply(project: Project) {
super.apply(project)
myProject = project
setupAttributeMatchingStrategy(project, isKotlinGranularMetadata = false)
}
override fun addCompilerPluginDependency(dependency: Provider<Any>) {
myProject.dependencies.addProvider(
PLUGIN_CLASSPATH_CONFIGURATION_NAME,
dependency
)
}
override fun getCompilerPlugins(): FileCollection {
return myProject.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
}
override fun createKotlinJvmOptions(): KotlinJvmOptions {
return KotlinJvmOptionsImpl()
}
override val kotlinExtension: KotlinProjectExtension by lazy {
myProject.objects.newInstance(KotlinProjectExtension::class.java, myProject)
}
override val kaptExtension: KaptExtension by lazy {
myProject.objects.newInstance(KaptExtension::class.java)
}
override fun registerKotlinJvmCompileTask(taskName: String): TaskProvider<out KotlinJvmCompile> {
return taskCreator.registerKotlinJVMTask(
myProject, taskName, KotlinJvmOptionsImpl(), KotlinCompileConfig(myProject, kotlinExtension)
)
}
override fun registerKaptGenerateStubsTask(taskName: String): TaskProvider<out KaptGenerateStubs> {
val taskConfig = KaptGenerateStubsConfig(myProject, kotlinExtension, kaptExtension)
return myProject.registerTask(taskName, KaptGenerateStubsTask::class.java, emptyList()).also {
taskConfig.execute(it)
}
}
override fun registerKaptTask(taskName: String): TaskProvider<out Kapt> {
val taskConfiguration = KaptWithoutKotlincConfig(myProject, kaptExtension)
return myProject.registerTask(taskName, KaptWithoutKotlincTask::class.java, emptyList()).also {
taskConfiguration.execute(it)
}
}
}
@@ -933,7 +933,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
val configAction = KotlinCompileConfig(compilation)
configAction.configureTask { task ->
task.parentKotlinOptionsImpl.value(rootKotlinOptions).disallowChanges()
task.parentKotlinOptions.value(rootKotlinOptions).disallowChanges()
task.useModuleDetection.value(true).disallowChanges()
// store kotlin classes in separate directory. They will serve as class-path to java compiler
task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/$variantDataName"))
@@ -20,6 +20,7 @@ import org.gradle.api.GradleException
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.api.model.ObjectFactory
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.compilerRunner.registerCommonizerClasspathConfigurationIfNecessary
@@ -40,8 +41,9 @@ import org.jetbrains.kotlin.gradle.report.HttpReportService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry
import org.jetbrains.kotlin.gradle.tooling.registerBuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.utils.addGradlePluginMetadataAttributes
@@ -53,22 +55,15 @@ import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion
import kotlin.reflect.KClass
abstract class KotlinBasePluginWrapper : Plugin<Project> {
open val projectExtensionClass: KClass<out KotlinTopLevelExtension> get() = KotlinProjectExtension::class
abstract val pluginVariant: String
internal open fun kotlinSourceSetFactory(project: Project): NamedDomainObjectFactory<KotlinSourceSet> =
if (PropertiesProvider(project).experimentalKpmModelMapping)
FragmentMappedKotlinSourceSetFactory(project)
else DefaultKotlinSourceSetFactory(project)
/**
* Base Kotlin plugin that is responsible for creating basic build services, configurations,
* and other setup that is common for all Kotlin projects.
*/
abstract class KotlinBasePlugin : Plugin<Project> {
override fun apply(project: Project) {
val kotlinPluginVersion = project.getKotlinPluginVersion()
project.logger.info("Using Kotlin Gradle Plugin $pluginVariant variant")
val statisticsReporter = KotlinBuildStatsService.getOrCreateInstance(project)
statisticsReporter?.report(StringMetrics.KOTLIN_COMPILER_VERSION, kotlinPluginVersion)
@@ -86,6 +81,69 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
addGradlePluginMetadataAttributes(project)
}
KotlinGradleBuildServices.registerIfAbsent(project, kotlinPluginVersion).get()
KotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
BuildMetricsReporterService.registerIfAbsent(project)?.also {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
}
HttpReportService.registerIfAbsent(project, kotlinPluginVersion)?.also {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
}
}
private fun addKotlinCompilerConfiguration(project: Project) {
project
.configurations
.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME)
.defaultDependencies {
it.add(
project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:${project.getKotlinPluginVersion()}")
)
}
project
.tasks
.withType(AbstractKotlinCompileTool::class.java)
.configureEach { task ->
task.defaultCompilerClasspath.setFrom(
project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME)
)
}
}
protected fun setupAttributeMatchingStrategy(
project: Project,
isKotlinGranularMetadata: Boolean = project.isKotlinGranularMetadataEnabled
) = with(project.dependencies.attributesSchema) {
KotlinPlatformType.setupAttributesMatchingStrategy(this)
KotlinUsages.setupAttributesMatchingStrategy(this, isKotlinGranularMetadata)
KotlinJsCompilerAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema)
ProjectLocalConfigurations.setupAttributesMatchingStrategy(this)
CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this)
}
open fun whenBuildEvaluated(project: Project) {
}
}
abstract class KotlinBasePluginWrapper : KotlinBasePlugin() {
open val projectExtensionClass: KClass<out KotlinTopLevelExtension> get() = KotlinProjectExtension::class
abstract val pluginVariant: String
internal open fun kotlinSourceSetFactory(project: Project): NamedDomainObjectFactory<KotlinSourceSet> =
if (PropertiesProvider(project).experimentalKpmModelMapping)
FragmentMappedKotlinSourceSetFactory(project)
else DefaultKotlinSourceSetFactory(project)
override fun apply(project: Project) {
super.apply(project)
val kotlinPluginVersion = project.getKotlinPluginVersion()
project.logger.info("Using Kotlin Gradle Plugin $pluginVariant variant")
project.configurations.maybeCreate(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME).apply {
isVisible = false
isCanBeConsumed = false
@@ -96,17 +154,6 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
project.registerDefaultVariantImplementations()
KotlinGradleBuildServices.registerIfAbsent(project, kotlinPluginVersion).get()
KotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
BuildMetricsReporterService.registerIfAbsent(project)?.also {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
}
HttpReportService.registerIfAbsent(project, kotlinPluginVersion)
?.also { BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it) }
project.createKotlinExtension(projectExtensionClass).apply {
coreLibrariesVersion = kotlinPluginVersion
@@ -140,38 +187,8 @@ abstract class KotlinBasePluginWrapper : Plugin<Project> {
)
}
private fun addKotlinCompilerConfiguration(project: Project) {
project
.configurations
.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME)
.defaultDependencies {
it.add(
project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:${project.getKotlinPluginVersion()}")
)
}
project
.tasks
.withType(AbstractKotlinCompile::class.java)
.configureEach { task ->
task.defaultCompilerClasspath.setFrom(
project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME)
)
}
}
open fun whenBuildEvaluated(project: Project) {
}
internal open fun createTestRegistry(project: Project) = KotlinTestsRegistry(project)
private fun setupAttributeMatchingStrategy(project: Project) = with(project.dependencies.attributesSchema) {
KotlinPlatformType.setupAttributesMatchingStrategy(this)
KotlinUsages.setupAttributesMatchingStrategy(project, this)
KotlinJsCompilerAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema)
ProjectLocalConfigurations.setupAttributesMatchingStrategy(this)
CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this)
}
internal abstract fun getPlugin(
project: Project,
): Plugin<Project>
@@ -278,10 +295,12 @@ abstract class AbstractKotlinPm20PluginWrapper(
get() = KotlinPm20ProjectExtension::class
}
fun Project.getKotlinPluginVersion(): String {
fun Project.getKotlinPluginVersion() = getKotlinPluginVersion(project.logger)
fun getKotlinPluginVersion(logger: Logger): String {
if (!kotlinPluginVersionFromResources.isInitialized()) {
project.logger.kotlinDebug("Loading version information")
project.logger.kotlinDebug("Found project version [${kotlinPluginVersionFromResources.value}")
logger.kotlinDebug("Loading version information")
logger.kotlinDebug("Found project version [${kotlinPluginVersionFromResources.value}")
}
return kotlinPluginVersionFromResources.value
}
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.*
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.targets.metadata.isCompatibilityMetadataVariantEnabled
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
object KotlinUsages {
const val KOTLIN_API = "kotlin-api"
@@ -167,7 +166,7 @@ object KotlinUsages {
closestMatch(candidateValues.single { it?.name == name }!!)
}
internal fun setupAttributesMatchingStrategy(project: Project, attributesSchema: AttributesSchema) {
internal fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema, isKotlinGranularMetadata: Boolean) {
attributesSchema.attribute(USAGE_ATTRIBUTE) { strategy ->
strategy.compatibilityRules.add(KotlinJavaRuntimeJarsCompatibility::class.java)
strategy.disambiguationRules.add(KotlinUsagesDisambiguation::class.java)
@@ -175,7 +174,7 @@ object KotlinUsages {
strategy.compatibilityRules.add(KotlinCinteropCompatibility::class.java)
strategy.disambiguationRules.add(KotlinCinteropDisambiguation::class.java)
if (project.isKotlinGranularMetadataEnabled) {
if (isKotlinGranularMetadata) {
strategy.compatibilityRules.add(KotlinMetadataCompatibility::class.java)
strategy.disambiguationRules.add(KotlinMetadataDisambiguation::class.java)
}
@@ -37,7 +37,7 @@ import javax.inject.Inject
@CacheableTask
abstract class KotlinJsIrLink @Inject constructor(
private val objectFactory: ObjectFactory,
objectFactory: ObjectFactory,
workerExecutor: WorkerExecutor,
private val projectLayout: ProjectLayout
) : Kotlin2JsCompile(
@@ -17,9 +17,11 @@
package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Internal
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
class CompilerPluginOptions() : CompilerPluginConfig() {
@@ -51,44 +53,6 @@ class CompilerPluginOptions() : CompilerPluginConfig() {
return newOptions
}
@Input
fun getAsTaskInputArgs(): Map<String, String> {
val result = mutableMapOf<String, String>()
subpluginOptionsByPluginId.forEach { (id, subpluginOptions) ->
result += computeForSubpluginId(id, subpluginOptions)
}
return result
}
private fun computeForSubpluginId(subpluginId: String, subpluginOptions: List<SubpluginOption>): Map<String, String> {
// There might be several options with the same key. We group them together
// and add an index to the Gradle input property name to resolve possible duplication:
val result = mutableMapOf<String, String>()
val pluginOptionsGrouped = subpluginOptions.groupBy { it.key }
for ((optionKey, optionsGroup) in pluginOptionsGrouped) {
optionsGroup.forEachIndexed { index, option ->
val indexSuffix = if (optionsGroup.size > 1) ".$index" else ""
when (option) {
is InternalSubpluginOption -> return@forEachIndexed
is CompositeSubpluginOption -> {
val subpluginIdWithWrapperKey = "$subpluginId.$optionKey$indexSuffix"
result += computeForSubpluginId(subpluginIdWithWrapperKey, option.originalOptions)
}
is FilesSubpluginOption -> when (option.kind) {
FilesOptionKind.INTERNAL -> Unit
}.run { /* exhaustive when */ }
else -> {
result["$subpluginId." + option.key + indexSuffix] = option.value
}
}
}
}
return result
}
private fun copyOptionsFrom(options: CompilerPluginConfig) {
options.allOptions().forEach { entry ->
optionsByPluginId[entry.key] = entry.value.toMutableList()
@@ -96,7 +60,16 @@ class CompilerPluginOptions() : CompilerPluginConfig() {
}
}
internal fun ListProperty<CompilerPluginOptions>.toSingleCompilerPluginOptions(): CompilerPluginOptions {
val values = this.orNull ?: return CompilerPluginOptions()
return values.reduceOrNull(CompilerPluginOptions::plus) ?: CompilerPluginOptions()
internal fun ListProperty<out CompilerPluginConfig>.toSingleCompilerPluginOptions(): CompilerPluginOptions {
var res = CompilerPluginOptions()
this.get().forEach { res += it }
return res
}
internal fun Provider<List<SubpluginOption>>.toCompilerPluginOptions(): Provider<CompilerPluginOptions> {
return map {
val res = CompilerPluginOptions()
it.forEach { res.addPluginArgument(Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID, it) }
return@map res
}
}
@@ -133,7 +133,7 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
val jdkVersion = javaVersion.get()
// parentKotlinOptionsImpl is set from 'kotlin-android' plugin
val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptionsImpl.orNull)
val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptions.orNull)
.mapNotNull { (it as KotlinJvmOptionsImpl).jvmTarget }
if (appliedJvmTargets.isEmpty()) {
@@ -158,7 +158,7 @@ internal abstract class DefaultKotlinJavaToolchain @Inject constructor(
) {
kotlinCompileTaskProvider()?.let { task ->
// parentKotlinOptionsImpl is set from 'kotlin-android' plugin
val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptionsImpl.orNull)
val appliedJvmTargets = listOfNotNull(task.kotlinOptions, task.parentKotlinOptions.orNull)
.mapNotNull { (it as KotlinJvmOptionsImpl).jvmTarget }
if (appliedJvmTargets.isEmpty()) {
@@ -14,7 +14,6 @@ import org.gradle.api.file.*
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logger
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
@@ -77,7 +76,7 @@ const val USING_JS_IR_BACKEND_MESSAGE = "Using Kotlin/JS IR backend"
abstract class AbstractKotlinCompileTool<T : CommonToolArguments> @Inject constructor(
objectFactory: ObjectFactory
) : DefaultTask(),
PatternFilterable,
KotlinCompileTool,
CompilerArgumentAwareWithInput<T>,
TaskWithLocalState {
@@ -91,30 +90,17 @@ abstract class AbstractKotlinCompileTool<T : CommonToolArguments> @Inject constr
private val sourceFiles = objectFactory.fileCollection()
@get:InputFiles
@get:SkipWhenEmpty
@get:NormalizeLineEndings
@get:IgnoreEmptyDirectories
@get:PathSensitive(PathSensitivity.RELATIVE)
open val sources: FileCollection = objectFactory.fileCollection()
override val sources: FileCollection = objectFactory.fileCollection()
.from(
{ sourceFiles.asFileTree.matching(patternFilterable) }
)
/**
* Sets the source for this task.
* The given source object is evaluated as per [org.gradle.api.Project.files].
*/
open fun setSource(source: Any) {
sourceFiles.from(source)
override fun source(vararg sources: Any) {
sourceFiles.from(sources)
}
/**
* Sets the source for this task.
* The given source object is evaluated as per [org.gradle.api.Project.files].
*/
open fun setSource(vararg source: Any) {
sourceFiles.from(source)
override fun setSource(vararg sources: Any) {
sourceFiles.from(sources)
}
fun disallowSourceChanges() {
@@ -167,14 +153,6 @@ abstract class AbstractKotlinCompileTool<T : CommonToolArguments> @Inject constr
patternFilterable.exclude(excludeSpec)
}
@get:Classpath
@get:NormalizeLineEndings
@get:Incremental
abstract val libraries: ConfigurableFileCollection
@get:OutputDirectory
abstract val destinationDirectory: DirectoryProperty
@get:Internal
override val metrics: Property<BuildMetricsReporter> = project.objects
.property(BuildMetricsReporterImpl())
@@ -254,7 +232,8 @@ abstract class GradleCompileTaskProvider @Inject constructor(
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constructor(
objectFactory: ObjectFactory
) : AbstractKotlinCompileTool<T>(objectFactory),
CompileUsingKotlinDaemonWithNormalization {
CompileUsingKotlinDaemonWithNormalization,
BaseKotlinCompile {
init {
cacheOnlyIfEnabledForKotlin()
@@ -300,21 +279,10 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
internal fun reportingSettings() = buildMetricsReporterService.orNull?.parameters?.reportingSettings ?: ReportingSettings()
@get:Input
internal abstract val useModuleDetection: Property<Boolean>
@get:Internal
protected val multiModuleICSettings: MultiModuleICSettings
get() = MultiModuleICSettings(buildHistoryFile.get().asFile, useModuleDetection.get())
@get:NormalizeLineEndings
@get:Classpath
abstract val pluginClasspath: ConfigurableFileCollection
@get:Nested
internal abstract val pluginOptions: ListProperty<CompilerPluginOptions>
/**
* Plugin Data provided by [KpmCompilerPlugin]
*/
@@ -326,9 +294,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
@get:Internal
internal val javaOutputDir: DirectoryProperty = objectFactory.directoryProperty()
@get:Internal
internal abstract val sourceSetName: Property<String>
@get:InputFiles
@get:IgnoreEmptyDirectories
@get:NormalizeLineEndings
@@ -336,9 +301,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
@get:PathSensitive(PathSensitivity.RELATIVE)
internal val commonSourceSet: ConfigurableFileCollection = objectFactory.fileCollection()
@get:Input
internal abstract val moduleName: Property<String>
@get:Internal
val abiSnapshotFile
get() = taskBuildCacheableOutputDirectory.file(IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME)
@@ -352,9 +314,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
@get:Internal
internal val friendSourceSets = objectFactory.listProperty(String::class.java)
@get:Internal // takes part in the compiler arguments
abstract val friendPaths: ConfigurableFileCollection
private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
@get:Internal
@@ -496,9 +455,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
taskOutputsBackup: TaskOutputsBackup?
)
@get:Input
internal abstract val multiPlatformEnabled: Property<Boolean>
@get:Internal
internal val abstractKotlinCompileArgumentsContributor by lazy {
AbstractKotlinCompileArgumentsContributor(
@@ -533,7 +489,7 @@ class KotlinJvmCompilerArgumentsProvider
val compileClasspath: Iterable<File> = taskProvider.libraries
val destinationDir: File = taskProvider.destinationDirectory.get().asFile
internal val kotlinOptions: List<KotlinJvmOptionsImpl> = listOfNotNull(
taskProvider.parentKotlinOptionsImpl.orNull as? KotlinJvmOptionsImpl,
taskProvider.parentKotlinOptions.orNull as? KotlinJvmOptionsImpl,
taskProvider.kotlinOptions as KotlinJvmOptionsImpl
)
}
@@ -550,9 +506,6 @@ abstract class KotlinCompile @Inject constructor(
KotlinJvmCompile,
UsesKotlinJavaToolchain {
@get:Internal("Takes part in compiler args.")
internal abstract val parentKotlinOptionsImpl: Property<KotlinJvmOptions>
/** A package prefix that is used for locating Java sources in a directory structure with non-full-depth packages.
*
* Example: a Java source file with `package com.example.my.package` is located in directory `src/main/java/my/package`.
@@ -575,16 +528,16 @@ abstract class KotlinCompile @Inject constructor(
"Replaced with 'libraries' input",
replaceWith = ReplaceWith("libraries")
)
@Internal
fun getClasspath(): FileCollection = libraries
fun setClasspath(classpath: FileCollection) {
libraries.setFrom(classpath)
}
@Deprecated(
"Replaced with 'libraries' input",
replaceWith = ReplaceWith("libraries")
)
fun setClasspath(configuration: FileCollection) {
libraries.setFrom(configuration)
}
@Internal
fun getClasspath(): FileCollection = libraries
@get:Input
abstract val useKotlinAbiSnapshot: Property<Boolean>
@@ -881,17 +834,17 @@ abstract class KotlinCompile @Inject constructor(
}
// override setSource to track Java and script sources as well
override fun setSource(source: Any) {
javaSourceFiles.from(source)
scriptSourceFiles.from(source)
super.setSource(source)
override fun source(vararg sources: Any) {
javaSourceFiles.from(sources)
scriptSourceFiles.from(sources)
super.setSource(sources)
}
// override source to track Java and script sources as well
override fun setSource(vararg source: Any) {
javaSourceFiles.from(*source)
scriptSourceFiles.from(*source)
super.setSource(*source)
override fun setSource(vararg sources: Any) {
javaSourceFiles.from(*sources)
scriptSourceFiles.from(*sources)
super.setSource(*sources)
}
private fun getClasspathChanges(inputChanges: InputChanges): ClasspathChanges = when {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -12,6 +12,7 @@ import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
@@ -21,7 +22,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinO
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME
import java.util.concurrent.Callable
import org.jetbrains.kotlin.project.model.LanguageSettings
/**
* Configuration for the base compile task, [org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile].
@@ -31,39 +32,22 @@ import java.util.concurrent.Callable
*/
internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile<*>>(
project: Project,
private val ext: KotlinTopLevelExtension,
private val languageSettings: Provider<LanguageSettings>
) : TaskConfigAction<TASK>(project) {
constructor(compilation: KotlinCompilationData<*>) : this(compilation.project) {
val ext = compilation.project.topLevelExtension
init {
configureTaskProvider { taskProvider ->
project.runOnceAfterEvaluated("apply properties and language settings to ${taskProvider.name}") {
taskProvider.configure {
applyLanguageSettingsToKotlinOptions(
compilation.languageSettings, (it as org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>).kotlinOptions
languageSettings.get(), (it as org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>).kotlinOptions
)
}
}
}
configureTask { task ->
task.friendPaths.from({ compilation.friendPaths })
if (compilation is KotlinCompilation<*>) {
task.friendSourceSets.value(providers.provider { compilation.associateWithClosure.map { it.name } })
.disallowChanges()
task.pluginClasspath.from(compilation.project.configurations.getByName(compilation.pluginConfigurationName))
}
task.moduleName.set(providers.provider { compilation.moduleName })
task.sourceSetName.value(providers.provider { compilation.compilationPurpose })
task.multiPlatformEnabled.value(
providers.provider {
compilation.project.plugins.any {
it is KotlinPlatformPluginBase ||
it is AbstractKotlinMultiplatformPluginWrapper ||
it is AbstractKotlinPm20PluginWrapper
}
}
)
val propertiesProvider = project.kotlinPropertiesProvider
task.taskBuildCacheableOutputDirectory
@@ -85,7 +69,6 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
}
task.compilerExecutionStrategy.value(propertiesProvider.kotlinCompilerExecutionStrategy)
// Default values
task.incremental = false
task.useModuleDetection.convention(false)
}
@@ -96,6 +79,33 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
protected fun getClasspathSnapshotDir(task: TASK): Provider<Directory> =
getKotlinBuildDir(task).map { it.dir("classpath-snapshot") }
constructor(compilation: KotlinCompilationData<*>) : this(
compilation.project, compilation.project.topLevelExtension, compilation.project.provider { compilation.languageSettings }
) {
configureTask { task ->
task.friendPaths.from({ compilation.friendPaths })
if (compilation is KotlinCompilation<*>) {
task.friendSourceSets
.value(providers.provider { compilation.associateWithClosure.map { it.name } })
.disallowChanges()
task.pluginClasspath.from(
compilation.project.configurations.getByName(compilation.pluginConfigurationName)
)
}
task.moduleName.set(providers.provider { compilation.moduleName })
task.sourceSetName.value(providers.provider { compilation.compilationPurpose })
task.multiPlatformEnabled.value(
providers.provider {
compilation.project.plugins.any {
it is KotlinPlatformPluginBase ||
it is AbstractKotlinMultiplatformPluginWrapper ||
it is AbstractKotlinPm20PluginWrapper
}
}
)
}
}
}
internal abstract class TaskConfigAction<TASK : Task>(protected val project: Project) {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -15,7 +15,6 @@ import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.dsl.topLevelExtension
import org.jetbrains.kotlin.gradle.internal.*
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_SUBPLUGIN_ID
@@ -26,47 +25,26 @@ import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isInc
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformAction
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformLegacyAction
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinAndroidPluginWrapper
import org.jetbrains.kotlin.gradle.plugin.KaptExtension
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.toCompilerPluginOptions
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.gradle.utils.listProperty
import java.io.File
import java.util.concurrent.Callable
internal open class KaptConfigAction<TASK : KaptTask>(
internal open class KaptConfig<TASK : KaptTask>(
project: Project,
protected val ext: KaptExtension,
) : TaskConfigAction<TASK>(project) {
internal constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : this(kotlinCompileTask.project, ext) {
init {
configureTaskProvider { taskProvider ->
val kaptClasspathSnapshot = getKaptClasspathSnapshot(taskProvider)
taskProvider.configure { task ->
task.classpath.from(kotlinCompileTask.libraries)
task.compiledSources.from(
kotlinCompileTask.destinationDirectory,
Callable { kotlinCompileTask.javaOutputDir.takeIf { it.isPresent } })
.disallowChanges()
task.sourceSetName.value(kotlinCompileTask.sourceSetName).disallowChanges()
val kaptSources = objectFactory
.fileCollection()
.from(kotlinCompileTask.javaSources, task.stubsDir)
.asFileTree
.matching { it.include("**/*.java") }
.filter {
it.exists() &&
!isAncestor(task.destinationDir.get().asFile, it) &&
!isAncestor(task.classesDir.get().asFile, it)
}
task.source.from(kaptSources).disallowChanges()
task.verbose.set(KaptTask.queryKaptVerboseProperty(project))
task.compilerClasspath.from(providers.provider { kotlinCompileTask.defaultCompilerClasspath })
task.isIncremental = project.isIncrementalKapt()
task.useBuildCache = ext.useBuildCache
@@ -83,6 +61,29 @@ internal open class KaptConfigAction<TASK : KaptTask>(
}
}
internal constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : this(kotlinCompileTask.project, ext) {
configureTask { task ->
task.classpath.from(kotlinCompileTask.libraries)
task.compiledSources.from(
kotlinCompileTask.destinationDirectory,
Callable { kotlinCompileTask.javaOutputDir.takeIf { it.isPresent } })
.disallowChanges()
task.sourceSetName.value(kotlinCompileTask.sourceSetName).disallowChanges()
val kaptSources = objectFactory.fileCollection()
.from(kotlinCompileTask.javaSources, task.stubsDir)
.asFileTree
.matching { it.include("**/*.java") }
.filter {
it.exists() &&
!isAncestor(task.destinationDir.get().asFile, it) &&
!isAncestor(task.classesDir.get().asFile, it)
}
task.source.from(kaptSources).disallowChanges()
}
}
private fun getKaptClasspathSnapshot(taskProvider: TaskProvider<TASK>): FileCollection? {
return if (project.isIncrementalKapt()) {
maybeRegisterTransform(project)
@@ -173,12 +174,9 @@ private fun isAncestor(dir: File, file: File): Boolean {
}
}
internal class KaptWithoutKotlincConfigAction(kotlinCompileTask: KotlinCompile, ext: KaptExtension) :
KaptConfigAction<KaptWithoutKotlincTask>(kotlinCompileTask, ext) {
internal class KaptWithoutKotlincConfig : KaptConfig<KaptWithoutKotlincTask> {
init {
initKaptWorkersConfiguration(project.topLevelExtension)
configureTask { task ->
task.addJdkClassesToClasspath.set(
project.providers.provider {
@@ -194,7 +192,7 @@ internal class KaptWithoutKotlincConfigAction(kotlinCompileTask: KotlinCompile,
}
}
private fun initKaptWorkersConfiguration(kotlinExt: KotlinTopLevelExtension) {
constructor(kotlinCompileTask: KotlinCompile, ext: KaptExtension) : super(kotlinCompileTask, ext) {
project.configurations.findByName(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME)
?: project.configurations.create(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply {
dependencies.addAllLater(project.listProperty {
@@ -203,16 +201,33 @@ internal class KaptWithoutKotlincConfigAction(kotlinCompileTask: KotlinCompile,
project.dependencies.create(kaptDependency),
project.kotlinDependency(
"kotlin-stdlib",
kotlinExt.coreLibrariesVersion
project.topLevelExtension.coreLibrariesVersion
)
)
})
}
configureTask { task ->
task.addJdkClassesToClasspath.set(
project.providers.provider {
project.plugins.none { it is AbstractKotlinAndroidPluginWrapper }
}
)
task.kaptJars.from(project.configurations.getByName(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME))
}
}
constructor(project: Project, ext: KaptExtension) : super(project, ext) {
configureTask { task ->
val kotlinSourceDir = objectFactory.fileCollection().from(task.kotlinSourcesDestinationDir)
val nonAndroidDslOptions = getNonAndroidDslApOptions(ext, project, kotlinSourceDir, null, null)
task.kaptPluginOptions.add(nonAndroidDslOptions.toCompilerPluginOptions())
}
}
}
internal class KaptWithKotlincConfigAction(kotlinCompileTask: KotlinCompile, ext: KaptExtension) :
KaptConfigAction<KaptWithKotlincTask>(kotlinCompileTask, ext) {
internal class KaptWithKotlincConfig(kotlinCompileTask: KotlinCompile, ext: KaptExtension) :
KaptConfig<KaptWithKotlincTask>(kotlinCompileTask, ext) {
init {
configureTask { task ->
@@ -227,6 +242,7 @@ internal class KaptWithKotlincConfigAction(kotlinCompileTask: KotlinCompile, ext
task.kaptPluginOptions.add(incAptCacheOption)
}
task.compilerClasspath.from(providers.provider { kotlinCompileTask.defaultCompilerClasspath })
task.pluginClasspath.from(kotlinCompileTask.pluginClasspath)
task.additionalPluginOptionsAsInputs.value(kotlinCompileTask.pluginOptions).disallowChanges()
task.compileKotlinArgumentsContributor.set(providers.provider { kotlinCompileTask.compilerArgumentsContributor })
@@ -1,40 +1,58 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.tasks.configuration
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_SUBPLUGIN_ID
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.isIncludeCompileClasspath
import org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask
import org.jetbrains.kotlin.gradle.internal.KaptTask
import org.jetbrains.kotlin.gradle.internal.KotlinJvmCompilerArgumentsContributor
import org.jetbrains.kotlin.gradle.internal.buildKaptSubpluginOptions
import org.jetbrains.kotlin.gradle.plugin.KaptExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompilerArgumentsProvider
import java.util.concurrent.Callable
internal class KaptGenerateStubsConfig(compilation: KotlinCompilationData<*>, kotlinTaskProvider: TaskProvider<KotlinCompile>) :
BaseKotlinCompileConfig<KaptGenerateStubsTask>(compilation) {
internal class KaptGenerateStubsConfig : BaseKotlinCompileConfig<KaptGenerateStubsTask> {
private val kaptExtension: KaptExtension = project.extensions.getByType(KaptExtension::class.java)
init {
constructor(compilation: KotlinCompilationData<*>, kotlinTaskProvider: TaskProvider<KotlinCompile>) : super(compilation) {
configureFromExtension(project.extensions.getByType(KaptExtension::class.java))
configureTask { task ->
val kotlinCompileTask = kotlinTaskProvider.get()
task.useModuleDetection.value(kotlinCompileTask.useModuleDetection).disallowChanges()
task.moduleName.value(kotlinCompileTask.moduleName).disallowChanges()
task.libraries.from({ kotlinCompileTask.libraries })
task.compileKotlinArgumentsContributor.set(providers.provider { kotlinCompileTask.compilerArgumentsContributor })
task.pluginOptions.addAll(kotlinCompileTask.pluginOptions)
}
}
constructor(project: Project, ext: KotlinTopLevelExtension, kaptExtension: KaptExtension) : super(project, ext) {
configureFromExtension(kaptExtension)
configureTask { task ->
task.compileKotlinArgumentsContributor.set(
providers.provider {
KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(task))
}
)
}
}
private fun configureFromExtension(kaptExtension: KaptExtension) {
configureTask { task ->
task.verbose.set(KaptTask.queryKaptVerboseProperty(project))
task.pluginOptions.add(buildOptions(kaptExtension, task))
task.pluginOptions.add(buildOptions(task))
if (!isIncludeCompileClasspath()) {
if (!isIncludeCompileClasspath(kaptExtension)) {
task.onlyIf {
!(it as KaptGenerateStubsTask).kaptClasspath.isEmpty
}
@@ -42,9 +60,9 @@ internal class KaptGenerateStubsConfig(compilation: KotlinCompilationData<*>, ko
}
}
private fun isIncludeCompileClasspath() = kaptExtension.includeCompileClasspath ?: project.isIncludeCompileClasspath()
private fun isIncludeCompileClasspath(kaptExtension: KaptExtension) = kaptExtension.includeCompileClasspath ?: project.isIncludeCompileClasspath()
private fun buildOptions(task: KaptGenerateStubsTask): Provider<CompilerPluginOptions> {
private fun buildOptions(kaptExtension: KaptExtension, task: KaptGenerateStubsTask): Provider<CompilerPluginOptions> {
val javacOptions = project.provider { kaptExtension.getJavacOptions() }
return project.provider {
val compilerPluginOptions = CompilerPluginOptions()
@@ -56,7 +74,7 @@ internal class KaptGenerateStubsConfig(compilation: KotlinCompilationData<*>, ko
generatedSourcesDir = objectFactory.fileCollection().from(task.destinationDirectory.asFile),
generatedClassesDir = objectFactory.fileCollection().from(task.destinationDirectory.asFile),
incrementalDataDir = objectFactory.fileCollection().from(task.destinationDirectory.asFile),
includeCompileClasspath = isIncludeCompileClasspath(),
includeCompileClasspath = isIncludeCompileClasspath(kaptExtension),
kaptStubsDir = objectFactory.fileCollection().from(task.stubsDir.asFile)
).forEach {
compilerPluginOptions.addPluginArgument(KAPT_SUBPLUGIN_ID, it)
@@ -8,44 +8,29 @@ package org.jetbrains.kotlin.gradle.tasks.configuration
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Attribute
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.internal.transforms.ClasspathEntrySnapshotTransform
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.project.model.LanguageSettings
internal typealias KotlinCompileConfig = BaseKotlinCompileConfig<KotlinCompile>
internal open class BaseKotlinCompileConfig<TASK : KotlinCompile> : AbstractKotlinCompileConfig<TASK> {
@Suppress("ConvertSecondaryConstructorToPrimary")
constructor(compilation: KotlinCompilationData<*>) : super(compilation) {
val javaTaskProvider = when (compilation) {
is KotlinJvmCompilation -> compilation.compileJavaTaskProvider
is KotlinJvmAndroidCompilation -> compilation.compileJavaTaskProvider
is KotlinWithJavaCompilation<*> -> compilation.compileJavaTaskProvider
else -> null
}
init {
configureTaskProvider { taskProvider ->
val snapshotConfiguration = runAtConfigurationTime(taskProvider)
taskProvider.configure { task ->
javaTaskProvider?.let {
task.associatedJavaCompileTaskTargetCompatibility.value(javaTaskProvider.map { it.targetCompatibility })
task.associatedJavaCompileTaskSources.from(javaTaskProvider.map { it.source })
task.associatedJavaCompileTaskName.value(javaTaskProvider.name)
}
task.moduleName.value(providers.provider {
(compilation.kotlinOptions as? KotlinJvmOptions)?.moduleName ?: task.parentKotlinOptionsImpl.orNull?.moduleName
?: compilation.moduleName
})
task.incremental = propertiesProvider.incrementalJvm ?: true
if (propertiesProvider.useFir == true) {
@@ -71,6 +56,34 @@ internal open class BaseKotlinCompileConfig<TASK : KotlinCompile> : AbstractKotl
}
}
@Suppress("ConvertSecondaryConstructorToPrimary")
constructor(compilation: KotlinCompilationData<*>) : super(compilation) {
val javaTaskProvider = when (compilation) {
is KotlinJvmCompilation -> compilation.compileJavaTaskProvider
is KotlinJvmAndroidCompilation -> compilation.compileJavaTaskProvider
is KotlinWithJavaCompilation<*> -> compilation.compileJavaTaskProvider
else -> null
}
configureTaskProvider { taskProvider ->
taskProvider.configure { task ->
javaTaskProvider?.let {
task.associatedJavaCompileTaskTargetCompatibility.value(javaTaskProvider.map { it.targetCompatibility })
task.associatedJavaCompileTaskSources.from(javaTaskProvider.map { it.source })
task.associatedJavaCompileTaskName.value(javaTaskProvider.name)
}
task.moduleName.value(providers.provider {
(compilation.kotlinOptions as? KotlinJvmOptions)?.moduleName ?: task.parentKotlinOptions.orNull?.moduleName
?: compilation.moduleName
})
}
}
}
constructor(project: Project, ext: KotlinTopLevelExtension) : super(
project, ext, languageSettings = getDefaultLangSetting(project, ext)
)
companion object {
private const val TRANSFORMS_REGISTERED = "_kgp_internal_kotlin_compile_transforms_registered"
@@ -78,6 +91,14 @@ internal open class BaseKotlinCompileConfig<TASK : KotlinCompile> : AbstractKotl
private const val DIRECTORY_ARTIFACT_TYPE = "directory"
private const val JAR_ARTIFACT_TYPE = "jar"
const val CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE = "classpath-entry-snapshot"
private fun getDefaultLangSetting(project: Project, ext: KotlinTopLevelExtension): Provider<LanguageSettings> {
return project.provider {
DefaultLanguageSettingsBuilder().also {
it.freeCompilerArgsProvider = project.provider { listOfNotNull(ext.explicitApi?.toCompilerArg()) }
}
}
}
}
private fun registerTransformsOnce(project: Project) {
@@ -110,7 +131,7 @@ internal open class BaseKotlinCompileConfig<TASK : KotlinCompile> : AbstractKotl
return if (propertiesProvider.useClasspathSnapshot) {
registerTransformsOnce(project)
project.configurations.detachedConfiguration(
project.dependencies.create(objectFactory.fileCollection().from(taskProvider.get().libraries))
project.dependencies.create(objectFactory.fileCollection().from(project.provider { taskProvider.get().libraries }))
)
} else null
}
@@ -0,0 +1,168 @@
/*
* Copyright 2010-2022 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
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask
import org.jetbrains.kotlin.gradle.mpp.buildProject
import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinJvmFactory
import org.jetbrains.kotlin.gradle.tasks.Kapt
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import kotlin.test.assertEquals
class KaptApiTest {
@get:Rule
val tmpDir = TemporaryFolder()
private lateinit var project: ProjectInternal
private lateinit var plugin: KotlinJvmFactory
companion object {
private const val TASK_NAME = "kapt"
private const val GENERATE_STUBS = "kaptGenerateStubs"
}
@Before
fun setUpProject() {
project = buildProject {}
plugin = project.plugins.apply(KotlinBaseApiPlugin::class.java)
project.configurations.create(Kapt3GradleSubplugin.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME)
}
@Test
fun testTaskName() {
val task = configureKapt {}
assertEquals(TASK_NAME, task.name)
}
@Test
fun testSources() {
val sourcePath = tmpDir.newFolder().resolve("foo.kt").also {
it.createNewFile()
}
val task = configureKapt {
source.from(sourcePath)
}
assertEquals(setOf(sourcePath), task.source.files)
val sourcesDir = tmpDir.newFolder().also {
it.resolve("a.kt").createNewFile()
it.resolve("b.kt").createNewFile()
}
task.source.from(sourcesDir)
assertEquals(setOf(sourcePath, sourcesDir), task.source.files)
}
@Test
fun testKaptClasspath() {
val kaptClasspath = tmpDir.newFolder()
val externalClasspath = tmpDir.newFolder()
val configurationNames = listOf("a", "b", "c")
val task = configureKapt {
this.kaptClasspath.from(kaptClasspath)
kaptExternalClasspath.from(externalClasspath)
kaptClasspathConfigurationNames.set(configurationNames)
}
assertEquals(setOf(kaptClasspath), task.kaptClasspath.files)
assertEquals(setOf(externalClasspath), task.kaptExternalClasspath.files)
assertEquals(configurationNames, task.kaptClasspathConfigurationNames.get())
}
@Test
fun testKaptOutputs() {
val incAptCache = tmpDir.newFolder()
val classesDir = tmpDir.newFolder()
val destinationDir = tmpDir.newFolder()
val generatedKotlinSources = tmpDir.newFolder()
val task = configureKapt {
this.incAptCache.fileValue(incAptCache)
this.classesDir.fileValue(classesDir)
this.destinationDir.fileValue(destinationDir)
kotlinSourcesDestinationDir.fileValue(generatedKotlinSources)
}
assertEquals(incAptCache, task.incAptCache.get().asFile)
assertEquals(classesDir, task.classesDir.get().asFile)
assertEquals(destinationDir, task.destinationDir.get().asFile)
assertEquals(generatedKotlinSources, task.kotlinSourcesDestinationDir.get().asFile)
}
@Test
fun testClasspath() {
val compiledSources = tmpDir.newFolder()
val classpath = tmpDir.newFolder()
val includeCompileClasspath = false
val task = configureKapt {
this.compiledSources.from(compiledSources)
this.classpath.from(classpath)
this.includeCompileClasspath.set(includeCompileClasspath)
}
assertEquals(setOf(compiledSources), task.compiledSources.files)
assertEquals(setOf(classpath), task.classpath.files)
assertEquals(includeCompileClasspath, task.includeCompileClasspath.get())
}
@Test
fun testOptions() {
val sourceCompatibility = "11"
val addJdkClassesToMissingClasspathSnapshot = true
val task = configureKapt {
defaultJavaSourceCompatibility.set(sourceCompatibility)
addJdkClassesToClasspath.set(addJdkClassesToMissingClasspathSnapshot)
}
assertEquals(sourceCompatibility, task.defaultJavaSourceCompatibility.get())
assertEquals(addJdkClassesToMissingClasspathSnapshot, task.addJdkClassesToClasspath.get())
}
@Test
fun testKaptExtension() {
plugin.kaptExtension.useBuildCache = false
plugin.kaptExtension.includeCompileClasspath = true
val task = configureKapt {}
assertEquals(false, task.useBuildCache)
assertEquals(true, task.includeCompileClasspath.get())
}
@Test
fun testGenerateStubs() {
val task = plugin.registerKaptGenerateStubsTask(GENERATE_STUBS).get()
assertEquals(GENERATE_STUBS, task.name)
}
@Test
fun testGenerateStubsOptions() {
val stubsDir = tmpDir.newFolder()
val kaptClasspath = setOf(tmpDir.newFolder())
val task = plugin.registerKaptGenerateStubsTask(GENERATE_STUBS).let { provider ->
provider.configure {
it.stubsDir.fileValue(stubsDir)
it.kaptClasspath.from(kaptClasspath)
}
provider.get()
}
assertEquals(stubsDir, task.stubsDir.get().asFile)
assertEquals(kaptClasspath, task.kaptClasspath.files)
}
private fun configureKapt(configAction: Kapt.() -> Unit): KaptWithoutKotlincTask {
val provider = plugin.registerKaptTask(TASK_NAME)
provider.configure(configAction)
return provider.get() as KaptWithoutKotlincTask
}
}
@@ -0,0 +1,160 @@
/*
* Copyright 2010-2022 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
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.mpp.buildProject
import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinJvmFactory
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class KotlinCompileApiTest {
@get:Rule
val tmpDir = TemporaryFolder()
private lateinit var project: ProjectInternal
private lateinit var plugin: KotlinJvmFactory
private lateinit var taskApi: KotlinJvmCompile
private lateinit var taskImpl: KotlinCompile
companion object {
private const val TASK_NAME = "kotlinCompile"
}
@Before
fun setUpProject() {
project = buildProject {}
plugin = project.plugins.apply(KotlinBaseApiPlugin::class.java)
plugin.registerKotlinJvmCompileTask(TASK_NAME).configure { task ->
taskApi = task
}
taskImpl = project.tasks.getByName(TASK_NAME) as KotlinCompile
}
@Test
fun testTaskName() {
assertEquals(TASK_NAME, taskImpl.name)
}
@Test
fun testSources() {
val sourcePath = tmpDir.newFolder().resolve("foo.kt").also {
it.createNewFile()
}
taskApi.setSource(sourcePath)
assertEquals(setOf(sourcePath), taskImpl.sources.files)
val sourcesDir = tmpDir.newFolder().also {
it.resolve("a.kt").createNewFile()
it.resolve("b.kt").createNewFile()
}
taskApi.setSource(sourcePath, sourcesDir)
assertEquals(
setOf(sourcePath, sourcesDir.resolve("a.kt"), sourcesDir.resolve("b.kt")),
taskImpl.sources.files
)
}
@Test
fun testFriendPaths() {
val friendPath = tmpDir.newFolder()
taskApi.friendPaths.from(friendPath)
assertEquals(setOf(friendPath), taskImpl.friendPaths.files)
}
@Test
fun testLibraries() {
val classpathEntries = setOf(tmpDir.newFolder(), tmpDir.newFolder())
taskApi.libraries.from(classpathEntries)
assertEquals(classpathEntries, taskImpl.libraries.files)
}
@Test
fun testPluginClasspath() {
val pluginDependency = tmpDir.newFile()
plugin.addCompilerPluginDependency(project.provider { project.files(pluginDependency) })
val anotherCompilerPlugin = tmpDir.newFile()
taskApi.pluginClasspath.from(plugin.getCompilerPlugins(), anotherCompilerPlugin)
assertEquals(setOf(pluginDependency, anotherCompilerPlugin), taskImpl.pluginClasspath.files)
}
@Test
fun testModuleName() {
taskApi.moduleName.set("foo")
assertEquals("foo", taskImpl.moduleName.get())
}
@Test
fun testSourceSetName() {
taskApi.sourceSetName.set("sourceSetFoo")
assertEquals("sourceSetFoo", taskImpl.sourceSetName.get())
}
@Test
fun testTaskBuildDirectory() {
val taskBuildDir = tmpDir.newFolder()
taskApi.destinationDirectory.fileValue(taskBuildDir)
assertEquals(taskBuildDir, taskImpl.destinationDirectory.get().asFile)
}
@Test
fun testDestinationDirectory() {
val destinationDir = tmpDir.newFolder()
taskApi.destinationDirectory.fileValue(destinationDir)
assertEquals(destinationDir, taskImpl.destinationDirectory.get().asFile)
}
@Test
fun testMultiplatform() {
taskApi.multiPlatformEnabled.set(true)
assertEquals(true, taskImpl.multiPlatformEnabled.get())
taskApi.multiPlatformEnabled.set(false)
assertEquals(false, taskImpl.multiPlatformEnabled.get())
}
@Test
fun testModuleDetection() {
taskApi.useModuleDetection.set(true)
assertEquals(true, taskImpl.useModuleDetection.get())
taskApi.useModuleDetection.set(false)
assertEquals(false, taskImpl.useModuleDetection.get())
}
@Test
fun testParentKotlinOptions() {
val parentOptions = plugin.createKotlinJvmOptions()
parentOptions.moduleName = "foo"
parentOptions.javaParameters = true
parentOptions.languageVersion = "lang_version"
taskApi.parentKotlinOptions.set(parentOptions)
taskImpl.parentKotlinOptions.get().let {
assertEquals(parentOptions.moduleName, it.moduleName)
assertEquals(parentOptions.javaParameters, it.javaParameters)
assertEquals(parentOptions.languageVersion, it.languageVersion)
}
}
@Test
fun testTopLevelExtension() {
plugin.kotlinExtension.explicitApi = ExplicitApiMode.Strict
project.evaluate()
assertTrue(ExplicitApiMode.Strict.toCompilerArg() in taskImpl.kotlinOptions.freeCompilerArgs)
}
}