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) addVariant(mainSourceSet)
val flavorSourceSets = AndroidGradleWrapper.getProductFlavorsSourceSets(androidExtension).filterNotNull() val flavorSourceSets = androidExtension.productFlavors
.mapNotNull { androidExtension.sourceSets.findByName(it.name) }
for (sourceSet in flavorSourceSets) { for (sourceSet in flavorSourceSets) {
addVariant(sourceSet) addVariant(sourceSet)
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * 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.
* 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.
*/ */
package org.jetbrains.kotlin.gradle.plugin.android 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.BaseExtension
import com.android.build.gradle.BasePlugin import com.android.build.gradle.BasePlugin
import com.android.build.gradle.api.AndroidSourceSet 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.VariantManager
import com.android.build.gradle.internal.variant.BaseVariantData import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.builder.model.SourceProvider
import org.gradle.api.file.ConfigurableFileTree import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.internal.DefaultDomainObjectSet
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.util.PatternFilterable import java.io.File
import org.jetbrains.annotations.NotNull import java.lang.reflect.Field
import org.jetbrains.annotations.Nullable import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Modifier
class AndroidGradleWrapper { object AndroidGradleWrapper {
static def getRuntimeJars(BasePlugin basePlugin, BaseExtension baseExtension) { fun getRuntimeJars(basePlugin: BasePlugin, baseExtension: BaseExtension): Any? {
if (basePlugin.getMetaClass().getMetaMethod("getRuntimeJarList")) { return basePlugin("getRuntimeJarList") ?: baseExtension("getBootClasspath") ?: basePlugin("getBootClasspath")
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")
} }
def variantConfiguration = variantData.variantConfiguration fun isJackEnabled(variantData: Any): Boolean {
if (variantConfiguration.getMetaClass().getMetaMethod("getJavaCompileOptions")) { val variantConfiguration = variantData("getVariantConfiguration")
def javaCompileOptions = variantConfiguration.getJavaCompileOptions() return (variantConfiguration("getUseJack") ?: variantConfiguration("getJackOptions")("isEnabled")) as? Boolean ?: false
if (javaCompileOptions.getMetaClass().getMetaMethod("getAnnotationProcessorOptions")) {
return javaCompileOptions.getAnnotationProcessorOptions().getArguments()
}
} }
return null fun getJavaTask(variantData: Any): AbstractCompile? {
} return getJavaCompile(variantData)
@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())
} }
// generated sources private fun getJavaCompile(baseVariantData: Any): AbstractCompile? {
def getJavaSourcesMethod = variantData.getMetaClass().getMetaMethod("getJavaSources") return (baseVariantData["javaCompileTask"] ?: baseVariantData["javaCompilerTask"]) as? AbstractCompile
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())
}
} }
def getExtraSourcesMethod = variantData.getMetaClass().getMetaMethod("getExtraGeneratedSourceFolders") fun getAnnotationProcessorOptionsFromAndroidVariant(variantData: Any?): Map<String, String>? {
if (getExtraSourcesMethod.returnType.metaClass == List.metaClass) { if (variantData !is BaseVariantData<*>) {
def folders = variantData.getExtraGeneratedSourceFolders() throw IllegalArgumentException("BaseVariantData instance expected")
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
} }
else if (localDep.metaClass.getMetaMethod("jarFile") != null) {
// android tools < 2.2 val compileOptions = variantData("getVariantConfiguration")("getJavaCompileOptions") ?: return null
jarToLibraryArtifactMap[localDep.jarFile] = bundle
@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) { val libraries = getVariantLibraryDependencies(variantData) ?: return jarToLibraryArtifactMap
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
}
}
@Nullable for (lib in libraries) {
private static Iterable<Object> getVariantLibraryDependencies(BaseVariantData variantData) { lib ?: continue
def variantDependency = variantData.variantDependency val bundle = getLibraryArtifactFile(lib) ?: continue
val libJarFile = lib("getJarFile") as? File ?: continue
if (variantDependency.metaClass.getMetaMethod("getAndroidDependencies") != null) { jarToLibraryArtifactMap[libJarFile] = bundle
// android tools < 2.2
return variantDependency.getAndroidDependencies() as Iterable<Object> @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") private fun getLibraryArtifactFile(lib: Any): File? {
if (getCompileDependencies != null) { return if (lib.javaClass.name == "com.android.builder.dependency.level2.AndroidDependency") {
def compileDependencies = variantDependency.getCompileDependencies() // android tools >= 2.3
if (compileDependencies.metaClass.getMetaMethod("getDirectAndroidDependencies") != null) { lib("getArtifactFile") as? File
return compileDependencies.getDirectAndroidDependencies() // android >= 2.3 } else {
} else if (compileDependencies.metaClass.getMetaMethod("getAndroidDependencies")) { // android tools <= 2.2
return compileDependencies.getAndroidDependencies() // android 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
}
} }