diff --git a/README b/README index 266b4fd0ef2e42e5ed462ddeb3fc1cdd80037a36..8cb3ee96dd3dcbfdb19859ca50f9ef9075bd0ff6 100644 --- a/README +++ b/README @@ -17,36 +17,22 @@ the :compile-check extension to its compile-file* to build stuff. macro FINAL-FORMS () This macro will expand into any final forms so far registered. - You need to have finalizers enabled to use this macro. - In a file that has finalizers enabled, you MUST include (FINAL-FORMS) + The forms will be expanded inside an + (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) + but you can override that with your own EVAL-WHEN. + You need to have finalizers enabled to use this macro (see WITH-FINALIZERS). + In a file that uses finalizers, you MUST include (FINAL-FORMS) after the last finalizer was used and before the end of the file, or the compilation will fail. - -function CHECK-FINALIZERS-AROUND-COMPILE (FUN) - Assuming your system :depends-on (:asdf-finalizers), - you may use this function as your :around-compile function - for an ASDF system, module or file, as in - :around-compile "asdf-finalizers:check-finalizers-around-compile" - This will allow you to use finalizers within covered source files, - and will issue an error if you fail to evaluate (FINAL-FORMS) - after the last finalizer was used and before the end of the file. - Alternatively, you may use ASDF::FINALIZED-CL-SOURCE-FILE below. - You may also have your own custom :around-compile hooks - chain into CHECK-FINALIZERS-AROUND-COMPILE - to achieve the same effect and more. - -class ASDF::FINALIZED-CL-SOURCE-FILE (CL-SOURCE-FILE) - Assuming your system :defsystem-depends-on (:asdf-finalizers), - you may use this class as your system's :default-component-class, - or as the class of a component as in - (:finalized-cl-source-file "foo" :depends-on ("bar" "baz")) - This will automatically declare CHECK-FINALIZERS-AROUND-COMPILE - as the relevant component's :around-compile hook. + Typically, you will write (FINAL-FORMS) as the very last form in your file, + or if you didn't use the asdf-finalizers package, you will instead write + (ASDF-FINALIZERS:FINAL-FORMS). function EVAL-AT-TOPLEVEL (FORM &optional ALREADY-DONE-P-FORM WARNING &rest WARNING-ARGUMENTS) This function, to be used within a macro, deftype, reader-macro, etc., will evaluate toplevel FORM now during the macroexpansion phase, but also - register it to be evaluated at the toplevel before the end of current file, + register it to be evaluated at the toplevel as part of the FINAL-FORMS, + so that assuming you use the FINAL-FORMS afterwards but before the end of current file, so it is available to whoever load the associated FASL or CFASL. If the FORM has already been registered, it is skipped. Either now or when loading the (C?)FASL, the evaluation of FORM will be skipped @@ -57,24 +43,44 @@ function EVAL-AT-TOPLEVEL (FORM &optional ALREADY-DONE-P-FORM WARNING &rest WARN and a build from clean will hopefully catch him if he didn't. function REGISTER-FINAL-FORM (FORM) - Register a constant piece of code to the evaluated at toplevel + This function, to be used within a macro, reader-macro, deftype, etc., + will register a constant piece of code to the evaluated at toplevel at the end of the current code fragment (e.g. file). - If will be expanded inside an - (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) - but you can override that with your own eval-when. function REGISTER-FINALIZER (THUNK) - Register a THUNK to be called during finalization. - Any dependencies must be enforced by calling thunk dependencies. - Any form returned by the THUNK will be included in the finalized code. - If will be expanded inside an - (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) - but you can override that with your own eval-when. + This function, to be used within a macro, reader-macro, deftype, etc., + will register a THUNK to be called during finalization. + Dependencies may be enforced by thunk calling thunk dependencies. + Any form returned by the THUNK will be included in the finalized code + after the code from any previously registered thunk of constant code fragment, + and after the code from any registered dependency. -macro WITH-FINALIZERS (() &body BODY) +macro WITH-FINALIZERS ((&key FINALIZE) &body BODY) Evaluate BODY in a context where finalizers are enabled. By default, don't finalize, because we want to catch code that fails to finalize in the same file that requires code finalization. - For convenience, to test code at the REPL, if FINALIZE is provided and true, - evaluate finalization forms. + This macro is typically used by ASDF when you configure it as below. + For convenience, you may also use it to test code at the REPL; + you may then pass an argument FINALIZE with true value, + and WITH-FINALIZERS will evaluate finalization forms. + +function CHECK-FINALIZERS-AROUND-COMPILE (FUN) + Assuming your system :depends-on (:asdf-finalizers), + you may use this function as your :around-compile function + for an ASDF system, module or file, as in + :around-compile "asdf-finalizers:check-finalizers-around-compile" + This will allow you to use finalizers within covered source files, + and will issue an error if you fail to evaluate (FINAL-FORMS) + after the last finalizer was used and before the end of the file. + Alternatively, you may use ASDF::FINALIZED-CL-SOURCE-FILE below. + You may also have your own custom :around-compile hooks + chain into CHECK-FINALIZERS-AROUND-COMPILE + to achieve the same effect and more. +class ASDF::FINALIZED-CL-SOURCE-FILE (CL-SOURCE-FILE) + Assuming your system :defsystem-depends-on (:asdf-finalizers), + you may use this class as your system's :default-component-class, + or as the class of a component as in + (:finalized-cl-source-file "foo" :depends-on ("bar" "baz")) + This will automatically declare CHECK-FINALIZERS-AROUND-COMPILE + as the relevant component's :around-compile hook. diff --git a/asdf-support.lisp b/asdf-support.lisp index b00d36f60732ea9f7fce9db27201db7474ef35d1..49c05e3bafa9c829c9674d23fd245bacb2761229 100644 --- a/asdf-support.lisp +++ b/asdf-support.lisp @@ -13,8 +13,25 @@ okp)) (defun check-finalizers-around-compile (fun) + "Assuming your system :depends-on (:asdf-finalizers), + you may use this function as your :around-compile function + for an ASDF system, module or file, as in + :around-compile \"asdf-finalizers:check-finalizers-around-compile\" + This will allow you to use finalizers within covered source files, + and will issue an error if you fail to evaluate (FINAL-FORMS) + after the last finalizer was used and before the end of the file. + Alternatively, you may use ASDF::FINALIZED-CL-SOURCE-FILE below. + You may also have your own custom :around-compile hooks + chain into CHECK-FINALIZERS-AROUND-COMPILE + to achieve the same effect and more." (with-finalizers () (funcall fun :compile-check 'compile-check-finalizers))) (defclass asdf::finalized-cl-source-file (cl-source-file) - ((around-compile :initargs 'check-finalizers-around-compile))) + ((around-compile :initargs 'check-finalizers-around-compile)) + (:documentation "Assuming your system :defsystem-depends-on (:asdf-finalizers), + you may use this class as your system's :default-component-class, + or as the class of a component as in + (:finalized-cl-source-file \"foo\" :depends-on (\"bar\" \"baz\")) + This will automatically declare CHECK-FINALIZERS-AROUND-COMPILE + as the relevant component's :around-compile hook.")) diff --git a/finalizers.lisp b/finalizers.lisp index f8e0dfec24009a8aa1a8a49e6e4cc75db33fe3db..52d22ea9ccb901655ad6277d03e0bc77f31e8c32 100644 --- a/finalizers.lisp +++ b/finalizers.lisp @@ -30,13 +30,24 @@ off when your application is done compiled and you're at runtime.") (values)) (defmacro final-forms () - "Evaluate registered finalization thunks." + "This macro will expand into any final forms so far registered. + The forms will be expanded inside an + (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) + but you can override that with your own EVAL-WHEN. + You need to have finalizers enabled to use this macro (see WITH-FINALIZERS). + In a file that uses finalizers, you MUST include (FINAL-FORMS) + after the last finalizer was used and before the end of the file, + or the compilation will fail. + Typically, you will write (FINAL-FORMS) as the very last form in your file, + or if you didn't use the asdf-finalizers package, you will instead write + (ASDF-FINALIZERS:FINAL-FORMS)." `(eval-when (:compile-toplevel :load-toplevel :execute) - (final-forms-too))) + ;; This indirection is because some Lisps (e.g. CCL 1.8) may be confused + ;; if the state from *finalizers* isn't properly flushed by this eval-when. + (final-forms-internal))) -(defmacro final-forms-too () +(defmacro final-forms-internal () (when *finalizers* - ;;(eval-when (:compile-toplevel :execute) *finalizers*) ; trying to debug on CCL. (expand-final-forms))) (defun expand-final-forms () @@ -64,13 +75,12 @@ off when your application is done compiled and you're at runtime.") (defun register-finalizer (finalizer) - "Register a thunk to be called during finalization (if a function) -or a constant form to be included (if a cons). -Any dependencies must be enforced by calling thunk dependencies. -Any form returned by the thunk will be included in the finalized code. -It will be wrapped inside an - (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) -but you can override that with your own explicit eval-when." + "This function, to be used within a macro, reader-macro, deftype, etc., + will register a THUNK to be called during finalization. + Dependencies may be enforced by thunk calling thunk dependencies. + Any form returned by the THUNK will be included in the finalized code + after the code from any previously registered thunk of constant code fragment, + and after the code from any registered dependency." (check-type finalizer (or function cons)) (unless (using-finalizers-p) (error 'finalizers-off-simple-error @@ -82,11 +92,9 @@ but you can override that with your own explicit eval-when." (push finalizer *finalizers*)) (defun register-final-form (form) - "Register a constant piece of code to the evaluated at toplevel -at the end of the current code fragment (e.g. file). -It will be wrapped inside an - (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) ...) -but you can override that with your own explicit eval-when." + "This function, to be used within a macro, reader-macro, deftype, etc., + will register a constant piece of code to the evaluated at toplevel + at the end of the current code fragment (e.g. file)." (check-type form cons) (register-finalizer form)) @@ -98,10 +106,12 @@ but you can override that with your own explicit eval-when." (defmacro with-finalizers ((&key finalize) &body body) "Evaluate BODY in a context where finalizers are enabled. -By default, don't finalize, because we want to catch code -that fails to finalize in the same file that requires code finalization. -For convenience, to test code at the REPL, if FINALIZE is provided and true, -evaluate finalization forms." + By default, don't finalize, because we want to catch code + that fails to finalize in the same file that requires code finalization. + This macro is typically used by ASDF when you configure it as below. + For convenience, you may also use it to test code at the REPL; + you may then pass an argument FINALIZE with true value, + and WITH-FINALIZERS will evaluate finalization forms." `(call-with-finalizers #'(lambda () ,@body) :finalize ,finalize)) (defun call-with-finalizers (thunk &key finalize) @@ -114,16 +124,17 @@ evaluate finalization forms." (defun eval-at-toplevel (form &optional already-done-p-form warning &rest warning-arguments) "This function, to be used within a macro, deftype, reader-macro, etc., -will evaluate toplevel FORM now during the macroexpansion phase, but also -register it to be evaluated at the toplevel before the end of current file, -so it is available to whoever load the associated FASL or CFASL. -If the FORM has already been registered, it is skipped. -Either now or when loading the (C?)FASL, the evaluation of FORM will be skipped -when ALREADY-DONE-P-FORM evaluates to a true value. -When finalizers are not enabled, warn with given warning and arguments or -with a default warning, unless ALREADY-DONE-P-FORM evaluated to a true value, -at which point we trust the user to somehow have done the right thing, -and a build from clean will hopefully catch him if he didn't." + will evaluate toplevel FORM now during the macroexpansion phase, but also + register it to be evaluated at the toplevel as part of the FINAL-FORMS, + so that assuming you use the FINAL-FORMS afterwards but before the end of current file, + so it is available to whoever load the associated FASL or CFASL. + If the FORM has already been registered, it is skipped. + Either now or when loading the (C?)FASL, the evaluation of FORM will be skipped + when ALREADY-DONE-P-FORM evaluates to a true value. + When finalizers are not enabled, warn with given warning and arguments or + with a default warning, unless ALREADY-DONE-P-FORM evaluated to a true value, + at which point we trust the user to somehow have done the right thing, + and a build from clean will hopefully catch him if he didn't." (let ((whole `(eval-at-toplevel ,form ,already-done-p-form)) (already-done-p (eval already-done-p-form))) (unless already-done-p diff --git a/list-of.lisp b/list-of.lisp index 81da5a6fc200feee924fe8864e1527f017339da4..6140cafc52209a599e04a864068ad54aa1e4b135 100644 --- a/list-of.lisp +++ b/list-of.lisp @@ -31,7 +31,7 @@ ((nil) 'null) (otherwise (let ((predicate (list-of-predicate-for type))) - (eval-at-toplevel + (eval-at-toplevel ;; now, and amongst final-forms if enabled `(ensure-list-of-predicate ',type ',predicate) `(fboundp ',predicate) ;; hush unnecessary eval-at-toplevel warnings "Defining ~S outside of finalized Lisp code" `(list-of ,type))