[Gradle][KT-53342] Merge Android25ProjectHandler and AbstractAndroidProjectHandler

Since there was just a single implementation of AbstractAndroidProjectHandler,
merging those classes makes it easier to remove dead code and
maintain the implementation.
This commit is contained in:
sebastian.sellmair
2022-07-27 11:52:13 +02:00
committed by Space
parent b6ee6d536c
commit 194fff11bd
6 changed files with 495 additions and 513 deletions
@@ -216,7 +216,7 @@ private fun chooseAndAddStdlibDependency(
private fun androidMainSourceSetName(project: Project): String { private fun androidMainSourceSetName(project: Project): String {
val target = project.findAndroidTarget() ?: error("No Android target found") val target = project.findAndroidTarget() ?: error("No Android target found")
return AbstractAndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(target, "main") return AndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(target, "main")
} }
private fun isRelatedToAndroidTestSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet): Boolean { private fun isRelatedToAndroidTestSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet): Boolean {
@@ -230,7 +230,7 @@ private fun isRelatedToAndroidTestSourceSet(project: Project, kotlinSourceSet: K
(androidExtension.testVariants + androidExtension.unitTestVariants).forEach { variant -> (androidExtension.testVariants + androidExtension.unitTestVariants).forEach { variant ->
if (variant.sourceSets.any { if (variant.sourceSets.any {
kotlinSourceSet.name == AbstractAndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(androidTarget, it.name) kotlinSourceSet.name == AndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(androidTarget, it.name)
} }
) return true ) return true
} }
@@ -6,19 +6,14 @@
package org.jetbrains.kotlin.gradle.plugin package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.* import com.android.build.gradle.*
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.SourceKind
import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.* import org.gradle.api.*
import org.gradle.api.attributes.Category import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.Directory import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.api.plugins.InvalidPluginException
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.PublishingExtension
@@ -31,11 +26,8 @@ import org.gradle.jvm.tasks.Jar
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder
import org.jetbrains.kotlin.gradle.plugin.AbstractAndroidProjectHandler.Companion.kotlinSourceSetNameForAndroidSourceSet
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
import org.jetbrains.kotlin.gradle.plugin.internal.MavenPluginConfigurator import org.jetbrains.kotlin.gradle.plugin.internal.MavenPluginConfigurator
@@ -48,8 +40,6 @@ import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.configuration.* import org.jetbrains.kotlin.gradle.tasks.configuration.*
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.tooling.includeKotlinToolingMetadataInApk
import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.*
import java.io.File import java.io.File
import java.net.URL import java.net.URL
@@ -720,7 +710,7 @@ internal open class KotlinAndroidPlugin(
companion object { companion object {
const val MINIMAL_SUPPORTED_AGP_VERSION = "3.6.4" const val MINIMAL_SUPPORTED_AGP_VERSION = "3.6.4"
fun androidTargetHandler(): AbstractAndroidProjectHandler { fun androidTargetHandler(): AndroidProjectHandler {
val tasksProvider = KotlinTasksProvider() val tasksProvider = KotlinTasksProvider()
if (androidPluginVersion != null) { if (androidPluginVersion != null) {
@@ -732,11 +722,7 @@ internal open class KotlinAndroidPlugin(
} }
} }
val kotlinTools = KotlinConfigurationTools( return AndroidProjectHandler(KotlinConfigurationTools(tasksProvider))
tasksProvider
)
return Android25ProjectHandler(kotlinTools)
} }
internal fun Project.dynamicallyApplyWhenAndroidPluginIsApplied( internal fun Project.dynamicallyApplyWhenAndroidPluginIsApplied(
@@ -774,240 +760,6 @@ class KotlinConfigurationTools internal constructor(
val kotlinTasksProvider: KotlinTasksProvider val kotlinTasksProvider: KotlinTasksProvider
) )
abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTools: KotlinConfigurationTools) {
companion object {
fun kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget: KotlinAndroidTarget, androidSourceSetName: String) =
lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, androidSourceSetName)
}
protected val logger = Logging.getLogger(this.javaClass)
abstract fun getFlavorNames(variant: BaseVariant): List<String>
abstract fun getBuildTypeName(variant: BaseVariant): String
abstract fun getLibraryOutputTask(variant: BaseVariant): Any?
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: TaskProvider<out AbstractCompile>,
kotlinTask: TaskProvider<out KotlinCompile>
)
fun configureTarget(kotlinAndroidTarget: KotlinAndroidTarget) {
syncKotlinAndAndroidSourceSets(kotlinAndroidTarget)
val project = kotlinAndroidTarget.project
val ext = project.extensions.getByName("android") as BaseExtension
val kotlinOptions = KotlinJvmOptionsImpl()
kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
val plugin = 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" })
project.forEachVariant { 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<in KotlinJvmAndroidCompilation>).add(compilation)
}
}
project.whenEvaluated {
forEachVariant { variant ->
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
postprocessVariant(variant, compilation, project, ext, plugin)
val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project)
subpluginEnvironment.addSubpluginOptions(project, compilation)
}
checkAndroidAnnotationProcessorDependencyUsage(project)
addKotlinDependenciesToAndroidSourceSets(project, kotlinAndroidTarget)
}
project.includeKotlinToolingMetadataInApk()
addAndroidUnitTestTasksAsDependenciesToAllTest(project)
}
/**
* 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
) {
if (project.configurations.findByName(androidSourceSet.apiConfigurationName) != null) {
project.addExtendsFromRelation(androidSourceSet.apiConfigurationName, apiConfigurationName)
} else {
// If any dependency is added to this configuration, report an error:
project.configurations.getByName(apiConfigurationName).dependencies.all {
throw InvalidUserCodeException(
"API dependencies are not allowed for Android source set ${androidSourceSet.name}. " +
"Please use an implementation dependency instead."
)
}
}
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 = kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, androidSourceSet.name)
project.kotlinExtension.sourceSets.findByName(kotlinSourceSetName)?.let { kotlinSourceSet ->
addDependenciesToAndroidSourceSet(
androidSourceSet,
kotlinSourceSet.apiConfigurationName,
kotlinSourceSet.implementationConfigurationName,
kotlinSourceSet.compileOnlyConfigurationName,
kotlinSourceSet.runtimeOnlyConfigurationName
)
}
}
}
private fun addAndroidUnitTestTasksAsDependenciesToAllTest(project: Project) {
val allTestTaskName = project.kotlinTestRegistry.allTestsTaskName
project.tasks.matching { it.name == allTestTaskName }.configureEach { task ->
task.dependsOn(project.provider {
val androidUnitTestTasks = mutableListOf<Any>()
forEachVariant(project) { variant ->
if (variant is UnitTestVariant) {
// There's no API for getting the Android unit test tasks from the variant, so match them by name:
androidUnitTestTasks.add(project.provider {
project.tasks.matching { it.name == lowerCamelCaseName("test", variant.name) }
})
}
}
androidUnitTestTasks
})
}
}
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 defaultSourceSet = project.kotlinExtension.sourceSets.maybeCreate(compilation.defaultSourceSetName)
val configAction = KotlinCompileConfig(compilation)
configAction.configureTask { task ->
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"))
task.description = "Compiles the $variantDataName kotlin."
}
tasksProvider.registerKotlinJVMTask(project, compilation.compileKotlinTaskName, compilation.kotlinOptions, configAction)
// Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet)
compilation.androidVariant.forEachKotlinSourceSet(
compilation.target as KotlinAndroidTarget,
project.kotlinExtension
) { kotlinSourceSet ->
compilation.source(kotlinSourceSet)
}
}
private fun postprocessVariant(
variantData: BaseVariant,
compilation: KotlinJvmAndroidCompilation,
project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin
) {
getTestedVariantData(variantData)?.let { testedVariant ->
val testedVariantName = getVariantName(testedVariant)
val testedCompilation = compilation.target.compilations.getByName(testedVariantName)
compilation.associateWith(testedCompilation)
}
val javaTask = variantData.getJavaTaskProvider()
val kotlinTask = compilation.compileKotlinTaskProvider
compilation.androidVariant.forEachJavaSourceDir { sources ->
kotlinTask.configure {
it.setSource(sources.dir)
it.dependsOn(sources)
}
}
wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask)
}
}
internal inline fun BaseVariant.forEachKotlinSourceSet(
kotlinAndroidTarget: KotlinAndroidTarget,
kotlinExtension: KotlinProjectExtension,
action: (KotlinSourceSet) -> Unit
) {
sourceSets
.mapNotNull { provider ->
val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, provider.name)
kotlinExtension.sourceSets.findByName(kotlinSourceSetName)
}
.forEach(action)
}
internal inline fun BaseVariant.forEachKotlinSourceDirectorySet(action: (SourceDirectorySet) -> Unit) {
sourceSets
.mapNotNull { it.getExtension<SourceDirectorySet>(KOTLIN_DSL_NAME) }
.forEach(action)
}
internal inline fun BaseVariant.forEachJavaSourceDir(action: (ConfigurableFileTree) -> Unit) {
getSourceFolders(SourceKind.JAVA).forEach(action)
}
internal fun ifKaptEnabled(project: Project, block: () -> Unit) { internal fun ifKaptEnabled(project: Project, block: () -> Unit) {
var triggered = false var triggered = false
@@ -12,8 +12,7 @@ import org.gradle.api.internal.plugins.DslObject
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.AbstractAndroidProjectHandler.Companion.kotlinSourceSetNameForAndroidSourceSet import org.jetbrains.kotlin.gradle.plugin.AndroidProjectHandler.Companion.kotlinSourceSetNameForAndroidSourceSet
import org.jetbrains.kotlin.gradle.plugin.addConvention
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory.Companion.defaultSourceFolder import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory.Companion.defaultSourceFolder
internal const val IS_ANDROID_SOURCE_SET = "android-source-set" internal const val IS_ANDROID_SOURCE_SET = "android-source-set"
@@ -1,256 +0,0 @@
/*
* Copyright 2010-2019 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.
*/
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.plugin
import com.android.build.api.attributes.BuildTypeAttr
import com.android.build.gradle.*
import com.android.build.gradle.api.*
import com.android.build.gradle.tasks.MergeResources
import org.gradle.api.Project
import org.gradle.api.artifacts.ArtifactCollection
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.Category
import org.gradle.api.file.FileCollection
import org.gradle.api.specs.Spec
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.thisTaskProvider
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import java.io.File
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import java.util.concurrent.Callable
class Android25ProjectHandler(
kotlinConfigurationTools: KotlinConfigurationTools
) : AbstractAndroidProjectHandler(kotlinConfigurationTools) {
override fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariant,
javaTask: TaskProvider<out AbstractCompile>,
kotlinTask: TaskProvider<out KotlinCompile>
) {
val preJavaKotlinOutput = project.files(project.provider {
mutableListOf<File>().apply {
add(kotlinTask.get().destinationDirectory.get().asFile)
if (Kapt3GradleSubplugin.isEnabled(project)) {
// Add Kapt3 output as well, since there's no SyncOutputTask with the new API
val kaptClasssesDir = Kapt3GradleSubplugin.getKaptGeneratedClassesDir(project, getVariantName(variantData))
add(kaptClasssesDir)
}
}
}).builtBy(kotlinTask)
val preJavaClasspathKey = variantData.registerPreJavacGeneratedBytecode(preJavaKotlinOutput)
kotlinTask.configure { kotlinTaskInstance ->
kotlinTaskInstance.libraries
.from(variantData.getCompileClasspath(preJavaClasspathKey))
.from(Callable { AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt) })
kotlinTaskInstance.javaOutputDir.set(javaTask.flatMap { it.destinationDirectory })
}
// Find the classpath entries that come from the tested variant and register them as the friend paths, lazily
val originalArtifactCollection = variantData.getCompileClasspathArtifacts(preJavaClasspathKey)
val testedVariantDataIsNotNull = getTestedVariantData(variantData) != null
val projectPath = project.path
compilation.testedVariantArtifacts.set(
originalArtifactCollection.artifactFiles.filter(
AndroidTestedVariantArtifactsFilter(
originalArtifactCollection,
testedVariantDataIsNotNull,
projectPath
)
)
)
compilation.output.classesDirs.from(
kotlinTask.flatMap { it.destinationDirectory },
javaTask.flatMap { it.destinationDirectory }
)
}
override fun getFlavorNames(variant: BaseVariant): List<String> = variant.productFlavors.map { it.name }
override fun getBuildTypeName(variant: BaseVariant): String = variant.buildType.name
// TODO the return type is actually `AbstractArchiveTask | TaskProvider<out AbstractArchiveTask>`;
// change the signature once the Android Gradle plugin versions that don't support task providers are dropped
override fun getLibraryOutputTask(variant: BaseVariant): Any? {
val getPackageLibraryProvider = variant.javaClass.methods
.find { it.name == "getPackageLibraryProvider" && it.parameterCount == 0 }
return if (getPackageLibraryProvider != null) {
@Suppress("UNCHECKED_CAST")
getPackageLibraryProvider(variant) as TaskProvider<out AbstractArchiveTask>
} else {
(variant as? LibraryVariant)?.packageLibrary
}
}
override fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinJvmAndroidCompilation) {
val project = compilation.target.project
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation)
compilation.compileDependencyFiles = variant.compileConfiguration.apply {
usesPlatformOf(compilation.target)
project.addExtendsFromRelation(name, compilation.compileDependencyConfigurationName)
}
compilation.runtimeDependencyFiles = variant.runtimeConfiguration.apply {
usesPlatformOf(compilation.target)
project.addExtendsFromRelation(name, compilation.runtimeDependencyConfigurationName)
}
val buildTypeAttrValue = project.objects.named(BuildTypeAttr::class.java, variant.buildType.name)
listOf(compilation.compileDependencyConfigurationName, compilation.runtimeDependencyConfigurationName).forEach {
project.configurations.findByName(it)?.attributes?.attribute(Attribute.of(BuildTypeAttr::class.java), buildTypeAttrValue)
}
// TODO this code depends on the convention that is present in the Android plugin as there's no public API
// We should request such API in the Android plugin
val apiElementsConfigurationName = "${variant.name}ApiElements"
val runtimeElementsConfigurationName = "${variant.name}RuntimeElements"
// KT-29476, the Android *Elements configurations need Kotlin MPP dependencies:
if (project.configurations.findByName(apiElementsConfigurationName) != null) {
project.addExtendsFromRelation(apiElementsConfigurationName, compilation.apiConfigurationName)
}
if (project.configurations.findByName(runtimeElementsConfigurationName) != null) {
project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.implementationConfigurationName)
project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.runtimeOnlyConfigurationName)
}
listOf(apiElementsConfigurationName, runtimeElementsConfigurationName).forEach { outputConfigurationName ->
project.configurations.findByName(outputConfigurationName)?.let { configuration ->
configuration.usesPlatformOf(compilation.target)
configuration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
}
}
}
}
internal fun getTestedVariantData(variantData: BaseVariant): BaseVariant? = when (variantData) {
is TestVariant -> variantData.testedVariant
is UnitTestVariant -> variantData.testedVariant as? BaseVariant
else -> null
}
internal fun getVariantName(variant: BaseVariant): String = variant.name
@Suppress("UNCHECKED_CAST")
internal fun BaseVariant.getJavaTaskProvider(): TaskProvider<out JavaCompile> =
this::class.java.methods.firstOrNull { it.name == "getJavaCompileProvider" }
?.invoke(this) as? TaskProvider<JavaCompile>
?: @Suppress("DEPRECATION") javaCompile.thisTaskProvider
internal fun forEachVariant(project: Project, action: (BaseVariant) -> Unit) {
val androidExtension = project.extensions.getByName("android")
when (androidExtension) {
is AppExtension -> androidExtension.applicationVariants.all(action)
is LibraryExtension -> {
androidExtension.libraryVariants.all(action)
if (androidExtension is FeatureExtension) {
androidExtension.featureVariants.all(action)
}
}
is TestExtension -> androidExtension.applicationVariants.all(action)
}
if (androidExtension is TestedExtension) {
androidExtension.testVariants.all(action)
androidExtension.unitTestVariants.all(action)
}
}
internal fun BaseVariant.getResDirectories(): FileCollection {
val getAllResourcesMethod =
this::class.java.methods.firstOrNull { it.name == "getAllRawAndroidResources" }
if (getAllResourcesMethod != null) {
val allResources = getAllResourcesMethod.invoke(this) as FileCollection
return allResources
}
val project = mergeResources.project
return project.files(Callable { mergeResources?.computeResourceSetList0() ?: emptyList() })
}
//TODO A public API is expected for this purpose. Once it is available, use the public API
private fun MergeResources.computeResourceSetList0(): List<File>? {
val computeResourceSetListMethod = MergeResources::class.java.declaredMethods
.firstOrNull { it.name == "computeResourceSetList" && it.parameterCount == 0 } ?: return null
val oldIsAccessible = computeResourceSetListMethod.isAccessible
try {
computeResourceSetListMethod.isAccessible = true
val resourceSets = computeResourceSetListMethod.invoke(this) as? Iterable<*>
return resourceSets
?.mapNotNull { resourceSet ->
val getSourceFiles = resourceSet?.javaClass?.methods?.find { it.name == "getSourceFiles" && it.parameterCount == 0 }
val files = getSourceFiles?.invoke(resourceSet)
@Suppress("UNCHECKED_CAST")
files as? Iterable<File>
}
?.flatten()
} finally {
computeResourceSetListMethod.isAccessible = oldIsAccessible
}
}
/** Filter for the AGP test variant classpath artifacts. */
class AndroidTestedVariantArtifactsFilter(
private val artifactCollection: ArtifactCollection,
private val testedVariantDataIsNotNull: Boolean,
private val projectPath: String
) : Serializable, Spec<File> {
/** Make transient as it should be derived from the [artifactCollection] property which may change in configuration cached runs. */
@Transient
private var filteredFiles = initFilteredFiles()
private fun initFilteredFiles(): Lazy<Set<File>> {
return lazy {
artifactCollection.filter {
it.id.componentIdentifier is TestedComponentIdentifier ||
// If tests depend on the main classes transitively, through a test dependency on another module which
// depends on this module, then there's no artifact with a TestedComponentIdentifier, so consider the artifact of the
// current module a friend path, too:
testedVariantDataIsNotNull &&
(it.id.componentIdentifier as? ProjectComponentIdentifier)?.projectPath == projectPath
}
.mapTo(mutableSetOf()) { it.file }
}
}
private fun writeObject(objectOutputStream: ObjectOutputStream) {
objectOutputStream.defaultWriteObject()
}
private fun readObject(objectInputStream: ObjectInputStream) {
objectInputStream.defaultReadObject()
filteredFiles = initFilteredFiles()
}
override fun isSatisfiedBy(element: File): Boolean {
return element in filteredFiles.value
}
}
@@ -0,0 +1,487 @@
/*
* Copyright 2010-2019 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.
*/
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.plugin
import com.android.build.api.attributes.BuildTypeAttr
import com.android.build.gradle.*
import com.android.build.gradle.api.*
import com.android.build.gradle.tasks.MergeResources
import org.gradle.api.InvalidUserCodeException
import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.Project
import org.gradle.api.artifacts.ArtifactCollection
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.Category
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logging
import org.gradle.api.plugins.InvalidPluginException
import org.gradle.api.specs.Spec
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.syncKotlinAndAndroidSourceSets
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
import org.jetbrains.kotlin.gradle.tasks.thisTaskProvider
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.tooling.includeKotlinToolingMetadataInApk
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import org.jetbrains.kotlin.gradle.utils.androidPluginIds
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import java.util.concurrent.Callable
internal class AndroidProjectHandler(
private val kotlinConfigurationTools: KotlinConfigurationTools
) {
companion object {
fun kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget: KotlinAndroidTarget, androidSourceSetName: String) =
lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, androidSourceSetName)
}
private val logger = Logging.getLogger(this.javaClass)
fun configureTarget(kotlinAndroidTarget: KotlinAndroidTarget) {
syncKotlinAndAndroidSourceSets(kotlinAndroidTarget)
val project = kotlinAndroidTarget.project
val ext = project.extensions.getByName("android") as BaseExtension
val kotlinOptions = KotlinJvmOptionsImpl()
kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
val plugin = 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" })
project.forEachVariant { 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<in KotlinJvmAndroidCompilation>).add(compilation)
}
}
project.whenEvaluated {
forEachVariant { variant ->
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
postprocessVariant(variant, compilation, project, ext, plugin)
val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project)
subpluginEnvironment.addSubpluginOptions(project, compilation)
}
checkAndroidAnnotationProcessorDependencyUsage(project)
addKotlinDependenciesToAndroidSourceSets(project, kotlinAndroidTarget)
}
project.includeKotlinToolingMetadataInApk()
addAndroidUnitTestTasksAsDependenciesToAllTest(project)
}
/**
* 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
) {
if (project.configurations.findByName(androidSourceSet.apiConfigurationName) != null) {
project.addExtendsFromRelation(androidSourceSet.apiConfigurationName, apiConfigurationName)
} else {
// If any dependency is added to this configuration, report an error:
project.configurations.getByName(apiConfigurationName).dependencies.all {
throw InvalidUserCodeException(
"API dependencies are not allowed for Android source set ${androidSourceSet.name}. " +
"Please use an implementation dependency instead."
)
}
}
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.AndroidProjectHandler.configureTarget]
*/
(project.extensions.getByName("android") as BaseExtension).sourceSets.forEach { androidSourceSet ->
val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, androidSourceSet.name)
project.kotlinExtension.sourceSets.findByName(kotlinSourceSetName)?.let { kotlinSourceSet ->
addDependenciesToAndroidSourceSet(
androidSourceSet,
kotlinSourceSet.apiConfigurationName,
kotlinSourceSet.implementationConfigurationName,
kotlinSourceSet.compileOnlyConfigurationName,
kotlinSourceSet.runtimeOnlyConfigurationName
)
}
}
}
private fun addAndroidUnitTestTasksAsDependenciesToAllTest(project: Project) {
val allTestTaskName = project.kotlinTestRegistry.allTestsTaskName
project.tasks.matching { it.name == allTestTaskName }.configureEach { task ->
task.dependsOn(project.provider {
val androidUnitTestTasks = mutableListOf<Any>()
forEachVariant(project) { variant ->
if (variant is UnitTestVariant) {
// There's no API for getting the Android unit test tasks from the variant, so match them by name:
androidUnitTestTasks.add(project.provider {
project.tasks.matching { it.name == lowerCamelCaseName("test", variant.name) }
})
}
}
androidUnitTestTasks
})
}
}
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.
val variantDataName = getVariantName(variantData)
logger.kotlinDebug("Process variant [$variantDataName]")
val defaultSourceSet = project.kotlinExtension.sourceSets.maybeCreate(compilation.defaultSourceSetName)
val configAction = KotlinCompileConfig(compilation)
configAction.configureTask { task ->
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"))
task.description = "Compiles the $variantDataName kotlin."
}
tasksProvider.registerKotlinJVMTask(project, compilation.compileKotlinTaskName, compilation.kotlinOptions, configAction)
// Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet)
compilation.androidVariant.forEachKotlinSourceSet(
compilation.target as KotlinAndroidTarget,
project.kotlinExtension
) { kotlinSourceSet ->
compilation.source(kotlinSourceSet)
}
}
private fun postprocessVariant(
variantData: BaseVariant,
compilation: KotlinJvmAndroidCompilation,
project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin
) {
getTestedVariantData(variantData)?.let { testedVariant ->
val testedVariantName = getVariantName(testedVariant)
val testedCompilation = compilation.target.compilations.getByName(testedVariantName)
compilation.associateWith(testedCompilation)
}
val javaTask = variantData.getJavaTaskProvider()
val kotlinTask = compilation.compileKotlinTaskProvider
compilation.androidVariant.forEachJavaSourceDir { sources ->
kotlinTask.configure {
it.setSource(sources.dir)
it.dependsOn(sources)
}
}
wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask)
}
private fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariant,
javaTask: TaskProvider<out AbstractCompile>,
kotlinTask: TaskProvider<out KotlinCompile>
) {
val preJavaKotlinOutput = project.files(project.provider {
mutableListOf<File>().apply {
add(kotlinTask.get().destinationDirectory.get().asFile)
if (Kapt3GradleSubplugin.isEnabled(project)) {
// Add Kapt3 output as well, since there's no SyncOutputTask with the new API
val kaptClasssesDir = Kapt3GradleSubplugin.getKaptGeneratedClassesDir(project, getVariantName(variantData))
add(kaptClasssesDir)
}
}
}).builtBy(kotlinTask)
val preJavaClasspathKey = variantData.registerPreJavacGeneratedBytecode(preJavaKotlinOutput)
kotlinTask.configure { kotlinTaskInstance ->
kotlinTaskInstance.libraries
.from(variantData.getCompileClasspath(preJavaClasspathKey))
.from(Callable { AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt) })
kotlinTaskInstance.javaOutputDir.set(javaTask.flatMap { it.destinationDirectory })
}
// Find the classpath entries that come from the tested variant and register them as the friend paths, lazily
val originalArtifactCollection = variantData.getCompileClasspathArtifacts(preJavaClasspathKey)
val testedVariantDataIsNotNull = getTestedVariantData(variantData) != null
val projectPath = project.path
compilation.testedVariantArtifacts.set(
originalArtifactCollection.artifactFiles.filter(
AndroidTestedVariantArtifactsFilter(
originalArtifactCollection,
testedVariantDataIsNotNull,
projectPath
)
)
)
compilation.output.classesDirs.from(
kotlinTask.flatMap { it.destinationDirectory },
javaTask.flatMap { it.destinationDirectory }
)
}
fun getFlavorNames(variant: BaseVariant): List<String> = variant.productFlavors.map { it.name }
fun getBuildTypeName(variant: BaseVariant): String = variant.buildType.name
// TODO the return type is actually `AbstractArchiveTask | TaskProvider<out AbstractArchiveTask>`;
// change the signature once the Android Gradle plugin versions that don't support task providers are dropped
fun getLibraryOutputTask(variant: BaseVariant): Any? {
val getPackageLibraryProvider = variant.javaClass.methods
.find { it.name == "getPackageLibraryProvider" && it.parameterCount == 0 }
return if (getPackageLibraryProvider != null) {
@Suppress("UNCHECKED_CAST")
getPackageLibraryProvider(variant) as TaskProvider<out AbstractArchiveTask>
} else {
(variant as? LibraryVariant)?.packageLibrary
}
}
fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinJvmAndroidCompilation) {
val project = compilation.target.project
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation)
compilation.compileDependencyFiles = variant.compileConfiguration.apply {
usesPlatformOf(compilation.target)
project.addExtendsFromRelation(name, compilation.compileDependencyConfigurationName)
}
compilation.runtimeDependencyFiles = variant.runtimeConfiguration.apply {
usesPlatformOf(compilation.target)
project.addExtendsFromRelation(name, compilation.runtimeDependencyConfigurationName)
}
val buildTypeAttrValue = project.objects.named(BuildTypeAttr::class.java, variant.buildType.name)
listOf(compilation.compileDependencyConfigurationName, compilation.runtimeDependencyConfigurationName).forEach {
project.configurations.findByName(it)?.attributes?.attribute(Attribute.of(BuildTypeAttr::class.java), buildTypeAttrValue)
}
// TODO this code depends on the convention that is present in the Android plugin as there's no public API
// We should request such API in the Android plugin
val apiElementsConfigurationName = "${variant.name}ApiElements"
val runtimeElementsConfigurationName = "${variant.name}RuntimeElements"
// KT-29476, the Android *Elements configurations need Kotlin MPP dependencies:
if (project.configurations.findByName(apiElementsConfigurationName) != null) {
project.addExtendsFromRelation(apiElementsConfigurationName, compilation.apiConfigurationName)
}
if (project.configurations.findByName(runtimeElementsConfigurationName) != null) {
project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.implementationConfigurationName)
project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.runtimeOnlyConfigurationName)
}
listOf(apiElementsConfigurationName, runtimeElementsConfigurationName).forEach { outputConfigurationName ->
project.configurations.findByName(outputConfigurationName)?.let { configuration ->
configuration.usesPlatformOf(compilation.target)
configuration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
}
}
}
}
internal fun getTestedVariantData(variantData: BaseVariant): BaseVariant? = when (variantData) {
is TestVariant -> variantData.testedVariant
is UnitTestVariant -> variantData.testedVariant as? BaseVariant
else -> null
}
internal fun getVariantName(variant: BaseVariant): String = variant.name
@Suppress("UNCHECKED_CAST")
internal fun BaseVariant.getJavaTaskProvider(): TaskProvider<out JavaCompile> =
this::class.java.methods.firstOrNull { it.name == "getJavaCompileProvider" }
?.invoke(this) as? TaskProvider<JavaCompile>
?: @Suppress("DEPRECATION") javaCompile.thisTaskProvider
internal fun forEachVariant(project: Project, action: (BaseVariant) -> Unit) {
val androidExtension = project.extensions.getByName("android")
when (androidExtension) {
is AppExtension -> androidExtension.applicationVariants.all(action)
is LibraryExtension -> {
androidExtension.libraryVariants.all(action)
if (androidExtension is FeatureExtension) {
androidExtension.featureVariants.all(action)
}
}
is TestExtension -> androidExtension.applicationVariants.all(action)
}
if (androidExtension is TestedExtension) {
androidExtension.testVariants.all(action)
androidExtension.unitTestVariants.all(action)
}
}
internal fun BaseVariant.getResDirectories(): FileCollection {
val getAllResourcesMethod =
this::class.java.methods.firstOrNull { it.name == "getAllRawAndroidResources" }
if (getAllResourcesMethod != null) {
val allResources = getAllResourcesMethod.invoke(this) as FileCollection
return allResources
}
val project = mergeResources.project
return project.files(Callable { mergeResources?.computeResourceSetList0() ?: emptyList() })
}
//TODO A public API is expected for this purpose. Once it is available, use the public API
private fun MergeResources.computeResourceSetList0(): List<File>? {
val computeResourceSetListMethod = MergeResources::class.java.declaredMethods
.firstOrNull { it.name == "computeResourceSetList" && it.parameterCount == 0 } ?: return null
val oldIsAccessible = computeResourceSetListMethod.isAccessible
try {
computeResourceSetListMethod.isAccessible = true
val resourceSets = computeResourceSetListMethod.invoke(this) as? Iterable<*>
return resourceSets
?.mapNotNull { resourceSet ->
val getSourceFiles = resourceSet?.javaClass?.methods?.find { it.name == "getSourceFiles" && it.parameterCount == 0 }
val files = getSourceFiles?.invoke(resourceSet)
@Suppress("UNCHECKED_CAST")
files as? Iterable<File>
}
?.flatten()
} finally {
computeResourceSetListMethod.isAccessible = oldIsAccessible
}
}
/** Filter for the AGP test variant classpath artifacts. */
class AndroidTestedVariantArtifactsFilter(
private val artifactCollection: ArtifactCollection,
private val testedVariantDataIsNotNull: Boolean,
private val projectPath: String
) : Serializable, Spec<File> {
/** Make transient as it should be derived from the [artifactCollection] property which may change in configuration cached runs. */
@Transient
private var filteredFiles = initFilteredFiles()
private fun initFilteredFiles(): Lazy<Set<File>> {
return lazy {
artifactCollection.filter {
it.id.componentIdentifier is TestedComponentIdentifier ||
// If tests depend on the main classes transitively, through a test dependency on another module which
// depends on this module, then there's no artifact with a TestedComponentIdentifier, so consider the artifact of the
// current module a friend path, too:
testedVariantDataIsNotNull &&
(it.id.componentIdentifier as? ProjectComponentIdentifier)?.projectPath == projectPath
}
.mapTo(mutableSetOf()) { it.file }
}
}
private fun writeObject(objectOutputStream: ObjectOutputStream) {
objectOutputStream.defaultWriteObject()
}
private fun readObject(objectInputStream: ObjectInputStream) {
objectInputStream.defaultReadObject()
filteredFiles = initFilteredFiles()
}
override fun isSatisfiedBy(element: File): Boolean {
return element in filteredFiles.value
}
}
internal inline fun BaseVariant.forEachKotlinSourceSet(
kotlinAndroidTarget: KotlinAndroidTarget,
kotlinExtension: KotlinProjectExtension,
action: (KotlinSourceSet) -> Unit
) {
sourceSets
.mapNotNull { provider ->
val kotlinSourceSetName = AndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, provider.name)
kotlinExtension.sourceSets.findByName(kotlinSourceSetName)
}
.forEach(action)
}
internal inline fun BaseVariant.forEachKotlinSourceDirectorySet(action: (SourceDirectorySet) -> Unit) {
sourceSets
.mapNotNull { it.getExtension<SourceDirectorySet>(KOTLIN_DSL_NAME) }
.forEach(action)
}
internal inline fun BaseVariant.forEachJavaSourceDir(action: (ConfigurableFileTree) -> Unit) {
getSourceFolders(SourceKind.JAVA).forEach(action)
}
@@ -67,7 +67,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
var publishLibraryVariantsGroupedByFlavor = false var publishLibraryVariantsGroupedByFlavor = false
private fun checkPublishLibraryVariantsExist() { private fun checkPublishLibraryVariantsExist() {
fun AbstractAndroidProjectHandler.getLibraryVariantNames() = fun AndroidProjectHandler.getLibraryVariantNames() =
mutableSetOf<String>().apply { mutableSetOf<String>().apply {
project.forEachVariant { project.forEachVariant {
if (getLibraryOutputTask(it) != null) if (getLibraryOutputTask(it) != null)
@@ -99,7 +99,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
return publishLibraryVariants?.contains(getVariantName(variant)) ?: true return publishLibraryVariants?.contains(getVariantName(variant)) ?: true
} }
private fun AbstractAndroidProjectHandler.doCreateComponents(): Set<KotlinTargetComponent> { private fun AndroidProjectHandler.doCreateComponents(): Set<KotlinTargetComponent> {
val publishableVariants = mutableListOf<BaseVariant>() val publishableVariants = mutableListOf<BaseVariant>()
.apply { project.forEachVariant { add(it) } } .apply { project.forEachVariant { add(it) } }
@@ -173,7 +173,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
}.toSet() }.toSet()
} }
private fun AbstractAndroidProjectHandler.createAndroidUsageContexts( private fun AndroidProjectHandler.createAndroidUsageContexts(
variant: BaseVariant, variant: BaseVariant,
compilation: KotlinCompilation<*>, compilation: KotlinCompilation<*>,
artifactClassifier: String?, artifactClassifier: String?,