From ee222567ee95eaac8f6f4c877242dd116bfb8337 Mon Sep 17 00:00:00 2001 From: Nikodemus Siivola Date: Sat, 11 Oct 2008 13:34:44 +0000 Subject: [PATCH] 1.0.21.17: --script commandline argument * Works as both runtime and toplevel argument (which may imply the separation between the two is suspect?): * As a runtime argument it implies --noinform and the end of runtime arguments. * As a toplevel argument it implies --disable-debugger and the end of toplevel arguments. It additionally inhibits sysinit and userinit processing unless an explicit --userinit or --sysinit option is given before it. Then SBCL loads the specified specified file with :VERBOSE NIL and :PRINT NIL, discarding the first line if it start with #!. When the script file has been processed, SBCL exits without entering the REPL. * Documentation & a test. * Based loosely on an earlier patch by Kevin Reid. --- NEWS | 3 ++ doc/manual/start-stop.texinfo | 102 +++++++++++++++--------------------------- doc/sbcl.1 | 19 +++++++- src/code/toplevel.lisp | 49 ++++++++++++++++++-- src/runtime/runtime.c | 11 ++++- tests/toplevel.sh | 10 ++++- version.lisp-expr | 2 +- 7 files changed, 123 insertions(+), 73 deletions(-) diff --git a/NEWS b/NEWS index a341dbe86..5befbfa1b 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ changes in sbcl-1.0.22 relative to 1.0.21: * minor incompatible change: --disable-debugger toplevel option now takes effect before processing of initialization files and --eval or --load options. + * new feature: new commandline argument: --script, which supports + shebang lines. See documentation for details. (based on work by + Kevin Reid) * enhancement: inoccous calls to EVAL or generic functions dispatching on subclasses of eg. STREAM no longer cause compiler notes to appear. * enhancement: the system no longer resignals errors from --load and diff --git a/doc/manual/start-stop.texinfo b/doc/manual/start-stop.texinfo index d4829927f..be2a88a0a 100644 --- a/doc/manual/start-stop.texinfo +++ b/doc/manual/start-stop.texinfo @@ -66,12 +66,31 @@ Integration}. @node Shebang Scripts @comment node-name, next, previous, up @subsection Shebang Scripts +@vindex sb-ext:*posix-argv* +@vindex *posix-argv* + +Standard Unix tools that are interpreters follow a common command line +protocol that is necessary to work with ``shebang scripts''. SBCL supports +this via the @code{--script} command line option. + +Example file (@file{hello.lisp}): + +@lisp +#!/usr/local/bin/sbcl --script +(write-line "Hello, World!") +@end lisp -SBCL doesn't come with built-in support for shebang-line execution, -but this can be provided with a shell trampoline, or by dispatching -from initialization files (@pxref{Unix-style Command Line Protocol} for -an example.) +Usage examples: + +@smallexample +$ ./hello.lisp +Hello, World! +@end smallexample +@smallexample +$ sbcl --script hello.lisp +Hello, World! +@end smallexample @node Stopping SBCL @comment node-name, next, previous, up @@ -195,6 +214,11 @@ startup. This makes it easier to write Lisp programs which work cleanly in Unix pipelines. See also the @code{--noprint} and @code{--disable-debugger} options. +@item --script @var{filename} +As a runtime option this is equivalent to @code{--noinform} +@code{--end-runtime-options} @code{--script} @var{filename}. See the +description of @code{--script} as a toplevel option below. + @item --help Print some basic information about SBCL, then exit. @@ -260,6 +284,14 @@ before loading of initialization files or processing @code{--eval} and @code{--load} options. See @code{sb-ext:disable-debugger} for details. @xref{Debugger Entry}. +@item --script @var{filename} +Implies @code{--no-userinit} @code{--no-sysinit} +@code{--disable-debugger} @code{--end-toplevel-options}. + +Causes the system to load the specified file instead of entering the +read-eval-print-loop, and exit afterwards. If the file begins with a +shebang line, it is ignored. + @end table @@ -313,71 +345,9 @@ Some examples of what you may consider doing in the initialization files follow. @menu -* Unix-style Command Line Protocol:: * Automatic Recompilation of Stale Fasls:: @end menu -@node Unix-style Command Line Protocol -@comment node-name, next, previous, up -@subsubsection Unix-style Command Line Protocol -@vindex sb-ext:*posix-argv* -@vindex *posix-argv* - -Standard Unix tools that are interpreters follow a common command line -protocol that is necessary to work with ``shebang scripts''. SBCL -doesn't do this by default, but adding the following snippet to an -initialization file does the trick: - -@lisp -;;; If the first user-processable command-line argument is a filename, -;;; disable the debugger, load the file handling shebang-line and quit. -(let ((script (and (second *posix-argv*) - (probe-file (second *posix-argv*))))) - (when script - ;; Handle shebang-line - (set-dispatch-macro-character #\# #\! - (lambda (stream char arg) - (declare (ignore char arg)) - (read-line stream))) - ;; Disable debugger - (setf *invoke-debugger-hook* - (lambda (condition hook) - (declare (ignore hook)) - ;; Uncomment to get backtraces on errors - ;; (sb-debug:backtrace 20) - (format *error-output* "Error: ~A~%" condition) - (quit))) - (load script) - (quit))) -@end lisp - -Example file (@file{hello.lisp}): - -@lisp -#!/usr/local/bin/sbcl --noinform -(write-line "Hello, World!") -@end lisp - -Usage examples: - -@smallexample -$ ./hello.lisp -Hello, World! -@end smallexample - -@smallexample -$ sbcl hello.lisp -This is SBCL 0.8.13.70, an implementation of ANSI Common Lisp. -More information about SBCL is available at . - -SBCL is free software, provided as is, with absolutely no warranty. -It is mostly in the public domain; some portions are provided under -BSD-style licenses. See the CREDITS and COPYING files in the -distribution for more information. -Hello, World! -@end smallexample - - @node Automatic Recompilation of Stale Fasls @comment node-name, next, previous, up @subsubsection Automatic Recompilation of Stale Fasls diff --git a/doc/sbcl.1 b/doc/sbcl.1 index e4f4146d1..5776d61d7 100644 --- a/doc/sbcl.1 +++ b/doc/sbcl.1 @@ -269,6 +269,11 @@ startup. (This makes it easier to write Lisp programs which work cleanly in Unix pipelines. See also the "\-\-noprint" and "\-\-disable\-debugger" options.) .TP 3 +.B \-\-script +As a runtime option equivalent to \-\-noinform +\-\-end\-toplevel\-options \-\-script . See the description +of \-\-script as a toplevel option below. +.TP 3 .B \-\-help Print some basic information about SBCL, then exit. .TP 3 @@ -325,6 +330,13 @@ debugger, allowing interactive diagnosis and possible intercession. This option disables the debugger, causing errors to print a backtrace and exit with status 1 instead -- which is a mode of operation better suited for batch processing. See the user manual on \f(CRSB\-EXT:DISABLE\-DEBUGGER\fR for details. +.B \-\-script +Implies \-\-no-sysinit \-\-no-userinit \-\-disable-debugger +\-\-end\-toplevel\-options. + +Causes the system to load the specified file and exit immediately +afterwards, instead of entering the readl-eval-print loop. If the file +begins with a shebang line, it is ignored. .PP Regardless of the order in which toplevel options appear on the command @@ -341,7 +353,12 @@ Any user initialization file is loaded, unless prohibited. \-\-eval and \-\-load options are processed in the order given. .PP -Finally, the read-eval-print loop is entered. +Finally, either the read-eval-print loop is entered or the file +specified with \-\-script option is loaded. + +When running in the read-eval-print loop the system exits on end of +file. Similarly, the system exits immediately after processing the +file specified with \-\-script. Note that when running SBCL with the \-\-core option, using a core file created by a user call to the diff --git a/src/code/toplevel.lisp b/src/code/toplevel.lisp index e2663661d..135028c16 100644 --- a/src/code/toplevel.lisp +++ b/src/code/toplevel.lisp @@ -381,6 +381,30 @@ command-line.") (dolist (option options) (process-1 option))))) +;;; Skips past the shebang line on stream, if any. +(defun maybe-skip-shebang-line (stream) + (let ((p (file-position stream))) + (flet ((next () (read-byte stream nil))) + (unwind-protect + (when (and (eq (next) (char-code #\#)) + (eq (next) (char-code #\!))) + (setf p nil) + (loop for x = (next) + until (or (not x) (eq x (char-code #\newline))))) + (when p + (file-position stream p)))) + t)) + +(defun process-script (script) + (let ((pathname (native-pathname script)) + (ok nil)) + (unwind-protect + (with-open-file (f pathname :element-type :default) + (maybe-skip-shebang-line f) + (load f :verbose nil :print nil) + (setf ok t)) + (quit :unix-status (if ok 0 1))))) + ;; Errors while processing the command line cause the system to QUIT, ;; instead of trying to go into the Lisp debugger, because trying to ;; go into the Lisp debugger would get into various annoying issues of @@ -415,6 +439,8 @@ command-line.") (reversed-options nil) ;; Has a --noprint option been seen? (noprint nil) + ;; Has a --script option been seen? + (script nil) ;; everything in *POSIX-ARGV* except for argv[0]=programname (options (rest *posix-argv*))) @@ -436,7 +462,14 @@ command-line.") (pop options) (startup-error "unexpected end of command line options")))) - (cond ((string= option "--sysinit") + (cond ((string= option "--script") + (pop-option) + (setf disable-debugger t + no-userinit t + no-sysinit t + script (pop-option)) + (return)) + ((string= option "--sysinit") (pop-option) (if sysinit (startup-error "multiple --sysinit options") @@ -516,13 +549,23 @@ command-line.") (process-init-file sysinit :system)) (unless no-userinit (process-init-file userinit :user)) - (process-eval/load-options (nreverse reversed-options))) + (process-eval/load-options (nreverse reversed-options)) + (when script + (process-script script) + (bug "PROCESS-SCRIPT returned"))) (abort () - :report "Skip to toplevel READ/EVAL/PRINT loop." + :report (lambda (s) + (write-string + (if script + ;; In case script calls (enable-debugger)! + "Abort script, exiting lisp." + "Skip to toplevel READ/EVAL/PRINT loop.") + s)) (/show0 "CONTINUEing from pre-REPL RESTART-CASE") (values)) ; (no-op, just fall through) (quit () :report "Quit SBCL (calling #'QUIT, killing the process)." + :test (lambda (c) (declare (ignore c)) (not script)) (/show0 "falling through to QUIT from pre-REPL RESTART-CASE") (quit :unix-status 1)))) diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 02898e3d1..79dbcbf0e 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -219,6 +219,7 @@ main(int argc, char *argv[], char *envp[]) /* other command line options */ boolean noinform = 0; + char *script = 0; boolean end_runtime_options = 0; lispobj initial_function; @@ -235,7 +236,15 @@ main(int argc, char *argv[], char *envp[]) int argi = 1; while (argi < argc) { char *arg = argv[argi]; - if (0 == strcmp(arg, "--noinform")) { + if (0 == strcmp(arg, "--script")) { + /* This is both a runtime and a toplevel option. As a + * runtime option, it is equivalent to --noinform. + * This exits, and does not increment argi, so that + * TOPLEVEL-INIT sees the option. */ + noinform = 1; + end_runtime_options = 1; + break; + } else if (0 == strcmp(arg, "--noinform")) { noinform = 1; ++argi; } else if (0 == strcmp(arg, "--core")) { diff --git a/tests/toplevel.sh b/tests/toplevel.sh index a22d4365b..f7e055bc2 100644 --- a/tests/toplevel.sh +++ b/tests/toplevel.sh @@ -27,4 +27,12 @@ if [ "`grep -c FOO::BAR $TEST_FILESTEM`" != 1 ] ; then echo failed DEFPACKAGE-then-PRINT from --eval form exit $EXIT_LOSE fi -exit $EXIT_TEST_WIN \ No newline at end of file + +# --script +run_sbcl --script script-test.lisp --eval foo \ + < /dev/null > $TEST_FILESTEM +if [ "`grep -c :SCRIPT-OK $TEST_FILESTEM`" != 1 ] ; then + echo "failed --script test" + exit $EXIT_LOSE +fi +exit $EXIT_TEST_WIN diff --git a/version.lisp-expr b/version.lisp-expr index ad842bd7e..b818344c7 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -17,4 +17,4 @@ ;;; checkins which aren't released. (And occasionally for internal ;;; versions, especially for internal versions off the main CVS ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".) -"1.0.21.16" +"1.0.21.17" -- 2.11.4.GIT