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

org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC3
Show newest version
/*
 * Copyright 2010-2020 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.
 */

package org.jetbrains.kotlin.gradle.dsl

import org.gradle.api.Action
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.internal.plugins.DslObject
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.internal.KOTLIN_BUILD_TOOLS_API_IMPL
import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsSingleTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.targets.js.calculateJsCompilerType
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrSingleTargetPreset
import org.jetbrains.kotlin.gradle.tasks.CompileUsingKotlinDaemon
import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.castIsolatedKotlinPluginClassLoaderAware
import org.jetbrains.kotlin.gradle.utils.configureExperimentalTryK2
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import javax.inject.Inject
import kotlin.reflect.KClass

private const val KOTLIN_PROJECT_EXTENSION_NAME = "kotlin"

internal fun Project.createKotlinExtension(extensionClass: KClass): KotlinTopLevelExtension {
    return extensions.create(KOTLIN_PROJECT_EXTENSION_NAME, extensionClass.java, this)
}

internal val Project.topLevelExtension: KotlinTopLevelExtension
    get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()

internal val Project.topLevelExtensionOrNull: KotlinTopLevelExtension?
    get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME)?.castIsolatedKotlinPluginClassLoaderAware()

internal val Project.kotlinExtensionOrNull: KotlinProjectExtension?
    get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME)?.castIsolatedKotlinPluginClassLoaderAware()

val Project.kotlinExtension: KotlinProjectExtension
    get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()

internal val Project.kotlinJvmExtensionOrNull: KotlinJvmProjectExtension?
    get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME)?.castIsolatedKotlinPluginClassLoaderAware()

internal val Project.kotlinJvmExtension: KotlinJvmProjectExtension
    get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()

internal val Project.multiplatformExtensionOrNull: KotlinMultiplatformExtension?
    get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME)?.castIsolatedKotlinPluginClassLoaderAware()

internal val Project.multiplatformExtension: KotlinMultiplatformExtension
    get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()

internal val Project.pm20Extension: KotlinPm20ProjectExtension
    get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()

internal val Project.pm20ExtensionOrNull: KotlinPm20ProjectExtension?
    get() = extensions.findByName(KOTLIN_PROJECT_EXTENSION_NAME)?.castIsolatedKotlinPluginClassLoaderAware()

abstract class KotlinTopLevelExtension(internal val project: Project) : KotlinTopLevelExtensionConfig {

    override lateinit var coreLibrariesVersion: String

    private val toolchainSupport = ToolchainSupport.createToolchain(project, this)

    /**
     * Configures [Java toolchain](https://docs.gradle.org/current/userguide/toolchains.html) both for Kotlin JVM and Java tasks.
     *
     * @param action - action to configure [JavaToolchainSpec]
     */
    fun jvmToolchain(action: Action) {
        toolchainSupport.applyToolchain(action)
    }

    /**
     * Configures [Java toolchain](https://docs.gradle.org/current/userguide/toolchains.html) both for Kotlin JVM and Java tasks.
     *
     * @param jdkVersion - jdk version as number. For example, 17 for Java 17.
     */
    fun jvmToolchain(jdkVersion: Int) {
        jvmToolchain {
            it.languageVersion.set(JavaLanguageVersion.of(jdkVersion))
        }
    }

    /**
     * Configures Kotlin daemon JVM arguments for all tasks in this project.
     *
     * **Note**: In case other projects are using different JVM arguments, new instance of Kotlin daemon will be started.
     */
    @get:JvmSynthetic
    var kotlinDaemonJvmArgs: List
        @Deprecated("", level = DeprecationLevel.ERROR)
        get() = throw UnsupportedOperationException()
        set(value) {
            project
                .tasks
                .withType()
                .configureEach {
                    it.kotlinDaemonJvmArguments.set(value)
                }
        }

    override var explicitApi: ExplicitApiMode? = null

    override fun explicitApi() {
        explicitApi = ExplicitApiMode.Strict
    }

    override fun explicitApiWarning() {
        explicitApi = ExplicitApiMode.Warning
    }

    /**
     * Can be used to configure objects that are not yet created, or will be created in
     * 'afterEvaluate' (e.g. typically Android source sets containing flavors and buildTypes)
     *
     * Will fail project evaluation if the domain object is not created before 'afterEvaluate' listeners in the buildscript.
     *
     * @param configure: Called inline, if the value is already present. Called once the domain object is created.
     */
    @ExperimentalKotlinGradlePluginApi
    fun  NamedDomainObjectContainer.invokeWhenCreated(name: String, configure: T.() -> Unit) {
        configureEach { if (it.name == name) it.configure() }
        project.launchInStage(KotlinPluginLifecycle.Stage.ReadyForExecution) {
            if (name !in names) {
                /* Expect 'named' to throw corresponding exception */
                named(name).configure(configure)
            }
        }
    }

    /**
     * Allows to use a different version of the Kotlin Build Tools API implementation and effectively a different version of the compiler.
     *
     * By default, the Kotlin Build Tools API implementation of the same version as the KGP is used.
     *
     * Currently only has an effect if the `kotlin.compiler.runViaBuildToolsApi` Gradle property is set to `true`.
     */
    @ExperimentalKotlinGradlePluginApi
    @ExperimentalBuildToolsApi
    fun useCompilerVersion(version: String) {
        project.dependencies.add(BUILD_TOOLS_API_CLASSPATH_CONFIGURATION_NAME, "$KOTLIN_MODULE_GROUP:$KOTLIN_BUILD_TOOLS_API_IMPL:$version")
    }
}

internal fun ExplicitApiMode.toCompilerValue() = when (this) {
    ExplicitApiMode.Strict -> "strict"
    ExplicitApiMode.Warning -> "warning"
    ExplicitApiMode.Disabled -> "disable"
}

internal fun KotlinTopLevelExtension.explicitApiModeAsCompilerArg(): String? {
    val cliOption = explicitApi?.toCompilerValue()

    return cliOption?.let { "-Xexplicit-api=$it" }
}

@KotlinGradlePluginDsl
open class KotlinProjectExtension @Inject constructor(project: Project) : KotlinTopLevelExtension(project), KotlinSourceSetContainer {
    override var sourceSets: NamedDomainObjectContainer
        @Suppress("UNCHECKED_CAST")
        get() = DslObject(this).extensions.getByName("sourceSets") as NamedDomainObjectContainer
        internal set(value) {
            DslObject(this).extensions.add("sourceSets", value)
        }

    internal suspend fun awaitSourceSets(): NamedDomainObjectContainer {
        KotlinPluginLifecycle.Stage.AfterFinaliseRefinesEdges.await()
        return sourceSets
    }
}

abstract class KotlinSingleTargetExtension(project: Project) : KotlinProjectExtension(project) {
    abstract val target: TARGET

    fun target(body: Action) = body.execute(target)
}

abstract class KotlinSingleJavaTargetExtension(project: Project) : KotlinSingleTargetExtension>(project)

abstract class KotlinJvmProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) {
    override lateinit var target: KotlinWithJavaTarget
        internal set

    open fun target(body: KotlinWithJavaTarget.() -> Unit) = target.run(body)

    val compilerOptions: KotlinJvmCompilerOptions = project.objects
        .newInstance(KotlinJvmCompilerOptionsDefault::class.java)
        .configureExperimentalTryK2(project)

    fun compilerOptions(configure: Action) {
        configure.execute(compilerOptions)
    }

    fun compilerOptions(configure: KotlinJvmCompilerOptions.() -> Unit) {
        configure(compilerOptions)
    }
}

abstract class Kotlin2JsProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) {
    private lateinit var _target: KotlinWithJavaTarget

    override val target: KotlinWithJavaTarget
        get() {
            if (!::_target.isInitialized) throw IllegalStateException("Extension target is not initialized!")

            return _target
        }

    internal fun setTarget(target: KotlinWithJavaTarget) {
        _target = target
    }

    open fun target(body: KotlinWithJavaTarget.() -> Unit) = target.run(body)
}

abstract class KotlinJsProjectExtension(project: Project) :
    KotlinSingleTargetExtension(project),
    KotlinJsCompilerTypeHolder {
    lateinit var irPreset: KotlinJsIrSingleTargetPreset

    lateinit var legacyPreset: KotlinJsSingleTargetPreset

    private val targetSetObservers = mutableListOf<(KotlinJsTargetDsl?) -> Unit>()

    // target is public property
    // Users can write kotlin.target and it should work
    // So call of target should init default configuration
    @Deprecated("Use `target` instead", ReplaceWith("target"))
    var _target: KotlinJsTargetDsl? = null
        private set(value) {
            field = value
            targetSetObservers.forEach { it(value) }
        }

    fun registerTargetObserver(observer: (KotlinJsTargetDsl?) -> Unit) {
        targetSetObservers.add(observer)
    }

    companion object {
        internal fun warnAboutDeprecatedCompiler(project: Project, compilerType: KotlinJsCompilerType) {
            if (PropertiesProvider(project).jsCompilerNoWarn) return
            val logger = project.logger
            when (compilerType) {
                KotlinJsCompilerType.LEGACY -> logger.warn(LEGACY_DEPRECATED)
                KotlinJsCompilerType.IR -> {}
                KotlinJsCompilerType.BOTH -> logger.warn(BOTH_DEPRECATED)
            }
        }

        private val LEGACY_DEPRECATED =
            """
                |
                |==========
                |This project currently uses the Kotlin/JS Legacy compiler backend, which has been deprecated and will be removed in a future release.
                |
                |Please migrate the project to the new IR-based compiler (https://kotl.in/jsir).
                |==========
                |
            """.trimMargin()

        private val BOTH_DEPRECATED =
            """
                |
                |==========
                |This project currently uses Both mode, which requires the Kotlin/JS Legacy compiler backend.
                |This backend has been deprecated and will be removed in a future release.
                |
                |Please migrate the project to the new IR-based compiler (https://kotl.in/jsir).
                |==========
                |
            """.trimMargin()
    }

    @Deprecated("Use js() instead", ReplaceWith("js()"))
    @Suppress("DEPRECATION")
    override val target: KotlinJsTargetDsl
        get() {
            if (_target == null) {
                js {}
            }
            return _target!!
        }

    override val compilerTypeFromProperties: KotlinJsCompilerType? = project.kotlinPropertiesProvider.jsCompiler

    @Suppress("DEPRECATION")
    private fun jsInternal(
        compiler: KotlinJsCompilerType? = null,
        body: KotlinJsTargetDsl.() -> Unit
    ): KotlinJsTargetDsl {
        if (_target != null) {
            val previousCompilerType = _target!!.calculateJsCompilerType()
            check(compiler == null || previousCompilerType == compiler) {
                "You already registered Kotlin/JS target with another compiler: ${previousCompilerType.lowerName}"
            }
        }

        if (_target == null) {
            val compilerOrFromProperties = compiler ?: compilerTypeFromProperties
            val compilerOrDefault = compilerOrFromProperties ?: defaultJsCompilerType
            warnAboutDeprecatedCompiler(project, compilerOrDefault)
            val target: KotlinJsTargetDsl = when (compilerOrDefault) {
                KotlinJsCompilerType.LEGACY -> legacyPreset
                    .also {
                        it.irPreset = null
                    }
                    .createTargetInternal("js")

                KotlinJsCompilerType.IR -> irPreset
                    .also {
                        it.mixedMode = false
                    }
                    .createTargetInternal("js")

                KotlinJsCompilerType.BOTH -> legacyPreset
                    .also {
                        irPreset.mixedMode = true
                        it.irPreset = irPreset
                    }
                    .createTargetInternal(
                        lowerCamelCaseName(
                            "js",
                            LEGACY.lowerName
                        )
                    )
            }

            this._target = target

            target.project.components.addAll(target.components)
        }

        target.run(body)

        return target
    }

    fun js(
        compiler: KotlinJsCompilerType = defaultJsCompilerType,
        body: KotlinJsTargetDsl.() -> Unit = { }
    ): KotlinJsTargetDsl = jsInternal(compiler, body)

    fun js(
        compiler: String,
        body: KotlinJsTargetDsl.() -> Unit = { }
    ): KotlinJsTargetDsl = js(
        KotlinJsCompilerType.byArgument(compiler),
        body
    )

    fun js(
        body: KotlinJsTargetDsl.() -> Unit = { }
    ) = jsInternal(body = body)

    fun js() = js { }

    fun js(compiler: KotlinJsCompilerType, configure: Action) =
        js(compiler = compiler) {
            configure.execute(this)
        }

    fun js(compiler: String, configure: Action) =
        js(compiler = compiler) {
            configure.execute(this)
        }

    fun js(configure: Action) = jsInternal {
        configure.execute(this)
    }

    @Deprecated("Use js instead", ReplaceWith("js(body)"))
    open fun target(body: KotlinJsTargetDsl.() -> Unit) = js(body)

    @Deprecated(
        "Needed for IDE import using the MPP import mechanism",
        level = DeprecationLevel.HIDDEN
    )
    @Suppress("DEPRECATION")
    fun getTargets(): NamedDomainObjectContainer? =
        _target?.let { target ->
            target.project.container(KotlinTarget::class.java)
                .apply { add(target) }
        }
}

abstract class KotlinCommonProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) {
    override lateinit var target: KotlinWithJavaTarget
        internal set

    open fun target(
        body: KotlinWithJavaTarget.() -> Unit
    ) = target.run(body)
}

abstract class KotlinAndroidProjectExtension(project: Project) : KotlinSingleTargetExtension(project) {
    override lateinit var target: KotlinAndroidTarget
        internal set

    open fun target(body: KotlinAndroidTarget.() -> Unit) = target.run(body)

    val compilerOptions: KotlinJvmCompilerOptions = project.objects
        .newInstance(KotlinJvmCompilerOptionsDefault::class.java)
        .configureExperimentalTryK2(project)

    fun compilerOptions(configure: Action) {
        configure.execute(compilerOptions)
    }

    fun compilerOptions(configure: KotlinJvmCompilerOptions.() -> Unit) {
        configure(compilerOptions)
    }
}

enum class NativeCacheKind(val produce: String?, val outputKind: CompilerOutputKind?) {
    NONE(null, null),
    DYNAMIC("dynamic_cache", CompilerOutputKind.DYNAMIC_CACHE),
    STATIC("static_cache", CompilerOutputKind.STATIC_CACHE);

    companion object {
        fun byCompilerArgument(argument: String): NativeCacheKind? =
            NativeCacheKind.values().firstOrNull { it.name.equals(argument, ignoreCase = true) }
    }
}

// This is a temporary parameter for the translation period.
enum class NativeCacheOrchestration {
    Gradle,
    Compiler;

    companion object {
        fun byCompilerArgument(argument: String): NativeCacheOrchestration? =
            NativeCacheOrchestration.values().firstOrNull { it.name.equals(argument, ignoreCase = true) }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy