Reduce overhead on wiring task inputs to Android layouts

^KT-53118 In Progress
This commit is contained in:
Yahor Berdnikau
2022-08-04 16:19:55 +02:00
committed by Space
parent 6dd9115ee9
commit d6032be06e
3 changed files with 29 additions and 100 deletions
@@ -1027,12 +1027,12 @@ abstract class AbstractKotlinAndroidGradleTests : BaseGradleIT() {
project.projectDir.resolve("app/build.gradle").appendText(
"""
|
|androidExtensions {
| experimental = true
|
}
""".trimMargin()
|androidExtensions {
| experimental = true
|}
|
""".trimMargin()
)
project.build("assembleDebug") {
@@ -9,7 +9,6 @@ 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
@@ -18,7 +17,6 @@ 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
@@ -375,43 +373,6 @@ internal fun forEachVariant(project: Project, action: (BaseVariant) -> Unit) {
}
}
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,
@@ -13,12 +13,10 @@ import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.internal.variant.TestVariantData
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.model.builder.KotlinAndroidExtensionModelBuilder
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
@@ -130,31 +128,30 @@ class AndroidSubplugin : KotlinCompilerPluginSupportPlugin {
)
)
kotlinCompilation.compileKotlinTaskProvider.configure {
it.androidLayoutResourceFiles.from(getLayoutDirectories(project, sourceSet.res.srcDirs))
it.androidLayoutResourceFiles.from(
sourceSet.res.sourceDirectoryTrees.layoutDirectories
)
}
}
addVariant(mainSourceSet)
val flavorSourceSets = androidExtension.productFlavors
.mapNotNull { androidExtension.sourceSets.findByName(it.name) }
for (sourceSet in flavorSourceSets) {
addVariant(sourceSet)
androidExtension.productFlavors.configureEach { flavor ->
androidExtension.sourceSets.findByName(flavor.name)?.let {
addVariant(it)
}
}
return project.provider { wrapPluginOptions(pluginOptions, "configuration") }
}
private fun getLayoutDirectories(project: Project, resDirectories: Iterable<File>): FileCollection {
fun isLayoutDirectory(file: File) = file.name == "layout" || file.name.startsWith("layout-")
return project.files(Callable {
resDirectories.flatMap { resDir ->
(resDir.listFiles(::isLayoutDirectory)).orEmpty().asList()
private val List<ConfigurableFileTree>.layoutDirectories
get() = map { tree ->
tree.matching {
it.include("**/layout/**")
it.include("**/layout-*/**")
}
})
}
}
private fun applyExperimental(
kotlinCompile: TaskProvider<out KotlinCompile>,
@@ -178,55 +175,39 @@ class AndroidSubplugin : KotlinCompilerPluginSupportPlugin {
val mainSourceSet = androidExtension.sourceSets.getByName("main")
pluginOptions += SubpluginOption("package", getApplicationPackage(androidExtension, project, mainSourceSet))
fun addVariant(name: String, resDirectories: FileCollection) {
fun addVariant(name: String, resDirectories: List<ConfigurableFileTree>) {
val optionValue = lazy {
buildString {
append(name)
append(';')
resDirectories.joinTo(this, separator = ";") { it.canonicalPath }
resDirectories.map { it.dir }.joinTo(this, separator = ";") { it.canonicalPath }
}
}
pluginOptions += CompositeSubpluginOption(
"variant", optionValue, listOf(
SubpluginOption("variantName", name),
// use INTERNAL option kind since the resources are tracked as sources (see below)
FilesSubpluginOption("resDirs", resDirectories)
FilesSubpluginOption(
"resDirs",
resDirectories.map { it.dir }
)
)
)
kotlinCompile.configure {
it.inputs.files(getLayoutDirectories(project, resDirectories))
.withPathSensitivity(PathSensitivity.RELATIVE)
.withPropertyName("androidExtensionLayoutsFrom$name")
.skipWhenEmpty(true)
.run {
if (GradleVersion.current() >= GradleVersion.version("6.8")) {
ignoreEmptyDirectories()
} else {
this!!
}
}
it.androidLayoutResourceFiles.from(resDirectories.layoutDirectories)
}
}
fun addSourceSetAsVariant(name: String) {
val sourceSet = androidExtension.sourceSets.findByName(name) ?: return
val srcDirs = sourceSet.res.srcDirs.toList()
val srcDirs = sourceSet.res.sourceDirectoryTrees
if (srcDirs.isNotEmpty()) {
addVariant(sourceSet.name, project.files(srcDirs))
addVariant(sourceSet.name, srcDirs)
}
}
val resDirectoriesForAllVariants = mutableListOf<FileCollection>()
forEachVariant(project) { variant ->
if (getTestedVariantData(variant) != null) return@forEachVariant
resDirectoriesForAllVariants += variant.getResDirectories()
}
val commonResDirectories = getCommonResDirectories(project, resDirectoriesForAllVariants)
addVariant("main", commonResDirectories)
addSourceSetAsVariant("main")
getVariantComponentNames(variantData)?.let { (variantName, flavorName, buildTypeName) ->
addSourceSetAsVariant(buildTypeName)
@@ -252,19 +233,6 @@ class AndroidSubplugin : KotlinCompilerPluginSupportPlugin {
private data class VariantComponentNames(val variantName: String, val flavorName: String, val buildTypeName: String)
private fun getCommonResDirectories(project: Project, resDirectories: List<FileCollection>): FileCollection {
val lazyFiles = lazy {
if (resDirectories.isEmpty()) {
emptySet<File>()
} else {
resDirectories.first().toMutableSet().apply {
resDirectories.drop(1).forEach { retainAll(it) }
}
}
}
return project.files(Callable { lazyFiles.value })
}
private fun getApplicationPackage(androidExtension: BaseExtension, project: Project, mainSourceSet: AndroidSourceSet): String {
val manifestFile = mainSourceSet.manifest.srcFile
val applicationPackage = getApplicationPackage(androidExtension, manifestFile)