Building Common Lisp Executables

By Susam Pal on 10 May 2018

Since Common Lisp is a language standard (not an implementation) it is hard to provide a single set of instructions or guidelines that would work for all implementations. There are various implementations of Common Lisp that target native machine code, C code, bytecode, JVM, etc. So the build instructions, project structure, etc. depend on the target.

Using SBCL

Here is a minimal example that builds a Lisp program into a binary executable with SBCL:

(defun main ()
  (format t "hello, world~%"))
(sb-ext:save-lisp-and-die "hello" :executable t :toplevel #'main)

The SBCL-specific save-lisp-and-die function saves the Lisp process as a core image. The :executable t keyword argument includes the SBCL runtime in the image to ensure that the image is a standalone executable. This is why the executable for even a simple hello-world program tends to be quite large (30 MB to 50 MB)! The :toplevel argument specifies the function to run when the core file is run.

Here are some example commands to get you started:

$ cat hello.lisp
(defun main ()
  (format t "hello, world~%"))
(sb-ext:save-lisp-and-die "hello" :executable t :toplevel #'main)
$ sbcl --load hello.lisp
$ ./hello
hello, world

Moving Unportable Code to Command Line Argument

If you would rather not have SBCL specific code in the Lisp source code file, then you could move the sb-ext:save-lisp-and-die call out of your source file to the SBCL command invocation. The source code now looks like this:

(defun main ()
  (format t "hello, world~%"))

The shell commands now look like this:

$ cat hello.lisp
(defun main ()
  (format t "hello, world~%"))
$ sbcl --load hello.lisp --eval "(sb-ext:save-lisp-and-die \"hello\" :executable t :toplevel #'main)"
$ ./hello
hello, world

Using Buildapp

By the way, there is also Buildapp that provides a layer of abstraction for building executables from Lisp programs. It works with SBCL and CCL. It requires the toplevel function to be called with an argument though. Therefore the source code needs to be modified to the following:

(defun main (argv)
  (declare (ignore argv))
  (format t "hello, world~%"))

Then Buildapp can be invoked like this:

$ cat hello.lisp
(defun main (argv)
  (declare (ignore argv))
  (format t "hello, world~%"))
$ buildapp --load hello.lisp --entry main --output hello
;; loading file #P"/Users/susam/hello.lisp"
$ ./hello
hello, world
Comments | #lisp | #programming | #technology