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

com.careem.mockingbird.MockingbirdPluginKspDelegate.kt Maven / Gradle / Ivy

Go to download

A Koltin multiplatform library that provides an easier way to mock and write unit tests for a multiplatform project

The newest version!
/*
 * Copyright Careem, an Uber Technologies Inc. company
 *
 * 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 com.careem.mockingbird

import com.careem.mockingbird.mockingbird_compiler.BuildConfig
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget

class MockingbirdPluginKspDelegate {
    private val targetConfigurationFactory: TargetConfigurationFactory by lazy { TargetConfigurationFactory() }

    fun apply(target: Project) {
        target.afterEvaluate {
            if (!hasKspPlugin(this)) {
                logger.info("KSP plugin not found, falling back to legacy plugin")
                return@afterEvaluate
            }

            // TODO revisit this whole logic once Ksp will be able to generate code for commonTest ( not the case today see: https://github.com/google/ksp/issues/567 )
            // The following code will workaround the current ksp limitations doing the follow:
            // 1. To avoid running ksp for each target the plugin will run ksp for a single target jvm will be prefered if the target is available otherwise it will pick the first target
            // 2. Since current multiplatform ksp implementation can target specific targets, mocks generated will not be resolved
            //    in commonTest. The plugin will add this the code generated at point 1 as source set for common test so that
            //    this code will be available for each platform and resolvable by the IDE

            val kotlin: KotlinProjectExtension = project.kotlinExtension

            val jvmTarget = kotlin.targets.firstOrNull { it.platformType == KotlinPlatformType.jvm }
                ?: kotlin.targets.firstOrNull { it.platformType == KotlinPlatformType.androidJvm }
                ?: throw GradleException("Could not find JVM or Android target")

            val targetConfiguration = targetConfigurationFactory.get(jvmTarget)
            addKSPDependency(project, targetConfiguration.getKspConfiguration())

            project.afterEvaluate {
                val commonTest = kotlin.sourceSets.getByName("commonTest")
                val platformSourceSet = kotlin.sourceSets.getByName(targetConfiguration.getSourceSet())

                addRuntimeDependencies(commonTest)

                val targetGeneratedSources = targetConfiguration.getSrcDir(this)


                // Workaround, we used ksp to generate for a specific target, we need to add this
                // generated code to commonTest and remove the target we used just for generation, this
                // target will get the generated code through the commonTest source set
                val filteredSourceSet =
                    platformSourceSet.kotlin.srcDirs.filter { it.path !in targetGeneratedSources }

                // Adding ksp generated code as source set for commonTest
                commonTest.kotlin.srcDirs(targetGeneratedSources)
                platformSourceSet.kotlin.setSrcDirs(filteredSourceSet)

                // Turns out that the simple fact of calling `project.tasks.withType>()`
                // breaks KSP if it isn't set in a deep level of afterEvaluate
                afterEvaluate {
                    afterEvaluate {
                        // Adding KSP JVM as a dependency to all Kotlin compilations
                        project.tasks.withType>().all {
                            if (name.startsWith("compile") && name.contains("TestKotlin")) {
                                dependsOn(targetConfiguration.getKspTask())
                            }
                        }
                    }
                }
            }
        }
    }

    private fun addKSPDependency(project: Project, kspConfiguration: String) {
        project.dependencies {
            add(
                kspConfiguration,
                "com.careem.mockingbird:mockingbird-processor:${BuildConfig.VERSION}"
            )
        }
    }

    private fun addRuntimeDependencies(sourceSet: KotlinSourceSet) {
        sourceSet.dependencies {
            implementation("com.careem.mockingbird:mockingbird:${BuildConfig.VERSION}")
        }
    }

    private fun hasKspPlugin(target: Project): Boolean =
        target.plugins.findPlugin("com.google.devtools.ksp") != null
}

private val KotlinProjectExtension.targets: Iterable
    get() = when (this) {
        is KotlinSingleTargetExtension<*> -> listOf(this.target)
        is KotlinMultiplatformExtension -> targets
        else -> error("Unexpected 'kotlin' extension $this")
    }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy