All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.gradle.targets.android.Android25ProjectHandler.kt Maven / Gradle / Ivy

There is a newer version: 2.0.20-RC
Show newest version
/*
 * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.plugin

import com.android.build.gradle.*
import com.android.build.gradle.api.*
import com.android.build.gradle.tasks.MergeResources
import com.android.builder.model.SourceProvider
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.KaptTask
import org.jetbrains.kotlin.gradle.internal.KaptVariantData
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
import java.io.File
import java.util.concurrent.Callable

class Android25ProjectHandler(
    kotlinConfigurationTools: KotlinConfigurationTools
) : AbstractAndroidProjectHandler(kotlinConfigurationTools) {

    override fun forEachVariant(project: Project, action: (BaseVariant) -> Unit) {
        val androidExtension = project.extensions.getByName("android")
        when (androidExtension) {
            is AppExtension -> androidExtension.applicationVariants.all(action)
            is LibraryExtension -> {
                androidExtension.libraryVariants.all(action)
                if (androidExtension is FeatureExtension) {
                    androidExtension.featureVariants.all(action)
                }
            }
            is TestExtension -> androidExtension.applicationVariants.all(action)
        }
        if (androidExtension is TestedExtension) {
            androidExtension.testVariants.all(action)
            androidExtension.unitTestVariants.all(action)
        }
    }

    override fun wireKotlinTasks(
        project: Project,
        compilation: KotlinJvmAndroidCompilation,
        androidPlugin: BasePlugin,
        androidExt: BaseExtension,
        variantData: BaseVariant,
        javaTask: AbstractCompile,
        kotlinTask: KotlinCompile
    ) {
        val preJavaKotlinOutputFiles = mutableListOf().apply {
            add(kotlinTask.destinationDir)
            if (Kapt3GradleSubplugin.isEnabled(project)) {
                // Add Kapt3 output as well, since there's no SyncOutputTask with the new API
                val kaptClasssesDir = Kapt3GradleSubplugin.getKaptGeneratedClassesDir(project, getVariantName(variantData))
                add(kaptClasssesDir)
            }
        }
        val preJavaKotlinOutput = project.files(*preJavaKotlinOutputFiles.toTypedArray()).builtBy(kotlinTask)

        val preJavaClasspathKey = variantData.registerPreJavacGeneratedBytecode(preJavaKotlinOutput)
        kotlinTask.dependsOn(variantData.getSourceFolders(SourceKind.JAVA))

        kotlinTask.mapClasspath {
            val kotlinClasspath = variantData.getCompileClasspath(preJavaClasspathKey)
            kotlinClasspath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
        }

        // Find the classpath entries that comes from the tested variant and register it as the friend path, lazily
        compilation.testedVariantArtifacts.set(project.files(project.provider {
            variantData.getCompileClasspathArtifacts(preJavaClasspathKey)
                .filter { it.id.componentIdentifier is TestedComponentIdentifier }
                .map { it.file }
        }))

        kotlinTask.javaOutputDir = javaTask.destinationDir

        compilation.output.classesDirs.run {
            from(project.files(kotlinTask.taskData.destinationDir).builtBy(kotlinTask))
            from(project.files(project.provider { javaTask.destinationDir }).builtBy(javaTask))
        }
    }

    override fun getSourceProviders(variantData: BaseVariant): Iterable =
        variantData.sourceSets

    override fun getAllJavaSources(variantData: BaseVariant): Iterable =
        variantData.getSourceFolders(SourceKind.JAVA).map { it.dir }

    override fun getFlavorNames(variant: BaseVariant): List = variant.productFlavors.map { it.name }

    override fun getBuildTypeName(variant: BaseVariant): String = variant.buildType.name

    override fun getJavaTask(variantData: BaseVariant): AbstractCompile? {
        @Suppress("DEPRECATION") // There is always a Java compile task -- the deprecation was for Jack
        return variantData::class.java.methods.firstOrNull { it.name == "getJavaCompileProvider" }
            ?.invoke(variantData)
            ?.let { taskProvider ->
                // org.gradle.api.tasks.TaskProvider is added in Gradle 4.8
                taskProvider::class.java.methods.firstOrNull { it.name == "get" }?.invoke(taskProvider) as AbstractCompile?
            } ?: variantData.javaCompile
    }

    override fun addJavaSourceDirectoryToVariantModel(variantData: BaseVariant, javaSourceDirectory: File) =
        variantData.addJavaSourceFoldersToModel(javaSourceDirectory)

    override fun getResDirectories(variantData: BaseVariant): FileCollection {
        val getAllResourcesMethod =
            variantData::class.java.methods.firstOrNull { it.name == "getAllRawAndroidResources" }
        if (getAllResourcesMethod != null) {
            val allResources = getAllResourcesMethod.invoke(variantData) as FileCollection
            return allResources
        }

        val project = variantData.mergeResources.project
        return project.files(Callable { variantData.mergeResources?.computeResourceSetList0() ?: emptyList() })
    }

    // TODO the return type is actually `AbstractArchiveTask | TaskProvider`;
    //      change the signature once the Android Gradle plugin versions that don't support task providers are dropped
    override fun getLibraryOutputTask(variant: BaseVariant): Any? {
        val getPackageLibraryProvider = variant.javaClass.methods
            .find { it.name == "getPackageLibraryProvider" && it.parameterCount == 0 }

        return if (getPackageLibraryProvider != null) {
            @Suppress("UNCHECKED_CAST")
            getPackageLibraryProvider(variant) as TaskProvider
        } else {
            (variant as? LibraryVariant)?.packageLibrary
        }
    }

    override fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinJvmAndroidCompilation) {
        val project = compilation.target.project

        AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation)

        compilation.compileDependencyFiles = variant.compileConfiguration.apply {
            usesPlatformOf(compilation.target)
            project.addExtendsFromRelation(name, compilation.compileDependencyConfigurationName)
        }

        compilation.runtimeDependencyFiles = variant.runtimeConfiguration.apply {
            usesPlatformOf(compilation.target)
            project.addExtendsFromRelation(name, compilation.runtimeDependencyConfigurationName)
        }

        // TODO this code depends on the convention that is present in the Android plugin as there's no public API
        // We should request such API in the Android plugin
        val apiElementsConfigurationName = "${variant.name}ApiElements"
        val runtimeElementsConfigurationName = "${variant.name}RuntimeElements"

        // KT-29476, the Android *Elements configurations need Kotlin MPP dependencies:
        if (project.configurations.findByName(apiElementsConfigurationName) != null) {
            project.addExtendsFromRelation(apiElementsConfigurationName, compilation.apiConfigurationName)
        }
        if (project.configurations.findByName(runtimeElementsConfigurationName) != null) {
            project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.implementationConfigurationName)
            project.addExtendsFromRelation(runtimeElementsConfigurationName, compilation.runtimeOnlyConfigurationName)
        }

        listOf(apiElementsConfigurationName, runtimeElementsConfigurationName).forEach { outputConfigurationName ->
            project.configurations.findByName(outputConfigurationName)?.usesPlatformOf(compilation.target)
        }
    }

    private inner class KaptVariant(variantData: BaseVariant) : KaptVariantData(variantData) {
        override val name: String = getVariantName(variantData)
        override val sourceProviders: Iterable = getSourceProviders(variantData)
        override fun addJavaSourceFoldersToModel(generatedFilesDir: File) =
            addJavaSourceDirectoryToVariantModel(variantData, generatedFilesDir)

        override val annotationProcessorOptions: Map? =
            variantData.javaCompileOptions.annotationProcessorOptions.arguments

        override fun registerGeneratedJavaSource(
            project: Project,
            kaptTask: KaptTask,
            javaTask: AbstractCompile
        ) {
            val kaptSourceOutput = project.fileTree(kaptTask.destinationDir).builtBy(kaptTask)
            kaptSourceOutput.include("**/*.java")
            variantData.registerExternalAptJavaOutput(kaptSourceOutput)
            variantData.dataBindingDependencyArtifactsIfSupported?.let { kaptTask.dependsOn(it) }
        }

        override val annotationProcessorOptionProviders: List<*>
            get() = try {
                // Public API added in Android Gradle Plugin 3.2.0-alpha15:
                val apOptions = variantData.javaCompileOptions.annotationProcessorOptions
                apOptions.javaClass.getMethod("getCompilerArgumentProviders").invoke(apOptions) as List<*>
            } catch (e: NoSuchMethodException) {
                emptyList()
            }
    }

    //TODO A public API is expected for this purpose. Once it is available, use the public API
    private fun MergeResources.computeResourceSetList0(): List? {
        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
                }
                ?.flatten()

        } finally {
            computeResourceSetListMethod.isAccessible = oldIsAccessible
        }
    }

    //TODO once the Android plugin reaches its 3.0.0 release, consider compiling against it (remove the reflective call)
    private val BaseVariant.dataBindingDependencyArtifactsIfSupported: FileCollection?
        get() = this::class.java.methods
            .find { it.name == "getDataBindingDependencyArtifacts" }
            ?.also { it.isAccessible = true }
            ?.invoke(this) as? FileCollection

    override fun wrapVariantDataForKapt(variantData: BaseVariant): KaptVariantData =
        KaptVariant(variantData)
}

internal fun getTestedVariantData(variantData: BaseVariant): BaseVariant? = when (variantData) {
    is TestVariant -> variantData.testedVariant
    is UnitTestVariant -> variantData.testedVariant as? BaseVariant
    else -> null
}

internal fun getVariantName(variant: BaseVariant): String = variant.name




© 2015 - 2024 Weber Informatics LLC | Privacy Policy