Fix binary incompatibility with Android Gradle plugin 3.2.0-alpha06

Remove a non-reflective usage of the ResourceSet class, which has been
moved to a different package between versions. Access it through
reflection instead. This is a workaround. We can introduce a proper
fix once we have a public API in the Android plugin.

The test case that could detect the compatibility issue is
`testAndroidExtensionsManyVariants` (it uses experimental Android
extensions), but we did not run it with 3.2.0-alpha6. This commit
adds a test class with the same tests for 3.2.0-alpha6.

Issue #KT-23192 Fixed
This commit is contained in:
Sergey Igushkin
2018-03-13 20:56:59 +03:00
parent 3a7a34cc4e
commit 0039be6972
3 changed files with 26 additions and 11 deletions
@@ -13,7 +13,15 @@ import java.io.File
class KotlinAndroidGradleIT : AbstractKotlinAndroidGradleTests(gradleVersion = GradleVersionRequired.AtLeast("3.4"), androidGradlePluginVersion = "2.3.0") class KotlinAndroidGradleIT : AbstractKotlinAndroidGradleTests(gradleVersion = GradleVersionRequired.AtLeast("3.4"), androidGradlePluginVersion = "2.3.0")
class KotlinAndroidWithJackGradleIT : AbstractKotlinAndroidWithJackGradleTests(androidGradlePluginVersion = "2.3.+") class KotlinAndroidWithJackGradleIT : AbstractKotlinAndroidWithJackGradleTests(androidGradlePluginVersion = "2.3.+")
class KotlinAndroid30GradleIT : AbstractKotlinAndroidGradleTests(gradleVersion = GradleVersionRequired.AtLeast("4.1"), androidGradlePluginVersion = "3.0.0-beta1") { // TODO If we there is a way to fetch the latest Android plugin version, test against the latest version
class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtLeast("4.5"), "3.2.0-alpha06")
class KotlinAndroid30GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtLeast("4.1"), "3.0.0")
abstract class KotlinAndroid3GradleIT(
gradleVersionRequired: GradleVersionRequired,
androidGradlePluginVersion: String
) : AbstractKotlinAndroidGradleTests(gradleVersionRequired, androidGradlePluginVersion) {
@Test @Test
fun testApplyWithFeaturePlugin() { fun testApplyWithFeaturePlugin() {
@@ -314,5 +322,4 @@ abstract class AbstractKotlinAndroidWithJackGradleTests(
assertContains("Kotlin Gradle plugin does not support the deprecated Jack toolchain") assertContains("Kotlin Gradle plugin does not support the deprecated Jack toolchain")
} }
} }
} }
@@ -13,7 +13,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() jcenter()
maven { url 'https://maven.google.com' } maven { url 'https://maven.google.com' }
} }
} }
@@ -4,7 +4,6 @@ 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 com.android.build.gradle.tasks.MergeResources
import com.android.builder.model.SourceProvider import com.android.builder.model.SourceProvider
import com.android.ide.common.res2.ResourceSet
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
@@ -120,7 +119,7 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
} }
override fun getResDirectories(variantData: BaseVariant): List<File> { override fun getResDirectories(variantData: BaseVariant): List<File> {
return variantData.mergeResources?.computeResourceSetList0()?.flatMap { it.sourceFiles } ?: emptyList() return variantData.mergeResources?.computeResourceSetList0() ?: emptyList()
} }
private inner class KaptVariant(variantData: BaseVariant) : KaptVariantData<BaseVariant>(variantData) { private inner class KaptVariant(variantData: BaseVariant) : KaptVariantData<BaseVariant>(variantData) {
@@ -143,17 +142,26 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
} }
} }
//TODO once the Android plugin reaches its 3.0.0 release, consider compiling against it (remove the reflective call) //TODO A public API is expected for this purpose. Once it is available, use the public API
//TODO this is a private API for now private fun MergeResources.computeResourceSetList0(): List<File>? {
private fun MergeResources.computeResourceSetList0(): List<ResourceSet>? {
val computeResourceSetListMethod = MergeResources::class.java.declaredMethods val computeResourceSetListMethod = MergeResources::class.java.declaredMethods
.firstOrNull { it.name == "computeResourceSetList" && it.parameterCount == 0 } ?: return null .firstOrNull { it.name == "computeResourceSetList" && it.parameterCount == 0 } ?: return null
val oldIsAccessible = computeResourceSetListMethod.isAccessible val oldIsAccessible = computeResourceSetListMethod.isAccessible
try { try {
computeResourceSetListMethod.isAccessible = true computeResourceSetListMethod.isAccessible = true
@Suppress("UNCHECKED_CAST")
return computeResourceSetListMethod.invoke(this) as? List<ResourceSet> 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 { } finally {
computeResourceSetListMethod.isAccessible = oldIsAccessible computeResourceSetListMethod.isAccessible = oldIsAccessible
} }