Skip to content

Repository files navigation

C-Binding KMP Library Automation

Kotlin License Documentation

This project provides a unified and automated way to include and call native C code from Kotlin Multiplatform (KMP).



🎯 Goal

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).

✨ Key Features

  • 🚀 Generated Glue: JNI bridge C and Kotlin external fun bindings generated from your headers, with spec-correct symbol mangling.
  • 📦 Real Buffers: Zero-copy direct ByteBuffer marshalling plus primitive-array overloads (GetPrimitiveArrayCritical) — pass pixel/model buffers to C, get results back.
  • 🔩 Opaque Handles: typedef struct X X; handles cross the boundary as Long.
  • 🔤 Strings: const char* in and a char* out, int32_t cap out-buffer, as real UTF-8 (not JNI's modified UTF-8), behind generated String wrappers 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.

📁 Project Structure

  • 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.

🚀 Quick Start

1. Define your C function

// native/c/mylib.h
int add_numbers(int a, int b);

2. Configure the generator

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")
}

3. Build the project

./gradlew :shared:assemble

4. Call from Kotlin

import com.abyxcz.cbindingkmp.shared.generated.add_numbersJNI

val result = add_numbersJNI(10, 20)

For more details, see the Getting Started Guide.

📘 Documentation Index

📜 License

This project is licensed under the MIT License - see the LICENSE file for details.

About

C Bindings for Kotlin Multiplatform

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages