[K/N][build] Move old Konan plugin to native-build-tools

This commit is contained in:
Pavel Punegov
2023-08-31 15:27:19 +02:00
committed by Space Team
parent 04c5ac0eb6
commit b1a85d45c6
34 changed files with 76 additions and 2916 deletions
@@ -92,7 +92,6 @@ kotlin {
sourceSets {
main {
kotlin.srcDir("src/main/kotlin")
kotlin.srcDir("../../kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin")
}
}
}
@@ -0,0 +1,95 @@
/*
* 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.plugin.experimental.internal
import org.gradle.api.Project
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.internal.os.OperatingSystem
import org.gradle.language.cpp.internal.NativeVariantIdentity
import org.gradle.nativeplatform.MachineArchitecture
import org.gradle.nativeplatform.OperatingSystemFamily
import org.gradle.nativeplatform.TargetMachine
import org.gradle.nativeplatform.TargetMachineFactory
import org.gradle.nativeplatform.platform.NativePlatform
import org.gradle.nativeplatform.platform.internal.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.visibleName
interface KotlinNativePlatform : NativePlatform {
val target: KonanTarget
}
fun KonanTarget.getGradleOS(): OperatingSystemInternal = family.visibleName.let {
DefaultOperatingSystem(it, OperatingSystem.forName(it))
}
fun KonanTarget.getGradleOSFamily(objectFactory: ObjectFactory): OperatingSystemFamily {
return objectFactory.named(OperatingSystemFamily::class.java, family.visibleName)
}
fun KonanTarget.getGradleCPU(): ArchitectureInternal = architecture.visibleName.let {
Architectures.forInput(it)
}
fun KonanTarget.toTargetMachine(objectFactory: ObjectFactory): TargetMachine = object : TargetMachine {
override fun getOperatingSystemFamily(): OperatingSystemFamily =
getGradleOSFamily(objectFactory)
override fun getArchitecture(): MachineArchitecture =
objectFactory.named(MachineArchitecture::class.java, this@toTargetMachine.architecture.visibleName)
}
class DefaultKotlinNativePlatform(name: String, override val target: KonanTarget) :
DefaultNativePlatform(name, target.getGradleOS(), target.getGradleCPU()),
KotlinNativePlatform {
constructor(target: KonanTarget) : this(target.visibleName, target)
// TODO: Extend ImmutableDefaultNativePlatform and get rid of these methods after switch to Gradle 4.8
private fun notImplemented(): Nothing = throw NotImplementedError("Not Implemented in Kotlin/Native plugin")
override fun operatingSystem(name: String?) = notImplemented()
override fun withArchitecture(architecture: ArchitectureInternal?) = notImplemented()
override fun architecture(name: String?) = notImplemented()
}
// NativeVariantIdentity constructor was changed in Gradle 5.1
// So we have to use reflection to create instance of this class in earlier versions.
internal fun compatibleVariantIdentity(
project: Project,
name: String,
baseName: Provider<String>,
group: Provider<String>,
version: Provider<String>,
debuggable: Boolean,
optimized: Boolean,
target: KonanTarget,
linkUsage: UsageContext?,
runtimeUsage: UsageContext?
): NativeVariantIdentity =
NativeVariantIdentity::class.java.getConstructor(
String::class.java,
Provider::class.java,
Provider::class.java,
Provider::class.java,
Boolean::class.javaPrimitiveType,
Boolean::class.javaPrimitiveType,
OperatingSystemFamily::class.java,
UsageContext::class.java,
UsageContext::class.java
).newInstance(
name,
baseName,
group,
version,
debuggable,
optimized,
target.getGradleOSFamily(project.objects),
linkUsage,
runtimeUsage
)
@@ -0,0 +1,99 @@
/*
* 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.plugin.konan
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.ProjectProperty
import java.io.File
import java.util.*
/**
* The plugin allows an IDE to specify some building parameters. These parameters
* are passed to the plugin via environment variables. Two variables are supported:
* - CONFIGURATION_BUILD_DIR - A path to a destination directory for all compilation tasks.
* The IDE should take care about specifying different directories
* for different targets. This setting has less priority than
* an explicitly specified destination directory in the build script.
*
* - DEBUGGING_SYMBOLS - If YES, the debug support will be enabled for all artifacts. This option has less
* priority than explicitly specified enableDebug option in the build script and
* enableDebug project property.
*
* - KONAN_ENABLE_OPTIMIZATIONS - If YES, optimizations will be enabled for all artifacts by default. This option
* has less priority than explicitly specified enableOptimizations option in the
* build script.
*
* Support for environment variables should be explicitly enabled by setting a project property:
* konan.useEnvironmentVariables = true.
*/
internal interface EnvironmentVariables {
val configurationBuildDir: File?
val debuggingSymbols: Boolean
val enableOptimizations: Boolean
}
internal class EnvironmentVariablesUnused: EnvironmentVariables {
override val configurationBuildDir: File?
get() = null
override val debuggingSymbols: Boolean
get() = false
override val enableOptimizations: Boolean
get() = false
}
internal class EnvironmentVariablesImpl(val project: Project): EnvironmentVariables {
override val configurationBuildDir: File?
get() = System.getenv("CONFIGURATION_BUILD_DIR")?.let {
project.file(it)
}
override val debuggingSymbols: Boolean
get() = System.getenv("DEBUGGING_SYMBOLS")?.uppercase() == "YES"
override val enableOptimizations: Boolean
get() = System.getenv("KONAN_ENABLE_OPTIMIZATIONS")?.uppercase() == "YES"
}
/**
* Due to https://github.com/gradle/gradle/issues/3468 we cannot use environment
* variables in Java 9. Until Gradle API for environment variables is provided
* we use project properties instead of them. TODO: Return to using env vars when the issue is fixed.
*/
internal class EnvironmentVariablesFromProperties(val project: Project): EnvironmentVariables {
override val configurationBuildDir: File?
get() = project.findProperty(ProjectProperty.KONAN_CONFIGURATION_BUILD_DIR)?.let {
project.file(it)
}
override val debuggingSymbols: Boolean
get() = project.findProperty(ProjectProperty.KONAN_DEBUGGING_SYMBOLS)?.toString()?.uppercase(Locale.getDefault()).let {
it == "YES" || it == "TRUE"
}
override val enableOptimizations: Boolean
get() = project.findProperty(ProjectProperty.KONAN_OPTIMIZATIONS_ENABLE)?.toString()?.uppercase(Locale.getDefault()).let {
it == "YES" || it == "TRUE"
}
}
internal val Project.useEnvironmentVariables: Boolean
get() = findProperty(ProjectProperty.KONAN_USE_ENVIRONMENT_VARIABLES)?.toString()?.toBoolean() ?: false
/*
TODO: Return to using env vars when the issue is fixed.
Take into account the useEnvironmentVariables property (and may be rename it) in the following way:
if (useEnvironmentVariables) {
EnvironmentVariablesImpl(project)
} else {
EnvironmentVariablesUnused()
}
*/
internal val Project.environmentVariables: EnvironmentVariables
get() = EnvironmentVariablesFromProperties(project)
@@ -0,0 +1,159 @@
/*
* 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.plugin.konan
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.internal.CollectionCallbackActionDecorator
import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.reflect.Instantiator
import kotlin.reflect.KClass
open class KonanArtifactContainer(val project: ProjectInternal) : DefaultPolymorphicDomainObjectContainer<KonanBuildingConfig<*>>(
KonanBuildingConfig::class.java,
project.services.get(Instantiator::class.java),
CollectionCallbackActionDecorator.NOOP
) {
private inner class KonanBuildingConfigFactory<T: KonanBuildingConfig<*>>(val configClass: KClass<T>)
: NamedDomainObjectFactory<T> {
var targets: Iterable<String> = emptyList()
override fun create(name: String): T =
instantiator.newInstance(configClass.java, name, project, targets)
}
private val factories = mutableMapOf<KClass<out KonanBuildingConfig<*>>, KonanBuildingConfigFactory<*>>()
private fun <T: KonanBuildingConfig<*>> createFactory(configClass: KClass<T>) {
val factory = KonanBuildingConfigFactory(configClass)
super.registerFactory(configClass.java, factory)
factories.put(configClass, factory)
}
init {
createFactory(KonanProgram::class)
createFactory(KonanDynamic::class)
createFactory(KonanFramework::class)
createFactory(KonanLibrary::class)
createFactory(KonanBitcode::class)
createFactory(KonanInteropLibrary::class)
}
private fun determineTargets(configClass: KClass<out KonanBuildingConfig<*>>, args: Map<String, Any?>) {
val targetsArg = args["targets"]
val targets = when {
targetsArg == null -> project.konanExtension.targets
targetsArg is Iterable<*> -> targetsArg.map { it.toString() }
else -> listOf(targetsArg.toString())
}
factories[configClass]?.targets = targets
}
private fun <T: KonanBuildingConfig<*>> create(name: String,
configClass: KClass<T>,
args: Map<String, Any?>,
configureAction: Action<T>) {
determineTargets(configClass, args)
super.create(name, configClass.java, configureAction)
}
private fun <T: KonanBuildingConfig<*>> create(name: String,
configClass: KClass<T>,
args: Map<String, Any?>,
configureAction: T.() -> Unit) {
determineTargets(configClass, args)
super.create(name, configClass.java, configureAction)
}
private fun <T: KonanBuildingConfig<*>> create(name: String,
configClass: KClass<T>,
args: Map<String, Any?>) {
determineTargets(configClass, args)
super.create(name, configClass.java)
}
fun program(args: Map<String, Any?>, name: String) = create(name, KonanProgram::class, args)
fun program(args: Map<String, Any?>, name: String, configureAction: Action<KonanProgram>) =
create(name, KonanProgram::class, args, configureAction)
fun program(args: Map<String, Any?>, name: String, configureAction: KonanProgram.() -> Unit) =
create(name, KonanProgram::class, args, configureAction)
fun program(args: Map<String, Any?>, name: String, configureAction: Closure<*>) =
program(args, name) { project.configure(this, configureAction) }
fun dynamic(args: Map<String, Any?>, name: String) = create(name, KonanDynamic::class, args)
fun dynamic(args: Map<String, Any?>, name: String, configureAction: Action<KonanDynamic>) =
create(name, KonanDynamic::class, args, configureAction)
fun dynamic(args: Map<String, Any?>, name: String, configureAction: KonanDynamic.() -> Unit) =
create(name, KonanDynamic::class, args, configureAction)
fun dynamic(args: Map<String, Any?>, name: String, configureAction: Closure<*>) =
dynamic(args, name) { project.configure(this, configureAction) }
fun framework(args: Map<String, Any?>, name: String) = create(name, KonanFramework::class, args)
fun framework(args: Map<String, Any?>, name: String, configureAction: Action<KonanFramework>) =
create(name, KonanFramework::class, args, configureAction)
fun framework(args: Map<String, Any?>, name: String, configureAction: KonanFramework.() -> Unit) =
create(name, KonanFramework::class, args, configureAction)
fun framework(args: Map<String, Any?>, name: String, configureAction: Closure<*>) =
framework(args, name) { project.configure(this, configureAction) }
fun library(args: Map<String, Any?>, name: String) = create(name, KonanLibrary::class, args)
fun library(args: Map<String, Any?>, name: String, configureAction: Action<KonanLibrary>) =
create(name, KonanLibrary::class, args, configureAction)
fun library(args: Map<String, Any?>, name: String, configureAction: KonanLibrary.() -> Unit) =
create(name, KonanLibrary::class, args, configureAction)
fun library(args: Map<String, Any?>, name: String, configureAction: Closure<*>) =
library(args, name) { project.configure(this, configureAction) }
fun bitcode(args: Map<String, Any?>, name: String) = create(name, KonanBitcode::class, args)
fun bitcode(args: Map<String, Any?>, name: String, configureAction: Action<KonanBitcode>) =
create(name, KonanBitcode::class, args, configureAction)
fun bitcode(args: Map<String, Any?>, name: String, configureAction: KonanBitcode.() -> Unit) =
create(name, KonanBitcode::class, args, configureAction)
fun bitcode(args: Map<String, Any?>, name: String, configureAction: Closure<*>) =
bitcode(args, name) { project.configure(this, configureAction) }
fun interop(args: Map<String, Any?>, name: String) = create(name, KonanInteropLibrary::class, args)
fun interop(args: Map<String, Any?>, name: String, configureAction: Action<KonanInteropLibrary>) =
create(name, KonanInteropLibrary::class, args, configureAction)
fun interop(args: Map<String, Any?>, name: String, configureAction: KonanInteropLibrary.() -> Unit) =
create(name, KonanInteropLibrary::class, args, configureAction)
fun interop(args: Map<String, Any?>, name: String, configureAction: Closure<*>) =
interop(args, name) { project.configure(this, configureAction) }
fun program(name: String) = program(emptyMap(), name)
fun program(name: String, configureAction: Action<KonanProgram>) = program(emptyMap(), name, configureAction)
fun program(name: String, configureAction: KonanProgram.() -> Unit) = program(emptyMap(), name, configureAction)
fun program(name: String, configureAction: Closure<*>) = program(emptyMap(), name, configureAction)
fun dynamic(name: String) = dynamic(emptyMap(), name)
fun dynamic(name: String, configureAction: Action<KonanDynamic>) = dynamic(emptyMap(), name, configureAction)
fun dynamic(name: String, configureAction: KonanDynamic.() -> Unit) = dynamic(emptyMap(), name, configureAction)
fun dynamic(name: String, configureAction: Closure<*>) = dynamic(emptyMap(), name, configureAction)
fun framework(name: String) = framework(emptyMap(), name)
fun framework(name: String, configureAction: Action<KonanFramework>) = framework(emptyMap(), name, configureAction)
fun framework(name: String, configureAction: KonanFramework.() -> Unit) = framework(emptyMap(), name, configureAction)
fun framework(name: String, configureAction: Closure<*>) = framework(emptyMap(), name, configureAction)
fun library(name: String) = library(emptyMap(), name)
fun library(name: String, configureAction: Action<KonanLibrary>) = library(emptyMap(), name, configureAction)
fun library(name: String, configureAction: KonanLibrary.() -> Unit) = library(emptyMap(), name, configureAction)
fun library(name: String, configureAction: Closure<*>) = library(emptyMap(), name, configureAction)
fun bitcode(name: String) = bitcode(emptyMap(), name)
fun bitcode(name: String, configureAction: Action<KonanBitcode>) = bitcode(emptyMap(), name, configureAction)
fun bitcode(name: String, configureAction: KonanBitcode.() -> Unit) = bitcode(emptyMap(), name, configureAction)
fun bitcode(name: String, configureAction: Closure<*>) = bitcode(emptyMap(), name, configureAction)
fun interop(name: String) = interop(emptyMap(), name)
fun interop(name: String, configureAction: Action<KonanInteropLibrary>) = interop(emptyMap(), name, configureAction)
fun interop(name: String, configureAction: KonanInteropLibrary.() -> Unit) = interop(emptyMap(), name, configureAction)
fun interop(name: String, configureAction: Closure<*>) = interop(emptyMap(), name, configureAction)
}
@@ -0,0 +1,211 @@
/*
* 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.plugin.konan
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Named
import org.gradle.api.Task
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.publish.maven.MavenPom
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanBuildingTask
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
import kotlin.NoSuchElementException
/** Base class for all Kotlin/Native artifacts. */
abstract class KonanBuildingConfig<T : KonanBuildingTask>(
private val name_: String,
val type: Class<T>,
val project: ProjectInternal,
val targets: Iterable<String>
) : KonanBuildingSpec, Named {
internal val mainVariant = KonanSoftwareComponent(project)
override fun getName() = name_
protected val targetToTask = mutableMapOf<KonanTarget, TaskProvider<T>>()
fun tasks() = targetToTask.values
private val aggregateBuildTask: TaskProvider<Task>
internal var pomActions = mutableListOf<Action<MavenPom>>()
private val konanTargets: Iterable<KonanTarget>
get() = project.hostManager.toKonanTargets(targets).distinct()
init {
for (targetName in targets.distinct()) {
val konanTarget = project.hostManager.targetByName(targetName)
if (!project.hostManager.isEnabled(konanTarget)) {
project.logger.info("The target is not enabled on the current host: $targetName")
continue
}
if (!targetIsSupported(konanTarget)) {
project.logger.info("The target $targetName is not supported by the artifact $name")
continue
}
if (this[konanTarget] == null) {
val task = createTask(konanTarget)
targetToTask[konanTarget] = task
// Allow accessing targets just by their names in Groovy DSL.
(this as? ExtensionAware)?.extensions?.add(konanTarget.visibleName, task)
}
if (targetName != konanTarget.visibleName) {
createTargetAliasTaskIfDeclared(targetName)
}
}
aggregateBuildTask = createAggregateTask()
}
protected open fun generateTaskName(target: KonanTarget) =
"compileKonan${name.replaceFirstChar { it.uppercase() }}${target.visibleName.replaceFirstChar { it.uppercase() }}"
protected open fun generateAggregateTaskName() =
"compileKonan${name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}"
protected open fun generateTargetAliasTaskName(targetName: String) =
"compileKonan${name.replaceFirstChar { it.uppercase() }}${targetName.replaceFirstChar { it.uppercase() }}"
protected abstract fun generateTaskDescription(task: T): String
protected abstract fun generateAggregateTaskDescription(task: Task): String
protected abstract fun generateTargetAliasTaskDescription(task: Task, targetName: String): String
protected abstract val defaultBaseDir: File
protected open fun targetIsSupported(target: KonanTarget): Boolean = true
data class OutputPlacement(val destinationDir: File, val artifactName: String)
// There are two options for output placement.
// 1. Gradle's build directory. We use it by default, e.g. if user runs Gradle from command line.
// In this case all produced files has the same name but are placed in different directories
// depending on their targets (e.g. linux/foo.kexe and macbook/foo.kexe).
// 2. Custom path provided by IDE. In this case CONFIGURATION_BUILD_DIR environment variable should
// contain a path to a destination directory. All produced files are placed in this directory so IDE
// should take care about setting different CONFIGURATION_BUILD_DIR for different targets.
protected fun determineOutputPlacement(target: KonanTarget): OutputPlacement {
val configurationBuildDir = project.environmentVariables.configurationBuildDir
return if (configurationBuildDir != null) {
OutputPlacement(configurationBuildDir, name)
} else {
OutputPlacement(defaultBaseDir.targetSubdir(target), name)
}
}
private fun createTask(target: KonanTarget): TaskProvider<T> =
project.tasks.register(generateTaskName(target), type) {
val outputDescription = determineOutputPlacement(target)
init(this@KonanBuildingConfig, outputDescription.destinationDir, outputDescription.artifactName, target)
group = BasePlugin.BUILD_GROUP
description = generateTaskDescription(this)
}
private fun createAggregateTask(): TaskProvider<Task> =
project.tasks.register(generateAggregateTaskName()) {
group = BasePlugin.BUILD_GROUP
description = generateAggregateTaskDescription(this)
targetToTask.filter {
project.targetIsRequested(it.key)
}.forEach {
dependsOn(it.value)
}
}.also {
project.compileAllTask.configure { dependsOn(it) }
}
protected fun createTargetAliasTaskIfDeclared(targetName: String): TaskProvider<Task>? {
val canonicalTarget = project.hostManager.targetByName(targetName)
return this[canonicalTarget]?.let { canonicalBuild ->
project.tasks.register(generateTargetAliasTaskName(targetName)) {
group = BasePlugin.BUILD_GROUP
description = generateTargetAliasTaskDescription(this, targetName)
dependsOn(canonicalBuild)
}
}
}
internal operator fun get(target: KonanTarget) = targetToTask[target]
fun getByTarget(target: String) =
findByTarget(target) ?: throw NoSuchElementException("No such target for artifact $name: $target")
fun findByTarget(target: String) = this[project.hostManager.targetByName(target)]
fun getArtifactByTarget(target: String) = getByTarget(target).get().artifact
fun findArtifactByTarget(target: String) = findByTarget(target)?.get()?.artifact
// Common building DSL.
override fun artifactName(name: String) = tasks().forEach { it.configure { artifactName(name) } }
fun baseDir(dir: Any) =
tasks().forEach {
it.configure {
destinationDir(
project.file(dir).targetSubdir(konanTarget)
)
}
}
override fun libraries(closure: Closure<Unit>) =
tasks().forEach { it.configure { libraries(closure) } }
override fun libraries(action: Action<KonanLibrariesSpec>) =
tasks().forEach { it.configure { libraries(action) } }
override fun libraries(configure: KonanLibrariesSpec.() -> Unit) =
tasks().forEach { it.configure { libraries(configure) } }
override fun noDefaultLibs(flag: Boolean) =
tasks().forEach { it.configure { noDefaultLibs(flag) } }
override fun noEndorsedLibs(flag: Boolean) =
tasks().forEach { it.configure { noEndorsedLibs(flag) } }
override fun dumpParameters(flag: Boolean) =
tasks().forEach { it.configure { dumpParameters(flag) } }
override fun extraOpts(vararg values: Any) =
tasks().forEach { it.configure { extraOpts(*values) } }
override fun extraOpts(values: List<Any>) =
tasks().forEach { it.configure { extraOpts(values) } }
fun dependsOn(vararg dependencies: Any?) =
tasks().forEach { it.configure { dependsOn(*dependencies) } }
fun target(targetString: String, configureAction: T.() -> Unit) {
val target = project.hostManager.targetByName(targetString)
if (!project.hostManager.isEnabled(target)) {
project.logger.info("Target '$targetString' of artifact '$name' is not supported on the current host")
return
}
val task = this[target]
?: throw InvalidUserDataException("Target '$targetString' is not declared. Please add it into project.konanTasks list")
task.configure(configureAction)
}
fun target(targetString: String, configureAction: Action<T>) =
target(targetString) { configureAction.execute(this) }
fun target(targetString: String, configureAction: Closure<Unit>) =
target(targetString) { project.configure(this, configureAction) }
fun pom(action: Action<MavenPom>) = pomActions + action
}
@@ -0,0 +1,156 @@
/*
* 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.plugin.konan
import groovy.lang.Closure
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.plugin.tasks.*
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.KonanTarget.WASM32
import java.io.File
abstract class KonanCompileConfig<T: KonanCompileTask>(name: String,
type: Class<T>,
project: ProjectInternal,
targets: Iterable<String>)
: KonanBuildingConfig<T>(name, type, project, targets), KonanCompileSpec {
protected abstract val typeForDescription: String
override fun generateTaskDescription(task: T) =
"Build the Kotlin/Native $typeForDescription '${task.name}' for target '${task.konanTarget}'"
override fun generateAggregateTaskDescription(task: Task) =
"Build the Kotlin/Native $typeForDescription '${task.name}' for all supported and declared targets"
override fun generateTargetAliasTaskDescription(task: Task, targetName: String) =
"Build the Kotlin/Native $typeForDescription '${task.name}' for target '$targetName'"
override fun srcDir(dir: Any) = tasks().forEach { it.configure { srcDir(dir) } }
override fun srcFiles(vararg files: Any) = tasks().forEach { it.configure { srcFiles(*files) } }
override fun srcFiles(files: Collection<Any>) = tasks().forEach { it.configure { srcFiles(files) } }
override fun nativeLibrary(lib: Any) = tasks().forEach { it.configure { nativeLibrary(lib) } }
override fun nativeLibraries(vararg libs: Any) = tasks().forEach { it.configure { nativeLibraries(*libs) } }
override fun nativeLibraries(libs: FileCollection) = tasks().forEach { it.configure { nativeLibraries(libs) } }
@Deprecated("Use commonSourceSets instead", ReplaceWith("commonSourceSets(sourceSetName)"))
override fun commonSourceSet(sourceSetName: String) = tasks().forEach { it.configure { commonSourceSets(sourceSetName) } }
override fun commonSourceSets(vararg sourceSetNames: String) = tasks().forEach { it.configure { commonSourceSets(*sourceSetNames) } }
override fun enableMultiplatform(flag: Boolean) = tasks().forEach { it.configure { enableMultiplatform(flag) } }
override fun commonSrcDir(dir: Any) = tasks().forEach { it.configure { commonSrcDir(dir) } }
override fun commonSrcFiles(vararg files: Any) = tasks().forEach { it.configure { commonSrcFiles(*files) } }
override fun commonSrcFiles(files: Collection<Any>) = tasks().forEach { it.configure { commonSrcFiles(files) } }
override fun linkerOpts(values: List<String>) = tasks().forEach { it.configure { linkerOpts(values) } }
override fun linkerOpts(vararg values: String) = tasks().forEach { it.configure { linkerOpts(*values) } }
override fun enableDebug(flag: Boolean) = tasks().forEach { it.configure { enableDebug(flag) } }
override fun noStdLib(flag: Boolean) = tasks().forEach { it.configure { noStdLib(flag) } }
override fun noMain(flag: Boolean) = tasks().forEach { it.configure { noMain(flag) } }
override fun noPack(flag: Boolean) = tasks().forEach { it.configure { noPack(flag) } }
override fun enableOptimizations(flag: Boolean) = tasks().forEach { it.configure { enableOptimizations(flag) } }
override fun enableAssertions(flag: Boolean) = tasks().forEach { it.configure { enableAssertions(flag) } }
override fun entryPoint(entryPoint: String) = tasks().forEach { it.configure { entryPoint(entryPoint) } }
override fun measureTime(flag: Boolean) = tasks().forEach { it.configure { measureTime(flag) } }
override fun dependencies(closure: Closure<Unit>) = tasks().forEach { it.configure { dependencies(closure) } }
}
open class KonanProgram(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets
) : KonanCompileConfig<KonanCompileProgramTask>(name,
KonanCompileProgramTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "executable"
override val defaultBaseDir: File
get() = project.konanBinBaseDir
}
open class KonanDynamic(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileDynamicTask>(name,
KonanCompileDynamicTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "dynamic library"
override val defaultBaseDir: File
get() = project.konanBinBaseDir
override fun targetIsSupported(target: KonanTarget): Boolean = target != WASM32
}
open class KonanFramework(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileFrameworkTask>(name,
KonanCompileFrameworkTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "framework"
override val defaultBaseDir: File
get() = project.konanBinBaseDir
override fun targetIsSupported(target: KonanTarget): Boolean =
target.family.isAppleFamily
}
open class KonanLibrary(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileLibraryTask>(name,
KonanCompileLibraryTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "library"
override val defaultBaseDir: File
get() = project.konanLibsBaseDir
}
open class KonanBitcode(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileBitcodeTask>(name,
KonanCompileBitcodeTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "bitcode"
override fun generateTaskDescription(task: KonanCompileBitcodeTask) =
"Generates bitcode for the artifact '${task.name}' and target '${task.konanTarget}'"
override fun generateAggregateTaskDescription(task: Task) =
"Generates bitcode for the artifact '${task.name}' for all supported and declared targets'"
override fun generateTargetAliasTaskDescription(task: Task, targetName: String) =
"Generates bitcode for the artifact '${task.name}' for '$targetName'"
override val defaultBaseDir: File
get() = project.konanBitcodeBaseDir
}
@@ -0,0 +1,73 @@
/*
* 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.plugin.konan
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.plugin.konan.KonanInteropSpec.IncludeDirectoriesSpec
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanInteropTask
import java.io.File
open class KonanInteropLibrary(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets
) : KonanBuildingConfig<KonanInteropTask>(name, KonanInteropTask::class.java, project, targets),
KonanInteropSpec
{
override fun generateTaskDescription(task: KonanInteropTask) =
"Build the Kotlin/Native interop library '${task.name}' for target '${task.konanTarget}'"
override fun generateAggregateTaskDescription(task: Task) =
"Build the Kotlin/Native interop library '${task.name}' for all supported and declared targets'"
override fun generateTargetAliasTaskDescription(task: Task, targetName: String) =
"Build the Kotlin/Native interop library '${task.name}' for '$targetName'"
override val defaultBaseDir: File
get() = project.konanLibsBaseDir
// DSL
inner class IncludeDirectoriesSpecImpl: IncludeDirectoriesSpec {
override fun allHeaders(vararg includeDirs: Any) = allHeaders(includeDirs.toList())
override fun allHeaders(includeDirs: Collection<Any>) = tasks().forEach {
it.configure { this@configure.includeDirs.allHeaders(includeDirs) }
}
override fun headerFilterOnly(vararg includeDirs: Any) = headerFilterOnly(includeDirs.toList())
override fun headerFilterOnly(includeDirs: Collection<Any>) = tasks().forEach {
it.configure { this@configure.includeDirs.headerFilterOnly(includeDirs) }
}
}
val includeDirs = IncludeDirectoriesSpecImpl()
override fun defFile(file: Any) = tasks().forEach { it.configure { defFile(file) } }
override fun packageName(value: String) = tasks().forEach { it.configure { packageName(value) } }
override fun compilerOpts(vararg values: String) = tasks().forEach { it.configure { compilerOpts(*values) } }
override fun headers(vararg files: Any) = tasks().forEach { it.configure { headers(*files) } }
override fun headers(files: FileCollection) = tasks().forEach { it.configure { headers(files) } }
override fun includeDirs(vararg values: Any) = tasks().forEach { it.configure { includeDirs(*values) } }
override fun includeDirs(closure: Closure<Unit>) = includeDirs { project.configure(this, closure) }
override fun includeDirs(action: Action<IncludeDirectoriesSpec>) = includeDirs { action.execute(this) }
override fun includeDirs(configure: IncludeDirectoriesSpec.() -> Unit) = includeDirs.configure()
override fun linkerOpts(values: List<String>) = tasks().forEach { it.configure { linkerOpts(values) } }
override fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
override fun link(vararg files: Any) = tasks().forEach { it.configure { link(*files) } }
override fun link(files: FileCollection) = tasks().forEach { it.configure { link(files) } }
override fun dependencies(closure: Closure<Unit>) = tasks().forEach { it.configure { dependencies(closure) }}
}
@@ -0,0 +1,156 @@
/*
* 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.plugin.konan
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanArtifactWithLibrariesTask
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanBuildingTask
import org.jetbrains.kotlin.konan.*
import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.SearchPathResolver
import java.io.File
open class KonanLibrariesSpec(
@Internal val task: KonanArtifactWithLibrariesTask,
@Internal val project: Project
) {
@InputFiles val files = mutableSetOf<FileCollection>()
@Input val namedKlibs = mutableSetOf<String>()
@Internal val artifacts = mutableListOf<KonanBuildingTask>()
val artifactFiles: List<File>
@InputFiles get() = artifacts.map { it.artifact }
@Internal val explicitRepos = mutableSetOf<File>()
val repos: Set<File>
@Input get() = mutableSetOf<File>().apply {
addAll(explicitRepos)
add(task.destinationDir) // TODO: Check if task is a library - create a Library interface
add(task.destinationDir) // TODO: Check if task is a library - create a Library interface
add(task.project.konanLibsBaseDir.targetSubdir(target))
addAll(artifacts.flatMap { it.libraries.repos })
addAll(task.platformConfiguration.files.map { it.parentFile })
}
val target: KonanTarget
@Internal get() = task.konanTarget
private val friendsTasks = mutableSetOf<KonanBuildingTask>()
@get:Internal // Taken into account by tasks's dependOn.
val friends: Set<File> get() = mutableSetOf<File>().apply {
addAll(friendsTasks.map { it.artifact })
}
// DSL Methods
/** Absolute path */
fun file(file: Any) = files.add(project.files(file))
fun files(vararg files: Any) = this.files.addAll(files.map { project.files(it) })
fun files(collection: FileCollection) = this.files.add(collection)
/** The compiler with search the library in repos */
fun klib(lib: String) = namedKlibs.add(lib)
fun klibs(vararg libs: String) = namedKlibs.addAll(libs)
fun klibs(libs: Iterable<String>) = namedKlibs.addAll(libs)
private fun klibInternal(lib: KonanBuildingConfig<*>, friend: Boolean) {
if (!(lib is KonanLibrary || lib is KonanInteropLibrary)) {
throw InvalidUserDataException("Config ${lib.name} is not a library")
}
val libraryTask = lib[target]?.get() ?:
throw InvalidUserDataException("Library ${lib.name} has no target ${target.visibleName}")
if (libraryTask == task) {
throw InvalidUserDataException("Attempt to use a library as its own dependency: " +
"${task.name} (in project: ${project.path})")
}
artifacts.add(libraryTask)
task.dependsOn(libraryTask)
if (friend) friendsTasks.add(libraryTask)
}
/** Direct link to a config */
fun klib(lib: KonanLibrary) = klibInternal(lib, false)
/** Direct link to a config */
fun klib(lib: KonanInteropLibrary) = klibInternal(lib, false)
/** Artifact in the specified project by name */
fun artifact(libraryProject: Project, name: String, friend: Boolean) {
project.evaluationDependsOn(libraryProject)
klibInternal(libraryProject.konanArtifactsContainer.getByName(name), friend)
}
fun artifact(libraryProject: Project, name: String) = artifact(libraryProject, name, false)
/** Artifact in the current project by name */
fun artifact(name: String, friend: Boolean) = artifact(project, name, friend)
fun artifact(name: String) = artifact(project, name, false)
/** Artifact by direct link */
fun artifact(artifact: KonanLibrary) = klib(artifact)
/** Direct link to a config */
fun artifact(artifact: KonanInteropLibrary) = klib(artifact)
private fun allArtifactsFromInternal(libraryProjects: Array<out Project>,
filter: (KonanBuildingConfig<*>) -> Boolean) {
libraryProjects.forEach { prj ->
project.evaluationDependsOn(prj)
prj.konanArtifactsContainer.filter(filter).forEach {
klibInternal(it, false)
}
}
}
/** All libraries (both interop and non-interop ones) from the projects by direct references */
fun allLibrariesFrom(vararg libraryProjects: Project) = allArtifactsFromInternal(libraryProjects) {
it is KonanLibrary || it is KonanInteropLibrary
}
/** All interop libraries from the projects by direct references */
fun allInteropLibrariesFrom(vararg libraryProjects: Project) = allArtifactsFromInternal(libraryProjects) {
it is KonanInteropLibrary
}
/** Add repo for library search */
fun useRepo(directory: Any) = explicitRepos.add(project.file(directory))
/** Add repos for library search */
fun useRepos(vararg directories: Any) = directories.forEach { useRepo(it) }
/** Add repos for library search */
fun useRepos(directories: Iterable<Any>) = directories.forEach { useRepo(it) }
private fun Project.evaluationDependsOn(another: Project) {
if (this != another) { evaluationDependsOn(another.path) }
}
fun asFiles(): List<File> = asFiles(
defaultResolver(
repos.map { it.absolutePath },
task.konanTarget,
Distribution(project.konanHome)
)
)
fun asFiles(resolver: SearchPathResolver<*>): List<File> = mutableListOf<File>().apply {
files.flatMapTo(this) { it.files }
addAll(artifactFiles)
addAll(task.platformConfiguration.files)
namedKlibs.mapTo(this) { project.file(resolver.resolve(it).libraryFile.absolutePath) }
}
}
@@ -0,0 +1,445 @@
/*
* 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.plugin.konan
import groovy.lang.Closure
import org.codehaus.groovy.runtime.GStringImpl
import org.gradle.api.*
import org.gradle.api.component.ComponentWithVariants
import org.gradle.api.component.SoftwareComponent
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.gradle.language.cpp.internal.NativeVariantIdentity
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.Companion.COMPILE_ALL_TASK_NAME
import org.jetbrains.kotlin.gradle.plugin.tasks.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.buildDistribution
import org.jetbrains.kotlin.konan.target.customerDistribution
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.konan.util.DependencyDirectories
import java.io.File
import javax.inject.Inject
/**
* We use the following properties:
* org.jetbrains.kotlin.native.home - directory where compiler is located (aka dist in konan project output).
* org.jetbrains.kotlin.native.version - a konan compiler version for downloading.
* konan.build.targets - list of targets to build (by default all the declared targets are built).
* konan.jvmArgs - additional args to be passed to a JVM executing the compiler/cinterop tool.
*/
internal fun Project.warnAboutDeprecatedProperty(property: KonanPlugin.ProjectProperty) =
property.deprecatedPropertyName?.let { deprecated ->
if (project.hasProperty(deprecated)) {
logger.warn("Project property '$deprecated' is deprecated. Use '${property.propertyName}' instead.")
}
}
internal fun Project.hasProperty(property: KonanPlugin.ProjectProperty) = with(property) {
when {
hasProperty(propertyName) -> true
deprecatedPropertyName != null && hasProperty(deprecatedPropertyName) -> true
else -> false
}
}
internal fun Project.findProperty(property: KonanPlugin.ProjectProperty): Any? = with(property) {
return findProperty(propertyName) ?: deprecatedPropertyName?.let { findProperty(it) }
}
internal fun Project.getProperty(property: KonanPlugin.ProjectProperty) = findProperty(property)
?: throw IllegalArgumentException("No such property in the project: ${property.propertyName}")
internal fun Project.getProperty(property: KonanPlugin.ProjectProperty, defaultValue: Any) =
findProperty(property) ?: defaultValue
internal fun Project.setProperty(property: KonanPlugin.ProjectProperty, value: Any) {
extensions.extraProperties.set(property.propertyName, value)
}
// konanHome extension is set by downloadKonanCompiler task.
internal val Project.konanHome: String
get() {
return project.kotlinNativeDist.absolutePath
}
// Used only for distribution downloading that is not used in the project and should be removed
internal val Project.konanVersion: String
get() = project.findProperty(KonanPlugin.ProjectProperty.KONAN_VERSION)
?.toString()
?: project.version.toString()
internal val Project.konanBuildRoot get() = buildDir.resolve("konan")
internal val Project.konanBinBaseDir get() = konanBuildRoot.resolve("bin")
internal val Project.konanLibsBaseDir get() = konanBuildRoot.resolve("libs")
internal val Project.konanBitcodeBaseDir get() = konanBuildRoot.resolve("bitcode")
internal fun File.targetSubdir(target: KonanTarget) = resolve(target.visibleName)
internal val Project.konanDefaultSrcFiles get() = fileTree("${projectDir.canonicalPath}/src/main/kotlin")
internal fun Project.konanDefaultDefFile(libName: String)
= file("${projectDir.canonicalPath}/src/main/c_interop/$libName.def")
@Suppress("UNCHECKED_CAST")
internal val Project.konanArtifactsContainer: KonanArtifactContainer
get() = extensions.getByName(KonanPlugin.ARTIFACTS_CONTAINER_NAME) as KonanArtifactContainer
// TODO: The Kotlin/Native compiler is downloaded manually by a special task so the compilation tasks
// are configured without the compile distribution. After target management refactoring
// we need .properties files from the distribution to configure targets. This is worked around here
// by using HostManager instead of PlatformManager. But we need to download the compiler at the configuration
// stage (e.g. by getting it from maven as a plugin dependency) and bring back the PlatformManager here.
internal val Project.hostManager: HostManager
get() = findProperty("hostManager") as HostManager? ?:
if (hasProperty("org.jetbrains.kotlin.native.experimentalTargets"))
HostManager(buildDistribution(rootProject.rootDir.absolutePath), true)
else
HostManager(customerDistribution(konanHome))
internal val Project.konanTargets: List<KonanTarget>
get() = hostManager.toKonanTargets(konanExtension.targets)
.filter{ hostManager.isEnabled(it) }
.distinct()
@Suppress("UNCHECKED_CAST")
internal val Project.konanExtension: KonanExtension
get() = extensions.getByName(KonanPlugin.KONAN_EXTENSION_NAME) as KonanExtension
internal val Project.requestedTargets
get() = findProperty(KonanPlugin.ProjectProperty.KONAN_BUILD_TARGETS)?.let {
it.toString().trim().split("\\s+".toRegex())
}.orEmpty()
internal val Project.jvmArgs
get() = (findProperty(KonanPlugin.ProjectProperty.KONAN_JVM_ARGS) as String?)?.split("\\s+".toRegex()).orEmpty()
internal val Project.compileAllTask
get() = getOrRegisterTask(COMPILE_ALL_TASK_NAME)
internal fun Project.targetIsRequested(target: KonanTarget): Boolean {
val targets = requestedTargets
return (targets.isEmpty() || targets.contains(target.visibleName) || targets.contains("all"))
}
/**
* Looks for task with given name in the given project.
* If such task isn't found, will register it. Returns registered/found task.
*/
private fun Project.getOrRegisterTask(name: String): TaskProvider<out Task> = if (tasks.names.contains(name)) {
tasks.named(name)
} else {
tasks.register(name, DefaultTask::class.java)
}
internal fun Project.konanCompilerName(): String =
"kotlin-native-${project.simpleOsName}-${project.konanVersion}"
internal fun Project.konanCompilerDownloadDir(): String =
DependencyDirectories.localKonanDir.resolve(project.konanCompilerName()).absolutePath
// region Useful extensions and functions ---------------------------------------
internal fun MutableList<String>.addArg(parameter: String, value: String) {
add(parameter)
add(value)
}
internal fun MutableList<String>.addArgs(parameter: String, values: Iterable<String>) {
values.forEach {
addArg(parameter, it)
}
}
internal fun MutableList<String>.addArgIfNotNull(parameter: String, value: String?) {
if (value != null) {
addArg(parameter, value)
}
}
internal fun MutableList<String>.addKey(key: String, enabled: Boolean) {
if (enabled) {
add(key)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: FileCollection) {
values.files.forEach {
addArg(parameter, it.canonicalPath)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: Collection<FileCollection>) {
values.forEach {
addFileArgs(parameter, it)
}
}
// endregion
internal fun dumpProperties(task: Task) {
fun Iterable<String>.dump() = joinToString(prefix = "[", separator = ",\n${" ".repeat(22)}", postfix = "]")
fun Collection<FileCollection>.dump() = flatMap { it.files }.map { it.canonicalPath }.dump()
when (task) {
is KonanCompileTask -> with(task) {
println()
println("Compilation task: $name")
println("destinationDir : $destinationDir")
println("artifact : ${artifact.canonicalPath}")
println("srcFiles : ${srcFiles.dump()}")
println("produce : $produce")
println("libraries : ${libraries.files.dump()}")
println(" : ${libraries.artifacts.map {
it.artifact.canonicalPath
}.dump()}")
println(" : ${libraries.namedKlibs.dump()}")
println("nativeLibraries : ${nativeLibraries.dump()}")
println("linkerOpts : $linkerOpts")
println("enableDebug : $enableDebug")
println("noStdLib : $noStdLib")
println("noMain : $noMain")
println("enableOptimization : $enableOptimizations")
println("enableAssertions : $enableAssertions")
println("noDefaultLibs : $noDefaultLibs")
println("noEndorsedLibs : $noEndorsedLibs")
println("target : $target")
println("languageVersion : $languageVersion")
println("apiVersion : $apiVersion")
println("konanVersion : ${KotlinVersion.CURRENT}")
println("konanHome : $konanHome")
println()
}
is KonanInteropTask -> with(task) {
println()
println("Stub generation task: $name")
println("destinationDir : $destinationDir")
println("artifact : $artifact")
println("libraries : ${libraries.files.dump()}")
println(" : ${libraries.artifacts.map {
it.artifact.canonicalPath
}.dump()}")
println(" : ${libraries.namedKlibs.dump()}")
println("defFile : $defFile")
println("target : $target")
println("packageName : $packageName")
println("compilerOpts : $compilerOpts")
println("linkerOpts : $linkerOpts")
println("headers : ${headers.dump()}")
println("linkFiles : ${linkFiles.dump()}")
println("konanVersion : ${KotlinVersion.CURRENT}")
println("konanHome : $konanHome")
println()
}
else -> {
println("Unsupported task.")
}
}
}
open class KonanExtension {
var targets = mutableListOf("host")
var languageVersion: String? = null
var apiVersion: String? = null
var jvmArgs = mutableListOf<String>()
}
open class KonanSoftwareComponent(val project: ProjectInternal?): SoftwareComponentInternal, ComponentWithVariants {
private val usages = mutableSetOf<UsageContext>()
override fun getUsages(): MutableSet<out UsageContext> = usages
private val variants = mutableSetOf<SoftwareComponent>()
override fun getName() = "main"
override fun getVariants(): Set<SoftwareComponent> = variants
fun addVariant(component: SoftwareComponent) = variants.add(component)
}
class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderRegistry)
: Plugin<ProjectInternal> {
enum class ProjectProperty(val propertyName: String, val deprecatedPropertyName: String? = null) {
KONAN_HOME ("org.jetbrains.kotlin.native.home", "konan.home"),
KONAN_VERSION ("org.jetbrains.kotlin.native.version"),
KONAN_BUILD_TARGETS ("konan.build.targets"),
KONAN_JVM_ARGS ("konan.jvmArgs"),
KONAN_JVM_LAUNCHER ("konan.javaLauncher"),
KONAN_USE_ENVIRONMENT_VARIABLES("konan.useEnvironmentVariables"),
DOWNLOAD_COMPILER ("download.compiler"),
// Properties used instead of env vars until https://github.com/gradle/gradle/issues/3468 is fixed.
// TODO: Remove them when an API for env vars is provided.
KONAN_CONFIGURATION_BUILD_DIR ("konan.configuration.build.dir"),
KONAN_DEBUGGING_SYMBOLS ("konan.debugging.symbols"),
KONAN_OPTIMIZATIONS_ENABLE ("konan.optimizations.enable"),
}
companion object {
internal const val ARTIFACTS_CONTAINER_NAME = "konanArtifacts"
internal const val COMPILE_ALL_TASK_NAME = "compileKonan"
internal const val KONAN_EXTENSION_NAME = "konan"
internal val REQUIRED_GRADLE_VERSION = GradleVersion.version("6.7")
}
private fun Project.cleanKonan() = project.tasks.withType(KonanBuildingTask::class.java).forEach {
project.delete(it.artifact)
}
private fun checkGradleVersion() = GradleVersion.current().let { current ->
check(current >= REQUIRED_GRADLE_VERSION) {
"Kotlin/Native Gradle plugin is incompatible with this version of Gradle.\n" +
"The minimal required version is $REQUIRED_GRADLE_VERSION\n" +
"Current version is ${current}"
}
}
private lateinit var konanJvmLauncher: JavaLauncher
private fun getJavaLauncher(project: Project): Provider<JavaLauncher> = project.providers.provider {
if (!::konanJvmLauncher.isInitialized) {
val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain
val service = project.extensions.getByType(JavaToolchainService::class.java)
konanJvmLauncher = try {
service.launcherFor(toolchain).get()
} catch (ex: GradleException) {
// If the JDK that was set is not available get the JDK 11 as a default
service.launcherFor(object : Action<JavaToolchainSpec> {
override fun execute(toolchainSpec: JavaToolchainSpec) {
toolchainSpec.languageVersion.set(JavaLanguageVersion.of(11)) // FIXME: not resolved from buildSrc JdkMajorVersion.JDK_11_0.majorVersion))
}
}).get()
}
}
konanJvmLauncher
}
override fun apply(project: ProjectInternal) {
checkGradleVersion()
project.plugins.apply("base")
project.plugins.apply("java")
// Create necessary tasks and extensions.
project.extensions.create(KONAN_EXTENSION_NAME, KonanExtension::class.java)
val container = project.extensions.create(
KonanArtifactContainer::class.java,
ARTIFACTS_CONTAINER_NAME,
KonanArtifactContainer::class.java,
project
)
project.setProperty(ProjectProperty.KONAN_JVM_LAUNCHER, getJavaLauncher(project))
project.warnAboutDeprecatedProperty(ProjectProperty.KONAN_HOME)
// Set additional project properties like org.jetbrains.kotlin.native.home, konan.build.targets etc.
if (!project.useCustomDist) {
project.setProperty(ProjectProperty.KONAN_HOME, project.konanCompilerDownloadDir())
project.setProperty(ProjectProperty.DOWNLOAD_COMPILER, true)
}
// Register and set up aggregate building tasks.
val compileKonanTask = project.getOrRegisterTask(COMPILE_ALL_TASK_NAME).configure {
group = BasePlugin.BUILD_GROUP
description = "Compiles all the Kotlin/Native artifacts"
}
project.tasks.named("build").configure {
dependsOn(compileKonanTask)
}
project.tasks.named("clean").configure {
doLast { project.cleanKonan() }
}
project.afterEvaluate {
project.tasks
.withType(KonanCompileProgramTask::class.java)
.forEach { task ->
val isCrossCompile = (task.target != HostManager.host.visibleName)
if (!isCrossCompile && !project.hasProperty("konanNoRun"))
task.runTask = project.tasks.register(
"run${task.artifactName.replaceFirstChar { it.uppercase() }}", Exec::class.java) {
group = "run"
dependsOn(task)
val artifactPathClosure = object : Closure<String>(this) {
override fun call() = task.artifactPath
}
// Use GString to evaluate a path to the artifact lazily thus allow changing it at configuration phase.
val lazyArtifactPath = GStringImpl(arrayOf(artifactPathClosure), arrayOf(""))
executable(lazyArtifactPath)
// Add values passed in the runArgs project property as arguments.
argumentProviders.add(task.RunArgumentProvider())
}
}
}
val runTask = project.getOrRegisterTask("run")
project.afterEvaluate {
project.konanArtifactsContainer
.filterIsInstance(KonanProgram::class.java)
.forEach { program ->
program.tasks().forEach { compile ->
compile.configure { this@configure.runTask?.let { runTask.configure { dependsOn(it) } } }
}
}
}
// Enable multiplatform support
project.pluginManager.apply(KotlinNativePlatformPlugin::class.java)
project.afterEvaluate {
project.pluginManager.withPlugin("maven-publish") {
container.all { buildingConfig ->
val konanSoftwareComponent = buildingConfig.mainVariant
project.extensions.configure(PublishingExtension::class.java) {
val builtArtifact = buildingConfig.name
val mavenPublication = publications.maybeCreate(builtArtifact, MavenPublication::class.java)
mavenPublication.apply {
artifactId = builtArtifact
groupId = project.group.toString()
from(konanSoftwareComponent)
}
(mavenPublication as MavenPublicationInternal).publishWithOriginalFileName()
buildingConfig.pomActions.forEach {
mavenPublication.pom(it)
}
}
project.extensions.configure(PublishingExtension::class.java) {
for (v in konanSoftwareComponent.variants) {
this@configure.publications.create(v.name, MavenPublication::class.java) {
val coordinates = (v as NativeVariantIdentity).coordinates
project.logger.info("variant with coordinates($coordinates) and module: ${coordinates.module}")
artifactId = coordinates.module.name
groupId = coordinates.group
version = coordinates.version
from(v)
(this as MavenPublicationInternal).publishWithOriginalFileName()
buildingConfig.pomActions.forEach {
pom(it)
}
}
}
}
true
}
}
}
}
}
@@ -0,0 +1,106 @@
/*
* 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.plugin.konan
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.file.FileCollection
interface KonanArtifactSpec {
fun artifactName(name: String)
}
interface KonanArtifactWithLibrariesSpec: KonanArtifactSpec {
fun libraries(closure: Closure<Unit>)
fun libraries(action: Action<KonanLibrariesSpec>)
fun libraries(configure: KonanLibrariesSpec.() -> Unit)
fun noDefaultLibs(flag: Boolean)
fun noEndorsedLibs(flag: Boolean)
fun dependencies(closure: Closure<Unit>)
}
interface KonanBuildingSpec: KonanArtifactWithLibrariesSpec {
fun dumpParameters(flag: Boolean)
fun extraOpts(vararg values: Any)
fun extraOpts(values: List<Any>)
}
interface KonanCompileSpec: KonanBuildingSpec {
fun srcDir(dir: Any)
fun srcFiles(vararg files: Any)
fun srcFiles(files: Collection<Any>)
// DSL. Native libraries.
fun nativeLibrary(lib: Any)
fun nativeLibraries(vararg libs: Any)
fun nativeLibraries(libs: FileCollection)
// DSL. Multiplatform projects
fun enableMultiplatform(flag: Boolean)
// TODO: Get rid of commonSourceSet in 0.7
@Deprecated("Use commonSourceSets instead", ReplaceWith("commonSourceSets(sourceSetName)"))
fun commonSourceSet(sourceSetName: String)
fun commonSourceSets(vararg sourceSetNames: String)
fun commonSrcDir(dir: Any)
fun commonSrcFiles(vararg files: Any)
fun commonSrcFiles(files: Collection<Any>)
// DSL. Other parameters.
fun linkerOpts(vararg values: String)
fun linkerOpts(values: List<String>)
fun enableDebug(flag: Boolean)
fun noStdLib(flag: Boolean)
fun noMain(flag: Boolean)
fun noPack(flag: Boolean)
fun enableOptimizations(flag: Boolean)
fun enableAssertions(flag: Boolean)
fun entryPoint(entryPoint: String)
fun measureTime(flag: Boolean)
}
interface KonanInteropSpec: KonanBuildingSpec {
interface IncludeDirectoriesSpec {
fun allHeaders(vararg includeDirs: Any)
fun allHeaders(includeDirs: Collection<Any>)
fun headerFilterOnly(vararg includeDirs: Any)
fun headerFilterOnly(includeDirs: Collection<Any>)
}
fun defFile(file: Any)
fun packageName(value: String)
fun compilerOpts(vararg values: String)
fun header(file: Any) = headers(file)
fun headers(vararg files: Any)
fun headers(files: FileCollection)
fun includeDirs(vararg values: Any)
fun includeDirs(closure: Closure<Unit>)
fun includeDirs(action: Action<IncludeDirectoriesSpec>)
fun includeDirs(configure: IncludeDirectoriesSpec.() -> Unit)
fun linkerOpts(vararg values: String)
fun linkerOpts(values: List<String>)
fun link(vararg files: Any)
fun link(files: FileCollection)
}
@@ -0,0 +1,181 @@
/*
* 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.plugin.konan
import kotlinBuildProperties
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.ProjectProperty.KONAN_HOME
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.nio.file.Files
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.konan.properties.resolvablePropertyString
import org.jetbrains.kotlin.konan.util.DependencyDirectories
import java.io.File
import java.util.Properties
import org.jetbrains.kotlin.compilerRunner.KotlinToolRunner
import org.jetbrains.kotlin.konan.target.AbstractToolConfig
import java.net.URLClassLoader
import java.util.concurrent.ConcurrentHashMap
internal interface KonanToolRunner {
fun run(args: List<String>)
}
internal fun KonanToolRunner.run(vararg args: String) = run(args.toList())
private const val runFromDaemonPropertyName = "kotlin.native.tool.runFromDaemon"
@Suppress("DEPRECATION") // calling KotlinToolRunner(project) constructor is deprecated
internal abstract class KonanCliRunner(
protected val toolName: String,
project: Project,
val additionalJvmArgs: List<String> = emptyList(),
val konanHome: String = project.konanHome
) : KotlinToolRunner(project), KonanToolRunner {
final override val displayName get() = toolName
final override val mainClass get() = "org.jetbrains.kotlin.cli.utilities.MainKt"
final override val daemonEntryPoint get() = "daemonMain"
override val mustRunViaExec get() = false.also { System.setProperty(runFromDaemonPropertyName, "true") }
final override val execSystemPropertiesBlacklist: Set<String>
get() = super.execSystemPropertiesBlacklist + runFromDaemonPropertyName
// We need to unset some environment variables which are set by XCode and may potentially affect the tool executed.
final override val execEnvironmentBlacklist: Set<String> by lazy {
HashSet<String>().also { collector ->
KonanPlugin::class.java.getResourceAsStream("/env_blacklist")?.let { stream ->
stream.reader().use { r -> r.forEachLine { collector.add(it) } }
}
}
}
final override val execSystemProperties by lazy { mapOf("konan.home" to konanHome) }
final override val classpath by lazy {
project.fileTree("$konanHome/konan/lib/").apply {
include("trove4j.jar")
include("kotlin-native-compiler-embeddable.jar")
}.files
}
final override fun checkClasspath() =
check(classpath.isNotEmpty()) {
"""
Classpath of the tool is empty: $toolName
Probably the '${KONAN_HOME.propertyName}' project property contains an incorrect path.
Please change it to the compiler root directory and rerun the build.
""".trimIndent()
}
data class IsolatedClassLoaderCacheKey(val classpath: Set<File>)
// TODO: can't we use this for other implementations too?
final override val isolatedClassLoaderCacheKey get() = IsolatedClassLoaderCacheKey(classpath)
// A separate map for each build for automatic cleaning the daemon after the build have finished.
@Suppress("UNCHECKED_CAST")
final override val isolatedClassLoaders = project.project(":kotlin-native").ext["toolClassLoadersMap"] as ConcurrentHashMap<Any, URLClassLoader>
override fun transformArgs(args: List<String>) = listOf(toolName) + args
final override fun getCustomJvmArgs() = additionalJvmArgs
}
/** Kotlin/Native compiler runner */
internal class KonanCliCompilerRunner(
project: Project,
additionalJvmArgs: List<String> = emptyList(),
val useArgFile: Boolean = true,
konanHome: String = project.konanHome
) : KonanCliRunner("konanc", project, additionalJvmArgs, konanHome) {
override fun transformArgs(args: List<String>): List<String> {
if (!useArgFile) return super.transformArgs(args)
val argFile = Files.createTempFile(/* prefix = */ "konancArgs", /* suffix = */ ".lst").toFile().apply { deleteOnExit() }
argFile.printWriter().use { w ->
for (arg in args) {
val escapedArg = arg
.replace("\\", "\\\\")
.replace("\"", "\\\"")
w.println("\"$escapedArg\"")
}
}
return listOf(toolName, "@${argFile.absolutePath}")
}
}
private val load0 = Runtime::class.java.getDeclaredMethod("load0", Class::class.java, String::class.java).also {
it.isAccessible = true
}
internal class CliToolConfig(konanHome: String, target: String) : AbstractToolConfig(konanHome, target, emptyMap()) {
override fun loadLibclang() {
// Load libclang into the system class loader. This is needed to allow developers to make changes
// in the tooling infrastructure without having to stop the daemon (otherwise libclang might end up
// loaded in two different class loaders which is not allowed by the JVM).
load0.invoke(Runtime.getRuntime(), String::class.java, libclang)
}
}
/** Kotlin/Native C-interop tool runner */
internal class KonanCliInteropRunner(
private val project: Project,
additionalJvmArgs: List<String> = emptyList(),
konanHome: String = project.konanHome
) : KonanCliRunner("cinterop", project, additionalJvmArgs, konanHome) {
private val projectDir = project.projectDir.toString()
override val mustRunViaExec: Boolean
get() = if (project.kotlinBuildProperties.getBoolean("kotlin.native.allowRunningCinteropInProcess")) {
super.mustRunViaExec
} else {
true
}
override fun transformArgs(args: List<String>): List<String> {
return super.transformArgs(args) + listOf("-Xproject-dir", projectDir)
}
override val execEnvironment by lazy {
val result = mutableMapOf<String, String>()
result.putAll(super.execEnvironment)
result["LIBCLANG_DISABLE_CRASH_RECOVERY"] = "1"
llvmExecutablesPath?.let {
result["PATH"] = "$it;${System.getenv("PATH")}"
}
result
}
fun init(target: String) {
CliToolConfig(konanHome, target).prepare()
}
private val llvmExecutablesPath: String? by lazy {
if (HostManager.host == KonanTarget.MINGW_X64) {
// TODO: Read it from Platform properties when it is accessible.
val konanProperties = Properties().apply {
project.file("$konanHome/konan/konan.properties").inputStream().use(::load)
}
konanProperties.resolvablePropertyString("llvmHome.mingw_x64")?.let { toolchainDir ->
DependencyDirectories.defaultDependenciesRoot
.resolve("$toolchainDir/bin")
.absolutePath
}
} else
null
}
}
internal class KonanKlibRunner(
project: Project,
additionalJvmArgs: List<String> = emptyList(),
konanHome: String = project.konanHome
) : KonanCliRunner("klib", project, additionalJvmArgs, konanHome)
@@ -0,0 +1,43 @@
/*
* 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.plugin.konan
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformImplementationPluginBase
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileTask
import javax.inject.Inject
open class KotlinNativePlatformPlugin: KotlinPlatformImplementationPluginBase("native") {
private val Project.konanMultiplatformTasks: Collection<KonanCompileTask>
get() = tasks.withType(KonanCompileTask::class.java).filter { it.enableMultiplatform }
open class RequestedCommonSourceSet @Inject constructor(private val name: String): Named {
override fun getName() = name
}
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) {
val commonSourceSetName = commonSourceSet.name
platformProject.konanMultiplatformTasks
.filter { it.commonSourceSets.contains(commonSourceSetName) }
.forEach { task: KonanCompileTask ->
getKotlinSourceDirectorySetSafe(commonSourceSet)!!.srcDirs.forEach {
task.commonSrcDir(it)
}
}
}
override fun namedSourceSetsContainer(project: Project): NamedDomainObjectContainer<*> =
project.container(RequestedCommonSourceSet::class.java).apply {
project.konanMultiplatformTasks.forEach { task ->
task.commonSourceSets.forEach { maybeCreate(it) }
}
}
}
@@ -0,0 +1,178 @@
/*
* 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.plugin.tasks
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage
import org.gradle.api.capabilities.Capability
import org.gradle.api.internal.component.DefaultSoftwareComponentVariant
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.tasks.DefaultTaskDependency
import org.gradle.api.tasks.*
import org.gradle.language.cpp.CppBinary
import org.gradle.nativeplatform.Linkage
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.compatibleVariantIdentity
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.konanTargets
import java.io.File
import java.util.*
internal val Project.host
get() = HostManager.host.visibleName
internal val Project.simpleOsName
get() = HostManager.platformName()
/** A task with a KonanTarget specified. */
abstract class KonanTargetableTask: DefaultTask() {
@get:Input
val konanTargetName: String
get() = konanTarget.name
@get:Internal
internal lateinit var konanTarget: KonanTarget
internal open fun init(target: KonanTarget) {
this.konanTarget = target
}
val isCrossCompile: Boolean
@Internal get() = (konanTarget != HostManager.host)
val target: String
@Internal get() = konanTarget.visibleName
}
/** A task building an artifact. */
abstract class KonanArtifactTask: KonanTargetableTask(), KonanArtifactSpec {
open val artifact: File
@OutputFile get() = destinationDir.resolve(artifactFullName)
@Internal lateinit var destinationDir: File
@Internal lateinit var artifactName: String
@Internal lateinit var platformConfiguration: Configuration
@Internal lateinit var configuration: Configuration
protected val artifactFullName: String
@Internal get() = "$artifactPrefix$artifactName$artifactSuffix"
val artifactPath: String
@Internal get() = artifact.canonicalPath
protected abstract val artifactSuffix: String
@Internal get
protected abstract val artifactPrefix: String
@Internal get
internal open fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
super.init(target)
this.destinationDir = destinationDir
this.artifactName = artifactName
configuration = project.configurations.maybeCreate("artifact$artifactName")
platformConfiguration = project.configurations.create("artifact${artifactName}_${target.name}")
platformConfiguration.extendsFrom(configuration)
platformConfiguration.attributes{
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.NATIVE_LINK))
attribute(CppBinary.LINKAGE_ATTRIBUTE, Linkage.STATIC)
attribute(CppBinary.OPTIMIZED_ATTRIBUTE, false)
attribute(CppBinary.DEBUGGABLE_ATTRIBUTE, false)
attribute(Attribute.of("org.gradle.native.kotlin.platform", String::class.java), target.name)
}
val artifactNameWithoutSuffix = artifact.name.removeSuffix("$artifactSuffix")
project.pluginManager.withPlugin("maven-publish") {
platformConfiguration.artifacts.add(object: PublishArtifact {
override fun getName(): String = artifactNameWithoutSuffix
override fun getExtension() = if (artifactSuffix.startsWith('.')) artifactSuffix.substring(1) else artifactSuffix
override fun getType() = artifactSuffix
override fun getClassifier():String? = target.name
override fun getFile() = artifact
override fun getDate() = Date(artifact.lastModified())
override fun getBuildDependencies(): TaskDependency =
DefaultTaskDependency().apply { add(this@KonanArtifactTask) }
})
val objectFactory = project.objects
val linkUsage = objectFactory.named(Usage::class.java, Usage.NATIVE_LINK)
val konanSoftwareComponent = config.mainVariant
val variantName = "${artifactNameWithoutSuffix}_${target.name}"
val context = DefaultSoftwareComponentVariant(
"${variantName}Link",
platformConfiguration.attributes,
platformConfiguration.allArtifacts,
mutableSetOf(),
mutableSetOf(),
mutableSetOf(),
emptySet(),
)
konanSoftwareComponent.addVariant(
compatibleVariantIdentity(
project,
variantName,
project.provider{ artifactName },
project.provider{ project.group.toString() },
project.provider{ project.version.toString() },
false,
false,
target,
context,
null
)
)
}
}
fun dependencies(closure: Closure<Unit>) {
if (konanTarget in project.konanTargets)
project.dependencies(closure)
}
// DSL.
override fun artifactName(name: String) {
artifactName = name
}
fun destinationDir(dir: Any) {
destinationDir = project.file(dir)
}
}
/** Task building an artifact with libraries */
abstract class KonanArtifactWithLibrariesTask: KonanArtifactTask(), KonanArtifactWithLibrariesSpec {
@Nested
val libraries = KonanLibrariesSpec(this, project)
@Input
var noDefaultLibs = false
@Input
var noEndorsedLibs = false
// DSL
override fun libraries(closure: Closure<Unit>) = libraries { project.configure(this, closure) }
override fun libraries(action: Action<KonanLibrariesSpec>) = libraries { action.execute(this) }
override fun libraries(configure: KonanLibrariesSpec.() -> Unit) { libraries.configure() }
override fun noDefaultLibs(flag: Boolean) {
noDefaultLibs = flag
}
override fun noEndorsedLibs(flag: Boolean) {
noEndorsedLibs = flag
}
}
@@ -0,0 +1,53 @@
/*
* 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.plugin.tasks
import org.gradle.api.tasks.Console
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import org.jetbrains.kotlin.dependsOnDist
import org.jetbrains.kotlin.gradle.plugin.konan.KonanBuildingConfig
import org.jetbrains.kotlin.gradle.plugin.konan.KonanBuildingSpec
import org.jetbrains.kotlin.gradle.plugin.konan.KonanToolRunner
import org.jetbrains.kotlin.gradle.plugin.konan.konanHome
/** Base class for both interop and compiler tasks. */
abstract class KonanBuildingTask: KonanArtifactWithLibrariesTask(), KonanBuildingSpec {
@get:Internal
internal abstract val toolRunner: KonanToolRunner
override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
super.init(config, destinationDir, artifactName, target)
}
@Console
var dumpParameters: Boolean = false
@Input
val extraOpts = mutableListOf<String>()
val konanHome
@Input get() = project.konanHome
@TaskAction
abstract fun run()
// DSL.
override fun dumpParameters(flag: Boolean) {
dumpParameters = flag
}
override fun extraOpts(vararg values: Any) = extraOpts(values.toList())
override fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
}
@@ -0,0 +1,111 @@
/*
* 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.plugin.konan.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.Directory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.plugin.konan.KonanCliCompilerRunner
import org.jetbrains.kotlin.gradle.plugin.konan.konanHome
import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.PlatformManager
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.*
import java.io.File
enum class KonanCacheKind(val outputKind: CompilerOutputKind) {
STATIC(CompilerOutputKind.STATIC_CACHE),
DYNAMIC(CompilerOutputKind.DYNAMIC_CACHE)
}
open class KonanCacheTask: DefaultTask() {
@get:InputDirectory
var originalKlib: File? = null
@get:Input
lateinit var klibUniqName: String
@get:Input
lateinit var cacheRoot: String
@get:Input
lateinit var target: String
@get:Internal
// TODO: Reuse NativeCacheKind from Big Kotlin plugin when it is available.
val cacheDirectory: File
get() = File("$cacheRoot/$target-g$cacheKind")
@get:OutputDirectory
val cacheFile: File
get() = cacheDirectory.resolve(if (makePerFileCache) "${klibUniqName}-per-file-cache" else "${klibUniqName}-cache")
/**
* Note: we can't use this function instead of [klibUniqName] in [cacheFile],
* because the latter is `@OutputDirectory`, so Gradle can call it even before
* the task dependencies are finished, and [originalKlib] might be not build yet.
*/
private fun readKlibUniqNameFromManifest(): String {
val konanHome = compilerDistributionPath.get().absolutePath
val resolver = defaultResolver(
emptyList(),
PlatformManager(konanHome).targetByName(target),
Distribution(konanHome)
)
return resolver.resolve(originalKlib!!.absolutePath).uniqueName
}
@get:Input
var cacheKind: KonanCacheKind = KonanCacheKind.STATIC
@get:Input
var makePerFileCache: Boolean = false
@get:Input
/** Path to a compiler distribution that is used to build this cache. */
val compilerDistributionPath: Property<File> = project.objects.property(File::class.java).apply {
set(project.provider { project.kotlinNativeDist })
}
@get:Input
var cachedLibraries: Map<File, File> = emptyMap()
@TaskAction
fun compile() {
// This code uses bootstrap version of util-klib and fails due to the older default ABI than library being used
// A possible solution is to read it manually from manifest file or this check should be done by the compiler itself
// check(klibUniqName == readKlibUniqNameFromManifest()) {
// "klibUniqName mismatch: configured '$klibUniqName', resolved '${readKlibUniqNameFromManifest()}'"
// }
// Compiler doesn't create a cache if the cacheFile already exists. So we need to remove it manually.
if (cacheFile.exists()) {
val deleted = cacheFile.deleteRecursively()
check(deleted) { "Cannot delete stale cache: ${cacheFile.absolutePath}" }
}
cacheDirectory.mkdirs()
val konanHome = compilerDistributionPath.get().absolutePath
val additionalCacheFlags = PlatformManager(konanHome).let {
it.targetByName(target).let(it::loader).additionalCacheFlags
}
requireNotNull(originalKlib)
val args = mutableListOf(
"-g",
"-target", target,
"-produce", cacheKind.outputKind.name.toLowerCase(),
"-Xadd-cache=${originalKlib?.absolutePath}",
"-Xcache-directory=${cacheDirectory.absolutePath}"
)
if (makePerFileCache)
args += "-Xmake-per-file-cache"
args += additionalCacheFlags
args += cachedLibraries.map { "-Xcached-library=${it.key},${it.value}" }
KonanCliCompilerRunner(project, konanHome = konanHome).run(args)
}
}
@@ -0,0 +1,414 @@
/*
* 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.plugin.tasks
import groovy.lang.Closure
import org.codehaus.groovy.runtime.GStringImpl
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.*
import org.gradle.process.CommandLineArgumentProvider
import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.dumpProperties
import org.jetbrains.kotlin.gradle.plugin.konan.environmentVariables
import org.jetbrains.kotlin.gradle.plugin.konan.konanBuildRoot
import org.jetbrains.kotlin.gradle.plugin.konan.targetSubdir
import java.io.File
/**
* A task compiling the target executable/library using Kotlin/Native compiler
*/
abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
@get:Internal
override val toolRunner = KonanCliCompilerRunner(project, project.konanExtension.jvmArgs)
abstract val produce: CompilerOutputKind
@Internal get
// Output artifact --------------------------------------------------------
override val artifactSuffix: String
@Internal get() = produce.suffix(konanTarget)
override val artifactPrefix: String
@Internal get() = produce.prefix(konanTarget)
// Multiplatform support --------------------------------------------------
@Input var commonSourceSets = listOf("main")
@Internal var enableMultiplatform = false
private val commonSrcFiles_ = mutableSetOf<FileCollection>()
val commonSrcFiles: Collection<FileCollection>
@Internal get() = if (enableMultiplatform) commonSrcFiles_ else emptyList()
// Other compilation parameters -------------------------------------------
private val srcFiles_ = mutableSetOf<FileCollection>()
val srcFiles: Collection<FileCollection>
@Internal get() = srcFiles_.takeIf { !it.isEmpty() } ?: listOf(project.konanDefaultSrcFiles)
val allSources: Collection<FileCollection>
@InputFiles get() = listOf(srcFiles, commonSrcFiles).flatten()
private val allSourceFiles: List<File>
get() = allSources
.flatMap { it.files }
.filter { it.name.endsWith(".kt") }
@InputFiles val nativeLibraries = mutableSetOf<FileCollection>()
@Input val linkerOpts = mutableListOf<String>()
@Input var enableDebug = project.findProperty("enableDebug")?.toString()?.toBoolean()
?: project.environmentVariables.debuggingSymbols
@Input var noStdLib = false
@Input var noMain = false
@Input var noPack: Boolean = false
@Input var enableOptimizations = project.environmentVariables.enableOptimizations
@Input var enableAssertions = false
@Optional @Input var entryPoint: String? = null
@Console var measureTime = false
val languageVersion : String?
@Optional @Input get() = project.konanExtension.languageVersion
val apiVersion : String?
@Optional @Input get() = project.konanExtension.apiVersion
/**
* Is the two-stage compilation enabled.
*
* In regular (one-stage) compilation, sources are directly compiled into a final native binary.
* In two-stage compilation, sources are compiled into a klib first and then a final native binary is produced from this klib.
*/
@get:Input
abstract val enableTwoStageCompilation: Boolean
protected fun directoryToKt(dir: Any) = project.fileTree(dir).apply {
include("**/*.kt")
exclude { it.file.startsWith(project.buildDir) }
}
// Command line ------------------------------------------------------------
// Exclude elements matching the predicate.
private fun List<String>.excludeFlags(predicate: (String) -> Boolean) = filterNot(predicate)
// Exclude the listed elements.
private fun List<String>.excludeFlags(vararg keys: String) = keys.toSet().let { keysToExclude ->
excludeFlags { it in keysToExclude }
}
// Exclude the arguments passed by the given keys.
// E.g. if the list contains the following elements: ["-l", "foo", "-r", "bar"],
// call exclude("-r") returns the following list: ["-l", "foo"].
private fun List<String>.excludeArguments(vararg args: String): List<String> {
val argsToExclude = args.toSet()
val xPrefixesToExclude = argsToExclude.filter { it.startsWith("-X") }.map { "$it=" }
val result = mutableListOf<String>()
var i = 0
while (i < size) {
val key = this[i]
when {
key in argsToExclude -> {
// Skip the key and the following arg.
i++
}
// Support args passed as -X<arg>=<value>.
xPrefixesToExclude.any { key.startsWith(it) } -> { /* Skip the key. */ }
else -> result += key
}
i++
}
return result
}
// Don't include coverage flags into the first stage because they are not supported when compiling a klib.
private fun firstStageExtraOpts() = extraOpts
.excludeFlags("-Xcoverage")
.excludeArguments("-Xcoverage-file", "-Xlibrary-to-cover", "-Xpartial-linkage", "-Xpartial-linkage-loglevel")
// Don't include the -Xemit-lazy-objc-header and -language-version flags into
// the second stage because this stage have no sources.
private fun secondStageExtraOpts() = extraOpts
.excludeArguments("-Xemit-lazy-objc-header", "-language-version")
/** Args passed to the compiler at the first stage of two-stage compilation (klib building). */
protected fun buildFirstStageArgs(klibPath: String) = mutableListOf<String>().apply {
addArg("-output", klibPath)
addArg("-produce", CompilerOutputKind.LIBRARY.name.toLowerCase())
addAll(buildCommonArgs())
addAll(firstStageExtraOpts())
allSourceFiles.mapTo(this) { it.absolutePath }
commonSrcFiles
.flatMap { it.files }
.mapTo(this) { "-Xcommon-sources=${it.absolutePath}" }
}
/** Args passed to the compiler at the second stage of two-stage compilation (producing a final binary from the klib). */
protected fun buildSecondStageArgs(klibPath: String) = mutableListOf<String>().apply {
addArg("-output", artifact.canonicalPath)
addArg("-produce", produce.name.toLowerCase())
addArgIfNotNull("-entry", entryPoint)
addAll(buildCommonArgs())
addFileArgs("-native-library", nativeLibraries)
linkerOpts.forEach {
addArg("-linker-option", it)
}
addAll(secondStageExtraOpts())
add("-Xinclude=${klibPath}")
}
/** Args passed to the compiler at both stages of the two-stage compilation and during the singe-stage compilation. */
protected open fun buildCommonArgs() = mutableListOf<String>().apply {
addArgs("-repo", libraries.repos.map { it.canonicalPath })
if (platformConfiguration.files.isNotEmpty()) {
platformConfiguration.files.filter { it.name.endsWith(".klib") }.forEach {
// The library's directory is added in libraries.repos.
addArg("-library", it.nameWithoutExtension)
}
}
addFileArgs("-library", libraries.files)
addArgs("-library", libraries.namedKlibs)
// The library's directory is added in libraries.repos.
addArgs("-library", libraries.artifacts.map { it.artifact.nameWithoutExtension })
addArgIfNotNull("-target", konanTarget.visibleName)
addArgIfNotNull("-language-version", languageVersion)
addArgIfNotNull("-api-version", apiVersion)
addArgIfNotNull("-entry", entryPoint)
addKey("-g", enableDebug)
addKey("-nostdlib", noStdLib)
addKey("-nomain", noMain)
addKey("-opt", enableOptimizations)
addKey("-ea", enableAssertions)
addKey("-Xtime", measureTime)
addKey("-Xprofile-phases", measureTime)
addKey("-no-default-libs", noDefaultLibs)
addKey("-no-endorsed-libs", noEndorsedLibs)
addKey("-Xmulti-platform", enableMultiplatform)
if (libraries.friends.isNotEmpty())
addArg("-friend-modules", libraries.friends.joinToString(File.pathSeparator))
}
/** Args passed to the compiler if the two-stage compilation is disabled. */
fun buildSingleStageArgs() = mutableListOf<String>().apply {
addArg("-output", artifact.canonicalPath)
addArg("-produce", produce.name.toLowerCase())
addArgIfNotNull("-entry", entryPoint)
addAll(buildCommonArgs())
addFileArgs("-native-library", nativeLibraries)
linkerOpts.forEach {
addArg("-linker-option", it)
}
if (produce != CompilerOutputKind.LIBRARY) {
add("-Xpartial-linkage=enable")
add("-Xpartial-linkage-loglevel=error")
}
addAll(extraOpts)
allSourceFiles.mapTo(this) { it.absolutePath }
commonSrcFiles
.flatMap { it.files }
.mapTo(this) { "-Xcommon-sources=${it.absolutePath}" }
}
// region DSL.
// DSL. Input/output files.
override fun srcDir(dir: Any) {
srcFiles_.add(directoryToKt(dir))
}
override fun srcFiles(vararg files: Any) {
srcFiles_.add(project.files(files))
}
override fun srcFiles(files: Collection<Any>) = srcFiles(*files.toTypedArray())
// DSL. Native libraries.
override fun nativeLibrary(lib: Any) = nativeLibraries(lib)
override fun nativeLibraries(vararg libs: Any) {
nativeLibraries.add(project.files(*libs))
}
override fun nativeLibraries(libs: FileCollection) {
nativeLibraries.add(libs)
}
// DSL. Multiplatform projects.
override fun enableMultiplatform(flag: Boolean) {
enableMultiplatform = flag
}
@Deprecated("Use commonSourceSets instead", ReplaceWith("commonSourceSets(sourceSetName)"))
override fun commonSourceSet(sourceSetName: String) {
commonSourceSets = listOf(sourceSetName)
enableMultiplatform(true)
}
override fun commonSourceSets(vararg sourceSetNames: String) {
commonSourceSets = sourceSetNames.toList()
enableMultiplatform(true)
}
override fun commonSrcDir(dir: Any) {
commonSrcFiles_.add(directoryToKt(dir))
}
override fun commonSrcFiles(vararg files: Any) {
commonSrcFiles_.add(project.files(files))
}
override fun commonSrcFiles(files: Collection<Any>) = commonSrcFiles(*files.toTypedArray())
// DSL. Other parameters.
override fun linkerOpts(values: List<String>) = linkerOpts(*values.toTypedArray())
override fun linkerOpts(vararg values: String) {
linkerOpts.addAll(values)
}
override fun enableDebug(flag: Boolean) {
enableDebug = flag
}
override fun noStdLib(flag: Boolean) {
noStdLib = flag
}
override fun noMain(flag: Boolean) {
noMain = flag
}
override fun noPack(flag: Boolean) {
noPack = flag
}
override fun enableOptimizations(flag: Boolean) {
enableOptimizations = flag
}
override fun enableAssertions(flag: Boolean) {
enableAssertions = flag
}
override fun entryPoint(entryPoint: String) {
this.entryPoint = entryPoint
}
override fun measureTime(flag: Boolean) {
measureTime = flag
}
// endregion
override fun run() {
destinationDir.mkdirs()
if (dumpParameters) {
dumpProperties(this)
}
if (enableTwoStageCompilation) {
logger.info("Start two-stage compilation")
val intermediateDir = project.konanBuildRoot
.resolve("intermediate")
.targetSubdir(konanTarget)
.apply { mkdirs() }
val klibPrefix = CompilerOutputKind.LIBRARY.prefix(konanTarget)
val klibSuffix = CompilerOutputKind.LIBRARY.suffix(konanTarget)
val intermediateKlib = intermediateDir.resolve("$klibPrefix$artifactName$klibSuffix").absolutePath
logger.info("Start first stage")
toolRunner.run(buildFirstStageArgs(intermediateKlib))
logger.info("Start second stage")
toolRunner.run(buildSecondStageArgs(intermediateKlib))
} else {
toolRunner.run(buildSingleStageArgs())
}
}
}
abstract class KonanCompileNativeBinary: KonanCompileTask() {
@Input
override var enableTwoStageCompilation: Boolean = false
}
open class KonanCompileProgramTask: KonanCompileNativeBinary() {
override val produce: CompilerOutputKind get() = CompilerOutputKind.PROGRAM
@Internal
var runTask: TaskProvider<Exec>? = null
inner class RunArgumentProvider: CommandLineArgumentProvider {
override fun asArguments() = project.findProperty("runArgs")?.let {
it.toString().split(' ')
} ?: emptyList()
}
}
open class KonanCompileDynamicTask: KonanCompileNativeBinary() {
override val produce: CompilerOutputKind get() = CompilerOutputKind.DYNAMIC
val headerFile: File
@OutputFile get() = destinationDir.resolve("$artifactPrefix${artifactName}_api.h")
}
open class KonanCompileFrameworkTask: KonanCompileNativeBinary() {
override val produce: CompilerOutputKind get() = CompilerOutputKind.FRAMEWORK
override val artifact
@OutputDirectory get() = super.artifact
}
open class KonanCompileLibraryTask: KonanCompileTask() {
override val artifact: File
@Internal get() = destinationDir.resolve(artifactFullName)
val artifactFile: File?
@Optional @OutputFile get() = if (!noPack) artifact else null
val artifactDirectory: File?
@Optional @OutputDirectory get() = if (noPack) artifact else null
override val artifactSuffix: String
@Internal get() = if (!noPack) produce.suffix(konanTarget) else ""
override fun buildCommonArgs() = super.buildCommonArgs().apply {
addKey("-nopack", noPack)
}
override val produce: CompilerOutputKind get() = CompilerOutputKind.LIBRARY
override val enableTwoStageCompilation: Boolean = false
}
open class KonanCompileBitcodeTask: KonanCompileNativeBinary() {
override val produce: CompilerOutputKind get() = CompilerOutputKind.BITCODE
}
@@ -0,0 +1,192 @@
/*
* 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.plugin.tasks
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.*
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.KonanInteropSpec.IncludeDirectoriesSpec
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.gradle.plugin.konan.dumpProperties
import org.jetbrains.kotlin.gradle.plugin.konan.konanDefaultDefFile
import org.jetbrains.kotlin.gradle.plugin.konan.konanExtension
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlin.run
/**
* A task executing cinterop tool with the given args and compiling the stubs produced by this tool.
*/
open class KonanInteropTask @Inject constructor(@Internal val workerExecutor: WorkerExecutor) : KonanBuildingTask(), KonanInteropSpec {
private val interopRunner = KonanCliInteropRunner(project, project.konanExtension.jvmArgs)
@get:Internal
override val toolRunner: KonanToolRunner = interopRunner
override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
super.init(config, destinationDir, artifactName, target)
this.defFile = project.konanDefaultDefFile(artifactName)
}
// Output directories -----------------------------------------------------
override val artifactSuffix: String
@Internal get() = ".klib"
override val artifactPrefix: String
@Internal get() = ""
// Interop stub generator parameters -------------------------------------
@Internal var enableParallel: Boolean = false
@InputFile lateinit var defFile: File
@Optional @Input var packageName: String? = null
@Input val compilerOpts = mutableListOf<String>()
@Input val linkerOpts = mutableListOf<String>()
@Nested val includeDirs = IncludeDirectoriesSpecImpl()
@InputFiles val headers = mutableSetOf<FileCollection>()
@InputFiles val linkFiles = mutableSetOf<FileCollection>()
fun buildArgs() = mutableListOf<String>().apply {
addArg("-o", artifact.canonicalPath)
addArgIfNotNull("-target", konanTarget.visibleName)
addArgIfNotNull("-def", defFile.canonicalPath)
addArgIfNotNull("-pkg", packageName)
addFileArgs("-header", headers)
compilerOpts.forEach {
addArg("-compiler-option", it)
}
val linkerOpts = mutableListOf<String>().apply { addAll(linkerOpts) }
linkFiles.forEach {
linkerOpts.addAll(it.files.map { it.canonicalPath })
}
linkerOpts.forEach {
addArg("-linker-option", it)
}
addArgs("-compiler-option", includeDirs.allHeadersDirs.map { "-I${it.absolutePath}" })
addArgs("-headerFilterAdditionalSearchPrefix", includeDirs.headerFilterDirs.map { it.absolutePath })
addArgs("-repo", libraries.repos.map { it.canonicalPath })
addFileArgs("-library", libraries.files)
addArgs("-library", libraries.namedKlibs)
addArgs("-library", libraries.artifacts.map { it.artifact.canonicalPath })
addKey("-no-default-libs", noDefaultLibs)
addKey("-no-endorsed-libs", noEndorsedLibs)
addAll(extraOpts)
}
// region DSL.
inner class IncludeDirectoriesSpecImpl: IncludeDirectoriesSpec {
@Input val allHeadersDirs = mutableSetOf<File>()
@Input val headerFilterDirs = mutableSetOf<File>()
override fun allHeaders(vararg includeDirs: Any) = allHeaders(includeDirs.toList())
override fun allHeaders(includeDirs: Collection<Any>) {
allHeadersDirs.addAll(includeDirs.map { project.file(it) })
}
override fun headerFilterOnly(vararg includeDirs: Any) = headerFilterOnly(includeDirs.toList())
override fun headerFilterOnly(includeDirs: Collection<Any>) {
headerFilterDirs.addAll(includeDirs.map { project.file(it) })
}
}
override fun defFile(file: Any) {
defFile = project.file(file)
}
override fun packageName(value: String) {
packageName = value
}
override fun compilerOpts(vararg values: String) {
compilerOpts.addAll(values)
}
override fun header(file: Any) = headers(file)
override fun headers(vararg files: Any) {
headers.add(project.files(files))
}
override fun headers(files: FileCollection) {
headers.add(files)
}
override fun includeDirs(vararg values: Any) = includeDirs.allHeaders(values.toList())
override fun includeDirs(closure: Closure<Unit>) = includeDirs { project.configure(this, closure) }
override fun includeDirs(action: Action<IncludeDirectoriesSpec>) = includeDirs { action.execute(this) }
override fun includeDirs(configure: IncludeDirectoriesSpec.() -> Unit) = includeDirs.configure()
override fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
override fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
override fun link(vararg files: Any) {
linkFiles.add(project.files(files))
}
override fun link(files: FileCollection) {
linkFiles.add(files)
}
// endregion
internal interface RunToolParameters: WorkParameters {
var taskName: String
var args: List<String>
}
internal abstract class RunTool @Inject constructor() : WorkAction<RunToolParameters> {
override fun execute() {
val toolRunner = interchangeBox.remove(parameters.taskName) ?: error(":(")
toolRunner.run(parameters.args)
}
}
override fun run() {
interopRunner.init(target)
destinationDir.mkdirs()
if (dumpParameters) {
dumpProperties(this)
}
val args = buildArgs()
if (enableParallel) {
val workQueue = workerExecutor.noIsolation()
interchangeBox[this.path] = toolRunner
workQueue.submit(RunTool::class.java) {
taskName = path
this.args = args
}
} else {
toolRunner.run(args)
}
}
}
internal val interchangeBox = ConcurrentHashMap<String, KonanToolRunner>()
@@ -0,0 +1,48 @@
/*
* 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
import groovy.lang.Closure
import org.gradle.api.Task
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.gradle.plugin.konan.KonanKlibRunner
import java.io.File
open class KonanKlibInstallTask : DefaultTask() {
@get:InputFile
var klib: Provider<File> = project.provider { project.buildDir }
@get:Internal
var repo: File = project.rootDir
@get:Input
val repoPath
get() = repo.absolutePath
val installDir: Provider<File>
@OutputDirectory
get() = project.provider {
val klibName = klib.get().nameWithoutExtension
project.file("${repo.absolutePath}/$klibName")
}
@get:Input
var target: String = HostManager.hostName
@TaskAction
fun exec() {
val args = listOf(
"install", klib.get().absolutePath,
"-target", target,
"-repository", repo.absolutePath
)
KonanKlibRunner(project, konanHome = project.kotlinNativeDist.absolutePath).run(args)
}
}