‘How do I create a Lisp application binary without including Quicklisp or Qlot?’

Quicklisp helps you work faster because it knows where to find libraries and Qlot keeps you mostly out of dependency hell. Without these two tools application development would be dead slow. However, applications distributed as standalone binaries should not contain them.

Quicklisp adds unnecessary code and it will likely try to create a ~/quicklisp directory on your user’s machine. This can fail for any number of reasons (such as file permissions or lack of internet access) which will only cause headaches.

Qlot also adds unnecessary code and it will cause the same problems as Quicklisp.

The process of building a binary using Buildapp and Quicklisp without actually having them inside the final application is as follows:

  1. Load your project using the normal Quicklisp method, i.e. (ql:quickload).
  2. Export a manifest file which lists the absolute path to every ASDF system currently loaded into the Lisp image. This is done using (ql:write-asdf-manifest-file).
  3. Exit from Lisp.
  4. Use Buildapp to create your binary. The manifest file must be passed as a parameter, --load-system must be used to load your system and Quicklisp must NOT be loaded.

@xach explained the procedure in this Stack Overflow answer.

When Qlot is employed in a project, the above process becomes:

  1. Load Qlot.
  2. Load your project using Qlot’s method, i.e. (qlot:quickload).
  3. Export the manifest file using (qlot:with-local-quicklisp (ql:write-asdf-manifest-file)).
  4. Exit from Lisp.
  5. Use Buildapp to create your binary.

Since Quicklisp is not aware of Qlot (ql:write-asdf-manifest) must be wrapped with (qlot:with-local-quicklisp) in order to generate paths to the project-local Qlot managed libraries instead of the system wide Quicklisp managed libraries.

Example

This is an excerpt of a makefile from a project which uses Qlot. The makefile is used to create a standalone binary that does not contain Quicklisp or Qlot. The prep-quicklisp.lisp script is used to setup Quicklisp for loading the project system from an arbitrary location.

# CCL Flags for manifest build
MANIFEST_FLAGS =  --no-init 
MANIFEST_FLAGS += --batch 
MANIFEST_FLAGS += --load prep-quicklisp.lisp
MANIFEST_FLAGS += --eval '(ql:quickload :qlot)'
MANIFEST_FLAGS += --eval '(qlot:install :$(QL_SYSTEM))'
MANIFEST_FLAGS += --eval '(qlot:quickload :$(QL_SYSTEM))'
MANIFEST_FLAGS += --eval '(qlot:with-local-quicklisp :$(QL_SYSTEM) (ql:write-asdf-manifest-file \#P"$(MANIFEST)" :if-exists :supersede :exclude-local-projects nil))'
MANIFEST_FLAGS += --eval '(quit)'

# Buildapp settings
B_FLAGS =  --output $(OUTDIR)/$(TARGET)
B_FLAGS += --manifest-file $(MANIFEST)
B_FLAGS += --load-system $(QL_SYSTEM)
B_FLAGS += --entry app:main

make_manifest: 
	$(LISP) $(MANIFEST_FLAGS)

make_app: 
	$(BUILDAPP) $(B_FLAGS)