vend
is a dependency management tool for Common Lisp. The concept is simple:
Just vendor your dependencies!
vend
’s focus is first and foremost on simplicity.
vend get
to fetch dependencies directly into your project.vend repl
to open an isolated Lisp session.vend
only has 3 dependencies itself and compiles to a 300kb binary.- Neither Quicklisp (the tool) nor Quicklisp (the repository) are used.
- Trivial integration with Sly / Slime / Lem / Slimv.
It’s time for some peace of mind.
Fast internet connections and centralised repositories have gotten us used to the idea that dependencies are free; things we can pluck off a shelf and employ with no risk. In languages like Javascript and Rust, it’s not uncommon to have projects with several hundred dependencies.
But are these really free? Have you inspected each one? Do you know and trust
the authors, as well as the pipeline that feeds you updates? Is your project
still guaranteed to compile in 5-10 years with no extra intervention? Can you
write code on a plane? Can users reliably build your project after little more
than a git clone
?
The truth is that your dependencies are your code. And quite literally so - they might make up the majority of your final compiled artifact. By vendoring your dependencies directly into your project, you’re taking responsibility for its true form.
Dependency management in Common Lisp has traditionally centred around Quicklisp. A desire for access to more rapid package updates spawned Ultralisp. The need for version pinning and isolation birthed Qlot. The want for a better distribution system brought us OCICL.
But, could there be a simpler paradigm than just downloading the code and putting it right there?
With vend
:
- We need not perform bespoke installation scripts to get started.
- We need not wait for Quicklisp to update.
- We need not relegate all our systems to
~/common-lisp/
. - We need not worry about where ASDF is looking for systems.
- We need not fret over tools performing strange internal overrides.
- We need not manage extra config files or lockfiles.
Plus, vend
is actually an external tool with extra commands to help you inspect
and manage your dependencies.
For library development, you are encouraged to:
vend get
to fetch dependencies.- Add
vendored/*
to your.gitignore
.
As this allows downstream users the most freedom when consuming your library.
For application development, you are encouraged to:
vend get
to fetch dependencies.rm -rf vendored/**/.git/
- Actively commit
vendored/
to git.
By committing these dependencies directly, it is never a mystery to your users how your software should be provisioned.
In all cases, vend
requires ECL to build and run.
vend
’s dependencies are all vendored, so it is enough to run:
ecl --load build.lisp mv vend ~/.local/bin/
vend
is available on the AUR and can be installed with tools like Aura:
aura -A vend
If rough order of support / integration quality.
⚠ In all cases below, when starting a Lisp session, you must do so from the top-level of the repository. Doing this from the
.asd
file makes it easy.
Sly and Slime have variables for setting how Lisp REPLs should be launched:
(setq sly-default-lisp 'sbcl
sly-lisp-implementations '((sbcl ("vend" "repl" "sbcl") :coding-system utf-8-unix)
(ecl ("vend" "repl" "ecl") :coding-system utf-8-unix)
(abcl ("vend" "repl" "abcl") :coding-system utf-8-unix)
(clasp ("vend" "repl" "clasp") :coding-system utf-8-unix)))
Adjust as necessary for Slime.
Note that adding "--dynamic-space-size" "4GB"
to the sbcl
list is useful for
hungry projects like Trial.
As of 2025 January, you also need to manually disable sly-stepper
and
sly-quicklisp
or they will interfere with the REPL starting:
(package! sly-stepper :disable t)
(package! sly-quicklisp :disable t)
Lem is built and configured in Common Lisp and so offers excellent support for
it. To start a REPL with vend
:
C-u M-x slime <RET> vend repl
And all your local systems will be available for loading.
Slimv is a port of Slime from Emacs that utilises Slime’s Swank backend server for a very similar experience to Emacs. However, unlike Emacs which supports multiple running Lisps, Slimv requires one standalone server that persists through Vim restarts.
If we want our dependencies in vendored/
to be visible to Slimv, we must start
its server manually from our project directory:
> cd project/ > vend repl ecl --load /home/YOU/.vim/pack/common-lisp/start/slimv/slime/start-swank.lisp
Now, ,c
(REPL Connect) within Vim will automatically find the running server,
and you can load any system available in your project and in vendored/
.
If you want to switch projects, you would need to quit the REPL server manually and restart it as above. You may also wish to set a shell alias or create a wrapper script for the long invocation shown above.
From the top-level directory of your project, simply vend get
to fetch all
dependencies. They will be stored in vendored/
. From here, they are yours. You
are free to inspect, edit, and remove them as you please.
> vend get [vend] Downloading dependencies. [vend] Fetching FN-MACRO [vend] Fetching ARROW-MACROS [vend] Fetching TRANSDUCERS ... [vend] Done.
If during your usage of vend
you discover a project that fails to resolve,
please open an Issue.
From the top-level directory of your project, vend repl
opens a Lisp REPL while
instructing ASDF to only look within this directory tree for .asd
files.
> vend repl This is SBCL 2.4.9, an implementation of ANSI Common Lisp. > (asdf:load-system :transducers) ; Lots of compilation here. T >
By default, vend repl
starts SBCL. You can easily override this:
> vend repl ecl ECL (Embeddable Common-Lisp) 24.5.10 > (+ 1 1)
vend repl
actually accepts any number of arguments, which is useful for adding
additional settings for hungry projects like Trial:
> vend repl sbcl --dynamic-space-size 4GB
After running vend get
, you can inspect your full dependency graph via vend graph
:
> vend graph
This produces a deps.dot
file, which can be viewed directly with xdot
:
> xdot deps.dot
Or you can render it into a static PNG to send around to your friends to brag about how few dependencies you’re using:
> cat deps.dot | dot -Tpng -o deps.png
In the case of vend
, this produces an image like:
If the graph is too messy, you can “focus” it with an extra argument to vend graph
:
vend graph lem
In the case of the large Lem project, this would display a graph of only the core application and not its test suites, etc.
Since your dependencies are your code, you should care about what’s in there.
> vend check DYNAMIC-CLASSES is deprecated. PGLOADER -> CL-MARKDOWN -> DYNAMIC-CLASSES TRIVIAL-BACKTRACE is deprecated. PGLOADER -> TRIVIAL-BACKTRACE
Woops! And while Common Lisp has a culture of “done means done, not dead”, it’s still important to know what you’re getting yourself into.
Search the known systems via some term.
> vend search woo woo https://github.com/fukamachi/woo.git wookie https://github.com/orthecreedence/wookie.git
vend
does not cover all of what’s available on Quicklisp, but it does have
significant enough coverage to resolve and compile a number of large, modern
projects:
- Resolves: Does
vend get
complete? - Compiles: Does
(asdf:load-system :foo)
withinvend repl
complete? - Tests: Does
(asdf:test-system :foo)
(or similar) withinvend repl
pass?
Project | Resolves? | Compiles? | Tests? | Category | Notes |
---|---|---|---|---|---|
Alloy | ✅ | ✅ | ✅ | UI | |
April | ✅ | ✅ | - | Language | |
Clog | ✅ | ✅ | - | GUI | |
Coalton | ✅ | ✅ | ✅ | Language | |
GTK4 | ✅ | ✅ | - | GUI | |
Kandria | ✅ | ✅ | - | Game | |
Lem | ✅ | ❌ | - | Editor | Usage of package-inferred-system |
Lisp-stat | ✅ | ✅ | - | Math | |
McCLIM | ✅ | ✅ | ✅ | GUI | |
Mito | ✅ | ✅ | ✅ | Database | |
Nodgui | ✅ | ✅ | ✅ | GUI | |
Nyxt | ✅ | ✅ | ✅ | Browser | |
OCICL | ✅ | ✅ | - | Dev tool | |
Postmodern | ✅ | ✅ | ✅ | Database | |
Qlot | ❌ | - | - | Dev tool | Usage of package-inferred-system |
Quicklisp | ✅ | ❌ | - | Dev tool | System is unloadable: (1) (2) |
Radiance | ✅ | ✅ | ✅ | Web | |
Roswell | ✅ | ❌ | - | Dev tool | Requires quicklisp internally |
Trial | ✅ | ✅ | ✅ | Gamedev | trial-assets manual setup for demos |
Woo | ✅ | ✅ | ❌ | Web | Usage of package-inferred-system |
If during your usage of vend
you discover a project that fails to resolve,
please open an Issue.
vend repl
works with the following compilers:
Compiler | Status | Notes |
---|---|---|
SBCL | ✅ | |
ECL | ✅ | |
ABCL | ✅ | |
Clasp | ✅ | |
CCL | ✅ | |
Allegro | ✅ | |
LispWorks | ❓ |
Historical implementations are not considered.
No. Submodules need to be recloned by your users, which is a weaker long-term
guarantee than true vendoring. Submodules are also a pain in the neck to manage.
With vend
, if you want to change and make new commits to vendored dependencies,
you’re still free to do so.
Probably not. At least, vend
assumes that Quicklisp doesn’t exist, and it tells
ASDF to only look for systems in the current directory tree. It’s not clear what
a call to (ql:quickload ...)
would do in that case.
If you want new packages available to vend repl
, you can:
- Manually clone them into
vendored/
(discouraged). - Add them to your
.asd
explicitly and callvend get
.
Either way, it’s expected that you ensure that when a user freshly clones your
repository, runs vend get
, and then loads your system, everything should work.
The intent is that by vendoring, you’re taking responsibility for the “true shape” of your program. So, upgrading dependencies should always be a conscious choice, done for a specific reason. Therefore there is no “bulk update” button.
To update a single dependency, you can git pull
it specifically. If you’ve
already committed that dependency to your repo (as in the application case),
you’re still able to:
rm -rf vendored/old-dep vend get rm -rf vendored/old-dep/.git/
But you’re discouraged from doing this habitually.
See build.lisp for how vend
is built, which uses ECL. For SBCL, consider adding:
(sb-ext:save-lisp-and-die #p"foobar"
:toplevel #'foobar:launch
:executable t
:compression t)
vend
itself is built with ECL but you’re free to use it to manage Lisp projects
of any (modern) compiler. ECL typically produces very small binaries; in the
case of vend
it’s only a few hundred kilobytes, which eases distribution.