Spec-Zone.ru › Kotlin 2

Kotlin/Native в виде динамической библиотеки — руководство

Вы можете создавать динамические библиотеки, чтобы использовать код на Kotlin в существующих программах. Это позволяет совместно использовать код на разных платформах и в разных языках, включая JVM, Python, Android и другие.

Для iOS и других платформ Apple мы рекомендуем создавать фреймворк. См. руководство Kotlin/Native в виде фреймворка Apple.

Вы можете использовать код Kotlin/Native в существующих нативных приложениях или библиотеках. Для этого нужно скомпилировать код на Kotlin в динамическую библиотеку в формате .so, .dylib или .dll.

В этом руководстве вы узнаете, как:

  • скомпилировать код на Kotlin в динамическую библиотеку

  • изучить сгенерированные заголовочные файлы C

  • использовать динамическую библиотеку Kotlin из C

  • скомпилировать и запустить проект

Для создания библиотеки Kotlin можно использовать командную строку напрямую или с помощью файла скрипта (например, файла .sh или .bat). Однако этот подход плохо подходит для крупных проектов с сотнями файлов и библиотек. Система сборки упрощает этот процесс: она скачивает и кэширует двоичные файлы компилятора Kotlin/Native и библиотеки с транзитивными зависимостями, а также запускает компилятор и тесты. Kotlin/Native поддерживает систему сборки Gradle с помощью плагина Kotlin Multiplatform.

Рассмотрим расширенные варианты использования Kotlin/Native для взаимодействия с C и сборки проектов Kotlin Multiplatform с помощью Gradle.

Если вы используете Mac и хотите создавать и запускать приложения для macOS или других платформ Apple, сначала также необходимо установить инструменты командной строки Xcode, запустить их и принять условия лицензии.

Создание библиотеки Kotlin

Компилятор Kotlin/Native может создать динамическую библиотеку из кода на Kotlin. Динамическая библиотека часто сопровождается заголовочным файлом .h, который используется для вызова скомпилированного кода из C.

Создадим библиотеку Kotlin и используем её из программы на C.

Подробные сведения о первых шагах, создании нового проекта Kotlin/Native и его открытии в IntelliJ IDEA см. в руководстве Начало работы с Kotlin/Native.

  1. Перейдите в каталог src/nativeMain/kotlin и создайте файл lib.kt со следующим содержимым библиотеки:

    package example
    
    object Object { 
        val field = "A"
    }
    
    class Clazz {
        fun memberFunction(p: Int): ULong = 42UL
    }
    
    fun forIntegers(b: Byte, s: Short, i: UInt, l: Long) { }
    fun forFloats(f: Float, d: Double) { }
    
    fun strings(str: String) : String? {
        return "That is '$str' from C"
    }
    
    val globalString = "A global String"
    
  2. Обновите файл сборки Gradle build.gradle(.kts) следующим образом:

    import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
    
    plugins {
        kotlin("multiplatform") version "2.4.20"
    }
    
    repositories {
        mavenCentral()
    }
    
    kotlin {
        macosArm64()    // macOS on Apple Silicon
        // linuxArm64() // Linux on ARM64 platforms
        // linuxX64()   // Linux on x86_64 platforms
        // mingwX64()   // on Windows
    
        targets.withType<KotlinNativeTarget>().configureEach {
            binaries {
                sharedLib {
                    baseName = "native"       // macOS
                    // baseName = "native"    // Linux
                    // baseName = "libnative" // Windows
                }
            }
        }
    }
    
    tasks.wrapper {
        gradleVersion = "9.7.0"
        distributionType = Wrapper.DistributionType.ALL
    }
    
    import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
    
    plugins {
        id 'org.jetbrains.kotlin.multiplatform' version '2.4.20'
    }
    
    repositories {
        mavenCentral()
    }
    
    kotlin {
        macosArm64()    // Apple Silicon macOS
        // linuxArm64() // Linux on ARM64 platforms
        // linuxX64()   // Linux on x86_64 platforms
        // mingwX64()   // Windows
    
        targets.withType(KotlinNativeTarget).configureEach {
            binaries {
                sharedLib {
                    baseName = "native"       // macOS
                    // baseName = "native"    // Linux
                    // baseName = "libnative" // Windows
                }
            }
        }
    }
    
    wrapper {
        gradleVersion = "9.7.0"
        distributionType = "ALL"
    }
    
    • Блок binaries {} настраивает проект для создания динамической или разделяемой библиотеки.

    • libnative используется в качестве имени библиотеки и префикса имени сгенерированного заголовочного файла. Кроме того, он служит префиксом для всех объявлений в заголовочном файле.

  3. Чтобы собрать библиотеку, запустите задачу Gradle linkDebugShared<YourTargetName> в IDE или выполните в терминале команду, как в этом примере:

    ./gradlew linkDebugSharedMacosArm64
    

В результате сборки библиотека будет создана в каталоге build/bin/<yourTargetName>/debugShared вместе со следующими файлами:

  • macOS: libnative_api.h и libnative.dylib

  • Linux: libnative_api.h и libnative.so

  • Windows: libnative_api.h, libnative.def и libnative.dll

Также можно использовать задачу Gradle linkNative, чтобы создать оба варианта библиотеки: debug и release.

Компилятор Kotlin/Native использует одинаковые правила для создания файла .h на всех платформах. Рассмотрим API библиотеки Kotlin для C.

Сгенерированный заголовочный файл

Рассмотрим, как объявления Kotlin преобразуются в функции C.

Откройте заголовочный файл libnative_api.h в каталоге build/bin/<yourTargetName>/debugShared. В самом начале находится стандартная часть заголовка и футера C/C++:

#ifndef KONAN_LIBNATIVE_H
#define KONAN_LIBNATIVE_H
#ifdef __cplusplus
extern "C" {
#endif

/// The rest of the generated code

#ifdef __cplusplus
}  /* extern "C" */
#endif
#endif  /* KONAN_LIBNATIVE_H */

Далее в файле libnative_api.h находится блок с общими определениями типов:

#ifdef __cplusplus
typedef bool            libnative_KBoolean;
#else
typedef _Bool           libnative_KBoolean;
#endif
typedef unsigned short     libnative_KChar;
typedef signed char        libnative_KByte;
typedef short              libnative_KShort;
typedef int                libnative_KInt;
typedef long long          libnative_KLong;
typedef unsigned char      libnative_KUByte;
typedef unsigned short     libnative_KUShort;
typedef unsigned int       libnative_KUInt;
typedef unsigned long long libnative_KULong;
typedef float              libnative_KFloat;
typedef double             libnative_KDouble;
typedef float __attribute__ ((__vector_size__ (16))) libnative_KVector128;
typedef void*              libnative_KNativePtr;

Kotlin использует префикс libnative_ для всех объявлений в созданном файле libnative_api.h. Ниже приведён полный список соответствий типов:

Определение Kotlin

Тип C

libnative_KBoolean

bool или _Bool

libnative_KChar

unsigned short

libnative_KByte

signed char

libnative_KShort

short

libnative_KInt

int

libnative_KLong

long long

libnative_KUByte

unsigned char

libnative_KUShort

unsigned short

libnative_KUInt

unsigned int

libnative_KULong

unsigned long long

libnative_KFloat

float

libnative_KDouble

double

libnative_KVector128

float __attribute__ ((__vector_size__ (16))

libnative_KNativePtr

void*

Раздел определений файла libnative_api.h показывает, как примитивные типы Kotlin соответствуют примитивным типам C. Компилятор Kotlin/Native автоматически генерирует эти записи для каждой библиотеки. Обратное преобразование описано в руководстве Сопоставление примитивных типов данных из C.

После автоматически сгенерированных определений типов вы найдёте отдельные определения типов, используемых в вашей библиотеке:

struct libnative_KType;
typedef struct libnative_KType libnative_KType;

/// Automatically generated type definitions

typedef struct {
  libnative_KNativePtr pinned;
} libnative_kref_example_Object;
typedef struct {
  libnative_KNativePtr pinned;
} libnative_kref_example_Clazz;

В C структура объявляется с помощью синтаксиса typedef struct { ... } TYPE_NAME.

Дополнительные пояснения к этому шаблону см. в этой ветке на StackOverflow.

Как видно из этих определений, типы Kotlin сопоставляются по одному и тому же шаблону: Object сопоставляется с libnative_kref_example_Object, а Clazz — с libnative_kref_example_Clazz. Все структуры содержат только поле pinned с указателем. Тип поля libnative_KNativePtr ранее в файле определён как void*.

Поскольку C не поддерживает пространства имён, компилятор Kotlin/Native генерирует длинные имена, чтобы избежать возможных конфликтов с другими символами в существующем нативном проекте.

Служебные функции среды выполнения

Структура libnative_ExportedSymbols определяет все функции, предоставляемые Kotlin/Native и вашей библиотекой. Для имитации пакетов в ней активно используются вложенные анонимные структуры. Префикс libnative_ образован от имени библиотеки.

libnative_ExportedSymbols содержит в заголовочном файле несколько вспомогательных функций:

typedef struct {
  /* Service functions. */
  void (*DisposeStablePointer)(libnative_KNativePtr ptr);
  void (*DisposeString)(const char* string);

Эти функции работают с объектами Kotlin/Native. Функция DisposeStablePointer вызывается для освобождения ссылки на объект Kotlin, а функция DisposeString — для освобождения строки Kotlin, которая в C имеет тип char*.

Следующая часть файла libnative_api.h содержит объявления структур для функций среды выполнения:

libnative_KBoolean (*IsInstance)(libnative_KNativePtr ref, const libnative_KType* type);
libnative_KBoolean (*IsInstance)(libnative_KNativePtr ref, const libnative_KType* type);
libnative_kref_kotlin_Byte (*createNullableByte)(libnative_KByte);
libnative_KByte (*getNonNullValueOfByte)(libnative_kref_kotlin_Byte);
libnative_kref_kotlin_Short (*createNullableShort)(libnative_KShort);
libnative_KShort (*getNonNullValueOfShort)(libnative_kref_kotlin_Short);
libnative_kref_kotlin_Int (*createNullableInt)(libnative_KInt);
libnative_KInt (*getNonNullValueOfInt)(libnative_kref_kotlin_Int);
libnative_kref_kotlin_Long (*createNullableLong)(libnative_KLong);
libnative_KLong (*getNonNullValueOfLong)(libnative_kref_kotlin_Long);
libnative_kref_kotlin_Float (*createNullableFloat)(libnative_KFloat);
libnative_KFloat (*getNonNullValueOfFloat)(libnative_kref_kotlin_Float);
libnative_kref_kotlin_Double (*createNullableDouble)(libnative_KDouble);
libnative_KDouble (*getNonNullValueOfDouble)(libnative_kref_kotlin_Double);
libnative_kref_kotlin_Char (*createNullableChar)(libnative_KChar);
libnative_KChar (*getNonNullValueOfChar)(libnative_kref_kotlin_Char);
libnative_kref_kotlin_Boolean (*createNullableBoolean)(libnative_KBoolean);
libnative_KBoolean (*getNonNullValueOfBoolean)(libnative_kref_kotlin_Boolean);
libnative_kref_kotlin_Unit (*createNullableUnit)(void);
libnative_kref_kotlin_UByte (*createNullableUByte)(libnative_KUByte);
libnative_KUByte (*getNonNullValueOfUByte)(libnative_kref_kotlin_UByte);
libnative_kref_kotlin_UShort (*createNullableUShort)(libnative_KUShort);
libnative_KUShort (*getNonNullValueOfUShort)(libnative_kref_kotlin_UShort);
libnative_kref_kotlin_UInt (*createNullableUInt)(libnative_KUInt);
libnative_KUInt (*getNonNullValueOfUInt)(libnative_kref_kotlin_UInt);
libnative_kref_kotlin_ULong (*createNullableULong)(libnative_KULong);
libnative_KULong (*getNonNullValueOfULong)(libnative_kref_kotlin_ULong);

Функцию IsInstance можно использовать, чтобы проверить, является ли объект Kotlin (на который ссылается указатель .pinned) экземпляром типа. Фактический набор сгенерированных операций зависит от того, как используются типы.

В Kotlin/Native есть собственный сборщик мусора, но он не управляет объектами Kotlin, к которым осуществляется доступ из C. При этом Kotlin/Native поддерживает взаимодействие со Swift/Objective-C, а сборщик мусора интегрирован с ARC в Swift/Objective-C.

Функции вашей библиотеки

Рассмотрим отдельные объявления структур, используемые в вашей библиотеке. Поле libnative_kref_example имитирует структуру пакетов в вашем коде Kotlin с префиксом libnative_kref.:

typedef struct {
  /* User functions. */
  struct {
    struct {
      struct {
        struct {
          libnative_KType* (*_type)(void);
          libnative_kref_example_Object (*_instance)();
          const char* (*get_field)(libnative_kref_example_Object thiz);
        } Object;
        struct {
          libnative_KType* (*_type)(void);
          libnative_kref_example_Clazz (*Clazz)();
          libnative_KULong (*memberFunction)(libnative_kref_example_Clazz thiz, libnative_KInt p);
        } Clazz;
        const char* (*get_globalString)();
        void (*forFloats)(libnative_KFloat f, libnative_KDouble d);
        void (*forIntegers)(libnative_KByte b, libnative_KShort s, libnative_KUInt i, libnative_KLong l);
        const char* (*strings)(const char* str);
      } example;
    } root;
  } kotlin;
} libnative_ExportedSymbols;

В коде используются объявления анонимных структур. Здесь struct { ... } foo объявляет поле во внешней структуре анонимного типа, у которого нет имени.

Поскольку C также не поддерживает объекты, для имитации семантики объектов используются указатели на функции. Указатель на функцию объявляется как RETURN_TYPE (* FIELD_NAME)(PARAMETERS).

Поле libnative_kref_example_Clazz представляет Clazz из Kotlin. Доступ к libnative_KULong осуществляется через поле memberFunction. Единственное отличие состоит в том, что memberFunction принимает ссылку thiz в качестве первого параметра. Поскольку C не поддерживает объекты, указатель thiz передаётся явно.

В поле Clazz (то есть libnative_kref_example_Clazz_Clazz) находится конструктор, который служит функцией-конструктором для создания экземпляра Clazz.

Доступ к объекту Kotlin object Object осуществляется как к libnative_kref_example_Object. Функция _instance возвращает единственный экземпляр объекта.

Свойства преобразуются в функции. Префиксы get_ и set_ используются в именах функций-геттеров и функций-сеттеров соответственно. Например, доступное только для чтения свойство globalString из Kotlin преобразуется в функцию get_globalString в C.

Глобальные функции forFloats, forIntegers и strings преобразуются в указатели на функции в анонимной структуре libnative_kref_example.

Точка входа

Теперь, когда вы знаете, как создаётся API, можно перейти к начальной точке — инициализации структуры libnative_ExportedSymbols. Рассмотрим заключительную часть файла libnative_api.h:

extern libnative_ExportedSymbols* libnative_symbols(void);

Функция libnative_symbols позволяет открыть доступ из нативного кода к библиотеке Kotlin/Native. Это точка входа для доступа к библиотеке. Имя библиотеки используется как префикс имени функции.

Может потребоваться хранить возвращённый указатель libnative_ExportedSymbols* отдельно для каждого потока.

Использование сгенерированных заголовков из C

Использовать сгенерированные заголовки из C просто. Создайте в каталоге библиотеки файл main.c со следующим кодом:

#include "libnative_api.h"
#include "stdio.h"

int main(int argc, char** argv) {
  // Obtain reference for calling Kotlin/Native functions
  libnative_ExportedSymbols* lib = libnative_symbols();

  lib->kotlin.root.example.forIntegers(1, 2, 3, 4);
  lib->kotlin.root.example.forFloats(1.0f, 2.0);

  // Use C and Kotlin/Native strings
  const char* str = "Hello from Native!";
  const char* response = lib->kotlin.root.example.strings(str);
  printf("in: %s\nout:%s\n", str, response);
  lib->DisposeString(response);

  // Create Kotlin object instance
  libnative_kref_example_Clazz newInstance = lib->kotlin.root.example.Clazz.Clazz();
  long x = lib->kotlin.root.example.Clazz.memberFunction(newInstance, 42);
  lib->DisposeStablePointer(newInstance.pinned);

  printf("DemoClazz returned %ld\n", x);

  return 0;
}

Сборка и запуск проекта

macOS

Чтобы скомпилировать код C и связать его с динамической библиотекой, перейдите в каталог библиотеки и выполните следующую команду:

clang main.c libnative.dylib

Компилятор создаст исполняемый файл с именем a.out. Запустите его, чтобы выполнить код Kotlin из библиотеки C.

Linux

Чтобы скомпилировать код C и связать его с динамической библиотекой, перейдите в каталог библиотеки и выполните следующую команду:

gcc main.c libnative.so

Компилятор создаст исполняемый файл с именем a.out. Запустите его, чтобы выполнить код Kotlin из библиотеки C. В Linux необходимо добавить . в LD_LIBRARY_PATH, чтобы приложение загружало библиотеку libnative.so из текущей папки.

Windows

Сначала необходимо установить компилятор Microsoft Visual C++, поддерживающий целевую платформу x64_64.

Проще всего установить Microsoft Visual Studio на компьютере с Windows. Во время установки выберите необходимые компоненты для работы с C++, например Разработка классических приложений на C++.

В Windows динамические библиотеки можно подключить, сгенерировав статическую библиотеку-обёртку, или вручную с помощью функции LoadLibrary либо аналогичных функций Win32API.

Воспользуемся первым вариантом и создадим статическую библиотеку-обёртку для libnative.dll:

  1. Вызовите lib.exe из набора инструментов, чтобы создать статическую библиотеку-обёртку libnative.lib, которая автоматизирует использование DLL в коде:

    lib /def:libnative.def /out:libnative.lib
    
  2. Скомпилируйте main.c в исполняемый файл. Добавьте сгенерированный файл libnative.lib в команду сборки и запустите её:

    cl.exe main.c libnative.lib
    

    Команда создаст файл main.exe, который можно запустить.

Что дальше

  • Подробнее о взаимодействии со Swift/Objective-C

  • Ознакомьтесь с руководством «Kotlin/Native в виде фреймворка Apple»

31 марта 2026 г.
Сопоставление строк из C — руководствоСоздание приложения с использованием взаимодействия с C и libcurl — руководство

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/native-dynamic-libraries.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API