diff --git a/konan/utils/build.gradle.kts b/konan/utils/build.gradle.kts new file mode 100644 index 00000000000..48c6d60abcc --- /dev/null +++ b/konan/utils/build.gradle.kts @@ -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() diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/KonanVersion.kt b/konan/utils/src/org/jetbrains/kotlin/konan/KonanVersion.kt new file mode 100644 index 00000000000..396adedc56b --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/KonanVersion.kt @@ -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 +} diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/MetaVersion.kt b/konan/utils/src/org/jetbrains/kotlin/konan/MetaVersion.kt new file mode 100644 index 00000000000..204f03b220d --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/MetaVersion.kt @@ -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") +} diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/target/KonanTarget.kt b/konan/utils/src/org/jetbrains/kotlin/konan/target/KonanTarget.kt new file mode 100644 index 00000000000..737ab424243 --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/target/KonanTarget.kt @@ -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 +} + +private class NoSubTargets: SubTargetProvider { + override fun availableSubTarget(genericName: String): List = 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 by lazy { + predefinedTargets + configurableSubtargets + } + + val targets = targetValues.associate{ it.visibleName to it } + + fun toKonanTargets(names: Iterable): List { + 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 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> by lazy { + val result = mutableMapOf>() + 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 = targetAliases[target] ?: emptyList() + } +} + +class TargetSupportException (message: String = "", cause: Throwable? = null) : Exception(message, cause) \ No newline at end of file diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/util/DependencyDirectories.kt b/konan/utils/src/org/jetbrains/kotlin/konan/util/DependencyDirectories.kt new file mode 100644 index 00000000000..b66dfe4a768 --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/util/DependencyDirectories.kt @@ -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") +} diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/util/VisibleNamed.kt b/konan/utils/src/org/jetbrains/kotlin/konan/util/VisibleNamed.kt new file mode 100644 index 00000000000..8b3bc3c4296 --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/util/VisibleNamed.kt @@ -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.visibleName get() = name.toLowerCase() +interface Named { + val name: String + val visibleName get() = name +} diff --git a/libraries/tools/kotlin-gradle-plugin-api/build.gradle b/libraries/tools/kotlin-gradle-plugin-api/build.gradle index e30d085ea73..856e661f5e5 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-api/build.gradle @@ -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' } diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlatformType.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlatformType.kt index 3e10d50ee72..f5721052869 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlatformType.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlatformType.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle b/libraries/tools/kotlin-gradle-plugin/build.gradle index 77ba615b9b4..d6b9e573ed9 100644 --- a/libraries/tools/kotlin-gradle-plugin/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin/build.gradle @@ -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') diff --git a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt index 725b8e2572c..c43e750f7c1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt @@ -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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt new file mode 100644 index 00000000000..60727d2289b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt @@ -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 + val environment: Map + val additionalSystemProperties: Map + + fun run(args: List) + 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 +): 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 by lazy { + KonanToolRunner::class.java.getResourceAsStream("/env_blacklist")?.let { stream -> + stream.reader().use { it.readLines() } + } ?: emptyList() + } + + 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>.escapeQuotesForWindows() = + if (HostManager.hostIsMingw) { + map { (key, value) -> key.escapeQuotes() to value.escapeQuotes() } + } else { + this + } + + override fun run(args: List) { + 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 = 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 = emptyList()) + : KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs) + +internal class KonanKlibRunner(project: Project, additionalJvmArgs: List = emptyList()) + : KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index e3d103c966a..ed1e69d485b 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -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: diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index a6ea4605cb0..e482996fc08 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -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( private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry ) { - fun configureTarget( - target: KotlinOnlyTarget + 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 configureCompilations(platformTarget: KotlinOnlyTarget) { + + 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 configureCompilationDefaults(target: KotlinOnlyTarget) { + 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( + buildOutputCleanupRegistry: BuildOutputCleanupRegistry +) : AbstractKotlinTargetConfigurator>(buildOutputCleanupRegistry) { + + override fun configureTarget( + target: KotlinOnlyTarget + ) { + super.configureTarget(target) + setCompatibilityOfAbstractCompileTasks(target.project) + } + + override 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 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(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 } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt index 4b6f8f69351..80f1fde3b2d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt @@ -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 { + + override val itemClass: Class + 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) + } + } + } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt index 76df3e6afe4..738a58a4cb6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationOutput.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index d892be86645..caf18db9949 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -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)) + } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt index bf3bd4952ca..6b339e27f01 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinSoftwareComponent.kt @@ -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, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt new file mode 100644 index 00000000000..55a345ea819 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt @@ -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 = 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 +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index adae5df53aa..3b4d1ffa047 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -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 = 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) \ No newline at end of file +) : 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() + + fun extraOpts(vararg values: Any) = extraOpts(values.toList()) + fun extraOpts(values: List) { + extraOpts.addAll(values.map { it.toString() }) + } + + var buildTypes = mutableListOf() + var outputKinds = mutableListOf() + + // 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 + } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index a7701887eb6..fa6d6095a57 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -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( protected val project: Project, @@ -31,7 +36,7 @@ abstract class KotlinOnlyTargetPreset( compilations = project.container(compilationFactory.itemClass, compilationFactory) } - KotlinTargetConfigurator(buildOutputCleanupRegistry).configureTarget(result) + KotlinTargetConfigurator(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" } -} \ No newline at end of file +} + +class KotlinNativeTargetPreset( + private val name: String, + val project: Project, + val konanTarget: KonanTarget, + private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry +) : KotlinTargetPreset { + + 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()) + } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt index c68a2fafff6..6b05061318b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt @@ -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( override var disambiguationClassifier: String? = null internal set } + +class KotlinNativeTarget( + project: Project, + val konanTarget: KonanTarget +) : KotlinOnlyTarget(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 + ) + } +} + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt new file mode 100644 index 00000000000..62c4834af35 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt @@ -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 + @Input get() = compilation.extraOpts + + + val outputFile: Property = project.objects.property(File::class.java) + + // endregion + + // region Useful extensions + internal fun MutableList.addArg(parameter: String, value: String) { + add(parameter) + add(value) + } + + internal fun MutableList.addArgs(parameter: String, values: Iterable) { + values.forEach { + addArg(parameter, it) + } + } + + internal fun MutableList.addArgIfNotNull(parameter: String, value: String?) { + if (value != null) { + addArg(parameter, value) + } + } + + internal fun MutableList.addKey(key: String, enabled: Boolean) { + if (enabled) { + add(key) + } + } + + internal fun MutableList.addFileArgs(parameter: String, values: FileCollection) { + values.files.forEach { + addArg(parameter, it.canonicalPath) + } + } + + internal fun MutableList.addFileArgs(parameter: String, values: Collection) { + values.forEach { + addFileArgs(parameter, it) + } + } + + internal fun MutableList.addListArg(parameter: String, values: List) { + if (values.isNotEmpty()) { + addArg(parameter, values.joinToString(separator = " ")) + } + } + // endregion + + @TaskAction + fun compile() { + val output = outputFile.get() + output.parentFile.mkdirs() + + val args = mutableListOf().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") + } + } + +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4799f1be07e..3710066892a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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