Compiling Your First Clojure Binary¶
You have Gloat installed. Now let's give it a Clojure program and turn that program into native executables with several different Clojure engines.
Along the way we'll compare their size and speed, use Gloat's pruning option, and send the same program to a web page as browser WebAssembly.
Time: about 20 minutes, plus the first GraalVM download and build
You'll need: the gloat command from the
first tutorial and a Unix-like shell
Write FizzBuzz¶
Create a file named fizzbuzz.clj:
(ns main.core)
(defn -main [& args]
(let [n (if (seq args)
(read-string (first args))
100)]
(doseq [x (map (fn [x] (cond (zero? (mod x 15)) "FizzBuzz"
(zero? (mod x 5)) "Buzz"
(zero? (mod x 3)) "Fizz"
:else x))
(range 1 (inc n)))]
(println x))))
This tutorial uses the main.core namespace.
A compiled program must have a -main function.
That is the function Gloat calls when the executable starts.
The & args parameter receives the command-line arguments.
This program reads the first one as the upper limit, or uses 100 when no
argument is supplied.
Compile a Native Binary¶
Gloat's default engine is Glojure.
Compile the program to a native executable named fizzbuzz-glj:
Now run it with 16 as the upper limit:
fizzbuzz-glj is a self-contained native executable.
It does not need Gloat, Go, Clojure, Java, or a JVM installed on the machine
where it runs.
Make It Smaller with -Xprune¶
The normal executable contains a broad Clojure runtime. FizzBuzz only uses a small part of that runtime, so Gloat can remove unused parts while it builds:
Run the pruned executable to confirm that its behavior is unchanged:
It prints the same output as fizzbuzz-glj, but the executable is much
smaller.
-Xprune is primarily a size optimization; always measure before drawing
conclusions about runtime speed.
Try the let-go Engines¶
Gloat offers two executable-producing engines based on let-go. The optional let-go tools are installed into Gloat's local cache the first time one of these commands needs them.
The lgvm engine bundles the program for let-go's bytecode VM:
The lglvm engine lowers functions to native Go where it can and falls back
to the VM for the rest:
Both executables should print the same FizzBuzz output as the Glojure builds.
Try the GraalVM Engine¶
Gloat can also drive GraalVM Native Image:
The first build installs GraalVM and Leiningen into Gloat's local cache, so it will take longer than later builds.
Gloat's GraalVM engine currently produces host-native Clojure executables
only.
It does not support cross-compilation, WebAssembly, or Gloat processing
extensions such as -Xprune.
Compare Size and Speed¶
Get the exact size of each executable in bytes:
For a simple runtime comparison, give each executable one warm-up run and then time it five times. Redirecting standard output keeps terminal rendering from dominating the measurement:
for binary in \
fizzbuzz-glj \
fizzbuzz-glj-prune \
fizzbuzz-lgvm \
fizzbuzz-lglvm \
fizzbuzz-graalvm
do
echo "== $binary =="
"./$binary" > /dev/null
for run in 1 2 3 4 5; do
time "./$binary" > /dev/null
done
done
Use the middle real result after sorting the five runs.
On a Linux x86-64 laptop with an Intel Core i7-1365U, Gloat 0.1.67,
Glojure 0.7.3, let-go 1.12.2, and GraalVM 25, the results were:
| Build | Size | Median runtime |
|---|---|---|
| Glojure | 18.9 MiB | 0.007s |
Glojure with -Xprune |
7.7 MiB | 0.004s |
let-go lgvm |
13.6 MiB | 0.005s |
let-go lglvm |
13.2 MiB | 0.005s |
| GraalVM Native Image | 27.2 MiB | 0.006s |
These are sample results, not performance promises. They measure this FizzBuzz implementation on one machine. Run the commands on your own system when the numbers matter.
The useful result here is not that one engine always wins. It is that Gloat gives you one interface for building and comparing several ways to run the same Clojure source.
Put FizzBuzz in a Web Page¶
The default Glojure engine can also compile the program for JavaScript-hosted
WebAssembly.
This command prunes the runtime, creates fizzbuzz.js and fizzbuzz.html,
passes 16 to -main, starts a local web server, and opens the page:
The browser page displays the same FizzBuzz output through 16. Press Ctrl-C in the terminal when you are finished with the local server.
Despite its .js filename, fizzbuzz.js is the WebAssembly binary.
The generated fizzbuzz.html contains the Go WebAssembly JavaScript runtime,
loads that binary, passes the arguments, and captures its output for the page.
What You Just Did¶
Starting with one small Clojure file, you produced:
- A native Glojure executable
- A much smaller pruned Glojure executable
- Two native let-go executables
- A GraalVM Native Image executable
- A browser-ready WebAssembly program and HTML page
See Getting Started for the complete output-format and engine reference, or return to the tutorial index for the next walkthrough.