This walks a real external consumer from an empty Kotlin/Native project to running Kotlin
that calls a C++ library — using the published K++ plugin (no includeBuild, no cloning the
K++ repo). If you want to read a finished version of exactly this, see
samples/minimal.
- JDK 21 to run the Gradle build.
- An LLVM/Clang 22 toolchain. K++'s front-end is self-hosted on Clang: it parses your headers
with a bundled Clang.
clang++must be onPATH(it locates Clang's builtin headers and also compiles the generated C++ wrapper). If your LLVM-22 install is not on the default path (e.g. an apt.llvm.org install under/usr/lib/llvm-22), see Pointing K++ at your LLVM below. - A C++ library to bind (headers + a
.a/.so), or just hand-write a small one as the sample does.
You do not need to build K++ from source: the krapper tool binary rides bundled inside the
published plugin jar; the plugin extracts and runs it itself. (The LLVM toolchain requirement
inside the K++ repository does not apply to a from-published consumer — it only needs LLVM present
at runtime, since the bundled krapper is LLVM-linked.)
settings.gradle.kts — the plugin resolves from the Gradle Plugin Portal / Maven Central:
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}build.gradle.kts:
plugins {
kotlin("multiplatform") version "2.4.0"
id("com.monkopedia.kplusplus.compiler") version "0.3.6"
}
repositories {
mavenCentral()
}In a multi-project build, apply this in whichever project owns the bindings; the plugin
does not need to be declared at the root alongside the Kotlin plugin. (It did under 0.3.4
only — the plugin's own runtime classes were shadowed by whatever the root buildscript
classloader owned, and the first kplusplusSync failed with
NoSuchMethodError: Job.invokeOnCompletion$default. 0.3.5 shades that runtime into the plugin
jar, which is the real fix; if you are stuck on 0.3.4, adding
id("com.monkopedia.kplusplus.compiler") apply false to the root plugins { } block works
around it. See #194.)
Add a kplusplus { } block pointing at the header(s) and library you want to bind:
kplusplus {
header("cpp/geometry.h") // the header(s) to bind
library("build/cpplib/libgeometry.a") // the native library to link
cppStandard = "c++17" // default is c++14
// headerDirectory("cpp/include") // extra -I roots for your headers, if any
// instantiate("std::vector<int>") // force template specializations you call
}Wire the native binary so it links against the system libstdc++ (the front-end parses against it),
build the .a, and add a main that calls the generated API. The complete, runnable version of
all of this is samples/minimal/build.gradle.kts — copy it
as your starting point.
./gradlew runReleaseExecutableKlinker
The first build is slow (it downloads the plugin + bundled tools, generates the bindings, then compiles and links the generated C++ wrapper); later runs are incremental.
The cpp front-end finds Clang's own builtin headers (stddef.h etc.) via clang++ on PATH.
Your library's headers under a versioned LLVM install (e.g. <clang/AST/...> under
/usr/lib/llvm-22/include) are on the default include search path only for a system install. If
your LLVM-22 is a side-by-side / apt.llvm.org install, the plugin needs to know where its headers
are.
By default the plugin auto-discovers this by running llvm-config --includedir on PATH (the same
probe the K++ repo's own build uses) and threads that directory into the parse. If llvm-config
is not on PATH, or points at the wrong install, override it — in order of precedence:
-
The DSL, in your
kplusplus { }block:kplusplus { // ... llvmConfig = "/usr/lib/llvm-22/bin/llvm-config" } -
Or the project property, on the command line / in
gradle.properties:./gradlew runReleaseExecutableKlinker -PllvmConfig=/usr/lib/llvm-22/bin/llvm-config
When a configured llvm-config is not an executable, or does not report a valid include directory,
the build fails immediately with a message telling you exactly what to set — rather than surfacing a
cryptic file not found deep in the parse. When llvm-config reports a default system path (e.g.
/usr/include), nothing extra is added: Clang already searches it.
samples/minimal— the smallest end-to-end binding, a working copy of this guide.samples/multiproject— the same thing as two Gradle projects, with the Kotlin plugin declared at the root.samples/v8— the heavyweight demo (binds and runs V8).- README — the
kplusplus { }DSL reference andfixup { }escape hatch. - docs/ARCHITECTURE.md — how the self-hosted front-end and generator fit together.