Basic native support in kotlin-multiplatform (#1811)

* Add dependency on KN shared in gradle-plugin

* Basic support for Kotlin/Native:
     * Target presets
     * Compilation into a klib
     * Compilation into native binraies: framework and executable
     * Basic dependencies between projects
     * No jars in native publications
     * Replace '-' with '_' in framework names
    * Escape quotes in native command lines on Windows
* Move targets from Kotlin/Native repo
* Download KN compiler using Gradle's mechanisms
* Support source set dependencies in native
This commit is contained in:
ilmat192
2018-08-19 20:45:31 +07:00
committed by GitHub
parent 8966e220f0
commit 2251440f04
23 changed files with 1315 additions and 107 deletions
+21
View File
@@ -0,0 +1,21 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
description = "Kotlin/Native utils"
jvmTarget = "1.6"
dependencies {
compile(projectDist(":kotlin-stdlib"))
}
sourceSets {
"main" { projectDefault() }
"test" {}
}
standardPublicJars()
publish()
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.konan
import java.io.Serializable
interface KonanVersion : Serializable {
val meta: MetaVersion
val major: Int
val minor: Int
val maintenance: Int
val build: Int
fun toString(showMeta: Boolean, showBuild: Boolean): String
companion object
}
data class KonanVersionImpl(
override val meta: MetaVersion = MetaVersion.DEV,
override val major: Int,
override val minor: Int,
override val maintenance: Int,
override val build: Int = -1
) : KonanVersion {
override fun toString(showMeta: Boolean, showBuild: Boolean) = buildString {
append(major)
append('.')
append(minor)
if (maintenance != 0) {
append('.')
append(maintenance)
}
if (showMeta) {
append('-')
append(meta.metaString)
}
if (showBuild && build != -1) {
append('-')
append(build)
}
}
private val isRelease: Boolean
get() = meta == MetaVersion.RELEASE
private val versionString by lazy { toString(!isRelease, !isRelease) }
override fun toString() = versionString
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.konan
/**
* https://en.wikipedia.org/wiki/Software_versioning
* scheme major.minor[.build[.revision]].
*/
enum class MetaVersion(val metaString: String) {
DEV("dev"),
EAP("eap"),
ALPHA("alpha"),
BETA("beta"),
RC1("rc1"),
RC2("rc2"),
RELEASE("release")
}
@@ -0,0 +1,279 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.target
import org.jetbrains.kotlin.konan.target.KonanTarget.*
import org.jetbrains.kotlin.konan.util.Named
enum class Family(val exeSuffix:String, val dynamicPrefix: String, val dynamicSuffix: String,
val staticPrefix: String, val staticSuffix: String) {
OSX ("kexe", "lib", "dylib", "lib", "a"),
IOS ("kexe", "lib", "dylib", "lib", "a"),
LINUX ("kexe", "lib", "so" , "lib", "a"),
MINGW ("exe" , "" , "dll" , "lib", "a"),
ANDROID ("so" , "lib", "so" , "lib", "a"),
WASM ("wasm", "" , "wasm" , "", "wasm"),
ZEPHYR ("o" , "lib", "a" , "lib", "a")
}
enum class Architecture(val bitness: Int) {
X64(64),
ARM64(64),
ARM32(32),
MIPS32(32),
MIPSEL32(32),
WASM32(32);
}
sealed class KonanTarget(override val name: String, val family: Family, val architecture: Architecture) : Named {
object ANDROID_ARM32 : KonanTarget( "android_arm32", Family.ANDROID, Architecture.ARM32)
object ANDROID_ARM64 : KonanTarget( "android_arm64", Family.ANDROID, Architecture.ARM64)
object IOS_ARM32 : KonanTarget( "ios_arm32", Family.IOS, Architecture.ARM32)
object IOS_ARM64 : KonanTarget( "ios_arm64", Family.IOS, Architecture.ARM64)
object IOS_X64 : KonanTarget( "ios_x64", Family.IOS, Architecture.X64)
object LINUX_X64 : KonanTarget( "linux_x64", Family.LINUX, Architecture.X64)
object MINGW_X64 : KonanTarget( "mingw_x64", Family.MINGW, Architecture.X64)
object MACOS_X64 : KonanTarget( "macos_x64", Family.OSX, Architecture.X64)
object LINUX_ARM32_HFP :KonanTarget( "linux_arm32_hfp", Family.LINUX, Architecture.ARM32)
object LINUX_MIPS32 : KonanTarget( "linux_mips32", Family.LINUX, Architecture.MIPS32)
object LINUX_MIPSEL32 : KonanTarget( "linux_mipsel32", Family.LINUX, Architecture.MIPSEL32)
object WASM32 : KonanTarget( "wasm32", Family.WASM, Architecture.WASM32)
// Tunable targets
class ZEPHYR(val subName: String, val genericName: String = "zephyr") :
KonanTarget("${genericName}_$subName", Family.ZEPHYR, Architecture.ARM32)
override fun toString() = name
}
fun hostTargetSuffix(host: KonanTarget, target: KonanTarget) =
if (target == host) host.name else "${host.name}-${target.name}"
enum class CompilerOutputKind {
PROGRAM {
override fun suffix(target: KonanTarget?) = ".${target!!.family.exeSuffix}"
},
DYNAMIC {
override fun suffix(target: KonanTarget?) = ".${target!!.family.dynamicSuffix}"
override fun prefix(target: KonanTarget?) = "${target!!.family.dynamicPrefix}"
},
STATIC {
override fun suffix(target: KonanTarget?) = ".${target!!.family.staticSuffix}"
override fun prefix(target: KonanTarget?) = "${target!!.family.staticPrefix}"
},
FRAMEWORK {
override fun suffix(target: KonanTarget?): String = ".framework"
},
LIBRARY {
override fun suffix(target: KonanTarget?) = ".klib"
},
BITCODE {
override fun suffix(target: KonanTarget?) = ".bc"
};
abstract fun suffix(target: KonanTarget? = null): String
open fun prefix(target: KonanTarget? = null): String = ""
}
interface TargetManager {
val target: KonanTarget
val targetName : String
fun list() : Unit
val hostTargetSuffix: String
val targetSuffix: String
}
/** */
interface SubTargetProvider {
fun availableSubTarget(genericName: String): List<String>
}
private class NoSubTargets: SubTargetProvider {
override fun availableSubTarget(genericName: String): List<String> = emptyList()
}
private class TargetManagerImpl(val userRequest: String?, val hostManager: HostManager): TargetManager {
override val target = determineCurrent()
override val targetName
get() = target.visibleName
override fun list() {
hostManager.enabled.forEach {
val isDefault = if (it == target) "(default)" else ""
val aliasList = HostManager.listAliases(it.visibleName).joinToString(", ")
println(String.format("%1$-30s%2$-10s%3\$s", "${it.visibleName}:", "$isDefault", aliasList))
}
}
fun determineCurrent(): KonanTarget {
return if (userRequest == null || userRequest == "host") {
HostManager.host
} else {
val resolvedAlias = HostManager.resolveAlias(userRequest)
hostManager.targets[hostManager.known(resolvedAlias)]!!
}
}
override val hostTargetSuffix get() = hostTargetSuffix(HostManager.host, target)
override val targetSuffix get() = target.name
}
open class HostManager(subtargetProvider: SubTargetProvider = NoSubTargets()) {
fun targetManager(userRequest: String? = null): TargetManager = TargetManagerImpl(userRequest, this)
// TODO: need a better way to enumerated predefined targets.
private val predefinedTargets = listOf(
KonanTarget.ANDROID_ARM32, ANDROID_ARM64,
IOS_ARM32, IOS_ARM64, IOS_X64,
LINUX_X64, LINUX_ARM32_HFP, LINUX_MIPS32, LINUX_MIPSEL32,
MINGW_X64,
MACOS_X64,
WASM32)
private val zephyrSubtargets = subtargetProvider.availableSubTarget("zephyr").map { ZEPHYR(it) }
private val configurableSubtargets = zephyrSubtargets
val targetValues: List<KonanTarget> by lazy {
predefinedTargets + configurableSubtargets
}
val targets = targetValues.associate{ it.visibleName to it }
fun toKonanTargets(names: Iterable<String>): List<KonanTarget> {
return names.map {
if (it == "host") HostManager.host
else targets[known(resolveAlias(it))]!!
}
}
fun known(name: String): String {
if (targets[name] == null) {
throw TargetSupportException("Unknown target: $name. Use -list_targets to see the list of available targets")
}
return name
}
fun targetByName(name: String): KonanTarget {
if (name == "host") return host
val target = targets[resolveAlias(name)]
if (target == null) throw TargetSupportException("Unknown target name: $name")
return target
}
val enabled: List<KonanTarget> by lazy {
when (host) {
KonanTarget.LINUX_X64 -> listOf(
KonanTarget.LINUX_X64,
KonanTarget.LINUX_ARM32_HFP,
KonanTarget.LINUX_MIPS32,
KonanTarget.LINUX_MIPSEL32,
KonanTarget.ANDROID_ARM32,
KonanTarget.ANDROID_ARM64,
KonanTarget.WASM32
) + zephyrSubtargets
KonanTarget.MINGW_X64 -> listOf(
KonanTarget.MINGW_X64,
KonanTarget.WASM32
) + zephyrSubtargets
KonanTarget.MACOS_X64 -> listOf(
KonanTarget.MACOS_X64,
KonanTarget.IOS_ARM32,
KonanTarget.IOS_ARM64,
KonanTarget.IOS_X64,
KonanTarget.ANDROID_ARM32,
KonanTarget.ANDROID_ARM64,
KonanTarget.WASM32
) + zephyrSubtargets
else ->
throw TargetSupportException("Unknown host platform: $host")
}
}
fun isEnabled(target: KonanTarget) = enabled.contains(target)
companion object {
fun host_os(): String {
val javaOsName = System.getProperty("os.name")
return when {
javaOsName == "Mac OS X" -> "osx"
javaOsName == "Linux" -> "linux"
javaOsName.startsWith("Windows") -> "windows"
else -> throw TargetSupportException("Unknown operating system: ${javaOsName}")
}
}
@JvmStatic
fun simpleOsName(): String {
val hostOs = host_os()
return if (hostOs == "osx") "macos" else hostOs
}
val jniHostPlatformIncludeDir: String
get() = when(host) {
KonanTarget.MACOS_X64 -> "darwin"
KonanTarget.LINUX_X64 -> "linux"
KonanTarget.MINGW_X64 ->"win32"
else -> throw TargetSupportException("Unknown host: $host.")
}
fun host_arch(): String {
val javaArch = System.getProperty("os.arch")
return when (javaArch) {
"x86_64" -> "x86_64"
"amd64" -> "x86_64"
"arm64" -> "arm64"
else -> throw TargetSupportException("Unknown hardware platform: ${javaArch}")
}
}
val host: KonanTarget = when (host_os()) {
"osx" -> KonanTarget.MACOS_X64
"linux" -> KonanTarget.LINUX_X64
"windows" -> KonanTarget.MINGW_X64
else -> throw TargetSupportException("Unknown host target: ${host_os()} ${host_arch()}")
}
val hostIsMac = (host == KonanTarget.MACOS_X64)
val hostIsLinux = (host == KonanTarget.LINUX_X64)
val hostIsMingw = (host == KonanTarget.MINGW_X64)
val hostSuffix get() = host.name
@JvmStatic
val hostName get() = host.name
val knownTargetTemplates = listOf("zephyr")
private val targetAliasResolutions = mapOf(
"linux" to "linux_x64",
"macbook" to "macos_x64",
"macos" to "macos_x64",
"imac" to "macos_x64",
"raspberrypi" to "linux_arm32_hfp",
"iphone32" to "ios_arm32",
"iphone" to "ios_arm64",
"ipad" to "ios_arm64",
"ios" to "ios_arm64",
"iphone_sim" to "ios_x64",
"mingw" to "mingw_x64"
)
private val targetAliases: Map<String, List<String>> by lazy {
val result = mutableMapOf<String, MutableList<String>>()
targetAliasResolutions.entries.forEach {
result.getOrPut(it.value, { mutableListOf() } ).add(it.key)
}
result
}
fun resolveAlias(request: String): String = targetAliasResolutions[request] ?: request
fun listAliases(target: String): List<String> = targetAliases[target] ?: emptyList()
}
}
class TargetSupportException (message: String = "", cause: Throwable? = null) : Exception(message, cause)
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.util
import java.io.File
object DependencyDirectories {
val localKonanDir: File by lazy {
File(System.getenv("KONAN_DATA_DIR") ?: (System.getProperty("user.home") + File.separator + ".konan"))
}
@JvmStatic
val defaultDependenciesRoot: File
get() = localKonanDir.resolve("dependencies")
val defaultDependencyCacheDir: File
get() = localKonanDir.resolve("cache")
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.util
val <T : Enum<T>> T.visibleName get() = name.toLowerCase()
interface Named {
val name: String
val visibleName get() = name
}
@@ -15,7 +15,8 @@ pill {
dependencies {
compile project(':kotlin-stdlib')
compile project('::konan:konan-utils')
compileOnly gradleApi()
compileOnly 'com.android.tools.build:gradle:0.4.2'
}
@@ -12,8 +12,7 @@ import org.gradle.api.attributes.CompatibilityCheckDetails
import java.io.Serializable
enum class KotlinPlatformType: Named, Serializable {
common, jvm, js, androidJvm,
native; // TODO: split native into separate entries here or transform the enum to interface and implement entries in K/N
common, jvm, js, androidJvm, native;
override fun toString(): String = name
override fun getName(): String = name
@@ -38,6 +38,7 @@ dependencies {
compileOnly project(':compiler:daemon-common')
compile project(':kotlin-stdlib')
compile project(':konan:konan-utils')
compileOnly project(':kotlin-reflect-api')
compileOnly project(':kotlin-android-extensions')
compileOnly project(':kotlin-build-common')
@@ -102,7 +102,7 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
override fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinJvmAndroidCompilation) {
val project = compilation.target.project
KotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, compilation.target, project.configurations)
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, compilation.target, project.configurations)
compilation.compileDependencyFiles = variant.compileConfiguration.apply {
usesPlatformOf(compilation.target)
@@ -0,0 +1,162 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Named
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.DependencyDirectories
/** Copied from Kotlin/Native repository. */
// TODO: Rename to FQ names
internal enum class KotlinNativeProjectProperty(val propertyName: String) {
KONAN_HOME ("konan.home"),
KONAN_JVM_ARGS ("konan.jvmArgs"),
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"),
KONAN_PUBLICATION_ENABLED ("konan.publication.enabled")
}
internal fun Project.hasProperty(property: KotlinNativeProjectProperty) = hasProperty(property.propertyName)
internal fun Project.findProperty(property: KotlinNativeProjectProperty): Any? = findProperty(property.propertyName)
internal fun Project.setProperty(property: KotlinNativeProjectProperty, value: Any?) =
extensions.extraProperties.set(property.propertyName, value)
internal fun Project.getProperty(property: KotlinNativeProjectProperty) = findProperty(property)
?: throw IllegalArgumentException("No such property in the project: ${property.propertyName}")
internal val Project.jvmArgs
get() = (findProperty(KotlinNativeProjectProperty.KONAN_JVM_ARGS) as String?)?.split("\\s+".toRegex()).orEmpty()
// konanHome extension is set by downloadKonanCompiler task.
internal val Project.konanHome: String
get() {
assert(hasProperty(KotlinNativeProjectProperty.KONAN_HOME))
return project.file(getProperty(KotlinNativeProjectProperty.KONAN_HOME)).canonicalPath
}
internal interface KonanToolRunner: Named {
val mainClass: String
val classpath: FileCollection
val jvmArgs: List<String>
val environment: Map<String, Any>
val additionalSystemProperties: Map<String, String>
fun run(args: List<String>)
fun run(vararg args: String) = run(args.toList())
}
internal abstract class KonanCliRunner(
val toolName: String,
val fullName: String,
val project: Project,
private val additionalJvmArgs: List<String>
): KonanToolRunner {
override val mainClass = "org.jetbrains.kotlin.cli.utilities.MainKt"
override fun getName() = toolName
// We need to unset some environment variables which are set by XCode and may potentially affect the tool executed.
protected val blacklistEnvironment: List<String> by lazy {
KonanToolRunner::class.java.getResourceAsStream("/env_blacklist")?.let { stream ->
stream.reader().use { it.readLines() }
} ?: emptyList<String>()
}
override val classpath: FileCollection =
project.fileTree("${project.konanHome}/konan/lib/")
.apply { include("*.jar") }
override val jvmArgs = mutableListOf("-ea").apply {
if (additionalJvmArgs.none { it.startsWith("-Xmx") } &&
project.jvmArgs.none { it.startsWith("-Xmx") }) {
add("-Xmx3G")
}
addAll(additionalJvmArgs)
addAll(project.jvmArgs)
}
override val additionalSystemProperties = mutableMapOf(
"konan.home" to project.konanHome,
"java.library.path" to "${project.konanHome}/konan/nativelib"
)
override val environment = mutableMapOf("LIBCLANG_DISABLE_CRASH_RECOVERY" to "1")
private fun String.escapeQuotes() = replace("\"", "\\\"")
private fun List<Pair<String, String>>.escapeQuotesForWindows() =
if (HostManager.hostIsMingw) {
map { (key, value) -> key.escapeQuotes() to value.escapeQuotes() }
} else {
this
}
override fun run(args: List<String>) {
project.logger.info("Run tool: $toolName with args: ${args.joinToString(separator = " ")}")
if (classpath.isEmpty) {
throw IllegalStateException("Classpath of the tool is empty: $toolName\n" +
"Probably the 'konan.home' project property contains an incorrect path.\n" +
"Please change it to the compiler root directory and rerun the build.")
}
project.javaexec { spec ->
spec.main = mainClass
spec.classpath = classpath
spec.jvmArgs(jvmArgs)
spec.systemProperties(
System.getProperties()
.map { (k, v) -> k.toString() to v.toString() }
.escapeQuotesForWindows()
.toMap()
)
spec.systemProperties(additionalSystemProperties)
spec.args(listOf(toolName) + args)
blacklistEnvironment.forEach { spec.environment.remove(it) }
spec.environment(environment)
}
}
}
internal class KonanInteropRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("cinterop", "Kotlin/Native cinterop tool", project, additionalJvmArgs)
{
init {
if (HostManager.host == KonanTarget.MINGW_X64) {
//TODO: Oh-ho-ho fix it in more convinient way.
environment.put("PATH", DependencyDirectories.defaultDependenciesRoot.absolutePath +
"\\msys2-mingw-w64-x86_64-gcc-7.2.0-clang-llvm-5.0.0-windows-x86-64" +
"\\bin;${environment.get("PATH")}")
}
}
}
internal class KonanCompilerRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs)
internal class KonanKlibRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs)
@@ -432,7 +432,7 @@ internal abstract class AbstractKotlinPlugin(
// Setup the consuming configurations:
project.dependencies.attributesSchema.attribute(KotlinPlatformType.attribute)
kotlinTarget.compilations.all { compilation ->
KotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, kotlinTarget, project.configurations)
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, kotlinTarget, project.configurations)
}
// Setup the published configurations:
@@ -18,6 +18,8 @@ import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.ArtifactAttributes
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaBasePlugin
@@ -26,22 +28,26 @@ import org.gradle.api.tasks.testing.Test
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.nativeplatform.test.tasks.RunTestExecutable
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import java.io.File
import java.util.*
import java.util.concurrent.Callable
open class KotlinTargetConfigurator(
abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>(
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) {
fun <KotlinCompilationType: KotlinCompilation> configureTarget(
target: KotlinOnlyTarget<KotlinCompilationType>
open fun configureTarget(
target: KotlinTargetType
) {
configureCompilationDefaults(target)
configureCompilations(target)
@@ -49,11 +55,12 @@ open class KotlinTargetConfigurator(
configureArchivesAndComponent(target)
configureTest(target)
configureBuild(target)
setCompatibilityOfAbstractCompileTasks(target.project)
}
private fun <KotlinCompilationType: KotlinCompilation> configureCompilations(platformTarget: KotlinOnlyTarget<KotlinCompilationType>) {
abstract fun configureArchivesAndComponent(target: KotlinTargetType)
protected fun configureCompilations(platformTarget: KotlinTargetType) {
val project = platformTarget.project
val main = platformTarget.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME)
@@ -75,7 +82,7 @@ open class KotlinTargetConfigurator(
}
private fun <KotlinCompilationType: KotlinCompilation> configureCompilationDefaults(target: KotlinOnlyTarget<KotlinCompilationType>) {
protected fun configureCompilationDefaults(target: KotlinTargetType) {
val project = target.project
target.compilations.all { compilation ->
@@ -94,49 +101,7 @@ open class KotlinTargetConfigurator(
}
}
private fun configureArchivesAndComponent(target: KotlinOnlyTarget<*>) {
val project = target.project
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val jar = project.tasks.create(target.artifactsTaskName, Jar::class.java)
jar.description = "Assembles a jar archive containing the main classes."
jar.group = BasePlugin.BUILD_GROUP
jar.from(mainCompilation.output)
val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName)
target.disambiguationClassifier?.let { jar.classifier = it }
// Workaround: adding the artifact during configuration seems to interfere with the Java plugin, which results into missing
// task dependency 'assemble -> jar' if the Java plugin is applied after this steps
project.afterEvaluate {
project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, jar) { jarArtifact ->
jarArtifact.builtBy(jar)
jarArtifact.type = ArtifactTypeDefinition.JAR_TYPE
addJar(apiElementsConfiguration, jarArtifact)
if (mainCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = project.configurations.getByName(mainCompilation.deprecatedRuntimeConfigurationName)
val runtimeElementsConfiguration = project.configurations.getByName(target.runtimeElementsConfigurationName)
addJar(runtimeConfiguration, jarArtifact)
addJar(runtimeElementsConfiguration, jarArtifact)
// TODO Check Gradle's special split into variants for classes & resources -- do we need that too?
}
}
}
}
private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) {
val publications = configuration.outgoing
// Configure an implicit variant
publications.artifacts.add(jarArtifact)
publications.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE)
}
private fun configureTest(target: KotlinTarget) {
protected fun configureTest(target: KotlinTarget) {
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles
?: return // Otherwise, there is no runtime classpath
@@ -151,7 +116,7 @@ open class KotlinTargetConfigurator(
}
}
private fun configureResourceProcessing(
protected fun configureResourceProcessing(
compilation: KotlinCompilationWithResources,
resourceSet: FileCollection
) {
@@ -168,7 +133,7 @@ open class KotlinTargetConfigurator(
resourcesTask.from(resourceSet)
}
private fun createLifecycleTask(compilation: KotlinCompilation) {
protected fun createLifecycleTask(compilation: KotlinCompilation) {
val project = compilation.target.project
(compilation.output.classesDirs as ConfigurableFileCollection).from(project.files().builtBy(compilation.compileAllTaskName))
@@ -186,7 +151,7 @@ open class KotlinTargetConfigurator(
}
}
private fun defineConfigurationsForTarget(target: KotlinOnlyTarget<*>) {
protected fun defineConfigurationsForTarget(target: KotlinTargetType) {
val project = target.project
val configurations = project.configurations
@@ -243,7 +208,7 @@ open class KotlinTargetConfigurator(
defaultConfiguration.extendsFrom(runtimeElementsConfiguration).usesPlatformOf(target)
}
private fun configureBuild(target: KotlinOnlyTarget<*>) {
protected fun configureBuild(target: KotlinTargetType) {
val project = target.project
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
project.tasks.maybeCreate(buildNeededTaskName, DefaultTask::class.java).apply {
@@ -279,14 +244,6 @@ open class KotlinTargetConfigurator(
task.dependsOn(configuration.getTaskDependencyFromProjectDependency(useDependedOn, otherProjectTaskName))
}
private fun setCompatibilityOfAbstractCompileTasks(project: Project) = with (project) {
tasks.withType(AbstractKotlinCompile::class.java).all {
// Workaround: these are input properties and should not hold null values:
it.targetCompatibility = ""
it.sourceCompatibility = ""
}
}
companion object {
const val buildNeededTaskName = "buildAllNeeded"
const val buildDependentTaskName = "buildAllDependents"
@@ -363,14 +320,260 @@ open class KotlinTargetConfigurator(
}
}
private val KotlinCompilation.deprecatedCompileConfigurationName: String
internal val KotlinCompilation.deprecatedCompileConfigurationName: String
get() = disambiguateName("compile")
private val KotlinCompilationToRunnableFiles.deprecatedRuntimeConfigurationName: String
internal val KotlinCompilationToRunnableFiles.deprecatedRuntimeConfigurationName: String
get() = disambiguateName("runtime")
}
}
open class KotlinTargetConfigurator<KotlinCompilationType: KotlinCompilation>(
buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) : AbstractKotlinTargetConfigurator<KotlinOnlyTarget<KotlinCompilationType>>(buildOutputCleanupRegistry) {
override fun configureTarget(
target: KotlinOnlyTarget<KotlinCompilationType>
) {
super.configureTarget(target)
setCompatibilityOfAbstractCompileTasks(target.project)
}
override fun configureArchivesAndComponent(target: KotlinOnlyTarget<KotlinCompilationType>) {
val project = target.project
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val jar = project.tasks.create(target.artifactsTaskName, Jar::class.java)
jar.description = "Assembles a jar archive containing the main classes."
jar.group = BasePlugin.BUILD_GROUP
jar.from(mainCompilation.output)
val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName)
target.disambiguationClassifier?.let { jar.classifier = it }
// Workaround: adding the artifact during configuration seems to interfere with the Java plugin, which results into missing
// task dependency 'assemble -> jar' if the Java plugin is applied after this steps
project.afterEvaluate {
project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, jar) { jarArtifact ->
jarArtifact.builtBy(jar)
jarArtifact.type = ArtifactTypeDefinition.JAR_TYPE
addJar(apiElementsConfiguration, jarArtifact)
if (mainCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = project.configurations.getByName(mainCompilation.deprecatedRuntimeConfigurationName)
val runtimeElementsConfiguration = project.configurations.getByName(target.runtimeElementsConfigurationName)
addJar(runtimeConfiguration, jarArtifact)
addJar(runtimeElementsConfiguration, jarArtifact)
// TODO Check Gradle's special split into variants for classes & resources -- do we need that too?
}
}
}
}
private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) {
val publications = configuration.outgoing
// Configure an implicit variant
publications.artifacts.add(jarArtifact)
publications.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE)
}
private fun setCompatibilityOfAbstractCompileTasks(project: Project) = with (project) {
tasks.withType(AbstractKotlinCompile::class.java).all {
// Workaround: these are input properties and should not hold null values:
it.targetCompatibility = ""
it.sourceCompatibility = ""
}
}
}
open class KotlinNativeTargetConfigurator(
buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) : AbstractKotlinTargetConfigurator<KotlinNativeTarget>(buildOutputCleanupRegistry) {
private val Collection<*>.isDimensionVisible: Boolean
get() = size > 1
private fun createDimensionSuffix(dimensionName: String, multivalueProperty: Collection<*>): String =
if (multivalueProperty.isDimensionVisible) {
dimensionName.toLowerCase().capitalize()
} else {
""
}
private fun Project.createTestTask(compilation: KotlinNativeCompilation, testExecutableLinkTask: KotlinNativeCompile) {
val taskName = lowerCamelCaseName("run", compilation.name, compilation.target.name)
val testTask = tasks.create(taskName, RunTestExecutable::class.java).apply {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Executes Kotlin/Native unit tests from the '${compilation.name}' compilation " +
"for target '${compilation.platformType.name}'"
val testExecutableProperty = testExecutableLinkTask.outputFile
executable = testExecutableProperty.get().absolutePath
// TODO: Provide a normal test path!
outputDir = project.layout.buildDirectory.dir("test-results").get().asFile
onlyIf { testExecutableProperty.get().exists() }
inputs.file(testExecutableProperty)
dependsOn(testExecutableLinkTask)
dependsOnCompilerDownloading()
}
tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).apply {
dependsOn(testTask)
}
}
private fun Project.createBinaryLinkTasks(compilation: KotlinNativeCompilation) = whenEvaluated {
val konanTarget = compilation.target.konanTarget
val buildTypes = compilation.buildTypes
val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) }
val linkAll = project.tasks.maybeCreate(compilation.linkAllTaskName)
for (buildType in compilation.buildTypes) {
for (kind in availableOutputKinds) {
val compilerOutputKind = kind.compilerOutputKind
val compilationSuffix = compilation.name
val buildTypeSuffix = createDimensionSuffix(buildType.name, buildTypes)
val targetSuffix = compilation.target.name
val kindSuffix = kind.taskNameClassifier
val taskName = lowerCamelCaseName("link", compilationSuffix, buildTypeSuffix, kindSuffix, targetSuffix)
val linkTask = project.tasks.create(
taskName,
KotlinNativeCompile::class.java
).apply {
this.compilation = compilation
outputKind = compilerOutputKind
group = BasePlugin.BUILD_GROUP
description = "Links ${kind.description} from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'"
optimized = buildType.optimized
debuggable = buildType.debuggable
if (outputKind == CompilerOutputKind.FRAMEWORK) {
outputs.dir(outputFile)
} else {
outputs.file(outputFile)
}
outputFile.set(provider {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
val buildTypeSubDirectory = buildType.name.toLowerCase()
val compilationName = compilation.compilationName
val prefix = compilerOutputKind.prefix(konanTarget)
val suffix = compilerOutputKind.suffix(konanTarget)
var filename = "$prefix$compilationName$suffix"
if (outputKind == CompilerOutputKind.FRAMEWORK) {
filename = filename.replace('-', '_')
}
File(project.buildDir, "bin/$targetSubDirectory$compilationName/$buildTypeSubDirectory/$filename")
})
dependsOnCompilerDownloading()
linkAll.dependsOn(this)
}
if (compilation.isTestCompilation &&
buildType == NativeBuildType.DEBUG &&
konanTarget == HostManager.host
) {
createTestTask(compilation, linkTask)
}
}
}
}
private fun Project.createKlibPublishableArtifact(compilation: KotlinNativeCompilation, compileTask: KotlinNativeCompile) {
val apiElements = configurations.getByName(compilation.target.apiElementsConfigurationName)
val klibArtifact = DefaultPublishArtifact(
compilation.name,
"klib",
"klib",
null,
Date(),
compileTask.outputFile.get(),
compileTask
)
compilation.target.disambiguationClassifier?.let { klibArtifact.classifier = it }
project.extensions.getByType(DefaultArtifactPublicationSet::class.java).addCandidate(klibArtifact)
with(apiElements.outgoing) {
artifacts.add(klibArtifact)
attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB)
}
}
private fun Project.createKlibCompilationTask(compilation: KotlinNativeCompilation) {
val compileTask = tasks.create(
compilation.compileKotlinTaskName,
KotlinNativeCompile::class.java
).apply {
this.compilation = compilation
outputKind = CompilerOutputKind.LIBRARY
group = BasePlugin.BUILD_GROUP
description = "Compiles a klibrary from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'"
outputs.file(outputFile)
outputFile.set(provider {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
val classifier = compilation.target.disambiguationClassifier?.let { "-$it" }.orEmpty()
val compilationName = compilation.compilationName
val klibName = if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME)
project.name
else
compilationName
File(project.buildDir, "classes/kotlin/$targetSubDirectory$compilationName/$klibName$classifier.klib")
})
dependsOnCompilerDownloading()
compilation.output.tryAddClassesDir {
project.files(this.outputFile).builtBy(this)
}
}
project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTask)
if (compilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
project.tasks.getByName(compilation.target.artifactsTaskName).apply {
dependsOn(compileTask)
dependsOn(compilation.linkAllTaskName)
}
project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).apply {
dependsOn(compileTask)
dependsOn(compilation.linkAllTaskName)
}
createKlibPublishableArtifact(compilation, compileTask)
}
}
override fun configureArchivesAndComponent(target: KotlinNativeTarget) = with(target.project) {
if (!HostManager().isEnabled(target.konanTarget)) {
return
}
tasks.create(target.artifactsTaskName)
target.compilations.all {
createKlibCompilationTask(it)
createBinaryLinkTasks(it)
}
}
private fun Task.dependsOnCompilerDownloading() =
dependsOn(KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME)
object NativeArtifactFormat {
const val KLIB = "org.jerbrains.kotlin.klib"
}
}
internal fun Project.usageByName(usageName: String): Usage =
if (isGradleVersionAtLeast(4, 0)) {
// `project.objects` is an API introduced in Gradle 4.0
@@ -383,5 +586,9 @@ internal fun Project.usageByName(usageName: String): Usage =
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
attributes.attribute(KotlinPlatformType.attribute, target.platformType)
// TODO: Provide an universal way to copy attributes from the target.
if (target is KotlinNativeTarget) {
attributes.attribute(KotlinNativeTarget.konanTargetAttribute, target.konanTarget.name)
}
return this
}
@@ -3,15 +3,6 @@
* that can be found in the license/LICENSE.txt file.
*/
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.mpp
@@ -82,4 +73,25 @@ class KotlinJsCompilationFactory(
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name, target.createCompilationOutput(name))
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name, target.createCompilationOutput(name)).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
outputKinds = mutableListOf(NativeOutputKind.EXECUTABLE)
buildTypes = mutableListOf(NativeBuildType.DEBUG)
isTestCompilation = true
} else {
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
}
@@ -3,16 +3,6 @@
* that can be found in the license/LICENSE.txt file.
*/
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.mpp
import org.gradle.api.Project
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.konan.target.HostManager
internal val Project.multiplatformExtension get(): KotlinMultiplatformExtension? =
project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
@@ -86,6 +87,9 @@ class KotlinMultiplatformPlugin(
add(KotlinJsTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
add(KotlinAndroidTargetPreset(project, kotlinPluginVersion))
add(KotlinJvmWithJavaTargetPreset(project, kotlinPluginVersion))
HostManager().targets.forEach { _, target ->
add(KotlinNativeTargetPreset(target.presetName, project, target, buildOutputCleanupRegistry))
}
}
}
@@ -33,6 +33,12 @@ class KotlinSoftwareComponent(
}
}
// At the moment all KN artifacts have JAVA_API usage.
// TODO: Replace it with a specific usage
object NativeUsage {
const val KOTLIN_KLIB = "kotlin-klib"
}
internal class KotlinPlatformUsageContext(
val project: Project,
val kotlinTarget: KotlinTarget,
@@ -0,0 +1,48 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Named
import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
object KotlinNativeUsage {
const val KLIB = "kotlin-native-klib"
const val FRAMEWORK = "kotlin-native-framework"
}
enum class NativeBuildType(val optimized: Boolean, val debuggable: Boolean): Named {
RELEASE(true, false),
DEBUG(false, true);
override fun getName(): String = name.toLowerCase()
}
enum class NativeOutputKind(
val compilerOutputKind: CompilerOutputKind,
val taskNameClassifier: String,
val description: String = taskNameClassifier,
val additionalCompilerFlags: List<String> = emptyList(),
val runtimeUsageName: String? = null,
val linkUsageName: String? = null,
val publishable: Boolean = true
) {
EXECUTABLE(
CompilerOutputKind.PROGRAM,
"executable",
description = "an executable",
runtimeUsageName = Usage.NATIVE_RUNTIME
),
FRAMEWORK(
CompilerOutputKind.FRAMEWORK,
"framework",
description = "an Objective-C framework",
linkUsageName = KotlinNativeUsage.FRAMEWORK,
publishable = false
) {
override fun availableFor(target: KonanTarget) =
target.family == Family.OSX || target.family == Family.IOS
};
open fun availableFor(target: KonanTarget) = true
}
@@ -3,16 +3,6 @@
* that can be found in the license/LICENSE.txt file.
*/
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.mpp
import groovy.lang.Closure
@@ -59,21 +49,30 @@ internal class DefaultKotlinDependencyHandler(
}
abstract class AbstractKotlinCompilation(
final override val target: KotlinTarget,
target: KotlinTarget,
override val compilationName: String
) : KotlinCompilation, HasKotlinDependencies {
// Don't declare this property in the constructor to avoid NPE
// when an overriding property of a subclass is accessed instead.
override val target: KotlinTarget = target
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet) {
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(sourceSet.kotlin)
}
override fun source(sourceSet: KotlinSourceSet) {
if (kotlinSourceSets.add(sourceSet)) {
with(target.project) {
whenEvaluated {
sourceSet.getSourceSetHierarchy().forEach { sourceSet ->
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(sourceSet.kotlin)
addSourcesToCompileTask(sourceSet)
// Use `forced = false` since `api`, `implementation`, and `compileOnly` may be missing in some cases like
// old Java & Android projects:
@@ -242,4 +241,67 @@ class KotlinCommonCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
) : AbstractKotlinCompilation(target, name)
) : AbstractKotlinCompilation(target, name)
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String,
override val output: SourceSetOutput
) : AbstractKotlinCompilation(target, name) {
// A FileCollection containing source files from all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks.
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal var allSources: FileCollection = target.project.files()
val linkAllTaskName: String
get() = lowerCamelCaseName(
"link",
compilationName.takeIf { it != "main" }.orEmpty(),
target.disambiguationClassifier
)
var isTestCompilation = false
// Native-specific DSL.
val extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
// Naming
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"compileKlibraries"
)
override val compileAllTaskName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }.orEmpty(),
"klibrary"
)
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet) {
allSources += sourceSet.kotlin
}
// TODO: Can we do it better?
companion object {
val DEBUG = NativeBuildType.DEBUG
val RELEASE = NativeBuildType.RELEASE
val EXECUTABLE = NativeOutputKind.EXECUTABLE
val FRAMEWORK = NativeOutputKind.FRAMEWORK
}
}
@@ -10,9 +10,14 @@ import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask
import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask.Companion.KONAN_DOWNLOAD_TASK_NAME
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation>(
protected val project: Project,
@@ -31,7 +36,7 @@ abstract class KotlinOnlyTargetPreset<T : KotlinCompilation>(
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinTargetConfigurator(buildOutputCleanupRegistry).configureTarget(result)
KotlinTargetConfigurator<T>(buildOutputCleanupRegistry).configureTarget(result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
@@ -184,7 +189,7 @@ class KotlinJvmWithJavaTargetPreset(
target.compilations.all { compilation ->
// Set up dependency resolution using platforms:
KotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, target, project.configurations)
AbstractKotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, target, project.configurations)
}
target.compilations.getByName("test").run {
@@ -200,4 +205,43 @@ class KotlinJvmWithJavaTargetPreset(
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) : KotlinTargetPreset<KotlinNativeTarget> {
override fun getName(): String = name
private fun createCompilerDownloadingTask() = with(project) {
if (!hasProperty(KotlinNativeProjectProperty.KONAN_HOME)) {
setProperty(KotlinNativeProjectProperty.KONAN_HOME, KonanCompilerDownloadTask.compilerDirectory)
setProperty(KotlinNativeProjectProperty.DOWNLOAD_COMPILER, true)
}
tasks.maybeCreate(KONAN_DOWNLOAD_TASK_NAME, KonanCompilerDownloadTask::class.java)
}
override fun createTarget(name: String): KotlinNativeTarget {
val result = KotlinNativeTarget(project, konanTarget).apply {
targetName = name
disambiguationClassifier = name
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
createCompilerDownloadingTask()
KotlinNativeTargetConfigurator(buildOutputCleanupRegistry).configureTarget(result)
return result
}
}
internal val KonanTarget.presetName: String
get() = when(this) {
KonanTarget.ANDROID_ARM32 -> "androidNativeArm32"
KonanTarget.ANDROID_ARM64 -> "androidNativeArm64"
else -> lowerCamelCaseName(*this.name.split('_').toTypedArray())
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.plugins.JavaPlugin
@@ -15,6 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
abstract class AbstractKotlinTarget (
override val project: Project
@@ -118,3 +120,25 @@ open class KotlinOnlyTarget<T : KotlinCompilation>(
override var disambiguationClassifier: String? = null
internal set
}
class KotlinNativeTarget(
project: Project,
val konanTarget: KonanTarget
) : KotlinOnlyTarget<KotlinNativeCompilation>(project, KotlinPlatformType.native) {
init {
attributes.attribute(konanTargetAttribute, konanTarget.name)
}
// TODO: Should binary files be output of a target or a compilation?
override val artifactsTaskName: String
get() = disambiguateName("link")
companion object {
val konanTargetAttribute = Attribute.of(
"org.jetbrains.kotlin.native.target",
String::class.java
)
}
}
@@ -0,0 +1,238 @@
package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.KonanVersionImpl
import org.jetbrains.kotlin.konan.MetaVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.util.DependencyDirectories
import java.io.File
// TODO: It's just temporary tasks used while KN isn't integrated with Big Kotlin compilation infrastructure.
open class KotlinNativeCompile : DefaultTask() {
init {
super.dependsOn(KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME)
}
@Internal
lateinit var compilation: KotlinNativeCompilation
// region inputs/outputs
@Input
lateinit var outputKind: CompilerOutputKind
// Inputs and outputs
val sources: FileCollection
@InputFiles
@SkipWhenEmpty
get() = compilation.allSources
val libraries: FileCollection
@InputFiles get() = compilation.compileDependencyFiles.filter {
it.extension == "klib"
}
@Input
var optimized = false
@Input
var debuggable = true
val processTests
@Input get() = compilation.isTestCompilation
val target: String
@Input get() = compilation.target.konanTarget.name
val additionalCompilerOptions: Collection<String>
@Input get() = compilation.extraOpts
val outputFile: Property<File> = project.objects.property(File::class.java)
// endregion
// region Useful extensions
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)
}
}
internal fun MutableList<String>.addListArg(parameter: String, values: List<String>) {
if (values.isNotEmpty()) {
addArg(parameter, values.joinToString(separator = " "))
}
}
// endregion
@TaskAction
fun compile() {
val output = outputFile.get()
output.parentFile.mkdirs()
val args = mutableListOf<String>().apply {
addArg("-o", outputFile.get().absolutePath)
addKey("-opt", optimized)
addKey("-g", debuggable)
addKey("-ea", debuggable)
addKey("-tr", processTests)
addArg("-target", target)
addArg("-p", outputKind.name.toLowerCase())
add("-Xmulti-platform")
addAll(additionalCompilerOptions)
libraries.files.forEach { library ->
library.parent?.let { addArg("-r", it) }
addArg("-l", library.nameWithoutExtension)
}
// TODO: Filter only kt files?
addAll(sources.files.map { it.absolutePath })
}
KonanCompilerRunner(project).run(args)
}
}
open class KonanCompilerDownloadTask : DefaultTask() {
internal companion object {
val simpleOsName: String = HostManager.simpleOsName()
val compilerDirectory: File
get() = DependencyDirectories.localKonanDir.resolve("kotlin-native-$simpleOsName-$compilerVersion")
// TODO: Support project property for Kotlin/Native compiler version
val compilerVersion: KonanVersion = KonanVersionImpl(MetaVersion.RELEASE, 0, 8, 2)
internal const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds"
const val KONAN_DOWNLOAD_TASK_NAME = "checkNativeCompiler"
}
private val useZip = HostManager.hostIsMingw
private val archiveExtension
get() = if (useZip) {
"zip"
} else {
"tar.gz"
}
private fun archiveFileTree(archive: File): FileTree =
if (useZip) {
project.zipTree(archive)
} else {
project.tarTree(archive)
}
private fun setupRepo(url: String): ArtifactRepository {
return project.repositories.ivy { repo ->
repo.setUrl(url)
repo.layout("pattern") {
val layout = it as IvyPatternRepositoryLayout
layout.artifact("[artifact]-[revision].[ext]")
}
repo.metadataSources {
it.artifact()
}
}
}
private fun removeRepo(repo: ArtifactRepository) {
project.repositories.remove(repo)
}
private fun downloadAndExtract() {
val versionString = compilerVersion.toString()
val url = buildString {
append("$BASE_DOWNLOAD_URL/")
append(if (compilerVersion.meta == MetaVersion.DEV) "dev/" else "releases/")
append("$versionString/")
append(simpleOsName)
}
val repo = setupRepo(url)
val compilerDependency = project.dependencies.create(
mapOf(
"name" to "kotlin-native-$simpleOsName",
"version" to versionString,
"ext" to archiveExtension
)
)
val configuration = project.configurations.detachedConfiguration(compilerDependency)
val archive = configuration.files.single()
logger.info("Use Kotlin/Native compiler archive: ${archive.absolutePath}")
logger.lifecycle("Unpack Kotlin/Native compiler (version $versionString)...")
project.copy {
it.from(archiveFileTree(archive))
it.into(DependencyDirectories.localKonanDir)
}
removeRepo(repo)
}
@TaskAction
fun checkCompiler() {
if (!project.hasProperty(KotlinNativeProjectProperty.DOWNLOAD_COMPILER)) {
val konanHome = project.getProperty(KotlinNativeProjectProperty.KONAN_HOME)
logger.info("Use a user-defined compiler path: $konanHome")
} else {
if (!compilerDirectory.exists()) {
downloadAndExtract()
}
logger.info("Use Kotlin/Native distribution: $compilerDirectory")
}
}
}
+2
View File
@@ -49,6 +49,7 @@ include ":kotlin-build-common",
":js:js.translator",
":js:js.dce",
":js:js.tests",
":konan:konan-utils",
":jps-plugin",
":kotlin-jps-plugin",
":core:descriptors",
@@ -231,6 +232,7 @@ project(':compiler:ir.psi2ir').projectDir = "$rootDir/compiler/ir/ir.psi2ir" as
project(':compiler:ir.ir2cfg').projectDir = "$rootDir/compiler/ir/ir.ir2cfg" as File
project(':compiler:ir.backend.common').projectDir = "$rootDir/compiler/ir/backend.common" as File
project(':compiler:backend.js').projectDir = "$rootDir/compiler/ir/backend.js" as File
project(':konan:konan-utils').projectDir = "$rootDir/konan/utils" as File
project(':kotlin-jps-plugin').projectDir = "$rootDir/prepare/jps-plugin" as File
project(':idea:idea-android-output-parser').projectDir = "$rootDir/idea/idea-android/idea-android-output-parser" as File
project(':plugins:android-extensions-compiler').projectDir = "$rootDir/plugins/android-extensions/android-extensions-compiler" as File