Spec-Zone.ru › Kotlin 1.7

Создание приложения с использованием C Interop и libcurl – учебник

Этот учебник демонстрирует, как использовать IntelliJ IDEA для создания приложения командной строки. Вы узнаете, как создать простой HTTP-клиент, который может выполняться на указанных платформах в режиме native с использованием Kotlin/Native и библиотеки libcurl.

Результатом будет исполняемое приложение командной строки, которое можно запустить на macOS и Linux и выполнить простые запросы HTTP GET.

Хотя можно использовать командную строку, либо напрямую, либо комбинируя её со скриптовым файлом (например, файлом .sh или файлом .bat), такой подход не масштабируется для больших проектов с сотнями файлов и библиотек. В этом случае лучше использовать Kotlin/Native компилятор с системой сборки, так как она помогает загружать и кэшировать бинарные файлы и библиотеки Kotlin/Native компилятора с транзитивными зависимостями, а также запускать компилятор и тесты. Kotlin/Native может использовать систему сборки Gradle через плагин kotlin-multiplatform.

Для начала установите последнюю версию IntelliJ IDEA. Учебник подходит как для IntelliJ IDEA Community Edition, так и для IntelliJ IDEA Ultimate.

Создание проекта Kotlin/Native

  1. В IntelliJ IDEA выберите Файл | Новый | Проект.

  2. В левой панели выберите Kotlin Multiplatform | Приложение Native.

  3. Укажите имя и выберите папку, где вы сохраните своё приложение.

    Новый проект. Приложение Native в IntelliJ IDEA
  4. Нажмите Далее, а затем Готово.

IntelliJ IDEA создаст новый проект с необходимыми файлами и папками. Важно понимать, что приложение, написанное на Kotlin/Native, может быть нацелено на разные платформы, если код не имеет платформоспецифических требований. Ваш код размещен в папке NativeMain с соответствующим NativeTest. Для этого учебника сохраните структуру папок как есть.

Структура проекта приложения Native

Вместе с новым проектом генерируется файл build.gradle(.kts). Обратите особое внимание на следующее в файле сборки:

kotlin {
    val hostOs = System.getProperty("os.name")
    val isMingwX64 = hostOs.startsWith("Windows")
    val nativeTarget = when {
        hostOs == "Mac OS X" -> macosX64("native")
        hostOs == "Linux" -> linuxX64("native")
        isMingwX64 -> mingwX64("native")
        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    }

    nativeTarget.apply {
        binaries {
            executable {
                entryPoint = "main"
            }
        }
    }
}

kotlin {
    def hostOs = System.getProperty("os.name")
    def isMingwX64 = hostOs.startsWith("Windows")
    def nativeTarget
        if (hostOs == "Mac OS X") nativeTarget = macosX64('native')
        else if (hostOs == "Linux") nativeTarget = linuxX64("native")
        else if (isMingwX64) nativeTarget = mingwX64("native")
        else throw new FileNotFoundException("Host OS is not supported in Kotlin/Native.")

    nativeTarget.with {
        binaries {
            executable {
                entryPoint = 'main'
            }
        }
    }
}
  • Цели определяются с помощью macOSX64, linuxX64, и mingwX64 для macOS, Linux и Windows. Полный список поддерживаемых платформ см. в обзоре Kotlin Native.

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

  • Взаимодействие с C настроен как дополнительный шаг в сборке. По умолчанию все символы из C импортируются в пакет interop. Вы можете импортировать весь пакет в файлы .kt. Узнайте больше о настройке этого.

Создание файла определения

При написании приложений native часто требуется доступ к определенным функциям, которые не входят в стандартную Kotlin библиотеку, такие как выполнение HTTP-запросов, чтение и запись из диска и т. д.

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

Идеальный сценарий для взаимодействия — вызывать функции C, как будто вы вызываете функции Kotlin, следуя тому же подписи и соглашениям. Именно для этого пригодится инструмент cinterop. Он принимает C библиотеку и генерирует соответствующие Kotlin bindings, так что библиотека может использоваться как будто это Kotlin код.

Для генерации этих bindinгов создайте файл определения библиотеки .def, который содержит информацию о необходимых заголовках. В этом приложении вам понадобится библиотека libcurl для выполнения HTTP-вызовов. Чтобы создать файл определения:

  1. Выберите папку src и создайте новую папку с помощью Файл | Новый | Папка.

  2. Назовите новую папку nativeInterop/cinterop. Это стандартная конвенция для расположения файлов заголовков, хотя её можно переопределить в файле build.gradle при использовании другого расположения.

  3. Выберите эту новую подпапку и создайте новый файл libcurl.def с помощью Файл | Новый | Файл.

  4. Обновите свой файл следующим кодом:

    headers = curl/curl.h
    headerFilter = curl/*
    
    compilerOpts.linux = -I/usr/include -I/usr/include/x86_64-linux-gnu
    linkerOpts.osx = -L/opt/local/lib -L/usr/local/opt/curl/lib -lcurl
    linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lcurl
    
    • headers — это список файлов заголовков для генерации Kotlin заглушек. Вы можете добавить несколько файлов в эту запись, разделяя каждый новой строкой с \. В этом случае это только curl.h. Ссылаемые файлы должны быть доступны в системном пути (в этом случае это /usr/include/curl).

    • headerFilter показывает, что именно включено. В C все заголовки также включаются, когда один файл ссылается на другой с помощью директивы #include. Иногда это не нужно, и вы можете добавить этот параметр используя шаблоны glob для тонкой настройки.

      headerFilter — это необязательный аргумент и в основном используется, когда библиотека установлена как системная библиотека. Вы не хотите загружать внешние зависимости (например, системные заголовки stdint.h) в библиотеку для взаимодействия. Это может быть важно для оптимизации размера библиотеки и предотвращения возможных конфликтов между системой и предоставляемой средой компиляции Kotlin/Native.

    • Следующие строки относятся к предоставлению параметров для компоновщика и компилятора, которые могут отличаться в зависимости от различных целевых платформ. В данном случае это macOS (суффикс .osx) и Linux (суффикс .linux). Параметры без суффикса также возможны (например, linkerOpts=) и применяются ко всем платформам.

Конвенция заключается в том, что каждая библиотека получает свой файл определения, обычно с тем же именем, что и библиотека. Дополнительную информацию обо всех доступных параметрах для cinterop см. в разделе Взаимодействия.

Для работы примера вам нужны бинарные файлы библиотеки curl на вашей системе. На macOS и Linux они обычно включены. На Windows вы можете скомпилировать их из исходных кодов (вам понадобится Visual Studio или инструменты командной строки Windows SDK). Более подробную информацию см. в соответствующей записи в блоге. В качестве альтернативы вы можете рассмотреть бинарник MinGW/MSYS2 curl.

Добавление взаимодействия в процесс сборки

Чтобы использовать файлы заголовков, убедитесь, что они генерируются как часть процесса сборки. Для этого добавьте следующую запись в файл build.gradle(.kts).

nativeTarget.apply {
    compilations.getByName("main") {    // NL
        cinterops {                     // NL
            val libcurl by creating     // NL
        }                               // NL
    }                                   // NL
    binaries {
        executable {
            entryPoint = "main"
        }
    }
}
nativeTarget.with {
    compilations.main { // NL
        cinterops {     // NL
            libcurl     // NL
        }               // NL
    }                   // NL
    binaries {
        executable {
            entryPoint = 'main'
        }
    }
}

Новые строки помечены // NL. Сначала добавляется cinterops, а затем запись для каждого файла def. По умолчанию используется имя файла. Вы можете переопределить его с помощью дополнительных параметров:

val libcurl by creating {
    defFile(project.file("src/nativeInterop/cinterop/libcurl.def"))
    packageName("com.jetbrains.handson.http")
    compilerOpts("-I/path")
    includeDirs.allHeaders("path")
}
libcurl {
    defFile project.file("src/nativeInterop/cinterop/libcurl.def")
    packageName 'com.jetbrains.handson.http'
    compilerOpts '-I/path'
    includeDirs.allHeaders("path")
}

См. раздел Взаимодействие с C для получения более подробной информации об доступных параметрах.

Написание кода приложения

Теперь у вас есть библиотека и соответствующие Kotlin-заглушки, и вы можете использовать их в своём приложении. Для этого руководства преобразуйте пример simple.c в Kotlin.

В папке src/nativeMain/kotlin/ обновите файл Main.kt следующим кодом:

import kotlinx.cinterop.*
import libcurl.*

fun main(args: Array<String>) {
    val curl = curl_easy_init()
    if (curl != null) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://example.com")
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)
        val res = curl_easy_perform(curl)
        if (res != CURLE_OK) {
            println("curl_easy_perform() failed ${curl_easy_strerror(res)?.toKString()}")
        }
        curl_easy_cleanup(curl)
    }
}

Как вы видите, явные объявления переменных устранены в версии Kotlin, но всё остальное практически такое же, как и в версии на C. Все вызовы, которые вы ожидаете в библиотеке libcurl, доступны в эквиваленте на Kotlin.

Это буквальный перевод строка за строкой. Вы также можете написать это более идиоматичным для Kotlin способом.

Компиляция и запуск приложения

  1. Скомпилируйте приложение. Для этого выполните следующую команду в терминале:

    ./gradlew runDebugExecutableNative
    

    В этом случае сгенерированная часть cinterop неявно включается в сборку.

  2. Если при компиляции ошибок не возникло, нажмите зелёную иконку Запуск в области слева от метода main() или используйте комбинацию клавиш Alt+Enter для вызова меню запуска в IntelliJ IDEA.

    IntelliJ IDEA откроет вкладку Запуск и отобразит вывод — содержимое https://example.com:

    Вывод приложения с HTML-кодом

Вы можете увидеть фактический вывод, так как вызов curl_easy_perform печатает результат в стандартный вывод. Вы можете скрыть это, используя curl_easy_setopt.

Полный код вы можете получить здесь.

Последнее изменение: 06 сентября 2022
Преобразование строк из C – учебник Взаимодействие с Swift/Objective-C

© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/native-app-with-c-and-libcurl.html

Spec-Zone.ru

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