The usual way to develop programs in Common Lisp is to use
SLIME in Emacs, which starts an
implementation and provides a REPL. When a program needs to be running in
production, one can either execute it from source or compile it to an
executable core, for example with sb-ext:save-lisp-and-die
in SBCL.
While executable cores works well for conventional applications, they are less suitable for small scripts which should be easy to run without having to build anything.
Writing a basic script with SBCL is easy:
#!/bin/sh
#|
exec sbcl --script "$0" "$@"
|#
(format t "Hello world!~%")
Since UNIX shebangs cannot be used to run commands with more than one
argument, it is impossible to call SBCL directly (it requires the --script
argument, and #!/usr/bin/env sbcl --script
contains two arguments). However
it is possible to start as a simple shell script and just execute SBCL with
the right arguments. And since we can include any shell commands, it is
possible to support multiple Common Lisp implementations depending on the
environment.
This method works. But if your script has any dependency, configuring ASDF can be tricky. ASDF can pick up system directory paths from multiple places, and you do not want your program to depend on your development environment. If you run your script in a CI environment or a production system, you will not have access to your ASDF configuration and your systems.
Fortunately, ASDF makes it possible to manually configure the source registry
at runtime using asdf:initialize-source-registry
, giving you total control
on the places which will be used to find systems.
For example, if your Common Lisp systems happen to be stored in a systems
directory at the same level as your script, you can use the :here
directive:
#!/bin/sh
#|
exec sbcl --script "$0" "$@"
|#
(require 'asdf)
(asdf:initialize-source-registry
`(:source-registry :ignore-inherited-configuration
(:tree (:here "systems"))))
And if you store all your systems in a Git repository, you can use
submodules to include a
systems
directory in every project, making it simple to manage the systems
you need and their version. Additionally, anyone with an implementation
installed, SBCL in this example, can now execute these scripts without having
to install or configure anything. This is quite useful when you work with
people who do not know Common Lisp.
Of course, you can use the same method when building executables: just create a script whose only job is to setup ASDF, load your program, and dump an executable core. This way, you can make sure you control exactly which set of systems is used. And it can easily be run in a CI environment.