‘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:
- Load your project using the normal Quicklisp method,
i.e.
(ql:quickload)
. - 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)
. - Exit from Lisp.
- 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:
- Load Qlot.
- Load your project using Qlot’s method, i.e.
(qlot:quickload)
. - Export the manifest file using
(qlot:with-local-quicklisp (ql:write-asdf-manifest-file))
. - Exit from Lisp.
- 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)