TopHome
<2024-03-16 Sat>techlisp

Building a CL project

Running and incrementally compiling a CL project is fine and all, but how do you create an actual project that can be compiled into an executable?

You need the following things:

  1. Your CL compiler. I assume SBCL for the rest of the post.
  2. Quicklisp up and running to install dependencies.
  3. ASDF to define you system and indicate what are the needed dependencies. It is important to understand that ASDF does not do any installation or loading (which is all quicklisp), but only the specification of your project.

Start with a system definition file that looks like the following:

(asdf:defsystem hw
  :version "0.0.0"
  :author "Chander Govindarajan"
  :serial T
  :components ((:file "hw"))
  :depends-on (:bordeaux-threads)
  :build-operation "program-op"
  :build-pathname "hw"
  :entry-point "hw:hw-main")

Notice how we specify the components (ie files, without their .lisp extension). So, obviously for this to work we need a file called "hw.lisp" in the top-level. I have added an external package "bordeaux-threads" as an example dependency, which asdf won't install. Good thing is that you don't have to install it either manually, as we will see below.

Now, the file "hw.lisp" would look like the following:

(defpackage hw
  (:use :cl)
  (:export "HW-MAIN"))

(in-package :hw)

(defun hw-main ()
  (format t "Hello from CL.~%"))

Specifically:

  1. We need to define a package here and specify "use" and "exports". Every package "use"ed will be imported in as is, so you don't have to specify symbols using "packagename:symbol" format and directly use "symbol". You export the functions that need to be public.
  2. You specify "in-package" to indicate that the rest of file is inside this package. You can have packages span multiple files, by including just the "in-package" portion in the other files.

For example, if you have a "utils.lisp":

(in-package :hw)

(defun do-something ()
    (+ 1 2)) ; do something useful here

you just need to include this file in your asd definition.

Armed with this knowledge, you can look at existing CL projects and examine their asdf defintions. For example, of alloy.

Now, to actually compile this, use a Makefile that looks like the following:

build:
        sbcl --eval '(asdf:load-asd "hw.asd")' --eval '(ql:quickload :hw)' --eval '(asdf:make :hw)' --eval '(quit)'

What is going on here?

  1. Load the system definition from the local file.
  2. Quickload your package, which will then look at dependencies and download them.
  3. Make your package with asdf. The last 3 lines of the ".asd" file correspond to the configuration for this build process.

This will leave you with a "hw" executable which you can run as normal.

See also:

  1. https://lispcookbook.github.io/cl-cookbook/getting-started.html
  2. https://lispcookbook.github.io/cl-cookbook/packages.html
  3. https://lispcookbook.github.io/cl-cookbook/systems.html
  4. https://lispcookbook.github.io/cl-cookbook/scripting.html