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