>

Статьи>

Kotlin Multiplatform для ОС Аврора

Kotlin Multiplatform для ОС Аврора

Logo

Kotlin Multiplatform в ОС Аврора

В этой статье рассказывается о текущей поддержке Kotlin Multiplatform в операционной системе Аврора. Показывается, как создать приложение ОС Аврора с модулем Kotlin Multiplatform, используя новые для ОС Аврора инструменты. Будет написано демонстрационное приложение, отображающее взаимодействие KMP и ОС Аврора.

Для самостоятельного изучения доступен большой пример приложения с использованием базы данных, запросов в сеть — StudentBox. StudentBox позволяет записывать и редактировать список уроков на день для студента/ученика для Android и ОС Аврора:

preview_StudentBox

Инструменты разработчика

Основные инструменты для разработки приложений под ОС Аврора — Qt/C++ QML. Qt/C++ дают высокую производительность, стабильность, прямой доступ к API ОС Аврора.

Но в разработке мобильных приложений не всегда задача стоит реализовать максимально производительное приложение. Важны и другие качества инструментов: скорость разработки, простота, количество доступных вакансий на рынке. Компания ОМП последовательно расширяет и улучшает инструменты разработки приложений.

В релиз вышел фреймворк Flutter. В фреймворк была добавлена поддержка ОС Аврора. Идёт работа над его улучшением, доработка существующих плагинов и написание новых. Сейчас доступно уже более 50 плагинов, и портирование приложения Flutter под ОС Аврора не составляет большого труда. А язык Dart снижает порог входа в разработку, относительно С++.

Гибридные приложения на базе WebView/CEF позволяют писать приложения на JavaScript-фреймворках, таких как React, Angular, Vue и т.д.

Ведутся работы по поддержке PWA (прогрессивные веб-приложения). Планируется добавить поддержку в ОС Аврора 5.2, то есть, в недалёком будущем.

Подробнее ознакомиться со всеми инструментами можно на сайте developer.auroraos.ru.

preview_frameworks

Kotlin Multiplatform

Для Kotlin Multiplatform работы по поддержке ведутся давно, разработчики пробовали разные подходы, и в этой статье представляют инструменты поддержки KMP в ОС Аврора в Open-Source!

Kotlin Multiplatform (KMP) — технология, позволяющая переиспользовать код для разных платформ, написанный на Kotlin. Она позволяет вынести общую бизнес-логику в библиотеку, адаптированную для платформ Android, iOS, Web, Linux. Благодаря наличию Kotlin/Native делиться кодом можно везде, где есть возможность использовать С-библиотеки.

В ОС Аврора, как и в iOS, нет JVM. Kotlin поддерживает нативную компиляцию (Kotlin/Native), которая позволяет собрать статическую или динамическую библиотеку из проекта Kotlin Multiplatform. Низкоуровневые С-библиотеки, генерируемые KMP, нельзя назвать удобными для использования. В ОС Аврора эта проблема решена с помощью плагина QtBinding, который создаёт прослойку между низкоуровневым C и Qt. Поэтому писать приложения для ОС Аврора стало не сложнее, чем для iOS.

В Qt легко интегрируются С-библиотеки Kotlin/Native. Тем не менее такой подход сопряжён с некоторыми трудностями во время разработки:

  • генерируемый заголовочный файл содержит огромное количество строк;
  • экспортируемый интерфейс не поддерживает классы из сторонних библиотек;
  • ресурсы необходимо вручную освобождать с помощью вспомогательных экспортируемых функций;
  • в экспортируемом интерфейсе отсутствует объектно-ориентированный интерфейс классов;
  • не экспортируется интерфейс, поддерживающий работу с Coroutines.

QtBindings

Для упрощения работы с Kotlin/Native был разработан Gradle-плагин QtBindings, позволяющий адаптировать С-библиотеку для удобного использования в Qt. Плагин автоматически избавляет разработчика от лишней работы:

  • обновляет API экспортируемого Kotlin кода;
  • упрощает работу со стандартными коллекциями;
  • создаёт Qt-классы для экспортируемых Kotlin-классов;
  • автоматически освобождает ресурсы.

Задача QtBindings — автоматизировать и упростить работу с Kotlin/Native. Он позволяет:

  • генерировать обёртки для классов и функций;
  • использовать Coroutines через QFuture;
  • работать с Kotlin-списками (List, MutableList) через QList.

Разработчику достаточно указать аннотацию @QtExport для классов или функций. QtBindings создаст все необходимы файлы и классы, которые можно будет подключить к приложению для ОС Аврора.

Планы по исследованиям KMP и CMP

Это начало развития инструментов, ещё предстоит их упростить, оптимизировать. Но уже сейчас можно переиспользовать код Kotlin в ОС Аврора, включаться в разработку инструментов — разработчики всегда будут рады предложениям по доработкам, замечаниям и мерж-реквестам.

Сейчас говорится о технологии KMP — использование общей бизнес-логики в приложениях ОС Аврора. Compose Multiplatform будет позже, он есть в планах:

preview

Проект с QtBindings

Будет создан проект, «Hello, world», с использованием Kotlin Multiplatform для ОС Аврора. Будет показано как создать проект с нуля, использовать методы KMP в ОС Аврора, синхронные и асинхронные (suspend). Для этого потребуются инструменты:

  • Аврора SDK — набор инструментов для сборки, разработки, отладки и тестирования прикладного ПО для ОС Аврора.
  • IntelliJ IDEA Community Edition — IDE для профессиональной разработки на Java и Kotlin.

Kotlin Multiplatform используется стандартный, доступный в IDEA. Более детально ознакомиться с Kotlin Multiplatform можно на сайте документации "Get started with Kotlin Multiplatform". Для создания приложения используются цели Kotlin/Native linuxX64 и linuxArm64.

Демонстрационное приложение будет максимально простое для данной темы, его главная задача — показать взаимодействие KMP и ОС Аврора. Выполнятся синхронные и асинхронные методы и выведутся данные в терминал:

preview

Проект Kotlin Multiplatform

Для создания библиотеки Kotlin Multiplatform можно воспользоваться сайтом Kotlin Multiplatform Wizard. Необходимый шаблон библиотеки называется Multiplatform Library:

preview

По умолчанию в шаблоне Multiplatform Library добавлены все доступные цели сборки. Для реализации библиотеки под проект ОС Аврора необходимо оставить только цель linuxX64Main. Всё лишнее можно удалить.

После правок структура проекта должна выглядеть так:

.
├── build.gradle.kts
├── .gitignore
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library
│   ├── build.gradle.kts
│   └── src
│       ├── commonMain
│       │   └── kotlin
│       │       └── CustomFibi.kt
│       └── linuxX64Main
│           └── kotlin
│               └── fibiprops.linuxX64.kt
├── README.md
└── settings.gradle.kts

9 directories, 13 files

Название библиотеки будет часто фигурировать в проекте. Для удобства его можно поместить в library/gradle.properties:

#Library
libName=demo_kmp

На данный момент в шаблоне есть цель linuxX64, для поддержки архитектуры aarch64 необходимо добавить цель linuxArm64. В названиях целей можно обозначить задачу создания библиотеки Kotlin/Native для ОС Аврора.

plugins {
    alias(libs.plugins.kotlinMultiplatform)
}

group = "ru.auroraos.demo"
version = "0.0.1"

kotlin {
    linuxX64("auroraX64") {
        binaries {
            staticLib {
                baseName = findProperty("libName").toString()
            }
        }
    }
    linuxArm64("auroraArm64") {
        binaries {
            staticLib {
                baseName = findProperty("libName").toString()
            }
        }
    }
    sourceSets {
        val commonMain by getting {
            dependencies {
                //поместить мультиплатформенные зависимости здесь
            }
        }
    }
}

Необходимо создать директории auroraArm64Main и auroraX64Main из существующего шаблона linuxX64Main.

Следует обратить внимание на директории и названия файлов в них.

После правок целей структура проекта должна выглядеть так:

.
├── build.gradle.kts
├── .gitignore
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library
│   ├── build.gradle.kts
│   └── src
│       ├── auroraArm64Main
│       │   └── kotlin
│       │       └── fibiprops.auroraArm64.kt
│       ├── auroraX64Main
│       │   └── kotlin
│       │       └── fibiprops.auroraX64.kt
│       └── commonMain
│           └── kotlin
│               └── CustomFibi.kt
├── README.md
└── settings.gradle.kts

11 directories, 14 files

Чтобы убедиться, что все правки внесены верно, можно собрать проект:

./gradlew linkReleaseStaticAuroraX64 linkReleaseStaticAuroraX64

В конце лога сборки должно быть выведено сообщение: BUILD SUCCESSFUL.

Шаблон под разработку библиотеки с бизнес-логикой приложения для ОС Аврора готов. Позже будет добавлено все необходимое для того, чтобы выполнять функции из библиотеки в приложении. Но сначала необходимо создать приложение.

Проект ОС Аврора

Аврора IDE, входящая в состав Аврора SDK, позволяет создать приложение из доступных шаблонов. Для этого необходимо открыть Аврора IDE, нажать New в разделе Welcome → Projects.

preview

После конфигурации проекта структура проекта должна выглядеть так:

.
├── icons
│   ├── 108x108
│   │   └── ru.auroraos.demo_kmp.png
│   ├── 128x128
│   │   └── ru.auroraos.demo_kmp.png
│   ├── 172x172
│   │   └── ru.auroraos.demo_kmp.png
│   └── 86x86
│       └── ru.auroraos.demo_kmp.png
├── qml
│   ├── cover
│   │   └── DefaultCoverPage.qml
│   ├── demo_kmp.qml
│   ├── icons
│   │   └── demo_kmp.svg
│   └── pages
│       ├── AboutPage.qml
│       └── MainPage.qml
├── rpm
│   └── ru.auroraos.demo_kmp.spec
├── ru.auroraos.demo_kmp.desktop
├── ru.auroraos.demo_kmp.pro
├── src
│   └── main.cpp
└── translations
    ├── ru.auroraos.demo_kmp-ru.ts
    └── ru.auroraos.demo_kmp.ts

13 directories, 15 files

Собрать проект можно, нажав 🔨 (слева внизу).

preview

Публикация QtBindings

QtBindings на данный момент недоступен в публичных Maven-репозиториях. Для использования QtBindings необходимо использовать Maven Local Repositories.

Для сборки и публикации плагина QtBindings в Maven Local нужно клонировать открытый проект из GitLab:

git clone git@hub.mos.ru:auroraos/kotlin-multiplatform/qt-bindings.git

Затем перейти в директорию с проектом и выполнить команду:

./gradlew publishToMavenLocal

Эта команда соберёт проект и опубликует его в Maven Local, после чего плагин станет доступен на персональном компьютере разработчика.

В будущем планируется выложить плагин в публичный Maven репозиторий. На данный момент это самый простой способ использовать плагин.

Объединение проектов

Подготовка библиотеки Multiplatform

Kotlin Multiplatform для целей Linux позволяет собрать динамическую или статическую библиотеку, которую можно использовать в Qt. Статическая библиотека более удобна в использовании, поэтому в примере используется именно она.

К проекту библиотеки Kotlin/Native необходимо подключить QtBindings. Он был добавлен в Maven Local, который необходимо включить в проекте. В файл settings.gradle.kts нужно дописать методы mavenLocal():

pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
        mavenLocal()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        mavenLocal()
    }
}

В файл gradle/libs.versions.toml нужно добавить плагин QtBindings и KSP:

[versions]
kotlin = "2.1.10"
qtbindings = "0.1.0"
devtoolsKsp = "2.1.10-1.0.31"

[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
qtBindings = { id = "ru.auroraos.kmp.qtbindings", version.ref = "qtbindings" }
devtoolsKsp = { id = "com.google.devtools.ksp", version.ref = "devtoolsKsp" }

Добавить плагины в build.gradle.kts:

plugins {
    alias(libs.plugins.kotlinMultiplatform) apply false
    alias(libs.plugins.qtBindings) apply false
    alias(libs.plugins.devtoolsKsp) apply false
}

Добавить плагины в library/build.gradle.kts:

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.qtBindings)
    alias(libs.plugins.devtoolsKsp)
}

Плагину QtBindings необходимо указать название библиотеки. Название должно соответствовать названию библиотеки, указанному в целях auroraArm64Main и auroraX64Main.

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.qtBindings)
    alias(libs.plugins.devtoolsKsp)
}

qtBindings {
    libName = findProperty("libName").toString()
}

Аннотация @QtExport

В демонстрационной функции шаблона библиотеки используется метод с sequence:

fun generateFibi() = sequence {
    var a = firstElement
    yield(a)
    var b = secondElement
    yield(b)
    while (true) {
        val c = a + b
        yield(c)
        a = b
        b = c
    }
}

Эта функциональность на данный момент не поддерживается, список поддержки будет расширяться, и будет обновляться документация по QtBindings. Более подробно с информацией о поддержке можно ознакомиться в документации.

Можно добавить промежуточную функцию generateFibiWrapper и включить её в экспорт:

import ru.auroraos.kmp.qtbindings.QtExport

@QtExport
fun generateFibiWrapper(take: Int): List<Int> {
    return generateFibi().take(take).toList()
}

Всё необходимое для генерации привязок имеется, можно запустить сборку библиотеки Kotlin/Native:

./gradlew linkReleaseStaticAuroraX64 linkReleaseStaticAuroraArm64

После добавления QtBindings и сборки будут созданы С++ файлы, которые можно добавить к проекту для ОС Аврора:

  • library/build/generated/ksp/auroraArm64/auroraArm64Main/resources/*;
  • library/build/generated/ksp/auroraX64/auroraX64Main/resources/*.
.
├── io
│   └── github
│       └── kotlin
│           └── fibonacci
│               ├── CustomFibi.cpp
│               └── CustomFibi.hpp
└── ru
    └── aurora
        └── kmp
            └── qtbindings
                ├── CallbackContext.cpp
                ├── CallbackContext.hpp
                ├── CoroutineException.cpp
                ├── CoroutineException.hpp
                ├── CoroutineLauncher.cpp
                ├── CoroutineLauncher.hpp
                ├── CoroutineOperation.hpp
                └── cruntime.h

9 directories, 10 files

Подготовка приложения ОС Аврора

После сборки проекта библиотеки Kotlin/Native, QtBindings создаст файлы C++ *.hpp и *.cpp. Их нужно добавить напрямую в проект, но удобнее отделить их от основного проекта. Для этого в проекте можно сделать субдиректорию.

В корне проекта ОС Аврора нужно:

  1. Создать директорию ru.auroraos.demo_kmp для основного приложения.
  2. Создать директорию kmp.bindings для сгенерированных Qt-привязок.
  3. Создать директорию kmp.libs для C-библиотек Kotlin Multiplatform.

Далее необходимо перенести основной проект ОС Аврора в директорию ru.auroraos.demo_kmp. Структура проекта примет следующий вид:

.
├── kmp.bindings
├── kmp.libs
├── rpm
│   └── ru.auroraos.demo_kmp.spec
└── ru.auroraos.demo_kmp
    ├── icons
    │   ├── 108x108
    │   │   └── ru.auroraos.demo_kmp.png
    │   ├── 128x128
    │   │   └── ru.auroraos.demo_kmp.png
    │   ├── 172x172
    │   │   └── ru.auroraos.demo_kmp.png
    │   └── 86x86
    │       └── ru.auroraos.demo_kmp.png
    ├── qml
    │   ├── cover
    │   │   └── DefaultCoverPage.qml
    │   ├── demo_kmp.qml
    │   ├── icons
    │   │   └── demo_kmp.svg
    │   └── pages
    │       ├── AboutPage.qml
    │       └── MainPage.qml
    ├── ru.auroraos.demo_kmp.desktop
    ├── ru.auroraos.demo_kmp.pro
    ├── src
    │   └── main.cpp
    └── translations
        ├── ru.auroraos.demo_kmp-ru.ts
        └── ru.auroraos.demo_kmp.ts

16 directories, 16 files

В корень проекта ОС Аврора необходимо добавить файл demo_kmp.pro со следующим содержимым:

TEMPLATE = subdirs

OTHER_FILES += $$files(rpm/*)

SUBDIRS += \
    kmp.bindings \
    ru.auroraos.demo_kmp \

ru.auroraos.demo_kmp.depends = kmp.bindings

CONFIG += ordered

Затем в Аврора IDE можно открыть файл demo_kmp.pro и указать необходимые цели сборки:

preview

Объединение проектов

После любых модификаций библиотеки Kotlin/Native следует её сборка, что, вероятно, изменит файлы, генерируемые QtBindings. Для того, чтобы не переносить изменения самостоятельно, можно автоматизировать этот процесс через Gradle-задачи.

Чтобы получить относительные пути копирования необходимых файлов, можно совместить проект библиотеки Multiplatform и приложения для ОС Аврора:

.
├── auroraApp
│   ├── demo_kmp.pro
│   ├── kmp.bindings
│   ├── kmp.libs
│   ├── rpm
│   │   └── ru.auroraos.demo_kmp.spec
│   └── ru.auroraos.demo_kmp
│       ├── icons
│       │   ├── 108x108
│       │   │   └── ru.auroraos.demo_kmp.png
│       │   ├── 128x128
│       │   │   └── ru.auroraos.demo_kmp.png
│       │   ├── 172x172
│       │   │   └── ru.auroraos.demo_kmp.png
│       │   └── 86x86
│       │       └── ru.auroraos.demo_kmp.png
│       ├── qml
│       │   ├── cover
│       │   │   └── DefaultCoverPage.qml
│       │   ├── demo_kmp.qml
│       │   ├── icons
│       │   │   └── demo_kmp.svg
│       │   └── pages
│       │       ├── AboutPage.qml
│       │       └── MainPage.qml
│       ├── ru.auroraos.demo_kmp.desktop
│       ├── ru.auroraos.demo_kmp.pro
│       ├── src
│       │   └── main.cpp
│       └── translations
│           ├── ru.auroraos.demo_kmp-ru.ts
│           └── ru.auroraos.demo_kmp.ts
└── library
    ├── build.gradle.kts
    ├── gradle
    │   ├── libs.versions.toml
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradle.properties
    ├── gradlew
    ├── gradlew.bat
    ├── library
    │   ├── build.gradle.kts
    │   └── src
    │       ├── auroraArm64Main
    │       │   └── kotlin
    │       │       └── fibiprops.auroraArm64.kt
    │       ├── auroraX64Main
    │       │   └── kotlin
    │       │       └── fibiprops.auroraX64.kt
    │       └── commonMain
    │           └── kotlin
    │               └── CustomFibi.kt
    ├── README.md
    └── settings.gradle.kts

28 directories, 28 files

Добавление задачи Gradle

Для автоматизации процесса обновления приложения ОС Аврора, после сборки проекта библиотеки Kotlin/Native нужно зарегистрировать задачу. Эта задача выполнит следующее:

  1. Соберёт статические C-библиотеки для архитектур x86_64 и aarch64.
  2. Скопирует собранные библиотеки в проект ОС Аврора.
  3. Скопирует привязки QtBindings в проект ОС Аврора.
  4. Создаст pro-файл для компиляции Qt-привязок в приложении ОС Аврора.
  5. Выведет информацию по подключению библиотек к приложению ОС Аврора.
interface Injected {
    @get:Inject val fs: FileSystemOperations
    @get:Inject val layout: ProjectLayout
    @get:Inject val objects: ObjectFactory
}

tasks.register("buildAuroraLibs") {
    // Применение интерфейса Injected
    val libName = findProperty("libName").toString()
    val injected = project.objects.newInstance<Injected>()
    // Задача зависит
    dependsOn("linkReleaseStaticAuroraX64", "linkReleaseStaticAuroraArm64")
    // Копирование kmp lib x64
    doLast {
        injected.fs.delete {
            delete("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/x64")
        }
        injected.fs.copy {
            from(injected.layout.buildDirectory.dir("bin/auroraX64/releaseStatic")) { include("**/*.a", "**/*.h") }
            into("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/x64")
        }
    }
    // Копирование kmp lib arm64
    doLast {
        injected.fs.delete {
            delete("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/arm64")
        }
        injected.fs.copy {
            from(injected.layout.buildDirectory.dir("bin/auroraArm64/releaseStatic")) { include("**/*.a", "**/*.h") }
            into("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/arm64")
        }
    }
    // Копирование привязок kmp
    // Таргет, откуда будут браться привязки, не важен
    doLast {
        injected.fs.copy {
            from(injected.layout.buildDirectory.dir("generated/ksp/auroraX64/auroraX64Main/resources"))
            into("${injected.layout.projectDirectory}/../../auroraApp/kmp.bindings")
        }
    }
    // Привязки gen pro
    doLast {
        val search = injected.layout.buildDirectory.dir("generated/ksp/auroraX64/auroraX64Main/resources")
        val headers = mutableListOf<String>()
        val sources = mutableListOf<String>()
        val includes = mutableListOf<String>()
        injected.objects.fileTree().from(search).forEach {
            when {
                it.path.endsWith(".h") || it.path.endsWith(".hpp") -> {
                    headers.add(it.path.replace("${search.get().asFile}/", ""))
                }

                it.path.endsWith(".cpp") -> {
                    sources.add(it.path.replace("${search.get().asFile}/", ""))
                }
            }
        }
        headers.forEach {
            includes.add(it.replaceAfterLast("/", ""))
        }
        val file = File("${injected.layout.projectDirectory}/../../auroraApp/kmp.bindings/kmp.bindings.pro")
        file.writeText("""
            |TEMPLATE = lib
            |TARGET = kmpbindings
            |CONFIG += staticlib warn_off
            |
            |HEADERS += \
            |${headers.joinToString("\n") { "    $it \\" }}
            |
            |SOURCES += \
            |${sources.joinToString("\n") { "    $it \\" }}
            |
            |contains(QMAKE_HOST.arch, armv7l): {
            |    error("Неподдерживаемая архитектура armv7l")
            |}
            |contains(QMAKE_HOST.arch, x86_64): {
            |    LIB_DEMO_KMP=kmp.libs/x64
            |}
            |contains(QMAKE_HOST.arch, aarch64): {
            |    LIB_DEMO_KMP=kmp.libs/arm64
            |}
            |
            |INCLUDEPATH += $${'$'}PWD/../${'$'}${'$'}{LIB_DEMO_KMP}
            |${includes.distinct().joinToString("\n") { "INCLUDEPATH += $${'$'}PWD/$it" }}
        """.trimMargin())
        file.createNewFile()
        // Печать, что необходимо добавить в профиль заявки
        println("""
            |Проверьте pro-файл вашего приложения:
            |################################
            |# kmp.targets
            |contains(QMAKE_HOST.arch, armv7l): {
            |    error("Неподдерживаемая архитектура armv7l")
            |}
            |contains(QMAKE_HOST.arch, x86_64): {
            |    LIB_DEMO_KMP=kmp.libs/x64
            |}
            |contains(QMAKE_HOST.arch, aarch64): {
            |    LIB_DEMO_KMP=kmp.libs/arm64
            |}
            |
            |# kmp.bindings
            |${includes.distinct().joinToString("\n") { "INCLUDEPATH += $${'$'}PWD/../kmp.bindings/$it" }}
            |LIBS += -L${'$'}${'$'}OUT_PWD/../kmp.bindings -lkmpbindings
            |
            |# kmp.libs
            |INCLUDEPATH += ${'$'}${'$'}PWD/../${'$'}${'$'}{LIB_DEMO_KMP}
            |LIBS += -L${'$'}${'$'}PWD/../${'$'}${'$'}{LIB_DEMO_KMP} -l${libName}
            |################################
        """.trimMargin())
    }
}

В задаче buildAuroraLibs привязки копируются с учётом их идентичности для целей, но это может быть не так. Если в приложении они отличаются, следует доработать задачу согласно логике проекта.

Вывод задачи Gradle:

# kmp.targets
contains(QMAKE_HOST.arch, armv7l): {
    error("Unsupported architecture armv7l")
}
contains(QMAKE_HOST.arch, x86_64): {
    LIB_DEMO_KMP=kmp.libs/x64
}
contains(QMAKE_HOST.arch, aarch64): {
    LIB_DEMO_KMP=kmp.libs/arm64
}

# kmp.bindings
INCLUDEPATH += $$PWD/../kmp.bindings/ru/aurora/kmp/qtbindings/
INCLUDEPATH += $$PWD/../kmp.bindings/io/github/kotlin/fibonacci/
LIBS += -L$$OUT_PWD/../kmp.bindings -lkmpbindings

# kmp.libs
INCLUDEPATH += $$PWD/../$${LIB_DEMO_KMP}
LIBS += -L$$PWD/../$${LIB_DEMO_KMP} -ldemo_kmp

Вывод из Gradle следует добавить в конец файла auroraApp/ru.auroraos.demo_kmp/ru.auroraos.demo_kmp.pro проекта ОС Аврора.

Теперь приложение ОС Аврора можно собрать:

preview

Выполнение метода

На данный момент общий проект содержит:

  1. Библиотеку Kotlin/Native.
  2. Приложение Qt/Qml для ОС Аврора.
  3. Задачу buildAuroraLibs, автоматизирующую процесс сборки и синхронизации.
  4. Статическую библиотеку с файлами генерации QtBindings, интегрированную с приложением.
  5. Метод в библиотеке Kotlin/Native с аннотацией @QtExport, позволяющий выполнить его в приложении.

Метод generateFibiWrapper с аннотацией @QtExport помещён в пространство имён соответственно пакету:

namespace io {
namespace github {
namespace kotlin {
namespace fibonacci {

QList<int> generateFibiWrapper();

} /* namespace fibonacci */
} /* namespace kotlin */
} /* namespace github */
} /* namespace io */

В шаблоне приложения ОС Аврора можно найти основную функцию С++ — main. В ней можно проверить результат работы QtBindings и библиотеки Kotlin/Native:

#include <auroraapp.h>
#include <QtQuick>
#include <QDebug>

#include "CustomFibi.hpp"

int main(int argc, char *argv[])
{
    // Запуск из библиотеки Kotlin/Native
    qDebug() << io::github::kotlin::fibonacci::generateFibiWrapper(5);

    // Приложение Qt/Qml
    QScopedPointer<QGuiApplication> application(Aurora::Application::application(argc, argv));
    application->setOrganizationName(QStringLiteral("ru.auroraos"));
    application->setApplicationName(QStringLiteral("demo_kmp"));

    QScopedPointer<QQuickView> view(Aurora::Application::createView());
    view->setSource(Aurora::Application::pathTo(QStringLiteral("qml/demo_kmp.qml")));
    view->show();

    return application->exec();
}

После запуска приложения в логах можно наблюдать выполнение функции generateFibiWrapper и привязок QtBindings из библиотеки Kotlin/Native:

preview

Использование Coroutines

QtBindings позволяет работать с Kotlin Coroutines через Qt:

  • ожидать завершение suspend-функции;
  • отменять его при необходимости;
  • получать исключения.

В библиотеке Kotlin/Native есть синхронная функция generateFibiWrapper, которая была вызвана ранее. Для демонстрации работы с асинхронными функциями можно добавить suspend-функцию:

@QtExport
suspend fun generateFibiWrapperAsync(take: Int): List<Int> {
    delay(5000L)
    return generateFibi().take(take).toList()
}

После добавления функции в библиотеку Kotlin/Native её необходимо собрать и обновить файлы, генерируемые QtBindings в проекте ОС Аврора. Сделать это можно через добавленную ранее задачу Gradle buildAuroraLibs:

./gradlew buildAuroraLibs

QtBindings из suspend-функции создаст QFuture, который можно использовать с QFutureWatcher, что позволит использовать весь основной функционал Coroutines.

Вызвать функцию из Qt можно так:

auto fibiWrapperAsync = new QFutureWatcher<QList<int>>();
auto feature = io::github::kotlin::fibonacci::generateFibiWrapperAsync(5);
fibiWrapperAsync->setFuture(feature);
QObject::connect(fibiWrapperAsync, &QFutureWatcher<QList<int>>::finished, [=]() {
    qDebug() << fibiWrapperAsync->result();
    fibiWrapperAsync->deleteLater();
});

preview

QtBindings позволяет завершать запросы и получать исключения из suspend-функций. Нужно добавить метод, на котором можно это проверить:

@QtExport
suspend fun testCatchAsync(isError: Boolean): Boolean {
    delay(5000L)
    if (isError) {
        throw RuntimeException("Моя ошибка")
    }
    return true
}

Вот так можно вызвать функцию testCatchAsync с отменой её ожидания:

auto testCatchAsyncCancel = new QFutureWatcher<bool>();
auto featureCancel = io::github::kotlin::fibonacci::testCatchAsync(false);
testCatchAsyncCancel->setFuture(featureCancel);
QObject::connect(testCatchAsyncCancel, &QFutureWatcher<bool>::canceled, [=]() {
    qDebug() << "testCatchAsyncCancel - canceled";
});
// Отмена
testCatchAsyncCancel->cancel();

preview

Для перехвата ошибки в функции testCatchAsync нужно указать параметр isError как true и воспользоваться try/catch:

auto testCatchAsyncError = new QFutureWatcher<bool>();
auto featureError = io::github::kotlin::fibonacci::testCatchAsync(true);
testCatchAsyncError->setFuture(featureError);
QObject::connect(testCatchAsyncError, &QFutureWatcher<bool>::canceled, [=]() {
    try {
        auto value =  testCatchAsyncError->result();
        qDebug() << value;
    } catch (Aurora::Kmp::QtBindings::CoroutineException& ex) {
        qDebug() << ex.message();
    }
});

preview

Заключение

QtBindings позволяет взаимодействовать с библиотекой Kotlin Multiplatform нативными средствами Qt. Он существенно упрощает разработку приложений: добавление привязок происходит не сложнее, чем на других платформах, и разработчику больше не нужно погружаться в низкоуровневую логику С-библиотек.

Развитие поддержки Kotlin Multiplatform в ОС Аврора только начинается, но уже можно писать приложения с использованием целей linuxX64 и linuxArm64 на ОС Аврора!

Ссылки

Kotlin Multiplatform

Другие инструменты

Мы используем cookies для персонализации сайта и его более удобного использования. Вы можете запретить cookies в настройках браузера.

Пожалуйста ознакомьтесь с политикой использования cookies.