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))))))