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( project.projectDir.resolve("app/build.gradle").appendText(
""" """
|
|androidExtensions {
| experimental = true
| |
} |androidExtensions {
""".trimMargin() | experimental = true
|}
|
""".trimMargin()
) )
project.build("assembleDebug") { project.build("assembleDebug") {
@@ -9,7 +9,6 @@ package org.jetbrains.kotlin.gradle.plugin
import com.android.build.api.attributes.BuildTypeAttr import com.android.build.api.attributes.BuildTypeAttr
import com.android.build.gradle.* import com.android.build.gradle.*
import com.android.build.gradle.api.* import com.android.build.gradle.api.*
import com.android.build.gradle.tasks.MergeResources
import org.gradle.api.InvalidUserCodeException import org.gradle.api.InvalidUserCodeException
import org.gradle.api.NamedDomainObjectCollection import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.Project 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.Attribute
import org.gradle.api.attributes.Category import org.gradle.api.attributes.Category
import org.gradle.api.file.ConfigurableFileTree import org.gradle.api.file.ConfigurableFileTree
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.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. */ /** Filter for the AGP test variant classpath artifacts. */
class AndroidTestedVariantArtifactsFilter( class AndroidTestedVariantArtifactsFilter(
private val artifactCollection: ArtifactCollection, private val artifactCollection: ArtifactCollection,
@@ -13,12 +13,10 @@ import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.internal.variant.TestVariantData import com.android.build.gradle.internal.variant.TestVariantData
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project 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.provider.Provider
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry 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.model.builder.KotlinAndroidExtensionModelBuilder
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
@@ -130,31 +128,30 @@ class AndroidSubplugin : KotlinCompilerPluginSupportPlugin {
) )
) )
kotlinCompilation.compileKotlinTaskProvider.configure { kotlinCompilation.compileKotlinTaskProvider.configure {
it.androidLayoutResourceFiles.from(getLayoutDirectories(project, sourceSet.res.srcDirs)) it.androidLayoutResourceFiles.from(
sourceSet.res.sourceDirectoryTrees.layoutDirectories
)
} }
} }
addVariant(mainSourceSet) addVariant(mainSourceSet)
val flavorSourceSets = androidExtension.productFlavors androidExtension.productFlavors.configureEach { flavor ->
.mapNotNull { androidExtension.sourceSets.findByName(it.name) } androidExtension.sourceSets.findByName(flavor.name)?.let {
addVariant(it)
for (sourceSet in flavorSourceSets) { }
addVariant(sourceSet)
} }
return project.provider { wrapPluginOptions(pluginOptions, "configuration") } return project.provider { wrapPluginOptions(pluginOptions, "configuration") }
} }
private fun getLayoutDirectories(project: Project, resDirectories: Iterable<File>): FileCollection { private val List<ConfigurableFileTree>.layoutDirectories
fun isLayoutDirectory(file: File) = file.name == "layout" || file.name.startsWith("layout-") get() = map { tree ->
tree.matching {
return project.files(Callable { it.include("**/layout/**")
resDirectories.flatMap { resDir -> it.include("**/layout-*/**")
(resDir.listFiles(::isLayoutDirectory)).orEmpty().asList()
} }
}) }
}
private fun applyExperimental( private fun applyExperimental(
kotlinCompile: TaskProvider<out KotlinCompile>, kotlinCompile: TaskProvider<out KotlinCompile>,
@@ -178,55 +175,39 @@ class AndroidSubplugin : KotlinCompilerPluginSupportPlugin {
val mainSourceSet = androidExtension.sourceSets.getByName("main") val mainSourceSet = androidExtension.sourceSets.getByName("main")
pluginOptions += SubpluginOption("package", getApplicationPackage(androidExtension, project, mainSourceSet)) pluginOptions += SubpluginOption("package", getApplicationPackage(androidExtension, project, mainSourceSet))
fun addVariant(name: String, resDirectories: FileCollection) { fun addVariant(name: String, resDirectories: List<ConfigurableFileTree>) {
val optionValue = lazy { val optionValue = lazy {
buildString { buildString {
append(name) append(name)
append(';') append(';')
resDirectories.joinTo(this, separator = ";") { it.canonicalPath } resDirectories.map { it.dir }.joinTo(this, separator = ";") { it.canonicalPath }
} }
} }
pluginOptions += CompositeSubpluginOption( pluginOptions += CompositeSubpluginOption(
"variant", optionValue, listOf( "variant", optionValue, listOf(
SubpluginOption("variantName", name), SubpluginOption("variantName", name),
// use INTERNAL option kind since the resources are tracked as sources (see below) // use INTERNAL option kind since the resources are tracked as sources (see below)
FilesSubpluginOption("resDirs", resDirectories) FilesSubpluginOption(
"resDirs",
resDirectories.map { it.dir }
)
) )
) )
kotlinCompile.configure { kotlinCompile.configure {
it.inputs.files(getLayoutDirectories(project, resDirectories)) it.androidLayoutResourceFiles.from(resDirectories.layoutDirectories)
.withPathSensitivity(PathSensitivity.RELATIVE)
.withPropertyName("androidExtensionLayoutsFrom$name")
.skipWhenEmpty(true)
.run {
if (GradleVersion.current() >= GradleVersion.version("6.8")) {
ignoreEmptyDirectories()
} else {
this!!
}
}
} }
} }
fun addSourceSetAsVariant(name: String) { fun addSourceSetAsVariant(name: String) {
val sourceSet = androidExtension.sourceSets.findByName(name) ?: return val sourceSet = androidExtension.sourceSets.findByName(name) ?: return
val srcDirs = sourceSet.res.srcDirs.toList() val srcDirs = sourceSet.res.sourceDirectoryTrees
if (srcDirs.isNotEmpty()) { if (srcDirs.isNotEmpty()) {
addVariant(sourceSet.name, project.files(srcDirs)) addVariant(sourceSet.name, srcDirs)
} }
} }
val resDirectoriesForAllVariants = mutableListOf<FileCollection>() addSourceSetAsVariant("main")
forEachVariant(project) { variant ->
if (getTestedVariantData(variant) != null) return@forEachVariant
resDirectoriesForAllVariants += variant.getResDirectories()
}
val commonResDirectories = getCommonResDirectories(project, resDirectoriesForAllVariants)
addVariant("main", commonResDirectories)
getVariantComponentNames(variantData)?.let { (variantName, flavorName, buildTypeName) -> getVariantComponentNames(variantData)?.let { (variantName, flavorName, buildTypeName) ->
addSourceSetAsVariant(buildTypeName) addSourceSetAsVariant(buildTypeName)
@@ -252,19 +233,6 @@ class AndroidSubplugin : KotlinCompilerPluginSupportPlugin {
private data class VariantComponentNames(val variantName: String, val flavorName: String, val buildTypeName: String) 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 { private fun getApplicationPackage(androidExtension: BaseExtension, project: Project, mainSourceSet: AndroidSourceSet): String {
val manifestFile = mainSourceSet.manifest.srcFile val manifestFile = mainSourceSet.manifest.srcFile
val applicationPackage = getApplicationPackage(androidExtension, manifestFile) val applicationPackage = getApplicationPackage(androidExtension, manifestFile)