Challenge: Compile the cgreeter c library, generate interop bindings within Kotlin/Native, and invoke the c library function in a Kotlin file.
-
Download CLion: https://www.jetbrains.com/clion/download
-
clone the project repo:
git clone https://github.com/mutexkid/kotlin-native-workshop -
open a terminal and go to the root directory of the repo. type
./gradlew tasksto list the available tasks for the project. Notice the following output:Build tasks ----------- assemble - Assembles the outputs of this project. build - Assembles and tests this project. clean - Deletes the build directory. compileKonan - Compiles all the Kotlin/Native artifacts compileKonanKgreeter - Build the Kotlin/Native executable 'compileKonanKgreeter' for all supported and declared targets compileKonanKgreeterMacbook - Build the Kotlin/Native executable 'compileKonanKgreeterMacbook' for target 'macbook' compileKonanKgreeterMacos_x64 - Build the Kotlin/Native executable 'compileKonanKgreeterMacos_x64' for target 'macos_x64' -
run the task
compileKonanKgreeterMacbookby entering./gradlew compileKonanKgreeterMacbookto create a native binary for macbook. Note that on the first time running, the kotlin/native compiler and cinterop tools will be downloaded - wait patiently! -
Notice that the
builddirectory has been created. Run the native binary that was compiled by entering:./build/konan/bin/macos_x64/kgreeter.kexe. -
Observe the following output:
Hello from Kotlin/Native!
- Observe the
CmakeLists.txtfile in the directory./src/libs/cgreeter. This is a configuration tool similar togradlefor c projects. To build the c library, you first must generate a Makefile usingcmake(command line tool). Change to the./src/libs/cgreeterdirectory and typecmake CmakeLists.txtto generate the build config. - Compile the library by running
makewithin the./src/libs/cgreeterdirectory. - Notice that a static library was generated by the compiler:
./src/libs/cgreeter/libcgreeter.a
-
Now that the library is compiled, you will generate interop bindings that allow you to invoke the c function from kotlin/native. The
cinteroptool (part of the kotlin/native toolchain) requires 2 things: a header file (similar to a java interface), and a library (either a .a or .dylib on *nix machines). -
Open cgreeter.c. Notice the signature of the
sayHelloInCfunction. -
Define a header file for the cgreeter library so that the interop tool can create a matching stub file. Create a file called
cgreeter.hand add the signature only for sayHelloInC:cgreeter.h:void sayHelloInC(char *name);
-
Next, you will generate the interop bindings that will allow you to call the function from kotlin/native. Kotlin/Native uses the
cinteroptool to allow you to invoke functions in the c library you compiled from Kotlin, and thecinteroptool takes the specification for the library you wish to bind to as a gradle defintion. -
Add an interop entry to the
build.gradlefile, and the cgreeter artifact dependency to the kgreeter program definition. After completing, your build.gradle should look like the following:build.gradle:plugins { id "org.jetbrains.kotlin.konan" version "0.8" } konanArtifacts { interop('cgreeter', targets: ['macbook']) { defFile 'cgreeter.def' } program('kgreeter', targets: ['macbook']) { srcDir "${project.rootDir}/src/main/kotlin" libraries { artifact 'cgreeter' } } } -
Notice that
cgreeter.defis referenced in the build.gradle but doesn't exist yet. Time to fix that. Create a file calledcgreeter.defin the project root, and add the following:cgreeter.def:package = cgreeter headers = cgreeter.h compilerOpts=-I./src/libs/cgreeter/ libraryPaths =./src/libs/cgreeter/ staticLibraries = libcgreeter.aThe definition specifies the location of the header and binary for the cgreeter library so that the cinterop tool can generate bindings.
-
Verify the cinterop config works correctly by running
./gradlew compileKonanKgreeterMacbook. Notice that a new step was added to the gradle output:> :compileKonanCgreeterMacos_x64. In this step, the interop bindings were generated for the c library and added to the project (underbuild/konan/libs/macos_x64/cgreeter.klib-build)
-
The interop stubs have been created for the c library. Now you can call the library from Kotlin/Native. Add the following to hello.kt:
hello.ktimport kotlinx.cinterop.cstr fun main(args: Array<String>) { println("Hello from Kotlin/Native!") cgreeter.sayHelloInC("Josh".cstr) }
-
Compile and run the program:
./gradlew compileKonanKgreeter && ./build/konan/bin/macos_x64/kgreeter.kexe -
Notice several things about the file:
import kotlinx.cinterop: K/N cinterop features are imported from this package and include types to represent C Pointers, C Variables, and many other useful features: https://kotlinlang.org/docs/tutorials/native/interop-with-c.htmlcgreeter.sayHelloInC(): here you imported the cgreeter library that you compiled and generated bindings for"Josh".cstrYou converted a Kotlin string type to the corresponding c type, achar *. (using the kotlinx.cinterop.cstrextension)