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

co.touchlab.faktory.KMMBridge.kt Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright (c) 2024 Touchlab.
 * 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 co.touchlab.faktory

import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Zip
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.plugin.mpp.Framework
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFrameworkConfig
import java.io.File
import kotlin.collections.filter
import kotlin.collections.flatMap
import kotlin.collections.forEach

@Suppress("unused")
class KMMBridgePlugin : Plugin {

    override fun apply(project: Project): Unit = with(project) {
        val extension = extensions.create(EXTENSION_NAME)

        extension.dependencyManagers.convention(emptyList())
        extension.buildType.convention(NativeBuildType.RELEASE)

        afterEvaluate {
            configureXcFramework()
            configureLocalDev()
            if (enablePublishing) {
                configureArtifactManagerAndDeploy()
            }
        }
    }

    private fun Project.configureZipTask(extension: KmmBridgeExtension): Pair, File> {
        val zipFile = zipFilePath()
        val zipTask = tasks.register("zipXCFramework") {
            group = TASK_GROUP_NAME
            from("$layoutBuildDir/XCFrameworks/${extension.buildType.get().getName()}")
            destinationDirectory.set(zipFile.parentFile)
            archiveFileName.set(zipFile.name)
        }

        return Pair(zipTask, zipFile)
    }

    // Collect all declared frameworks in project and combine into xcframework
    private fun Project.configureXcFramework() {
        val extension = kmmBridgeExtension
        var xcFrameworkConfig: XCFrameworkConfig? = null

        val spmBuildTargets: Set =
            project.spmBuildTargets?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }?.toSet() ?: emptySet()

        kotlin.targets
            .withType()
            .filter { it.konanTarget.family.isAppleFamily }
            .flatMap { it.binaries.filterIsInstance() }
            .forEach { framework ->
                val theName = framework.baseName
                val currentName = extension.frameworkName.orNull
                if (currentName == null) {
                    extension.frameworkName.set(theName)
                } else {
                    if (currentName != theName) {
                        throw IllegalStateException("Only one framework name currently allowed. Found $currentName and $theName")
                    }
                }
                val shouldAddTarget =
                    spmBuildTargets.isEmpty() || spmBuildTargets.contains(framework.target.konanTarget.name)
                if (shouldAddTarget) {
                    if (xcFrameworkConfig == null) {
                        xcFrameworkConfig = XCFramework(theName)
                    }
                    xcFrameworkConfig!!.add(framework)
                }
            }
    }

    private fun Project.configureLocalDev() {
        val extension = kmmBridgeExtension
        extension.localDevManager.orNull?.configureLocalDev(this)
    }

    private fun Project.configureArtifactManagerAndDeploy() {
        val extension = extensions.getByType()

        // Early-out with a warning if user hasn't added required config yet, to ensure project still syncs
        val artifactManager = extension.artifactManager.orNull ?: run {
            project.logger.warn("You must apply an artifact manager! Call `artifactManager.set(...)` or a configuration function like `mavenPublishArtifacts()` in your `kmmbridge` block.")
            return
        }

        val (zipTask, zipFile) = configureZipTask(extension)

        // Zip task depends on the XCFramework assemble task
        zipTask.configure {
            dependsOn(findXCFrameworkAssembleTask())
        }

        // Upload task depends on the zip task
        val uploadTask = tasks.register("uploadXCFramework") {
            group = TASK_GROUP_NAME

            dependsOn(zipTask)
            inputs.file(zipFile)
            outputs.files(urlFile)
            outputs.upToDateWhen { false } // We want to always upload when this task is called

            @Suppress("ObjectLiteralToLambda")
            doLast(object : Action {
                override fun execute(t: Task) {
                    logger.info("Uploading XCFramework version $version")
                    val deployUrl = artifactManager.deployArtifact(project, zipFile, version.toString())
                    urlFile.writeText(deployUrl)
                }
            })
        }

        val dependencyManagers = extension.dependencyManagers.get()

        // Publish task depends on the upload task
        val publishRemoteTask = tasks.register("kmmBridgePublish") {
            group = TASK_GROUP_NAME
            dependsOn(uploadTask)

            @Suppress("ObjectLiteralToLambda")
            doLast(object : Action {
                override fun execute(t: Task) {
                    artifactManager.finishArtifact(project, version.toString(), dependencyManagers)
                }
            })
        }

        // MavenPublishArtifactManager is somewhat complex because we have to hook into maven publishing
        // If you are exploring the task dependencies, be aware of that code
        artifactManager.configure(this, version.toString(), uploadTask, publishRemoteTask)


        for (dependencyManager in dependencyManagers) {
            dependencyManager.configure(this, uploadTask, publishRemoteTask)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy