본문으로 건너뛰기

최종 네이티브 바이너리 빌드

기본적으로 Kotlin/Native 타겟은 *.klib 라이브러리 아티팩트로 컴파일됩니다. 이 아티팩트는 Kotlin/Native 자체에서 의존성으로 사용할 수 있지만, 실행하거나 네이티브 라이브러리로 사용할 수는 없습니다.

실행 파일 또는 공유 라이브러리와 같은 최종 네이티브 바이너리를 선언하려면 네이티브 타겟의 binaries 속성을 사용하세요. 이 속성은 기본 *.klib 아티팩트 외에도 이 타겟을 위해 빌드된 네이티브 바이너리 컬렉션을 나타내며, 이를 선언하고 구성하기 위한 메서드 집합을 제공합니다.

노트

kotlin-multiplatform 플러그인은 기본적으로 프로덕션 바이너리를 생성하지 않습니다. 기본적으로 사용 가능한 유일한 바이너리는 test 컴파일에서 유닛 테스트를 실행할 수 있게 해주는 디버그 테스트 실행 파일입니다.

Kotlin/Native 컴파일러에서 생성된 바이너리에는 타사 코드, 데이터 또는 파생 작업이 포함될 수 있습니다. 즉, Kotlin/Native로 컴파일된 최종 바이너리를 배포하는 경우, 항상 필요한 라이선스 파일을 바이너리 배포에 포함해야 합니다.

바이너리 선언

binaries 컬렉션의 요소를 선언하려면 다음 팩토리 메서드를 사용하세요.

팩토리 메서드바이너리 종류사용 가능한 타겟
executable제품 실행 파일모든 네이티브 타겟
test테스트 실행 파일모든 네이티브 타겟
sharedLib공유 네이티브 라이브러리WebAssembly를 제외한 모든 네이티브 타겟
staticLib정적 네이티브 라이브러리WebAssembly를 제외한 모든 네이티브 타겟
frameworkObjective-C frameworkmacOS, iOS, watchOS, 및 tvOS 타겟만 해당

가장 간단한 버전은 추가 매개변수가 필요하지 않으며 각 빌드 유형에 대해 하나의 바이너리를 생성합니다. 현재, 두 가지 빌드 유형을 사용할 수 있습니다:

  • DEBUG – 디버그 정보가 포함된 비최적화 바이너리를 생성합니다.
  • RELEASE – 디버그 정보가 없는 최적화 바이너리를 생성합니다.

다음 스니펫은 디버그 및 릴리스의 두 가지 실행 파일 바이너리를 생성합니다:

kotlin {
linuxX64 { // 타겟을 대신 정의하세요.
binaries {
executable {
// 바이너리 구성.
}
}
}
}

추가 구성이 필요하지 않은 경우 람다를 생략할 수 있습니다:

binaries {
executable()
}

바이너리를 생성할 빌드 유형을 지정할 수 있습니다. 다음 예에서는 debug 실행 파일만 생성됩니다:

binaries {
executable(listOf(DEBUG)) {
// 바이너리 구성.
}
}

사용자 지정 이름으로 바이너리를 선언할 수도 있습니다:

binaries {
executable("foo", listOf(DEBUG)) {
// 바이너리 구성.
}

// 빌드 유형 목록을 생략할 수 있습니다
// (이 경우 사용 가능한 모든 빌드 유형이 사용됩니다).
executable("bar") {
// 바이너리 구성.
}
}

첫 번째 인수는 바이너리 파일의 기본 이름인 이름 접두사를 설정합니다. 예를 들어, Windows의 경우 코드는 foo.exebar.exe 파일을 생성합니다. 이름 접두사를 사용하여 빌드 스크립트에서 바이너리에 액세스할 수도 있습니다.

바이너리 액세스

바이너리에 액세스하여 구성하거나 해당 속성(예: 출력 파일 경로)을 가져올 수 있습니다.

고유한 이름으로 바이너리를 가져올 수 있습니다. 이 이름은 이름 접두사(지정된 경우), 빌드 유형 및 바이너리 종류를 기반으로 다음 패턴을 따릅니다: <선택적-이름-접두사><빌드-유형><바이너리-종류>, 예를 들어 releaseFramework 또는 testDebugExecutable입니다.

노트

정적 및 공유 라이브러리에는 각각 static 및 shared 접미사가 있습니다(예: fooDebugStatic 또는 barReleaseShared).

// 그러한 바이너리가 없으면 실패합니다.
binaries["fooDebugExecutable"]
binaries.getByName("fooDebugExecutable")

// 그러한 바이너리가 없으면 null을 반환합니다.
binaries.findByName("fooDebugExecutable")

또는 유형화된 getter를 사용하여 이름 접두사 및 빌드 유형별로 바이너리에 액세스할 수 있습니다.

// 그러한 바이너리가 없으면 실패합니다.
binaries.getExecutable("foo", DEBUG)
binaries.getExecutable(DEBUG) // 이름 접두사가 설정되지 않은 경우 첫 번째 인수를 건너뜁니다.
binaries.getExecutable("bar", "DEBUG") // 빌드 유형에 문자열을 사용할 수도 있습니다.

// 유사한 getter는 다른 바이너리 종류에 사용할 수 있습니다:
// getFramework, getStaticLib 및 getSharedLib.

// 그러한 바이너리가 없으면 null을 반환합니다.
binaries.findExecutable("foo", DEBUG)

// 유사한 getter는 다른 바이너리 종류에 사용할 수 있습니다:
// findFramework, findStaticLib 및 findSharedLib.

바이너리로 의존성 내보내기

Objective-C framework 또는 네이티브 라이브러리(공유 또는 정적)를 빌드할 때 현재 프로젝트의 클래스뿐만 아니라 해당 의존성의 클래스도 패키징해야 할 수 있습니다. export 메서드를 사용하여 바이너리로 내보낼 의존성을 지정합니다.

kotlin {
sourceSets {
macosMain.dependencies {
// 내보내집니다.
api(project(":dependency"))
api("org.example:exported-library:1.0")
// 내보내지 않습니다.
api("org.example:not-exported-library:1.0")
}
}
macosX64("macos").binaries {
framework {
export(project(":dependency"))
export("org.example:exported-library:1.0")
}
sharedLib {
// 서로 다른 바이너리에 서로 다른 의존성 집합을 내보낼 수 있습니다.
export(project(':dependency'))
}
}
}

예를 들어, Kotlin에서 여러 모듈을 구현하고 Swift에서 해당 모듈에 액세스하려고 합니다. Swift 애플리케이션에서 여러 Kotlin/Native framework를 사용하는 것은 제한적이지만, umbrella framework를 생성하고 이러한 모든 모듈을 해당 framework로 내보낼 수 있습니다.

노트

해당 소스 세트의 api 의존성만 내보낼 수 있습니다.

의존성을 내보내면 해당 의존성의 모든 API가 framework API에 포함됩니다. 컴파일러는 해당 의존성의 코드 중 일부만 사용하더라도 해당 코드를 framework에 추가합니다. 이렇게 하면 내보낸 의존성(및 어느 정도까지는 해당 의존성의 의존성)에 대해 데드 코드 제거가 비활성화됩니다.

기본적으로 내보내기는 비전이적으로 작동합니다. 즉, 라이브러리 bar에 의존하는 라이브러리 foo를 내보내는 경우 foo의 메서드만 출력 framework에 추가됩니다.

transitiveExport 옵션을 사용하여 이 동작을 변경할 수 있습니다. true로 설정하면 라이브러리 bar의 선언도 내보내집니다.

주의

transitiveExport는 사용하지 않는 것이 좋습니다. 내보낸 의존성의 모든 전이적 의존성을 framework에 추가하기 때문입니다. 이렇게 하면 컴파일 시간과 바이너리 크기가 모두 늘어날 수 있습니다.

대부분의 경우 이러한 모든 의존성을 framework API에 추가할 필요가 없습니다. Swift 또는 Objective-C 코드에서 직접 액세스해야 하는 의존성에 대해 명시적으로 export를 사용하세요.

binaries {
framework {
export(project(":dependency"))
// 전이적으로 내보냅니다.
transitiveExport = true
}
}

유니버설 framework 빌드

기본적으로 Kotlin/Native에서 생성된 Objective-C framework는 하나의 플랫폼만 지원합니다. 그러나 lipo 도구를 사용하여 이러한 framework를 단일 유니버설(fat) 바이너리로 병합할 수 있습니다. 이 작업은 특히 32비트 및 64비트 iOS framework에 유용합니다. 이 경우 결과 유니버설 framework를 32비트 및 64비트 장치 모두에서 사용할 수 있습니다.

주의

fat framework는 초기 framework와 동일한 기본 이름을 가져야 합니다. 그렇지 않으면 오류가 발생합니다.

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

kotlin {
// 타겟을 생성하고 구성합니다.
val watchos32 = watchosArm32("watchos32")
val watchos64 = watchosArm64("watchos64")
configure(listOf(watchos32, watchos64)) {
binaries.framework {
baseName = "my_framework"
}
}
// fat framework를 빌드하는 작업을 생성합니다.
tasks.register<FatFrameworkTask>("debugFatFramework") {
// fat framework는 초기 framework와 동일한 기본 이름을 가져야 합니다.
baseName = "my_framework"
// 기본 대상 디렉터리는 "<빌드 디렉터리>/fat-framework"입니다.
destinationDir = buildDir.resolve("fat-framework/debug")
// 병합할 framework를 지정합니다.
from(
watchos32.binaries.getFramework("DEBUG"),
watchos64.binaries.getFramework("DEBUG")
)
}
}

XCFramework 빌드

모든 Kotlin Multiplatform 프로젝트는 XCFramework를 출력으로 사용하여 모든 대상 플랫폼 및 아키텍처에 대한 로직을 단일 번들에 모을 수 있습니다. 유니버설 (fat) framework와 달리 애플리케이션을 App Store에 게시하기 전에 불필요한 모든 아키텍처를 제거할 필요가 없습니다.

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

plugins {
kotlin("multiplatform") version "2.1.20"
}

kotlin {
val xcf = XCFramework()
val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64())

iosTargets.forEach {
it.binaries.framework {
baseName = "shared"
xcf.add(this)
}
}
}

XCFramework를 선언하면 Kotlin Gradle 플러그인이 여러 Gradle 작업을 등록합니다.

  • assembleXCFramework
  • assemble<Framework name>DebugXCFramework
  • assemble<Framework name>ReleaseXCFramework

프로젝트에서 CocoaPods 통합을 사용하는 경우 Kotlin CocoaPods Gradle 플러그인을 사용하여 XCFramework를 빌드할 수 있습니다. 여기에는 등록된 모든 타겟으로 XCFramework를 빌드하고 podspec 파일을 생성하는 다음 작업이 포함됩니다.

  • podPublishReleaseXCFramework: 릴리스 XCFramework와 함께 podspec 파일을 생성합니다.
  • podPublishDebugXCFramework: 디버그 XCFramework와 함께 podspec 파일을 생성합니다.
  • podPublishXCFramework: 디버그 및 릴리스 XCFramework를 모두 podspec 파일과 함께 생성합니다.

이를 통해 CocoaPods를 통해 모바일 앱과 별도로 프로젝트의 공유 부분을 배포할 수 있습니다. XCFramework를 사용하여 개인 또는 공용 podspec 리포지토리에 게시할 수도 있습니다.

주의

Kotlin framework를 서로 다른 버전의 Kotlin용으로 빌드한 경우 공용 리포지토리에 게시하는 것은 권장되지 않습니다. 이렇게 하면 최종 사용자의 프로젝트에서 충돌이 발생할 수 있습니다.

Info.plist 파일 사용자 지정

framework를 생성할 때 Kotlin/Native 컴파일러는 정보 속성 목록 파일 Info.plist를 생성합니다. 해당 바이너리 옵션을 사용하여 해당 속성을 사용자 지정할 수 있습니다.

속성바이너리 옵션
CFBundleIdentifierbundleId
CFBundleShortVersionStringbundleShortVersionString
CFBundleVersionbundleVersion

이 기능을 활성화하려면 -Xbinary=$option=$value 컴파일러 플래그를 전달하거나 특정 framework에 대해 binaryOption("option", "value") Gradle DSL을 설정합니다.

binaries {
framework {
binaryOption("bundleId", "com.example.app")
binaryOption("bundleVersion", "2")
}
}