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

kotlinx.kover.gradle.plugin.appliers.ProjectApplier.kt Maven / Gradle / Ivy

There is a newer version: 0.9.0-RC
Show newest version
/*
 * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */

package kotlinx.kover.gradle.plugin.appliers

import kotlinx.kover.gradle.plugin.appliers.reports.AndroidVariantApplier
import kotlinx.kover.gradle.plugin.appliers.reports.DefaultVariantApplier
import kotlinx.kover.gradle.plugin.appliers.reports.androidReports
import kotlinx.kover.gradle.plugin.commons.*
import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEPENDENCY_CONFIGURATION_NAME
import kotlinx.kover.gradle.plugin.dsl.KoverNames.PROJECT_EXTENSION_NAME
import kotlinx.kover.gradle.plugin.dsl.KoverNames.REPORT_EXTENSION_NAME
import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl
import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportExtensionImpl
import kotlinx.kover.gradle.plugin.locators.CompilationsListener
import kotlinx.kover.gradle.plugin.locators.CompilationsListenerManager
import kotlinx.kover.gradle.plugin.tasks.services.KoverAgentJarTask
import kotlinx.kover.gradle.plugin.tools.CoverageTool
import kotlinx.kover.gradle.plugin.tools.CoverageToolFactory
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.register

/**
 * Main Gradle Plugin applier of the project.
 */
internal class ProjectApplier(private val project: Project) {
    private lateinit var projectExtension: KoverProjectExtensionImpl
    private lateinit var reportExtension: KoverReportExtensionImpl

    private val androidAppliers: MutableMap = mutableMapOf()

    /**
     * The code executed right at the moment of applying of the plugin.
     */
    fun onApply() {
        val koverDependencies = project.configurations.create(DEPENDENCY_CONFIGURATION_NAME) {
            asBucket()
        }

        projectExtension = project.extensions.create(PROJECT_EXTENSION_NAME)
        reportExtension = project.extensions.create(REPORT_EXTENSION_NAME)

        val toolProvider = CoverageToolFactory.get(project, projectExtension)

        // DEPS
        val agentClasspath = project.configurations.create(JVM_AGENT_CONFIGURATION_NAME) {
            asTransitiveDependencies()
        }
        project.dependencies.add(JVM_AGENT_CONFIGURATION_NAME, toolProvider.map { tool -> tool.jvmAgentDependency })

        val reporterClasspath = project.configurations.create(JVM_REPORTER_CONFIGURATION_NAME) {
            asTransitiveDependencies()
        }
        project.dependencies.add(
            JVM_REPORTER_CONFIGURATION_NAME,
            toolProvider.map { tool -> tool.jvmReporterDependency })
        project.dependencies.add(
            JVM_REPORTER_CONFIGURATION_NAME,
            toolProvider.map { tool -> tool.jvmReporterExtraDependency })

        val defaultApplier = DefaultVariantApplier(
            project,
            koverDependencies,
            reporterClasspath,
            toolProvider
        )

        val instrData = collectInstrumentationData(toolProvider, agentClasspath)

        val listener = object : CompilationsListener {
            override fun onJvmCompilation(kit: JvmCompilationKit) {
                kit.tests.instrument(instrData)
                defaultApplier.applyCompilationKit(kit)
            }

            override fun onAndroidCompilations(kits: List) {
                kits.forEach { kit ->
                    kit.tests.instrument(instrData)
                    val applier =
                        AndroidVariantApplier(project, kit.buildVariant, koverDependencies, reporterClasspath, toolProvider)

                    val configs =
                        reportExtension.namedReports[kit.buildVariant] ?: project.androidReports(kit.buildVariant)

                    applier.applyConfig(configs, reportExtension.filters, reportExtension.verify)
                    applier.applyCompilationKit(kit)

                    androidAppliers[kit.buildVariant] = applier
                }
            }

            override fun onFinalize() {
                reportExtension.namedReports.keys.forEach { variantName ->
                    if (variantName !in androidAppliers) {
                        throw KoverIllegalConfigException("Build variant '$variantName' not found in project '${project.path}' - impossible to configure Android reports for it.\nAvailable variations: ${androidAppliers.keys}")
                    }
                }

                reportExtension.default.merged.forEach { variantName ->
                    val applier = androidAppliers[variantName] ?: throw KoverIllegalConfigException("Build variant '$variantName' not found in project '${project.path}' - impossible to merge default reports with its measurements.\n" +
                            "Available variations: ${androidAppliers.keys}")
                    defaultApplier.mergeWith(applier)
                }

                defaultApplier.applyConfig(reportExtension.default, reportExtension.filters, reportExtension.verify)
            }
        }

        CompilationsListenerManager.locate(project, projectExtension, listener)
    }

    /**
     * Collect all configured data, required for online instrumentation.
     */
    private fun collectInstrumentationData(
        toolProvider: Provider,
        agentClasspath: Configuration
    ): InstrumentationData {
        /*
        * Uses lazy jar search for the agent, because an eager search will cause a resolution at the configuration stage,
        * which may affect performance.
        * See https://github.com/Kotlin/kotlinx-kover/issues/235
        */
        val findAgentJarTask = project.tasks.register(FIND_JAR_TASK)
        findAgentJarTask.configure {
            // depends on agent classpath to resolve it in execute-time
            dependsOn(agentClasspath)

            this.tool.convention(toolProvider)
            this.agentJar.set(project.layout.buildDirectory.map { dir -> dir.file(agentFilePath(toolProvider.get().variant)) })
            this.agentClasspath.from(agentClasspath)
        }

        return InstrumentationData(
            findAgentJarTask,
            toolProvider,
            projectExtension.instrumentation.classes
        )
    }
}

/**
 * All configured data used in online instrumentation.
 */
internal class InstrumentationData(
    val findAgentJarTask: TaskProvider,
    val toolProvider: Provider,
    val excludedClasses: Set
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy