文接上一篇# Kotlin/Native 构建(一)。
Kotlin/Native 预构建工具链
当 Kotlin/Native 项目在 AS 中构建时,会下载 Kotlin/Native 预构建工具链和 Kotlin/Native 项目所需依赖库文件。它们下载位置位于:
- Kotlin/Native 预构建工具链:
~/.konan/kotlin-native-prebuilt-macos-aarch64-2.1.10-RC2 - Kotlin/Native 项目所需依赖库:
~/.konan/dependencies
目录结构:
.konan
├── dependencies
│ ├── aarch64-unknown-linux-gnu-gcc-8.3.0-glibc-2.25-kernel-4.9-2 # arm64 Linux 交叉编译工具链
│ ├── cache
│ ├── libffi-3.3-1-macos-arm64
│ ├── lldb-4-macos # LLVM 调试器
│ ├── llvm-16.0.0-aarch64-macos-essentials-63 # arm64 macos LLVM 16 工具链
│ ├── msys2-mingw-w64-x86_64-2 # Windows 交叉编译工具链
│ ├── target-sysroot-1-android_ndk # Android NDK 交叉编译的系统根目录
│ ├── target-toolchain-2-osx-android_ndk # macOS 到 Android 的 NDK 交叉编译工具链
│ └── x86_64-unknown-linux-gnu-gcc-8.3.0-glibc-2.19-kernel-4.9-2 # x86_64 Linux 交叉编译工具链
└── kotlin-native-prebuilt-macos-aarch64-2.1.10-RC2
├── bin # 存放编译和调试工具
├── klib # 用于存储预编译库(类似于 Java 的 `.jar`)
├── konan # 用于存储运行时库和目标平台支持
├── licenses # 许可证文件
├── provisioned.ok
├── sources # Kotlin 标准库源码
└── tools # 工具目录
- kotlin-native-prebuilt 文件:有构建工具链(负责前端编译,后端编译,链接),平台适配(macOS aarch64 架构的交叉编译配置,与 macOS 系统 API交互的必要头文件和符号)等。
- dependencies 文件:有 Kotlin/Native 标准库,标准 C 运行时库,交叉编译工具链,目标平台的 sysroot(系统根目录)等。
kotlin-native-prebuilt
目录结构:
.
├── bin
│ ├── cinterop # C 互操作工具(生成 .klib)
│ ├── generate-platform # 生成目标平台定义
│ ├── jsinterop # JavaScript 互操作(实验性)
│ ├── klib # 处理 .klib 库文件
│ ├── konan-lldb # LLDB 调试工具
│ ├── konanc # Kotlin/Native 编译器
│ ├── kotlinc-native # Kotlin/Native 编译器(可执行入口)
│ └── run_konan # Kotlin/Native 运行时入口
├── klib
│ ├── cache # 已编译的 klib 缓存
│ ├── common # 公共 Kotlin/Native 库
│ ├── commonized # 针对不同平台的库
│ └── platform # 平台相关库(iOS、macOS、Linux)
├── konan
│ ├── konan.properties # 配置 Kotlin/Native 版本和路径
│ ├── lib # Kotlin/Native 运行时库
│ ├── nativelib # 目标平台的本机库
│ ├── platformDef # 平台定义文件
│ ├── platforms # 不同目标平台配置
│ ├── swift_export # Swift 互操作相关工具
│ └── targets # 交叉编译的目标平台文件
├── licenses
│ ├── COPYRIGHT.txt
│ ├── COPYRIGHT_HEADER.txt
│ ├── LICENSE.txt
│ ├── NOTICE.txt
│ ├── README.md
│ └── third_party
├── provisioned.ok
├── sources
│ └── kotlin-stdlib-native-sources.zip # Kotlin 标准库源码
└── tools
├── env_blacklist
└── konan_lldb.py
这里主要介绍一下 bin 目录。
bin 目录
bin/ 目录包含 Kotlin/Native 核心工具,用于编译、调试、C 互操作、库管理等。
konanc
konanc:Kotlin/Native 编译器。
新建一个 Main.kt:
import kotlin.experimental.ExperimentalNativeApi
fun main() {
println("Hello from Kotlin/Native")
}
@OptIn(ExperimentalNativeApi::class)
@CName("nativeFunction")
fun nativeFunction(): String {
return "Hello from Kotlin/Native."
}
生成目标平台 linux_arm64 动态库 *.so:
konanc Main.kt -o kn -p dynamic -target linux_arm64
在 Main.kt 同级目录下会生成:
.
├── Main.kt
├── kn_api.h # 头文件
└── libkn.so # arm64_v8a so
生成目标平台 ios_arm64 *.framework:
konanc Main.kt -o kn -p framework -target ios_arm64
在 Main.kt 同级目录下会生成:
.
├── Main.kt
├── kn.framework
│ ├── Headers
│ │ └── kn.h
│ ├── Info.plist
│ ├── Modules
│ │ └── module.modulemap
│ └── kn
└── kn.framework.dSYM
└── Contents
├── Info.plist
└── Resources
├── DWARF
│ └── kn
└── Relocations
└── aarch64
└── kn.yml
直接编译 kotlin 源码:
konanc Main.kt -o kn
生成 .kexe 可运行文件:
.
├── Main.kt
├── kn.kexe
└── kn.kexe.dSYM
└── Contents
├── Info.plist
└── Resources
├── DWARF
│ └── kn.kexe
└── Relocations
└── aarch64
└── kn.kexe.yml
执行 kn.kexe:
./kn.kexe
Hello from Kotlin/Native
kotlinc-native
kotlinc-native:Kotlin/Native 编译器入口。提供与 konanc 相同操作命令,在 Kotlin/Native 项目中,使用 kotlinc-native 更好。
指定本地 llvm 路径:
kotlinc-native -Xllvm-variant={dev|user|absolute path to llvm}
dev: 包含额外开发工具的较大 LLVM 发行版。
user: 仅包含必要工具的较小 LLVM 发行版。
<absolute path>: 本地 LLVM 发行版的绝对路径。
更多高级编译器选项:kotlinc-native -X
cinterop
cinterop:C 互操作工具。
与 C 库 curl 交互,定义一个文件 curl.def(查看def文件定义规则):
headers = curl/curl.h # 指定要导入的 C/C++ 头文件
headerFilter = curl/* # 指定要过滤的头文件内容
compilerOpts.osx = -I/opt/homebrew/include # 指定 macOS 平台编译器的选项
linkerOpts.osx = -L/opt/homebrew/lib -lcurl # 指定 macOS 平台链接器的选项,在`/opt/homebrew/lib` 目录下找 curl
生成 macOS平台 klib 文件:
cinterop -def curl.def -o curl
在 curl.def 同级目录下会生成:
.
├── curl-build
│ ├── manifest.properties
│ └── natives
│ └── cstubs.bc
├── curl.def
└── curl.klib
通过 curl.klib 就可以直接使用 curl:
import curl.CURLOPT_URL
import curl.curl_easy_cleanup
import curl.curl_easy_init
import curl.curl_easy_perform
import curl.curl_easy_setopt
import kotlin.experimental.ExperimentalNativeApi
import kotlinx.cinterop.*
@OptIn(ExperimentalForeignApi::class)
fun main() {
memScoped {
val curl = curl_easy_init()
if (curl != null) {
val url = "https://www.bilibili.com"
curl_easy_setopt(curl, CURLOPT_URL, url)
curl_easy_perform(curl)
curl_easy_cleanup(curl)
} else {
println("Failed to initialize curl")
}
}
}
klib
klib:管理 Kotlin/Native 预编译库 (.klib),类似 Java 的 .jar。
查看 klib 信息:
klib info curl.klib
Full path: /android-workplace/kmp-native-wizard/src/nativeInterop/cinterop/curl.klib
Module name (metadata): <curl>
Non-empty package FQNs (1):
curl
Has IR: false
Has LLVM bitcode: true
Manifest properties:
abi_version=1.201.0
builtins_platform=NATIVE
compilerOpts.osx=-I/opt/homebrew/include
compiler_version=2.1.10-RC2
depends=stdlib org.jetbrains.kotlin.native.platform.posix
exportForwardDeclarations=cnames.structs.curl_mime cnames.structs.curl_mimepart cnames.structs.curl_pushheaders cnames.structs.Curl_URL
headerFilter=curl/*
headers=curl/curl.h
includedForwardDeclarations=cnames.structs.curl_mime cnames.structs.curl_mimepart cnames.structs.curl_pushheaders cnames.structs.Curl_URL
includedHeaders=usr/include/curl/curl.h usr/include/curl/curlver.h usr/include/curl/system.h usr/include/curl/easy.h usr/include/curl/multi.h usr/include/curl/urlapi.h usr/include/curl/options.h usr/include/curl/header.h usr/include/curl/websockets.h 738c17d164114f55f5df47f14b063c84ed7a4004dfcf35b65466dcc2c87c9d96
interop=true
ir_provider=kotlin.native.cinterop
ir_signature_versions=1,2
linkerOpts.osx=-L/opt/homebrew/lib -lcurl
metadata_version=1.4.1
native_targets=macos_arm64
package=curl
unique_name=curl
generate-platform
generate-platform:生成 Kotlin/Native 的目标平台配置,用于跨平台支持。
生成平台 ios_arm64 配置:
generate-platform -target ios_arm64
默认输入路径为:.konan/kotlin-native-prebuilt-macos-aarch64-2.1.10-RC2/konan/platformDef/linux_arm64
默认输出路径为:.konan/kotlin-native-prebuilt-macos-aarch64-2.1.10-RC2/klib/platform/linux_arm64
jsinterop(已被移除)
jsinterop:与 JavaScript 进行互操作(实验性)。
konan-lldb
konan-lldb:提供 LLDB 调试支持,用于断点调试和查看变量。
konan-lldb kn.kexe
run_konan
run_konan:运行 .kexe 文件。
dependencies
目录结构:
.
├── aarch64-unknown-linux-gnu-gcc-8.3.0-glibc-2.25-kernel-4.9-2 # arm64 Linux 交叉编译工具链
├── cache
├── libffi-3.3-1-macos-arm64
├── lldb-4-macos # LLVM 调试器
├── llvm-16.0.0-aarch64-macos-essentials-63 # macos arm64 LLVM 16 工具链
├── msys2-mingw-w64-x86_64-2
├── target-sysroot-1-android_ndk
├── target-toolchain-2-osx-android_ndk
└── x86_64-unknown-linux-gnu-gcc-8.3.0-glibc-2.19-kernel-4.9-2
这里主要介绍 llvm-16.0.0-aarch64-macos-essentials-63 目录。
llvm-16.0.0-aarch64-macos-essentials-63
llvm-16.0.0-aarch64-macos-essentials-63/ 目录包含 arm macos LLVM 16 工具链(clang、lld、llvm-ar 等),用于Kotlin/Native 代码编译、优化和链接。
目录结构:
.
├── bin
│ ├── clang -> clang-16
│ ├── clang++ -> clang
│ ├── clang-16
│ ├── clang-cache -> clang
│ ├── ld.lld -> lld
│ ├── lld
│ ├── llvm-ar
│ ├── llvm-cov
│ ├── llvm-profdata
│ └── wasm-ld -> lld
└── lib
├── clang
└── libclang.dylib
bin/目录包含 macos arm64 LLVM 执行工具,用于 Kotlin/Native 编译过程。
clang/clang++
C/C++ 项目构建流程主要包含:预处理、编译、汇编、链接。流程阶段产物:.i →
.s → .o → .so or .dylib。
新建一个 Main.cpp:
#include <iostream>
int main() {
std::cout << "Hello from Kotlin/Native." << std::endl;
return 0;
}
编译 c++ 代码:
clang++ Main.cpp -o Main --sysroot=$(xcrun --show-sdk-path)
xcrun --show-sdk-path=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
在 Main.cpp 同级目录下会生成 Main。Main 是可执行文件:
./Main
Hello from Kotlin/Native.
编译生成汇编代码.s:
clang++ -S Main.cpp -o Main.s --sysroot=$(xcrun --show-sdk-path)
编译生成目标文件.o:
clang++ -c Main.cpp -o Main.o --sysroot=$(xcrun --show-sdk-path)
链接成可执行文件.dylib:
clang++ -shared -o Main.dylib Main.o --sysroot=$(xcrun --show-sdk-path)
查看 .dylib信息:
dyld_info Main.dylib
Main.dylib [arm64]:
-platform:
platform minOS sdk
macOS 14.0 14.0
-segments:
load-offset segment section sect-size seg-size perm
0x00000000 __TEXT 16KB r.x
0x00003170 __text 3128
0x00003DA8 __stubs 192
0x00003E68 __gcc_except_tab 124
0x00003EE4 __cstring 26
0x00003F00 __unwind_info 256
0x00004000 __DATA_CONST 16KB rw.
0x00004000 __got 152
-dependents:
attributes load path
/usr/lib/libc++.1.dylib
/usr/lib/libSystem.B.dylib
Main.dylib依赖 C++ 标准库(libc++):/usr/lib/libc++.1.dylib,和核心系统库(libc标准 C 库、libm数学库、libpthread线程库等):/usr/lib/libSystem.B.dylib。
交叉编译
查看clang/clang++ 版本信息:
./clang++ --version
clang++ version 16.0.0 (https://github.com/Kotlin/llvm-project 4e2cc6a8ac2afae0d0ddde4a51818642f0f5304f)
Target: arm64-apple-darwin23.3.0
Thread model: posix
InstalledDir: ~/.konan/dependencies/llvm-16.0.0-aarch64-macos-essentials-63/bin/.
首先 llvm 版本为 16.0.0,交叉编译需要关注一下版本号匹配。
Target 组成为:<CPU架构>-<厂商>-<操作系统>-<环境>,Target: arm64-apple-darwin23.3.0 表示 arm64 CPU架构,apple 厂商,darwin23.3.0 操作系统。
指定交叉编译 aarch64-unknown-linux-gnu 目标平台:arm64 CPU架构,未知厂商,linux 系统,gnu 环境
./clang++ -target aarch64-unknown-linux-gnu
指定交叉编译 aarch64-unkonwn-linux-android30 目标平台:arm64 CPU架构,未知厂商,linux 系统,android30 环境
./clang++ -target aarch64-unkonwn-linux-android30
交叉编译通常需要指定参数:指定头文件搜索路径(#include):./clang++ -I <dir>;指定库文件搜索路径(.so, .a):./clang++ -L <dir>;指定系统根目录,头文件 & 库的默认路径:-isysroot <dir>。
使用 llvm-16.0.0-aarch64-macos-essentials-63/bin/clang++ 生成 arm64 Android 平台 *.so:
./clang++ -target aarch64-unknown-linux-android30 -shared -o libMain.so Main.cpp -fuse-ld=lld --sysroot=/Users/wangjiang/Public/software/android-sdk-macos/ndk/28.0.12916984/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -L /Users/wangjiang/Public/software/android-sdk-macos/ndk/28.0.12916984/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/19/lib/linux/aarch64
这里 -shared 表示生成共享库;-fuse-ld=lld 表示指定 llvm 的 ld.lld 链接器;ndk 版本为 28.0.12916984。
查看生成的 libMain.so:
file libMain.so
libMain.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, not stripped
ld.lld
ld.lld 链接 *.o文件生成目标产物.so / .dylib。
链接生成 libMain.so:
./ld.lld -shared -o libMain.so Main.o --sysroot=/Users/wangjiang/Public/software/android-sdk-macos/ndk/28.0.12916984/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -L /Users/wangjiang/Public/software/android-sdk-macos/ndk/28.0.12916984/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/19/lib/linux/aarch64
使用 clang/clang++ 与 ld.lld 直接链接生成的目标产物 .so/.dylib是有所不同的,clang/clang++ 生成时,会默认链接链接 C++ 标准库(如 libc++_shared.so)和 C 标准库(如 libc.so)等。而 ld.lld 直接链接生成时,不会自动引入标准库,除非手动指定。
构建流程汇总
在 Kotlin/Native 项目构建过程中,使用 kotlin-native-prebuilt 和 dependencies 相关流程为:
graph TD
A[Source Code Kotlin] -->|Kotlin Compiler| B[kotlinc-native]
B -->|Generates| C[IR Intermediate Representation]
C -->|LLVM Backend| D[LLVM IR]
D -->|Optimized by LLVM| E[Optimized LLVM IR]
E -->|LLVM Code Generation| F[Machine Code]
subgraph kotlin-native-prebuilt-macos-aarch64-2.1.10-RC2
B[kotlinc-native] -->|Calls| G[konanc]
G -->|Interacts with| H[cinterop]
G -->|Uses| I[klib]
G -->|Debugging Support| J[konan-lldb]
end
subgraph dependencies
K[LLVM Toolchain]
L[Clang]
M[LLD Linker]
N[Sysroot / Libs]
end
G -->|Uses| K
K -->|Provides| L
K -->|Provides| M
K -->|Provides| N
L -->|Compiles| F
M -->|Links| O[Executable .kexe or Shared Library .so/.dylib/.framework]
总结
Kotlin/Native 项目通过 kotlin-native-prebuilt预构建工具链和 dependencies依赖库一起完成从 Kotlin 代码生成可运行的本机二进制文件的任务。
在构建过程中,Kotlin 代码首先通过 Kotlin 编译器生成 IR,IR 通过 LLVM Backend 生成 LLVM IR,LLVM IR 通过 LLVM 生成机器码,最后机器码通过链接器生成目标平台产物 .so\.framework。
这其中,kotlinc-native 与 clang/clang++ 以及 ld.lld 工具一起工作。Kotlin/Native 和 C/C++ 项目构建最后一步相同,把机器码.o链接成二进制文件.so\.framework。
在 AS 中构建 Kotlin/Native 项目,看不到构建过程,通过了解底层kotlinc-native、clang/clang++、ld.lld的工作方式,能解决Kotlin/Native 项目支持Android,iOS,Harmony所遇到的构建问题。