WIP: Basic support for Kotlin/Native

 * Target presets
 * Compilation into a klib
 * Compilation into native binraies: framework and executable
 * Basic dependencies between projects
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 8656d43..e060fb2 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
@@ -7,19 +7,33 @@
 
 import org.gradle.api.Named
 import org.gradle.api.attributes.Attribute
+import org.jetbrains.kotlin.konan.target.HostManager
+import org.jetbrains.kotlin.konan.target.KonanTarget
 import java.io.Serializable
 
-enum class KotlinPlatformType: Named, Serializable {
-    common, jvm, js,
-    native; // TODO: split native into separate entries here or transform the enum to interface and implement entries in K/N
+// TODO: Do we really need a separate platform type for each native target?
+open class KotlinPlatformType(private val name: String) : Named, Serializable {
 
     override fun toString(): String = name
     override fun getName(): String = name
 
     companion object {
+
+        val COMMON = KotlinPlatformType("common")
+        val JVM = KotlinPlatformType("JVM")
+        val JS = KotlinPlatformType("JS")
+
         val attribute = Attribute.of(
             "org.jetbrains.kotlin.platform.type",
             KotlinPlatformType::class.java
         )
     }
-}
\ No newline at end of file
+}
+
+// TODO: Make KonanTarget serializable
+data class KotlinNativePlatformType(val konanTargetName: String) : KotlinPlatformType(konanTargetName) {
+    val konanTarget: KonanTarget
+        get() = HostManager().targetByName(konanTargetName)
+}
+
+fun KonanTarget.toKotlinPlatformType(): KotlinNativePlatformType = KotlinNativePlatformType(name)
\ No newline at end of file
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 0000000..366e37f
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinNativeToolRunner.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.KonanVersion
+import org.jetbrains.kotlin.konan.target.HostManager
+import org.jetbrains.kotlin.konan.target.KonanTarget
+import org.jetbrains.kotlin.konan.util.DependencyProcessor
+
+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.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")
+
+    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 }.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", DependencyProcessor.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)
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 8cd9bd0..4d6df79 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
@@ -455,13 +455,13 @@
     kotlinPluginVersion: String
 ) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion) {
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.jvm
+        get() = KotlinPlatformType.JVM
 
     override fun buildSourceSetProcessor(project: Project, compilation: KotlinCompilation, kotlinPluginVersion: String) =
             Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
 
     override fun apply(project: Project) {
-        val target = KotlinWithJavaTarget(project, KotlinPlatformType.js, "kotlin").apply {
+        val target = KotlinWithJavaTarget(project, KotlinPlatformType.JS, "kotlin").apply {
             disambiguationClassifier = null // don't add anything to the task names
         }
         (project.kotlinExtension as KotlinSingleTargetProjectExtension).target = target
@@ -477,7 +477,7 @@
 ) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion) {
 
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.common
+        get() = KotlinPlatformType.COMMON
 
     override fun buildSourceSetProcessor(
         project: Project,
@@ -487,7 +487,7 @@
         KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
 
     override fun apply(project: Project) {
-        val target = KotlinWithJavaTarget(project, KotlinPlatformType.js, "common").apply {
+        val target = KotlinWithJavaTarget(project, KotlinPlatformType.JS, "common").apply {
             disambiguationClassifier = "common"
         }
         (project.kotlinExtension as KotlinSingleTargetProjectExtension).target = target
@@ -501,7 +501,7 @@
     kotlinPluginVersion: String
 ) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion) {
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.js
+        get() = KotlinPlatformType.JS
 
     override fun buildSourceSetProcessor(
         project: Project,
@@ -513,7 +513,7 @@
         )
 
     override fun apply(project: Project) {
-        val target = KotlinWithJavaTarget(project, KotlinPlatformType.js, "2Js").apply {
+        val target = KotlinWithJavaTarget(project, KotlinPlatformType.JS, "2Js").apply {
             disambiguationClassifier = "2Js"
         }
 
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/base/KotlinCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/base/KotlinCompilationFactory.kt
index d7cf742..756c658 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/base/KotlinCompilationFactory.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/base/KotlinCompilationFactory.kt
@@ -76,8 +76,6 @@
     }
 }
 
-class Kotlin
-
 class KotlinJsCompilationFactory(
     val project: Project,
     val target: KotlinOnlyTarget<KotlinJsCompilation>
@@ -87,4 +85,25 @@
 
     override fun create(name: String): KotlinJsCompilation =
             KotlinJsCompilation(target, name, project.createSourceSetOutput(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, project.createSourceSetOutput(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/KotlinCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilation.kt
index 4ef18fee..00be67c 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilation.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilation.kt
@@ -43,8 +43,8 @@
     }
 }
 
-abstract class AbstractKotlinCompilation(
-    final override val target: KotlinTarget,
+abstract class AbstractKotlinCompilation<T : KotlinTarget>(
+    final override val target: T,
     override val compilationName: String
 ) : KotlinCompilation, HasKotlinDependencies {
     private val attributeContainer = HierarchyAttributeContainer(target.attributes)
@@ -108,8 +108,8 @@
     override fun toString(): String = "compilation '$compilationName' ($target)"
 }
 
-abstract class AbstractKotlinCompilationToRunnableFiles(target: KotlinTarget, name: String)
-    : AbstractKotlinCompilation(target, name), KotlinCompilationToRunnableFiles {
+abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinTarget>(target: T, name: String)
+    : AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles {
     override val runtimeDependencyConfigurationName: String
         get() = lowerCamelCaseName(compilationName, target.disambiguationClassifier, "runtimeClasspath")
 
@@ -128,7 +128,7 @@
     target: KotlinTarget,
     name: String,
     override val output: SourceSetOutput
-) : AbstractKotlinCompilationToRunnableFiles(target, name), KotlinCompilationWithResources {
+) : AbstractKotlinCompilationToRunnableFiles<KotlinTarget>(target, name), KotlinCompilationWithResources {
     override val processResourcesTaskName: String
         get() = disambiguateName("processResources")
 }
@@ -137,7 +137,7 @@
     target: KotlinWithJavaTarget,
     name: String,
     val javaSourceSet: SourceSet
-) : AbstractKotlinCompilationToRunnableFiles(target, name), KotlinCompilationWithResources {
+) : AbstractKotlinCompilationToRunnableFiles<KotlinWithJavaTarget>(target, name), KotlinCompilationWithResources {
     override val output: SourceSetOutput
         get() = javaSourceSet.output
 
@@ -186,16 +186,63 @@
     target: KotlinAndroidTarget,
     name: String,
     override val output: SourceSetOutput
-): AbstractKotlinCompilation(target, name)
+): AbstractKotlinCompilation<KotlinAndroidTarget>(target, name)
 
 class KotlinJsCompilation(
     target: KotlinTarget,
     name: String,
     override val output: SourceSetOutput
-) : AbstractKotlinCompilation(target, name)
+) : AbstractKotlinCompilation<KotlinTarget>(target, name)
 
 class KotlinCommonCompilation(
     target: KotlinTarget,
     name: String,
     override val output: SourceSetOutput
-) : AbstractKotlinCompilation(target, name)
\ No newline at end of file
+) : AbstractKotlinCompilation<KotlinTarget>(target, name)
+
+class KotlinNativeCompilation(
+    target: KotlinNativeTarget,
+    name: String,
+    override val output: SourceSetOutput
+) : AbstractKotlinCompilationToRunnableFiles<KotlinNativeTarget>(target, name) {
+
+    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>()
+
+    // TODO: Integrate with Big Kotlin tasks and runners and remove this method.
+    override fun source(sourceSet: KotlinSourceSet) {
+        kotlinSourceSets += sourceSet
+
+        with(target.project) {
+            addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName)
+            addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName)
+            addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName)
+            if (this is KotlinCompilationToRunnableFiles) {
+                addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName)
+            }
+        }
+    }
+
+    // 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/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt
index 3909da3..90814c1 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
@@ -23,6 +23,8 @@
 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
+import org.jetbrains.kotlin.konan.target.KonanTarget
 
 internal val Project.multiplatformExtension get(): KotlinMultiplatformExtension? =
     project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
@@ -78,6 +80,9 @@
             add(KotlinJsTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
             add(KotlinAndroidTargetPreset(project, kotlinPluginVersion, buildOutputCleanupRegistry))
             add(KotlinJvmWithJavaTargetPreset(project, kotlinPluginVersion))
+            HostManager().targets.forEach { name, target ->
+                add(KotlinNativeTargetPreset(name, project, target.toKotlinPlatformType(), buildOutputCleanupRegistry))
+            }
         }
     }
 
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinPresets.kt
index 1a9784c..0ecd612 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinPresets.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinPresets.kt
@@ -6,18 +6,27 @@
 package org.jetbrains.kotlin.gradle.plugin.mpp
 
 import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.plugins.BasePlugin
 import org.gradle.api.plugins.JavaPlugin
 import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
 import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+import org.gradle.nativeplatform.test.tasks.RunTestExecutable
+import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
 import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
 import org.jetbrains.kotlin.gradle.dsl.sourceSetProvider
 import org.jetbrains.kotlin.gradle.plugin.*
 import org.jetbrains.kotlin.gradle.plugin.base.*
-import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider
-import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsTasksProvider
-import org.jetbrains.kotlin.gradle.tasks.KotlinCommonTasksProvider
-import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
+import org.jetbrains.kotlin.gradle.tasks.*
+import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
+import org.jetbrains.kotlin.gradle.utils.setClassesDirCompatible
+import org.jetbrains.kotlin.konan.target.CompilerOutputKind
+import org.jetbrains.kotlin.konan.target.HostManager
+import java.io.File
+import java.util.*
 
 abstract class KotlinOnlyTargetPreset<T : KotlinCompilation>(
     protected val project: Project,
@@ -70,7 +79,7 @@
         KotlinCommonCompilationFactory(project, forTarget)
 
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.common
+        get() = KotlinPlatformType.COMMON
 
     override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> =
         KotlinCommonSourceSetProcessor(
@@ -104,7 +113,7 @@
         KotlinJvmCompilationFactory(project, forTarget)
 
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.jvm
+        get() = KotlinPlatformType.JVM
 
     override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> =
         Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(), compilation, kotlinPluginVersion)
@@ -133,7 +142,7 @@
         KotlinJsCompilationFactory(project, forTarget)
 
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.js
+        get() = KotlinPlatformType.JS
 
     override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> =
         Kotlin2JsSourceSetProcessor(project, Kotlin2JsTasksProvider(), compilation, kotlinPluginVersion)
@@ -185,7 +194,7 @@
     override fun createTarget(name: String): KotlinWithJavaTarget {
         project.plugins.apply(JavaPlugin::class.java)
 
-        val target = KotlinWithJavaTarget(project, KotlinPlatformType.jvm, name)
+        val target = KotlinWithJavaTarget(project, KotlinPlatformType.JVM, name)
 
         AbstractKotlinPlugin.configureTarget(target) { compilation ->
             Kotlin2JvmSourceSetProcessor(
@@ -202,4 +211,170 @@
     companion object {
         const val PRESET_NAME = "jvmWithJava"
     }
+}
+
+class KotlinNativeTargetPreset(
+    private val name: String,
+    val project: Project,
+    val platformType: KotlinNativePlatformType,
+    private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
+) : KotlinTargetPreset<KotlinNativeTarget> {
+
+    override fun getName(): String = name
+
+    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 Task.dependsOnCompilerDownloading() {
+        val checkCompilerTask = project.tasks.maybeCreate(
+            KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME,
+            KonanCompilerDownloadTask::class.java
+        )
+        dependsOn(checkCompilerTask)
+    }
+
+    private fun createKlibCompilationTask(compilation: KotlinNativeCompilation) {
+        val compileTask = project.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}'"
+
+            outputFile.set {
+                val targetSubDirectory = 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.klib")
+            }
+
+            dependsOnCompilerDownloading()
+        }
+
+        compilation.output.tryAddClassesDir { project.files(compileTask.outputFile.asFile.get()) }
+
+        project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTask)
+        if (compilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
+            project.tasks.findByName(compilation.target.artifactsTaskName)?.dependsOn(compileTask)
+            val apiElements = project.configurations.getByName(compilation.target.apiElementsConfigurationName)
+            apiElements.artifacts.add(
+                DefaultPublishArtifact(
+                    compilation.name,
+                    "klib",
+                    "klib",
+                    "klib",
+                    Date(),
+                    compileTask.outputFile.asFile.get(),
+                    compileTask
+                )
+            )
+        }
+    }
+
+    private fun createTestTask(compilation: KotlinNativeCompilation, testExecutableLinkTask: KotlinNativeCompile) {
+        val taskName = lowerCamelCaseName("run", compilation.name, compilation.platformType.name)
+        val testTask = project.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.asFile.get().absolutePath
+            // TODO: Provide a normal test path!
+            outputDir = project.layout.buildDirectory.dir("test-results").get().asFile
+
+            onlyIf { testExecutableProperty.asFile.get().exists() }
+            inputs.file(testExecutableProperty)
+            dependsOn(testExecutableLinkTask)
+            dependsOnCompilerDownloading()
+        }
+    }
+
+    // Called in whenEvaluated block.
+    private fun createBinaryLinkTasks(compilation: KotlinNativeCompilation) {
+        val konanTarget = compilation.target.konanTarget
+        val buildTypes = compilation.buildTypes
+        val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) }
+
+        // TODO: Consider using lockable set property.
+        // TODO: Provide outgoing configurations somehow.
+        for (buildType in compilation.buildTypes) {
+            for (kind in availableOutputKinds) {
+                val compilerOutputKind = kind.compilerOutputKind
+
+                val compilationSuffix = compilation.name.takeIf { it != "main" }.orEmpty()
+                val buildTypeSuffix = createDimensionSuffix(buildType.name, buildTypes)
+                val targetSuffix = compilation.target.name
+                val taskName = lowerCamelCaseName("link", compilationSuffix, buildTypeSuffix, kind.taskNameClassifier, 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
+
+                    outputFile.set {
+                        val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
+                        val compilationName = compilation.compilationName
+                        val prefix = compilerOutputKind.prefix(konanTarget)
+                        val suffix = compilerOutputKind.suffix(konanTarget)
+                        File(project.buildDir, "bin/$targetSubDirectory$compilationName/$prefix$compilationName$suffix")
+                    }
+
+                    dependsOnCompilerDownloading()
+                }
+
+                project.tasks.maybeCreate(compilation.linkAllTaskName).dependsOn(linkTask)
+
+                if (compilation.isTestCompilation &&
+                    buildType == NativeBuildType.DEBUG &&
+                    konanTarget == HostManager.host
+                ) {
+                    createTestTask(compilation, linkTask)
+                }
+            }
+        }
+    }
+
+    override fun createTarget(name: String): KotlinNativeTarget {
+        val result = KotlinNativeTarget(project, platformType).apply {
+            targetName = name
+            disambiguationClassifier = name
+
+            val compilationFactory = KotlinNativeCompilationFactory(project, this)
+            compilations = project.container(compilationFactory.itemClass, compilationFactory)
+        }
+
+        KotlinOnlyTargetConfigurator(buildOutputCleanupRegistry).configureTarget(project, result)
+
+        // TODO: Move into KotlinNativeTargetConfigurator
+        result.compilations.all { compilation ->
+            createKlibCompilationTask(compilation)
+            project.whenEvaluated {
+                createBinaryLinkTasks(compilation)
+            }
+        }
+
+        return result
+    }
 }
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinTarget.kt
index e9e86ae..59d84f1 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinTarget.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinTarget.kt
@@ -11,11 +11,13 @@
 import org.gradle.api.internal.component.UsageContext
 import org.gradle.api.plugins.JavaPlugin
 import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.KotlinNativePlatformType
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
 import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
 import org.jetbrains.kotlin.gradle.plugin.base.KotlinJvmAndroidCompilationFactory
 import org.jetbrains.kotlin.gradle.plugin.base.KotlinWithJavaCompilationFactory
 import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
+import org.jetbrains.kotlin.konan.target.KonanTarget
 
 abstract class AbstractKotlinTarget (
     override val project: Project
@@ -55,7 +57,7 @@
         internal set
 
     override val platformType: KotlinPlatformType
-        get() = KotlinPlatformType.jvm
+        get() = KotlinPlatformType.JVM
 
     private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this)
 
@@ -106,3 +108,13 @@
     override var disambiguationClassifier: String? = null
         internal set
 }
+
+class KotlinNativeTarget(
+    project: Project,
+    override val platformType: KotlinNativePlatformType
+) : KotlinOnlyTarget<KotlinNativeCompilation>(project, platformType) {
+
+    val konanTarget: KonanTarget
+        get() = platformType.konanTarget
+}
+
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 0000000..df25947
--- /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<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
+}
\ No newline at end of file
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 0000000..df8a9b0
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt
@@ -0,0 +1,183 @@
+package org.jetbrains.kotlin.gradle.tasks
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleScriptException
+import org.gradle.api.Project
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.*
+import org.jetbrains.kotlin.compilerRunner.KonanCompilerRunner
+import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
+import org.jetbrains.kotlin.compilerRunner.getProperty
+import org.jetbrains.kotlin.compilerRunner.hasProperty
+import org.jetbrains.kotlin.gradle.plugin.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
+import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
+import org.jetbrains.kotlin.konan.KonanVersion
+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.DependencyProcessor
+import org.jetbrains.kotlin.konan.util.DependencySource
+import java.io.IOException
+
+// TODO: It's just a temporary task 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: Collection<FileCollection>
+        @InputFiles get() = compilation.kotlinSourceSets.map { it.kotlin }
+
+    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
+
+
+    @OutputFile val outputFile: RegularFileProperty = newOutputFile()
+    // 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.asFile.get()
+        output.parentFile.mkdirs()
+
+        val args = mutableListOf<String>().apply {
+            addArg("-o", outputFile.get().asFile.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.flatMap { it.files }.map { it.absolutePath })
+        }
+
+        KonanCompilerRunner(project).run(args)
+    }
+}
+
+
+open class KonanCompilerDownloadTask : DefaultTask() {
+
+    internal companion object {
+        internal const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds"
+        const val KONAN_DOWNLOAD_TASK_NAME = "checkKonanCompiler"
+    }
+
+    private val simpleOsName: String = HostManager.simpleOsName()
+
+    private val konanCompilerName: String =
+        "kotlin-native-$simpleOsName-${KonanVersion.CURRENT}"
+
+    @TaskAction
+    fun downloadAndExtract() {
+        if (!project.hasProperty(KotlinNativeProjectProperty.DOWNLOAD_COMPILER)) {
+            val konanHome = project.getProperty(KotlinNativeProjectProperty.KONAN_HOME)
+            logger.info("Use a user-defined compiler path: $konanHome")
+        } else {
+            try {
+                val downloadUrlDirectory = buildString {
+                    append("$BASE_DOWNLOAD_URL/")
+                    val version = KonanVersion.CURRENT
+                    when (version.meta) {
+                        MetaVersion.DEV -> append("dev/")
+                        else -> append("releases/")
+                    }
+                    append("$version/")
+                    append(simpleOsName)
+                }
+                val konanCompiler = konanCompilerName
+                val parentDir = DependencyProcessor.localKonanDir
+                logger.info("Downloading Kotlin/Native compiler from $downloadUrlDirectory/$konanCompiler into $parentDir")
+                DependencyProcessor(
+                    parentDir,
+                    downloadUrlDirectory,
+                    mapOf(konanCompiler to listOf(DependencySource.Remote.Public))
+                ).run()
+            } catch (e: IOException) {
+                throw GradleScriptException("Cannot download Kotlin/Native compiler", e)
+            }
+        }
+    }
+}
\ No newline at end of file