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.
package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.BasePlugin
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant
import com.android.builder.model.SourceProvider
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.MutableVersionConstraint
import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
import org.gradle.api.artifacts.maven.MavenResolver
import org.gradle.api.attributes.Usage
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.plugins.InvalidPluginException
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.plugins.MavenPluginConvention
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.tasks.Jar
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.internal.KaptVariantData
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.logging.kotlinWarn
import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.utils.*
import java.io.File
import java.net.URL
import java.util.concurrent.Callable
import java.util.jar.Manifest
const val PLUGIN_CLASSPATH_CONFIGURATION_NAME = "kotlinCompilerPluginClasspath"
const val NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME = "kotlinNativeCompilerPluginClasspath"
internal const val COMPILER_CLASSPATH_CONFIGURATION_NAME = "kotlinCompilerClasspath"
val KOTLIN_DSL_NAME = "kotlin"
val KOTLIN_JS_DSL_NAME = "kotlin2js"
val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions"
internal abstract class KotlinSourceSetProcessor>(
val project: Project,
val tasksProvider: KotlinTasksProvider,
val taskDescription: String,
val kotlinCompilation: AbstractKotlinCompilation<*>
) {
protected abstract fun doTargetSpecificProcessing()
protected val logger = Logging.getLogger(this.javaClass)!!
protected val sourceSetName: String = kotlinCompilation.compilationName
protected val kotlinTask: TaskProvider = registerKotlinCompileTask()
protected val javaSourceSet: SourceSet?
get() =
(kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet
?: kotlinCompilation.target.let {
if (it is KotlinJvmTarget && it.withJavaEnabled)
project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets.maybeCreate(kotlinCompilation.name)
else null
}
private val defaultKotlinDestinationDir: File
get() {
val kotlinExt = project.kotlinExtension
val targetSubDirectory =
if (kotlinExt is KotlinSingleJavaTargetExtension)
"" // In single-target projects, don't add the target name part to this path
else
kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
return File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}")
}
private fun registerKotlinCompileTask(): TaskProvider {
val name = kotlinCompilation.compileKotlinTaskName
logger.kotlinDebug("Creating kotlin compile task $name")
KotlinCompileTaskData.register(name, kotlinCompilation).apply {
destinationDir.set(project.provider { defaultKotlinDestinationDir })
}
return doRegisterTask(project, name) {
it.description = taskDescription
it.mapClasspath { kotlinCompilation.compileDependencyFiles }
kotlinCompilation.output.addClassesDir { project.files(kotlinTask.get().destinationDir).builtBy(kotlinTask.get()) }
}
}
open fun run() {
addKotlinDirectoriesToJavaSourceSet()
doTargetSpecificProcessing()
if (kotlinCompilation is KotlinWithJavaCompilation<*>) {
createAdditionalClassesTaskForIdeRunner()
}
}
private fun addKotlinDirectoriesToJavaSourceSet() {
val java = javaSourceSet ?: return
// Try to avoid duplicate Java sources in allSource; run lazily to allow changing the directory set:
val kotlinSrcDirsToAdd = Callable {
kotlinCompilation.kotlinSourceSets.map { filterOutJavaSrcDirsIfPossible(it.kotlin) }
}
java.allJava.srcDirs(kotlinSrcDirsToAdd)
java.allSource.srcDirs(kotlinSrcDirsToAdd)
}
private fun filterOutJavaSrcDirsIfPossible(sourceDirectorySet: SourceDirectorySet): FileCollection {
val java = javaSourceSet ?: return sourceDirectorySet
// If the API used below is not available, fall back to not filtering the Java sources.
if (SourceDirectorySet::class.java.methods.none { it.name == "getSourceDirectories" }) {
return sourceDirectorySet
}
fun getSourceDirectories(sourceDirectorySet: SourceDirectorySet): FileCollection {
val method = SourceDirectorySet::class.java.getMethod("getSourceDirectories")
return method(sourceDirectorySet) as FileCollection
}
// Build a lazily-resolved file collection that filters out Java sources from sources of this sourceDirectorySet
return getSourceDirectories(sourceDirectorySet).minus(getSourceDirectories(java.java))
}
private fun createAdditionalClassesTaskForIdeRunner() {
open class IDEClassesTask : DefaultTask()
// Workaround: as per KT-26641, when there's a Kotlin compilation with a Java source set, we create another task
// that has a name composed as 'Classes`, where the IDE module name is the default source set name:
val expectedClassesTaskName = "${kotlinCompilation.defaultSourceSetName}Classes"
project.tasks.run {
var shouldCreateTask = false
try {
named(expectedClassesTaskName)
} catch (e: UnknownDomainObjectException) {
shouldCreateTask = true
}
if (shouldCreateTask) {
project.registerTask(expectedClassesTaskName, IDEClassesTask::class.java) {
it.dependsOn(getByName(kotlinCompilation.compileAllTaskName))
}
}
}
}
protected abstract fun doRegisterTask(project: Project, taskName: String, configureAction: (T) -> (Unit)): TaskProvider
}
internal class Kotlin2JvmSourceSetProcessor(
project: Project,
tasksProvider: KotlinTasksProvider,
kotlinCompilation: AbstractKotlinCompilation<*>,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor(
project, tasksProvider, "Compiles the $kotlinCompilation.", kotlinCompilation
) {
override fun doRegisterTask(
project: Project,
taskName: String,
configureAction: (KotlinCompile) -> (Unit)
): TaskProvider =
tasksProvider.registerKotlinJVMTask(project, taskName, kotlinCompilation, configureAction)
override fun doTargetSpecificProcessing() {
ifKaptEnabled(project) {
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, kotlinCompilation.compilationName)
}
ScriptingGradleSubplugin.configureForSourceSet(project, kotlinCompilation.compilationName)
// TODO: here, the tasks are always triggered for configuration; once the subplugins are able to work without task instances,
// ensure that task configuration is properly avoided here;
project.whenEvaluated {
val kotlinTaskInstance = kotlinTask.get()
val javaTask = javaSourceSet?.let { project.tasks.findByName(it.compileJavaTaskName) as JavaCompile }
val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTaskInstance, javaTask, null, null, kotlinCompilation
)
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
.forEach { plugin -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> plugin.source(sourceSet.kotlin) } }
javaTask?.let { configureJavaTask(kotlinTaskInstance, it, logger) }
if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
registerKotlinOutputForJavaLibrary(kotlinTaskInstance.destinationDir, kotlinTaskInstance)
}
}
}
private fun registerKotlinOutputForJavaLibrary(outputDir: File, taskDependency: Task): Boolean {
val configuration = project.configurations.getByName("apiElements")
checkedReflection({
val getOutgoing = configuration.javaClass.getMethod("getOutgoing")
val outgoing = getOutgoing(configuration)
val getVariants = outgoing.javaClass.getMethod("getVariants")
val variants = getVariants(outgoing) as NamedDomainObjectCollection<*>
val variant = variants.getByName("classes")
val artifactMethod = variant.javaClass.getMethod("artifact", Any::class.java)
val artifactMap = mapOf(
"file" to outputDir,
"type" to "java-classes-directory",
"builtBy" to taskDependency
)
artifactMethod(variant, artifactMap)
return true
}, { reflectException ->
logger.kotlinWarn("Could not register Kotlin output of source set $sourceSetName for java-library: $reflectException")
return false
})
}
}
internal fun KotlinCompilationOutput.addClassesDir(classesDirProvider: () -> FileCollection) {
classesDirs.from(Callable { classesDirProvider() })
}
internal class Kotlin2JsSourceSetProcessor(
project: Project,
tasksProvider: KotlinTasksProvider,
kotlinCompilation: AbstractKotlinCompilation<*>,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor(
project, tasksProvider, taskDescription = "Compiles the Kotlin sources in $kotlinCompilation to JavaScript.",
kotlinCompilation = kotlinCompilation
) {
override fun doRegisterTask(
project: Project,
taskName: String,
configureAction: (Kotlin2JsCompile) -> (Unit)
): TaskProvider =
tasksProvider.registerKotlinJSTask(project, taskName, kotlinCompilation, configureAction)
override fun doTargetSpecificProcessing() {
project.tasks.named(kotlinCompilation.compileAllTaskName).configure {
it.dependsOn(kotlinTask)
}
registerCleanSourceMapTask()
if (kotlinCompilation is KotlinWithJavaCompilation<*>) {
kotlinCompilation.javaSourceSet.clearJavaSrcDirs()
}
// outputFile can be set later during the configuration phase, get it only after the phase:
project.runOnceAfterEvaluated("Kotlin2JsSourceSetProcessor.doTargetSpecificProcessing", kotlinTask) {
val kotlinTaskInstance = kotlinTask.get()
kotlinTaskInstance.kotlinOptions.outputFile = kotlinTaskInstance.outputFile.absolutePath
val outputDir = kotlinTaskInstance.outputFile.parentFile
val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTaskInstance, null, null, null, kotlinCompilation
)
if (outputDir.isParentOf(project.rootDir))
throw InvalidUserDataException(
"The output directory '$outputDir' (defined by outputFile of $kotlinTaskInstance) contains or " +
"matches the project root directory '${project.rootDir}'.\n" +
"Gradle will not be able to build the project because of the root directory lock.\n" +
"To fix this, consider using the default outputFile location instead of providing it explicitly."
)
kotlinTaskInstance.destinationDir = outputDir
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
.forEach { task -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } }
}
}
private fun registerCleanSourceMapTask() {
val taskName = kotlinCompilation.composeName("clean", "sourceMap")
registerTask(project, taskName, Delete::class.java) {
it.onlyIf { kotlinTask.get().kotlinOptions.sourceMap }
it.delete(object : Closure(this) {
override fun call(): String? = (kotlinTask.get().property("outputFile") as File).canonicalPath + ".map"
})
}
project.tasks.findByName("clean")?.dependsOn(taskName)
}
}
internal class KotlinCommonSourceSetProcessor(
project: Project,
compilation: AbstractKotlinCompilation<*>,
tasksProvider: KotlinTasksProvider,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor(
project, tasksProvider, taskDescription = "Compiles the kotlin sources in $compilation to Metadata.",
kotlinCompilation = compilation
) {
override fun doTargetSpecificProcessing() {
project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask)
// can be missing (e.g. in case of tests)
if (kotlinCompilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
project.tasks.findByName(kotlinCompilation.target.artifactsTaskName)?.dependsOn(kotlinTask)
}
project.runOnceAfterEvaluated("KotlinCommonSourceSetProcessor.doTargetSpecificProcessing", kotlinTask) {
val kotlinTaskInstance = kotlinTask.get()
val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTaskInstance, null, null, null, kotlinCompilation
)
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
.forEach { kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> it.source(sourceSet.kotlin) } }
}
}
// protected abstract fun doRegisterTask(project: Project, taskName: String, configureAction: (T) -> (Unit)): TaskHolder
override fun doRegisterTask(
project: Project,
taskName: String,
configureAction: (KotlinCompileCommon) -> (Unit)
): TaskProvider =
tasksProvider.registerKotlinCommonTask(project, taskName, kotlinCompilation, configureAction)
}
internal abstract class AbstractKotlinPlugin(
val tasksProvider: KotlinTasksProvider,
protected val kotlinPluginVersion: String,
val registry: ToolingModelBuilderRegistry
) : Plugin {
internal abstract fun buildSourceSetProcessor(
project: Project,
compilation: AbstractKotlinCompilation<*>,
kotlinPluginVersion: String
): KotlinSourceSetProcessor<*>
override fun apply(project: Project) {
project.plugins.apply(JavaPlugin::class.java)
val target = (project.kotlinExtension as KotlinSingleJavaTargetExtension).target
configureTarget(
target,
{ compilation -> buildSourceSetProcessor(project, compilation, kotlinPluginVersion) }
)
applyUserDefinedAttributes(target)
rewriteMppDependenciesInPom(target)
configureProjectGlobalSettings(project, kotlinPluginVersion)
registry.register(KotlinModelBuilder(kotlinPluginVersion, null))
project.components.addAll(target.components)
}
private fun rewriteMppDependenciesInPom(target: AbstractKotlinTarget) {
val project = target.project
fun shouldRewritePoms(): Boolean =
PropertiesProvider(project).keepMppDependenciesIntactInPoms != true
project.pluginManager.withPlugin("maven-publish") {
project.extensions.configure(PublishingExtension::class.java) { publishing ->
publishing.publications.withType(MavenPublication::class.java).all { publication ->
publication.pom.withXml { xml ->
if (shouldRewritePoms())
project.rewritePomMppDependenciesToActualTargetModules(xml, target.kotlinComponents.single())
}
}
}
}
project.pluginManager.withPlugin("maven") {
project.tasks.withType(Upload::class.java).all { uploadTask ->
uploadTask.repositories.withType(MavenResolver::class.java).all { mavenResolver ->
mavenResolver.pom.withXml { xml ->
if (shouldRewritePoms())
project.rewritePomMppDependenciesToActualTargetModules(xml, target.kotlinComponents.single())
}
}
}
// Setup conf2ScopeMappings so that the API dependencies are wriiten with the compile scope in the POMs in case of 'java' plugin
project.convention.getPlugin(MavenPluginConvention::class.java)
.conf2ScopeMappings.addMapping(0, project.configurations.getByName("api"), Conf2ScopeMappingContainer.COMPILE)
}
}
companion object {
fun configureProjectGlobalSettings(project: Project, kotlinPluginVersion: String) {
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
configureClassInspectionForIC(project)
}
fun configureTarget(
target: KotlinWithJavaTarget<*>,
buildSourceSetProcessor: (AbstractKotlinCompilation<*>) -> KotlinSourceSetProcessor<*>
) {
setUpJavaSourceSets(target)
configureSourceSetDefaults(target, buildSourceSetProcessor)
configureAttributes(target)
}
private fun configureClassInspectionForIC(project: Project) {
val classesTask = project.tasks.findByName(JavaPlugin.CLASSES_TASK_NAME)
val jarTask = project.tasks.findByName(JavaPlugin.JAR_TASK_NAME)
if (classesTask == null || jarTask !is Jar) {
project.logger.info(
"Could not configure class inspection task " +
"(classes task = ${classesTask?.javaClass?.canonicalName}, " +
"jar task = ${classesTask?.javaClass?.canonicalName}"
)
return
}
val inspectTask =
registerTask(project, "inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java) {
it.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME
it.jarTask = jarTask
it.dependsOn(classesTask)
}
jarTask.dependsOn(inspectTask)
}
internal fun setUpJavaSourceSets(
kotlinTarget: KotlinTarget,
duplicateJavaSourceSetsAsKotlinSourceSets: Boolean = true
) {
val project = kotlinTarget.project
val javaSourceSets = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
val kotlinSourceSetDslName = when (kotlinTarget.platformType) {
KotlinPlatformType.js -> KOTLIN_JS_DSL_NAME
else -> KOTLIN_DSL_NAME
}
// Workaround for indirect mutual recursion between the two `all { ... }` handlers:
val compilationsUnderConstruction = mutableMapOf>()
javaSourceSets.all { javaSourceSet ->
val kotlinCompilation =
compilationsUnderConstruction[javaSourceSet.name] ?: kotlinTarget.compilations.maybeCreate(javaSourceSet.name)
(kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet = javaSourceSet
if (duplicateJavaSourceSetsAsKotlinSourceSets) {
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name)
kotlinSourceSet.kotlin.source(javaSourceSet.java)
kotlinCompilation.source(kotlinSourceSet)
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinSourceSet)
} else {
javaSourceSet.addConvention(kotlinSourceSetDslName, kotlinCompilation.defaultSourceSet)
}
}
kotlinTarget.compilations.all { kotlinCompilation ->
val sourceSetName = kotlinCompilation.name
compilationsUnderConstruction[sourceSetName] = kotlinCompilation
(kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet = javaSourceSets.maybeCreate(sourceSetName)
// Another Kotlin source set following the other convention, named according to the compilation, not the Java source set:
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.defaultSourceSetName)
kotlinCompilation.source(kotlinSourceSet)
}
kotlinTarget.compilations.run {
getByName(KotlinCompilation.TEST_COMPILATION_NAME).associateWith(getByName(KotlinCompilation.MAIN_COMPILATION_NAME))
}
// Since the 'java' plugin (as opposed to 'java-library') doesn't known anything about the 'api' configurations,
// add the API dependencies of the main compilation directly to the 'apiElements' configuration, so that the 'api' dependencies
// are properly published with the 'compile' scope (KT-28355):
project.whenEvaluated {
project.configurations.apply {
val apiElementsConfiguration = getByName(kotlinTarget.apiElementsConfigurationName)
val mainCompilation = kotlinTarget.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val compilationApiConfiguration = getByName(mainCompilation.apiConfigurationName)
apiElementsConfiguration.extendsFrom(compilationApiConfiguration)
}
}
}
private fun configureAttributes(
kotlinTarget: KotlinWithJavaTarget<*>
) {
val project = kotlinTarget.project
// Setup the consuming configurations:
project.dependencies.attributesSchema.attribute(KotlinPlatformType.attribute)
kotlinTarget.compilations.all { compilation ->
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation)
}
project.configurations.getByName("default").apply {
setupAsLocalTargetSpecificConfigurationIfSupported(kotlinTarget)
}
// Setup the published configurations:
// Don't set the attributes for common module; otherwise their 'common' platform won't be compatible with the one in
// platform-specific modules
if (kotlinTarget.platformType != KotlinPlatformType.common) {
project.configurations.getByName(kotlinTarget.apiElementsConfigurationName).run {
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(kotlinTarget))
setupAsPublicConfigurationIfSupported(kotlinTarget)
usesPlatformOf(kotlinTarget)
}
project.configurations.getByName(kotlinTarget.runtimeElementsConfigurationName).run {
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(kotlinTarget))
setupAsPublicConfigurationIfSupported(kotlinTarget)
usesPlatformOf(kotlinTarget)
}
}
}
private fun configureSourceSetDefaults(
kotlinTarget: KotlinWithJavaTarget<*>,
buildSourceSetProcessor: (AbstractKotlinCompilation<*>) -> KotlinSourceSetProcessor<*>
) {
kotlinTarget.compilations.all { compilation ->
buildSourceSetProcessor(compilation).run()
}
}
}
}
internal fun configureDefaultVersionsResolutionStrategy(project: Project, kotlinPluginVersion: String) {
project.configurations.all { configuration ->
fun MutableVersionConstraint.chooseVersion(version: String) {
if (isGradleVersionAtLeast(5, 0)) {
// In Gradle 5.0, the semantics of 'prefer' has changed to be much less imperative, and now it's 'require' that we need:
val requireMethod = javaClass.getMethod("require", String::class.java)
requireMethod(this, version)
} else {
prefer(version)
}
}
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
configuration.withDependencies { dependencySet ->
dependencySet.filterIsInstance()
.filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() }
.forEach { it.version { constraint -> constraint.chooseVersion(kotlinPluginVersion) } }
}
}
}
internal open class KotlinPlugin(
kotlinPluginVersion: String,
registry: ToolingModelBuilderRegistry
) : AbstractKotlinPlugin(KotlinTasksProvider(targetName), kotlinPluginVersion, registry) {
companion object {
private const val targetName = "" // use empty suffix for the task names
}
override fun buildSourceSetProcessor(project: Project, compilation: AbstractKotlinCompilation<*>, kotlinPluginVersion: String) =
Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget(project, KotlinPlatformType.jvm, targetName).apply {
disambiguationClassifier = null // don't add anything to the task names
}
(project.kotlinExtension as KotlinJvmProjectExtension).target = target
super.apply(project)
project.pluginManager.apply(ScriptingGradleSubplugin::class.java)
}
}
internal open class KotlinCommonPlugin(
kotlinPluginVersion: String,
registry: ToolingModelBuilderRegistry
) : AbstractKotlinPlugin(KotlinTasksProvider(targetName), kotlinPluginVersion, registry) {
companion object {
private const val targetName = "common"
}
override fun buildSourceSetProcessor(
project: Project,
compilation: AbstractKotlinCompilation<*>,
kotlinPluginVersion: String
): KotlinSourceSetProcessor<*> =
KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget(project, KotlinPlatformType.common, targetName)
(project.kotlinExtension as KotlinCommonProjectExtension).target = target
super.apply(project)
}
}
internal open class Kotlin2JsPlugin(
kotlinPluginVersion: String,
registry: ToolingModelBuilderRegistry
) : AbstractKotlinPlugin(KotlinTasksProvider(targetName), kotlinPluginVersion, registry) {
companion object {
private const val targetName = "2Js"
}
override fun buildSourceSetProcessor(
project: Project,
compilation: AbstractKotlinCompilation<*>,
kotlinPluginVersion: String
): KotlinSourceSetProcessor<*> =
Kotlin2JsSourceSetProcessor(
project, tasksProvider, compilation, kotlinPluginVersion
)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget(project, KotlinPlatformType.js, targetName)
(project.kotlinExtension as Kotlin2JsProjectExtension).target = target
super.apply(project)
}
}
internal open class KotlinAndroidPlugin(
private val kotlinPluginVersion: String,
private val registry: ToolingModelBuilderRegistry
) : Plugin {
override fun apply(project: Project) {
checkGradleCompatibility()
val androidTarget = KotlinAndroidTarget("", project)
(project.kotlinExtension as KotlinAndroidProjectExtension).target = androidTarget
applyToTarget(kotlinPluginVersion, androidTarget)
applyUserDefinedAttributes(androidTarget)
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
registry.register(KotlinModelBuilder(kotlinPluginVersion, androidTarget))
project.whenEvaluated { project.components.addAll(androidTarget.components) }
}
companion object {
fun androidTargetHandler(
kotlinPluginVersion: String,
androidTarget: KotlinAndroidTarget
): AbstractAndroidProjectHandler {
val tasksProvider = AndroidTasksProvider(androidTarget.targetName)
val version = loadAndroidPluginVersion()
if (version != null) {
val minimalVersion = "3.0.0"
if (compareVersionNumbers(version, minimalVersion) < 0) {
throw IllegalStateException("Kotlin: Unsupported version of com.android.tools.build:gradle plugin: version $minimalVersion or higher should be used with kotlin-android plugin")
}
}
val kotlinTools = KotlinConfigurationTools(
tasksProvider,
kotlinPluginVersion
)
return Android25ProjectHandler(kotlinTools)
}
fun applyToTarget(
kotlinPluginVersion: String,
kotlinTarget: KotlinAndroidTarget
) {
androidTargetHandler(kotlinPluginVersion, kotlinTarget).configureTarget(kotlinTarget)
}
}
}
class KotlinConfigurationTools internal constructor(
val kotlinTasksProvider: KotlinTasksProvider,
val kotlinPluginVersion: String
)
abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTools: KotlinConfigurationTools) {
protected val logger = Logging.getLogger(this.javaClass)
abstract fun forEachVariant(project: Project, action: (BaseVariant) -> Unit): Unit
abstract fun getResDirectories(variantData: BaseVariant): FileCollection
abstract fun getFlavorNames(variant: BaseVariant): List
abstract fun getBuildTypeName(variant: BaseVariant): String
abstract fun getLibraryOutputTask(variant: BaseVariant): Any?
protected abstract fun getSourceProviders(variantData: BaseVariant): Iterable
protected abstract fun getAllJavaSources(variantData: BaseVariant): Iterable
protected abstract fun getJavaTask(variantData: BaseVariant): AbstractCompile?
protected abstract fun addJavaSourceDirectoryToVariantModel(variantData: BaseVariant, javaSourceDirectory: File): Unit
protected open fun checkVariantIsValid(variant: BaseVariant) = Unit
protected open fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinJvmAndroidCompilation) = Unit
protected abstract fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariant,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
)
protected abstract fun wrapVariantDataForKapt(variantData: BaseVariant): KaptVariantData
fun configureTarget(kotlinAndroidTarget: KotlinAndroidTarget) {
val project = kotlinAndroidTarget.project
val ext = project.extensions.getByName("android") as BaseExtension
ext.sourceSets.all { sourceSet ->
logger.kotlinDebug("Creating KotlinBaseSourceSet for source set $sourceSet")
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(
lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, sourceSet.name)
).apply {
kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin")))
kotlin.srcDirs(sourceSet.java.srcDirs)
}
sourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
ifKaptEnabled(project) {
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name)
}
}
val kotlinOptions = KotlinJvmOptionsImpl()
project.whenEvaluated {
// TODO don't require the flag once there is an Android Gradle plugin build that supports desugaring of Long.hashCode and
// Boolean.hashCode. Instead, run conditionally, only with the AGP versions that play well with Kotlin bytecode for
// JVM target 1.8.
// See: KT-31027
if (PropertiesProvider(project).setJvmTargetFromAndroidCompileOptions == true) {
applyAndroidJavaVersion(project.extensions.getByType(BaseExtension::class.java), kotlinOptions)
}
}
kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
val androidPluginIds = listOf(
"android", "com.android.application", "android-library", "com.android.library",
"com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp"
)
val plugin by lazy {
androidPluginIds.asSequence()
.mapNotNull { project.plugins.findPlugin(it) as? BasePlugin }
.firstOrNull()
?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " +
"plugins to be applied to the project:\n\t" +
androidPluginIds.joinToString("\n\t") { "* $it" })
}
forEachVariant(project) { variant ->
val variantName = getVariantName(variant)
// Create the compilation and configure it first, then add to the compilations container. As this code is executed
// in afterEvaluate, a user's build script might have already attached item handlers to the compilations container, and those
// handlers might break when fired on a compilation that is not yet properly configured (e.g. KT-29964):
kotlinAndroidTarget.compilationFactory.create(variantName).let { compilation ->
compilation.androidVariant = variant
setUpDependencyResolution(variant, compilation)
preprocessVariant(variant, compilation, project, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider)
@Suppress("UNCHECKED_CAST")
(kotlinAndroidTarget.compilations as NamedDomainObjectCollection).add(compilation)
}
}
project.whenEvaluated {
forEachVariant(project) { variant ->
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
postprocessVariant(variant, compilation, project, ext, plugin)
val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion)
applySubplugins(project, compilation, variant, subpluginEnvironment)
}
checkAndroidAnnotationProcessorDependencyUsage(project)
addKotlinDependenciesToAndroidSourceSets(project, kotlinAndroidTarget)
}
}
/**
* The Android variants have their configurations extendsFrom relation set up in a way that only some of the configurations of the
* variants propagate the dependencies from production variants to test ones. To make this dependency propagation work for the Kotlin
* source set dependencies as well, we need to add them to the Android source sets' api/implementation-like configurations,
* not just the classpath-like configurations of the variants.
*/
private fun addKotlinDependenciesToAndroidSourceSets(
project: Project,
kotlinAndroidTarget: KotlinAndroidTarget
) {
fun addDependenciesToAndroidSourceSet(
androidSourceSet: AndroidSourceSet,
apiConfigurationName: String,
implementationConfigurationName: String,
compileOnlyConfigurationName: String,
runtimeOnlyConfigurationName: String
) {
project.addExtendsFromRelation(androidSourceSet.apiConfigurationName, apiConfigurationName)
project.addExtendsFromRelation(androidSourceSet.implementationConfigurationName, implementationConfigurationName)
project.addExtendsFromRelation(androidSourceSet.compileOnlyConfigurationName, compileOnlyConfigurationName)
project.addExtendsFromRelation(androidSourceSet.runtimeOnlyConfigurationName, runtimeOnlyConfigurationName)
}
/** First, just add the dependencies from Kotlin source sets created for the Android source sets,
* see [org.jetbrains.kotlin.gradle.plugin.AbstractAndroidProjectHandler.configureTarget]
*/
(project.extensions.getByName("android") as BaseExtension).sourceSets.forEach { androidSourceSet ->
val kotlinSourceSetName = lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, androidSourceSet.name)
project.kotlinExtension.sourceSets.findByName(kotlinSourceSetName)?.let { kotlinSourceSet ->
addDependenciesToAndroidSourceSet(
androidSourceSet,
kotlinSourceSet.apiConfigurationName,
kotlinSourceSet.implementationConfigurationName,
kotlinSourceSet.compileOnlyConfigurationName,
kotlinSourceSet.runtimeOnlyConfigurationName
)
}
}
// Then also add the Kotlin compilation dependencies (which include the dependencies from all source sets that
// take part in the compilation) to Android source sets that are only included into a single variant corresponding
// to that compilation. This is needed in order for the dependencies to get propagated to
// the test variants; see KT-29343;
// Trivial mapping of Android variants to Android source set names is impossible here,
// because some variants have their dedicated source sets with mismatching names,
// e.g. variant 'fooBarDebugAndroidTest' <-> source set 'androidTestFooBarDebug'
// In single-platform projects, the Kotlin compilations already reference the Android plugin's configurations by the names,
// so there are no such separate things as the configurations of the compilations, and there's no need to setup the
// extendsFrom relationship.
if (kotlinAndroidTarget.disambiguationClassifier != null) {
val sourceSetToVariants = mutableMapOf>().apply {
forEachVariant(project) { variant ->
for (sourceSet in getSourceProviders(variant)) {
val androidSourceSet = sourceSet as? AndroidSourceSet ?: continue
getOrPut(androidSourceSet) { mutableListOf() }.add(variant)
}
}
}
for ((androidSourceSet, variants) in sourceSetToVariants) {
val variant = variants.singleOrNull()
?: continue // skip source sets that are included in multiple Android variants
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
addDependenciesToAndroidSourceSet(
androidSourceSet,
compilation.apiConfigurationName,
compilation.implementationConfigurationName,
compilation.compileOnlyConfigurationName,
compilation.runtimeOnlyConfigurationName
)
}
}
}
private fun applyAndroidJavaVersion(baseExtension: BaseExtension, kotlinOptions: KotlinJvmOptions) {
val javaVersion =
listOf(baseExtension.compileOptions.sourceCompatibility, baseExtension.compileOptions.targetCompatibility).min()!!
if (javaVersion >= JavaVersion.VERSION_1_8)
kotlinOptions.jvmTarget = "1.8"
}
private fun preprocessVariant(
variantData: BaseVariant,
compilation: KotlinJvmAndroidCompilation,
project: Project,
rootKotlinOptions: KotlinJvmOptionsImpl,
tasksProvider: KotlinTasksProvider
) {
// This function is called before the variantData is completely filled by the Android plugin.
// The fine details of variantData, such as AP options or Java sources, should not be trusted here.
checkVariantIsValid(variantData)
val variantDataName = getVariantName(variantData)
logger.kotlinDebug("Process variant [$variantDataName]")
val javaTask = getJavaTask(variantData)
if (javaTask == null) {
logger.info("KOTLIN: javaTask is missing for $variantDataName, so Kotlin files won't be compiled for it")
return
}
val defaultSourceSet = project.kotlinExtension.sourceSets.maybeCreate(compilation.defaultSourceSetName)
val kotlinTaskName = compilation.compileKotlinTaskName
KotlinCompileTaskData.register(kotlinTaskName, compilation).apply {
// store kotlin classes in separate directory. They will serve as class-path to java compiler
destinationDir.set(project.provider { File(project.buildDir, "tmp/kotlin-classes/$variantDataName") })
}
tasksProvider.registerKotlinJVMTask(project, kotlinTaskName, compilation) {
it.parentKotlinOptionsImpl = rootKotlinOptions
it.description = "Compiles the $variantDataName kotlin."
}
// Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet)
// In MPPs, add the common main Kotlin sources to non-test variants, the common test sources to test variants
val commonSourceSetName = if (getTestedVariantData(variantData) == null)
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME else
KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME
project.kotlinExtension.sourceSets.findByName(commonSourceSetName)?.let {
compilation.source(it)
}
}
private fun postprocessVariant(
variantData: BaseVariant,
compilation: KotlinJvmAndroidCompilation,
project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin
) {
val javaTask = getJavaTask(variantData) ?: return
getTestedVariantData(variantData)?.let { testedVariant ->
val testedVariantName = getVariantName(testedVariant)
val testedCompilation = compilation.target.compilations.getByName(testedVariantName)
compilation.associateWith(testedCompilation)
}
val kotlinTask = compilation.compileKotlinTask
configureSources(kotlinTask, variantData, compilation)
wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask)
}
private fun applySubplugins(
project: Project,
compilation: KotlinCompilation<*>,
variantData: BaseVariant,
subpluginEnvironment: SubpluginEnvironment
) {
val kotlinTask = project.tasks.getByName(compilation.compileKotlinTaskName) as KotlinCompile
val javaTask = getJavaTask(variantData)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask, wrapVariantDataForKapt(variantData), this, null
)
appliedPlugins.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { configureSources(it, variantData, null) }
}
private fun configureSources(compileTask: AbstractCompile, variantData: BaseVariant, compilation: KotlinCompilation<*>?) {
val logger = compileTask.project.logger
for (provider in getSourceProviders(variantData)) {
val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue
if (compilation != null) {
compilation.source(kotlinSourceSet)
} else {
compileTask.source(kotlinSourceSet.kotlin)
}
}
for (javaSrcDir in getAllJavaSources(variantData)) {
compileTask.source(javaSrcDir)
logger.kotlinDebug("Source directory $javaSrcDir was added to kotlin source for ${compileTask.name}")
}
}
}
internal fun configureJavaTask(kotlinTask: KotlinCompile, javaTask: AbstractCompile, logger: Logger) {
kotlinTask.javaOutputDir = javaTask.destinationDir
// Make Gradle check if the javaTask is up-to-date based on the Kotlin classes
javaTask.inputsCompatible.run {
dir(kotlinTask.destinationDir)
.withNormalizer(CompileClasspathNormalizer::class.java)
.withPropertyName("${kotlinTask.name}OutputClasses")
}
// Also, use kapt1 annotations file for up-to-date check since annotation processing is done with javac
javaTask.dependsOn(kotlinTask)
/*
* It's important to modify javaTask.classpath only in doFirst,
* because Android plugin uses ConventionMapping to modify it too (see JavaCompileConfigAction.execute),
* and setting classpath explicitly prevents usage of Android mappings.
* Also classpath set by Android can be modified after execution of some tasks (see VarianConfiguration.getCompileClasspath)
* ex. it adds some support libraries jars after execution of prepareComAndroidSupportSupportV42311Library task,
* so it's only safe to modify javaTask.classpath right before its usage
*/
// todo: remove?
javaTask.appendClasspathDynamically(kotlinTask.destinationDir!!)
}
private fun ifKaptEnabled(project: Project, block: () -> Unit) {
var triggered = false
fun trigger() {
if (triggered) return
triggered = true
block()
}
project.pluginManager.withPlugin("kotlin-kapt") { trigger() }
project.pluginManager.withPlugin("org.jetbrains.kotlin.kapt") { trigger() }
}
private fun SourceSet.clearJavaSrcDirs() {
java.setSrcDirs(emptyList())
}
internal fun Task.registerSubpluginOptionsAsInputs(subpluginId: String, subpluginOptions: List) {
// 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 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 -> Unit
is CompositeSubpluginOption -> {
val subpluginIdWithWrapperKey = "$subpluginId.$optionKey$indexSuffix"
registerSubpluginOptionsAsInputs(subpluginIdWithWrapperKey, option.originalOptions)
}
is FilesSubpluginOption -> when (option.kind) {
FilesOptionKind.INTERNAL -> Unit
}.run { /* exhaustive when */ }
else -> {
inputsCompatible.propertyCompatible("$subpluginId." + option.key + indexSuffix, Callable { option.value })
}
}
}
}
}
//copied from BasePlugin.getLocalVersion
internal fun loadAndroidPluginVersion(): String? {
try {
val clazz = BasePlugin::class.java
val className = clazz.simpleName + ".class"
val classPath = clazz.getResource(className).toString()
if (!classPath.startsWith("jar")) {
// Class not from JAR, unlikely
return null
}
val manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"
val jarConnection = URL(manifestPath).openConnection()
jarConnection.useCaches = false
val jarInputStream = jarConnection.inputStream
val attr = Manifest(jarInputStream).mainAttributes
jarInputStream.close()
return attr.getValue("Plugin-Version")
} catch (t: Throwable) {
return null
}
}
//Copied from StringUtil.compareVersionNumbers
internal fun compareVersionNumbers(v1: String?, v2: String?): Int {
if (v1 == null && v2 == null) {
return 0
}
if (v1 == null) {
return -1
}
if (v2 == null) {
return 1
}
val pattern = "[\\.\\_\\-]".toRegex()
val digitsPattern = "\\d+".toRegex()
val part1 = v1.split(pattern)
val part2 = v2.split(pattern)
var idx = 0
while (idx < part1.size && idx < part2.size) {
val p1 = part1[idx]
val p2 = part2[idx]
val cmp: Int
if (p1.matches(digitsPattern) && p2.matches(digitsPattern)) {
cmp = p1.toInt().compareTo(p2.toInt())
} else {
cmp = part1[idx].compareTo(part2[idx])
}
if (cmp != 0) return cmp
idx++
}
if (part1.size == part2.size) {
return 0
} else {
val left = part1.size > idx
val parts = if (left) part1 else part2
while (idx < parts.size) {
val p = parts[idx]
val cmp: Int
if (p.matches(digitsPattern)) {
cmp = Integer(p).compareTo(0)
} else {
cmp = 1
}
if (cmp != 0) return if (left) cmp else -cmp
idx++
}
return 0
}
}