Создание приложения с использованием C Interop и libcurl – учебник
Этот учебник демонстрирует, как использовать IntelliJ IDEA для создания командной строки приложения. Вы узнаете, как создать простой HTTP-клиент, который может выполняться на указанных платформах в родном виде с помощью Kotlin/Native и библиотеки libcurl.
Выходным результатом будет исполняемое приложение командной строки, которое вы сможете запустить на macOS и Linux и выполнить простые HTTP-GET запросы.
Для начала установите последнюю версию IntelliJ IDEA. Учебник подходит как для IntelliJ IDEA Community Edition, так и для IntelliJ IDEA Ultimate.
Создание проекта Kotlin/Native
В IntelliJ IDEA выберите Файл | Создать | Проект.
В панели слева выберите Kotlin Multiplatform | Приложение Native.
-
Укажите имя и выберите папку, в которую будет сохранёно ваше приложение.

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

Вместе с вашим новым проектом генерируется файл 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-запросов. Чтобы создать файл определения:
Выберите папку
srcи создайте новую подпапку с помощью Файл | Создать | Папку.Назовите новую подпапку nativeInterop/cinterop. Это стандартная соглашение для расположения файлов заголовков, хотя оно может быть переопределено в файле
build.gradle, если используется другое расположение.Выберите эту новую подпапку и создайте новый файл
libcurl.defс помощью Файл | Создать | Файл.-
Обновите ваш файл следующим кодом:
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, см. раздел взаимодействия.
Добавление взаимодействия в процесс сборки
Для использования файлов заголовков убедитесь, что они генерируются в процессе сборки. Для этого добавьте следующий элемент в файл 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.
Компиляция и запуск приложения
-
Скомпилируйте приложение. Для этого выполните следующую команду в терминале:
./gradlew runDebugExecutableNative
В данном случае сгенерированная часть
cinteropнеявно включается в сборку. -
Если при компиляции ошибок не возникло, нажмите зелёную иконку Запуск в области слева от метода
main()или используйте комбинацию клавиш Alt+Enter, чтобы открыть меню запуска в IntelliJ IDEA.IntelliJ IDEA откроет вкладку Запуск и отобразит вывод — содержимое
https://example.com.
Вы можете увидеть фактический вывод, потому что вызов curl_easy_perform выводит результат в стандартный вывод. Вы можете скрыть это, используя curl_easy_setopt.
© 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