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