org.jetbrains.kotlin.gradle.targets.native.KotilnNativeConfigureBinariesSideEffect.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2023 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.targets.native
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Exec
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.statistics.NativeLinkTaskMetrics
import org.jetbrains.kotlin.gradle.targets.KotlinTargetSideEffect
import org.jetbrains.kotlin.gradle.targets.native.toolchain.KotlinNativeProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
internal val KotlinNativeConfigureBinariesSideEffect = KotlinTargetSideEffect { target ->
val project = target.project
target.compilations.all {
// Create configurations eagerly to prevent issues when a configuration created during task materialization
// this can cause warnings during IDE import, because IDE "collects" configurations before task execution.
it.resolvableApiConfiguration()
}
// Create link and run tasks.
target.binaries.all {
project.createLinkTask(it)
}
target.binaries.withType(Executable::class.java).all {
project.createRunTask(it)
}
target.binaries.prefixGroups.all { prefixGroup ->
val linkGroupTask = project.locateOrRegisterTask(prefixGroup.linkTaskName) {
it.group = BasePlugin.BUILD_GROUP
it.description = "Links all binaries for target '${target.name}'."
}
prefixGroup.binaries.all {
linkGroupTask.dependsOn(it.linkTaskName)
}
}
// Create an aggregate link task for each compilation.
target.compilations.all {
project.registerTask(it.binariesTaskName) { task ->
task.group = BasePlugin.BUILD_GROUP
task.description = "Links all binaries for compilation '${it.name}' of target '${it.target.name}'."
}
}
project.launchInStage(KotlinPluginLifecycle.Stage.AfterEvaluateBuildscript) {
target.binaries.forEach { binary ->
project.tasks.named(binary.compilation.binariesTaskName).configure { binariesTask ->
binariesTask.dependsOn(binary.linkTaskName)
}
}
}
/**
* We create test binaries for all platforms but test runs only for:
* - host platforms: macosX64, linuxX64, mingwX64;
* - simulated platforms: iosX64, tvosX64, watchosX64.
* See more in [KotlinNativeTargetWithTestsConfigurator] and its subclasses.
*/
target.binaries.test(listOf(NativeBuildType.DEBUG)) { }
}
/**
* Creates a resolvable configuration from non-resolvable "api" of [KotlinNativeCompilation]
* Kotlin Native requires that only API dependencies can be exported. So we need to resolve API-only dependencies
* and exported dependencies to check that.
*/
private fun KotlinNativeCompilation.resolvableApiConfiguration(): Configuration {
val apiConfiguration = compilation.internal.configurations.apiConfiguration
return project
.configurations.maybeCreateResolvable(lowerCamelCaseName("resolvable", apiConfiguration.name)) {
extendsFrom(apiConfiguration)
val compileConfiguration = compilation.internal.configurations.compileDependencyConfiguration
compileConfiguration.copyAttributesTo(project, this)
}
}
private fun Project.createLinkTask(binary: NativeBinary) {
// workaround for too late compilation compilerOptions creation
// which leads to not able run project.afterEvaluate because of wrong context
// this afterEvaluate comes from NativeCompilerOptions
@Suppress("DEPRECATION") val compilationCompilerOptions = binary.compilation.compilerOptions
val konanPropertiesBuildService = KonanPropertiesBuildService.registerIfAbsent(project)
val linkTask = registerTask(
binary.linkTaskName, listOf(binary)
) { task ->
val target = binary.target
val compilation = binary.compilation
task.group = BasePlugin.BUILD_GROUP
task.description = "Links ${binary.outputKind.description} '${binary.name}' for a target '${target.name}'."
task.dependsOn(compilation.compileTaskProvider)
task.enabled = binary.konanTarget.enabledOnCurrentHost
task.konanPropertiesService.set(konanPropertiesBuildService)
task.usesService(konanPropertiesBuildService)
task.toolOptions.freeCompilerArgs.value(compilationCompilerOptions.options.freeCompilerArgs)
task.toolOptions.freeCompilerArgs.addAll(providers.provider { PropertiesProvider(project).nativeLinkArgs })
task.runViaBuildToolsApi.value(false).disallowChanges() // K/N is not yet supported
task.kotlinNativeProvider.set(project.provider {
KotlinNativeProvider(project, task.konanTarget, task.kotlinNativeBundleBuildService)
})
// Frameworks actively uses symlinks.
// Gradle build cache transforms symlinks into regular files https://guides.gradle.org/using-build-cache/#symbolic_links
task.outputs.cacheIf { task.outputKind != CompilerOutputKind.FRAMEWORK }
task.setSource(compilation.compileTaskProvider.flatMap { it.outputFile })
task.includes.clear() // we need to include non '.kt' or '.kts' files
task.disallowSourceChanges()
task.apiFiles.from({ compilation.resolvableApiConfiguration().incoming.files })
}
NativeLinkTaskMetrics.collectMetrics(this)
if (binary !is TestExecutable) {
tasks.named(binary.compilation.target.artifactsTaskName).dependsOn(linkTask)
locateOrRegisterTask(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(linkTask)
}
if (binary is Framework) {
createFrameworkArtifact(binary, linkTask)
}
}
private fun Project.createRunTask(binary: Executable) {
val taskName = binary.runTaskName ?: return
registerTask(taskName) { exec ->
exec.group = KotlinNativeTargetConfigurator.RUN_GROUP
exec.description = "Executes Kotlin/Native executable ${binary.name} for target ${binary.target.name}"
exec.enabled = binary.konanTarget.isCurrentHost
exec.executable = binary.outputFile.absolutePath
exec.workingDir = project.projectDir
exec.onlyIf { binary.outputFile.exists() }
exec.dependsOn(binary.linkTaskName)
}
}