How do you choose what a Kotlin/Native target produces — an executable, a shared library, or an Apple framework — using the Gradle binaries DSL?
answer
- binaries { executable / sharedLib / staticLib / framework }
- DEBUG vs RELEASE build types
- link<Type><Kind><Target> tasks
- framework: baseName, isStatic, export()
- framework is Apple-only
basics
~10 sInside the target's binaries {} block you call executable(), sharedLib(), staticLib(), or framework(). Each tells the compiler what kind of output to build for that platform.
solid answer
~40 sEach native target has a `binaries {}` configuration where you declare the output kind. `executable { entryPoint = "..." }` builds a runnable program; `sharedLib()` and `staticLib()` build C-callable dynamic/static libraries with a generated C header; `framework()` builds an Apple `.framework` for Swift/Objective-C. Each kind is also produced in two **build types**: `DEBUG` (unoptimized, assertions) and `RELEASE` (optimized). Gradle creates tasks accordingly, e.g. `linkReleaseExecutableLinuxX64` or `linkDebugFrameworkIosArm64`. For executables, `entryPoint` selects the `main` function; for frameworks you can set `baseName`, `isStatic`, and export dependent modules with `export(...)`. The output kind must match the target — a `framework()` only makes sense on Apple targets (macOS/iOS), while `mingwX64`/`linuxX64` typically produce executables or shared/static libs.
go deeper
Knows that targets produce executables and that there is a Gradle DSL to configure them.
Can name executable/sharedLib/staticLib/framework, set entryPoint, and knows DEBUG/RELEASE link tasks.
Understands framework export(), isStatic, header generation for C interop, and matching kind to platform.
Designs the output strategy across a KMP build (static vs dynamic framework, exported API surface, size/link trade-offs).
## The `binaries {}` DSL Every Kotlin/Native target exposes a `binaries {}` block inside the `kotlin {}` configuration. There you declare one or more **output kinds**: ```kotlin kotlin { linuxX64 { binaries { executable { entryPoint = "com.example.main" } sharedLib { baseName = "mylib" } // .so + C header staticLib() // .a + C header } } macosArm64 { binaries { framework { baseName = "Shared" isStatic = true } } } } ``` ## Output kinds - **`executable()`** — a runnable program. `entryPoint` names the fully qualified `main`. Useful for CLI tools and servers. - **`sharedLib()`** — a dynamic library (`.so`/`.dll`/`.dylib`) plus a generated **C header**, so C/C++ can call into Kotlin. - **`staticLib()`** — a static library (`.a`) plus a C header. - **`framework()`** — an **Apple framework** consumable from Swift/Objective-C. The core of Kotlin Multiplatform on iOS. ## Build types Each kind is built in two **build types**: - **`DEBUG`** — fast to build, unoptimized, assertions on. - **`RELEASE`** — optimized by LLVM, slower to build. Gradle generates link tasks combining build type + kind + target, e.g. `linkDebugExecutableLinuxX64`, `linkReleaseSharedLibraryLinuxX64`, `linkDebugFrameworkIosArm64`. ## Framework-specific options - **`baseName`** — the framework/library name. - **`isStatic`** — produce a static framework (no separate dylib to embed). - **`export(project(":other"))`** — re-export another module's public API into the framework's headers so it is visible to Swift. - **`embedBitcode`** existed historically but bitcode is deprecated on modern Apple toolchains. ## Matching kind to target The kind must be sensible for the platform: `framework()` is Apple-only; `linuxX64`/`mingwX64` produce executables or shared/static libs. Declaring a `framework()` on a non-Apple target is meaningless and will not be honored.
- How do you make another Gradle module's API visible in the generated framework's Swift headers?Use `export(project(":module"))` inside the `framework {}` block; transitive export needs `transitiveExport = true` or exporting each dependency.
- What is the difference between a shared and a static framework?A static framework (`isStatic = true`) links the Kotlin code directly into the app binary; a dynamic one ships a separate `.framework` that the app embeds and loads at runtime.
saying these in an interview costs you the question
- Thinking output kind is global rather than per-target in binaries {}
- Not knowing DEBUG vs RELEASE build types exist
- Believing executables are the only possible output
- Claiming framework() works on Linux/Windows targets
- Confusing export() with implementation dependency