Use C inside Clojure, then run it on the JVM or compile a native binary.
- One source to rule them all — write code once, run it on both jvm and native binary
(ns clobits.examples.sdl.startup
(:require [clobits.native-interop :refer [*native-image*]])
(:gen-class))
(if *native-image*
(do (println "In native image context")
(require '[bindings.sdl_ni])
(import '[bindings sdl]))
(do (println "In polyglot context")
(require '[bindings.sdl-ns :as sdl])))
(defn -main [& args]
(sdl/init (sdl/get-sdl-init-video)))
For a more complete example, check out src/clobits/examples/sdl/startup.clj
- Parse C function prototypes to Clojure maps
- Generate .h/.c-files and Clojure namespaces based off of C function prototypes or using Clojure maps
This project is a work in progress. I wanted to share this mostly to add another example of how to use Clojure + C using GraalVM.
I'd be very happy if you tried to use it and have questions. If you have a specific C library you want to get working, let me know. Since the generated code can be reused, it'd be nice if we could host the bits required for various C libraries at clojars. :)
- Calling variadic C functions that do not have a
va_args
variation (see printf vs vprintf)- If the function has a
va_args
variant, I'd recommend just using that for now
- If the function has a
If you know how to solve the problems below, please tell me how! :) Either through an issue, or @Saikyun on twitter.
- graalvm -- tested with
graalvm-ce-java11-20.2.0-dev
: https://github.com/graalvm/graalvm-ce-dev-builds/releases- download, then add the following to e.g. .zprofile
export GRAALVM_HOME = "/path/to/graalvm-ce-java11-20.2.0-dev/Contents/Home/"
export JAVA_HOME = $GRAALVM_HOME
- install native-image:
$GRAALVM_HOME/bin/gu install native-image
(will be installed by./compile
otherwise) - llvm toolchain -- https://www.graalvm.org/docs/reference-manual/languages/llvm/
- export LLVM_TOOLCHAIN as in the instructions
- leiningen -- https://leiningen.org/
Tested on macos and linux.
git clone https://github.com/Saikyun/clobits
cd clobits
make clean bindings
Displays hello
for a second.
make clean ncurses-poly
make clean ncurses-ni
You need libSDL2 on your path, and possibly added to "-Djava.library.path=<LIB_PATH_HERE>"
under your OS profile in project.clj
. I've put some defaults there, but I'm not sure they're universal.
make clean sdl-poly
make clean sdl-ni
You should see a red square on a white background. Cmd+Q to exit on MacOS. You can also press the X.
If you don't run clean
when switching between poly / ni targets you can get complaints about not finding clojure.core/seq?
. I think this has to do with how leningen caches .class-files. Just run make clean ni / poly
and you'll be fine.
Running make clean bindings
generates the folder src/bindings
. This folder is included in the repo so that one can read the generated source when looking at github. As soon as you start using this library, you will overwrite these files. They should not be modified by hand.
In src/bindings
you'll find:
These are necessary for polyglot to be able to call native libs.
Clojure code that generates classes and interfaces to be with native-image
Clojure namespace that is used with polyglot.
Look at src/clobits/examples/startup.clj
to see how you can use the same code for both polyglot and native-image.
- sogaiu -- support, testing and helpful discussion
- cornerwings -- java + native image example: https://github.com/cornerwings/graal-native-interaction
- u/duhace -- java + polyglot example: https://www.reddit.com/r/java/comments/8s7sr8/interacting_with_c_using_graalvm/
- borkdude -- help, project setup for clojure / native image compilation: https://github.com/borkdude/clj-kondo
Copyright © 2020 Jona Ekenberg
Distributed under the EPL License. See LICENSE.
This project contains code from:
- Clojure, which is licensed under the same EPL License.
- clj-kondo, which is licensed under the same EPL License.