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