What is the cinterop tool in Kotlin/Native, and what role does a .def file play when binding to a C library?
answer
- cinterop = C headers in, Kotlin .klib bindings out
- .def = package + headers + compilerOpts/linkerOpts
- Configured via cinterops { } in the native compilation
- Generated klib lands on the source set classpath
- Kotlin/Native only (not JVM/JS)
basics
~10 scinterop is a Kotlin/Native tool that reads C header files and produces Kotlin code so you can call the C library from Kotlin. A .def file tells it which headers and library to use.
solid answer
~30 scinterop is the Kotlin/Native command-line tool (driven through Gradle's cinterops block in the kotlin { } native target) that parses C/Objective-C headers and generates a Kotlin bindings library (a .klib). A .def file is the configuration: it names the package, the headers to include (headers = ...), and how to link (e.g. headerFilter, compilerOpts, linkerOpts, staticLibraries). Each C function/struct/enum becomes a Kotlin declaration. You don't write the bindings by hand; you point cinterop at the .def and Gradle wires the generated klib onto the source set's classpath so you can import and call it.
code
kotlin · 19 lines// libpng.def
// headers = png.h
// package = org.example.libpng
// linkerOpts = -lpng
// build.gradle.kts
kotlin {
linuxX64 {
compilations.getByName("main").cinterops {
val libpng by creating {
defFile("src/nativeInterop/cinterop/libpng.def")
}
}
}
}
// usage
import org.example.libpng.png_access_version_number
fun main() = println(png_access_version_number())go deeper
Knows cinterop turns C headers into Kotlin bindings and that a .def names headers and the library.
Can write a minimal .def with package/headers/linkerOpts and wire the cinterops block in Gradle.
Explains headerFilter, compilerOpts vs linkerOpts, static vs dynamic linking, and the klib-on-classpath flow.
Reasons about multi-target binding strategy, build reproducibility, and packaging native deps across CI/architectures.
## What cinterop is **cinterop** is a tool shipped with **Kotlin/Native** (the LLVM-backed Kotlin compiler that produces native binaries). Its job is **automatic binding generation**: you give it C (or Objective-C) headers, and it emits a **Kotlin library** (a `.klib`) full of Kotlin declarations that map onto the C API. You then `import` those declarations and call them like ordinary Kotlin. ## The .def file The **`.def` file** (definition file) is cinterop's configuration. It is a key/value text file. Common keys: - `headers` — the C header files to parse (e.g. `headers = png.h`). - `package` — the Kotlin package the generated declarations land in. - `headerFilter` — limits which transitively-included headers contribute declarations (avoids dragging in the whole system). - `compilerOpts` — flags passed to the C compiler front-end (e.g. `-I/path/to/include`). - `linkerOpts` — flags for the linker (e.g. `-lpng`, `-L/path/to/lib`). - `staticLibraries` / `libraryPaths` — bundle a static lib into the klib. ## Wiring it in Gradle You declare a cinterop in the native target: ```kotlin kotlin { macosArm64 { compilations.getByName("main") { cinterops { val libpng by creating { defFile(project.file("src/nativeInterop/cinterop/libpng.def")) packageName("org.example.libpng") } } } } } ``` Gradle runs cinterop, produces the klib, and adds it to the source set so `import org.example.libpng.*` works. ## What gets generated - C **functions** → Kotlin functions. - C **structs/unions** → Kotlin classes accessed through pointers (`CPointer<T>`). - C **enums** → Kotlin enums or constants. - C **typedefs**, macros (simple ones), and global variables → corresponding Kotlin entities. Because the C world is unmanaged, pointers, memory scopes (`memScoped`), and value types (`CValue`) appear in the generated API — but the starting point is always: **point cinterop at headers via a .def, get Kotlin bindings.**
- Can cinterop be used in a Kotlin/JVM module?No. cinterop and the generated klib are Kotlin/Native-only; on JVM you'd use JNI/JNA/the FFM API instead.
- Where do .def files conventionally live?Under src/nativeInterop/cinterop/ in the module, referenced by defFile(...) in the cinterops block.
A .def file is like an order form for a translator: you list which documents (headers) to translate and where to find the originals (lib paths); cinterop hands back the translated bindings.
saying these in an interview costs you the question
- Thinking cinterop works on the JVM target
- Claiming you write the Kotlin bindings by hand
- Confusing .def with a Kotlin source file
- Not knowing linkerOpts/compilerOpts go in the .def or Gradle config
- Saying cinterop runs at runtime rather than at build time