lisp-build.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       lisp-build.lisp (43986B)
       ---
            1 ;;;; -------------------------------------------------------------------------
            2 ;;;; Support to build (compile and load) Lisp files
            3 
            4 (uiop/package:define-package :uiop/lisp-build
            5   (:nicknames :asdf/lisp-build) ;; OBSOLETE, used by slime/contrib/swank-asdf.lisp
            6   (:use :uiop/common-lisp :uiop/package :uiop/utility
            7    :uiop/os :uiop/pathname :uiop/filesystem :uiop/stream :uiop/image)
            8   (:export
            9    ;; Variables
           10    #:*compile-file-warnings-behaviour* #:*compile-file-failure-behaviour*
           11    #:*output-translation-function*
           12    #:*optimization-settings* #:*previous-optimization-settings*
           13    #:*base-build-directory*
           14    #:compile-condition #:compile-file-error #:compile-warned-error #:compile-failed-error
           15    #:compile-warned-warning #:compile-failed-warning
           16    #:check-lisp-compile-results #:check-lisp-compile-warnings
           17    #:*uninteresting-conditions* #:*usual-uninteresting-conditions*
           18    #:*uninteresting-compiler-conditions* #:*uninteresting-loader-conditions*
           19    ;; Types
           20    #+sbcl #:sb-grovel-unknown-constant-condition
           21    ;; Functions & Macros
           22    #:get-optimization-settings #:proclaim-optimization-settings #:with-optimization-settings
           23    #:call-with-muffled-compiler-conditions #:with-muffled-compiler-conditions
           24    #:call-with-muffled-loader-conditions #:with-muffled-loader-conditions
           25    #:reify-simple-sexp #:unreify-simple-sexp
           26    #:reify-deferred-warnings #:unreify-deferred-warnings
           27    #:reset-deferred-warnings #:save-deferred-warnings #:check-deferred-warnings
           28    #:with-saved-deferred-warnings #:warnings-file-p #:warnings-file-type #:*warnings-file-type*
           29    #:enable-deferred-warnings-check #:disable-deferred-warnings-check
           30    #:current-lisp-file-pathname #:load-pathname
           31    #:lispize-pathname #:compile-file-type #:call-around-hook
           32    #:compile-file* #:compile-file-pathname* #:*compile-check*
           33    #:load* #:load-from-string #:combine-fasls)
           34   (:intern #:defaults #:failure-p #:warnings-p #:s #:y #:body))
           35 (in-package :uiop/lisp-build)
           36 
           37 (with-upgradability ()
           38   (defvar *compile-file-warnings-behaviour*
           39     (or #+clisp :ignore :warn)
           40     "How should ASDF react if it encounters a warning when compiling a file?
           41 Valid values are :error, :warn, and :ignore.")
           42 
           43   (defvar *compile-file-failure-behaviour*
           44     (or #+(or mkcl sbcl) :error #+clisp :ignore :warn)
           45     "How should ASDF react if it encounters a failure (per the ANSI spec of COMPILE-FILE)
           46 when compiling a file, which includes any non-style-warning warning.
           47 Valid values are :error, :warn, and :ignore.
           48 Note that ASDF ALWAYS raises an error if it fails to create an output file when compiling.")
           49 
           50   (defvar *base-build-directory* nil
           51     "When set to a non-null value, it should be an absolute directory pathname,
           52 which will serve as the *DEFAULT-PATHNAME-DEFAULTS* around a COMPILE-FILE,
           53 what more while the input-file is shortened if possible to ENOUGH-PATHNAME relative to it.
           54 This can help you produce more deterministic output for FASLs."))
           55 
           56 ;;; Optimization settings
           57 (with-upgradability ()
           58   (defvar *optimization-settings* nil
           59     "Optimization settings to be used by PROCLAIM-OPTIMIZATION-SETTINGS")
           60   (defvar *previous-optimization-settings* nil
           61     "Optimization settings saved by PROCLAIM-OPTIMIZATION-SETTINGS")
           62   (defparameter +optimization-variables+
           63     ;; TODO: allegro genera corman mcl
           64     (or #+(or abcl xcl) '(system::*speed* system::*space* system::*safety* system::*debug*)
           65         #+clisp '() ;; system::*optimize* is a constant hash-table! (with non-constant contents)
           66         #+clozure '(ccl::*nx-speed* ccl::*nx-space* ccl::*nx-safety*
           67                     ccl::*nx-debug* ccl::*nx-cspeed*)
           68         #+(or cmucl scl) '(c::*default-cookie*)
           69         #+clasp nil
           70         #+ecl (unless (use-ecl-byte-compiler-p) '(c::*speed* c::*space* c::*safety* c::*debug*))
           71         #+gcl '(compiler::*speed* compiler::*space* compiler::*compiler-new-safety* compiler::*debug*)
           72         #+lispworks '(compiler::*optimization-level*)
           73         #+mkcl '(si::*speed* si::*space* si::*safety* si::*debug*)
           74         #+sbcl '(sb-c::*policy*)))
           75   (defun get-optimization-settings ()
           76     "Get current compiler optimization settings, ready to PROCLAIM again"
           77     #-(or abcl allegro clasp clisp clozure cmucl ecl lispworks mkcl sbcl scl xcl)
           78     (warn "~S does not support ~S. Please help me fix that."
           79           'get-optimization-settings (implementation-type))
           80     #+clasp (cleavir-env:optimize (cleavir-env:optimize-info CLASP-CLEAVIR:*CLASP-ENV*))
           81     #+(or abcl allegro clisp clozure cmucl ecl lispworks mkcl sbcl scl xcl)
           82     (let ((settings '(speed space safety debug compilation-speed #+(or cmucl scl) c::brevity)))
           83       #.`(loop #+(or allegro clozure)
           84                ,@'(:with info = #+allegro (sys:declaration-information 'optimize)
           85                    #+clozure (ccl:declaration-information 'optimize nil))
           86                :for x :in settings
           87                ,@(or #+(or abcl clasp ecl gcl mkcl xcl) '(:for v :in +optimization-variables+))
           88                :for y = (or #+(or allegro clozure) (second (assoc x info)) ; normalize order
           89                             #+clisp (gethash x system::*optimize* 1)
           90                             #+(or abcl ecl mkcl xcl) (symbol-value v)
           91                             #+(or cmucl scl) (slot-value c::*default-cookie*
           92                                                        (case x (compilation-speed 'c::cspeed)
           93                                                              (otherwise x)))
           94                             #+lispworks (slot-value compiler::*optimization-level* x)
           95                             #+sbcl (sb-c::policy-quality sb-c::*policy* x))
           96                :when y :collect (list x y))))
           97   (defun proclaim-optimization-settings ()
           98     "Proclaim the optimization settings in *OPTIMIZATION-SETTINGS*"
           99     (proclaim `(optimize ,@*optimization-settings*))
          100     (let ((settings (get-optimization-settings)))
          101       (unless (equal *previous-optimization-settings* settings)
          102         (setf *previous-optimization-settings* settings))))
          103   (defmacro with-optimization-settings ((&optional (settings *optimization-settings*)) &body body)
          104     #+(or allegro clasp clisp)
          105     (let ((previous-settings (gensym "PREVIOUS-SETTINGS"))
          106           (reset-settings (gensym "RESET-SETTINGS")))
          107       `(let* ((,previous-settings (get-optimization-settings))
          108               (,reset-settings #+clasp (reverse ,previous-settings) #-clasp ,previous-settings))
          109          ,@(when settings `((proclaim `(optimize ,@,settings))))
          110          (unwind-protect (progn ,@body)
          111            (proclaim `(optimize ,@,reset-settings)))))
          112     #-(or allegro clasp clisp)
          113     `(let ,(loop :for v :in +optimization-variables+ :collect `(,v ,v))
          114        ,@(when settings `((proclaim `(optimize ,@,settings))))
          115        ,@body)))
          116 
          117 
          118 ;;; Condition control
          119 (with-upgradability ()
          120   #+sbcl
          121   (progn
          122     (defun sb-grovel-unknown-constant-condition-p (c)
          123       "Detect SB-GROVEL unknown-constant conditions on older versions of SBCL"
          124       (and (typep c 'sb-int:simple-style-warning)
          125            (string-enclosed-p
          126             "Couldn't grovel for "
          127             (simple-condition-format-control c)
          128             " (unknown to the C compiler).")))
          129     (deftype sb-grovel-unknown-constant-condition ()
          130       '(and style-warning (satisfies sb-grovel-unknown-constant-condition-p))))
          131 
          132   (defvar *usual-uninteresting-conditions*
          133     (append
          134      ;;#+clozure '(ccl:compiler-warning)
          135      #+cmucl '("Deleting unreachable code.")
          136      #+lispworks '("~S being redefined in ~A (previously in ~A)."
          137                    "~S defined more than once in ~A.") ;; lispworks gets confused by eval-when.
          138      #+sbcl
          139      '(sb-c::simple-compiler-note
          140        "&OPTIONAL and &KEY found in the same lambda list: ~S"
          141        sb-kernel:undefined-alien-style-warning
          142        sb-grovel-unknown-constant-condition ; defined above.
          143        sb-ext:implicit-generic-function-warning ;; Controversial.
          144        sb-int:package-at-variance
          145        sb-kernel:uninteresting-redefinition
          146        ;; BEWARE: the below four are controversial to include here.
          147        sb-kernel:redefinition-with-defun
          148        sb-kernel:redefinition-with-defgeneric
          149        sb-kernel:redefinition-with-defmethod
          150        sb-kernel::redefinition-with-defmacro) ; not exported by old SBCLs
          151      #+sbcl
          152      (let ((condition (find-symbol* '#:lexical-environment-too-complex :sb-kernel nil)))
          153        (when condition
          154          (list condition)))
          155      '("No generic function ~S present when encountering macroexpansion of defmethod. Assuming it will be an instance of standard-generic-function.")) ;; from closer2mop
          156     "A suggested value to which to set or bind *uninteresting-conditions*.")
          157 
          158   (defvar *uninteresting-conditions* '()
          159     "Conditions that may be skipped while compiling or loading Lisp code.")
          160   (defvar *uninteresting-compiler-conditions* '()
          161     "Additional conditions that may be skipped while compiling Lisp code.")
          162   (defvar *uninteresting-loader-conditions*
          163     (append
          164      '("Overwriting already existing readtable ~S." ;; from named-readtables
          165        #(#:finalizers-off-warning :asdf-finalizers)) ;; from asdf-finalizers
          166      #+clisp '(clos::simple-gf-replacing-method-warning))
          167     "Additional conditions that may be skipped while loading Lisp code."))
          168 
          169 ;;;; ----- Filtering conditions while building -----
          170 (with-upgradability ()
          171   (defun call-with-muffled-compiler-conditions (thunk)
          172     "Call given THUNK in a context where uninteresting conditions and compiler conditions are muffled"
          173     (call-with-muffled-conditions
          174      thunk (append *uninteresting-conditions* *uninteresting-compiler-conditions*)))
          175   (defmacro with-muffled-compiler-conditions ((&optional) &body body)
          176     "Trivial syntax for CALL-WITH-MUFFLED-COMPILER-CONDITIONS"
          177     `(call-with-muffled-compiler-conditions #'(lambda () ,@body)))
          178   (defun call-with-muffled-loader-conditions (thunk)
          179     "Call given THUNK in a context where uninteresting conditions and loader conditions are muffled"
          180     (call-with-muffled-conditions
          181      thunk (append *uninteresting-conditions* *uninteresting-loader-conditions*)))
          182   (defmacro with-muffled-loader-conditions ((&optional) &body body)
          183     "Trivial syntax for CALL-WITH-MUFFLED-LOADER-CONDITIONS"
          184     `(call-with-muffled-loader-conditions #'(lambda () ,@body))))
          185 
          186 
          187 ;;;; Handle warnings and failures
          188 (with-upgradability ()
          189   (define-condition compile-condition (condition)
          190     ((context-format
          191       :initform nil :reader compile-condition-context-format :initarg :context-format)
          192      (context-arguments
          193       :initform nil :reader compile-condition-context-arguments :initarg :context-arguments)
          194      (description
          195       :initform nil :reader compile-condition-description :initarg :description))
          196     (:report (lambda (c s)
          197                (format s (compatfmt "~@<~A~@[ while ~?~]~@:>")
          198                        (or (compile-condition-description c) (type-of c))
          199                        (compile-condition-context-format c)
          200                        (compile-condition-context-arguments c)))))
          201   (define-condition compile-file-error (compile-condition error) ())
          202   (define-condition compile-warned-warning (compile-condition warning) ())
          203   (define-condition compile-warned-error (compile-condition error) ())
          204   (define-condition compile-failed-warning (compile-condition warning) ())
          205   (define-condition compile-failed-error (compile-condition error) ())
          206 
          207   (defun check-lisp-compile-warnings (warnings-p failure-p
          208                                                   &optional context-format context-arguments)
          209     "Given the warnings or failures as resulted from COMPILE-FILE or checking deferred warnings,
          210 raise an error or warning as appropriate"
          211     (when failure-p
          212       (case *compile-file-failure-behaviour*
          213         (:warn (warn 'compile-failed-warning
          214                      :description "Lisp compilation failed"
          215                      :context-format context-format
          216                      :context-arguments context-arguments))
          217         (:error (error 'compile-failed-error
          218                        :description "Lisp compilation failed"
          219                        :context-format context-format
          220                        :context-arguments context-arguments))
          221         (:ignore nil)))
          222     (when warnings-p
          223       (case *compile-file-warnings-behaviour*
          224         (:warn (warn 'compile-warned-warning
          225                      :description "Lisp compilation had style-warnings"
          226                      :context-format context-format
          227                      :context-arguments context-arguments))
          228         (:error (error 'compile-warned-error
          229                        :description "Lisp compilation had style-warnings"
          230                        :context-format context-format
          231                        :context-arguments context-arguments))
          232         (:ignore nil))))
          233 
          234   (defun check-lisp-compile-results (output warnings-p failure-p
          235                                              &optional context-format context-arguments)
          236     "Given the results of COMPILE-FILE, raise an error or warning as appropriate"
          237     (unless output
          238       (error 'compile-file-error :context-format context-format :context-arguments context-arguments))
          239     (check-lisp-compile-warnings warnings-p failure-p context-format context-arguments)))
          240 
          241 
          242 ;;;; Deferred-warnings treatment, originally implemented by Douglas Katzman.
          243 ;;;
          244 ;;; To support an implementation, three functions must be implemented:
          245 ;;; reify-deferred-warnings unreify-deferred-warnings reset-deferred-warnings
          246 ;;; See their respective docstrings.
          247 (with-upgradability ()
          248   (defun reify-simple-sexp (sexp)
          249     "Given a simple SEXP, return a representation of it as a portable SEXP.
          250 Simple means made of symbols, numbers, characters, simple-strings, pathnames, cons cells."
          251     (etypecase sexp
          252       (symbol (reify-symbol sexp))
          253       ((or number character simple-string pathname) sexp)
          254       (cons (cons (reify-simple-sexp (car sexp)) (reify-simple-sexp (cdr sexp))))
          255       (simple-vector (vector (mapcar 'reify-simple-sexp (coerce sexp 'list))))))
          256 
          257   (defun unreify-simple-sexp (sexp)
          258     "Given the portable output of REIFY-SIMPLE-SEXP, return the simple SEXP it represents"
          259     (etypecase sexp
          260       ((or symbol number character simple-string pathname) sexp)
          261       (cons (cons (unreify-simple-sexp (car sexp)) (unreify-simple-sexp (cdr sexp))))
          262       ((simple-vector 2) (unreify-symbol sexp))
          263       ((simple-vector 1) (coerce (mapcar 'unreify-simple-sexp (aref sexp 0)) 'vector))))
          264 
          265   #+clozure
          266   (progn
          267     (defun reify-source-note (source-note)
          268       (when source-note
          269         (with-accessors ((source ccl::source-note-source) (filename ccl:source-note-filename)
          270                          (start-pos ccl:source-note-start-pos) (end-pos ccl:source-note-end-pos)) source-note
          271           (declare (ignorable source))
          272           (list :filename filename :start-pos start-pos :end-pos end-pos
          273                 #|:source (reify-source-note source)|#))))
          274     (defun unreify-source-note (source-note)
          275       (when source-note
          276         (destructuring-bind (&key filename start-pos end-pos source) source-note
          277           (ccl::make-source-note :filename filename :start-pos start-pos :end-pos end-pos
          278                                  :source (unreify-source-note source)))))
          279     (defun unsymbolify-function-name (name)
          280       (if-let (setfed (gethash name ccl::%setf-function-name-inverses%))
          281         `(setf ,setfed)
          282         name))
          283     (defun symbolify-function-name (name)
          284       (if (and (consp name) (eq (first name) 'setf))
          285           (let ((setfed (second name)))
          286             (gethash setfed ccl::%setf-function-names%))
          287           name))
          288     (defun reify-function-name (function-name)
          289       (let ((name (or (first function-name) ;; defun: extract the name
          290                       (let ((sec (second function-name)))
          291                         (or (and (atom sec) sec) ; scoped method: drop scope
          292                             (first sec)))))) ; method: keep gf name, drop method specializers
          293         (list name)))
          294     (defun unreify-function-name (function-name)
          295       function-name)
          296     (defun nullify-non-literals (sexp)
          297       (typecase sexp
          298         ((or number character simple-string symbol pathname) sexp)
          299         (cons (cons (nullify-non-literals (car sexp))
          300                     (nullify-non-literals (cdr sexp))))
          301         (t nil)))
          302     (defun reify-deferred-warning (deferred-warning)
          303       (with-accessors ((warning-type ccl::compiler-warning-warning-type)
          304                        (args ccl::compiler-warning-args)
          305                        (source-note ccl:compiler-warning-source-note)
          306                        (function-name ccl:compiler-warning-function-name)) deferred-warning
          307         (list :warning-type warning-type :function-name (reify-function-name function-name)
          308               :source-note (reify-source-note source-note)
          309               :args (destructuring-bind (fun &rest more)
          310                         args
          311                       (cons (unsymbolify-function-name fun)
          312                             (nullify-non-literals more))))))
          313     (defun unreify-deferred-warning (reified-deferred-warning)
          314       (destructuring-bind (&key warning-type function-name source-note args)
          315           reified-deferred-warning
          316         (make-condition (or (cdr (ccl::assq warning-type ccl::*compiler-whining-conditions*))
          317                             'ccl::compiler-warning)
          318                         :function-name (unreify-function-name function-name)
          319                         :source-note (unreify-source-note source-note)
          320                         :warning-type warning-type
          321                         :args (destructuring-bind (fun . more) args
          322                                 (cons (symbolify-function-name fun) more))))))
          323   #+(or cmucl scl)
          324   (defun reify-undefined-warning (warning)
          325     ;; Extracting undefined-warnings from the compilation-unit
          326     ;; To be passed through the above reify/unreify link, it must be a "simple-sexp"
          327     (list*
          328      (c::undefined-warning-kind warning)
          329      (c::undefined-warning-name warning)
          330      (c::undefined-warning-count warning)
          331      (mapcar
          332       #'(lambda (frob)
          333           ;; the lexenv slot can be ignored for reporting purposes
          334           `(:enclosing-source ,(c::compiler-error-context-enclosing-source frob)
          335             :source ,(c::compiler-error-context-source frob)
          336             :original-source ,(c::compiler-error-context-original-source frob)
          337             :context ,(c::compiler-error-context-context frob)
          338             :file-name ,(c::compiler-error-context-file-name frob) ; a pathname
          339             :file-position ,(c::compiler-error-context-file-position frob) ; an integer
          340             :original-source-path ,(c::compiler-error-context-original-source-path frob)))
          341       (c::undefined-warning-warnings warning))))
          342 
          343   #+sbcl
          344   (defun reify-undefined-warning (warning)
          345     ;; Extracting undefined-warnings from the compilation-unit
          346     ;; To be passed through the above reify/unreify link, it must be a "simple-sexp"
          347     (list*
          348      (sb-c::undefined-warning-kind warning)
          349      (sb-c::undefined-warning-name warning)
          350      (sb-c::undefined-warning-count warning)
          351      ;; the COMPILER-ERROR-CONTEXT struct has changed in SBCL, which means how we
          352      ;; handle deferred warnings must change... TODO: when enough time has
          353      ;; gone by, just assume all versions of SBCL are adequately
          354      ;; up-to-date, and cut this material.[2018/05/30:rpg]
          355      (mapcar
          356       #'(lambda (frob)
          357           ;; the lexenv slot can be ignored for reporting purposes
          358           `(
          359             #- #.(uiop/utility:symbol-test-to-feature-expression '#:compiler-error-context-%source '#:sb-c)
          360             ,@`(:enclosing-source
          361                 ,(sb-c::compiler-error-context-enclosing-source frob)
          362                 :source
          363                 ,(sb-c::compiler-error-context-source frob)
          364                 :original-source
          365                 ,(sb-c::compiler-error-context-original-source frob))
          366             #+ #.(uiop/utility:symbol-test-to-feature-expression '#:compiler-error-context-%source '#:sb-c)
          367             ,@ `(:%enclosing-source
          368                  ,(sb-c::compiler-error-context-enclosing-source frob)
          369                  :%source
          370                  ,(sb-c::compiler-error-context-source frob)
          371                  :original-form
          372                  ,(sb-c::compiler-error-context-original-form frob))
          373             :context ,(sb-c::compiler-error-context-context frob)
          374             :file-name ,(sb-c::compiler-error-context-file-name frob) ; a pathname
          375             :file-position ,(sb-c::compiler-error-context-file-position frob) ; an integer
          376             :original-source-path ,(sb-c::compiler-error-context-original-source-path frob)))
          377       (sb-c::undefined-warning-warnings warning))))
          378 
          379   (defun reify-deferred-warnings ()
          380     "return a portable S-expression, portably readable and writeable in any Common Lisp implementation
          381 using READ within a WITH-SAFE-IO-SYNTAX, that represents the warnings currently deferred by
          382 WITH-COMPILATION-UNIT. One of three functions required for deferred-warnings support in ASDF."
          383     #+allegro
          384     (list :functions-defined excl::.functions-defined.
          385           :functions-called excl::.functions-called.)
          386     #+clozure
          387     (mapcar 'reify-deferred-warning
          388             (if-let (dw ccl::*outstanding-deferred-warnings*)
          389               (let ((mdw (ccl::ensure-merged-deferred-warnings dw)))
          390                 (ccl::deferred-warnings.warnings mdw))))
          391     #+(or cmucl scl)
          392     (when lisp::*in-compilation-unit*
          393       ;; Try to send nothing through the pipe if nothing needs to be accumulated
          394       `(,@(when c::*undefined-warnings*
          395             `((c::*undefined-warnings*
          396                ,@(mapcar #'reify-undefined-warning c::*undefined-warnings*))))
          397         ,@(loop :for what :in '(c::*compiler-error-count*
          398                                 c::*compiler-warning-count*
          399                                 c::*compiler-note-count*)
          400                 :for value = (symbol-value what)
          401                 :when (plusp value)
          402                   :collect `(,what . ,value))))
          403     #+sbcl
          404     (when sb-c::*in-compilation-unit*
          405       ;; Try to send nothing through the pipe if nothing needs to be accumulated
          406       `(,@(when sb-c::*undefined-warnings*
          407             `((sb-c::*undefined-warnings*
          408                ,@(mapcar #'reify-undefined-warning sb-c::*undefined-warnings*))))
          409         ,@(loop :for what :in '(sb-c::*aborted-compilation-unit-count*
          410                                 sb-c::*compiler-error-count*
          411                                 sb-c::*compiler-warning-count*
          412                                 sb-c::*compiler-style-warning-count*
          413                                 sb-c::*compiler-note-count*)
          414                 :for value = (symbol-value what)
          415                 :when (plusp value)
          416                   :collect `(,what . ,value)))))
          417 
          418   (defun unreify-deferred-warnings (reified-deferred-warnings)
          419     "given a S-expression created by REIFY-DEFERRED-WARNINGS, reinstantiate the corresponding
          420 deferred warnings as to be handled at the end of the current WITH-COMPILATION-UNIT.
          421 Handle any warning that has been resolved already,
          422 such as an undefined function that has been defined since.
          423 One of three functions required for deferred-warnings support in ASDF."
          424     (declare (ignorable reified-deferred-warnings))
          425     #+allegro
          426     (destructuring-bind (&key functions-defined functions-called)
          427         reified-deferred-warnings
          428       (setf excl::.functions-defined.
          429             (append functions-defined excl::.functions-defined.)
          430             excl::.functions-called.
          431             (append functions-called excl::.functions-called.)))
          432     #+clozure
          433     (let ((dw (or ccl::*outstanding-deferred-warnings*
          434                   (setf ccl::*outstanding-deferred-warnings* (ccl::%defer-warnings t)))))
          435       (appendf (ccl::deferred-warnings.warnings dw)
          436                (mapcar 'unreify-deferred-warning reified-deferred-warnings)))
          437     #+(or cmucl scl)
          438     (dolist (item reified-deferred-warnings)
          439       ;; Each item is (symbol . adjustment) where the adjustment depends on the symbol.
          440       ;; For *undefined-warnings*, the adjustment is a list of initargs.
          441       ;; For everything else, it's an integer.
          442       (destructuring-bind (symbol . adjustment) item
          443         (case symbol
          444           ((c::*undefined-warnings*)
          445            (setf c::*undefined-warnings*
          446                  (nconc (mapcan
          447                          #'(lambda (stuff)
          448                              (destructuring-bind (kind name count . rest) stuff
          449                                (unless (case kind (:function (fboundp name)))
          450                                  (list
          451                                   (c::make-undefined-warning
          452                                    :name name
          453                                    :kind kind
          454                                    :count count
          455                                    :warnings
          456                                    (mapcar #'(lambda (x)
          457                                                (apply #'c::make-compiler-error-context x))
          458                                            rest))))))
          459                          adjustment)
          460                         c::*undefined-warnings*)))
          461           (otherwise
          462            (set symbol (+ (symbol-value symbol) adjustment))))))
          463     #+sbcl
          464     (dolist (item reified-deferred-warnings)
          465       ;; Each item is (symbol . adjustment) where the adjustment depends on the symbol.
          466       ;; For *undefined-warnings*, the adjustment is a list of initargs.
          467       ;; For everything else, it's an integer.
          468       (destructuring-bind (symbol . adjustment) item
          469         (case symbol
          470           ((sb-c::*undefined-warnings*)
          471            (setf sb-c::*undefined-warnings*
          472                  (nconc (mapcan
          473                          #'(lambda (stuff)
          474                              (destructuring-bind (kind name count . rest) stuff
          475                                (unless (case kind (:function (fboundp name)))
          476                                  (list
          477                                   (sb-c::make-undefined-warning
          478                                    :name name
          479                                    :kind kind
          480                                    :count count
          481                                    :warnings
          482                                    (mapcar #'(lambda (x)
          483                                                (apply #'sb-c::make-compiler-error-context x))
          484                                            rest))))))
          485                          adjustment)
          486                         sb-c::*undefined-warnings*)))
          487           (otherwise
          488            (set symbol (+ (symbol-value symbol) adjustment)))))))
          489 
          490   (defun reset-deferred-warnings ()
          491     "Reset the set of deferred warnings to be handled at the end of the current WITH-COMPILATION-UNIT.
          492 One of three functions required for deferred-warnings support in ASDF."
          493     #+allegro
          494     (setf excl::.functions-defined. nil
          495           excl::.functions-called. nil)
          496     #+clozure
          497     (if-let (dw ccl::*outstanding-deferred-warnings*)
          498       (let ((mdw (ccl::ensure-merged-deferred-warnings dw)))
          499         (setf (ccl::deferred-warnings.warnings mdw) nil)))
          500     #+(or cmucl scl)
          501     (when lisp::*in-compilation-unit*
          502       (setf c::*undefined-warnings* nil
          503             c::*compiler-error-count* 0
          504             c::*compiler-warning-count* 0
          505             c::*compiler-note-count* 0))
          506     #+sbcl
          507     (when sb-c::*in-compilation-unit*
          508       (setf sb-c::*undefined-warnings* nil
          509             sb-c::*aborted-compilation-unit-count* 0
          510             sb-c::*compiler-error-count* 0
          511             sb-c::*compiler-warning-count* 0
          512             sb-c::*compiler-style-warning-count* 0
          513             sb-c::*compiler-note-count* 0)))
          514 
          515   (defun save-deferred-warnings (warnings-file)
          516     "Save forward reference conditions so they may be issued at a latter time,
          517 possibly in a different process."
          518     (with-open-file (s warnings-file :direction :output :if-exists :supersede
          519                        :element-type *default-stream-element-type*
          520                        :external-format *utf-8-external-format*)
          521       (with-safe-io-syntax ()
          522         (let ((*read-eval* t))
          523           (write (reify-deferred-warnings) :stream s :pretty t :readably t))
          524         (terpri s))))
          525 
          526   (defun warnings-file-type (&optional implementation-type)
          527     "The pathname type for warnings files on given IMPLEMENTATION-TYPE,
          528 where NIL designates the current one"
          529     (case (or implementation-type *implementation-type*)
          530       ((:acl :allegro) "allegro-warnings")
          531       ;;((:clisp) "clisp-warnings")
          532       ((:cmu :cmucl) "cmucl-warnings")
          533       ((:sbcl) "sbcl-warnings")
          534       ((:clozure :ccl) "ccl-warnings")
          535       ((:scl) "scl-warnings")))
          536 
          537   (defvar *warnings-file-type* nil
          538     "Pathname type for warnings files, or NIL if disabled")
          539 
          540   (defun enable-deferred-warnings-check ()
          541     "Enable the saving of deferred warnings"
          542     (setf *warnings-file-type* (warnings-file-type)))
          543 
          544   (defun disable-deferred-warnings-check ()
          545     "Disable the saving of deferred warnings"
          546     (setf *warnings-file-type* nil))
          547 
          548   (defun warnings-file-p (file &optional implementation-type)
          549     "Is FILE a saved warnings file for the given IMPLEMENTATION-TYPE?
          550 If that given type is NIL, use the currently configured *WARNINGS-FILE-TYPE* instead."
          551     (if-let (type (if implementation-type
          552                       (warnings-file-type implementation-type)
          553                       *warnings-file-type*))
          554       (equal (pathname-type file) type)))
          555 
          556   (defun check-deferred-warnings (files &optional context-format context-arguments)
          557     "Given a list of FILES containing deferred warnings saved by CALL-WITH-SAVED-DEFERRED-WARNINGS,
          558 re-intern and raise any warnings that are still meaningful."
          559     (let ((file-errors nil)
          560           (failure-p nil)
          561           (warnings-p nil))
          562       (handler-bind
          563           ((warning #'(lambda (c)
          564                         (setf warnings-p t)
          565                         (unless (typep c 'style-warning)
          566                           (setf failure-p t)))))
          567         (with-compilation-unit (:override t)
          568           (reset-deferred-warnings)
          569           (dolist (file files)
          570             (unreify-deferred-warnings
          571              (handler-case
          572                  (with-safe-io-syntax ()
          573                    (let ((*read-eval* t))
          574                      (read-file-form file)))
          575                (error (c)
          576                  ;;(delete-file-if-exists file) ;; deleting forces rebuild but prevents debugging
          577                  (push c file-errors)
          578                  nil))))))
          579       (dolist (error file-errors) (error error))
          580       (check-lisp-compile-warnings
          581        (or failure-p warnings-p) failure-p context-format context-arguments)))
          582 
          583   #|
          584   Mini-guide to adding support for deferred warnings on an implementation.
          585 
          586   First, look at what such a warning looks like:
          587 
          588   (describe
          589   (handler-case
          590   (and (eval '(lambda () (some-undefined-function))) nil)
          591   (t (c) c)))
          592 
          593   Then you can grep for the condition type in your compiler sources
          594   and see how to catch those that have been deferred,
          595   and/or read, clear and restore the deferred list.
          596 
          597   Also look at
          598   (macroexpand-1 '(with-compilation-unit () foo))
          599   |#
          600 
          601   (defun call-with-saved-deferred-warnings (thunk warnings-file &key source-namestring)
          602     "If WARNINGS-FILE is not nil, record the deferred-warnings around a call to THUNK
          603 and save those warnings to the given file for latter use,
          604 possibly in a different process. Otherwise just call THUNK."
          605     (declare (ignorable source-namestring))
          606     (if warnings-file
          607         (with-compilation-unit (:override t #+sbcl :source-namestring #+sbcl source-namestring)
          608           (unwind-protect
          609                (let (#+sbcl (sb-c::*undefined-warnings* nil))
          610                  (multiple-value-prog1
          611                      (funcall thunk)
          612                    (save-deferred-warnings warnings-file)))
          613             (reset-deferred-warnings)))
          614         (funcall thunk)))
          615 
          616   (defmacro with-saved-deferred-warnings ((warnings-file &key source-namestring) &body body)
          617     "Trivial syntax for CALL-WITH-SAVED-DEFERRED-WARNINGS"
          618     `(call-with-saved-deferred-warnings
          619       #'(lambda () ,@body) ,warnings-file :source-namestring ,source-namestring)))
          620 
          621 
          622 ;;; from ASDF
          623 (with-upgradability ()
          624   (defun current-lisp-file-pathname ()
          625     "Portably return the PATHNAME of the current Lisp source file being compiled or loaded"
          626     (or *compile-file-pathname* *load-pathname*))
          627 
          628   (defun load-pathname ()
          629     "Portably return the LOAD-PATHNAME of the current source file or fasl.
          630     May return a relative pathname."
          631     *load-pathname*) ;; magic no longer needed for GCL.
          632 
          633   (defun lispize-pathname (input-file)
          634     "From a INPUT-FILE pathname, return a corresponding .lisp source pathname"
          635     (make-pathname :type "lisp" :defaults input-file))
          636 
          637   (defun compile-file-type (&rest keys)
          638     "pathname TYPE for lisp FASt Loading files"
          639     (declare (ignorable keys))
          640     #-(or clasp ecl mkcl) (load-time-value (pathname-type (compile-file-pathname "foo.lisp")))
          641     #+(or clasp ecl mkcl) (pathname-type (apply 'compile-file-pathname "foo" keys)))
          642 
          643   (defun call-around-hook (hook function)
          644     "Call a HOOK around the execution of FUNCTION"
          645     (call-function (or hook 'funcall) function))
          646 
          647   (defun compile-file-pathname* (input-file &rest keys &key output-file &allow-other-keys)
          648     "Variant of COMPILE-FILE-PATHNAME that works well with COMPILE-FILE*"
          649     (let* ((keys
          650              (remove-plist-keys `(#+(or (and allegro (not (version>= 8 2)))) :external-format
          651                                     ,@(unless output-file '(:output-file))) keys)))
          652       (if (absolute-pathname-p output-file)
          653           ;; what cfp should be doing, w/ mp* instead of mp
          654           (let* ((type (pathname-type (apply 'compile-file-type keys)))
          655                  (defaults (make-pathname
          656                             :type type :defaults (merge-pathnames* input-file))))
          657             (merge-pathnames* output-file defaults))
          658           (funcall *output-translation-function*
          659                    (apply 'compile-file-pathname input-file keys)))))
          660 
          661   (defvar *compile-check* nil
          662     "A hook for user-defined compile-time invariants")
          663 
          664   (defun* (compile-file*) (input-file &rest keys
          665                                       &key (compile-check *compile-check*) output-file warnings-file
          666                                       #+clisp lib-file #+(or clasp ecl mkcl) object-file #+sbcl emit-cfasl
          667                                       &allow-other-keys)
          668     "This function provides a portable wrapper around COMPILE-FILE.
          669 It ensures that the OUTPUT-FILE value is only returned and
          670 the file only actually created if the compilation was successful,
          671 even though your implementation may not do that. It also checks an optional
          672 user-provided consistency function COMPILE-CHECK to determine success;
          673 it will call this function if not NIL at the end of the compilation
          674 with the arguments sent to COMPILE-FILE*, except with :OUTPUT-FILE TMP-FILE
          675 where TMP-FILE is the name of a temporary output-file.
          676 It also checks two flags (with legacy british spelling from ASDF1),
          677 *COMPILE-FILE-FAILURE-BEHAVIOUR* and *COMPILE-FILE-WARNINGS-BEHAVIOUR*
          678 with appropriate implementation-dependent defaults,
          679 and if a failure (respectively warnings) are reported by COMPILE-FILE,
          680 it will consider that an error unless the respective behaviour flag
          681 is one of :SUCCESS :WARN :IGNORE.
          682 If WARNINGS-FILE is defined, deferred warnings are saved to that file.
          683 On ECL or MKCL, it creates both the linkable object and loadable fasl files.
          684 On implementations that erroneously do not recognize standard keyword arguments,
          685 it will filter them appropriately."
          686     #+(or clasp ecl)
          687     (when (and object-file (equal (compile-file-type) (pathname object-file)))
          688       (format t "Whoa, some funky ASDF upgrade switched ~S calling convention for ~S and ~S~%"
          689               'compile-file* output-file object-file)
          690       (rotatef output-file object-file))
          691     (let* ((keywords (remove-plist-keys
          692                       `(:output-file :compile-check :warnings-file
          693                                      #+clisp :lib-file #+(or clasp ecl mkcl) :object-file) keys))
          694            (output-file
          695              (or output-file
          696                  (apply 'compile-file-pathname* input-file :output-file output-file keywords)))
          697            (physical-output-file (physicalize-pathname output-file))
          698            #+(or clasp ecl)
          699            (object-file
          700              (unless (use-ecl-byte-compiler-p)
          701                (or object-file
          702                    #+ecl (compile-file-pathname output-file :type :object)
          703                    #+clasp (compile-file-pathname output-file :output-type :object))))
          704            #+mkcl
          705            (object-file
          706              (or object-file
          707                  (compile-file-pathname output-file :fasl-p nil)))
          708            (tmp-file (tmpize-pathname physical-output-file))
          709            #+clasp
          710            (tmp-object-file (compile-file-pathname tmp-file :output-type :object))
          711            #+sbcl
          712            (cfasl-file (etypecase emit-cfasl
          713                          (null nil)
          714                          ((eql t) (make-pathname :type "cfasl" :defaults physical-output-file))
          715                          (string (parse-namestring emit-cfasl))
          716                          (pathname emit-cfasl)))
          717            #+sbcl
          718            (tmp-cfasl (when cfasl-file (make-pathname :type "cfasl" :defaults tmp-file)))
          719            #+clisp
          720            (tmp-lib (make-pathname :type "lib" :defaults tmp-file)))
          721       (multiple-value-bind (output-truename warnings-p failure-p)
          722           (with-enough-pathname (input-file :defaults *base-build-directory*)
          723             (with-saved-deferred-warnings (warnings-file :source-namestring (namestring input-file))
          724               (with-muffled-compiler-conditions ()
          725                 (or #-(or clasp ecl mkcl)
          726                     (let (#+genera (si:*common-lisp-syntax-is-ansi-common-lisp* t))
          727                       (apply 'compile-file input-file :output-file tmp-file
          728                              #+sbcl (if emit-cfasl (list* :emit-cfasl tmp-cfasl keywords) keywords)
          729                              #-sbcl keywords))
          730                     #+ecl (apply 'compile-file input-file :output-file
          731                                 (if object-file
          732                                     (list* object-file :system-p t keywords)
          733                                     (list* tmp-file keywords)))
          734                     #+clasp (apply 'compile-file input-file :output-file
          735                                   (if object-file
          736                                       (list* tmp-object-file :output-type :object #|:system-p t|# keywords)
          737                                       (list* tmp-file keywords)))
          738                     #+mkcl (apply 'compile-file input-file
          739                                   :output-file object-file :fasl-p nil keywords)))))
          740         (cond
          741           ((and output-truename
          742                 (flet ((check-flag (flag behaviour)
          743                          (or (not flag) (member behaviour '(:success :warn :ignore)))))
          744                   (and (check-flag failure-p *compile-file-failure-behaviour*)
          745                        (check-flag warnings-p *compile-file-warnings-behaviour*)))
          746                 (progn
          747                   #+(or clasp ecl mkcl)
          748                   (when (and #+(or clasp ecl) object-file)
          749                     (setf output-truename
          750                           (compiler::build-fasl tmp-file
          751                            #+(or clasp ecl) :lisp-files #+mkcl :lisp-object-files (list #+clasp tmp-object-file #-clasp object-file))))
          752                   (or (not compile-check)
          753                       (apply compile-check input-file
          754                              :output-file output-truename
          755                              keywords))))
          756            (delete-file-if-exists physical-output-file)
          757            (when output-truename
          758              ;; see CLISP bug 677
          759              #+clisp
          760              (progn
          761                (setf tmp-lib (make-pathname :type "lib" :defaults output-truename))
          762                (unless lib-file (setf lib-file (make-pathname :type "lib" :defaults physical-output-file)))
          763                (rename-file-overwriting-target tmp-lib lib-file))
          764              #+sbcl (when cfasl-file (rename-file-overwriting-target tmp-cfasl cfasl-file))
          765              #+clasp
          766              (progn
          767                ;;; the following 4 rename-file-overwriting-target better be atomic, but we can't implement this right now
          768                #+:target-os-darwin
          769                (let ((temp-dwarf (pathname (strcat (namestring output-truename) ".dwarf")))
          770                      (target-dwarf (pathname (strcat (namestring physical-output-file) ".dwarf"))))
          771                  (when (probe-file temp-dwarf)
          772                    (rename-file-overwriting-target temp-dwarf target-dwarf)))
          773                ;;; need to rename the bc or ll file as well or test-bundle.script fails
          774                ;;; They might not exist with parallel compilation
          775                (let ((bitcode-src (compile-file-pathname tmp-file :output-type :bitcode))
          776                      (bitcode-target (compile-file-pathname physical-output-file :output-type :bitcode)))
          777                  (when (probe-file bitcode-src)
          778                    (rename-file-overwriting-target bitcode-src bitcode-target)))
          779                (rename-file-overwriting-target tmp-object-file object-file))
          780              (rename-file-overwriting-target output-truename physical-output-file)
          781              (setf output-truename (truename physical-output-file)))
          782            #+clasp (delete-file-if-exists tmp-file)
          783            #+clisp (progn (delete-file-if-exists tmp-file) ;; this one works around clisp BUG 677
          784                           (delete-file-if-exists tmp-lib))) ;; this one is "normal" defensive cleanup
          785           (t ;; error or failed check
          786            (delete-file-if-exists output-truename)
          787            #+clisp (delete-file-if-exists tmp-lib)
          788            #+sbcl (delete-file-if-exists tmp-cfasl)
          789            (setf output-truename nil)))
          790         (values output-truename warnings-p failure-p))))
          791 
          792   (defun load* (x &rest keys &key &allow-other-keys)
          793     "Portable wrapper around LOAD that properly handles loading from a stream."
          794     (with-muffled-loader-conditions ()
          795       (let (#+genera (si:*common-lisp-syntax-is-ansi-common-lisp* t))
          796         (etypecase x
          797           ((or pathname string #-(or allegro clozure genera) stream #+clozure file-stream)
          798            (apply 'load x keys))
          799           ;; Genera can't load from a string-input-stream
          800           ;; ClozureCL 1.6 can only load from file input stream
          801           ;; Allegro 5, I don't remember but it must have been broken when I tested.
          802           #+(or allegro clozure genera)
          803           (stream ;; make do this way
          804            (let ((*package* *package*)
          805                  (*readtable* *readtable*)
          806                  (*load-pathname* nil)
          807                  (*load-truename* nil))
          808              (eval-input x)))))))
          809 
          810   (defun load-from-string (string)
          811     "Portably read and evaluate forms from a STRING."
          812     (with-input-from-string (s string) (load* s))))
          813 
          814 ;;; Links FASLs together
          815 (with-upgradability ()
          816   (defun combine-fasls (inputs output)
          817     "Combine a list of FASLs INPUTS into a single FASL OUTPUT"
          818     #-(or abcl allegro clisp clozure cmucl lispworks sbcl scl xcl)
          819     (not-implemented-error 'combine-fasls "~%inputs: ~S~%output: ~S" inputs output)
          820     #+abcl (funcall 'sys::concatenate-fasls inputs output) ; requires ABCL 1.2.0
          821     #+(or allegro clisp cmucl sbcl scl xcl) (concatenate-files inputs output)
          822     #+clozure (ccl:fasl-concatenate output inputs :if-exists :supersede)
          823     #+lispworks
          824     (let (fasls)
          825       (unwind-protect
          826            (progn
          827              (loop :for i :in inputs
          828                    :for n :from 1
          829                    :for f = (add-pathname-suffix
          830                              output (format nil "-FASL~D" n))
          831                    :do (copy-file i f)
          832                        (push f fasls))
          833              (ignore-errors (lispworks:delete-system :fasls-to-concatenate))
          834              (eval `(scm:defsystem :fasls-to-concatenate
          835                       (:default-pathname ,(pathname-directory-pathname output))
          836                       :members
          837                       ,(loop :for f :in (reverse fasls)
          838                              :collect `(,(namestring f) :load-only t))))
          839              (scm:concatenate-system output :fasls-to-concatenate :force t))
          840         (loop :for f :in fasls :do (ignore-errors (delete-file f)))
          841         (ignore-errors (lispworks:delete-system :fasls-to-concatenate))))))