6.3.3 Kotlin/Native 库

原文链接: https://kotlinlang.org/docs/native-libraries.html

6.3.3 Kotlin/Native 库

库的编译

你可以使用项目的构建文件或 Kotlin/Native 编译器为你的库生成 *.klib 产物。

使用 Gradle 构建文件

你可以在 Gradle 构建文件中指定一个 Kotlin/Native 目标来编译 *.klib 库产物:

  1. 在你的 build.gradle(.kts) 文件中声明至少一个 Kotlin/Native 目标。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   // build.gradle.kts
   plugins {
       kotlin("multiplatform") version "2.4.20"
   }

   kotlin {
       macosArm64() // 在 macOS 上
       // linuxArm64() // 在 Linux 上
       // mingwX64()   // 在 Windows 上
   }
  1. 运行 <target>Klib 任务。例如:
1
   ./gradlew macosArm64Klib

Gradle 会自动为该目标编译源文件,并在项目的 build/libs 目录中生成 .klib 产物。

使用 Kotlin/Native 编译器

要使用 Kotlin/Native 编译器生成库:

  1. 下载并安装 Kotlin/Native 编译器。
  2. 要把 Kotlin/Native 源文件编译为库,请使用 -produce library 或 -p library 选项:
1
   kotlinc-native foo.kt -p library -o bar

该命令会把 foo.kt 文件的内容编译为一个名为 bar 的库,生成 bar.klib 产物。

  1. 要把另一个文件链接到某个库,请使用 -library <name> 或 -l <name> 选项。例如:
1
   kotlinc-native qux.kt -l bar

该命令会编译 qux.kt 源文件的内容和 bar.klib 库,并生成最终的可执行二进制文件 program.kexe。

klib 工具

klib 库管理工具允许你用以下语法检查库:

1
klib <command> <library path> [<option>]

目前可用的命令如下:

| 命令 | 说明 |

| info | 库的常规信息。 | | dump-abi | 转储库的 ABI 快照。快照中的每一行对应一个声明。如果某个声明发生了 ABI 不兼容的变更,会在快照的相应行中体现出来。 | | dump-ir | 把库声明的中间表示(IR)转储到输出。请仅在调试时使用。 | | dump-ir-signatures | 转储所有非私有库声明以及本库所消费的所有非私有声明的 IR 签名(作为两个独立的列表)。该命令完全依赖 IR 中的数据。 | | dump-ir-inlinable-functions | 把库中可内联函数的 IR 转储到输出。请仅在调试时使用。 | | dump-metadata | 把所有库声明的元数据转储到输出。请仅在调试时使用。 | | dump-metadata-signatures | 基于库元数据转储所有非私有库声明的 IR 签名。大多数情况下,其输出与 dump-ir-signatures 命令相同,后者基于 IR 渲染签名。不过,如果在编译期间使用了会转换 IR 的编译器插件(例如 Compose),被改写的声明可能会有不同的签名。 |

上述所有转储命令都接受一个额外的 -signature-version {N} 参数,它指示 klib 工具在转储签名时渲染哪个 IR 签名版本。如果未提供,则使用该库支持的最新版本。例如:

1
klib dump-metadata-signatures mylib.klib -signature-version 1

此外,dump-metadata 命令接受 -print-signatures {true|false} 参数,它指示 klib 工具在输出中为每个声明打印 IR 签名。

创建和使用一个库

  1. 把源代码放入 kotlinizer.kt 来创建一个库:
1
2
3
4
   package kotlinizer

   val String.kotlinized
       get() = "Kotlin $this"
  1. 把该库编译为 .klib:
1
   kotlinc-native kotlinizer.kt -p library -o kotlinizer
  1. 在当前目录中查看创建出来的库:
1
   ls kotlinizer.klib
  1. 查看该库的常规信息:
1
   klib info kotlinizer.klib
  1. 在 use.kt 文件中创建一个简短的程序:
1
2
3
4
5
   import kotlinizer.*

   fun main(args: Array<String>) {
       println("Hello, ${"world".kotlinized}!")
   }
  1. 编译该程序,并把 use.kt 源文件链接到你的库:
1
   kotlinc-native use.kt -l kotlinizer -o kohello
  1. 运行该程序:
1
   ./kohello.kexe

你应该会在输出中看到 Hello, Kotlin world!。

库的搜索顺序

注意: 库的搜索机制很快会发生变化。请留意本节内容的更新,并避免依赖已弃用的标志。

当给定 -library foo 选项时,编译器会按以下顺序搜索 foo 库:

  1. 当前编译目录或绝对路径。
  2. 安装在默认仓库中的库。

注意: 默认仓库是 ~/.konan。你可以通过设置 konan.data.dir Gradle 属性来更改它。或者,你可以使用 -Xkonan-data-dir 编译器选项,通过 cinterop 和 konanc 工具为该目录配置自定义路径。

  1. 安装在 $installation/klib 目录中的库。

库的格式

Kotlin/Native 库是包含预定义目录结构的 zip 文件,其布局如下:

把 foo.klib 解包为 foo/ 后得到:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
- foo/
  - $component_name/
    - ir/
      - Serialized Kotlin IR.
    - targets/
      - $platform/
        - kotlin/
          - Kotlin compiled to LLVM bitcode.
        - native/
          - Bitcode files of additional native objects.
      - $another_platform/
        - There can be several platform specific kotlin and native pairs.
    - linkdata/
      - A set of ProtoBuf files with serialized linkage metadata.
    - resources/
      - General resources such as images. (Not used yet).
    - manifest - A file in the java property format describing the library.

你可以在 Kotlin/Native 编译器安装目录的 klib/common/stdlib 目录中找到一个示例布局。

在 klib 中使用相对路径

源文件的序列化 IR 表示是 klib 库的一部分。它包含用于生成正确调试信息的文件路径。默认情况下,存储的路径是绝对路径。

借助 -Xklib-relative-path-base 编译器选项,你可以改变这种格式,只在产物中使用相对路径。要使其生效,请传入一个或多个源文件的基础路径作为参数:

Kotlin

1
2
3
4
5
6
7
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
// ...

tasks.named<KotlinCompilationTask<*>>("compileKotlin").configure {
    // $base 是源文件的基础路径
    compilerOptions.freeCompilerArgs.add("-Xklib-relative-path-base=$base")
}

Groovy

1
2
3
4
5
6
7
8
9
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
// ...

tasks.named('compileKotlin', KotlinCompilationTask) {
    compilerOptions {
        // $base 是源文件的基础路径
        freeCompilerArgs.add("-Xklib-relative-path-base=$base")
    }
}

接下来做什么?

了解如何使用 cinterop 工具生成 *.klib 产物