Spec-Zone.ru › Kotlin 1.8

Создание окончательных нативных библиотек

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

По умолчанию, целевой Kotlin/Native компилируется в библиотеку *.klib artifact, которую можно использовать в Kotlin/Native как зависимость, но нельзя запускать или использовать как нативную библиотеку.

Чтобы объявить окончательные нативные библиотеки, такие как исполняемые файлы или динамические библиотеки, используйте свойство binaries целевого нативного объекта. Это свойство представляет собой набор нативных библиотек, скомпилированных для данной цели, помимо стандартной *.klib библиотеки и предоставляет набор методов для их объявления и настройки.

Плагин kotlin-multiplatform по умолчанию не создаёт никаких библиотек для производства. Единственная доступная по умолчанию библиотека — это исполняемый файл для отладки тестов, который позволяет запускать unit-тесты из test компиляции.

Объявление библиотек

Используйте следующие фабричные методы для объявления элементов коллекции binaries.

Фабричный метод

Тип библиотеки

Доступно для

executable

Исполняемый файл продукта

Все нативные цели

test

Исполняемый файл теста

Все нативные цели

sharedLib

Динамическая библиотека

Все нативные цели, кроме WebAssembly

staticLib

Статическая библиотека

Все нативные цели, кроме WebAssembly

framework

Фреймворк Objective-C

Только цели macOS, iOS, watchOS и tvOS

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

  • DEBUG — создает не оптимизированную библиотеку с отладочной информацией

  • RELEASE — создает оптимизированную библиотеку без отладочной информации

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

kotlin {
    linuxX64 { // Define your target instead.
        binaries {
            executable {
                // Binary configuration.
            }
        }
    }
}

Вы можете опустить лямбда-выражение, если не требуется дополнительная настройка:

binaries {
    executable()
}

Можно указать, для каких типов сборки создавать библиотеки. В следующем примере создается только исполняемый файл debug:

binaries {
    executable(listOf(DEBUG)) {
        // Binary configuration.
    }
}
binaries {
    executable([DEBUG]) {
        // Binary configuration.
    }
}

Вы также можете объявить библиотеки с пользовательскими именами:

binaries {
    executable("foo", listOf(DEBUG)) {
        // Binary configuration.
    }

    // It's possible to drop the list of build types
    // (in this case, all the available build types will be used).
    executable("bar") {
        // Binary configuration.
    }
}
binaries {
    executable('foo', [DEBUG]) {
        // Binary configuration.
    }

    // It's possible to drop the list of build types
    // (in this case, all the available build types will be used).
    executable('bar') {
        // Binary configuration.
    }
}

Первый аргумент устанавливает префикс имени, который является стандартным именем для файла библиотеки. Например, для Windows код создает файлы foo.exe и bar.exe. Вы также можете использовать префикс имени для доступа к библиотеке в скрипте сборки.

Доступ к библиотекам

Вы можете получить доступ к библиотекам для их настройки или получения свойств (например, пути к выходному файлу).

Вы можете получить библиотеку по ее уникальному имени. Это имя основано на префиксе имени (если он указан), типе сборки и типе библиотеки по шаблону: <optional-name-prefix><build-type><binary-kind>, например, releaseFramework или testDebugExecutable.

Статические и динамические библиотеки имеют суффиксы static и shared соответственно, например, fooDebugStatic или barReleaseShared.

// Fails if there is no such binary.
binaries["fooDebugExecutable"]
binaries.getByName("fooDebugExecutable")

// Returns null if there is no such binary.
binaries.findByName("fooDebugExecutable")
// Fails if there is no such binary.
binaries['fooDebugExecutable']
binaries.fooDebugExecutable
binaries.getByName('fooDebugExecutable')

// Returns null if there is no such binary.
binaries.findByName('fooDebugExecutable')

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

// Fails if there is no such binary.
binaries.getExecutable("foo", DEBUG)
binaries.getExecutable(DEBUG)          // Skip the first argument if the name prefix isn't set.
binaries.getExecutable("bar", "DEBUG") // You also can use a string for build type.

// Similar getters are available for other binary kinds:
// getFramework, getStaticLib and getSharedLib.

// Returns null if there is no such binary.
binaries.findExecutable("foo", DEBUG)

// Similar getters are available for other binary kinds:
// findFramework, findStaticLib and findSharedLib.
// Fails if there is no such binary.
binaries.getExecutable('foo', DEBUG)
binaries.getExecutable(DEBUG)          // Skip the first argument if the name prefix isn't set.
binaries.getExecutable('bar', 'DEBUG') // You also can use a string for build type.

// Similar getters are available for other binary kinds:
// getFramework, getStaticLib and getSharedLib.

// Returns null if there is no such binary.
binaries.findExecutable('foo', DEBUG)

// Similar getters are available for other binary kinds:
// findFramework, findStaticLib and findSharedLib.

Экспорт зависимостей в библиотеки

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

При создании фреймворка Objective-C или нативной библиотеки (динамической или статической), возможно, вам потребуется упаковать не только классы текущего проекта, но и классы зависимостей. Укажите, какие зависимости экспортировать в библиотеку, используя метод export.

kotlin {
    sourceSets {
        macosMain.dependencies {
            // Will be exported.
            api(project(":dependency"))
            api("org.example:exported-library:1.0")
            // Will not be exported.
            api("org.example:not-exported-library:1.0")
        }
    }
    macosX64("macos").binaries {
        framework {
            export(project(":dependency"))
            export("org.example:exported-library:1.0")
        }
        sharedLib {
            // It's possible to export different sets of dependencies to different binaries.
            export(project(':dependency'))
        }
    }
}
kotlin {
    sourceSets {
        macosMain.dependencies {
            // Will be exported.
            api project(':dependency')
            api 'org.example:exported-library:1.0'
            // Will not be exported.
            api 'org.example:not-exported-library:1.0'
        }
    }
    macosX64("macos").binaries {
        framework {
            export project(':dependency')
            export 'org.example:exported-library:1.0'
        }
        sharedLib {
            // It's possible to export different sets of dependencies to different binaries.
            export project(':dependency')
        }
    }
}

Например, вы реализовали несколько модулей на Kotlin и хотите получить к ним доступ из Swift. Использование нескольких Kotlin/Native фреймворков в приложении Swift ограничено, но вы можете создать общий фреймворк и экспортировать все эти модули в него.

Вы можете экспортировать только api зависимости соответствующего набора исходных данных.

Когда вы экспортируете зависимость, она включает в себя весь API для API фреймворка. Компилятор добавляет код из этой зависимости в фреймворк, даже если вы используете лишь малую часть. Это отключает удаление неиспользуемого кода для экспортируемой зависимости (и для ее зависимостей, в некоторой степени).

По умолчанию экспорт происходит не транзитивно. Это означает, что если вы экспортируете библиотеку foo, зависящую от библиотеки bar, в выходной фреймворк будут добавлены только методы библиотеки foo.

Вы можете изменить это поведение, используя опцию transitiveExport. Если она установлена в значение true, объявления библиотеки bar также экспортируются.

Не рекомендуется использовать transitiveExport, так как это добавляет все транзитивные зависимости экспортируемых зависимостей в фреймворк. Это может увеличить время компиляции и размер бинарника.

В большинстве случаев вам не нужно добавлять все эти зависимости в API фреймворка. Используйте export явно для зависимостей, которые вам нужно напрямую использовать в коде Swift или Objective-C.

binaries {
    framework {
        export(project(":dependency"))
        // Export transitively.
        transitiveExport = true
    }
}
binaries {
    framework {
        export project(':dependency')
        // Export transitively.
        transitiveExport = true
    }
}

Создание универсальных фреймворков

Вы также можете попробовать новый Kotlin/Native DSL для создания универсальных фреймворков.

По умолчанию, фреймворк Objective-C, созданный Kotlin/Native, поддерживает только одну платформу. Однако вы можете объединить такие фреймворки в одну универсальную (fat) библиотеку с помощью инструмента lipo. Это особенно имеет смысл для iOS-фреймворков 32-bit и 64-bit. В этом случае вы можете использовать получившийся универсальный фреймворк как на устройствах 32-bit, так и на устройствах 64-bit.

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

import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask

kotlin {
    // Create and configure the targets.
    val ios32 = iosArm32("ios32")
    val ios64 = iosArm64("ios64")
    configure(listOf(ios32, ios64)) {
        binaries.framework {
            baseName = "my_framework"
        }
    }
    // Create a task to build a fat framework.
    tasks.register<FatFrameworkTask>("debugFatFramework") {
        // The fat framework must have the same base name as the initial frameworks.
        baseName = "my_framework"
        // The default destination directory is "<build directory>/fat-framework".
        destinationDir = buildDir.resolve("fat-framework/debug")
        // Specify the frameworks to be merged.
        from(
            ios32.binaries.getFramework("DEBUG"),
            ios64.binaries.getFramework("DEBUG")
        )
    }
}
import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask

kotlin {
    // Create and configure the targets.
    targets {
        iosArm32("ios32")
        iosArm64("ios64")
        configure([ios32, ios64]) {
            binaries.framework {
                baseName = "my_framework"
            }
        }
    }
    // Create a task building a fat framework.
    tasks.register("debugFatFramework", FatFrameworkTask) {
        // The fat framework must have the same base name as the initial frameworks.
        baseName = "my_framework"
        // The default destination directory is "<build directory>/fat-framework".
        destinationDir = file("$buildDir/fat-framework/debug")
        // Specify the frameworks to be merged.
        from(
            targets.ios32.binaries.getFramework("DEBUG"),
            targets.ios64.binaries.getFramework("DEBUG")
        )
    }
}
END_OF_DOCUMENT_MARKER

Создание XCFrameworks

Также можно попробовать новый Kotlin/Native DSL для создания XCFrameworks. Ссылка.

Все проекты Kotlin Multiplatform могут использовать XCFrameworks в качестве результата для сбора логики для всех целевых платформ и архитектур в одном пакете. В отличие от универсальных (fat) фреймворков, вам не нужно удалять все ненужные архитектуры перед публикацией приложения в App Store.

import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework

plugins {
    kotlin("multiplatform")
}

kotlin {
    val xcf = XCFramework()
    val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64())
    
    iosTargets.forEach {
        it.binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
}
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFrameworkConfig

plugins {
    id 'org.jetbrains.kotlin.multiplatform'
}

kotlin {
    def xcf = new XCFrameworkConfig(project)
    def iosTargets = [iosX64(), iosArm64(), iosSimulatorArm64()]
    
    iosTargets.forEach {
        it.binaries.framework {
            baseName = 'shared'
            xcf.add(it)
        }
    }
}

При объявлении XCFrameworks плагин Kotlin Gradle зарегистрирует три задачи Gradle:

  • assembleXCFramework

  • assembleDebugXCFramework (дополнительно артефакт отладки, содержащий dSYMs)

  • assembleReleaseXCFramework

Если в ваших проектах используется интеграция CocoaPods, вы можете создавать XCFrameworks с помощью плагина Kotlin CocoaPods Gradle. Он включает следующие задачи, которые создают XCFrameworks со всеми зарегистрированными целевыми приложениями и генерируют файлы podspec:

  • podPublishReleaseXCFramework, который генерирует релизный XCFramework вместе с файлом podspec.

  • podPublishDebugXCFramework, который генерирует отладочный XCFramework вместе с файлом podspec.

  • podPublishXCFramework, который генерирует как отладочный, так и релизный XCFrameworks вместе с файлом podspec.

Это может помочь вам распространять общие части вашего проекта отдельно от мобильных приложений через CocoaPods. Вы также можете использовать XCFrameworks для публикации в частных или общедоступных репозиториях podspec.

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

Настройка файла Info.plist

При создании фреймворка компилятор Kotlin/Native генерирует файл свойств информационного списка Info.plist. Вы можете настроить его свойства соответствующей опцией бинарника:

Свойство

Опция бинарника

CFBundleIdentifier

bundleId

CFBundleShortVersionString

bundleShortVersionString

CFBundleVersion

bundleVersion

Чтобы включить эту функцию, передайте флаг компилятора -Xbinary=$option=$value или задайте binaryOption("option", "value") Gradle DSL для конкретного фреймворка:

binaries {
    framework {
        binaryOption("bundleId", "com.example.app")
        binaryOption("bundleVersion", "2")
    }
}
Последнее изменение: 10 января 2023 г.
Создание окончательных нативных бинарных файлов (экспериментальный DSL) Справочник по Multiplatform Gradle DSL

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

Spec-Zone.ru

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