Quickest way to start would be to use the template.
For more involved examples, see a dedicated repository
Pre-requisites
The bindgen's binary is dynamically linked against Clang 14.
Therefore you must have it installed on the system that generates the code, you can follow Scala Native's instructions.
Code generated by bindgen has no runtime dependencies on bindgen, LLVM, or Clang.
Supported platforms
OS | Architecture | Tested on CI | Binary on Maven | Binary on Github releases | Comments |
---|---|---|---|---|---|
Linux | x64 | ✅ | ✅ | ✅ | |
Mac OS X | x64 | ✅ | ✅ | ✅ | |
Windows | x64 | ✅ | ✅ | ✅ | |
Mac OS X | M1 (arm64) | ❌¹ | ❌ | ✅² |
¹ Works with high confidence, M1 is my primary machine for developing this project ² SBT plugin will use that as a fallback (only for stable versions) |
Linux | arm64 | ❌ | ❌ | ❌ | Anecdotal evidence that it works, I run tests in an arm64 VM sometimes |
Windows | arm64 | ❌ | ❌ | ❌ | Anecdotal evidence that it works, I run tests in an arm64 VM sometimes |
Installing SBT plugin (stable)
project/plugins.sbt
addSbtPlugin("com.indoorvivants" % "bindgen-sbt-plugin" % "0.0.19")
Installing SBT plugin (latest)
project/plugins.sbt
resolvers += Resolver.sonatypeRepo("snapshots")
addSbtPlugin("com.indoorvivants" % "bindgen-sbt-plugin" % "0.0.20")
Usage
build.sbt
// only add this line if you're living on the edge and using
// a version that has "SNAPSHOT" in it
resolvers += Resolver.sonatypeRepo("snapshots")
scalaVersion := "3.2.2"
enablePlugins(ScalaNativePlugin, BindgenPlugin)
import bindgen.interface.Binding
bindgenBindings := Seq(
Binding
.builder(
/* 1 */ (Compile / resourceDirectory).value / "scala-native" / "header.h",
/* 2 */ "libtest"
)
.addCImport("header.h") /* 3 */
.build
)
)
- Path to the header file - in this example we're putting it into a location recognised by Scala Native's SBT plugin, any sources there will be linked alongside the Scala sources
- Package name where generated definitions will be put
- List of imports that will be added to generated C files (if there are any, see Semantics page for details)
There are more settings available, see Configuration page for details.
Now all that is left to do is put some definitions into the header file:
Source C
code
typedef enum { X = 1, Y = 2} bla;
void sum(bla one, bla two);
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object predef:
private[libtest] trait CEnumU[T](using eq: T =:= UInt):
given Tag[T] = Tag.UInt.asInstanceOf[Tag[T]]
extension (inline t: T)
inline def int: CInt = eq.apply(t).toInt
inline def uint: CUnsignedInt = eq.apply(t)
inline def value: CUnsignedInt = eq.apply(t)
object enumerations:
import predef.*
/**
* [bindgen] header: /tmp/10742935525184038292header.h
*/
opaque type bla = CUnsignedInt
object bla extends CEnumU[bla]:
given _tag: Tag[bla] = Tag.UInt
inline def define(inline a: Long): bla = a.toUInt
val X = define(1)
val Y = define(2)
inline def getName(inline value: bla): Option[String] =
inline value match
case X => Some("X")
case Y => Some("Y")
case _ => None
extension (a: bla)
inline def &(b: bla): bla = a & b
inline def |(b: bla): bla = a | b
inline def is(b: bla): Boolean = (a & b) == b
@extern
private[libtest] object extern_functions:
import _root_.libtest.enumerations.*
/**
* [bindgen] header: /tmp/10742935525184038292header.h
*/
def sum(one : bla, two : bla): Unit = extern
object functions:
import _root_.libtest.enumerations.*
import extern_functions.*
export extern_functions.*
object types:
export _root_.libtest.enumerations.*
object all:
export _root_.libtest.enumerations.bla
export _root_.libtest.functions.sum
After that in your Scala sources you can use libtest
package to access the
generated bindings.