G2K: Rewrite AndroidGradleWrapper in Kotlin

This commit is contained in:
Yan Zhulanow
2018-05-16 22:54:12 +03:00
parent 9aa02c7ae4
commit 5eedbf4177
2 changed files with 176 additions and 245 deletions
@@ -128,7 +128,9 @@ class AndroidSubplugin : KotlinGradleSubplugin<KotlinCompile> {
addVariant(mainSourceSet)
val flavorSourceSets = AndroidGradleWrapper.getProductFlavorsSourceSets(androidExtension).filterNotNull()
val flavorSourceSets = androidExtension.productFlavors
.mapNotNull { androidExtension.sourceSets.findByName(it.name) }
for (sourceSet in flavorSourceSets) {
addVariant(sourceSet)
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.android
@@ -19,259 +8,199 @@ package org.jetbrains.kotlin.gradle.plugin.android
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.ApkVariant
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.internal.VariantManager
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.builder.model.SourceProvider
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.internal.DefaultDomainObjectSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.util.PatternFilterable
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import java.io.File
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Modifier
class AndroidGradleWrapper {
static def getRuntimeJars(BasePlugin basePlugin, BaseExtension baseExtension) {
if (basePlugin.getMetaClass().getMetaMethod("getRuntimeJarList")) {
return basePlugin.getRuntimeJarList()
}
else if (baseExtension.getMetaClass().getMetaMethod("getBootClasspath")) {
return baseExtension.getBootClasspath()
}
else {
return basePlugin.getBootClasspath()
}
}
static def srcDir(Object androidSourceSet, Object kotlinDirSet) {
androidSourceSet.getJava().srcDir(kotlinDirSet)
}
static PatternFilterable getResourceFilter(Object androidSourceSet) {
def resources = androidSourceSet.getResources()
if (resources != null) {
return resources.getFilter()
}
return null
}
@NotNull
static String getVariantName(Object variant) {
return variant.getBuildType().getName()
}
@Nullable
private static getJackOptions(@NotNull Object variantData) {
def variantConfiguration = variantData.variantConfiguration
if (variantConfiguration.getMetaClass().getMetaMethod("getJackOptions")) {
return variantConfiguration.getJackOptions()
}
return null
}
static boolean isJackEnabled(@NotNull Object variantData) {
return getJackOptions(variantData)?.enabled ?: false
}
@Nullable
static AbstractCompile getJavaTask(@NotNull Object variantData) {
return getJavaCompile(variantData)
}
@Nullable
private static AbstractCompile getJavacTask(@NotNull Object baseVariantData) {
if (baseVariantData.getMetaClass().getMetaProperty("javacTask")) {
return baseVariantData.javacTask
}
return null
}
@Nullable
private static AbstractCompile getJavaCompile(@NotNull Object baseVariantData) {
if (baseVariantData.getMetaClass().getMetaProperty("javaCompileTask")) {
return baseVariantData.javaCompileTask
}
else if (baseVariantData.getMetaClass().getMetaProperty("javaCompilerTask")) {
return baseVariantData.javaCompilerTask
}
return null
}
@NotNull
static Set<File> getJavaSrcDirs(Object androidSourceSet) {
return androidSourceSet.getJava().getSrcDirs()
}
static def setNoJdk(Object kotlinOptionsExtension) {
kotlinOptionsExtension.noJdk = true
}
@NotNull
static List<String> getProductFlavorsNames(ApkVariant variant) {
return variant.getProductFlavors().iterator().collect { it.getName() }
}
@NotNull
static List<AndroidSourceSet> getProductFlavorsSourceSets(BaseExtension extension) {
return extension.productFlavors.iterator().collect { extension.sourceSets.findByName(it.name) }
}
@Nullable
static Map<String, String> getAnnotationProcessorOptionsFromAndroidVariant(@Nullable Object variantData) {
if (!(variantData instanceof BaseVariantData)) {
throw new IllegalArgumentException("BaseVariantData instance expected")
object AndroidGradleWrapper {
fun getRuntimeJars(basePlugin: BasePlugin, baseExtension: BaseExtension): Any? {
return basePlugin("getRuntimeJarList") ?: baseExtension("getBootClasspath") ?: basePlugin("getBootClasspath")
}
def variantConfiguration = variantData.variantConfiguration
if (variantConfiguration.getMetaClass().getMetaMethod("getJavaCompileOptions")) {
def javaCompileOptions = variantConfiguration.getJavaCompileOptions()
if (javaCompileOptions.getMetaClass().getMetaMethod("getAnnotationProcessorOptions")) {
return javaCompileOptions.getAnnotationProcessorOptions().getArguments()
}
fun isJackEnabled(variantData: Any): Boolean {
val variantConfiguration = variantData("getVariantConfiguration")
return (variantConfiguration("getUseJack") ?: variantConfiguration("getJackOptions")("isEnabled")) as? Boolean ?: false
}
return null
}
@NotNull
static DefaultDomainObjectSet<TestVariant> getTestVariants(BaseExtension extension) {
if (extension.getMetaClass().getMetaMethod("getTestVariants")) {
return extension.getTestVariants()
}
return Collections.emptyList()
}
@NotNull
static List<File> getRClassFolder(BaseVariant variant) {
def list = new ArrayList<File>()
if (variant.getMetaClass().getMetaMethod("getProcessResources")) {
list.add(variant.getProcessResources().getSourceOutputDir())
}
else {
for (Object variantOutput : variant.getOutputs()) {
list.add(variantOutput.processResources.sourceOutputDir)
}
}
return list
}
static VariantManager getVariantDataManager(BasePlugin plugin) {
return plugin.getVariantManager()
}
static List<File> getJavaSources(BaseVariantData variantData) {
def result = new LinkedHashSet<File>()
// user sources
List<SourceProvider> providers = variantData.variantConfiguration.getSortedSourceProviders()
for (SourceProvider provider : providers) {
result.addAll((provider as AndroidSourceSet).getJava().getSrcDirs())
fun getJavaTask(variantData: Any): AbstractCompile? {
return getJavaCompile(variantData)
}
// generated sources
def getJavaSourcesMethod = variantData.getMetaClass().getMetaMethod("getJavaSources")
if (getJavaSourcesMethod.returnType.metaClass == Object[].metaClass) {
result.addAll(variantData.getJavaSources().findAll { it instanceof File })
}
else if (getJavaSourcesMethod.returnType.metaClass == List.metaClass) {
def fileTrees = variantData.getJavaSources().findAll { it instanceof ConfigurableFileTree }
result.addAll(fileTrees.collect { it.getDir() })
}
else {
if (variantData.scope.getGenerateRClassTask() != null) {
result.add(variantData.scope.getRClassSourceOutputDir())
}
if (variantData.scope.getGenerateBuildConfigTask() != null) {
result.add(variantData.scope.getBuildConfigSourceOutputDir())
}
if (variantData.scope.getAidlCompileTask() != null) {
result.add(variantData.scope.getAidlSourceOutputDir())
}
if (variantData.scope.getGlobalScope().getExtension().getDataBinding().isEnabled()) {
result.add(variantData.scope.getClassOutputForDataBinding())
}
if (!variantData.variantConfiguration.getRenderscriptNdkModeEnabled()
&& variantData.scope.getRenderscriptCompileTask() != null) {
result.add(variantData.scope.getRenderscriptSourceOutputDir())
}
private fun getJavaCompile(baseVariantData: Any): AbstractCompile? {
return (baseVariantData["javaCompileTask"] ?: baseVariantData["javaCompilerTask"]) as? AbstractCompile
}
def getExtraSourcesMethod = variantData.getMetaClass().getMetaMethod("getExtraGeneratedSourceFolders")
if (getExtraSourcesMethod.returnType.metaClass == List.metaClass) {
def folders = variantData.getExtraGeneratedSourceFolders()
if (folders != null) {
result.addAll(folders)
}
}
return result.toList()
}
@NotNull
static Map<File, File> getJarToAarMapping(BaseVariantData variantData) {
def jarToLibraryArtifactMap = new HashMap<File, File>()
def libraries = getVariantLibraryDependencies(variantData)
if (libraries == null) return jarToLibraryArtifactMap
for (lib in libraries) {
Object bundle = getLibraryArtifactFile(lib)
jarToLibraryArtifactMap[lib.jarFile] = bundle
// local dependencies are detected as changed by gradle, because they are seem to be
// rewritten every time when bundle changes
// when local dep will actually change, record for bundle will be removed from registry
for (localDep in lib.localJars) {
if (localDep instanceof File) {
// android tools 2.2
jarToLibraryArtifactMap[localDep] = bundle
fun getAnnotationProcessorOptionsFromAndroidVariant(variantData: Any?): Map<String, String>? {
if (variantData !is BaseVariantData<*>) {
throw IllegalArgumentException("BaseVariantData instance expected")
}
else if (localDep.metaClass.getMetaMethod("jarFile") != null) {
// android tools < 2.2
jarToLibraryArtifactMap[localDep.jarFile] = bundle
val compileOptions = variantData("getVariantConfiguration")("getJavaCompileOptions") ?: return null
@Suppress("UNCHECKED_CAST")
return compileOptions("getAnnotationProcessorOptions")("getArguments") as? Map<String, String>
}
fun getVariantDataManager(plugin: BasePlugin): VariantManager {
return plugin("getVariantManager") as VariantManager
}
fun getJavaSources(variantData: BaseVariantData<*>): List<File> {
val result = LinkedHashSet<File>()
// user sources
for (provider in variantData.variantConfiguration.sortedSourceProviders) {
result.addAll((provider as AndroidSourceSet).java.srcDirs)
}
}
// generated sources
val javaSources = variantData("getJavaSources")
if (javaSources is Array<*>) {
result += javaSources.filterIsInstance<File>()
} else if (javaSources is List<*>) {
val fileTrees = javaSources.filterIsInstance<ConfigurableFileTree>()
result += fileTrees.map { it.dir }
} else {
if (variantData.scope.generateRClassTask != null) {
result += variantData.scope.rClassSourceOutputDir
}
if (variantData.scope.generateBuildConfigTask != null) {
result += variantData.scope.buildConfigSourceOutputDir
}
if (variantData.scope.aidlCompileTask != null) {
result += variantData.scope.aidlSourceOutputDir
}
if (variantData.scope.globalScope.extension.dataBinding.isEnabled) {
result += variantData.scope.classOutputForDataBinding
}
if (!variantData.variantConfiguration.renderscriptNdkModeEnabled && variantData.scope.renderscriptCompileTask != null) {
result += variantData.scope.renderscriptSourceOutputDir
}
}
val extraSources = variantData("getExtraGeneratedSourceFolders") as? List<*>
if (extraSources != null) {
result += extraSources.filterIsInstance<File>()
}
return result.toList()
}
return jarToLibraryArtifactMap
}
fun getJarToAarMapping(variantData: BaseVariantData<*>): Map<File, File> {
val jarToLibraryArtifactMap = hashMapOf<File, File>()
private static def getLibraryArtifactFile(Object lib) {
if (lib.class.name == "com.android.builder.dependency.level2.AndroidDependency") {
// android tools >= 2.3
return lib.artifactFile
} else {
// android tools <= 2.2
return lib.bundle
}
}
val libraries = getVariantLibraryDependencies(variantData) ?: return jarToLibraryArtifactMap
@Nullable
private static Iterable<Object> getVariantLibraryDependencies(BaseVariantData variantData) {
def variantDependency = variantData.variantDependency
for (lib in libraries) {
lib ?: continue
val bundle = getLibraryArtifactFile(lib) ?: continue
val libJarFile = lib("getJarFile") as? File ?: continue
if (variantDependency.metaClass.getMetaMethod("getAndroidDependencies") != null) {
// android tools < 2.2
return variantDependency.getAndroidDependencies() as Iterable<Object>
jarToLibraryArtifactMap[libJarFile] = bundle
@Suppress("UNCHECKED_CAST")
val localJarsForLib = lib("getLocalJars") as? List<*> ?: emptyList<Any>()
// local dependencies are detected as changed by gradle, because they are seem to be
// rewritten every time when bundle changes
// when local dep will actually change, record for bundle will be removed from registry
for (localDep in localJarsForLib) {
if (localDep is File) {
// android tools 2.2
jarToLibraryArtifactMap[localDep] = bundle
} else {
// android tools < 2.2
val jarFile = localDep("getJarFile") as? File ?: continue
jarToLibraryArtifactMap[jarFile] = bundle
}
}
}
return jarToLibraryArtifactMap
}
def getCompileDependencies = variantDependency.metaClass.getMetaMethod("getCompileDependencies")
if (getCompileDependencies != null) {
def compileDependencies = variantDependency.getCompileDependencies()
if (compileDependencies.metaClass.getMetaMethod("getDirectAndroidDependencies") != null) {
return compileDependencies.getDirectAndroidDependencies() // android >= 2.3
} else if (compileDependencies.metaClass.getMetaMethod("getAndroidDependencies")) {
return compileDependencies.getAndroidDependencies() // android 2.2
}
private fun getLibraryArtifactFile(lib: Any): File? {
return if (lib.javaClass.name == "com.android.builder.dependency.level2.AndroidDependency") {
// android tools >= 2.3
lib("getArtifactFile") as? File
} else {
// android tools <= 2.2
lib("getBundle") as? File
}
}
return null
}
private fun getVariantLibraryDependencies(variantData: BaseVariantData<*>): Iterable<*>? {
val variantDependency = variantData.variantDependency
variantDependency("getAndroidDependencies")?.let {
// android tools < 2.2
return it as Iterable<*>
}
val compileDependencies = variantDependency("getCompileDependencies") ?: return null
val result = compileDependencies("getDirectAndroidDependencies") // android >= 2.3
?: compileDependencies("getAndroidDependencies") // android 2.2
return result as? Iterable<*>
}
}
private operator fun Any?.invoke(methodName: String): Any? {
if (this == null) {
return null
}
fun Array<Method>.findMethod() =
singleOrNull { it.name == methodName && it.parameterCount == 0 && !Modifier.isStatic(it.modifiers) }
val clazz = this::class.java
val methodToInvoke = clazz.declaredMethods.findMethod() ?: clazz.methods.findMethod() ?: return null
val oldIsAccessible = methodToInvoke.isAccessible
try {
methodToInvoke.isAccessible = true
return methodToInvoke.invoke(this)
} catch (e: InvocationTargetException) {
throw e.targetException
} catch (e: Throwable) {
throw RuntimeException("Can't invoke method '$methodName()' on $this", e)
} finally {
methodToInvoke.isAccessible = oldIsAccessible
}
}
private operator fun Any?.get(name: String): Any? {
if (this == null) {
return null
}
this.invoke("get" + name.capitalize())?.let { return it }
fun Array<Field>.findField() = singleOrNull { it.name == name && !Modifier.isStatic(it.modifiers) }
val clazz = this::class.java
val fieldToInvoke = clazz.declaredFields.findField() ?: clazz.fields.findField() ?: return null
val oldIsAccessible = fieldToInvoke.isAccessible
try {
fieldToInvoke.isAccessible = true
return fieldToInvoke.get(this)
} catch (e: InvocationTargetException) {
throw e.targetException
} catch (e: Throwable) {
throw RuntimeException("Couldn't get value of field '$name' in $this", e)
} finally {
fieldToInvoke.isAccessible = oldIsAccessible
}
}