You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Kotlin多平台(KMP)下C++代码编译为klib及跨平台通用构建配置方案咨询

Kotlin多平台(KMP)下C++代码编译为klib及跨平台通用构建配置方案咨询

我完全懂你这种被分平台配置折腾得头大的感受——重复写Android NDK、iOS CocoaPods、桌面平台编译脚本确实太繁琐了!幸好针对纯标准C++代码(不用android.hWindows.h这类平台特定头文件),Kotlin多平台(KMP)确实有一套通用的Gradle配置方案,能帮你一次性搞定所有平台的编译、打包和集成,不用再分平台单独折腾。

核心思路:用Kotlin/Native + Gradle统一管理C++编译与跨平台产物

Kotlin/Native本身支持与C/C代码互操作,结合Gradle的KMP插件,我们可以把C代码的编译、链接、klib生成逻辑统一写到一个Gradle脚本里,让Gradle自动为每个目标平台生成对应产物(Android的.so、iOS的原生二进制、桌面的.dll/.so/.dylib、Web的WASM),还能打包成klib供其他KMP模块复用。

方案一:直接在KMP模块中集成C++代码(适合轻量算法代码)

这种方案不需要额外的CMake,直接用Gradle任务编译C++代码,配合Kotlin的CInterop暴露API给Kotlin调用。

1. 项目结构搭建

先创建一个KMP项目,新增一个专门处理C++代码的模块(比如叫cpp-algorithms),目录结构如下:

cpp-algorithms/
├── build.gradle.kts
└── src/
    └── main/
        └── cpp/
            ├── include/       // 暴露给Kotlin的头文件
            │   └── algorithms.h
            └── src/           // C++实现代码
                └── algorithms.cpp

2. 编写C++代码

  • 头文件algorithms.h(注意用extern "C"避免C++ name mangling,让Kotlin能正确识别):
#ifndef ALGORITHMS_H
#define ALGORITHMS_H

extern "C" {
    // 暴露给Kotlin的函数
    int add(int a, int b);
    long long calculateFibonacci(int n);
}

#endif
  • 实现文件algorithms.cpp
#include "algorithms.h"

int add(int a, int b) {
    return a + b;
}

long long calculateFibonacci(int n) {
    if (n <= 1) return n;
    return calculateFibonacci(n-1) + calculateFibonacci(n-2);
}

3. 配置Gradle脚本(build.gradle.kts

这个脚本会自动处理所有目标平台的C++编译、链接,以及klib生成:

plugins {
    kotlin("multiplatform") version "1.9.20"
}

kotlin {
    // 注册所有需要支持的目标平台
    // Android Native目标
    androidNativeArm32("androidArm32")
    androidNativeArm64("androidArm64")
    androidNativeX86("androidX86")
    androidNativeX64("androidX64")
    
    // iOS目标
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    
    // 桌面目标
    linuxX64()
    linuxArm64()
    macosX64()
    macosArm64()
    windowsX64()
    windowsArm64()
    
    // Web WASM目标
    wasmJs()

    // 配置CInterop,暴露C++ API给Kotlin
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        compilations.main.apply {
            // 创建CInterop配置
            val cppInterop = cinterops.create("cppAlgorithms") {
                includeDirs("src/main/cpp/include")
                cppFlags("-std=c++17", "-O2") // C++编译选项
                header("algorithms.h") // 指定要暴露的头文件
            }

            // 注册C++静态库编译任务
            val compileCppLib = tasks.register<Exec>("compileCppLib${targetName}") {
                val srcDir = file("src/main/cpp/src")
                val outputDir = file("build/cpp-libs/${targetName}")
                outputDir.mkdirs()

                // 根据平台选择编译器和输出格式
                val (compiler, outputLib) = when (targetName) {
                    in listOf("windowsX64", "windowsArm64") -> 
                        Pair("cl.exe", "${outputDir}/algorithms.lib")
                    else -> 
                        Pair("clang++", "${outputDir}/libalgorithms.a")
                }

                // 编译命令:把所有cpp文件编译成静态库
                val srcFiles = srcDir.listFiles { it.extension == "cpp" }?.joinToString(" ") ?: ""
                commandLine(
                    compiler,
                    "-c", srcFiles,
                    "-std=c++17",
                    "-O2",
                    "-I${srcDir.parentFile}/include",
                    "-o", outputLib
                )
            }

            // 让Kotlin编译依赖C++静态库编译任务
            dependencies {
                implementation(files(compileCppLib.get().outputs.files))
                implementation(cppInterop)
            }
        }
    }

    // 生成通用klib供其他KMP模块引用
    tasks.register<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>("generateCommonKlib") {
        target = kotlin.targets.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().first()
        sourceSets = listOf(sourceSets["commonMain"])
        outputDir = file("build/libs/klib")
    }
}

4. 在其他KMP模块中调用C++代码

比如在你的主App模块的commonMain中,直接导入CInterop生成的API:

import cppAlgorithms.add
import cppAlgorithms.calculateFibonacci

fun main() {
    println("2 + 3 = ${add(2, 3)}")
    println("Fibonacci(10) = ${calculateFibonacci(10)}")
}

方案二:用CMake管理复杂C代码(适合大型C项目)

如果你的C++代码依赖第三方库、项目结构复杂,用CMake管理更方便,我们可以在Gradle中集成CMake编译任务:

1. 添加CMakeLists.txt

cpp-algorithms/src/main/cpp/下创建CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(cpp-algorithms)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 收集所有C++源文件
file(GLOB SOURCES "src/*.cpp")

# 生成静态库
add_library(cpp-algorithms STATIC ${SOURCES})

# 指定头文件目录
target_include_directories(cpp-algorithms PUBLIC include)

2. 修改Gradle脚本集成CMake

build.gradle.ktstargets.withType块中替换成CMake编译逻辑:

targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
    compilations.main.apply {
        val cmakeBuild = tasks.register<Exec>("cmakeBuild${targetName}") {
            val buildDir = file("build/cmake/${targetName}")
            buildDir.mkdirs()
            workingDir = buildDir

            // 根据平台配置CMake工具链(这里以Android和iOS为例,其他平台可类似扩展)
            val toolchainArgs = when (targetName) {
                "androidArm32" -> listOf(
                    "-DCMAKE_TOOLCHAIN_FILE=${android.ndkDirectory}/build/cmake/android.toolchain.cmake",
                    "-DANDROID_ABI=armeabi-v7a",
                    "-DANDROID_PLATFORM=android-24"
                )
                "iosArm64" -> listOf(
                    "-DCMAKE_TOOLCHAIN_FILE=../../../../cmake/ios.toolchain.cmake", // 需要iOS CMake工具链
                    "-DIOS_PLATFORM=OS64",
                    "-DARCHS=arm64"
                )
                else -> emptyList()
            }

            // 执行CMake配置和编译
            commandLine(
                "cmake", "../../../src/main/cpp",
                "-DCMAKE_BUILD_TYPE=Release",
                *toolchainArgs.toTypedArray()
            )
            commandLine("make")
        }

        dependencies {
            implementation(files("build/cmake/${targetName}/libcpp-algorithms.a"))
            cinterops.create("cppAlgorithms") {
                includeDirs("src/main/cpp/include")
                header("algorithms.h")
            }
        }
        dependsOn(cmakeBuild)
    }
}

关键优势与注意点

  • 通用配置,一次编写全平台生效:不用再为每个平台单独写编译脚本,Gradle会自动处理各平台的编译器、工具链和产物格式。
  • 自动集成到KMP打包流程:Android平台的.so会自动嵌入到AAR/JAR,iOS平台会直接集成到IPA的二进制中,桌面平台的库会放在App目录,WASM会打包到Web产物里。
  • klib复用:生成的klib可以被其他KMP模块直接引用,避免重复编译C++代码。
  • 纯标准C++是前提:如果必须用到平台特定头文件,还是需要分平台添加条件编译,但你已经放宽条件到只用标准算法代码,这个方案完全覆盖你的需求。

内容来源于stack exchange

火山引擎 最新活动