Definition file
Kotlin/Native enables you to consume C and Objective-C libraries, allowing you to use their functionality in Kotlin. A special tool called cinterop takes a C or an Objective-C library and generates the corresponding Kotlin bindings, so that the library's methods can be used in your Kotlin code as usual.
To generate these bindings, each library needs a definition file, usually with the same name as the library. This is a property file that describes exactly how the library should be consumed. See the full list of available properties.
Here's a general workflow when working with a project:
Create a
.def
file describing what to include in the bindings.Use the generated bindings in your Kotlin code.
Run the Kotlin/Native compiler to produce the final executable.
Create and configure a definition file
Let's create a definition file and generate bindings for a C library:
In your IDE, select the
src
folder and create a new directory with File | New | Directory.Name the new directory
nativeInterop/cinterop
.This is the default convention for
.def
file locations, but it can be overridden in thebuild.gradle.kts
file if you use a different location.Select the new subfolder and create a
png.def
file with File | New | File.Add the necessary properties:
headers = png.h headerFilter = png.h package = png compilerOpts.linux = -I/usr/include -I/usr/include/x86_64-linux-gnu linkerOpts.osx = -L/opt/local/lib -L/usr/local/opt/png/lib -lpng linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lpngheaders
is the list of header files to generate Kotlin stubs for. You can add multiple files to this entry, separating each with a space. In this case, it's onlypng.h
. The referenced files need to be available on the specified path (in this case, it's/usr/include/png
).headerFilter
shows what exactly is included. In C, all the headers are also included when one file references another one with the#include
directive. Sometimes it's not necessary, and you can add this parameter using glob patterns to make adjustments.You can use
headerFilter
if you don't want to fetch external dependencies (such as systemstdint.h
header) into the interop library. Also, it may be useful for library size optimization and fixing potential conflicts between the system and the provided Kotlin/Native compilation environment.If the behavior for a certain platform needs to be modified, you can use a format like
compilerOpts.osx
orcompilerOpts.linux
to provide platform-specific values to the options. In this case, they are macOS (the.osx
suffix) and Linux (the.linux
suffix). Parameters without a suffix are also possible (for example,linkerOpts=
) and applied to all platforms.
To generate bindings, synchronize the Gradle files by clicking Sync Now in the notification.
After the bindings generation, the IDE can use them as a proxy view of the native library.
Properties
Here's the full list of properties you can use in your definition file to adjust the content of the generated binaries. For more information, see the corresponding sections below.
Property | Description |
---|---|
The list of headers from a library to be included in the bindings. | |
The list of Clang modules from an Objective-C library to be included in the bindings. | |
| Specifies the language. C is used by default; change to |
Compiler options that the cinterop tool passes to the C compiler. | |
Linker options that the cinterop tool passes to the linker. | |
A space-separated list of function names that should be ignored. | |
| |
Experimental. Includes a static library into | |
Experimental. A space-separated list of directories where the cinterop tool searches for the library to be included in | |
| Package prefix for the generated Kotlin API. |
Filters headers by globs and includes only them when importing a library. | |
Excludes specific headers when importing a library and takes priority over | |
A space-separated list of enums that should be generated as Kotlin enums. | |
A space-separated list of enums that should be generated as integral values. | |
A space-separated list of functions whose | |
| By default, it's assumed that C functions have unique names. If several functions have the same name, only one is picked. However, you can change this by specifying these functions in |
Disables the compiler check that doesn't allow calling a non-designated Objective-C initializer as a | |
Wraps exceptions from Objective-C code into Kotlin exceptions with the | |
Adds a custom message, for example, to help users resolve linker errors. |
In addition to the list of properties, you can include custom declarations in your definition file.
Import headers
If a C library does not have a Clang module and instead consists of a set of headers, use the headers
property to specify headers that should be imported:
Filter headers by globs
You can filter headers by globs using filter properties from the .def
file. To include declarations from headers, use the headerFilter
property. If a header matches any of the globs, its declarations are included in the bindings.
The globs are applied to the header paths relative to the appropriate include path elements, for example, time.h
or curl/curl.h
. So if the library is usually included with #include <SomeLibrary/Header.h>
, you can probably filter headers with the following filter:
If headerFilter
is not provided, all the headers are included. However, we encourage you to use headerFilter
and specify the glob as precisely as possible. In this case, the generated library contains only the necessary declarations. It can help avoid various issues when upgrading Kotlin or tools in your development environment.
Exclude headers
To exclude specific headers, use the excludeFilter
property. It can be helpful to remove redundant or problematic headers and optimize compilation, as declarations from the specified headers are not included in the bindings:
Import modules
If an Objective-C library has a Clang module, use the modules
property to specify the module to be imported:
Pass compiler and linker options
Use the compilerOpts
property to pass options to the C compiler, which is used to analyze headers under the hood. To pass options to the linker, which is used to link final executables, use linkerOpts
. For example:
You can also specify target-specific options that apply only to a certain target:
With this configuration, headers are analyzed using -DBAR=bar -DFOO=foo1
on Linux and -DBAR=bar -DFOO=foo2
on macOS. Note that any definition file option can have both common and platform-specific parts.
Ignore specific functions
Use the excludedFunctions
property to specify a list of the function names that should be ignored. This can be useful if a function declared in the header isn't guaranteed to be callable, and it's difficult or impossible to determine this automatically. You can also use this property to work around a bug in the interop itself.
Include a static library
Sometimes it's more convenient to ship a static library with your product, rather than assume it is available within the user's environment. To include a static library into .klib
, use staticLibrary
and libraryPaths
properties:
When given the above snippet, the cinterop tool searches libfoo.a
in /opt/local/lib
and /usr/local/opt/curl/lib
, and if found, includes the library binary in the klib
.
When using a klib
like this in your program, the library is linked automatically.
Configure enums generation
Use the strictEnums
property to generate enums as Kotlin enums or nonStrictEnums
to generate them as integral values. If an enum is not included in either of these lists, it is generated based on heuristics.
Set up string conversion
Use the noStringConversion
property to disable automatic conversion of the const char*
function parameters as Kotlin String
s.
Allow calling a non-designated initializer
By default, the Kotlin/Native compiler doesn't allow calling a non-designated Objective-C initializer as a super()
constructor. This behavior can be inconvenient if the designated Objective-C initializers aren't marked properly in the library. To disable these compiler checks, use the disableDesignatedInitializerChecks
property.
Handle Objective-C exceptions
By default, the program crashes if Objective-C exceptions reach the Objective-C to Kotlin interop boundary and get to the Kotlin code.
To propagate Objective-C exceptions to Kotlin, enable wrapping with the foreignExceptionMode = objc-wrap
property. In this case, Objective-C exceptions are translated into Kotlin exceptions that get the ForeignException
type.
Help resolve linker errors
Linker errors might occur when a Kotlin library depends on C or Objective-C libraries, for example, using the CocoaPods integration. If dependent libraries aren't installed locally on the machine or configured explicitly in the project build script, the "Framework not found" error occurs.
If you're a library author, you can help your users resolve linker errors with custom messages. To do that, add a userSetupHint=message
property to your .def
file or pass the -Xuser-setup-hint
compiler option to cinterop
.
Add custom declarations
Sometimes it is required to add custom C declarations to the library before generating bindings (for example, for macros). Instead of creating an additional header file with these declarations, you can include them directly to the end of the .def
file, after a separating line, containing only the separator sequence ---
:
Note that this part of the .def
file is treated as part of the header file, so functions with the body should be declared as static
. The declarations are parsed after including the files from the headers
list.
Generate bindings using command line
In addition to the definition file, you can specify what to include in bindings by passing the corresponding properties as options in the cinterop
call.
Here's an example of the command that produces a png.klib
compiled library:
Note that the generated bindings are generally platform-specific, so if you are developing for multiple targets, the bindings need to be regenerated.
For host libraries that are not included in the sysroot search paths, headers may be needed.
For a typical Unix library with a configuration script, the
compilerOpts
will likely contain the output of a configuration script with the--cflags
option (maybe without exact paths).The output of a configuration script with
--libs
can be passed to thelinkerOpts
property.