2 #+TITLE: ~runlisp~ -- run scripts written in Common Lisp
4 #+LaTeX_CLASS: strayman
5 #+LaTeX_HEADER: \usepackage{tikz, gnuplot-lua-tikz}
6 #+LaTeX_HEADER: \DeclareUnicodeCharacter{200B}{}
7 #+EXPORT_FILE_NAME: doc/README.pdf
9 ~runlisp~ is a small C program intended to be run from a script ~#!~
10 line. It selects and invokes a Common Lisp implementation, so as to run
11 the script. In this sense, ~runlisp~ is a partial replacement for
14 Currently, the following Lisp implementations are supported:
16 + Armed Bear Common Lisp (~abcl~),
17 + Clozure Common Lisp (~ccl~),
18 + GNU CLisp (~clisp~),
19 + Carnegie--Mellon Univerity Common Lisp (~cmucl~),
20 + Embeddable Common Lisp (~ecl~), and
21 + Steel Bank Common Lisp (~sbcl~).
23 Adding more Lisps is simply a matter of writing the necessary runes in a
24 configuration file. Of course, there's a benefit to having a collection
25 of high-quality configuration runes curated centrally, so I'm happy to
26 accept submissions in support of any free[fn:free] Lisp implementations.
28 [fn:free] Here I mean `free' as in freedom: see
29 https://www.fsf.org/about/what-is-free-software.
32 * Writing scripts in Common Lisp
36 The obvious way to use ~runlisp~ is in a shebang (~#!~) line at the top
37 of a script. For example:
39 : #! /usr/local/bin/runlisp
40 : (format t "Hello from Lisp!~%")
42 Script interpreters must be named with absolute pathnames in shebang
43 lines; if your ~runlisp~ is installed somewhere other than
44 ~/usr/local/bin/~ then you'll need to write something different.
45 Alternatively, a common hack involves abusing the ~env~ program as a
46 script interpreter, because it will do a path search for the program
49 : #! /usr/bin/env runlisp
50 : (format t "Hello from Lisp!~%")
54 Lisp implementations are not created equal -- for good reason. If your
55 script depends on the features of some particular Lisp implementation,
56 then you can tell ~runlisp~ that it must use that implementation to run
57 your script using the ~-L~ option; for example:
59 : #! /usr/local/bin/runlisp -Lsbcl
60 : (format t "Hello from Steel Bank Common Lisp!~%")
62 If your script supports several Lisps, but not all, then list them all
63 in the ~-L~ option, separated by commas:
65 : #! /usr/local/bin/runlisp -Lsbcl,ccl
66 : (format t #.(concatenate 'string
70 : #-(or sbcl ccl) "an unexpected"
73 It is not an error to include the name of an unrecognized Lisp system in
74 the ~-L~ option: such names are simply ignored. This allows a script to
75 declare support for unusual or locally installed Lisp systems without
76 compromising its portability to sites where such systems are unknown, or
77 which are still running older versions of ~runlisp~ which haven't been
78 updated with the necessary configuration for those systems.
82 If your script requires features of particular Lisp implementations
83 /and/ you don't want to hardcode an absolute path to ~runlisp~, then you
84 have a problem. Most Unix-like operating systems will parse a shebang
85 line into the initial ~#!~, the pathname to the interpreter program,
86 and a /single/ optional argument: any further spaces don't separate
87 further arguments: they just get included in the first argument, all the
88 way up to the end of the line. So
90 : #! /usr/bin/env runlisp -Lsbcl
91 : (format t "Hello from Steel Bank Common Lisp!~%")
93 won't work: it'll just try to run a program named ~runlisp -Lsbcl~, with
94 a space in the middle of its name, and that's quite unlikely to exist.
96 To help with this situation, ~runlisp~ reads /embedded options/ from
97 your script. Specifically, if the script's second line contains the
98 token ~@RUNLISP:~ then ~runlisp~ will parse additional options from this
99 line. So the following will work properly.
101 : #! /usr/bin/env runlisp
102 : ;;; @RUNLISP: -Lsbcl
103 : (format t "Hello from Steel Bank Common Lisp!~%")
105 Embedded options are split at spaces properly. Spaces can be escaped or
106 quoted in (an approximation to) the usual shell manner, should that be
107 necessary. See the manpage for the gory details.
109 ** Common environment
111 ~runlisp~ puts some effort into making sure that Lisp scripts get the
112 same view of the world regardless of which implementation is running
117 + The ~asdf~ and ~uiop~ systems are loaded and ready for use.
119 + The script's command-line arguments are available in
120 ~uiop:*command-line-arguments*~. Its name can be found by calling
121 ~(uiop:argv0)~ -- though it's probably also in ~*load-pathname*~.
123 + The prevailing Unix standard input, output, and error files are
124 available through the Lisp ~*standard-input*~, ~*standard-output*~,
125 and ~*error-ouptut*~ streams, respectively. (This is, alas, not a
126 foregone conclusion.)
128 + The keyword ~:runlisp-script~ is added to the ~*features*~ list.
129 This means that your script can tell whether it's being run from the
130 command line, and should therefore do its thing and then quit; or
131 merely being loaded into a Lisp system, e.g., for debugging or
132 development, and should sit still and not do anything until it's
135 See the manual for the complete list of guarantees.
138 * Invoking Lisp implementations
142 A secondary use of ~runlisp~ is in build scripts for Lisp programs. If
143 the entire project is just a Lisp library, then it's possibly acceptable
144 to just provide an ASDF system definition and expect users to type
145 ~(asdf:load-system "mumble")~ to use it. If it's a program, or there
146 are things other than Lisp which ASDF can't or shouldn't handle --
147 significant pieces in other languages, or a Lisp executable image to
148 make and install -- then it seems sensible to make the project's main
149 build system be something language-agnostic, say Unix ~make~, and
150 arrange for that to invoke ASDF at the appropriate time.
152 But how should that be arranged? It's relatively easy for a project'
153 Lisp code to support multiple Lisp implementation; but each
154 implementation wants different runes for evaluating Lisp forms from the
155 command line, and some of them don't provide an ideal environment for
156 integrating into a build system. So ~runlisp~ provides a simple common
157 command-line interface for evaluating Lisp forms. For example:
159 : $ runlisp -e '(format t "~A~%" (+ 1 2))'
162 If your build script needs to get information out of Lisp, then wrapping
163 ~format~, or even ~princ~, around forms is annoying; so ~runlisp~ has a
164 ~-p~ option which prints the values of the forms it evaluates.
166 : $ runlisp -e '(+ 1 2)'
169 If a form produces multiple values, then ~-p~ will print all of them, as
170 if by ~princ~, separated by spaces, on a single line:
172 : $ runlisp -p '(floor 5 2)'
175 There's also a ~-d~ option, which does the same thing as ~-p~, only it
176 prints values as if by ~prin1~. For example,
178 : $ runlisp -p '"Hello, world!"'
180 : runlisp -d '"Hello, world!"'
183 In addition to evaluating forms with ~-e~, and printing their values
184 with ~-d~ and ~-p~, you can also load a file of Lisp code using ~-l~.
186 When ~runlisp~ is acting on ~-e~, ~-p~, and/or ~-l~ options, it's said
187 to be running in /eval/ mode, rather than its usual /script/ mode. In
188 eval mode, it /doesn't/ set ~:runlisp-script~ in ~*features*~.
190 You can still insist that ~runlisp~ use a particular Lisp
191 implementation, or one of a subset of implementations, using the ~-L~
192 option mentioned above.
194 : $ runlisp -Lsbcl -p "(lisp-implementation-type)"
197 ** Command-line processing
199 When scripting a Lisp -- as opposed to running a Lisp script -- it's not
200 necessarily the case that your script knows in advance exactly what it
201 needs to ask Lisp to do. For example, it might need to tell Lisp to
202 install a program in a particular directory, determined by Autoconf.
203 While it's certainly /possible/ to quote such data and splice them into
204 Lisp forms, it's more convenient to pass them in separately. So
205 ~runlisp~ ensures that the command-line options are available to Lisp
206 forms via ~uiop:*command-line-arguments*~, as they are to a Lisp script.
208 : $ runlisp -p "uiop:*command-line-arguments*" one two three
209 : ("one" "two" "three")
211 When running Lisp forms like this, ~(uiop:argv0)~ isn't very
212 meaningful. (Currently, it reveals the name of the script which
213 ~runlisp~ uses to implement this feature.)
216 * Configuring =runlisp=
218 ** Where =runlisp= looks for configuration
220 You can influence which Lisp implementations are chosen by ~runlisp~ by
221 writing configuration files, and/or setting environment variables.
223 The ~runlisp~ program looks for configuration in a number of places.
225 + There's a system-global directory ~SYSCONFDIR/runlisp/runlisp.d/~.
226 All of the files in this directory named ~SOMETHING.conf~ are read,
227 in increasing lexicographical order by name. The package comes with
228 a file ~0base.conf~ intended to be read first, so that it can be
229 overridden if necessar. This sets up basic definitions, and defines
230 the necessary runes for those Lisp implementations which are
231 supported `out of the box'. New Lisp packages might come with
232 additional files to drop into this directory.
234 + There's a system-global file ~SYSCONFDIR/runlisp/runlisp.conf~ which
235 is intended to be edited by the system administrator to account for
236 any local quirks. This is read /after/ the directory, which is
237 intended to be used by distribution packages, so that the system
238 administrator can override them.
240 + Users can create files ~$HOME/.runlisp.conf~ and/or
241 ~$HOME/.config/runlisp.conf~[fn:xdg-config] in their home
242 directories to add support for privately installed Lisp systems, or
243 to override settings made by earlier configuration files.
245 But configuration files generally look like =.ini=-style files. A line
246 beginning with a semicolon ~;~ is a comment and is ignored. Most lines
247 are assignments, which look like
251 and assignments are split into sections by section headers in square
254 ~[~\relax{}/section/\relax{}~]~
256 The details of the configuration syntax are complicated, and explained
257 in the *runlisp.conf* manpage.
259 Configuration options can also be set on the command line, though the
260 effects are subtly different. Again, see the manual pages for details.
262 [fn:xdg-config] More properly, in ~$XDG_CONFIG_HOME/runlisp.conf~, if
266 ** Deciding which Lisp implementation to use
268 The ~prefer~ option specifies a /preference list/ of Lisp
269 implementations. The value is a list of Lisp implementation names, as
270 you'd give to ~-L~, separated by commas and/or spaces. If the
271 environment variable ~RUNLISP_PREFER~ is set, then this overrides any
272 value found in the configuration files. So your ~$HOME/.runlisp.conf~
273 file might look like this:
277 : prefer = sbcl, clisp
279 When deciding which Lisp implementation to use, ~runlisp~ works as
280 follows. It builds a list of /acceptable/ Lisp implementations from the
281 ~-L~ command-line option, and a list of /preferred/ Lisp implementations
282 from the ~prefer~ configuration option (or environment variable). If
283 there aren't any ~-L~ options, then it assumes that /all/ Lisp
284 implementations are acceptable; if no ~prefer~ option is set then it
285 assumes that /no/ Lisp implementations are preferred. It then works
286 through the preferred list in order: if it finds an implementation which
287 is installed and acceptable, then it uses that one. If that doesn't
288 work, then it works through the acceptable implementations that it
289 hasn't tried yet, in order, and if it finds one of those that's
290 installed, then it runs that one. Otherwise it reports an error and
294 ** Supporting new Lisp implementations
296 ~runlisp~ tries hard to make adding support for a new Lisp as painless
297 as possible. An awkward Lisp will of course cause trouble, but
298 ~runlisp~ itself is easy.
300 As a simple example, let's add support for the 32-bit version of
301 Clozure\nbsp{}CL. The source code for Clozure\nbsp{}CL easily builds
302 both 32- and 64-bit binaries in either 32- or 64-bit userlands, and one
303 might reasonably want to use the 32-bit CCL for some reason. The
304 following configuration stanza is sufficient
308 : command = ${@ENV:CCL32?ccl32}
310 + The first line heads a configuration section, providing the name
311 which will be used for this Lisp implementation, e.g., in ~-L~
312 options or ~prefer~ lists.
314 + The second line tells ~runlisp~ that configuration settings not
315 found in this section should be looked up in the ~ccl~ section
318 + The third line defines the command to be used to invoke the Lisp
319 system. It tries to find an environment variable named ~CCL32~,
320 falling back to looking up ~ccl32~ in the path otherwise.
322 And, err..., that's it. The ~@PARENTS~ setting uses the detailed
323 command-line runes for ~ccl~, so they don't need to be written out
326 That was rather anticlimactic, because all of the work got done
327 somewhere else. So let's look at a complete example: Steel Bank Common
328 Lisp. (SBCL's command-line interface is well thought-out, so this is an
329 ideal opportunity to explain how ~runlisp~ configuration works, without
330 getting bogged down in the details of fighting less amenable Lisps.)
332 The provided ~0base.conf~ file used to define SBCL as follows. (The
333 real version now contains a kludge for old versions, which needn't
338 : command = ${@ENV:SBCL?sbcl}
339 : image-file = ${@NAME}+asdf.core
342 : ${command} --noinform
343 : $?@IMAGE{--core "${image-path}" --eval "${image-restore}" |
344 : --eval "${run-script-prelude}"}
345 : --script "${@SCRIPT}"
348 : ${command} --noinform --no-userinit --no-sysinit --disable-debugger
349 : --eval "${dump-image-prelude}"
350 : --eval "(sb-ext:save-lisp-and-die \"${@IMAGENEW|q}\")"
352 Let's take this in slightly larger pieces.
354 + We see the ~[sbcl]~ section heading, and the ~command~ setting
355 again. These should now be unsurprising.
357 + There's no ~@PARENTS~ setting, so by default the ~sbcl~ section
358 inherits settings from the ~@COMMON~ section, defined in
359 ~0base.conf~. We shall use a number of definitions from this
362 + The ~image-file~ gives the name of the custom image file to look for
363 when trying to start SBCL, but not the directory. (The directory is
364 named by the ~image-dir~ configuration setting.) The image file
365 will be named ~sbcl+asdf.core~, but this isn't what's written.
366 Instead, it uses ~${@NAME}~, which is replaced by the name of the
367 section being processed. When we're running SBCL, this does the
368 same thing; but if someone wants to configure a new ~foo~ Lisp and
369 set ~@PARENTS~ to ~sbcl~, then the image file for ~foo~ will be
370 named ~foo+asdf.core~ by default. You needn't take such care when
371 configuring Lisp implementations for your own purposes, but it's
372 important for configurations which will be widely used.
374 + The ~run-script~ setting explains how to get SBCL to run a script.
375 This string is broken into words at (unquoted) spaces.
377 The syntax ~$?VAR{CONSEQ|ALT}~ means: if a configuration setting
378 ~VAR~ is defined, then expand to ~CONSEQ~; otherwise, expand to
379 ~ALT~. In this case, if the magic setting ~@IMAGE~ is defined, then
380 we add the tokens ~--core "${image-path}" --eval "${image-restore}"~
381 to the SBCL command line; otherwise, we add ~--eval
382 "${run-script-prelude}"~. The ~@IMAGE~ setting is defined by
383 ~runlisp~ only if (a)\nbsp{}a custom image was found in the correct
384 place, and (b)\nbsp{}use of custom images isn't disabled on its
387 The ~${image-path}~ token expands to the full pathname to the custom
388 image file; ~image-restore~ is a predefined Lisp expression to be
389 run when starting from a dumped image (e.g., to get ASDF to refresh
390 its idea of which systems are available).
392 The ~run-script-prelude~ is another (somewhat involved) Lisp
393 expression which sets up a Lisp environment suitable for running
394 scripts -- e.g., by arranging to ignore ~#!~ lines, and pushing
395 ~:runlisp-script~ onto ~*features*~.
397 Finally, regardless of whether we're using a custom or vanilla
398 image, we add the tokens ~--script "${@SCRIPT}"~ to the command
399 line. The ~${@SCRIPT}~ token is replaced by the actual script
400 pathname. ~runlisp~ then appends further arguments from its own
401 command line and runs the command. (For most Lisps, ~uiop~ needs a
402 ~--~ marker before the user arguments, but not for SBCL.)
404 + Finally, ~dump-image~ defines a command line for dumping a custom
405 images. The ~dump-image-prelude~ setting is a Lisp expression for
406 setting up a Lisp so that it will be in a useful state when dumped:
407 it's very similar to ~run-script-prelude~, and is built out of many
410 The thing we haven't seen before is ~${@IMAGENEW|q}~. The
411 ~@IMAGENEW~ setting is defined by the ~dump-runlisp-image~ program
412 to name the file in which the new image should be
413 saved.[fn:image-rename] The ~|q~ `filter' is new: it means that the
414 filename should be escaped suitable for inclusion in a Lisp quoted
415 string, by prefixing each ~\~ or ~"~ with a ~\~.
417 That's more or less all there is. SBCL is a particularly simple
418 example, but mostly because other Lisp implementations require fancier
419 stunts /at the Lisp level/. The ~runlisp~-level configuration isn't any
420 more complicated than SBCL.
422 [fn:image-rename] ~dump-runlisp-image~ wants to avoid clobbering an
423 existing image with a half-finished one, so it tries to arrange for the
424 new image to be written to a different file, and then renames it once
425 it's been created successfully.)
428 * What's wrong with =cl-launch=?
430 The short version is that ~cl-launch~ is slow and inconvenient.
431 ~cl-launch~ is a big, complicated Common Lisp/Bourne shell polyglot
432 which tries to do everything but doesn't quite succeed.
436 I took a trivial Lisp script:
438 : (format t "Hello from ~A!~%~
440 : Arguments = (~{`~A'~^, ~})~%"
441 : (lisp-implementation-type)
443 : uiop:*command-line-arguments*)
445 I timed how long it took to run on all of ~runlisp~'s supported Lisp
446 implementations, and compared them to how long ~cl-launch~ took: the
447 results are shown in table [[tab:runlisp-vanilla]]. ~runlisp~ is /at least/
448 two and half times faster at running this script than ~cl-launch~ on all
449 implementations except Clozure\nbsp{}CL[fn:slow-ccl], and approaching
450 four and a half times faster on SBCL.
452 #+CAPTION: ~cl-launch~ vs ~runlisp~ (with vanilla images)
453 #+NAME: tab:runlisp-vanilla
454 #+ATTR_LATEX: :float t :placement [tbp]
455 |------------------+-------------------+-----------------+----------------------|
456 | *Implementation* | *~cl-launch~ (s)* | *~runlisp~ (s)* | *~runlisp~ (factor)* |
457 |------------------+-------------------+-----------------+----------------------|
458 | ABCL | 7.3378 | 2.6474 | 2.772 |
459 | Clozure CL | 1.2888 | 0.9742 | 1.323 |
460 | GNU CLisp | 1.2405 | 0.2703 | 4.589 |
461 | CMU CL | 0.9521 | 0.3097 | 3.074 |
462 | ECL | 0.8020 | 0.3236 | 2.478 |
463 | SBCL | 0.3205 | 0.0874 | 3.667 |
464 |------------------+-------------------+-----------------+----------------------|
465 #+TBLFM: $4=$2/$3;%.3f
467 But this is using the `vanilla' Lisp images installed with the
468 implementations. ~runlisp~ by default builds custom images for most
469 Lisp implementations, which improves startup performance significantly;
470 see table [[tab:runlisp-custom]]. (I don't currently know how to build a
471 useful custom image for ABCL. ~runlisp~ does build a custom image for
472 ECL, but it doesn't help significantly.) These results are summarized
473 in figure [[fig:lisp-graph]].
475 #+CAPTION: ~cl-launch~ vs ~runlisp~ (with custom images)
476 #+NAME: tab:runlisp-custom
477 #+ATTR_LATEX: :float t :placement [tbp]
478 |------------------+-------------------+-----------------+----------------------|
479 | *Implementation* | *~cl-launch~ (s)* | *~runlisp~ (s)* | *~runlisp~ (factor)* |
480 |------------------+-------------------+-----------------+----------------------|
481 | ABCL | 7.3378 | 2.7023 | 2.715 |
482 | Clozure CL | 1.2888 | 0.0371 | 34.739 |
483 | GNU CLisp | 1.2405 | 0.0191 | 64.948 |
484 | CMU CL | 0.9521 | 0.0060 | 158.683 |
485 | ECL | 0.8020 | 0.3275 | 2.449 |
486 | SBCL | 0.3205 | 0.0064 | 50.078 |
487 |------------------+-------------------+-----------------+----------------------|
488 #+TBLFM: $4=$2/$3;%.3f
490 #+CAPTION: Comparison of ~runlisp~ and ~cl-launch~ times
491 #+NAME: fig:lisp-graph
492 #+ATTR_LATEX: :float t :placement [tbp]
493 [[file:doc/lisp-graph.tikz]]
495 Unlike ~cl-launch~, with some Lisp implementations at least, ~runlisp~
496 startup performance is usefully comparable to other popular scripting
497 language implementations. I wrote similarly trivial scripts in a number
498 of other languages, and timed them; the results are tabulated in table
499 [[tab:runlisp-interp]] and graphed in figure [[fig:interp-graph]].
501 #+CAPTION: ~runlisp~ vs other interpreters
502 #+NAME: tab:runlisp-interp
503 #+ATTR_LATEX: :float t :placement [tbp]
504 |------------------------------+-------------|
505 | *Implementation* | *Time (ms)* |
506 |------------------------------+-------------|
507 | Clozure CL | 37.1 |
511 |------------------------------+-------------|
514 |------------------------------+-------------|
515 | Debian Almquist shell (dash) | 1.2 |
518 |------------------------------+-------------|
519 | Tiny C (compile & run) | 1.6 |
520 | GCC (precompiled) | 0.6 |
521 |------------------------------+-------------|
523 #+CAPTION: Comparison of ~runlisp~ and other script interpreters
524 #+NAME: fig:interp-graph
525 #+Attr_latex: :float t :placement [tbp]
526 [[file:doc/interp-graph.tikz]]
528 (All the timings in this section were performed on the same 2020 Dell
529 XPS13 laptop running Debian `buster'. The tools used to make the
530 measurements are included in the source distribution, in the ~bench/~
533 [fn:slow-ccl] I don't know why Clozure\nbsp{}CL shows such a small
538 ~cl-launch~ has this elaborate machinery which reads shell script
539 fragments from various places and sets variables like ~$LISPS~, but it
542 Unlike other scripting languages such as Perl or Python, Common Lisp has
543 lots of implementations, and they all have various unique features (and
544 bugs) which a script might rely on (or need to avoid). Also, a user
545 might have preferences about which Lisps to use. ~cl-launch~'s approach
546 to this problem is a ~system_preferred_lisps~ shell function which can
547 be used in ~~/.cl-launchrc~ to select a Lisp system for a particular
548 `software system', though this notion doesn't appear to be well-defined,
549 but this all works by editing a single ~$LISPS~ shell variable. By
550 contrast, ~runlisp~ has a ~-L~ option with which scripts can specify the
551 Lisp systems they support (in a preference order), and a ~prefer~
552 configuration setting with which users can express their own
553 preferences: ~runlisp~ will never choose a Lisp system which the script
554 can't deal with, but it will respect the user's relative preferences.
556 Also, ~cl-launch~ is a monolith. Adding a new Lisp implementation to
557 it, or changing how a particular implementation is invoked, is rather
558 involved. By contrast, ~runlisp~ makes this remarkably easy, as
559 described in [[Supporting new Lisp implementations]].
561 ** It doesn't establish a (useful) common environment
563 A number of Lisp systems are annoyingly deficient in their handling of
566 For example, when GNU CLisp's ~-x~ option is used, it rebinds
567 ~*standard-input*~ to an internal string stream holding the expression
568 passed in on the command line, leaving the process's actual stdin nearly
569 impossible to access.
571 : $ date | cl-launch -l sbcl -i "(princ (read-line nil nil))" # expected
572 : Sun 9 Aug 14:39:10 BST 2020
573 : $ date | cl-launch -l clisp -i "(princ (read-line nil nil))" # bug!
576 As another example, Armed Bear Common Lisp doesn't seem to believe in
577 the stderr stream: when it starts up, ~*error-ouptut*~ is bound to the
578 standard output, just like ~*standard-output*~. Also, ~cl-launch~
579 loading ASDF causes a huge number of ~style-warning~ messages to be
580 written to stdout, making ABCL pretty much useless for writing filter
583 : $ cl-launch -l sbcl -i '(progn
584 : (format *standard-output* "output~%")
585 : (format *error-output* "error~%"))' \
586 : > >(sed 's/^/stdout: /') 2> >(sed 's/^/stderr: /')
589 : $ cl-launch -l abcl -i '(progn
590 : (format *standard-output* "output~%")
591 : (format *error-output* "error~%"))' \
592 : > >(sed 's/^/stdout: /') 2> >(sed 's/^/stderr: /')
593 : [1813 lines of compiler warnings tagged `stdout:']
597 ~runlisp~ takes care of all of this, providing a basic but useful common
598 level of shell integration for all its supported Lisp implementations.
601 + It ensures that the standard Unix `stdin', `stdout', and `stderr'
602 file descriptors are hooked up to the Lisp ~*standard-input*~,
603 ~*standard-output*~, and ~*error-output*~ streams.
605 + It ensures that starting a script doesn't write a deluge of
608 The complete details are given in ~runlisp~'s manpage.
610 ** Why might one prefer =cl-launch= anyway?
612 On the other hand, ~cl-launch~ is well established and full-featured.
614 ~cl-launch~ compiles scripts before trying to run them, so they'll run
615 faster on Lisps which use an interpreter by default. It has a caching
616 feature so running a script a second time doesn't need to recompile it.
617 If your scripts are compute-intensive and benefit from ahead-of-time
618 compilation then maybe ~cl-launch~ is preferable.
620 ~cl-launch~ supports more Lisp systems. I only have six installed on my
621 development machine at the moment, so those are the ones that ~runlisp~
622 supports. If you want your scripts to be able to run on other Lisps,
623 then ~cl-launch~ is the way to do that. Of course, I welcome patches to
624 help ~runlisp~ support other free Lisp implementations. ~cl-launch~
625 also supports proprietary Lisps: I have very little interest in these,
626 so if you want to run scripts using Allegro or LispWorks then
627 ~cl-launch~ is your only choice.