I looked into the state of GraalVM and Clojure and wrote some small work-related scripts.
GraalVM Build Tools
- Downloaded GraalVM and set $GRAALVM_HOME
- Found two main libraries for building GraalVM CLIs - https://github.com/luchiniatwork/cambada#packaging-as-a-native-image and https://github.com/taylorwood/clj.native-image (or https://github.com/taylorwood/lein-native-image for the lein equivalent). I chose clj.native-image as it seemed more focused on doing one thing well.
- First tried its template example which was easy to follow.
- Then added a graalvm build to one of our small cli tools
- Found it to be pretty straightforward except for a stdout flushing issue that was trivial to fix
- Didn't run into this issue which can break clojure 1.10 projects
- On my machine, first build took 90s and subsequent builds took 30s
- Looked at what would be required to build a CI process for building graalvm executables. This would be important for us in order for the team to rely on a reproducible executable. I found some examples in the wild but didn't spike on this:
- For additional resources, see https://github.com/BrunoBonacci/graalvm-clojure and https://github.com/lread/clj-graal-docs
I looked through and tried a number of CLIs and found the following three to be the most useful.
https://github.com/borkdude/jet is a handy tool for slicing and dicing json or edn. It is similar in functionality to what jq provides for json but with possibly more power and a clojure syntax.
I found jet has a sweet spot for exploring and drilling into api responses. I was able to use a github api to print the PR for a commit and open it. Here is that script:
$ curl -H 'Accept: application/vnd.github.groot-preview+json' -s 'https://api.github.com/repos/stedolan/jq/commits/0fd0f0b00dc4c3df97286de241ab400cf2f8c97c/pulls' | jet --from json --keywordize --query 'first :html_url' |xargs open
https://github.com/taylorwood/clojurl is a curl-like tool that can make web requests. Unlike curl, the response come back as a data structure and can be easily manipulated with something like jet. This seems it could be useful when working with APIs.
https://github.com/borkdude/babashka is the most promising tool as it provides a graalvm speed interpreter for a subset of Clojure. This tool can be used for concise one-line scripts or more impressively for Clojure scripts to replace shell scripts. It supports a number of standard libraries including tools.cli for option parsing, can make https requests and comes with a repl.
In order to build something useful for work and to test how useful babashka is, I wrote this script which expands on the above one-liner with jet by providing authentication, error handling and git repository detection. For command usage see the readme. I found the development experience to be super helpful as I could try small things in the babashka repl as well as rerun the script at lightning speed.
These are graalvm based clis that look interesting:
- https://github.com/liquidz/dad - Small configuration/ops tool that lets you write Clojure. For some examples to try, see https://github.com/liquidz/dad/blob/master/test/resources/test_task/tasks.clj
- https://github.com/retrogradeorbit/bootleg - Fast html templating solution that supports hiccup, mustache, markdown and enlive
- https://github.com/kkinnear/zprint - Pretty printer
Graalvm tooling for Clojure has gotten noticeably better the past 12 months. Clojure community is starting to build some useful clis with graalvm. Writing fast Clojure clis is easy to do with clj.native-image. However the compile times are slow and distribution for a team takes additional effort. To overcome those pains you can use babashka for faster development cycles and an easier distribution story.