This is what I do to solve this problem. It may not be exactly what you need, but there may be ideas here that can help.
For development
Make sure there is only one copy of the system you're trying to build. So, in particular make sure there's no installed copy which ASDF might find instead: see below.
Then what will work is this. Firstly make sure your ASDF system definition is cold-loadable, so in particular make sure it has the right (in-package :asdf-user)
at the top.
Then what will work to build your system is:
$ cd .../my-project
$ sbcl
[...]
* (require :asdf) ;already loaded in my case by init files
nil
* (load "my-project.asd")
t
* (asdf:load-system "my-project")
; compiling file "/media/psf/share/tmp/my-project/package.lisp" (written 15 DEC 2020 09:06:54 AM):
; processing (defpackage :com.example ...)
[...]
*
And now you're good. So the three tricks I do are:
- avoid thinking about the whole source registry hair at all, because if you think about it too much something with tentacles will tear your face off (I know this, it happened to me, I now have no face);
- make sure there is only one copy of the system so ASDF can't use the source registry hair I have avoided thinking about to find the wrong one;
- explicitly load the system definition file – wherever else ASDF is looking it will at least look in the same directory as that.
For production
The answer to this is Quicklisp. Do whatever is needed to make Quicklisp be installed in your Lisp. Then you need to know where its install directory is: there is some default but I never use that since I have my own notions of what the filesystem should look like.
Then the trick is that Quicklisp will find, build and load systems under its local-projects
directory (Quicklisp is made entirely of competence and magic as far as I can tell). So if you put a system there, then Quicklisp will simply and elegantly deal with getting it into the running image.
To do this installation ... I have makefiles. I know, I should use Lisp tools, but I live on *nix platforms and make
and install
are good at the whole copying-files bit.
A relevant chunk of Makefile
(actually this is really all of it) is:
# Where Quicklisp lives, and the name of this project using slashes
QUICKLISP = /local/tfb/packages/quicklisp
THIS = org/tfeb/sample
FASLS = *fasl *fsl
.PHONY: install uninstall clean
# To install the project make its directory, copy all the sources and
# the sysdcl into it, and then nuke Quicklisp's cache file so it searches
# next time
#
install:
@mkdir -p "$(QUICKLISP)/local-projects/$(THIS)"
@cd "$(QUICKLISP)/local-projects/$(THIS)" && rm -f $(FASLS)
@install -C -v -m 444 *.lisp *.asd "$(QUICKLISP)/local-projects/$(THIS)"
@rm -f "$(QUICKLISP)/local-projects/system-index.txt"
# To uninstall the project just nuke the directory and the cache
#
uninstall:
@rm -rf "$(QUICKLISP)/local-projects/$(THIS)"
@rm -f "$(QUICKLISP)/local-projects/system-index.txt"
clean:
@rm -f $(FASLS) *~
There are four interesting things here:
- I'm just using
make
as a file-copying machine – it's not compiling anything or anything like that, and it would be perfectly possible to use a script;
- you need to blow away Quicklisp's cache file so it searches again on start;
- I disable ASDF's output translations which is why I spend time blowing away the compiled files – after an install the project always should be rebuilt from cold;
- the
uninstall
target is what you need to run before development – it will nuke the installed version so ASDF doesn't find it.
Once you've run a suitable make install
in your project's directory, then (ql:quickload :org.tfeb.sample)
will just compile and load it for you.
Note that an alternative approach (suggested by Ehvince in a comment) would be to leave a symbolic link under Quicklisp's local-projects
directory back to the canonical version of the code. I don't do that but it would work fine, and might be better in some ways.