Edit Page

A Basic Kotlin/Native Application

Last Updated 15 April 2019
A look at how to compile our first Kotlin/Native application and open it in an IDE

In this tutorial, we'll look at how to

Obtaining the Compiler

The Kotlin/Native compiler is available for macOS, Linux, and Windows. It supports different targets including iOS (arm32, arm64, simulator x86_64), Windows (mingw32 and x86_64), Linux (x86_64, arm64, MIPS), macOS (x86_64), Raspberry PI, SMT32, WASM. For the full list of targets we can refer to the Kotlin/Native overview. While cross-platform compilation is possible (i.e., using one platform to compile for another), in this first tutorial we are only compiling for the operating system we're running on.

The best way to use the Kotlin/Native compiler is with a build system. It helps by downloading and caching the Kotlin/Native compiler binaries and libraries with transitive dependencies, and running the compiler and tests. It caches the compilation results too. A build system can also be used by an IDE to understand the project layout.

Kotlin/Native uses the Gradle build system through the kotlin-multiplatform plugin. We'll look at how to configure a Gradle build below. For some corner cases, a Kotlin/Native compiler can still be obtained manually (not recommended) from the Kotlin releases page on GitHub. In the tutorial, we are focusing on using the Gradle builds.

While the output of the compiler does not have any dependencies or virtual machine requirements, the compiler itself and the Gradle build system require a Java 1.8 or 11 runtime. Check out the https://jdk.java.net/11 or another resource for the best JRE, OpenJDK, or JDK distribution.

Creating Hello Kotlin

Our first application is simply going to print some text on the standard output. In our case, this text is "Hello Kotlin/Native". We can open up our favorite IDE or editor and write the following code in a file named hello.kt:

fun main() {
  println("Hello Kotlin/Native!")
}

Compiling the code from the console

To manually compile the application call the downloaded compiler and generate a hello.kexe (Linux and macOS) or hello.exe (Windows) binary file:

kotlinc-native hello.kt -o hello

While compilation from the console seems to be easy and clear, we should notice, that it does not scale well for bigger projects with hundreds of files and libraries. In addition to this, the command line approach does not explain to an IDE how to open such a project, where the sources are located, what dependencies are used, or how the dependencies are downloaded and so on.

Creating a Kotlin/Native Gradle project

The New Project wizard in IntelliJ IDEA can be used to start a new Kotlin/Native project with just one click. Check out the Kotlin section and select the Native | Gradle option to generate the project. For a better understanding and to explain what's happening, in this tutorial we'll create the project manually.

Let's first create a project folder. All the paths in this tutorial will be relative to this folder. Sometimes the missing directories will have to be created before new files are added.

Gradle supports two languages for the build scripts. We have the following options:

  • Groovy scripts in build.gradle files
  • Kotlin scripts in build.gradle.kts files

Groovy language is the oldest supported scripting language for Gradle, it leverages the power of the dynamic typing and runtime features of the language. Sometimes it can be harder to maintain Groovy build scripts. IDEs are struggling to get through the dynamism of Groovy to provide helpful insights or code completion.

Kotlin as a statically typed programming language plays well for writing Gradle build scripts. Thanks to the static type inference, the Kotlin compiler detects errors earlier and shows important compilation error messages and warnings. Both an IDE and the compiler can use the information about types to infer the available functions and properties in a given scope.

We create build.gradle build.gradle.kts Gradle build file with the following contents:

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.21'
}

repositories {
    mavenCentral()
}

kotlin {
  macosX64("native") {
    binaries {
      executable()
    }
  }
}

wrapper {
  gradleVersion = "5.3.1"
  distributionType = "ALL"
}
plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.21'
}

repositories {
    mavenCentral()
}

kotlin {
  linuxX64("native") {
    binaries {
      executable()
    }
  }
}

wrapper {
  gradleVersion = "5.3.1"
  distributionType = "ALL"
}
plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.21'
}

repositories {
    mavenCentral()
}

kotlin {
  mingwX64("native") {
    binaries {
      executable()
    }
  }
}

wrapper {
  gradleVersion = "5.3.1"
  distributionType = "ALL"
}
plugins {
    kotlin("multiplatform") version "1.3.21"
}

repositories {
    mavenCentral()
}

kotlin {
  macosX64("native") {
    binaries {
      executable()
    }
  }
}

tasks.withType<Wrapper> {
  gradleVersion = "5.3.1"
  distributionType = Wrapper.DistributionType.ALL
}
plugins {
    kotlin("multiplatform") version "1.3.21"
}

repositories {
    mavenCentral()
}

kotlin {
  linuxX64("native") {
    binaries {
      executable()
    }
  }
}

tasks.withType<Wrapper> {
  gradleVersion = "5.3.1"
  distributionType = Wrapper.DistributionType.ALL
}
plugins {
    kotlin("multiplatform") version "1.3.21"
}

repositories {
    mavenCentral()
}

kotlin {
  mingwX64("native") {
    binaries {
      executable()
    }
  }
}

tasks.withType<Wrapper> {
  gradleVersion = "5.3.1"
  distributionType = Wrapper.DistributionType.ALL
}

The prepared project sources can be directly downloaded from GitHub. GitHub. GitHub. GitHub. GitHub. GitHub.

Now need to create an empty settings.gradle.kts settings.gradle file in the project root directory.

Depending on the target platform, we use different functions, e.g. macosX64, mingwX64, linuxX64, iosX64, to create the Kotlin target. The function name is the platform which we are compiling our code for. These functions optionally take the target name as a parameter, which is "native" in our case. The specified target name is used to generate the source paths and task names in the project.

By the convention, all sources are located in the src/<target name>[Main|Test]/kotlin folders. It creates main and test source sets for every target. Let's place the hello.kt we previously created into the main source set folder, which is src/nativeMain/kotlin. The nativeMain folder comes from the "native" target name, which we specified in the build script above.

The project is ready. The next step is to open it in IntelliJ IDEA. For advanced build scenarios, it is recommended to refer to the more detailed documentation.

Anyone wanting to continue on without an IDE, will need to download and install the Gradle build tool. Make sure to use the right version of Gradle (e.g. 5.4.1 or newer). Running the gradle wrapper command will complete the project creation. Getting Started with Gradle explains in detail how to start using Gradle projects.

Opening the Project in IDE

We are using IntelliJ IDEA for this tutorial. Both the free and open source IntelliJ IDEA Community Edition and IntelliJ IDEA Ultimate Edition work for this tutorial. We can download and install both of them from https://jetbrains.com/idea/download if necessary. The Kotlin plugin is included with IntelliJ IDEA by default, but still, we need to make sure the Kotlin plugin version is 1.3.31 (or newer) in the Settings or Preferences dialog, under the Language & Frameworks | Kotlin section.

At this point, we should have a Gradle project that is ready to be opened in an IDE. IntelliJ IDEA (CLion, AppCode, or AndroidStudio) helps us to generate the Gradle Wrapper scripts for our project.

Now let's open the project in IntelliJ IDEA. For that we click on the File | Open… and select our build.gradle.kts build.gradle project file.

Open Project Dialog

Confirm to open the file as Project.

Gradle Import Dialog

Select Use gradle 'wrapper' task configuration option in the Gradle import dialog to complete the import. For existing projects, which already have Gradle wrapper scripts, the Use default Gradle wrapper option should be selected instead.

Use the path to the Java runtime version 1.8 or 11 for the Gradle JVM field. Check out the https://jdk.java.net/11 or https://adoptopenjdk.net/ for the best JRE, OpenJDK, or JDK distribution.

Running the application

Usually, a native binary can be compiled as debug with more debug information and fewer optimizations, and release where optimizations are enabled and there is no (or at least less) debug information available.
The binary files are created in the build/bin/native/debugExecutable or build/bin/native/releaseExecutable folders respectively. The file has a .kexe extension on Linux and macOS and an .exe extension on Windows. Use the following command to instruct the build to produce binaries:

./gradlew build
./gradlew build
gradlew.bat build

It's important to understand that this is now a native application, and no runtime or virtual machine is required. We can now run the compiled binary from the console:

Hello Kotlin/Native!

In addition to the build tasks, the Gradle build includes helpful tasks to run the application directly via runDebugExecutableNative and runReleaseExecutableNative.

The names of these tasks were created from the formula: run[Debug|Release]Executable<target name>, where target name is the capitalized target name that we specified in the build.gradle.kts build.gradle file out of our build, "native" in our case. Let's run the task in the IDE. For that, let's open the Gradle Tool Window and find the task in the list: Gradle Import Dialog

Alternatively, we may call the following command from the console:

./gradlew runDebugExecutableNative
./gradlew runDebugExecutableNative
gradlew.bat runDebugExecutableNative

The output should be:

> Task :runDebugExecutableNative
Hello Kotlin/Native!

BUILD SUCCESSFUL

Next Steps

Kotlin/Native can be used for many targets and applications, including, but not limited to macOS, Windows, Linux, and iOS.

Calling C, Objective-C, or Swift from Kotlin/Native is easy. Take a look at the C Interop documentation or Objective-C and Swift interop documentation or check out one of our tutorials.

With Kotlin multiplatform projects, it is possible to share the same Kotlin code between all the supported platforms. Check out the tutorial on sharing Kotlin code between iOS and Android or have a look at how to build your own multiplatform library.