Happy new year!
On 64-bit Allegro, run
(cffi:defcfun strerror :string (errnum :int)) (strerror 1)
and you will probably get
Received signal number 4 (Illegal instruction) [Condition of type EXCL:SYNCHRONOUS-OPERATING-SYSTEM-SIGNAL]
at least if the returned pointer is more than 32-bits.
The problem is that without this patch, cffi-allegro.lisp does not support 64-bit pointers. Does the patch address the problem properly? It seems to work for us.
The use of :unsigned-nat instead of :unsigned-long is recommended by Franz because on Windows 64-bit :unsigned-long is still 32-bits.
--- a/addons/cffi_0.10.3/src/cffi-allegro.lisp +++ b/addons/cffi_0.10.3/src/cffi-allegro.lisp @@ -135,7 +135,7 @@ SIZE-VAR is supplied, it will be bound to SIZE during BODY." ;; stack allocation pattern `(let ((,size-var ,size)) (declare (ignorable ,size-var)) - (ff:with-stack-fobject (,var '(:array :char ,size)) + (ff:with-stack-fobject (,var '(:array :char ,size) :allocation :foreign-static-gc) (let ((,var (ff:fslot-address ,var))) ;; (excl::stack-allocated-p var) => T ,@body)))) @@ -263,12 +263,12 @@ SIZE-VAR is supplied, it will be bound to SIZE during BODY." :unsigned-long) 'integer) (:float 'single-float) (:double 'double-float) - (:foreign-address :foreign-address) + (:foreign-address 'integer) (:void 'null))))
(defun foreign-allegro-type (type) (if (eq type :foreign-address) - nil + :unsigned-nat type))
(defun allegro-type-pair (type)
On Mon, Jan 5, 2009 at 1:02 AM, John Fremlin jf@msi.co.jp wrote:
@@ -263,12 +263,12 @@ SIZE-VAR is supplied, it will be bound to SIZE during BODY." :unsigned-long) 'integer) (:float 'single-float) (:double 'double-float)
(:foreign-address :foreign-address)
(:foreign-address 'integer) (:void 'null))))
This change is somewhat unexpected, is it really necessary?
(defun foreign-allegro-type (type) (if (eq type :foreign-address)
nil
:unsigned-nat type))
(defun allegro-type-pair (type)
Does it work if you use :FOREIGN-ADDRESS instead of :UNSIGNED-NAT in FOREIGN-ALLEGRO-TYPE?
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
On Mon, 5 Jan 2009 03:14:13 +0000, "Luís Oliveira" luismbo@gmail.com wrote:
On Mon, Jan 5, 2009 at 1:02 AM, John Fremlin jf@msi.co.jp wrote:
@@ -263,12 +263,12 @@ SIZE-VAR is supplied, it will be bound to SIZE during BODY." :unsigned-long) 'integer) (:float 'single-float) (:double 'double-float)
(:foreign-address :foreign-address)
(:foreign-address 'integer) (:void 'null))))
This change is somewhat unexpected, is it really necessary?
Yes.
It is needed because now foreign-allegro-type does not return nil for :foreign-address, and so Allegro will actually check the second value from allegro-type-pair (which it ignored before because the first value was nil).
(defun foreign-allegro-type (type) (if (eq type :foreign-address)
nil
:unsigned-nat type))
(defun allegro-type-pair (type)
Does it work if you use :FOREIGN-ADDRESS instead of :UNSIGNED-NAT in FOREIGN-ALLEGRO-TYPE?
No, that's the first thing I tried
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
There is no :foreign-address type in Allegro 8.1?
It is a very strange idea that there ever would be such a type. . .
http://www.franz.com/support/documentation/current/doc/ftype.htm
Why are you ignoring this patch?
On Mon, 5 Jan 2009 12:48:13 +0900, John Fremlin jf@msi.co.jp wrote:
On Mon, 5 Jan 2009 03:14:13 +0000, "Luís Oliveira" luismbo@gmail.com wrote:
On Mon, Jan 5, 2009 at 1:02 AM, John Fremlin jf@msi.co.jp wrote:
@@ -263,12 +263,12 @@ SIZE-VAR is supplied, it will be bound to SIZE during BODY." :unsigned-long) 'integer) (:float 'single-float) (:double 'double-float)
(:foreign-address :foreign-address)
(:foreign-address 'integer) (:void 'null))))
This change is somewhat unexpected, is it really necessary?
Yes.
It is needed because now foreign-allegro-type does not return nil for :foreign-address, and so Allegro will actually check the second value from allegro-type-pair (which it ignored before because the first value was nil).
(defun foreign-allegro-type (type) (if (eq type :foreign-address)
nil
:unsigned-nat type))
(defun allegro-type-pair (type)
Does it work if you use :FOREIGN-ADDRESS instead of :UNSIGNED-NAT in FOREIGN-ALLEGRO-TYPE?
No, that's the first thing I tried
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
There is no :foreign-address type in Allegro 8.1?
It is a very strange idea that there ever would be such a type. . .
http://www.franz.com/support/documentation/current/doc/ftype.htm
John Fremlin jf@msi.co.jp writes:
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
There is no :foreign-address type in Allegro 8.1?
It is a very strange idea that there ever would be such a type. . .
http://www.franz.com/support/documentation/current/doc/ftype.htm
Why are you ignoring this patch?
A delay of 3 days is hardly an abrupt dismissal. Furthermore, I do not believe your response that I have quoted addresses Luís's concerns about backward compatibility.
Stephen Compall s11@member.fsf.org writes:
John Fremlin jf@msi.co.jp writes:
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
There is no :foreign-address type in Allegro 8.1?
It is a very strange idea that there ever would be such a type. . .
http://www.franz.com/support/documentation/current/doc/ftype.htm
Why are you ignoring this patch?
A delay of 3 days is hardly an abrupt dismissal. Furthermore, I do not believe your response that I have quoted addresses Luís's concerns about backward compatibility.
Thanks for your response.
As I explained I don't believe there ever was a :foreign-address type.
(defun foreign-allegro-type (type) (if (eq type :foreign-address) nil ....
explicitly made it irrelevant.
Why does Luís think there was a :foreign-address type? Since when has any lisp had a type which is a :keyword? This is not a foreign-type but a lisp type.
To reiterate, my patch solves two problems:
use :unsigned-nat for pointers from C
store it in an Lisp 'integer
Before CFFI use to try
nil for pointers from C (meaning :int)
store it in a Lisp :foreign-address -- a non-existent type -- (actually fortunately ignored because the first part was nil)
I'm not sure when :unsigned-nat was introduced . . . it is indeed very likely that my patch will not work with old versions, in which case :unsigned-long should be used.
According to franz.com:
:nat and :unsigned-nat: these were added in release 7.0.
On Mon, Jan 5, 2009 at 3:48 AM, John Fremlin jf@msi.co.jp wrote:
On Mon, 5 Jan 2009 03:14:13 +0000, "Luís Oliveira" luismbo@gmail.com wrote:
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
There is no :foreign-address type in Allegro 8.1?
The page http://www.franz.com/support/documentation/8.1/doc/foreign-functions.htm documents the :FOREIGN-ADDRESS type.
Can you confirm that the patch I've attached works? Thanks.
"Luís Oliveira" luismbo@gmail.com writes:
On Mon, Jan 5, 2009 at 3:48 AM, John Fremlin jf@msi.co.jp wrote:
On Mon, 5 Jan 2009 03:14:13 +0000, "Luís Oliveira" luismbo@gmail.com wrote:
I'm asking these questions because I suspect both changes break with older versions of Allegro but I can't test it since the express edition 7.0 is no longer available. It's probably not a big deal but I'd like to at least try to maintain compatibility.
There is no :foreign-address type in Allegro 8.1?
The page http://www.franz.com/support/documentation/8.1/doc/foreign-functions.htm documents the :FOREIGN-ADDRESS type.
I kept trying to point out that this was being used by cffi as a *Lisp* type not a *foreign-type*. It is not a Lisp type.
Can you confirm that the patch I've attached works? Thanks.
Thanks, it looks like a great cleanup and a better way of integrating :unsigned-nat.
The other change from my patch was about allocating the stack objects in a space that will not be moved by the GC.
Is it a necessary according to the CFFI semantics of with-foreign-object?
+++ cffi_0.10.3/src/cffi-allegro.lisp @@ -135,7 +135,7 @@ SIZE-VAR is supplied, it will be bound to SIZE during BODY." ;; stack allocation pattern `(let ((,size-var ,size)) (declare (ignorable ,size-var)) - (ff:with-stack-fobject (,var '(:array :char ,size)) + (ff:with-stack-fobject (,var '(:array :char ,size) :allocation :foreign-static-gc) (let ((,var (ff:fslot-address ,var))) ;; (excl::stack-allocated-p var) => T ,@body))))
On Fri, Jan 9, 2009 at 12:51 AM, John Fremlin jf@msi.co.jp wrote:
The page http://www.franz.com/support/documentation/8.1/doc/foreign-functions.htm documents the :FOREIGN-ADDRESS type.
I kept trying to point out that this was being used by cffi as a *Lisp* type not a *foreign-type*. It is not a Lisp type.
Oh, yes, that part was clearly broken. But it was being used a foreign type as well, but even then it doesn't seem to work in all contexts.
The other change from my patch was about allocating the stack objects in a space that will not be moved by the GC.
I've applied that already, thanks! IIUC, :FOREIGN-STATIC-GC is only relevant when WITH-STACK-FOBJECT fails to stack allocate, in which case it actually allocates on the heap.
Hmm, now that I read the documentation more carefully, it seems that if WITH-STACK-FOBJECT allocates on the heap then it won't deallocate the object and we should be using WITH-STATIC-FOBJECT instead. Does that sound right? If it does, I'll apply the attached patch during the weekend or so.
Is it a necessary according to the CFFI semantics of with-foreign-object?
Indeed.
"Luís Oliveira" luismbo@gmail.com writes:
On Fri, Jan 9, 2009 at 12:51 AM, John Fremlin jf@msi.co.jp wrote:
The page http://www.franz.com/support/documentation/8.1/doc/foreign-functions.htm documents the :FOREIGN-ADDRESS type.
I kept trying to point out that this was being used by cffi as a *Lisp* type not a *foreign-type*. It is not a Lisp type.
Oh, yes, that part was clearly broken. But it was being used a foreign type as well, but even then it doesn't seem to work in all contexts.
Yes, I don't think it is even a "proper" foreign type, it is a bit like :void.
The other change from my patch was about allocating the stack objects in a space that will not be moved by the GC.
I've applied that already, thanks! IIUC, :FOREIGN-STATIC-GC is only relevant when WITH-STACK-FOBJECT fails to stack allocate, in which case it actually allocates on the heap.
http://www.franz.com/support/documentation/8.1/doc/operators/ff/with-stack-f...
The allocation argument defaults to :foreign, and can be one of :c, :aligned, :lisp, :lisp-short, :foreign, or :foreign-static-gc. Note that if allocation is :c or :aligned, it is not stack-allocated, but instead is allocated and deallocated at the appropriate places within the form.
This is actually a little misleading, as it goes on to say
Otherwise, the object is allocated as specified. In this case, if the allocation requires explicit de-allocation, it is the responsibility of the application to de-allocate the object.
http://www.franz.com/support/documentation/8.1/doc/operators/ff/with-static-...
with-static-fobject on the other hand behaves in a much more sensible way. It will try to allocate on the stack but if it can't it will unwind-protect a deallocation.
Hmm, now that I read the documentation more carefully, it seems that if WITH-STACK-FOBJECT allocates on the heap then it won't deallocate the object and we should be using WITH-STATIC-FOBJECT instead. Does that sound right? If it does, I'll apply the attached patch during the weekend or so.
Why do you still need the
(cond ((and (constantp size) (<= (eval size) ff:*max-stack-fobject-bytes*))
This was just to check whether the with-stack-fobject would actually allocate the thing on the stack (if it did not then we would have to free it by ourselves, and so we would not use with-stack-fobject at all).
You can just use with-static-fobject for everything now and let Allegro decide whether it can allocate on the stack?
On Fri, Jan 9, 2009 at 2:02 AM, John Fremlin jf@msi.co.jp wrote:
Why do you still need the
(cond ((and (constantp size) (<= (eval size) ff:*max-stack-fobject-bytes*))
This was just to check whether the with-stack-fobject would actually allocate the thing on the stack (if it did not then we would have to free it by ourselves, and so we would not use with-stack-fobject at all).
You can just use with-static-fobject for everything now and let Allegro decide whether it can allocate on the stack?
We still need it anyway because in order for stack allocation to occur we can't pass the :SIZE argument, and the TYPE has to be constant. Also, it seems that WITH-STATIC-FOBJECT is not enforcing FF:*MAX-STACK-FOBJECT-BYTES* so we probably should do that ourselves.
cffi-wrapper has made our lives with cffi-udp very much easier. It generates small shared libraries for interfacing with C functionality that is not well specified as an ABI.
However, for Lisps that are not Allegro CL (which copies the shared objects to the deployment directory), it can be quite irritating to deploy applications because (on Lispworks and SBCL at least) the full path the generated shared object is hard-coded in the image, and the Lisp will try to reopen that file before running user code.
SBCL for example (http://www.sbcl.org/manual/Loading-Shared-Object-Files.html)
load-shared-object interacts with sb-ext:save-lisp-and-die:
1. If dont-save is true (default is NIL), the shared object will be dropped when save-lisp-and-die is called -- otherwise shared objects are reloaded automatically when a saved core starts up. Specifying dont-save can be useful when the location of the shared object on startup is uncertain.
--- a/addons/cffi/grovel/grovel.lisp +++ b/addons/cffi/grovel/grovel.lisp @@ -767,7 +767,10 @@ error: (format out ";;;; This file was automatically generated by cffi-grovel.~% ;;;; Do not edit by hand.~%") (let ((*package* (find-package '#:cl))) - (format out "~%~S~%" `(cffi:load-foreign-library ,lib-file))) + (format out "~%~S~%" `(cffi:load-foreign-library + '(:or ,lib-file + ,(file-namestring lib-file) + (:default ,(pathname-name lib-file)))))) (dolist (form lisp-forms) (print form out)) (terpri out))
This patch doesn't help, because once the shared objects are loaded (before delivery) the path is stored by the Lisp environment and the other paths are not used.
Any advice or suggestions?
On Thu, 29 Jan 2009 18:53:14 +0900, John Fremlin said:
cffi-wrapper has made our lives with cffi-udp very much easier. It generates small shared libraries for interfacing with C functionality that is not well specified as an ABI.
However, for Lisps that are not Allegro CL (which copies the shared objects to the deployment directory), it can be quite irritating to deploy applications because (on Lispworks and SBCL at least) the full path the generated shared object is hard-coded in the image, and the Lisp will try to reopen that file before running user code.
SBCL for example (http://www.sbcl.org/manual/Loading-Shared-Object-Files.html)
load-shared-object interacts with sb-ext:save-lisp-and-die: 1. If dont-save is true (default is NIL), the shared object will be dropped when save-lisp-and-die is called -- otherwise shared objects are reloaded automatically when a saved core starts up. Specifying dont-save can be useful when the location of the shared object on startup is uncertain.
--- a/addons/cffi/grovel/grovel.lisp +++ b/addons/cffi/grovel/grovel.lisp @@ -767,7 +767,10 @@ error: (format out ";;;; This file was automatically generated by cffi-grovel.~% ;;;; Do not edit by hand.~%") (let ((*package* (find-package '#:cl)))
(format out "~%~S~%" `(cffi:load-foreign-library ,lib-file)))
(format out "~%~S~%" `(cffi:load-foreign-library
'(:or ,lib-file
,(file-namestring lib-file)
(:default ,(pathname-name lib-file)))))) (dolist (form lisp-forms) (print form out)) (terpri out))
This patch doesn't help, because once the shared objects are loaded (before delivery) the path is stored by the Lisp environment and the other paths are not used.
Any advice or suggestions?
I strongly recommend calling cffi:load-foreign-library at run time only (never before delivery). That gives you control over how the library is loaded on startup (for example reporting errors to the user).
Martin Simmons martin@lispworks.com writes: [...]
--- a/addons/cffi/grovel/grovel.lisp +++ b/addons/cffi/grovel/grovel.lisp @@ -767,7 +767,10 @@ error: (format out ";;;; This file was automatically generated by cffi-grovel.~% ;;;; Do not edit by hand.~%") (let ((*package* (find-package '#:cl)))
(format out "~%~S~%" `(cffi:load-foreign-library ,lib-file)))
(format out "~%~S~%" `(cffi:load-foreign-library
'(:or ,lib-file
,(file-namestring lib-file)
(:default ,(pathname-name lib-file)))))) (dolist (form lisp-forms) (print form out)) (terpri out))
This patch doesn't help, because once the shared objects are loaded (before delivery) the path is stored by the Lisp environment and the other paths are not used.
Any advice or suggestions?
I strongly recommend calling cffi:load-foreign-library at run time only (never before delivery). That gives you control over how the library is loaded on startup (for example reporting errors to the user).
Thanks for your input. I guess that is a very sensible approach on all Lisps except Allegro which bundles the shared objects.
Luis, would you consider a patch to add a special variable *cffi-wrapper-delay-load* which would set a special mode for the generated wrapper.lisps?
This mode would inhibit the loading of generated shared objects and the definition of functions depending on them, so that the image can be saved. When the image is loaded then the definitions should be made at that time.
On Fri, Jan 30, 2009 at 6:05 AM, John Fremlin jf@msi.co.jp wrote:
I strongly recommend calling cffi:load-foreign-library at run time only (never before delivery). That gives you control over how the library is loaded on startup (for example reporting errors to the user).
[...]
Luis, would you consider a patch to add a special variable *cffi-wrapper-delay-load* which would set a special mode for the generated wrapper.lisps?
I'd rather solve the problem in a more general way. (And document it in the manual.) Every library out there calls LOAD-FOREIGN-LIBRARY at load-time. What can we do to make that work in delivery-friendly way? Not using absolute paths is one way, but is that not enough for the wrapper libs?
Perhaps CFFI can unload the foreign libraries before delivery and take care of reloading them later, thereby providing *FOREIGN-LIBRARY-DIRECTORIES* and, eventually, error-handling hooks?
Luís Oliveira luismbo@gmail.com writes:
On Fri, Jan 30, 2009 at 6:05 AM, John Fremlin jf@msi.co.jp wrote:
[...]
Luis, would you consider a patch to add a special variable *cffi-wrapper-delay-load* which would set a special mode for the generated wrapper.lisps?
I'd rather solve the problem in a more general way. (And document it in the manual.) Every library out there calls LOAD-FOREIGN-LIBRARY at load-time. What can we do to make that work in delivery-friendly way?
That's a really good point. Even if you give a quite extensive selection of places to search for a library in, when the image is dumped the one location where it was found on the dumping machine is stored in the image. Restarting the image will crash before reaching user code, if the library is not in the exact same place (as I understand it).
The ability to unload all libraries before dumping makes a lot of sense.
Not using absolute paths is one way, but is that not enough for the wrapper libs?
The C libraries for wrapper files are generated inside the source directory, or wherever asdf decides to put them. This generally means that delivery without source (or at least, without the structure of the source tree) is inconvenient.
It would be great if the list of the generated libraries could be queried at delivery time, so they can all be copied into a destination directory.
Then something like the patch I sent to try to load them from a selection of locations (perhaps these should be more user-configurable?) would solve the problem.
(Of course, it should also be possible to bundle them all up into a single library file and even store that in the Lisp image. But that sounds like more than a few lines of work...)
Perhaps CFFI can unload the foreign libraries before delivery and take care of reloading them later, thereby providing *FOREIGN-LIBRARY-DIRECTORIES* and, eventually, error-handling hooks?
A function that unloaded all libraries and we could call before dumping the image would be great.
John Fremlin jf@msi.co.jp writes:
Luís Oliveira luismbo@gmail.com writes:
On Fri, Jan 30, 2009 at 6:05 AM, John Fremlin jf@msi.co.jp wrote:
Luis, would you consider a patch to add a special variable *cffi-wrapper-delay-load* which would set a special mode for the generated wrapper.lisps?
I'd rather solve the problem in a more general way. (And document it in the manual.) Every library out there calls LOAD-FOREIGN-LIBRARY at load-time. What can we do to make that work in delivery-friendly way?
That's a really good point. Even if you give a quite extensive selection of places to search for a library in, when the image is dumped the one location where it was found on the dumping machine is stored in the image. Restarting the image will crash before reaching user code, if the library is not in the exact same place (as I understand it).
The ability to unload all libraries before dumping makes a lot of sense.
Here is a preliminary patch that does exactly that.
I have tested it very briefly on Lispworks 5.1.2 and SBCL 1.0.29, both AMD64 only.
Note that these features are not necessary for Allegro, which is our primary platform, as it copies non-standard shared objects into the delivery directory. This is to support testing our application on other platforms.
The patch is a little complex and touches on many areas.
Not using absolute paths is one way, but is that not enough for the wrapper libs?
The C libraries for wrapper files are generated inside the source directory, or wherever asdf decides to put them. This generally means that delivery without source (or at least, without the structure of the source tree) is inconvenient.
It would be great if the list of the generated libraries could be queried at delivery time, so they can all be copied into a destination directory.
Then something like the patch I sent to try to load them from a selection of locations (perhaps these should be more user-configurable?) would solve the problem.
(Of course, it should also be possible to bundle them all up into a single library file and even store that in the Lisp image. But that sounds like more than a few lines of work...)
Perhaps CFFI can unload the foreign libraries before delivery and take care of reloading them later, thereby providing *FOREIGN-LIBRARY-DIRECTORIES* and, eventually, error-handling hooks?
A function that unloaded all libraries and we could call before dumping the image would be great.
cffi-devel mailing list cffi-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/cffi-devel
[Sorry; I accidentally sent the last email before finishing it (C-c C-c instead of C-c C-e).]
The problem:
Using the excellent cffi-wrappers on platforms that are not Allegro does not work well with delivery, because the path to the generated shared object (presumably near the source code) is hardcoded into the image, and the image will crash very early, before entering user code if this shared object is not available.
The solution:
Close all shared libraries before delivery. After delivery reopen them, searching *foreign-library-directories*.
Brief summary of the patch:
(1) The wrapper generation code is changed to use define-foreign-library and not to explicitly wedge the path into the library name, but to put the directory where it is generated into *foreign-library-directories*.
(2) cffi:close-all-foreign-libraries closes all open defined foreign libraries. Call this before delivery.
(3) (cffi:do-reload-all-compile-time-open-libraries) is a macro that will load all the required libraries again. Put this in your main function.
What I want:
Feedback and comments; maybe this patch touches too much? Apparently Stelian started doing something a little similar but half finished? If it looks viable for the mainline, we can add test cases and documentation.
Examples:
Here are a couple of example build procedures that add the application's binary directory to *foreign-library-directories*, so that the .so files can be put there. (Maybe it would be better to fully set *foreign-library-directories*.)
I have tested it on AMD64 Linux Lispworks and SBCL.
For example, Lispworks:
(compile (defun main () (setf *debugger-hook* 'mtsnmp::mtsnmp-debugger-hook) (pushnew (make-pathname :name nil :type nil :version nil :defaults (truename (first sys:*line-arguments-list*))) cffi:*foreign-library-directories* :test 'equalp) (handler-case (progn (cffi:do-reload-all-compile-time-open-libraries) (prog1 (apply 'my-application:main sys:*line-arguments-list*) (finish-output))) (condition (c) (ignore-errors (format *error-output* "Error: ~A~&" c) (finish-output *error-output*)) (lispworks:quit :status 1))) (lispworks:quit :status 0)))
(cffi:close-all-foreign-libraries)
(deliver 'main (multiple-value-bind (second minute hour date month year day daylight-p zone) (get-decoded-time) (format nil "binaries/~4,'0D~2,'0D~2,'0D-~2,'0D~2,'0D-~A-~A-~A" year month date hour minute (lisp-implementation-type) (lisp-implementation-version) (expt 2 (ceiling (log (log most-positive-fixnum 2) 2))))) 1 :multiprocessing t :keep-eval t :keep-conditions :all :keep-top-level nil :keep-debug-mode nil)
For example, SBCL
(in-package #:cl-user)
(defun main () (unwind-protect (handler-case (progn (pushnew (make-pathname :name nil :type nil :version nil :defaults (truename (first *posix-argv*))) cffi:*foreign-library-directories* :test 'equalp) (cffi:do-reload-all-compile-time-open-libraries) (apply 'my-application:main *posix-argv*) (sb-ext:quit :unix-status 0)) (condition (c) (ignore-errors (format *error-output* "Error: ~A~&" c)) (sb-ext:quit :unix-status 1))) (sb-ext:quit :unix-status 100)))
(defun make-image () (sb-ext:disable-debugger)
(cffi:close-all-foreign-libraries) (sb-ext:save-lisp-and-die (multiple-value-bind (second minute hour date month year) (get-decoded-time) (declare (ignore second)) (format nil "binaries/~4,'0D~2,'0D~2,'0D-~2,'0D~2,'0D-~A-~A-~A" year month date hour minute (lisp-implementation-type) (lisp-implementation-version) (expt 2 (ceiling (log (log most-positive-fixnum 2) 2))))) :executable t :toplevel 'main :purify t))
(make-image)