4 Starting Lisps
dxtr edited this page 2026-10-16 14:31:15 +02:00

Starting Lisps

stift exec

stift exec sbcl
stift exec ccl
stift exec sbcl --non-interactive --eval '(asdf:test-system "hello")'

For SBCL and CCL, stift adds the options that skip the init files and load the build list's setup file before anything else: for SBCL after its runtime options, such as --dynamic-space-size, and before --script; for CCL first. Init files are skipped because Quicklisp, loaded from one, adds a search of its own to ASDF, and a system missing from the build list would then come from Quicklisp without a word.

stift gives the program the terminal, waits for it, and exits with its status. Ctrl-C reaches the program, which handles it, and stift waits on. On Windows the console sends Ctrl-C to every program attached to it, stift included; that stift waits on there is not tested yet.

In a workspace, stift exec in any member, or at the workspace's root, starts the Lisp with the workspace's build list, the members' checkouts included (see Workspaces).

The setup file

Before it writes the setup file, stift checks that the stiftfile holds a consistent build list, and that no two of its projects provide one system (see Choosing Commits).

stift writes the setup file into its cache before every run, named after the build list, so it never goes stale. It calls ASDF and UIOP only, and defines nothing of its own. Loading it:

  1. checks that each directory of the build list is there;
  2. checks that only ASDF's own searches find systems: an asdf:*central-registry* that holds something, or a search Quicklisp added, is an error, with a continue restart that removes them;
  3. checks that no system already loaded would now be found in another directory, since loaded code cannot be replaced;
  4. sets ASDF's source registry to the directories of the build list's projects that hold .asd files, and nothing else, leaving out hidden directories such as .git and .qlot, and those an :exclude names;
  5. sets an output translation for each directory, so that its fasls go to fasl/IMPLEMENTATION/KEY/PROJECT/ in the cache.

KEY is a hash of the build list. Sources are shared by every build list that has their commit; fasls only by identical build lists.

The setup file names each directory that holds a system, in the order in which ASDF prefers one of two .asd files of one name in a project: the shallower first. A system put in a directory that held none is found once stift writes the setup file again, at the next stift exec or stift setup.

When the build list holds a newer ASDF, ASDF upgrades itself at the first operation.

Other programs

Any other program gets the build list in the environment variables ASDF reads, so that a Lisp it starts finds the build list:

  • CL_SOURCE_REGISTRY and ASDF_OUTPUT_TRANSLATIONS;
  • STIFT_SETUP, the setup file's path.
stift exec make test

The init files of a Lisp such a program starts are the program's business.

From CCL on Windows

The stift executable is built with SBCL, and starts any program on every system. The library, stift:exec-program, run in CCL on Windows, does not do all of it, because CCL there gives a program no environment variables of its own and passes some arguments wrongly. stift says so rather than run the program otherwise:

  • it starts no program other than a Lisp, which would not get the build list;
  • it passes no empty argument, and none with two spaces or tabs in a row, which CCL writes as "", which Windows reads as a quote.

SLY

SLY starts its Lisp through stift as it would through qlot exec:

(setq sly-lisp-implementations
      '((sbcl ("stift" "exec" "sbcl"))
        (ccl ("stift" "exec" "ccl"))))

Start it from a file of the project, so that stift finds the stiftfile.

A Lisp that runs already

stift setup fetches what the build list lacks, writes the setup file and prints its path:

(load (uiop:run-program '("stift" "setup") :output '(:string :stripped t)))