前言
为了让 Kotlin Multiplatform 的逻辑共享更丝滑的支持 Android, iOS, Harmony 平台,如访问数据库,网络,多线程,多媒体逻辑等,采用 Kotlin/Native 是一种不错的方式。
没有 JVM 环境,也能让 Kotlin 代码在 macOS, Linux, Windows, Android,iOS, Harmony 等平台上运行,这就是 Kotlin Multiplatform 中 Kotlin/Native 所做的事情。
Kotlin/Native 可以将 Kotlin 代码生成可运行的本机二进制文件:*.so,*.framework。*.so 可以给 Android 和 Harmony使用,*.framework 可以给 iOS使用。
Kotlin/Native 能生成可运行的本机二进制文件,主要是依靠:一个基于LLVM的 Kotlin 编译器后端和一个 Kotlin 标准库的本机实现。
LLVM
LLVM 项目是模块化和可重用编译器和工具链技术的集合。尽管 LLVM 的名称如此,但它与传统虚拟机几乎没有关系。名称“LLVM”本身并不是缩写;它是项目的全名。
LLVM 最初是伊利诺伊大学的一个研究项目,其目标是提供一种基于 SSA 的现代编译策略,能够支持任意编程语言的静态和动态编译。从那时起,LLVM 已发展成为一个由许多子项目组成的综合项目,其中许多子项目被各种 商业和开源项目用于生产,并被广泛用于学术研究。LLVM 项目中的代码根据 “Apache 2.0 许可证(LLVM 例外)”获得许可。
LLVM 主要用于程序的编译、优化和代码生成。
LLVM 的工作流程:
graph TD
A[源代码] --> |前端| B[LLVM IR]
B --> |优化| C[优化后的 LLVM IR]
C --> |后端| D[目标机器代码]
D --> |链接器| E[可执行文件]
subgraph "LLVM 架构"
B --> |分析 & 变换| C
C --> |指令选择 & 寄存器分配| D
end
上面工作流程中的重要组成部分包含:
-
源代码: 开发者编写的各种编程语言的源代码。
-
前端: 不同的编程语言有不同的前端,负责将源代码转换为 LLVM IR。
-
LLVM IR: 一种平台无关的中间表示形式,方便后续的优化和代码生成。
-
优化器: 对 LLVM IR 进行各种优化,提高代码执行效率。
-
后端: 将优化后的 LLVM IR 转换为目标平台的机器码。
-
机器码: 可以在目标平台上直接运行的二进制代码。
-
链接器: 将生成的目标代码与库文件链接,形成最终的可执行文件或库。
在 Kotlin/Native 中,通过 LLVM 实现 Kotlin 代码到本机二进制的转换。
C/C++ 项目构建工具和构建流程
Kotlin/Native 主要是与C/C++交互,产物也是对接 Native 项目,所以先了解一下 C/C++项目构建工具和构建流程。
C/C++ 项目构建工具有:Make, CMake, Bazel, Ninja, Meson, Autotools。在 Android Native 和 Harmony Native 项目中,都使用 CMake 构建工具。
CMake 适合跨平台中大型项目,通过 CMakeLists.txt 配置构建项目:
# 指定最低 CMake 版本
cmake_minimum_required(VERSION 3.10)
# 定义项目名称
project(MyProject)
# 设置 C++ 标准版本
set(CMAKE_CXX_STANDARD 17)
# 强制使用指定 C++ 版本
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加子目录
add_subdirectory(external/SomeLibrary)
# 头文件和源文件
include_directories(${PROJECT_SOURCE_DIR}/include)
aux_source_directory(src SRC_LIST)
# 目标
add_executable(my_program ${SRC_LIST})
target_link_libraries(my_program SomeLibrary)
# 编译选项
target_compile_options(my_program PRIVATE -Wall -Wextra -O2)
# 产物输出
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 平台相关
if(WIN32)
message(STATUS "Building for Windows")
add_definitions(-DPLATFORM_WINDOWS)
elseif(APPLE)
message(STATUS "Building for macOS")
elseif(UNIX)
message(STATUS "Building for Linux")
endif()
C/C++ 项目构建流程主要包含:预处理、编译、汇编、链接。
| 阶段 | 主要作用 | 工具 | 产物 |
|---|---|---|---|
| 预处理(Preprocessing) | 处理宏、头文件包含、条件编译指令 | 预处理器(如 cpp) | .i(预处理后的代码) |
| 编译(Compilation) | 将预处理后的代码转换为汇编代码 | 编译器(如 gcc、clang、MSVC) | .s(汇编代码) |
| 汇编(Assembly) | 将汇编代码转换为机器码(目标文件) | 汇编器(如 as) | .o 或 .obj(目标文件) |
| 链接(Linking) | 合并目标文件和库,生成可执行文件或共享库 | 链接器(如 ld、lld、MSVC link.exe) | 可执行文件(如 app、app.exe)或动态库(如 .so、.dll) |
Kotlin/Native 项目构建工具和构建流程
Kotlin/Native 项目构建工具:Gradle。通过 build.gradle.kts 配置构建项目:
//构建二进制文件
kotlin {
//生成ios平台下的 .framework
val xcf = XCFramework()
val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64())
iosTargets.forEach {
it.binaries.framework {
baseName = "shared"
xcf.add(this)
}
}
//生成android平台下的 .so
val androidTargets = listOf(androidNativeArm64(), androidNativeX64())
androidTargets.forEach {
it.binaries {
executable {
}
sharedLib {
baseName = "shared"
}
}
}
//生成linux平台下的 .so
val linuxTargets = listOf(linuxArm64(), linuxX64())
linuxTargets.forEach {
it.binaries {
executable {
}
sharedLib {
baseName = "shared"
}
}
}
}
Kotlin/Native 项目构建流程主要包含:前端(frontend)编译,后端(backend)编译,链接(linking)
- 前端(frontend)编译 :解析 Kotlin 代码,生成中间表示(IR)。
- 后端(backend)编译 :优化 IR,并用 LLVM 生成目标平台的机器码。
- 链接(linking) :与 C/C++ 代码、标准库和其他依赖项链接,生成最终的可执行文件或动态库。
KMP 项目使用 K2 编译器:
在 Kotlin/Native 项目中,经过前端编译后得到 Kotlin IR,Native backend 首先将 Kotlin IR 转换为 LLVM IR,LLVM IR 再通过 LLVM 后端生成目标平台机器码,机器码最后经过链接生成最终可执行文件。
graph TD
A[Kotlin 源代码 *.kt] -->|解析| B[Kotlin 编译器 K2]
B -->|生成 IR| C[Kotlin IR]
C -->|优化 & 转换| D[LLVM IR]
D -->|优化 & 代码生成| E[目标架构的汇编代码]
E -->|链接 C 库 & 运行时| F[目标产物 .kexe / .so / .dylib / .framework]
subgraph Kotlin/Native 构建流程
A --> B --> C --> D --> E --> F
end
总结
在 Kotlin/Native 项目中使用 Gradle 工具构建,在 C/C++ 项目中使用 Cmake 工具构建。在 Kotlin/Native 项目中的主要构建流程是前端编译、后段编译、链接,在 C/C++项目中的主要构建流程是预处理、编译、汇编、链接。
了解 C/C++ 项目与 Kotlin/Native 项目在构建工具和构建流程上的差异,有助于解决 Kotlin/Native 产物在Andriod, iOS, Harmony, Linux, Macos, Windows等上遇到的跨平台问题,比如找不到符号,找不到依赖库,交叉编译失败等。
下一篇Kotlin/Native 预构建工具链。