Kotlin/Native Windows 本机库开发指南
Ⅰ. Kotlin/Native 简介
Kotlin/Native 是一项将 Kotlin 代码直接编译为本机二进制文件(无需 JVM)的技术。其核心优势在于:
- 高效执行:基于 LLVM 后端,生成针对特定平台优化的机器码。
- 无缝互操作:可以轻松调用 C/C++ 等系统原生库。
- 跨平台支持:最初为 iOS 设计,现已扩展至 Windows, Linux, macOS, Android 等多个平台。
Ⅱ. Windows 平台本机库概览
在 Windows 系统中,代码库主要有以下几种形式,了解它们的区别对于开发至关重要。
文件类型 | 扩展名 | 功能 | 类比 |
---|---|---|---|
动态链接库 | .dll |
在程序运行时按需加载,可被多个程序共享。 | Linux .so |
静态链接库 | .lib |
在编译链接阶段,其代码被完整复制到最终的可执行文件中。 | Linux .a |
导入库 | .lib |
同样是 .lib 文件,但它仅包含符号引用,用于告诉链接器去哪里找对应的 .dll 。 |
- |
💡
.lib
文件的“双重身份”Windows 下的
.lib
文件存在歧义:
- **静态库 (Static Library)**:包含完整的二进制代码(
.obj
文件的集合),由cl.exe
(MSVC) 或ar
(MinGW) 生成,文件体积较大。- **导入库 (Import Library)**:仅包含一个指向
.dll
的符号列表和元数据,本身不含任何可执行代码。它在链接时使用,文件体积非常小 (通常只有几 KB)。
Ⅲ. 实战:创建并使用 Kotlin/Native 动态库
本节将完整演示如何使用 Kotlin/Native 创建一个 .dll
动态库,并再创建一个 Kotlin/Native 程序来调用它。
📦 环境准备
- Gradle 项目: 建议使用官方提供的 KMP 模板项目
git clone https://github.com/Kotlin/kmp-native-wizard.git
- Visual Studio Build Tools: 用于生成导入库
.lib
文件。它比完整的 Visual Studio 更轻量。
- 下载地址 (选择 Tools for Visual Studio -> Build Tools for Visual Studio)。
第 1 阶段:创建 Kotlin/Native 动态库 (.dll
)
步骤 1: 配置 Gradle 构建脚本
在 build.gradle.kts
文件中,声明我们的目标平台为 mingwX64
,并配置生成一个动态库。
1 | // build.gradle.kts |
步骤 2: 编写要导出的 Kotlin 代码
创建一个 Kotlin 文件,并使用 @CName
注解来导出一个函数,使其能被 C/C++ 调用。
1 | // src/nativeMain/kotlin/Mordecai.kt |
步骤 3: 编译生成动态库
执行 Gradle 任务来编译项目。
1 | # 执行此命令 |
产物说明:
mordecai.dll
: 核心的动态库文件。mordecai.def
: 模块定义文件,描述了 DLL 导出的函数。mordecai_api.h
: C/C++ 头文件,包含了导出函数的声明。
第 2 阶段:在 Kotlin/Native 程序中调用动态库
现在,我们将创建一个可执行文件 (.exe
) 来调用刚刚生成的 .dll
。
步骤 4: 生成导入库 (.lib
)
为了让链接器能够识别 .dll
中的函数,我们需要从 .def
文件生成一个导入库 .lib
。
打开
x64 Native Tools Command Prompt for VS 2022
(通过开始菜单搜索)。进入 DLL 所在的目录。
1
cd path\to\your\project\build\bin\native\debugShared
执行
lib.exe
命令生成导入库。1
2
3
4# /def: 指定定义文件
# /out: 指定输出的导入库文件名
# /machine:x64 必须指定目标平台为 x64,否则可能因架构不匹配而链接失败
lib /def:mordecai.def /out:mordecai.lib /machine:x64
执行完毕后,当前目录下会生成 mordecai.lib
文件。
步骤 5: 组织 C 互操作文件
为了让 Kotlin/Native 的 C 互操作工具 (cinterop
) 能够找到库文件和头文件,我们需要将它们组织到项目中。
在
src/
目录下创建以下结构:1
2
3
4
5
6
7
8src/
└── nativeInterop/
└── cinterop/
├── include/ # 存放头文件
│ └── mordecai_api.h
└── libs/ # 存放库文件
├── mordecai.dll
└── mordecai.lib拷贝文件:
- 将步骤 3 生成的
mordecai_api.h
拷贝到include/
目录。 - 将步骤 3 生成的
mordecai.dll
和步骤 4 生成的mordecai.lib
拷贝到libs/
目录。
- 将步骤 3 生成的
步骤 6: 配置 C 互操作 (cinterop
)
回到 build.gradle.kts
,添加 cinterop
配置,告诉 Kotlin 如何与我们的 C 库进行交互。同时,添加一个 executable
配置来生成 .exe
文件。
1 | // build.gradle.kts |
同时,创建 src/nativeInterop/cinterop/mordecai.def
文件,用于指导 cinterop
工具。
1 | # src/nativeInterop/cinterop/mordecai.def |
配置完成后,同步 Gradle 项目 (Sync Project with Gradle Files),cinterop
会自动运行并生成 Kotlin 绑定代码。
步骤 7: 编写主程序并运行
现在可以编写 main
函数来调用库中的 sayHelloFromKotlinNative
函数了。
1 | // src/nativeMain/kotlin/Main.kt |
执行 Gradle 任务来运行程序。
1 | ./gradlew runDebugExecutableMingwX64 |
⚠️ 运行时错误?
第一次运行时,你可能会遇到一个错误,提示找不到
mordecai.dll
。这是因为.exe
在运行时需要在其所在目录或系统路径中找到它依赖的.dll
文件。解决方案:将
src/nativeInterop/cinterop/libs/mordecai.dll
文件手动复制到可执行文件的输出目录build/bin/native/debugExecutable/
下,然后再次运行命令即可成功。
Ⅳ. 简化流程:使用静态库
如果不想处理 .dll
运行时依赖的问题,可以选择静态链接。这会将库代码直接编译进最终的 .exe
文件中,使其成为一个独立的单文件程序,但会增加可执行文件的体积。
配置 Gradle 生成静态库:
在build.gradle.kts
的binaries
块中,添加staticLib
配置。1
2
3
4
5
6
7
8
9
10kotlin {
mingwX64("native") {
binaries {
// 生成静态库 mordecai.a (MinGW 默认) 或 mordecai.lib (MSVC)
staticLib { baseName = "mordecai" }
executable() // 保持可执行文件配置
}
// ... cinterop 配置保持不变 ...
}
}编译生成静态库:
1
./gradlew linkNativeDebugStatic # 或 linkNativeReleaseStatic
这会在
build/bin/native/debugStatic/
目录下生成libmordecai.a
文件。组织并链接:
- 将
libmordecai.a
和mordecai_api.h
拷贝到src/nativeInterop/cinterop/
下对应的libs
和include
目录。 mordecai.def
文件中的linkerOpts
配置保持不变,链接器会自动识别并使用.a
文件。- 同步 Gradle 项目。
- 将
直接运行:
1
./gradlew runDebugExecutableMingwX64
由于所有代码都已链接进
.exe
,这次无需再手动拷贝任何文件,程序可以直接运行。
Ⅴ. 总结
本文详细介绍了在 Windows 平台上使用 Kotlin/Native 创建和消费原生库的两种主要方式:
- 动态链接:通过
.dll
和导入库.lib
实现。优点是代码共享、模块化更新;缺点是需要处理运行时依赖。 - 静态链接:通过静态库
.a
或.lib
实现。优点是部署简单、单文件分发;缺点是可执行文件体积较大。
掌握这两种技术,可以让你无缝地将 Kotlin/Native 集成到现有的 C/C++ 项目中,或者为其他语言提供高性能的 Kotlin 库。
⭐ 参考源码与自动化插件
本文所有流程的实现代码,以及一个可以自动拷贝依赖文件的 Gradle 插件,都可以在以下仓库找到:
https://github.com/crowforkotlin/mordecai-kn-mingw-example