简体   繁体   中英

Access C/C++ lib from a multiplatform kotlin project

For the first time, I'm using Android Studio to build a multiplatform project. I have created an Android app module that uses the multiplatform lib on Android. I have also used XCode to build an iOS app that uses the multiplatform lib on iOS. Everything works fine and I'm able to use the expect fun that is implemented by different actual fun for Android and iOS.

I have also created a library in C++ that exposes a C interface.

#ifndef PINGLIB_LIBRARY_H
#define PINGLIB_LIBRARY_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    long long elapsed;
} PingInfo;

typedef void (*PingCallback)(PingInfo pingInfo);
typedef struct
{
    PingCallback pingUpdate;
} PingObserver;

void* ping(const char * url, const PingCallback *pingCallback);
void subscribe(void* pingOperation);
void unsubscribe(void* pingOperation);

#ifdef __cplusplus
}
#endif

#endif //PINGLIB_LIBRARY_H

I use CLion to build the C++ code. I have created a .def file that I use to build the library using cinterop .

package = com.exercise.pinglib
headers = PingLibrary.h
linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu
compilerOpts = -std=c99 -I/Users/username/myproject/ping/ping/header
staticLibraries = libping.a
libraryPaths = /opt/local/lib /Users/username/myproject/ping/cmake-build-debug

libping.a is the library created building the C++ code. It is created in the folder /Users/username/myproject/ping/cmake-build-debug

When I run the command cinterop -def ping.def -o ping , it creates the klib file and a folder containing a manifest.properties file a natives subfolder containing a cstubs.bc file and a kotlin subfolder with a .kt file.

ping.klib
-ping-build
    manifest.properties
    -natives
        cstubs.bc
    -kotlin
        -com
            -exercise
                -pinglib
                    pinglib.kt

How can I use the library created by cinterop in my kotlin-multiplatform project?

I have found different ways to import it but I did not find any complete description of how to do it. Here they say that I can use something like:

    implementation files("ping.klib")

I did it for the iOS project but I still don't know how to access the kotlin classes neither on Android nor on iOS.

This is my build.gradle

apply plugin: 'com.android.library'
apply plugin: 'kotlin-multiplatform'

android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 15
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
kotlin {
    targets {
        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith('iphoneos') ? presets.iosArm64 : presets.iosX64
        fromPreset(iOSTarget, 'ios') {
            binaries {
                framework('shared')
            }
        }
        fromPreset(presets.android, 'android')
    }
    sourceSets {
        // for common code
        commonMain.dependencies {
            api 'org.jetbrains.kotlin:kotlin-stdlib-common'
        }
        androidMain.dependencies {
            api 'org.jetbrains.kotlin:kotlin-stdlib'
        }
        iosMain.dependencies {
            implementation files("ping.klib")
        }
    }
}
configurations {
    compileClasspath
}

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")
    final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
    final def framework = kotlin.targets.ios.binaries.getFramework("shared", mode)
    inputs.property "mode", mode
    dependsOn framework.linkTask
    from { framework.outputFile.parentFile }
    into frameworkDir
    doLast {
        new File(frameworkDir, 'gradlew').with {
            text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
            setExecutable(true)
        }
    }
}
tasks.build.dependsOn packForXCode

EDIT I have changed the question because, initially, I thought that cinterop was not creating the klib library but it was just a mistake: I was looking in the ping-build folder but the file is outside that folder. So I resolved half of the question.

EDIT2 I have added the build.script

I'm glad to see that everything is fine with the KLIB, now about the library use.
First of all, I have to mention that this library can be utilized only by Kotlin/Native compiler, meaning it will be available for some targets(see list here ). Then, if you're going to include C library use into an MPP project, it is always better to produce bindings via the Gradle script. It can be done inside of a target, see this doc for example. For your iOS target it should be like:

kotlin {
    iosX64 {  // Replace with a target you need.
        compilations.getByName("main") {
            val ping by cinterops.creating {
                defFile(project.file("ping.def"))
                packageName("c.ping")
            }
        }
    }
}

This snippet will add cinterop task to your Gradle, and provide module to include like import c.ping.* inside of the corresponding Kotlin files.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM