Kotlin/Native Gradle plugin

Since 1.3.40, a separate Gradle plugin for Kotlin/Native is deprecated in favor of the kotlin-multiplatform plugin. This plugin provides an IDE support along with support of the new multiplatform project model introduced in Kotlin 1.3.0. Below you can find a short list of differences between kotlin-platform-native and kotlin-muliplatform plugins. For more information see the kotlin-muliplatform documentation page. For kotlin-platform-native reference see the corresponding section.

Applying the multiplatform plugin

To apply the kotlin-multiplatform plugin, just add the following snippet into your build script:

  1. plugins {
  2. id("org.jetbrains.kotlin.multiplatform") version '1.3.40'
  3. }

Managing targets

With the kotlin-platform-native plugin a set of target platforms is specified as a list in properties of the main component:

  1. components.main {
  2. targets = ['macos_x64', 'linux_x64', 'mingw_x64']
  3. }

With the kotlin-multiplatform plugin target platforms can be added into a project using special methods available in the kotlin extension. Each method adds into a project one target which can be accessed using the targets property. Each target can be configured independently including output kinds, additional compiler options etc. See details about targets at the corresponding page.

  1. import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
  2. kotlin {
  3. // These targets are declared without any target-specific settings.
  4. macosX64()
  5. linuxX64()
  6. // You can specify a custom name used to access the target.
  7. mingwX64("windows")
  8. iosArm64 {
  9. // Additional settings for ios_arm64.
  10. }
  11. // You can access declared targets using the `targets` property.
  12. println(targets.macosX64)
  13. println(targets.windows)
  14. // You also can configure all native targets in a single block.
  15. targets.withType(KotlinNativeTarget) {
  16. // Native target configuration.
  17. }
  18. }

Each target includes two compilations: main and test compiling product and test sources respectively. A compilation is an abstraction over a compiler invocation and described at the corresponding page.

Managing sources

With the kotlin-platform-native plugin source sets are used to separate test and product sources. Also you can specify different sources for different platforms in the same source set:

  1. sourceSets {
  2. // Adding target-independent sources.
  3. main.kotlin.srcDirs += 'src/main/mySources'
  4. // Adding Linux-specific code.
  5. main.target('linux_x64').srcDirs += 'src/main/linux'
  6. }

With the kotlin-multiplatform plugin source sets are also used to group sources but source files for different platforms are located in different source sets. For each declared target two source sets are created: <target-name>Main and <target-name>Test containing product and test sources for this platform. Common for all platforms sources are located in commonMain and commonTest source sets created by default. More information about source sets can be found here.

  1. kotlin {
  2. sourceSets {
  3. // Adding target-independent sources.
  4. commonMain.kotlin.srcDirs += file("src/main/mySources")
  5. // Adding Linux-specific code.
  6. linuxX64Main.kotlin.srcDirs += file("src/main/linux")
  7. }
  8. }

Managing dependencies

With the kotlin-platform-native plugin dependencies are configured in a traditional for Gradle way by grouping them into configurations using the project dependencies block:

  1. dependencies {
  2. implementation 'org.sample.test:mylibrary:1.0'
  3. testImplementation 'org.sample.test:testlibrary:1.0'
  4. }

The kotlin-multiplatform plugin also uses configurations under the hood but it also provides a dependencies block for each source set allowing configuring dependencies of this sources set:

  1. kotlin.sourceSets {
  2. commonMain {
  3. dependencies {
  4. implementation("org.sample.test:mylibrary:1.0")
  5. }
  6. }
  7. commonTest {
  8. dependencies {
  9. implementation("org.sample.test:testlibrary:1.0")
  10. }
  11. }
  12. }

Note that a module referenced by a dependency declared for commonMain or commonTest source set must be published using the kotlin-multiplatform plugin. If you want to use libraries published by the kotlin-platform-native plugin, you need to declare a separate source set for common native sources.

  1. kotlin.sourceSets {
  2. // Create a common source set used by native targets only.
  3. nativeMain {
  4. dependsOn(commonMain)
  5. dependencies {
  6. // Depend on a library published by the kotlin-platform-naive plugin.
  7. implementation("org.sample.test:mylibrary:1.0")
  8. }
  9. }
  10. // Configure all native platform sources sets to use it as a common one.
  11. linuxX64Main.dependsOn(nativeMain)
  12. macosX64Main.dependsOn(nativeMain)
  13. //...
  14. }

See more info about dependencies at the corresponding page.

Output kinds

With the kotlin-platform-native plugin output kinds are specified as a list in properties of a component:

  1. components.main {
  2. // Compile the component into an executable and a Kotlin/Native library.
  3. outputKinds = [EXECUTABLE, KLIBRARY]
  4. }

With the kotlin-multiplatform plugin a compilation always produces a *.klib file. A separate binaries block is used to configure what final native binaries should be produced by each target. Each binary can be configured independently including linker options, executable entry point etc.

  1. kotlin {
  2. macosX64 {
  3. binaries {
  4. executable {
  5. // Binary configuration: linker options, name, etc.
  6. }
  7. framework {
  8. // ...
  9. }
  10. }
  11. }
  12. }

See more about native binaries declaration at the corresponding page.

Publishing

Both kotlin-platform-native and kotlin-multiplatform plugins automatically set up artifact publication when the maven-publish plugin is applied. See details about publication at the corresponding page. Note that currently only Kotlin/Native libraries (*.klib) can be published for native targets.

Cinterop support

With the kotlin-platform-native plugin interop with a native library can be declared in component dependencies:

  1. components.main {
  2. dependencies {
  3. cinterop('mystdio') {
  4. // Cinterop configuration.
  5. }
  6. }
  7. }

With the kotlin-multiplatform plugin interops are configured as a part of a compilation (see details here). The rest of an interop configuration is the same as for the kotlin-platform-native plugin.

  1. kotlin {
  2. macosX64 {
  3. compilations.main.cinterops {
  4. mystdio {
  5. // Cinterop configuration.
  6. }
  7. }
  8. }
  9. }

kotlin-platform-native reference

Overview

You may use the Gradle plugin to build Kotlin/Native projects. Builds of the plugin are available at the Gradle plugin portal, so you can apply it using Gradle plugin DSL:

  1. plugins {
  2. id "org.jetbrains.kotlin.platform.native" version "1.3.0-rc-146"
  3. }

You also can get the plugin from a Bintray repository. In addition to releases, this repo contains old and development versions of the plugin which are not available at the plugin portal. To get the plugin from the Bintray repo, include the following snippet in your build script:

  1. buildscript {
  2. repositories {
  3. mavenCentral()
  4. maven {
  5. url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
  6. }
  7. }
  8. dependencies {
  9. classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:1.3.0-rc-146"
  10. }
  11. }
  12. apply plugin: 'org.jetbrains.kotlin.platform.native'

By default the plugin downloads the Kotlin/Native compiler during the first run. If you have already downloaded the compiler manually you can specify the path to its root directory using org.jetbrains.kotlin.native.home project property (e.g. in gradle.properties).

  1. org.jetbrains.kotlin.native.home=/home/user/kotlin-native-0.8

In this case the compiler will not be downloaded by the plugin.

Source management

Source management in the kotlin.platform.native plugin is uniform with other Kotlin plugins and is based on source sets. A source set is a group of Kotlin/Native source which may contain both common and platform-specific code. The plugin provides a top-level script block sourceSets allowing you to configure source sets. Also it creates the default source sets main and test (for production and test code respectively).

By default the production sources are located in src/main/kotlin and the test sources - in src/test/kotlin.

  1. sourceSets {
  2. // Adding target-independent sources.
  3. main.kotlin.srcDirs += 'src/main/mySources'
  4. // Adding Linux-specific code. It will be compiled in Linux binaries only.
  5. main.target('linux_x64').srcDirs += 'src/main/linux'
  6. }

Targets and output kinds

By default the plugin creates software components for the main and test source sets. You can access them via the components container provided by Gradle or via the component property of a corresponding source set:

  1. // Main component.
  2. components.main
  3. sourceSets.main.component
  4. // Test component.
  5. components.test
  6. sourceSets.test.component

Components allow you to specify:

  • Targets (e.g. Linux/x64 or iOS/arm64 etc)
  • Output kinds (e.g. executable, library, framework etc)
  • Dependencies (including interop ones)

Targets can be specified by setting a corresponding component property:

  1. components.main {
  2. // Compile this component for 64-bit MacOS, Linux and Windows.
  3. targets = ['macos_x64', 'linux_x64', 'mingw_x64']
  4. }

The plugin uses the same notation as the compiler. By default, test component uses the same targets as specified for the main one.

Output kinds can also be specified using a special property:

  1. components.main {
  2. // Compile the component into an executable and a Kotlin/Native library.
  3. outputKinds = [EXECUTABLE, KLIBRARY]
  4. }

All constants used here are available inside a component configuration script block. The plugin supports producing binaries of the following kinds:

  • EXECUTABLE - an executable file;
  • KLIBRARY - a Kotlin/Native library (*.klib);
  • FRAMEWORK - an Objective-C framework;
  • DYNAMIC - shared native library;
  • STATIC - static native library.

Also each native binary is built in two variants (build types): debug (debuggable, not optimized) and release (not debuggable, optimized). Note that Kotlin/Native libraries have only debug variant because optimizations are preformed only during compilation of a final binary (executable, static lib etc) and affect all libraries used to build it.

Compile tasks

The plugin creates a compilation task for each combination of the target, output kind, and build type. The tasks have the following naming convention:

  1. compile<ComponentName><BuildType><OutputKind><Target>KotlinNative

For example compileDebugKlibraryMacos_x64KotlinNative, compileTestDebugKotlinNative.

The name contains the following parts (some of them may be empty):

  • <ComponentName> - name of a component. Empty for the main component.
  • <BuildType> - Debug or Release.
  • <OutputKind> - output kind name, e.g. Executabe or Dynamic. Empty if the component has only one output kind.
  • <Target> - target the component is built for, e.g. Macos_x64 or Wasm32. Empty if the component is built only for one target.

Also the plugin creates a number of aggregate tasks allowing you to build all the binaries for a build type (e.g. assembleAllDebug) or all the binaries for a particular target (e.g. assembleAllWasm32).

Basic lifecycle tasks like assemble, build, and clean are also available.

Running tests

The plugin builds a test executable for all the targets specified for the test component. If the current host platform is included in this list the test running tasks are also created. To run tests, execute the standard lifecycle check task:

  1. ./gradlew check

Dependencies

The plugin allows you to declare dependencies on files and other projects using traditional Gradle’s mechanism of configurations. The plugin supports Kotlin multiplatform projects allowing you to declare the expectedBy dependencies

  1. dependencies {
  2. implementation files('path/to/file/dependencies')
  3. implementation project('library')
  4. testImplementation project('testLibrary')
  5. expectedBy project('common')
  6. }

It’s possible to depend on a Kotlin/Native library published earlier in a maven repo. The plugin relies on Gradle’s metadata support so the corresponding feature must be enabled. Add the following line in your settings.gradle:

  1. enableFeaturePreview('GRADLE_METADATA')

Now you can declare a dependency on a Kotlin/Native library in the traditional group:artifact:version notation:

  1. dependencies {
  2. implementation 'org.sample.test:mylibrary:1.0'
  3. testImplementation 'org.sample.test:testlibrary:1.0'
  4. }

Dependency declaration is also possible in the component block:

  1. components.main {
  2. dependencies {
  3. implementation 'org.sample.test:mylibrary:1.0'
  4. }
  5. }
  6. components.test {
  7. dependencies {
  8. implementation 'org.sample.test:testlibrary:1.0'
  9. }
  10. }

Using cinterop

It’s possible to declare a cinterop dependency for a component:

  1. components.main {
  2. dependencies {
  3. cinterop('mystdio') {
  4. // src/main/c_interop/mystdio.def is used as a def file.
  5. // Set up compiler options
  6. compilerOpts '-I/my/include/path'
  7. // It's possible to set up different options for different targets
  8. target('linux') {
  9. compilerOpts '-I/linux/include/path'
  10. }
  11. }
  12. }
  13. }

Here an interop library will be built and added in the component dependencies.

Often it’s necessary to specify target-specific linker options for a Kotlin/Native binary using an interop. It can be done using the target script block:

  1. components.main {
  2. target('linux') {
  3. linkerOpts '-L/path/to/linux/libs'
  4. }
  5. }

Also the allTargets block is available.

  1. components.main {
  2. // Configure all targets.
  3. allTargets {
  4. linkerOpts '-L/path/to/libs'
  5. }
  6. }

Publishing

In the presence of maven-publish plugin the publications for all the binaries built are created. The plugin uses Gradle metadata to publish the artifacts so this feature must be enabled (see the dependencies section).

Now you can publish the artifacts with the standard Gradle publish task:

  1. ./gradlew publish

Only EXECUTABLE and KLIBRARY binaries are published currently.

The plugin allows you to customize the pom generated for the publication with the pom code block available for every component:

  1. components.main {
  2. pom {
  3. withXml {
  4. def root = asNode()
  5. root.appendNode('name', 'My library')
  6. root.appendNode('description', 'A Kotlin/Native library')
  7. }
  8. }
  9. }

Serialization plugin

The plugin is shipped with a customized version of the kotlinx.serialization plugin. To use it you don’t have to add new buildscript dependencies, just apply the plugins and add a dependency on the serialization library:

  1. apply plugin: 'org.jetbrains.kotlin.platform.native'
  2. apply plugin: 'kotlinx-serialization-native'
  3. dependencies {
  4. implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime-native'
  5. }

The example project for details.

DSL example

In this section a commented DSL is shown. See also the example projects that use this plugin, e.g. Kotlinx.coroutines, MPP http client

  1. plugins {
  2. id "org.jetbrains.kotlin.platform.native" version "1.3.0-rc-146"
  3. }
  4. sourceSets.main {
  5. // Plugin uses Gradle's source directory sets here,
  6. // so all the DSL methods available in SourceDirectorySet can be called here.
  7. // Platform independent sources.
  8. kotlin.srcDirs += 'src/main/customDir'
  9. // Linux-specific sources
  10. target('linux').srcDirs += 'src/main/linux'
  11. }
  12. components.main {
  13. // Set up targets
  14. targets = ['linux_x64', 'macos_x64', 'mingw_x64']
  15. // Set up output kinds
  16. outputKinds = [EXECUTABLE, KLIBRARY, FRAMEWORK, DYNAMIC, STATIC]
  17. // Specify custom entry point for executables
  18. entryPoint = "org.test.myMain"
  19. // Target-specific options
  20. target('linux_x64') {
  21. linkerOpts '-L/linux/lib/path'
  22. }
  23. // Targets independent options
  24. allTargets {
  25. linkerOpts '-L/common/lib/path'
  26. }
  27. dependencies {
  28. // Dependency on a published Kotlin/Native library.
  29. implementation 'org.test:mylib:1.0'
  30. // Dependency on a project
  31. implementation project('library')
  32. // Cinterop dependency
  33. cinterop('interop-name') {
  34. // Def-file describing the native API.
  35. // The default path is src/main/c_interop/<interop-name>.def
  36. defFile project.file("deffile.def")
  37. // Package to place the Kotlin API generated.
  38. packageName 'org.sample'
  39. // Options to be passed to compiler and linker by cinterop tool.
  40. compilerOpts 'Options for native stubs compilation'
  41. linkerOpts 'Options for native stubs'
  42. // Additional headers to parse.
  43. headers project.files('header1.h', 'header2.h')
  44. // Directories to look for headers.
  45. includeDirs {
  46. // All objects accepted by the Project.file method may be used with both options.
  47. // Directories for header search (an analogue of the -I<path> compiler option).
  48. allHeaders 'path1', 'path2'
  49. // Additional directories to search headers listed in the 'headerFilter' def-file option.
  50. // -headerFilterAdditionalSearchPrefix command line option analogue.
  51. headerFilterOnly 'path1', 'path2'
  52. }
  53. // A shortcut for includeDirs.allHeaders.
  54. includeDirs "include/directory" "another/directory"
  55. // Pass additional command line options to the cinterop tool.
  56. extraOpts '-verbose'
  57. // Additional configuration for Linux.
  58. target('linux') {
  59. compilerOpts 'Linux-specific options'
  60. }
  61. }
  62. }
  63. // Additional pom settings for publication.
  64. pom {
  65. withXml {
  66. def root = asNode()
  67. root.appendNode('name', 'My library')
  68. root.appendNode('description', 'A Kotlin/Native library')
  69. }
  70. }
  71. // Additional options passed to the compiler.
  72. extraOpts '--time'
  73. }