2022-02-17: SBCL Settings

I've been playing with my dotfiles again... in particular the ones for getting SBCL (the Lisp compiler of choice) up and running with various package managers and settings.

Obviously the *first* thing to do is to get it so that everything is debugable by default... having to go and up the optimization level manually is a drag and computers are fast:

(sb-ext:restrict-compiler-policy 'debug 3)

Oh and SBCL has a better prompt (well... a more Slime-y one) so we can turn that on too:

(ignore-errors (require 'sb-aclrepl))
(when (find-package 'sb-acl-repl)
  (push :aclrepl cl:*features*))

And then there's Quicklisp: the defacto package manager for most Lisp people. Normally you go and install it manually and add a couple of lines to your `.sbclrc` to load it... but lets automate that so it gets installed on first run instead!

A couple of macros to run programs (without resorting to libraries):

(defun run-program-with-exit-code (program &rest args)
  "Run the PROGRAM with ARGS, wait for it to finish then return its exit code."
  (let ((p (sb-ext:run-program program args :search t)))
    (sb-ext:process-wait p)
    (sb-ext:process-exit-code p)))

(defun run-programs-successfully (&rest script)
  "Run each program in the SCRIPT stopping if any return an exit code."
  (every (lambda (line)
           (zerop
            (apply #'run-program-with-exit-code line)))
         script))

Then if Quicklisp isn't already available download and install it... else load it.

#-quicklisp
(let* ((quicklisp-dir (merge-pathnames ".local/share/sbcl/quicklisp/"
                                       (user-homedir-pathname)))
       (quicklisp-init (merge-pathnames "setup.lisp" quicklisp-dir)))
  (unless (probe-file quicklisp-init)
    (format t "~&;; attempting to download quicklisp~%")
    (if (run-programs-successfully
         '("curl" "-O" "https://beta.quicklisp.org/quicklisp.lisp")
         '("curl" "-O" "https://beta.quicklisp.org/quicklisp.lisp.asc")
         '("curl" "-O" "https://beta.quicklisp.org/release-key.txt")
         '("gpg" "--import" "release-key.txt")
         '("gpg" "--verify" "quicklisp.lisp.asc" "quicklisp.lisp"))
        (if (probe-file #P"quicklisp.lisp")
            (progn
              (format t "~&;; attempting to install quicklisp~%")
              (if (run-programs-successfully
                   '("sbcl" "--non-interactive" "--eval"
                     (format nil "(quicklisp-quickstart:install :path \"~S\")" quicklisp-dir)))

                  (progn
                    (format t "~&;; quicklisp installed: restart sbcl~%")
                    (sb-ext:exit :abort t))
                (warn "failed to install quicklisp. Try installing manually")))
          (progn
            (warn "failed to download quicklisp: you should attempt to install it manually. See https://quicklisp.org/beta/")))
      (warn "Couldn't detect quicklisp and couldn't install it: you should attempt to install manually. See: https://quicklisp.org/beta/")))
  (when (probe-file quicklisp-init)
    (load quicklisp-init)))

I couldn't quite get SBCL to run and load the `quicklisp.lisp` installer itself (it'd complain about the `quicklisp-quickstart` package being missing) so I instead re-run SBCL and crash out this time. Sure it could be better but its quite neat and saves a job every time I change OS! Neat-o!