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

family.amma.deep_link.gradle_plugin.GenerateDeepLinksTask.kt Maven / Gradle / Ivy

package family.amma.deep_link.gradle_plugin

import family.amma.deep_link.generator.main.generateDeepLinks
import family.amma.deep_link.generator.main.GeneratorParams
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.work.ChangeType
import org.gradle.work.Incremental
import org.gradle.work.InputChanges
import java.io.File

@CacheableTask
abstract class GenerateDeepLinksTask : DefaultTask() {
    /** Package of R file. */
    @get:Input
    abstract val rFilePackage: Property

    @get:Input
    abstract val applicationId: Property

    @get:PathSensitive(PathSensitivity.ABSOLUTE)
    @get:Incremental
    @get:InputFiles
    abstract val navigationFiles: ConfigurableFileCollection

    @get:Input
    lateinit var generatorParams: GeneratorParams

    @get:OutputDirectory
    lateinit var buildDir: File

    @get:OutputDirectory
    lateinit var outputDir: File

    @get:OutputDirectory
    lateinit var pluginDir: File

    @TaskAction
    fun execute(inputChanges: InputChanges) {
        if (inputChanges.isIncremental) {
            doIncrementalTaskAction(
                changedInputs = inputChanges.getFileChanges(navigationFiles).map { it.file to it.changeType }.toMap(),
            )
        } else {
            doFullTaskAction(navigationFiles.files)
        }

        copyToMainModule()
    }

    private fun doIncrementalTaskAction(changedInputs: Map) {
        /*
        This option is one of the most optimal in terms of performance.
        It is definitely not worth using the option that involves the use of `.keys`.
        Going through the map is two times cheaper than creating new sets.
        */
        generateDeepLinks(navFiles = changedInputs.mapNotNull { (file, status) -> if (status != ChangeType.REMOVED) file else null })
        changedInputs
            .mapNotNull { (file, status) -> if (status == ChangeType.REMOVED) file else null }
            .forEach(File::delete)
    }

    private fun doFullTaskAction(navFiles: Collection) {
        if (buildDir.exists() && !buildDir.deleteRecursively()) {
            project.logger.warn("Failed to clear build directory for deep links")
        }
        if (!buildDir.exists() && !buildDir.mkdirs()) {
            throw GradleException("Failed to create directory for deep links")
        }
        if (outputDir.exists() && !outputDir.deleteRecursively()) {
            project.logger.warn("Failed to clear destination directory for deep links")
        }
        generateDeepLinks(navFiles)
    }

    private fun generateDeepLinks(navFiles: Collection) {
        val dispatcher = Dispatchers.IO
        runBlocking(dispatcher) {
            generateDeepLinks(rFilePackage.get(), applicationId.orNull ?: "", navFiles, buildDir, generatorParams, dispatcher)
        }
    }

    private fun copyToMainModule() {
        buildDir.copyRecursively(outputDir, overwrite = true)
        buildDir.deleteRecursively()

        try {
            if (!pluginDir.exists()) pluginDir.mkdir()
            generateFiles(
                pluginDir = pluginDir,
                namesToContent = mapOf(
                    "build.gradle.kts" to gradleKtsFileText,
                    ".gitignore" to gitIgnoreFileText
                )
            )
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

private fun generateFiles(pluginDir: File, namesToContent: Map) {
    namesToContent.forEach { (name, content) ->
        val file = File(pluginDir, name)
        if (!file.exists()) {
            file.createNewFile()
            file.writeText(content.trimIndent())
        }
    }
}

private const val gradleKtsFileText = """
    @file:Suppress("UNUSED_VARIABLE")
    
    plugins {
        id("java-library")
        id("kotlin")
    }
    
    sourceSets {
        named("main") {
            java.srcDirs("src/main/kotlin")
        }
    }
    
    dependencies {
        implementation(kotlin("stdlib"))
    }
"""

private const val gitIgnoreFileText = """
    /build
    /src/main/kotlin/*
    !src/main/kotlin/deep_link
"""




© 2015 - 2025 Weber Informatics LLC | Privacy Policy