Spec-Zone.ru › Kotlin 1.8

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

Этот учебник демонстрирует, как использовать IntelliJ IDEA для создания командной строки приложения. Вы узнаете, как создать простой HTTP-клиент, который может выполняться на указанных платформах в родном виде с помощью 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. Укажите имя и выберите папку, в которую будет сохранёно ваше приложение.

    New project. Native application in IntelliJ IDEA
  4. Нажмите Далее, а затем Готово.

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

Native application project structure

Вместе с вашим новым проектом генерируется файл 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. Узнайте больше о настройке этого.

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

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

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

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

Чтобы сгенерировать эти связки, создайте файл определения библиотеки .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.

    Application output with HTML-code

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

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

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

© 2010–2023 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