This project provides a unified and automated way to include and call native C code from Kotlin Multiplatform (KMP).
The goal is to allow developers to maintain a single C/C++ codebase (e.g., src/native/c) and automatically wire it up to:
- Android: Via JNI (Java Native Interface) using CMake.
- iOS / Native: Via Kotlin/Native CInterop.
- Desktop (JVM): Via JNI (shared library).
- 🚀 Generated Glue: JNI bridge C and Kotlin
external funbindings generated from your headers, with spec-correct symbol mangling. - 📦 Real Buffers: Zero-copy direct
ByteBuffermarshalling plus primitive-array overloads (GetPrimitiveArrayCritical) — pass pixel/model buffers to C, get results back. - 🔩 Opaque Handles:
typedef struct X X;handles cross the boundary asLong. - 🔤 Strings:
const char*in and achar* out, int32_t capout-buffer, as real UTF-8 (not JNI's modified UTF-8), behind generatedStringwrappers that size the buffer for you. Streaming by polling; see docs/supported-c-subset.md. - 🍏 iOS Static Linking: per-target static archives via
staticLibraries. - 📥 Prebuilt runtimes (
prebuilt {}): declare a third-party runtime once (an xcframework for iOS, an AAR or source archive for Android). The plugin fetches it, verifies its sha256, caches it, and wires cinterop include dirs, linker flags, CMake variables and task order. See docs/ios-prebuilt-linking.md. - ⚙️
cbinding {}DSL: configure headers, include names, JNI package and output; source sets are wired automatically.
The generator supports a documented C subset (scalars, primitive pointers, opaque handles, UTF-8 strings) and fails loudly on anything else — see docs/supported-c-subset.md.
native/c: Contains your C source code (mylib.c,mylib.h).shared: The KMP library, wired to use the generated bindings.plugin: The Gradle plugin module containing the automation logic.docs: Full documentation set for users and developers.
// native/c/mylib.h
int add_numbers(int a, int b);The plugin is published to GitHub Packages. Add the repository to pluginManagement
(reading GitHub Packages needs a token with read:packages, via GPR_USER/GPR_KEY
or gpr.user/gpr.key in ~/.gradle/gradle.properties):
// settings.gradle.kts
pluginManagement {
repositories {
maven {
url = uri("https://maven.pkg.github.com/tjmtic/CBindingKMP")
credentials {
username = System.getenv("GPR_USER") ?: providers.gradleProperty("gpr.user").orNull
password = System.getenv("GPR_KEY") ?: providers.gradleProperty("gpr.key").orNull
}
}
gradlePluginPortal()
mavenCentral()
}
}Working on the plugin itself? includeBuild("../CBindingKMP/plugin") inside pluginManagement
substitutes a sibling checkout instead.
// build.gradle.kts
plugins { id("com.abyxcz.cbinding") version "1.3.0" }
cbinding {
headersDir.set(file("native/c"))
includeHeaders.set(listOf("mylib.h"))
jniPackage.set("com.example.generated")
}./gradlew :shared:assembleimport com.abyxcz.cbindingkmp.shared.generated.add_numbersJNI
val result = add_numbersJNI(10, 20)For more details, see the Getting Started Guide.
- Introduction
- Getting Started
- Developer Guide
- Architecture & Automation Logic
- API Reference
- Contributing
This project is licensed under the MIT License - see the LICENSE file for details.