skip to content

What is the cinterop tool in Kotlin/Native, and what role does a .def file play when binding to a C library?

level: juniorimportance: must knowfreq 55%

answer

  1. cinterop = C headers in, Kotlin .klib bindings out
  2. .def = package + headers + compilerOpts/linkerOpts
  3. Configured via cinterops { } in the native compilation
  4. Generated klib lands on the source set classpath
  5. Kotlin/Native only (not JVM/JS)

basics

~10 s

cinterop 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 s

cinterop 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
kotlin
// 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

for a junior

Knows cinterop turns C headers into Kotlin bindings and that a .def names headers and the library.

for a middle

Can write a minimal .def with package/headers/linkerOpts and wire the cinterops block in Gradle.

for a senior

Explains headerFilter, compilerOpts vs linkerOpts, static vs dynamic linking, and the klib-on-classpath flow.

for a principal

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

context