Home News Notes About

Setting up a Common Lisp environment

See the relevant section of my dotfiles for the configuration files.

1. SBCL

I use SBCL as my Common Lisp implementation of choice. Source code and binaries can be found here; I usually prefer to let my package manager do the job:

sudo aptitude sbcl

As of [2020-04-19 Sun]:

sbcl --version

…results in…

SBCL 2.0.3.debian

…for me.

2. Quicklisp

Quicklisp is a library manager for Common Lisp.

  1. Download the install script.

    wget https://beta.quicklisp.org/quicklisp.lisp
    
  2. Load it.

    sbcl --load quicklisp.lisp
    
  3. Install Quicklisp. I prefer to install it in ~/.quicklisp.

    (quicklisp-quickstart:install :path ".quicklisp/")
    
  4. Notify SBCL about Quicklisp. Add this to your .sbclrc file:

    #-quicklisp
    (let ((quicklisp-init (merge-pathnames ".quicklisp/setup.lisp"
                                           (user-homedir-pathname))))
      (when (probe-file quicklisp-init)
        (load quicklisp-init)))
    

2.1. Optional

Run sbcl again to check everything is right by trying to download a package.

(ql:quickload "vecto")

Output:

To load "vecto":
  Load 1 ASDF system:
    asdf
  Install 5 Quicklisp releases:
    cl-vectors salza2 vecto zpb-ttf zpng
; Fetching #<URL "http://beta.quicklisp.org/archive/salza2/2013-07-20/salza2-2.0.9.tgz">
; 15.16KB
==================================================
15,525 bytes in 0.02 seconds (631.71KB/sec)
; Fetching #<URL "http://beta.quicklisp.org/archive/zpng/2015-04-07/zpng-1.2.2.tgz">
; 39.20KB
==================================================
40,141 bytes in 0.08 seconds (490.00KB/sec)
; Fetching #<URL "http://beta.quicklisp.org/archive/zpb-ttf/2013-07-20/zpb-ttf-1.0.3.tgz">
; 43.82KB
==================================================
44,869 bytes in 0.08 seconds (554.65KB/sec)
; Fetching #<URL "http://beta.quicklisp.org/archive/cl-vectors/2018-02-28/cl-vectors-20180228-git.tgz">
; 30.68KB
==================================================
31,415 bytes in 0.04 seconds (697.24KB/sec)
; Fetching #<URL "http://beta.quicklisp.org/archive/vecto/2017-12-27/vecto-1.5.tgz">
; 69.10KB
==================================================
70,758 bytes in 0.11 seconds (628.18KB/sec)
; Loading "vecto"
[package net.tuxee.aa]............................
[package net.tuxee.aa-bin]........................
[package net.tuxee.paths].........................
[package net.tuxee.vectors].......................
[package salza2]..................................
[package zpng]....................................
[package zpb-ttf].................................
[package vecto]........
("vecto")

3. ASDF

Another System Definition Facility. The following instructions are documented here, as of [2019-11-12 Tue].

  1. Create the following directory:

    ~/.config/common-lisp/source-registry.conf.d/

  2. Create a .conf file - I use my-asdf.conf. In this file, add a line like this one:

    (:tree "/home/alc/src/")
    

    You should find this file here.

    Since I keep my programming projects in /home/alc/src/, this will tell ASDF to recursively scan this directory looking for .asd files. Of course choose your own dir :)

    I wouldn't keep the .quicklisp directory in there. I didn't test it, but that might have a few funky effects.

3.1. Optional

Let's test things out by cloning a Lisp project managed with ASDF. I will use one of my projects for this test:

git clone https://github.com/alecigne/freecomm src/freecomm
sbcl

Then:

(ql:quickload :freecomm)

At this step the project dependencies should be downloaded:

* (ql:quickload "freecomm")
To load "freecomm":
  Load 1 ASDF system:
    freecomm
; Loading "freecomm"
..................................................
[package iterate].................................
[package cl-unicode]..............................
[package cl-unicode-names]........................
[package editor-hints.named-readtables]...........
[package editor-hints.named-readtables]...........
[package cl-interpol].............................
[package cl-csv]..................................
[package freecomm].
("freecomm")
(in-package :freecomm)

You should enter the freecomm package:

* (in-package :freecomm)
#<PACKAGE "FREECOMM">

4. SLIME

SLIME is the Superior Lisp Interaction Mode for Emacs.

  1. Install it.
    • Using use-package

      (use-package slime
        :config
        (when (eq system-type 'gnu/linux)
          (setq slime-contribs '(slime-fancy)
                slime-protocol-version 'ignore)
          (setq inferior-lisp-program "sbcl")))
      
    • Using the package manager directly

      Run M-x package-install RET slime RET. Don't forget to set your default Lisp by evaluating this expression:

      (setq inferior-lisp-program "sbcl")
      
  2. Launch it with M-x slime and start exploring.

5. Creating a Lisp project

Quickproject is pretty neat for creating a Common Lisp project from scratch.

  1. Create a project.

    (ql:quickload :quickproject)
    (quickproject:make-project #p"~/src/my-quickproject-test/")
    
  2. Load the project:

    (ql:quickload :my-quickproject-test)
    
  3. We will now create a small Common Lisp project that will be useful in the next section.

    In my-quickproject-test.lisp, add this main function:

    (defun main (argv)
      (declare (ignore argv))
      (write-line "Hello, world"))
    

    That's it!

6. Compilation

Buildapp is pretty cool.

  1. Install it.

    (ql:quickload :buildapp)
    
  2. Build buildapp itself.

    (buildapp:build-buildapp)
    

    The binary will be created in the current directory.

  3. We'll now build my-quickproject-test :) Change the buildapp directory to match yours; I use ~/bin/buildapp.

    ~/bin/buildapp --output my-quickproject-test \
                   --load-system my-quickproject-test \
                   --entry my-quickproject-test:main
    
  4. Run the program: ./my-quickproject-test. You should see:

    ❯ ./my-quickproject-test
    Hello, world
    

7. StumpWM

  1. Clone the StumpWM repository:

    git clone https://github.com/stumpwm/stumpwm
    
  2. Install the dependencies:

    (ql:quickload '("clx" "cl-ppcre" "alexandria" "swank"))
    
  3. Compile it:

    ./autogen.sh
    ./configure
    make
    
  4. I prefer to link to the executable:

    ln -s ~/src/stumpwm/stumpwm ~/bin/stumpwm
    
  5. Create ~/.xinitrc and add this line (point to your own executable or link):

    exec /home/alc/bin/stumpwm
    
  6. Run startx: it should work!

7.1. Optional

You can connect to the Lisp process that StumpWM uses from SLIME.

  1. Add this to your StumpWM init.lisp:

    (in-package :stumpwm)
    
    (require :swank)
    
    (swank-loader:init)
    
    (defcommand swank () ()
      (setf *top-level-error-action* :break)
      (swank:create-server :port 4005
                           :style swank:*communication-style*
                           :dont-close t))
    
    (swank)
    

    If you encounter an error when running StumpWM with startx, try setting SBCL_HOME. See this comment.

  2. Add this config to Emacs:

    (defun yourname-swank-listening-p ()
      (ignore-errors
        (let ((p (open-network-stream "SLIME Lisp Connection Test" nil "localhost" 4005)))
          (when p
            (delete-process p)
            t))))
    
    (defun yourname-swank-autoconnect (&rest args)
      (if (and (not (slime-connected-p))
               (yourname-swank-listening-p))
          (ignore-errors (slime-connect "localhost" 4005))))
    
    (yourname-swank-autoconnect))
    

    When you launch Emacs, if SLIME isn't already running and if a Swank connection is available, SLIME will connect to it. Then in the REPL, you can do this:

    (in-package :stumpwm)
    (message "hello world")
    

    The message should appear on the screen.