Search code examples
c++gradlekotlin-native

How do I add MinGW Kotlin Native dependencies


I'm trying to create a small application that will be able to communicate with the AWS IoT service. Since I want it to be fairly small and I wanted to try something new, I decided to go for Kotlin Native. I quickly noticed that AWS has released their C++ library that allows you to easily connect to the AWS IoT service (https://github.com/aws/aws-iot-device-sdk-cpp/tree/release) I downloaded it and even managed to compile with MinGW (yes, I'm on Windows). I noticed that it generated a bunch of *.o files. I reckon this is now the right time to import it to my Kotlin Native project. My build.gradle file for now looks completely standard

plugins {
    id 'kotlin-multiplatform' version '1.3.11'
}
repositories {
    mavenCentral()
}
kotlin {
    targets {
        // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
        // For Linux, preset should be changed to e.g. presets.linuxX64
        // For MacOS, preset should be changed to e.g. presets.macosX64
        fromPreset(presets.mingwX64, 'mingw')

        configure([mingw]) {
            // Comment to generate Kotlin/Native library (KLIB) instead of executable file:
            compilations.main.outputKinds('EXECUTABLE')
            // Change to specify fully qualified name of your application's entry point:
            compilations.main.entryPoint = 'sample.main'
        }
    }
    sourceSets {
        // Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
        // in gradle.properties file and re-import your project in IDE.
        mingwMain {
        }
        mingwTest {
        }
    }
}

task runProgram {
    def buildType = 'release' // Change to 'debug' to run application with debug symbols.
    dependsOn "link${buildType.capitalize()}ExecutableMingw"
    doLast {
        def programFile = kotlin.targets.mingw.compilations.main.getBinary('EXECUTABLE', buildType)
        exec {
            executable programFile
            args ''
        }
    }
}

for some reason I cannot find any examples as to how to add my freshly complied dependency. Normally when you code C++ you have to specify the path to Include directory and the Lib directory separately. AFAIK this is not something that gradle provides out of the box. How can I import this dependency then? Or perhaps there's some centralised repository I could simply pull such dependency from, like in pretty much every other programming language that is still used nowadays? At least this specific library doesn't seem to be available on NuGet :/


Solution

  • Kotlin/Native is not[1] interoperable with C++ at this moment. You can however create C wrapper for any C++ library and it's functions from Kotlin/Native[2].

    When using multiplatform gradle plugin you can define native interop with this syntax:

    kotlin {
        linuxX64 { // Replace with a target you need.
            compilations.main {
                cinterops {
                    myInterop {
                        // Def-file describing the native API.
                        // The default path is src/nativeInterop/cinterop/<interop-name>.def
                        defFile project.file("def-file.def")
    
                        // Package to place the Kotlin API generated.
                        packageName 'org.sample'
    
                        // Options to be passed to compiler by cinterop tool.
                        compilerOpts '-Ipath/to/headers'
    
                        // Directories for header search (an analogue of the -I<path> compiler option).
                        includeDirs.allHeaders("path1", "path2")
    
                        // Additional directories to search headers listed in the 'headerFilter' def-file option.
                        // -headerFilterAdditionalSearchPrefix command line option analogue.
                        includeDirs.headerFilterOnly("path1", "path2")
    
                        // A shortcut for includeDirs.allHeaders.
                        includeDirs("include/directory", "another/directory")
                    }
    
                    anotherInterop { /* ... */ }
                }
            }
        }
    }
    

    If you only define the interop name the plugin will look for .def file[3] in src/nativeInterop/cinterop/ directory and use it (in this case src/nativeInterop/cinterop/myInterop.def).

    kotlin {
        linuxX64 { 
            compilations.main {
                cinterops {
                    myInterop {
                    }
                }
            }
         }
    }
    

    The .def files[3] contains information about the library you are trying to use and typically look like this.

    headers = png.h
    headerFilter = png.h
    package = png
    

    More information about cinterop: https://kotlinlang.org/docs/reference/native/c_interop.html

    More information about multiplatform projects: https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html