Subject: Re: Is this a correct way of delivering CMUCL application ?
From: rpw3@rpw3.org (Rob Warnock)
Date: Fri, 17 Feb 2006 22:09:55 -0600
Newsgroups: comp.lang.lisp
Message-ID: <25adnYb_qZyOA2veRVn-vQ@speakeasy.net>
Nikhil Ketkar <nikhilketkar@gmail.com> wrote:
+---------------
| 1) I compiled and loaded each file in CMUCL
|    (load (compile-file "file-1.lisp"))
|     (load (compile-file "file-1.lisp"))
| ... for all files.
| 2) I dumped the core image like this
| (save-lisp "my-application.core")
| 
| 3) I wrote a shell script to launch this together like this
|    lisp -core my-application.core -eval "(main-function $1 $2)"
| where main-function is the top level function and the $1, $2 are the
| parameters which need to be passed.
| 
| 4) I send the lisp environment, the core file and this shell script to
| the end user.  He launches the shell script with the parameters.
| Is this ok?
+---------------

This seems fine, though as long as you're going to the trouble of
building a core image you might as well specify the call of your
MAIN-FUNCTION in the SAVE-LISP call itself, along with a few more
options to make things work more smoothly. So your step #2 might
become:

    (flet ((run-main ()
	     (let ((args (cdr (get-command-line-switch "core"))))
	       (if args
		 (apply #'main-function (mapcar #'read-from-string args))
		 (error "usage: lisp -load my-app.x86f arg1 arg2"))
	       (unix:unix-exit 0))))
      (save-lisp "my-app.core" :init-function #'run-main
			       :batch-mode t
			       :print-herald nil
			       :load-init-file nil))

[Note that I've used CMUCL's command line processing functions, which
are described in section 6.1 "Reading the Command Line" in the CMUCL
User's Manual.]

Then to run it in step #3, you just say the following [I've
included a stub MAIN-FUNCTION that just prints its args]:

    $ lisp -core my-app.core 123 465 last-arg '#(9 8 7)'
    MAIN-FUNCTION called with:
    arg[1]: 123
    arg[2]: 465
    arg[3]: LAST-ARG
    arg[4]: #(9 8 7)
    $ 

[If you were running on FreeBSD instead of Linux, I'd even suggest
using Fred Gilham's "executable" hack, which adds one more argument
to the SAVE-LISP, ":EXECUTABLE T", which writes out a single executable
ELF file that contains both the "lisp" executable and the core file,
but that code hasn't made it into the Linux version yet. However, see
<http://article.gmane.org/gmane.lisp.cmucl.devel/3029/> for Eric Marsden's
version of that, which requires that you be able to rebuild CMUCL...]

Conversely, if you're going to be generating updates of your
appplication fairly frequently and don't want to send a whole
new (*large!*) CMUCL image each time, you might take advantage
of the fact that the CMUCL loader accepts a concatentation of
FASL files as well as a single file. That is:

   1. Compile all your ".lisp" files as before, but only LOADing
      those needed to compile-file later ones.

   2. In the Unix/Linux shell, concatenate all of the ".x86f" FASLs
      together into one big one, in the correct order for loading, e.g.:

	$ cat file-1.x86f file-2.x86f ... file-N.x86f > my-app.x86f
	$

   3. Make your wrapper shell script call it this way [note that
      it now uses the standard CMUCL distribution "lisp.core"]:

       lisp -load my-app.x86f -eval "(main-function $1 $2)"

Then, as needed, you can ship out an updated "my-app.x86f", which
is likely to be a good deal smaller than an entire ".core" file.

To add some command-line processing like that shown above,
write one more tiny Lisp file that parses the CMUCL command
line arguments and passes them to your "main-function",
possibly something like this:

    (declaim (ftype function main-function)) ; muffle warning

    (defswitch "main")                       ; we want to parse "-main"

    (let ((args (get-command-line-switch "main")))
      (if args
	(apply #'main-function (mapcar #'read-from-string args))
	(error "usage: lisp -load my-app.x86f -main arg1 arg2..."))
      (unix:unix-exit 0))

Compile-file that [but *don't* LOAD it!] and concatenate the ".x86f"
onto the end of your "my-app.x86f", and then you can run the whole
thing like this:

    $ lisp -load my-app.x86f -main 123 465 last-arg '#(9 8 7)'
    ; Loading #p"/usr/u/rpw3/my-app.x86f".
    MAIN-FUNCTION called with:
    arg[1]: 123
    arg[2]: 465
    arg[3]: LAST-ARG
    arg[4]: #(9 8 7)
    $ 

Finally, if you want the app to exit on error (or EOF on stdin) instead
of dropping into the debugger, then also add the "-batch" switch [which
the first example did with (SAVE-LISP ... :BATCH-MODE T)]:

    $ lisp -batch -load my-app.x86f -main args...


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607