Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.gradle.internal.kapt.KaptWithoutKotlincTask.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2018 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.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.work.InputChanges
import org.gradle.workers.IsolationMode
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME
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.KotlinAndroidPluginWrapper
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.utils.PathUtil
import org.slf4j.LoggerFactory
import java.io.File
import java.io.Serializable
import java.net.URL
import java.net.URLClassLoader
import javax.inject.Inject
abstract class KaptWithoutKotlincTask @Inject constructor(
objectFactory: ObjectFactory,
private val providerFactory: ProviderFactory,
private val workerExecutor: WorkerExecutor
) : KaptTask(objectFactory) {
class Configurator(kotlinCompileTask: KotlinCompile): KaptTask.Configurator(kotlinCompileTask) {
override fun configure(task: KaptWithoutKotlincTask) {
super.configure(task)
task.addJdkClassesToClasspath.value(
task.project.providers.provider { task.project.plugins.none { it is KotlinAndroidPluginWrapper } }
).disallowChanges()
task.kaptJars.from(task.project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME)).disallowChanges()
}
}
@get:Classpath
abstract val kaptJars: ConfigurableFileCollection
@get:Input
var classLoadersCacheSize: Int = 0
@get:Input
var disableClassloaderCacheForProcessors: Set = emptySet()
@get:Input
var mapDiagnosticLocations: Boolean = false
@get:Input
lateinit var annotationProcessorFqNames: List
@get:Internal
internal val processorOptions = CompilerPluginOptions()
@get:Input
lateinit var javacOptions: Map
@get:Input
internal abstract val addJdkClassesToClasspath: Property
@get:Internal
internal val projectDir = project.projectDir
private fun getAnnotationProcessorOptions(): Map {
val options = processorOptions.subpluginOptionsByPluginId[Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID] ?: return emptyMap()
val result = mutableMapOf()
for (option in options) {
result[option.key] = option.value
}
annotationProcessorOptionProviders.forEach { providers ->
(providers as List<*>).forEach { provider ->
(provider as CommandLineArgumentProvider).asArguments().forEach { argument ->
result[argument.removePrefix("-A")] = ""
}
}
}
return result
}
@TaskAction
fun compile(inputChanges: InputChanges) {
logger.info("Running kapt annotation processing using the Gradle Worker API")
checkAnnotationProcessorClasspath()
val incrementalChanges = getIncrementalChanges(inputChanges)
val (changedFiles, classpathChanges) = when (incrementalChanges) {
is KaptIncrementalChanges.Unknown -> Pair(emptyList(), emptyList())
is KaptIncrementalChanges.Known -> Pair(incrementalChanges.changedSources.toList(), incrementalChanges.changedClasspathJvmNames)
}
val compileClasspath = classpath.files.toMutableList()
if (addJdkClassesToClasspath.get()) {
compileClasspath.addAll(0, PathUtil.getJdkClassesRootsFromCurrentJre())
}
val kaptFlagsForWorker = mutableSetOf().apply {
if (verbose.get()) add("VERBOSE")
if (mapDiagnosticLocations) add("MAP_DIAGNOSTIC_LOCATIONS")
if (includeCompileClasspath.get()) add("INCLUDE_COMPILE_CLASSPATH")
if (incrementalChanges is KaptIncrementalChanges.Known) add("INCREMENTAL_APT")
}
val optionsForWorker = KaptOptionsForWorker(
projectDir,
compileClasspath,
source.files.toList(),
changedFiles,
compiledSources.toList(),
incAptCache.orNull?.asFile,
classpathChanges.toList(),
destinationDir,
classesDir,
stubsDir.asFile.get(),
kaptClasspath.files.toList(),
kaptExternalClasspath.files.toList(),
annotationProcessorFqNames,
getAnnotationProcessorOptions(),
javacOptions,
kaptFlagsForWorker,
disableClassloaderCacheForProcessors
)
// Skip annotation processing if no annotation processors were provided.
if (annotationProcessorFqNames.isEmpty() && kaptClasspath.isEmpty()) {
logger.info("No annotation processors provided. Skip KAPT processing.")
return
}
val kaptClasspath = kaptJars
val isolationMode = getWorkerIsolationMode()
logger.info("Using workers $isolationMode isolation mode to run kapt")
val toolsJarURLSpec = defaultKotlinJavaToolchain.get()
.jdkToolsJar.orNull?.toURI()?.toURL()?.toString().orEmpty()
submitWork(
isolationMode,
optionsForWorker,
toolsJarURLSpec,
kaptClasspath
)
}
private fun getWorkerIsolationMode(): IsolationMode {
val toolchainProvider = defaultKotlinJavaToolchain.get()
val gradleJvm = toolchainProvider.currentJvm.get()
// Ensuring Gradle build JDK is set to kotlin toolchain by also comparing javaExecutable paths,
// as user may set JDK with same major Java version, but from different vendor
val isRunningOnGradleJvm = gradleJvm.javaVersion == toolchainProvider.javaVersion.get() &&
gradleJvm.javaExecutable.absolutePath == toolchainProvider.javaExecutable.get().asFile.absolutePath
val isolationModeStr = getValue("kapt.workers.isolation")?.toLowerCase()
return when {
(isolationModeStr == null || isolationModeStr == "none") && isRunningOnGradleJvm -> IsolationMode.NONE
else -> {
if (isolationModeStr == "none") {
logger.warn("Using non-default Kotlin java toolchain - 'kapt.workers.isolation == none' property is ignored!")
}
IsolationMode.PROCESS
}
}
}
private fun submitWork(
isolationMode: IsolationMode,
optionsForWorker: KaptOptionsForWorker,
toolsJarURLSpec: String,
kaptClasspath: FileCollection
) {
val workQueue = when (isolationMode) {
IsolationMode.PROCESS -> workerExecutor.processIsolation {
if (getValue("kapt.workers.log.classloading") == "true") {
// for tests
it.forkOptions.jvmArgs("-verbose:class")
}
it.forkOptions.executable = defaultKotlinJavaToolchain.get()
.javaExecutable
.asFile.get()
.absolutePath
logger.info("Kapt worker classpath: ${it.classpath}")
}
IsolationMode.NONE -> workerExecutor.noIsolation()
IsolationMode.AUTO, IsolationMode.CLASSLOADER -> throw UnsupportedOperationException(
"Kapt worker compilation does not support class loader isolation. " +
"Please use either \"none\" or \"process\" in gradle.properties."
)
}
workQueue.submit(KaptExecutionWorkAction::class.java) {
it.workerOptions.set(optionsForWorker)
it.toolsJarURLSpec.set(toolsJarURLSpec)
it.kaptClasspath.setFrom(kaptClasspath)
it.classloadersCacheSize.set(classLoadersCacheSize)
}
}
internal fun getValue(propertyName: String): String? =
if (isGradleVersionAtLeast(6, 5)) {
providerFactory.gradleProperty(propertyName).forUseAtConfigurationTime().orNull
} else {
project.findProperty(propertyName) as String?
}
internal interface KaptWorkParameters : WorkParameters {
val workerOptions: Property
val toolsJarURLSpec: Property
val kaptClasspath: ConfigurableFileCollection
val classloadersCacheSize: Property
}
internal abstract class KaptExecutionWorkAction : WorkAction {
override fun execute() {
KaptExecution(
parameters.workerOptions.get(),
parameters.toolsJarURLSpec.get(),
parameters.kaptClasspath.toList(),
parameters.classloadersCacheSize.get()
).run()
}
}
}
private class KaptExecution @Inject constructor(
val optionsForWorker: KaptOptionsForWorker,
val toolsJarURLSpec: String,
val kaptClasspath: List,
val classloadersCacheSize: Int
) : Runnable {
private companion object {
private const val JAVAC_CONTEXT_CLASS = "com.sun.tools.javac.util.Context"
private fun kaptClass(classLoader: ClassLoader) = Class.forName("org.jetbrains.kotlin.kapt3.base.Kapt", true, classLoader)
private var classLoadersCache: ClassLoadersCache? = null
private var cachedKaptClassLoader: ClassLoader? = null
}
private val logger = LoggerFactory.getLogger(KaptExecution::class.java)
override fun run(): Unit = with(optionsForWorker) {
val kaptClasspathUrls = kaptClasspath.map { it.toURI().toURL() }.toTypedArray()
val rootClassLoader = findRootClassLoader()
val kaptClassLoader = cachedKaptClassLoader ?: run {
val classLoaderWithToolsJar = if (toolsJarURLSpec.isNotEmpty() && !javacIsAlreadyHere()) {
URLClassLoader(arrayOf(URL(toolsJarURLSpec)), rootClassLoader)
} else {
rootClassLoader
}
val result = URLClassLoader(kaptClasspathUrls, classLoaderWithToolsJar)
cachedKaptClassLoader = result
result
}
if (classLoadersCache == null && classloadersCacheSize > 0) {
logger.info("Initializing KAPT classloaders cache with size = $classloadersCacheSize")
classLoadersCache = ClassLoadersCache(classloadersCacheSize, kaptClassLoader)
}
val kaptMethod = kaptClass(kaptClassLoader).declaredMethods.single { it.name == "kapt" }
kaptMethod.invoke(null, createKaptOptions(kaptClassLoader))
}
private fun javacIsAlreadyHere(): Boolean {
return try {
Class.forName(JAVAC_CONTEXT_CLASS, false, KaptExecution::class.java.classLoader) != null
} catch (e: Throwable) {
false
}
}
private fun createKaptOptions(classLoader: ClassLoader) = with(optionsForWorker) {
val flags = kaptClass(classLoader).declaredMethods.single { it.name == "kaptFlags" }.invoke(null, flags)
val mode = Class.forName("org.jetbrains.kotlin.base.kapt3.AptMode", true, classLoader)
.enumConstants.single { (it as Enum<*>).name == "APT_ONLY" }
val detectMemoryLeaksMode = Class.forName("org.jetbrains.kotlin.base.kapt3.DetectMemoryLeaksMode", true, classLoader)
.enumConstants.single { (it as Enum<*>).name == "NONE" }
//in case cache was enabled and then disabled
//or disabled for some modules
val processingClassLoader =
if (classloadersCacheSize > 0) {
classLoadersCache!!.getForSplitPaths(processingClasspath - processingExternalClasspath, processingExternalClasspath)
} else {
null
}
Class.forName("org.jetbrains.kotlin.base.kapt3.KaptOptions", true, classLoader).constructors.single().newInstance(
projectBaseDir,
compileClasspath,
javaSourceRoots,
changedFiles,
compiledSources,
incAptCache,
classpathChanges,
sourcesOutputDir,
classesOutputDir,
stubsOutputDir,
stubsOutputDir, // sic!
processingClasspath,
processors,
processingOptions,
javacOptions,
flags,
mode,
detectMemoryLeaksMode,
processingClassLoader,
disableClassloaderCacheForProcessors,
/*processorsPerfReportFile=*/null
)
}
private fun findRootClassLoader(): ClassLoader = KaptExecution::class.java.classLoader.rootOrSelf()
}
internal data class KaptOptionsForWorker(
val projectBaseDir: File,
val compileClasspath: List,
val javaSourceRoots: List,
val changedFiles: List,
val compiledSources: List,
val incAptCache: File?,
val classpathChanges: List,
val sourcesOutputDir: File,
val classesOutputDir: File,
val stubsOutputDir: File,
val processingClasspath: List,
val processingExternalClasspath: List,
val processors: List,
val processingOptions: Map,
val javacOptions: Map,
val flags: Set,
val disableClassloaderCacheForProcessors: Set
) : Serializable