| Commit | Line | Data |
|---|---|---|
| 115a05e6 | 1 | ;;; -*- mode: Common-Lisp; Base: 10 ; Syntax: ANSI-Common-Lisp ; coding: utf-8 -*- |
| b6f29d0e | 2 | ;;; This is ASDF 2.24: Another System Definition Facility. |
| 36d9b3bc | 3 | ;;; |
| 4 | ;;; Feedback, bug reports, and patches are all welcome: | |
| 5 | ;;; please mail to <asdf-devel@common-lisp.net>. | |
| 6 | ;;; Note first that the canonical source for ASDF is presently | |
| 7 | ;;; <URL:http://common-lisp.net/project/asdf/>. | |
| 8 | ;;; | |
| 9 | ;;; If you obtained this copy from anywhere else, and you experience | |
| 10 | ;;; trouble using it, or find bugs, you may want to check at the | |
| 11 | ;;; location above for a more recent version (and for documentation | |
| 12 | ;;; and test files, if your copy came without them) before reporting | |
| 1ff353ac | 13 | ;;; bugs. There are usually two "supported" revisions - the git master |
| 14 | ;;; branch is the latest development version, whereas the git release | |
| 15 | ;;; branch may be slightly older but is considered `stable' | |
| 36d9b3bc | 16 | |
| 17 | ;;; -- LICENSE START | |
| 18 | ;;; (This is the MIT / X Consortium license as taken from | |
| 19 | ;;; http://www.opensource.org/licenses/mit-license.html on or about | |
| 20 | ;;; Monday; July 13, 2009) | |
| 21 | ;;; | |
| 115a05e6 | 22 | ;;; Copyright (c) 2001-2012 Daniel Barlow and contributors |
| 36d9b3bc | 23 | ;;; |
| 24 | ;;; Permission is hereby granted, free of charge, to any person obtaining | |
| 25 | ;;; a copy of this software and associated documentation files (the | |
| 26 | ;;; "Software"), to deal in the Software without restriction, including | |
| 27 | ;;; without limitation the rights to use, copy, modify, merge, publish, | |
| 28 | ;;; distribute, sublicense, and/or sell copies of the Software, and to | |
| 29 | ;;; permit persons to whom the Software is furnished to do so, subject to | |
| 30 | ;;; the following conditions: | |
| 31 | ;;; | |
| 32 | ;;; The above copyright notice and this permission notice shall be | |
| 33 | ;;; included in all copies or substantial portions of the Software. | |
| 34 | ;;; | |
| 35 | ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 36 | ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 37 | ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 38 | ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
| 39 | ;;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
| 40 | ;;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
| 41 | ;;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 42 | ;;; | |
| 43 | ;;; -- LICENSE END | |
| 44 | ||
| 45 | ;;; The problem with writing a defsystem replacement is bootstrapping: | |
| 46 | ;;; we can't use defsystem to compile it. Hence, all in one file. | |
| 47 | ||
| 3dfe0f91 | 48 | #+xcvb (module ()) |
| 36d9b3bc | 49 | |
| 115a05e6 RT |
50 | (cl:in-package :common-lisp-user) |
| 51 | #+genera (in-package :future-common-lisp-user) | |
| 36d9b3bc | 52 | |
| b6f29d0e | 53 | #-(or abcl allegro clisp clozure cmu cormanlisp ecl gcl genera lispworks mcl mkcl sbcl scl xcl) |
| d0c8d6d5 | 54 | (error "ASDF is not supported on your implementation. Please help us port it.") |
| c3e0c711 | 55 | |
| 115a05e6 RT |
56 | ;;;; Create and setup packages in a way that is compatible with hot-upgrade. |
| 57 | ;;;; See https://bugs.launchpad.net/asdf/+bug/485687 | |
| 58 | ;;;; See these two eval-when forms, and more near the end of the file. | |
| 59 | ||
| 3871cbd7 | 60 | #+gcl (defpackage :asdf (:use :cl)) ;; GCL treats defpackage magically and needs this |
| 61 | ||
| 115a05e6 RT |
62 | (eval-when (:load-toplevel :compile-toplevel :execute) |
| 63 | ;;; Before we do anything, some implementation-dependent tweaks | |
| 576ae2a5 | 64 | ;; (declaim (optimize (speed 1) (debug 3) (safety 3))) ; NO: trust implementation defaults. |
| 3dfe0f91 | 65 | #+allegro |
| 66 | (setf excl::*autoload-package-name-alist* | |
| 67 | (remove "asdf" excl::*autoload-package-name-alist* | |
| c3e0c711 | 68 | :test 'equalp :key 'car)) ; need that BEFORE any mention of package ASDF as below |
| d0c8d6d5 | 69 | #+gcl ;; Debian's GCL 2.7 has bugs with compiling multiple-value stuff, but can run ASDF 2.011 |
| 70 | (when (or (< system::*gcl-major-version* 2) ;; GCL 2.6 fails to fully compile ASDF at all | |
| 71 | (and (= system::*gcl-major-version* 2) | |
| 72 | (< system::*gcl-minor-version* 7))) | |
| 73 | (pushnew :gcl-pre2.7 *features*)) | |
| b6f29d0e RT |
74 | #+(or abcl (and allegro ics) (and (or clisp cmu ecl mkcl) unicode) |
| 75 | clozure lispworks (and sbcl sb-unicode) scl) | |
| 115a05e6 | 76 | (pushnew :asdf-unicode *features*) |
| c3e0c711 | 77 | ;;; make package if it doesn't exist yet. |
| 78 | ;;; DEFPACKAGE may cause errors on discrepancies, so we avoid it. | |
| 79 | (unless (find-package :asdf) | |
| 80 | (make-package :asdf :use '(:common-lisp)))) | |
| 7fe7ec48 | 81 | |
| 82 | (in-package :asdf) | |
| 36d9b3bc | 83 | |
| 36d9b3bc | 84 | (eval-when (:load-toplevel :compile-toplevel :execute) |
| 115a05e6 RT |
85 | ;;; This would belong amongst implementation-dependent tweaks above, |
| 86 | ;;; except that the defun has to be in package asdf. | |
| 87 | #+ecl (defun use-ecl-byte-compiler-p () (and (member :ecl-bytecmp *features*) t)) | |
| 88 | #+ecl (unless (use-ecl-byte-compiler-p) (require :cmp)) | |
| b6f29d0e RT |
89 | #+mkcl (require :cmp) |
| 90 | #+mkcl (setq clos::*redefine-class-in-place* t) ;; Make sure we have strict ANSI class redefinition semantics | |
| 115a05e6 RT |
91 | |
| 92 | ;;; Package setup, step 2. | |
| 7fe7ec48 | 93 | (defvar *asdf-version* nil) |
| 94 | (defvar *upgraded-p* nil) | |
| c3e0c711 | 95 | (defvar *asdf-verbose* nil) ; was t from 2.000 to 2.014.12. |
| 96 | (defun find-symbol* (s p) | |
| 97 | (find-symbol (string s) p)) | |
| 98 | ;; Strip out formatting that is not supported on Genera. | |
| 99 | ;; Has to be inside the eval-when to make Lispworks happy (!) | |
| 576ae2a5 RT |
100 | (defun strcat (&rest strings) |
| 101 | (apply 'concatenate 'string strings)) | |
| c3e0c711 | 102 | (defmacro compatfmt (format) |
| d0c8d6d5 | 103 | #-(or gcl genera) format |
| 104 | #+(or gcl genera) | |
| c3e0c711 | 105 | (loop :for (unsupported . replacement) :in |
| 4c04d402 RT |
106 | (append |
| 107 | '(("~3i~_" . "")) | |
| 108 | #+genera '(("~@<" . "") ("; ~@;" . "; ") ("~@:>" . "") ("~:>" . ""))) :do | |
| c3e0c711 | 109 | (loop :for found = (search unsupported format) :while found :do |
| 576ae2a5 RT |
110 | (setf format (strcat (subseq format 0 found) replacement |
| 111 | (subseq format (+ found (length unsupported))))))) | |
| c3e0c711 | 112 | format) |
| 3871cbd7 | 113 | (let* (;; For bug reporting sanity, please always bump this version when you modify this file. |
| 1ff353ac | 114 | ;; Please also modify asdf.asd to reflect this change. The script bin/bump-version |
| 115 | ;; can help you do these changes in synch (look at the source for documentation). | |
| 116 | ;; Relying on its automation, the version is now redundantly present on top of this file. | |
| 3871cbd7 | 117 | ;; "2.345" would be an official release |
| 118 | ;; "2.345.6" would be a development version in the official upstream | |
| 1ff353ac | 119 | ;; "2.345.0.7" would be your seventh local modification of official release 2.345 |
| 120 | ;; "2.345.6.7" would be your seventh local modification of development version 2.345.6 | |
| b6f29d0e | 121 | (asdf-version "2.24") |
| c3e0c711 | 122 | (existing-asdf (find-class 'component nil)) |
| 7fe7ec48 | 123 | (existing-version *asdf-version*) |
| 36d9b3bc | 124 | (already-there (equal asdf-version existing-version))) |
| 125 | (unless (and existing-asdf already-there) | |
| c3e0c711 | 126 | (when (and existing-asdf *asdf-verbose*) |
| 3871cbd7 | 127 | (format *trace-output* |
| c3e0c711 | 128 | (compatfmt "~&~@<; ~@;Upgrading ASDF ~@[from version ~A ~]to version ~A~@:>~%") |
| 129 | existing-version asdf-version)) | |
| 36d9b3bc | 130 | (labels |
| 1ff353ac | 131 | ((present-symbol-p (symbol package) |
| c3e0c711 | 132 | (member (nth-value 1 (find-symbol* symbol package)) '(:internal :external))) |
| 1ff353ac | 133 | (present-symbols (package) |
| 134 | ;; #-genera (loop :for s :being :the :present-symbols :in package :collect s) #+genera | |
| 135 | (let (l) | |
| 136 | (do-symbols (s package) | |
| 137 | (when (present-symbol-p s package) (push s l))) | |
| 138 | (reverse l))) | |
| 139 | (unlink-package (package) | |
| 146e9122 | 140 | (let ((u (find-package package))) |
| 141 | (when u | |
| 1ff353ac | 142 | (ensure-unintern u (present-symbols u)) |
| 146e9122 | 143 | (loop :for p :in (package-used-by-list u) :do |
| 144 | (unuse-package u p)) | |
| 145 | (delete-package u)))) | |
| 36d9b3bc | 146 | (ensure-exists (name nicknames use) |
| 146e9122 | 147 | (let ((previous |
| 148 | (remove-duplicates | |
| 149 | (mapcar #'find-package (cons name nicknames)) | |
| 150 | :from-end t))) | |
| 151 | ;; do away with packages with conflicting (nick)names | |
| 152 | (map () #'unlink-package (cdr previous)) | |
| 153 | ;; reuse previous package with same name | |
| 154 | (let ((p (car previous))) | |
| 155 | (cond | |
| 156 | (p | |
| 36d9b3bc | 157 | (rename-package p name nicknames) |
| 158 | (ensure-use p use) | |
| 146e9122 | 159 | p) |
| 160 | (t | |
| 161 | (make-package name :nicknames nicknames :use use)))))) | |
| 36d9b3bc | 162 | (intern* (symbol package) |
| 163 | (intern (string symbol) package)) | |
| 164 | (remove-symbol (symbol package) | |
| c3e0c711 | 165 | (let ((sym (find-symbol* symbol package))) |
| 36d9b3bc | 166 | (when sym |
| c3e0c711 | 167 | #-cormanlisp (unexport sym package) |
| 7fe7ec48 | 168 | (unintern sym package) |
| 169 | sym))) | |
| 36d9b3bc | 170 | (ensure-unintern (package symbols) |
| 7fe7ec48 | 171 | (loop :with packages = (list-all-packages) |
| 172 | :for sym :in symbols | |
| 173 | :for removed = (remove-symbol sym package) | |
| 174 | :when removed :do | |
| 175 | (loop :for p :in packages :do | |
| c3e0c711 | 176 | (when (eq removed (find-symbol* sym p)) |
| 7fe7ec48 | 177 | (unintern removed p))))) |
| 36d9b3bc | 178 | (ensure-shadow (package symbols) |
| 179 | (shadow symbols package)) | |
| 180 | (ensure-use (package use) | |
| 115a05e6 RT |
181 | (dolist (used (package-use-list package)) |
| 182 | (unless (member (package-name used) use :test 'string=) | |
| 183 | (unuse-package used) | |
| 184 | (do-external-symbols (sym used) | |
| 185 | (when (eq sym (find-symbol* sym package)) | |
| 186 | (remove-symbol sym package))))) | |
| 36d9b3bc | 187 | (dolist (used (reverse use)) |
| 188 | (do-external-symbols (sym used) | |
| c3e0c711 | 189 | (unless (eq sym (find-symbol* sym package)) |
| 36d9b3bc | 190 | (remove-symbol sym package))) |
| 191 | (use-package used package))) | |
| 192 | (ensure-fmakunbound (package symbols) | |
| 193 | (loop :for name :in symbols | |
| c3e0c711 | 194 | :for sym = (find-symbol* name package) |
| 36d9b3bc | 195 | :when sym :do (fmakunbound sym))) |
| 196 | (ensure-export (package export) | |
| 7fe7ec48 | 197 | (let ((formerly-exported-symbols nil) |
| 198 | (bothly-exported-symbols nil) | |
| 199 | (newly-exported-symbols nil)) | |
| 1ff353ac | 200 | (do-external-symbols (sym package) |
| 7fe7ec48 | 201 | (if (member sym export :test 'string-equal) |
| 202 | (push sym bothly-exported-symbols) | |
| 203 | (push sym formerly-exported-symbols))) | |
| 204 | (loop :for sym :in export :do | |
| 576ae2a5 | 205 | (unless (member sym bothly-exported-symbols :test 'equal) |
| 7fe7ec48 | 206 | (push sym newly-exported-symbols))) |
| 207 | (loop :for user :in (package-used-by-list package) | |
| 208 | :for shadowing = (package-shadowing-symbols user) :do | |
| 209 | (loop :for new :in newly-exported-symbols | |
| c3e0c711 | 210 | :for old = (find-symbol* new user) |
| 7fe7ec48 | 211 | :when (and old (not (member old shadowing))) |
| 212 | :do (unintern old user))) | |
| 213 | (loop :for x :in newly-exported-symbols :do | |
| 214 | (export (intern* x package))))) | |
| 4c04d402 RT |
215 | (ensure-package (name &key nicknames use unintern |
| 216 | shadow export redefined-functions) | |
| 7fe7ec48 | 217 | (let* ((p (ensure-exists name nicknames use))) |
| 115a05e6 | 218 | (ensure-unintern p (append unintern #+cmu redefined-functions)) |
| 36d9b3bc | 219 | (ensure-shadow p shadow) |
| 220 | (ensure-export p export) | |
| 115a05e6 | 221 | #-cmu (ensure-fmakunbound p redefined-functions) |
| 36d9b3bc | 222 | p))) |
| 223 | (macrolet | |
| 224 | ((pkgdcl (name &key nicknames use export | |
| 4c04d402 | 225 | redefined-functions unintern shadow) |
| 3dfe0f91 | 226 | `(ensure-package |
| 227 | ',name :nicknames ',nicknames :use ',use :export ',export | |
| 228 | :shadow ',shadow | |
| d0c8d6d5 | 229 | :unintern ',unintern |
| 4c04d402 | 230 | :redefined-functions ',redefined-functions))) |
| 36d9b3bc | 231 | (pkgdcl |
| 36d9b3bc | 232 | :asdf |
| 91fcdf9d | 233 | :nicknames (:asdf-utilities) ;; DEPRECATED! Do not use, for backward compatibility only. |
| 7fe7ec48 | 234 | :use (:common-lisp) |
| 36d9b3bc | 235 | :redefined-functions |
| 236 | (#:perform #:explain #:output-files #:operation-done-p | |
| 237 | #:perform-with-restarts #:component-relative-pathname | |
| 7fe7ec48 | 238 | #:system-source-file #:operate #:find-component #:find-system |
| 1ff353ac | 239 | #:apply-output-translations #:translate-pathname* #:resolve-location |
| 4c04d402 RT |
240 | #:system-relative-pathname |
| 241 | #:inherit-source-registry #:process-source-registry | |
| 242 | #:process-source-registry-directive | |
| c3e0c711 | 243 | #:compile-file* #:source-file-type) |
| 36d9b3bc | 244 | :unintern |
| 245 | (#:*asdf-revision* #:around #:asdf-method-combination | |
| 576ae2a5 RT |
246 | #:split #:make-collector #:do-dep #:do-one-dep |
| 247 | #:resolve-relative-location-component #:resolve-absolute-location-component | |
| 3871cbd7 | 248 | #:output-files-for-system-and-operation) ; obsolete ASDF-BINARY-LOCATION function |
| 36d9b3bc | 249 | :export |
| 576ae2a5 | 250 | (#:defsystem #:oos #:operate #:find-system #:locate-system #:run-shell-command |
| c3e0c711 | 251 | #:system-definition-pathname #:with-system-definitions |
| 576ae2a5 | 252 | #:search-for-system-definition #:find-component #:component-find-path |
| 115a05e6 RT |
253 | #:compile-system #:load-system #:load-systems |
| 254 | #:require-system #:test-system #:clear-system | |
| 576ae2a5 RT |
255 | #:operation #:compile-op #:load-op #:load-source-op #:test-op |
| 256 | #:feature #:version #:version-satisfies | |
| c3e0c711 | 257 | #:upgrade-asdf |
| 115a05e6 | 258 | #:implementation-identifier #:implementation-type #:hostname |
| 576ae2a5 | 259 | #:input-files #:output-files #:output-file #:perform |
| 36d9b3bc | 260 | #:operation-done-p #:explain |
| 261 | ||
| 262 | #:component #:source-file | |
| 263 | #:c-source-file #:cl-source-file #:java-source-file | |
| c3e0c711 | 264 | #:cl-source-file.cl #:cl-source-file.lsp |
| 36d9b3bc | 265 | #:static-file |
| 266 | #:doc-file | |
| 267 | #:html-file | |
| 268 | #:text-file | |
| 269 | #:source-file-type | |
| 270 | #:module ; components | |
| 271 | #:system | |
| 272 | #:unix-dso | |
| 273 | ||
| 274 | #:module-components ; component accessors | |
| 115a05e6 | 275 | #:module-components-by-name |
| 36d9b3bc | 276 | #:component-pathname |
| 277 | #:component-relative-pathname | |
| 278 | #:component-name | |
| 279 | #:component-version | |
| 280 | #:component-parent | |
| 281 | #:component-property | |
| 282 | #:component-system | |
| 36d9b3bc | 283 | #:component-depends-on |
| 115a05e6 RT |
284 | #:component-encoding |
| 285 | #:component-external-format | |
| 36d9b3bc | 286 | |
| 287 | #:system-description | |
| 288 | #:system-long-description | |
| 289 | #:system-author | |
| 290 | #:system-maintainer | |
| 291 | #:system-license | |
| 292 | #:system-licence | |
| 293 | #:system-source-file | |
| 294 | #:system-source-directory | |
| 295 | #:system-relative-pathname | |
| 296 | #:map-systems | |
| 297 | ||
| 3871cbd7 | 298 | #:operation-description |
| 36d9b3bc | 299 | #:operation-on-warnings |
| 300 | #:operation-on-failure | |
| 7fe7ec48 | 301 | #:component-visited-p |
| 115a05e6 RT |
302 | |
| 303 | #:*system-definition-search-functions* ; variables | |
| 304 | #:*central-registry* | |
| 36d9b3bc | 305 | #:*compile-file-warnings-behaviour* |
| 306 | #:*compile-file-failure-behaviour* | |
| 307 | #:*resolve-symlinks* | |
| b6f29d0e | 308 | #:*load-system-operation* |
| 36d9b3bc | 309 | #:*asdf-verbose* |
| 4c04d402 | 310 | #:*verbose-out* |
| 36d9b3bc | 311 | |
| 312 | #:asdf-version | |
| 313 | ||
| 314 | #:operation-error #:compile-failed #:compile-warned #:compile-error | |
| 315 | #:error-name | |
| 316 | #:error-pathname | |
| 317 | #:load-system-definition-error | |
| 318 | #:error-component #:error-operation | |
| 319 | #:system-definition-error | |
| 320 | #:missing-component | |
| 321 | #:missing-component-of-version | |
| 322 | #:missing-dependency | |
| 323 | #:missing-dependency-of-version | |
| 324 | #:circular-dependency ; errors | |
| 325 | #:duplicate-names | |
| 326 | ||
| 327 | #:try-recompiling | |
| 328 | #:retry | |
| 329 | #:accept ; restarts | |
| 330 | #:coerce-entry-to-directory | |
| 331 | #:remove-entry-from-registry | |
| 332 | ||
| 115a05e6 RT |
333 | #:*encoding-detection-hook* |
| 334 | #:*encoding-external-format-hook* | |
| 335 | #:*default-encoding* | |
| 336 | #:*utf-8-external-format* | |
| 337 | ||
| 7fe7ec48 | 338 | #:clear-configuration |
| 1ff353ac | 339 | #:*output-translations-parameter* |
| 36d9b3bc | 340 | #:initialize-output-translations |
| 341 | #:disable-output-translations | |
| 342 | #:clear-output-translations | |
| 343 | #:ensure-output-translations | |
| 344 | #:apply-output-translations | |
| 3dfe0f91 | 345 | #:compile-file* |
| 36d9b3bc | 346 | #:compile-file-pathname* |
| 347 | #:enable-asdf-binary-locations-compatibility | |
| 36d9b3bc | 348 | #:*default-source-registries* |
| 1ff353ac | 349 | #:*source-registry-parameter* |
| 36d9b3bc | 350 | #:initialize-source-registry |
| 351 | #:compute-source-registry | |
| 352 | #:clear-source-registry | |
| 353 | #:ensure-source-registry | |
| 7fe7ec48 | 354 | #:process-source-registry |
| 115a05e6 RT |
355 | #:system-registered-p #:registered-systems #:loaded-systems |
| 356 | #:resolve-location | |
| c2fac11e | 357 | #:asdf-message |
| 576ae2a5 RT |
358 | #:user-output-translations-pathname |
| 359 | #:system-output-translations-pathname | |
| 360 | #:user-output-translations-directory-pathname | |
| 361 | #:system-output-translations-directory-pathname | |
| 362 | #:user-source-registry | |
| 363 | #:system-source-registry | |
| 364 | #:user-source-registry-directory | |
| 365 | #:system-source-registry-directory | |
| 36d9b3bc | 366 | |
| 7fe7ec48 | 367 | ;; Utilities |
| 3871cbd7 | 368 | ;; #:aif #:it |
| 115a05e6 RT |
369 | #:appendf #:orf |
| 370 | #:length=n-p | |
| 371 | #:remove-keys #:remove-keyword | |
| b6f29d0e | 372 | #:first-char #:last-char #:string-suffix-p |
| 7fe7ec48 | 373 | #:coerce-name |
| 115a05e6 RT |
374 | #:directory-pathname-p #:ensure-directory-pathname |
| 375 | #:absolute-pathname-p #:ensure-pathname-absolute #:pathname-root | |
| b6f29d0e | 376 | #:getenv #:getenv-pathname #:getenv-pathnames |
| 3db9313f | 377 | #:getenv-absolute-directory #:getenv-absolute-directories |
| 115a05e6 RT |
378 | #:probe-file* |
| 379 | #:find-symbol* #:strcat | |
| 380 | #:make-pathname-component-logical #:make-pathname-logical | |
| 381 | #:merge-pathnames* #:coerce-pathname #:subpathname #:subpathname* | |
| 382 | #:pathname-directory-pathname #:pathname-parent-directory-pathname | |
| 7fe7ec48 | 383 | #:read-file-forms |
| 115a05e6 | 384 | #:resolve-symlinks #:truenamize |
| 7fe7ec48 | 385 | #:split-string |
| 386 | #:component-name-to-pathname-components | |
| 387 | #:split-name-type | |
| 115a05e6 RT |
388 | #:subdirectories #:directory-files |
| 389 | #:while-collecting | |
| 390 | #:*wild* #:*wild-file* #:*wild-directory* #:*wild-inferiors* | |
| 391 | #:*wild-path* #:wilden | |
| 392 | #:directorize-pathname-host-device | |
| 393 | ))) | |
| c3e0c711 | 394 | #+genera (import 'scl:boolean :asdf) |
| 7fe7ec48 | 395 | (setf *asdf-version* asdf-version |
| 396 | *upgraded-p* (if existing-version | |
| 397 | (cons existing-version *upgraded-p*) | |
| 398 | *upgraded-p*)))))) | |
| 36d9b3bc | 399 | |
| 36d9b3bc | 400 | ;;;; ------------------------------------------------------------------------- |
| 401 | ;;;; User-visible parameters | |
| 402 | ;;;; | |
| 36d9b3bc | 403 | (defvar *resolve-symlinks* t |
| 404 | "Determine whether or not ASDF resolves symlinks when defining systems. | |
| 405 | ||
| 7fe7ec48 | 406 | Defaults to T.") |
| 36d9b3bc | 407 | |
| 7fe7ec48 | 408 | (defvar *compile-file-warnings-behaviour* |
| 409 | (or #+clisp :ignore :warn) | |
| 410 | "How should ASDF react if it encounters a warning when compiling a file? | |
| 411 | Valid values are :error, :warn, and :ignore.") | |
| 36d9b3bc | 412 | |
| 7fe7ec48 | 413 | (defvar *compile-file-failure-behaviour* |
| 414 | (or #+sbcl :error #+clisp :ignore :warn) | |
| 415 | "How should ASDF react if it encounters a failure (per the ANSI spec of COMPILE-FILE) | |
| 416 | when compiling a file? Valid values are :error, :warn, and :ignore. | |
| 417 | Note that ASDF ALWAYS raises an error if it fails to create an output file when compiling.") | |
| 36d9b3bc | 418 | |
| 419 | (defvar *verbose-out* nil) | |
| 420 | ||
| 36d9b3bc | 421 | (defparameter +asdf-methods+ |
| 422 | '(perform-with-restarts perform explain output-files operation-done-p)) | |
| 423 | ||
| b6f29d0e RT |
424 | (defvar *load-system-operation* 'load-op |
| 425 | "Operation used by ASDF:LOAD-SYSTEM. By default, ASDF:LOAD-OP. | |
| 426 | You may override it with e.g. ASDF:LOAD-FASL-OP from asdf-bundle, | |
| 427 | or ASDF:LOAD-SOURCE-OP if your fasl loading is somehow broken.") | |
| 428 | ||
| 429 | (defvar *compile-op-compile-file-function* 'compile-file* | |
| 430 | "Function used to compile lisp files.") | |
| 431 | ||
| 432 | ||
| 433 | ||
| 36d9b3bc | 434 | #+allegro |
| 435 | (eval-when (:compile-toplevel :execute) | |
| 436 | (defparameter *acl-warn-save* | |
| 437 | (when (boundp 'excl:*warn-on-nested-reader-conditionals*) | |
| 438 | excl:*warn-on-nested-reader-conditionals*)) | |
| 439 | (when (boundp 'excl:*warn-on-nested-reader-conditionals*) | |
| 440 | (setf excl:*warn-on-nested-reader-conditionals* nil))) | |
| 441 | ||
| 442 | ;;;; ------------------------------------------------------------------------- | |
| c3e0c711 | 443 | ;;;; Resolve forward references |
| 444 | ||
| 445 | (declaim (ftype (function (t) t) | |
| 446 | format-arguments format-control | |
| 447 | error-name error-pathname error-condition | |
| 448 | duplicate-names-name | |
| 449 | error-component error-operation | |
| 450 | module-components module-components-by-name | |
| 451 | circular-dependency-components | |
| 452 | condition-arguments condition-form | |
| 453 | condition-format condition-location | |
| 454 | coerce-name) | |
| 576ae2a5 | 455 | (ftype (function (&optional t) (values)) initialize-source-registry) |
| d0c8d6d5 | 456 | #-(or cormanlisp gcl-pre2.7) |
| c3e0c711 | 457 | (ftype (function (t t) t) (setf module-components-by-name))) |
| 458 | ||
| 459 | ;;;; ------------------------------------------------------------------------- | |
| d0c8d6d5 | 460 | ;;;; Compatibility various implementations |
| c3e0c711 | 461 | #+cormanlisp |
| 462 | (progn | |
| 463 | (deftype logical-pathname () nil) | |
| 576ae2a5 RT |
464 | (defun make-broadcast-stream () *error-output*) |
| 465 | (defun file-namestring (p) | |
| c3e0c711 | 466 | (setf p (pathname p)) |
| d0c8d6d5 | 467 | (format nil "~@[~A~]~@[.~A~]" (pathname-name p) (pathname-type p)))) |
| 468 | ||
| 469 | #.(or #+mcl ;; the #$ doesn't work on other lisps, even protected by #+mcl | |
| 470 | (read-from-string | |
| 471 | "(eval-when (:compile-toplevel :load-toplevel :execute) | |
| 472 | (ccl:define-entry-point (_getenv \"getenv\") ((name :string)) :string) | |
| 473 | (ccl:define-entry-point (_system \"system\") ((name :string)) :int) | |
| 474 | ;; Note: ASDF may expect user-homedir-pathname to provide | |
| 475 | ;; the pathname of the current user's home directory, whereas | |
| 476 | ;; MCL by default provides the directory from which MCL was started. | |
| 477 | ;; See http://code.google.com/p/mcl/wiki/Portability | |
| 478 | (defun current-user-homedir-pathname () | |
| 479 | (ccl::findfolder #$kuserdomain #$kCurrentUserFolderType)) | |
| 480 | (defun probe-posix (posix-namestring) | |
| 481 | \"If a file exists for the posix namestring, return the pathname\" | |
| 482 | (ccl::with-cstrs ((cpath posix-namestring)) | |
| 483 | (ccl::rlet ((is-dir :boolean) | |
| 484 | (fsref :fsref)) | |
| 485 | (when (eq #$noerr (#_fspathmakeref cpath fsref is-dir)) | |
| 486 | (ccl::%path-from-fsref fsref is-dir))))))")) | |
| c3e0c711 | 487 | |
| 488 | ;;;; ------------------------------------------------------------------------- | |
| 3871cbd7 | 489 | ;;;; General Purpose Utilities |
| 490 | ||
| 7fe7ec48 | 491 | (macrolet |
| 492 | ((defdef (def* def) | |
| 493 | `(defmacro ,def* (name formals &rest rest) | |
| 494 | `(progn | |
| d0c8d6d5 | 495 | #+(or ecl (and gcl (not gcl-pre2.7))) (fmakunbound ',name) |
| c3e0c711 | 496 | #-gcl ; gcl 2.7.0 notinline functions lose secondary return values :-( |
| 497 | ,(when (and #+ecl (symbolp name)) ; fails for setf functions on ecl | |
| 498 | `(declaim (notinline ,name))) | |
| 7fe7ec48 | 499 | (,',def ,name ,formals ,@rest))))) |
| 500 | (defdef defgeneric* defgeneric) | |
| 501 | (defdef defun* defun)) | |
| 502 | ||
| 36d9b3bc | 503 | (defmacro while-collecting ((&rest collectors) &body body) |
| 3dfe0f91 | 504 | "COLLECTORS should be a list of names for collections. A collector |
| 505 | defines a function that, when applied to an argument inside BODY, will | |
| 506 | add its argument to the corresponding collection. Returns multiple values, | |
| 507 | a list for each collection, in order. | |
| 508 | E.g., | |
| 509 | \(while-collecting \(foo bar\) | |
| 510 | \(dolist \(x '\(\(a 1\) \(b 2\) \(c 3\)\)\) | |
| 511 | \(foo \(first x\)\) | |
| 512 | \(bar \(second x\)\)\)\) | |
| 513 | Returns two values: \(A B C\) and \(1 2 3\)." | |
| 36d9b3bc | 514 | (let ((vars (mapcar #'(lambda (x) (gensym (symbol-name x))) collectors)) |
| 515 | (initial-values (mapcar (constantly nil) collectors))) | |
| 516 | `(let ,(mapcar #'list vars initial-values) | |
| 517 | (flet ,(mapcar #'(lambda (c v) `(,c (x) (push x ,v) (values))) collectors vars) | |
| 518 | ,@body | |
| 519 | (values ,@(mapcar #'(lambda (v) `(reverse ,v)) vars)))))) | |
| 520 | ||
| 521 | (defmacro aif (test then &optional else) | |
| 115a05e6 | 522 | "Anaphoric version of IF, On Lisp style" |
| 36d9b3bc | 523 | `(let ((it ,test)) (if it ,then ,else))) |
| 524 | ||
| 7fe7ec48 | 525 | (defun* pathname-directory-pathname (pathname) |
| 36d9b3bc | 526 | "Returns a new pathname with same HOST, DEVICE, DIRECTORY as PATHNAME, |
| 527 | and NIL NAME, TYPE and VERSION components" | |
| b986cd91 | 528 | (when pathname |
| 529 | (make-pathname :name nil :type nil :version nil :defaults pathname))) | |
| 36d9b3bc | 530 | |
| 1ff353ac | 531 | (defun* normalize-pathname-directory-component (directory) |
| 115a05e6 | 532 | "Given a pathname directory component, return an equivalent form that is a list" |
| 1ff353ac | 533 | (cond |
| 115a05e6 | 534 | #-(or cmu sbcl scl) ;; these implementations already normalize directory components. |
| 1ff353ac | 535 | ((stringp directory) `(:absolute ,directory) directory) |
| 536 | #+gcl | |
| 537 | ((and (consp directory) (stringp (first directory))) | |
| 538 | `(:absolute ,@directory)) | |
| 539 | ((or (null directory) | |
| 540 | (and (consp directory) (member (first directory) '(:absolute :relative)))) | |
| 541 | directory) | |
| 542 | (t | |
| e5ee8946 | 543 | (error (compatfmt "~@<Unrecognized pathname directory component ~S~@:>") directory)))) |
| 1ff353ac | 544 | |
| 545 | (defun* merge-pathname-directory-components (specified defaults) | |
| 115a05e6 | 546 | ;; Helper for merge-pathnames* that handles directory components. |
| 1ff353ac | 547 | (let ((directory (normalize-pathname-directory-component specified))) |
| 548 | (ecase (first directory) | |
| 549 | ((nil) defaults) | |
| 550 | (:absolute specified) | |
| 551 | (:relative | |
| 552 | (let ((defdir (normalize-pathname-directory-component defaults)) | |
| 553 | (reldir (cdr directory))) | |
| 554 | (cond | |
| 555 | ((null defdir) | |
| 556 | directory) | |
| 557 | ((not (eq :back (first reldir))) | |
| 558 | (append defdir reldir)) | |
| 559 | (t | |
| 560 | (loop :with defabs = (first defdir) | |
| 561 | :with defrev = (reverse (rest defdir)) | |
| 562 | :while (and (eq :back (car reldir)) | |
| 563 | (or (and (eq :absolute defabs) (null defrev)) | |
| 564 | (stringp (car defrev)))) | |
| 565 | :do (pop reldir) (pop defrev) | |
| 566 | :finally (return (cons defabs (append (reverse defrev) reldir))))))))))) | |
| 567 | ||
| 115a05e6 RT |
568 | (defun* make-pathname-component-logical (x) |
| 569 | "Make a pathname component suitable for use in a logical-pathname" | |
| 570 | (typecase x | |
| 571 | ((eql :unspecific) nil) | |
| 572 | #+clisp (string (string-upcase x)) | |
| 573 | #+clisp (cons (mapcar 'make-pathname-component-logical x)) | |
| 574 | (t x))) | |
| 575 | ||
| 576 | (defun* make-pathname-logical (pathname host) | |
| 577 | "Take a PATHNAME's directory, name, type and version components, | |
| 578 | and make a new pathname with corresponding components and specified logical HOST" | |
| 579 | (make-pathname | |
| 580 | :host host | |
| 581 | :directory (make-pathname-component-logical (pathname-directory pathname)) | |
| 582 | :name (make-pathname-component-logical (pathname-name pathname)) | |
| 583 | :type (make-pathname-component-logical (pathname-type pathname)) | |
| 584 | :version (make-pathname-component-logical (pathname-version pathname)))) | |
| 576ae2a5 | 585 | |
| 7fe7ec48 | 586 | (defun* merge-pathnames* (specified &optional (defaults *default-pathname-defaults*)) |
| d0c8d6d5 | 587 | "MERGE-PATHNAMES* is like MERGE-PATHNAMES except that |
| 588 | if the SPECIFIED pathname does not have an absolute directory, | |
| 589 | then the HOST and DEVICE both come from the DEFAULTS, whereas | |
| 590 | if the SPECIFIED pathname does have an absolute directory, | |
| 591 | then the HOST and DEVICE both come from the SPECIFIED. | |
| 36d9b3bc | 592 | Also, if either argument is NIL, then the other argument is returned unmodified." |
| 593 | (when (null specified) (return-from merge-pathnames* defaults)) | |
| 594 | (when (null defaults) (return-from merge-pathnames* specified)) | |
| e5ee8946 | 595 | #+scl |
| 596 | (ext:resolve-pathname specified defaults) | |
| 597 | #-scl | |
| 36d9b3bc | 598 | (let* ((specified (pathname specified)) |
| 599 | (defaults (pathname defaults)) | |
| 1ff353ac | 600 | (directory (normalize-pathname-directory-component (pathname-directory specified))) |
| 36d9b3bc | 601 | (name (or (pathname-name specified) (pathname-name defaults))) |
| 602 | (type (or (pathname-type specified) (pathname-type defaults))) | |
| 603 | (version (or (pathname-version specified) (pathname-version defaults)))) | |
| 576ae2a5 | 604 | (labels ((unspecific-handler (p) |
| 115a05e6 | 605 | (if (typep p 'logical-pathname) #'make-pathname-component-logical #'identity))) |
| 36d9b3bc | 606 | (multiple-value-bind (host device directory unspecific-handler) |
| 91fcdf9d | 607 | (ecase (first directory) |
| 36d9b3bc | 608 | ((:absolute) |
| 609 | (values (pathname-host specified) | |
| 610 | (pathname-device specified) | |
| 611 | directory | |
| 612 | (unspecific-handler specified))) | |
| 1ff353ac | 613 | ((nil :relative) |
| 36d9b3bc | 614 | (values (pathname-host defaults) |
| 615 | (pathname-device defaults) | |
| 1ff353ac | 616 | (merge-pathname-directory-components directory (pathname-directory defaults)) |
| 36d9b3bc | 617 | (unspecific-handler defaults)))) |
| 618 | (make-pathname :host host :device device :directory directory | |
| 619 | :name (funcall unspecific-handler name) | |
| 620 | :type (funcall unspecific-handler type) | |
| 621 | :version (funcall unspecific-handler version)))))) | |
| 622 | ||
| 1ff353ac | 623 | (defun* pathname-parent-directory-pathname (pathname) |
| 624 | "Returns a new pathname with same HOST, DEVICE, DIRECTORY as PATHNAME, | |
| 625 | and NIL NAME, TYPE and VERSION components" | |
| 626 | (when pathname | |
| 627 | (make-pathname :name nil :type nil :version nil | |
| c3e0c711 | 628 | :directory (merge-pathname-directory-components |
| 629 | '(:relative :back) (pathname-directory pathname)) | |
| 1ff353ac | 630 | :defaults pathname))) |
| 631 | ||
| 36d9b3bc | 632 | (define-modify-macro appendf (&rest args) |
| 633 | append "Append onto list") ;; only to be used on short lists. | |
| 634 | ||
| 635 | (define-modify-macro orf (&rest args) | |
| 636 | or "or a flag") | |
| 637 | ||
| 7fe7ec48 | 638 | (defun* first-char (s) |
| b986cd91 | 639 | (and (stringp s) (plusp (length s)) (char s 0))) |
| 640 | ||
| 7fe7ec48 | 641 | (defun* last-char (s) |
| b986cd91 | 642 | (and (stringp s) (plusp (length s)) (char s (1- (length s))))) |
| 643 | ||
| c3e0c711 | 644 | |
| 7fe7ec48 | 645 | (defun* asdf-message (format-string &rest format-args) |
| 36d9b3bc | 646 | (declare (dynamic-extent format-args)) |
| c3e0c711 | 647 | (apply 'format *verbose-out* format-string format-args)) |
| 36d9b3bc | 648 | |
| 7fe7ec48 | 649 | (defun* split-string (string &key max (separator '(#\Space #\Tab))) |
| b986cd91 | 650 | "Split STRING into a list of components separated by |
| 651 | any of the characters in the sequence SEPARATOR. | |
| 36d9b3bc | 652 | If MAX is specified, then no more than max(1,MAX) components will be returned, |
| 653 | starting the separation from the end, e.g. when called with arguments | |
| 654 | \"a.b.c.d.e\" :max 3 :separator \".\" it will return (\"a.b.c\" \"d\" \"e\")." | |
| c3e0c711 | 655 | (catch nil |
| 36d9b3bc | 656 | (let ((list nil) (words 0) (end (length string))) |
| 657 | (flet ((separatorp (char) (find char separator)) | |
| c3e0c711 | 658 | (done () (throw nil (cons (subseq string 0 end) list)))) |
| 36d9b3bc | 659 | (loop |
| 660 | :for start = (if (and max (>= words (1- max))) | |
| 661 | (done) | |
| 662 | (position-if #'separatorp string :end end :from-end t)) :do | |
| 663 | (when (null start) | |
| 664 | (done)) | |
| 665 | (push (subseq string (1+ start) end) list) | |
| 666 | (incf words) | |
| 667 | (setf end start)))))) | |
| 668 | ||
| 7fe7ec48 | 669 | (defun* split-name-type (filename) |
| 36d9b3bc | 670 | (let ((unspecific |
| 671 | ;; Giving :unspecific as argument to make-pathname is not portable. | |
| 672 | ;; See CLHS make-pathname and 19.2.2.2.3. | |
| 115a05e6 | 673 | ;; We only use it on implementations that support it, |
| b6f29d0e | 674 | #+(or abcl allegro clozure cmu gcl genera lispworks mkcl sbcl scl xcl) :unspecific |
| 115a05e6 | 675 | #+(or clisp ecl #|These haven't been tested:|# cormanlisp mcl) nil)) |
| 36d9b3bc | 676 | (destructuring-bind (name &optional (type unspecific)) |
| 677 | (split-string filename :max 2 :separator ".") | |
| 678 | (if (equal name "") | |
| 679 | (values filename unspecific) | |
| 680 | (values name type))))) | |
| 681 | ||
| 91fcdf9d | 682 | (defun* component-name-to-pathname-components (s &key force-directory force-relative) |
| 36d9b3bc | 683 | "Splits the path string S, returning three values: |
| 684 | A flag that is either :absolute or :relative, indicating | |
| 685 | how the rest of the values are to be interpreted. | |
| 686 | A directory path --- a list of strings, suitable for | |
| 687 | use with MAKE-PATHNAME when prepended with the flag | |
| 688 | value. | |
| 689 | A filename with type extension, possibly NIL in the | |
| 690 | case of a directory pathname. | |
| 691 | FORCE-DIRECTORY forces S to be interpreted as a directory | |
| 692 | pathname \(third return value will be NIL, final component | |
| 693 | of S will be treated as part of the directory path. | |
| 694 | ||
| 695 | The intention of this function is to support structured component names, | |
| 696 | e.g., \(:file \"foo/bar\"\), which will be unpacked to relative | |
| 697 | pathnames." | |
| 698 | (check-type s string) | |
| 91fcdf9d | 699 | (when (find #\: s) |
| e5ee8946 | 700 | (error (compatfmt "~@<A portable ASDF pathname designator cannot include a #\: character: ~3i~_~S~@:>") s)) |
| 36d9b3bc | 701 | (let* ((components (split-string s :separator "/")) |
| 702 | (last-comp (car (last components)))) | |
| 703 | (multiple-value-bind (relative components) | |
| 704 | (if (equal (first components) "") | |
| 2220a102 | 705 | (if (equal (first-char s) #\/) |
| 91fcdf9d | 706 | (progn |
| 707 | (when force-relative | |
| e5ee8946 | 708 | (error (compatfmt "~@<Absolute pathname designator not allowed: ~3i~_~S~@:>") s)) |
| 91fcdf9d | 709 | (values :absolute (cdr components))) |
| 36d9b3bc | 710 | (values :relative nil)) |
| 711 | (values :relative components)) | |
| 1ff353ac | 712 | (setf components (remove-if #'(lambda (x) (member x '("" ".") :test #'equal)) components)) |
| 713 | (setf components (substitute :back ".." components :test #'equal)) | |
| 36d9b3bc | 714 | (cond |
| 715 | ((equal last-comp "") | |
| 3dfe0f91 | 716 | (values relative components nil)) ; "" already removed |
| 36d9b3bc | 717 | (force-directory |
| 718 | (values relative components nil)) | |
| 719 | (t | |
| 720 | (values relative (butlast components) last-comp)))))) | |
| 721 | ||
| 7fe7ec48 | 722 | (defun* remove-keys (key-names args) |
| 36d9b3bc | 723 | (loop :for (name val) :on args :by #'cddr |
| 724 | :unless (member (symbol-name name) key-names | |
| 725 | :key #'symbol-name :test 'equal) | |
| 726 | :append (list name val))) | |
| 727 | ||
| 7fe7ec48 | 728 | (defun* remove-keyword (key args) |
| 36d9b3bc | 729 | (loop :for (k v) :on args :by #'cddr |
| 730 | :unless (eq k key) | |
| 731 | :append (list k v))) | |
| 732 | ||
| 7fe7ec48 | 733 | (defun* getenv (x) |
| 1ff353ac | 734 | (declare (ignorable x)) |
| 4c04d402 | 735 | #+(or abcl clisp ecl xcl) (ext:getenv x) |
| 1ff353ac | 736 | #+allegro (sys:getenv x) |
| 737 | #+clozure (ccl:getenv x) | |
| 738 | #+(or cmu scl) (cdr (assoc x ext:*environment-list* :test #'string=)) | |
| c3e0c711 | 739 | #+cormanlisp |
| 740 | (let* ((buffer (ct:malloc 1)) | |
| 741 | (cname (ct:lisp-string-to-c-string x)) | |
| 742 | (needed-size (win:getenvironmentvariable cname buffer 0)) | |
| 743 | (buffer1 (ct:malloc (1+ needed-size)))) | |
| 744 | (prog1 (if (zerop (win:getenvironmentvariable cname buffer1 needed-size)) | |
| 745 | nil | |
| 746 | (ct:c-string-to-lisp-string buffer1)) | |
| 747 | (ct:free buffer) | |
| 748 | (ct:free buffer1))) | |
| 1ff353ac | 749 | #+gcl (system:getenv x) |
| 750 | #+genera nil | |
| 751 | #+lispworks (lispworks:environment-variable x) | |
| 752 | #+mcl (ccl:with-cstrs ((name x)) | |
| 753 | (let ((value (_getenv name))) | |
| 754 | (unless (ccl:%null-ptr-p value) | |
| 755 | (ccl:%get-cstring value)))) | |
| b6f29d0e | 756 | #+mkcl (#.(or (find-symbol* 'getenv :si) (find-symbol* 'getenv :mk-ext)) x) |
| 1ff353ac | 757 | #+sbcl (sb-ext:posix-getenv x) |
| b6f29d0e | 758 | #-(or abcl allegro clisp clozure cmu cormanlisp ecl gcl genera lispworks mcl mkcl sbcl scl xcl) |
| c3e0c711 | 759 | (error "~S is not supported on your implementation" 'getenv)) |
| 7fe7ec48 | 760 | |
| 761 | (defun* directory-pathname-p (pathname) | |
| b986cd91 | 762 | "Does PATHNAME represent a directory? |
| 36d9b3bc | 763 | |
| 764 | A directory-pathname is a pathname _without_ a filename. The three | |
| b986cd91 | 765 | ways that the filename components can be missing are for it to be NIL, |
| 766 | :UNSPECIFIC or the empty string. | |
| 36d9b3bc | 767 | |
| b986cd91 | 768 | Note that this does _not_ check to see that PATHNAME points to an |
| 36d9b3bc | 769 | actually-existing directory." |
| 91fcdf9d | 770 | (when pathname |
| 771 | (let ((pathname (pathname pathname))) | |
| 772 | (flet ((check-one (x) | |
| 773 | (member x '(nil :unspecific "") :test 'equal))) | |
| 774 | (and (not (wild-pathname-p pathname)) | |
| 775 | (check-one (pathname-name pathname)) | |
| 776 | (check-one (pathname-type pathname)) | |
| 777 | t))))) | |
| 36d9b3bc | 778 | |
| 7fe7ec48 | 779 | (defun* ensure-directory-pathname (pathspec) |
| 36d9b3bc | 780 | "Converts the non-wild pathname designator PATHSPEC to directory form." |
| 781 | (cond | |
| 782 | ((stringp pathspec) | |
| 783 | (ensure-directory-pathname (pathname pathspec))) | |
| 784 | ((not (pathnamep pathspec)) | |
| e5ee8946 | 785 | (error (compatfmt "~@<Invalid pathname designator ~S~@:>") pathspec)) |
| 36d9b3bc | 786 | ((wild-pathname-p pathspec) |
| e5ee8946 | 787 | (error (compatfmt "~@<Can't reliably convert wild pathname ~3i~_~S~@:>") pathspec)) |
| 36d9b3bc | 788 | ((directory-pathname-p pathspec) |
| 789 | pathspec) | |
| 790 | (t | |
| 791 | (make-pathname :directory (append (or (pathname-directory pathspec) | |
| 792 | (list :relative)) | |
| 793 | (list (file-namestring pathspec))) | |
| 794 | :name nil :type nil :version nil | |
| 795 | :defaults pathspec)))) | |
| 796 | ||
| 1ff353ac | 797 | #+genera |
| 798 | (unless (fboundp 'ensure-directories-exist) | |
| d0c8d6d5 | 799 | (defun* ensure-directories-exist (path) |
| 1ff353ac | 800 | (fs:create-directories-recursively (pathname path)))) |
| 801 | ||
| 7fe7ec48 | 802 | (defun* absolute-pathname-p (pathspec) |
| 3871cbd7 | 803 | (and (typep pathspec '(or pathname string)) |
| 804 | (eq :absolute (car (pathname-directory (pathname pathspec)))))) | |
| 36d9b3bc | 805 | |
| 115a05e6 RT |
806 | (defun* coerce-pathname (name &key type defaults) |
| 807 | "coerce NAME into a PATHNAME. | |
| 808 | When given a string, portably decompose it into a relative pathname: | |
| 809 | #\\/ separates subdirectories. The last #\\/-separated string is as follows: | |
| 810 | if TYPE is NIL, its last #\\. if any separates name and type from from type; | |
| 811 | if TYPE is a string, it is the type, and the whole string is the name; | |
| 812 | if TYPE is :DIRECTORY, the string is a directory component; | |
| 813 | if the string is empty, it's a directory. | |
| 814 | Any directory named .. is read as :BACK. | |
| 815 | Host, device and version components are taken from DEFAULTS." | |
| 816 | ;; The defaults are required notably because they provide the default host | |
| 817 | ;; to the below make-pathname, which may crucially matter to people using | |
| 818 | ;; merge-pathnames with non-default hosts, e.g. for logical-pathnames. | |
| 819 | ;; NOTE that the host and device slots will be taken from the defaults, | |
| 820 | ;; but that should only matter if you later merge relative pathnames with | |
| 821 | ;; CL:MERGE-PATHNAMES instead of ASDF:MERGE-PATHNAMES* | |
| 822 | (etypecase name | |
| 823 | ((or null pathname) | |
| 824 | name) | |
| 825 | (symbol | |
| 826 | (coerce-pathname (string-downcase name) :type type :defaults defaults)) | |
| 827 | (string | |
| 828 | (multiple-value-bind (relative path filename) | |
| 829 | (component-name-to-pathname-components name :force-directory (eq type :directory) | |
| 830 | :force-relative t) | |
| 831 | (multiple-value-bind (name type) | |
| 832 | (cond | |
| 833 | ((or (eq type :directory) (null filename)) | |
| 834 | (values nil nil)) | |
| 835 | (type | |
| 836 | (values filename type)) | |
| 837 | (t | |
| 838 | (split-name-type filename))) | |
| 839 | (apply 'make-pathname :directory (cons relative path) :name name :type type | |
| 840 | (when defaults `(:defaults ,defaults)))))))) | |
| 841 | ||
| 842 | (defun* merge-component-name-type (name &key type defaults) | |
| 843 | ;; For backwards compatibility only, for people using internals. | |
| 844 | ;; Will be removed in a future release, e.g. 2.016. | |
| 845 | (warn "Please don't use ASDF::MERGE-COMPONENT-NAME-TYPE. Use ASDF:COERCE-PATHNAME.") | |
| 846 | (coerce-pathname name :type type :defaults defaults)) | |
| 847 | ||
| 848 | (defun* subpathname (pathname subpath &key type) | |
| 849 | (and pathname (merge-pathnames* (coerce-pathname subpath :type type) | |
| 850 | (pathname-directory-pathname pathname)))) | |
| 851 | ||
| 852 | (defun subpathname* (pathname subpath &key type) | |
| 853 | (and pathname | |
| 854 | (subpathname (ensure-directory-pathname pathname) subpath :type type))) | |
| 855 | ||
| 7fe7ec48 | 856 | (defun* length=n-p (x n) ;is it that (= (length x) n) ? |
| 36d9b3bc | 857 | (check-type n (integer 0 *)) |
| 858 | (loop | |
| 859 | :for l = x :then (cdr l) | |
| 860 | :for i :downfrom n :do | |
| 861 | (cond | |
| 862 | ((zerop i) (return (null l))) | |
| 863 | ((not (consp l)) (return nil))))) | |
| 864 | ||
| b6f29d0e | 865 | (defun* string-suffix-p (s suffix) |
| 36d9b3bc | 866 | (check-type s string) |
| 867 | (check-type suffix string) | |
| 868 | (let ((start (- (length s) (length suffix)))) | |
| 869 | (and (<= 0 start) | |
| 870 | (string-equal s suffix :start1 start)))) | |
| 871 | ||
| 7fe7ec48 | 872 | (defun* read-file-forms (file) |
| 36d9b3bc | 873 | (with-open-file (in file) |
| 874 | (loop :with eof = (list nil) | |
| 875 | :for form = (read in nil eof) | |
| 876 | :until (eq form eof) | |
| 877 | :collect form))) | |
| 878 | ||
| 7fe7ec48 | 879 | (defun* pathname-root (pathname) |
| e5ee8946 | 880 | (make-pathname :directory '(:absolute) |
| 881 | :name nil :type nil :version nil | |
| c3e0c711 | 882 | :defaults pathname ;; host device, and on scl, *some* |
| 883 | ;; scheme-specific parts: port username password, not others: | |
| e5ee8946 | 884 | . #.(or #+scl '(:parameters nil :query nil :fragment nil)))) |
| 36d9b3bc | 885 | |
| 7fe7ec48 | 886 | (defun* probe-file* (p) |
| 887 | "when given a pathname P, probes the filesystem for a file or directory | |
| 888 | with given pathname and if it exists return its truename." | |
| c2fac11e | 889 | (etypecase p |
| c3e0c711 | 890 | (null nil) |
| 891 | (string (probe-file* (parse-namestring p))) | |
| 892 | (pathname (unless (wild-pathname-p p) | |
| b6f29d0e | 893 | #.(or #+(or allegro clozure cmu cormanlisp ecl lispworks mkcl sbcl scl) |
| d0c8d6d5 | 894 | '(probe-file p) |
| c3e0c711 | 895 | #+clisp (aif (find-symbol* '#:probe-pathname :ext) |
| 896 | `(ignore-errors (,it p))) | |
| 897 | '(ignore-errors (truename p))))))) | |
| 7fe7ec48 | 898 | |
| d0c8d6d5 | 899 | (defun* truenamize (pathname &optional (defaults *default-pathname-defaults*)) |
| 36d9b3bc | 900 | "Resolve as much of a pathname as possible" |
| 901 | (block nil | |
| d0c8d6d5 | 902 | (when (typep pathname '(or null logical-pathname)) (return pathname)) |
| 903 | (let ((p (merge-pathnames* pathname defaults))) | |
| 36d9b3bc | 904 | (when (typep p 'logical-pathname) (return p)) |
| 7fe7ec48 | 905 | (let ((found (probe-file* p))) |
| 906 | (when found (return found))) | |
| d0c8d6d5 | 907 | (unless (absolute-pathname-p p) |
| 908 | (let ((true-defaults (ignore-errors (truename defaults)))) | |
| 909 | (when true-defaults | |
| 910 | (setf p (merge-pathnames pathname true-defaults))))) | |
| 911 | (unless (absolute-pathname-p p) (return p)) | |
| 7fe7ec48 | 912 | (let ((sofar (probe-file* (pathname-root p)))) |
| 36d9b3bc | 913 | (unless sofar (return p)) |
| 914 | (flet ((solution (directories) | |
| 915 | (merge-pathnames* | |
| 916 | (make-pathname :host nil :device nil | |
| 917 | :directory `(:relative ,@directories) | |
| 918 | :name (pathname-name p) | |
| 919 | :type (pathname-type p) | |
| 920 | :version (pathname-version p)) | |
| 921 | sofar))) | |
| d0c8d6d5 | 922 | (loop :with directory = (normalize-pathname-directory-component |
| 923 | (pathname-directory p)) | |
| 924 | :for component :in (cdr directory) | |
| 36d9b3bc | 925 | :for rest :on (cdr directory) |
| 7fe7ec48 | 926 | :for more = (probe-file* |
| 927 | (merge-pathnames* | |
| 928 | (make-pathname :directory `(:relative ,component)) | |
| 929 | sofar)) :do | |
| 36d9b3bc | 930 | (if more |
| 931 | (setf sofar more) | |
| 932 | (return (solution rest))) | |
| 933 | :finally | |
| 934 | (return (solution nil)))))))) | |
| 935 | ||
| 7fe7ec48 | 936 | (defun* resolve-symlinks (path) |
| b986cd91 | 937 | #-allegro (truenamize path) |
| 1ff353ac | 938 | #+allegro (if (typep path 'logical-pathname) |
| 939 | path | |
| 940 | (excl:pathname-resolve-symbolic-links path))) | |
| b986cd91 | 941 | |
| c3e0c711 | 942 | (defun* resolve-symlinks* (path) |
| 943 | (if *resolve-symlinks* | |
| 944 | (and path (resolve-symlinks path)) | |
| 945 | path)) | |
| 946 | ||
| d0c8d6d5 | 947 | (defun* ensure-pathname-absolute (path) |
| c3e0c711 | 948 | (cond |
| 949 | ((absolute-pathname-p path) path) | |
| 950 | ((stringp path) (ensure-pathname-absolute (pathname path))) | |
| 951 | ((not (pathnamep path)) (error "not a valid pathname designator ~S" path)) | |
| 952 | (t (let ((resolved (resolve-symlinks path))) | |
| 953 | (assert (absolute-pathname-p resolved)) | |
| 954 | resolved)))) | |
| 955 | ||
| 7fe7ec48 | 956 | (defun* default-directory () |
| b986cd91 | 957 | (truenamize (pathname-directory-pathname *default-pathname-defaults*))) |
| 958 | ||
| 7fe7ec48 | 959 | (defun* lispize-pathname (input-file) |
| 36d9b3bc | 960 | (make-pathname :type "lisp" :defaults input-file)) |
| 961 | ||
| c3e0c711 | 962 | (defparameter *wild* #-cormanlisp :wild #+cormanlisp "*") |
| 1ff353ac | 963 | (defparameter *wild-file* |
| c3e0c711 | 964 | (make-pathname :name *wild* :type *wild* |
| 965 | :version (or #-(or abcl xcl) *wild*) :directory nil)) | |
| 1ff353ac | 966 | (defparameter *wild-directory* |
| c3e0c711 | 967 | (make-pathname :directory `(:relative ,*wild*) :name nil :type nil :version nil)) |
| 1ff353ac | 968 | (defparameter *wild-inferiors* |
| 969 | (make-pathname :directory '(:relative :wild-inferiors) :name nil :type nil :version nil)) | |
| b986cd91 | 970 | (defparameter *wild-path* |
| 1ff353ac | 971 | (merge-pathnames *wild-file* *wild-inferiors*)) |
| b986cd91 | 972 | |
| 7fe7ec48 | 973 | (defun* wilden (path) |
| b986cd91 | 974 | (merge-pathnames* *wild-path* path)) |
| 975 | ||
| e5ee8946 | 976 | #-scl |
| d0c8d6d5 | 977 | (defun* directory-separator-for-host (&optional (pathname *default-pathname-defaults*)) |
| 1ff353ac | 978 | (let ((foo (make-pathname :directory '(:absolute "FOO") :defaults pathname))) |
| 979 | (last-char (namestring foo)))) | |
| 980 | ||
| e5ee8946 | 981 | #-scl |
| 7fe7ec48 | 982 | (defun* directorize-pathname-host-device (pathname) |
| b986cd91 | 983 | (let* ((root (pathname-root pathname)) |
| 984 | (wild-root (wilden root)) | |
| 985 | (absolute-pathname (merge-pathnames* pathname root)) | |
| 1ff353ac | 986 | (separator (directory-separator-for-host root)) |
| b986cd91 | 987 | (root-namestring (namestring root)) |
| 988 | (root-string | |
| 989 | (substitute-if #\/ | |
| 1ff353ac | 990 | #'(lambda (x) (or (eql x #\:) |
| 991 | (eql x separator))) | |
| b986cd91 | 992 | root-namestring))) |
| 993 | (multiple-value-bind (relative path filename) | |
| 91fcdf9d | 994 | (component-name-to-pathname-components root-string :force-directory t) |
| b986cd91 | 995 | (declare (ignore relative filename)) |
| 996 | (let ((new-base | |
| 997 | (make-pathname :defaults root | |
| 998 | :directory `(:absolute ,@path)))) | |
| 999 | (translate-pathname absolute-pathname wild-root (wilden new-base)))))) | |
| 1000 | ||
| e5ee8946 | 1001 | #+scl |
| 1002 | (defun* directorize-pathname-host-device (pathname) | |
| 1003 | (let ((scheme (ext:pathname-scheme pathname)) | |
| c3e0c711 | 1004 | (host (pathname-host pathname)) |
| 1005 | (port (ext:pathname-port pathname)) | |
| 1006 | (directory (pathname-directory pathname))) | |
| 115a05e6 RT |
1007 | (flet ((specificp (x) (and x (not (eq x :unspecific))))) |
| 1008 | (if (or (specificp port) | |
| 1009 | (and (specificp host) (plusp (length host))) | |
| 1010 | (specificp scheme)) | |
| 576ae2a5 | 1011 | (let ((prefix "")) |
| 115a05e6 | 1012 | (when (specificp port) |
| 576ae2a5 | 1013 | (setf prefix (format nil ":~D" port))) |
| 115a05e6 | 1014 | (when (and (specificp host) (plusp (length host))) |
| 576ae2a5 RT |
1015 | (setf prefix (strcat host prefix))) |
| 1016 | (setf prefix (strcat ":" prefix)) | |
| 115a05e6 | 1017 | (when (specificp scheme) |
| 576ae2a5 RT |
1018 | (setf prefix (strcat scheme prefix))) |
| 1019 | (assert (and directory (eq (first directory) :absolute))) | |
| 1020 | (make-pathname :directory `(:absolute ,prefix ,@(rest directory)) | |
| 1021 | :defaults pathname))) | |
| 115a05e6 | 1022 | pathname))) |
| e5ee8946 | 1023 | |
| 36d9b3bc | 1024 | ;;;; ------------------------------------------------------------------------- |
| 3871cbd7 | 1025 | ;;;; ASDF Interface, in terms of generic functions. |
| 1026 | (defgeneric* find-system (system &optional error-p)) | |
| 1027 | (defgeneric* perform-with-restarts (operation component)) | |
| 1028 | (defgeneric* perform (operation component)) | |
| 1029 | (defgeneric* operation-done-p (operation component)) | |
| 4c04d402 | 1030 | (defgeneric* mark-operation-done (operation component)) |
| 3871cbd7 | 1031 | (defgeneric* explain (operation component)) |
| 1032 | (defgeneric* output-files (operation component)) | |
| 1033 | (defgeneric* input-files (operation component)) | |
| 1034 | (defgeneric* component-operation-time (operation component)) | |
| 1035 | (defgeneric* operation-description (operation component) | |
| 1036 | (:documentation "returns a phrase that describes performing this operation | |
| 1037 | on this component, e.g. \"loading /a/b/c\". | |
| 1038 | You can put together sentences using this phrase.")) | |
| 1039 | ||
| 1040 | (defgeneric* system-source-file (system) | |
| 1041 | (:documentation "Return the source file in which system is defined.")) | |
| 1042 | ||
| 1043 | (defgeneric* component-system (component) | |
| 1044 | (:documentation "Find the top-level system containing COMPONENT")) | |
| 1045 | ||
| 1046 | (defgeneric* component-pathname (component) | |
| 1047 | (:documentation "Extracts the pathname applicable for a particular component.")) | |
| 1048 | ||
| 1049 | (defgeneric* component-relative-pathname (component) | |
| 1050 | (:documentation "Returns a pathname for the component argument intended to be | |
| 1051 | interpreted relative to the pathname of that component's parent. | |
| 1052 | Despite the function's name, the return value may be an absolute | |
| 1053 | pathname, because an absolute pathname may be interpreted relative to | |
| 1054 | another pathname in a degenerate way.")) | |
| 1055 | ||
| 1056 | (defgeneric* component-property (component property)) | |
| 1057 | ||
| 1058 | (defgeneric* (setf component-property) (new-value component property)) | |
| 1059 | ||
| 115a05e6 RT |
1060 | (defgeneric* component-external-format (component)) |
| 1061 | ||
| 1062 | (defgeneric* component-encoding (component)) | |
| 1063 | ||
| d0c8d6d5 | 1064 | (eval-when (#-gcl :compile-toplevel :load-toplevel :execute) |
| c3e0c711 | 1065 | (defgeneric* (setf module-components-by-name) (new-value module))) |
| 1066 | ||
| 3871cbd7 | 1067 | (defgeneric* version-satisfies (component version)) |
| 1068 | ||
| 1069 | (defgeneric* find-component (base path) | |
| 1070 | (:documentation "Finds the component with PATH starting from BASE module; | |
| 1071 | if BASE is nil, then the component is assumed to be a system.")) | |
| 1072 | ||
| 1073 | (defgeneric* source-file-type (component system)) | |
| 1074 | ||
| 1075 | (defgeneric* operation-ancestor (operation) | |
| 1076 | (:documentation | |
| 1077 | "Recursively chase the operation's parent pointer until we get to | |
| 1078 | the head of the tree")) | |
| 1079 | ||
| 1080 | (defgeneric* component-visited-p (operation component) | |
| 1081 | (:documentation "Returns the value stored by a call to | |
| 1082 | VISIT-COMPONENT, if that has been called, otherwise NIL. | |
| 1083 | This value stored will be a cons cell, the first element | |
| 1084 | of which is a computed key, so not interesting. The | |
| 1085 | CDR wil be the DATA value stored by VISIT-COMPONENT; recover | |
| 1086 | it as (cdr (component-visited-p op c)). | |
| 1087 | In the current form of ASDF, the DATA value retrieved is | |
| 1088 | effectively a boolean, indicating whether some operations are | |
| 1089 | to be performed in order to do OPERATION X COMPONENT. If the | |
| 1090 | data value is NIL, the combination had been explored, but no | |
| 1091 | operations needed to be performed.")) | |
| 1092 | ||
| 1093 | (defgeneric* visit-component (operation component data) | |
| 1094 | (:documentation "Record DATA as being associated with OPERATION | |
| 1095 | and COMPONENT. This is a side-effecting function: the association | |
| 1096 | will be recorded on the ROOT OPERATION \(OPERATION-ANCESTOR of the | |
| 1097 | OPERATION\). | |
| 1098 | No evidence that DATA is ever interesting, beyond just being | |
| 1099 | non-NIL. Using the data field is probably very risky; if there is | |
| 1100 | already a record for OPERATION X COMPONENT, DATA will be quietly | |
| 1101 | discarded instead of recorded. | |
| 1102 | Starting with 2.006, TRAVERSE will store an integer in data, | |
| 1103 | so that nodes can be sorted in decreasing order of traversal.")) | |
| 1104 | ||
| 1105 | ||
| 1106 | (defgeneric* (setf visiting-component) (new-value operation component)) | |
| 1107 | ||
| 1108 | (defgeneric* component-visiting-p (operation component)) | |
| 1109 | ||
| 1110 | (defgeneric* component-depends-on (operation component) | |
| 1111 | (:documentation | |
| 1112 | "Returns a list of dependencies needed by the component to perform | |
| 1113 | the operation. A dependency has one of the following forms: | |
| 1114 | ||
| 1115 | (<operation> <component>*), where <operation> is a class | |
| 1116 | designator and each <component> is a component | |
| 1117 | designator, which means that the component depends on | |
| 1118 | <operation> having been performed on each <component>; or | |
| 1119 | ||
| 1120 | (FEATURE <feature>), which means that the component depends | |
| 1121 | on <feature>'s presence in *FEATURES*. | |
| 1122 | ||
| 1123 | Methods specialized on subclasses of existing component types | |
| 1124 | should usually append the results of CALL-NEXT-METHOD to the | |
| 1125 | list.")) | |
| 1126 | ||
| 1127 | (defgeneric* component-self-dependencies (operation component)) | |
| 1128 | ||
| 1129 | (defgeneric* traverse (operation component) | |
| 1130 | (:documentation | |
| 1131 | "Generate and return a plan for performing OPERATION on COMPONENT. | |
| 1132 | ||
| 1133 | The plan returned is a list of dotted-pairs. Each pair is the CONS | |
| 1134 | of ASDF operation object and a COMPONENT object. The pairs will be | |
| 1135 | processed in order by OPERATE.")) | |
| 1136 | ||
| 1137 | ||
| 1138 | ;;;; ------------------------------------------------------------------------- | |
| 1139 | ;;; Methods in case of hot-upgrade. See https://bugs.launchpad.net/asdf/+bug/485687 | |
| 1140 | (when *upgraded-p* | |
| 115a05e6 RT |
1141 | (when (find-class 'module nil) |
| 1142 | (eval | |
| 1143 | '(defmethod update-instance-for-redefined-class :after | |
| 1144 | ((m module) added deleted plist &key) | |
| 1145 | (declare (ignorable deleted plist)) | |
| 1146 | (when *asdf-verbose* | |
| 1147 | (asdf-message (compatfmt "~&~@<; ~@;Updating ~A for ASDF ~A~@:>~%") | |
| 1148 | m (asdf-version))) | |
| 1149 | (when (member 'components-by-name added) | |
| 1150 | (compute-module-components-by-name m)) | |
| 1151 | (when (typep m 'system) | |
| 1152 | (when (member 'source-file added) | |
| 1153 | (%set-system-source-file | |
| 1154 | (probe-asd (component-name m) (component-pathname m)) m) | |
| 1155 | (when (equal (component-name m) "asdf") | |
| 1156 | (setf (component-version m) *asdf-version*)))))))) | |
| 3871cbd7 | 1157 | |
| 1158 | ;;;; ------------------------------------------------------------------------- | |
| 36d9b3bc | 1159 | ;;;; Classes, Conditions |
| 1160 | ||
| 1161 | (define-condition system-definition-error (error) () | |
| 1162 | ;; [this use of :report should be redundant, but unfortunately it's not. | |
| 1163 | ;; cmucl's lisp::output-instance prefers the kernel:slot-class-print-function | |
| 1164 | ;; over print-object; this is always conditions::%print-condition for | |
| 1165 | ;; condition objects, which in turn does inheritance of :report options at | |
| 1166 | ;; run-time. fortunately, inheritance means we only need this kludge here in | |
| 1167 | ;; order to fix all conditions that build on it. -- rgr, 28-Jul-02.] | |
| 1168 | #+cmu (:report print-object)) | |
| 1169 | ||
| 1170 | (define-condition formatted-system-definition-error (system-definition-error) | |
| 1171 | ((format-control :initarg :format-control :reader format-control) | |
| 1172 | (format-arguments :initarg :format-arguments :reader format-arguments)) | |
| 1173 | (:report (lambda (c s) | |
| c3e0c711 | 1174 | (apply 'format s (format-control c) (format-arguments c))))) |
| 36d9b3bc | 1175 | |
| 1176 | (define-condition load-system-definition-error (system-definition-error) | |
| 1177 | ((name :initarg :name :reader error-name) | |
| 1178 | (pathname :initarg :pathname :reader error-pathname) | |
| 1179 | (condition :initarg :condition :reader error-condition)) | |
| 1180 | (:report (lambda (c s) | |
| c3e0c711 | 1181 | (format s (compatfmt "~@<Error while trying to load definition for system ~A from pathname ~A: ~3i~_~A~@:>") |
| 1182 | (error-name c) (error-pathname c) (error-condition c))))) | |
| 36d9b3bc | 1183 | |
| 1184 | (define-condition circular-dependency (system-definition-error) | |
| 7fe7ec48 | 1185 | ((components :initarg :components :reader circular-dependency-components)) |
| 1186 | (:report (lambda (c s) | |
| c3e0c711 | 1187 | (format s (compatfmt "~@<Circular dependency: ~3i~_~S~@:>") |
| 1188 | (circular-dependency-components c))))) | |
| 36d9b3bc | 1189 | |
| 1190 | (define-condition duplicate-names (system-definition-error) | |
| 1191 | ((name :initarg :name :reader duplicate-names-name)) | |
| 1192 | (:report (lambda (c s) | |
| c3e0c711 | 1193 | (format s (compatfmt "~@<Error while defining system: multiple components are given same name ~A~@:>") |
| 1194 | (duplicate-names-name c))))) | |
| 36d9b3bc | 1195 | |
| 1196 | (define-condition missing-component (system-definition-error) | |
| 1197 | ((requires :initform "(unnamed)" :reader missing-requires :initarg :requires) | |
| 1198 | (parent :initform nil :reader missing-parent :initarg :parent))) | |
| 1199 | ||
| 1200 | (define-condition missing-component-of-version (missing-component) | |
| 1201 | ((version :initform nil :reader missing-version :initarg :version))) | |
| 1202 | ||
| 1203 | (define-condition missing-dependency (missing-component) | |
| 1204 | ((required-by :initarg :required-by :reader missing-required-by))) | |
| 1205 | ||
| 1206 | (define-condition missing-dependency-of-version (missing-dependency | |
| 1207 | missing-component-of-version) | |
| 1208 | ()) | |
| 1209 | ||
| 1210 | (define-condition operation-error (error) | |
| 1211 | ((component :reader error-component :initarg :component) | |
| 1212 | (operation :reader error-operation :initarg :operation)) | |
| 1213 | (:report (lambda (c s) | |
| e5ee8946 | 1214 | (format s (compatfmt "~@<Error while invoking ~A on ~A~@:>") |
| 1ff353ac | 1215 | (error-operation c) (error-component c))))) |
| 36d9b3bc | 1216 | (define-condition compile-error (operation-error) ()) |
| 1217 | (define-condition compile-failed (compile-error) ()) | |
| 1218 | (define-condition compile-warned (compile-error) ()) | |
| 1219 | ||
| 1ff353ac | 1220 | (define-condition invalid-configuration () |
| 1221 | ((form :reader condition-form :initarg :form) | |
| 1222 | (location :reader condition-location :initarg :location) | |
| 1223 | (format :reader condition-format :initarg :format) | |
| 1224 | (arguments :reader condition-arguments :initarg :arguments :initform nil)) | |
| 1225 | (:report (lambda (c s) | |
| e5ee8946 | 1226 | (format s (compatfmt "~@<~? (will be skipped)~@:>") |
| 1ff353ac | 1227 | (condition-format c) |
| 1228 | (list* (condition-form c) (condition-location c) | |
| 1229 | (condition-arguments c)))))) | |
| 1230 | (define-condition invalid-source-registry (invalid-configuration warning) | |
| e5ee8946 | 1231 | ((format :initform (compatfmt "~@<Invalid source registry ~S~@[ in ~S~]~@{ ~@?~}~@:>")))) |
| 1ff353ac | 1232 | (define-condition invalid-output-translation (invalid-configuration warning) |
| e5ee8946 | 1233 | ((format :initform (compatfmt "~@<Invalid asdf output-translation ~S~@[ in ~S~]~@{ ~@?~}~@:>")))) |
| 1ff353ac | 1234 | |
| 36d9b3bc | 1235 | (defclass component () |
| c3e0c711 | 1236 | ((name :accessor component-name :initarg :name :type string :documentation |
| 36d9b3bc | 1237 | "Component name: designator for a string composed of portable pathname characters") |
| c3e0c711 | 1238 | ;; We might want to constrain version with |
| 1239 | ;; :type (and string (satisfies parse-version)) | |
| 1240 | ;; but we cannot until we fix all systems that don't use it correctly! | |
| 36d9b3bc | 1241 | (version :accessor component-version :initarg :version) |
| 1ff353ac | 1242 | (description :accessor component-description :initarg :description) |
| 1243 | (long-description :accessor component-long-description :initarg :long-description) | |
| 1244 | ;; This one below is used by POIU - http://www.cliki.net/poiu | |
| 1245 | ;; a parallelizing extension of ASDF that compiles in multiple parallel | |
| 1246 | ;; slave processes (forked on demand) and loads in the master process. | |
| 1247 | ;; Maybe in the future ASDF may use it internally instead of in-order-to. | |
| 36d9b3bc | 1248 | (load-dependencies :accessor component-load-dependencies :initform nil) |
| 91fcdf9d | 1249 | ;; In the ASDF object model, dependencies exist between *actions* |
| 1250 | ;; (an action is a pair of operation and component). They are represented | |
| 1251 | ;; alists of operations to dependencies (other actions) in each component. | |
| 1252 | ;; There are two kinds of dependencies, each stored in its own slot: | |
| 1253 | ;; in-order-to and do-first dependencies. These two kinds are related to | |
| 1254 | ;; the fact that some actions modify the filesystem, | |
| 1255 | ;; whereas other actions modify the current image, and | |
| 1256 | ;; this implies a difference in how to interpret timestamps. | |
| 1257 | ;; in-order-to dependencies will trigger re-performing the action | |
| 1258 | ;; when the timestamp of some dependency | |
| 1259 | ;; makes the timestamp of current action out-of-date; | |
| 1260 | ;; do-first dependencies do not trigger such re-performing. | |
| 1261 | ;; Therefore, a FASL must be recompiled if it is obsoleted | |
| 1262 | ;; by any of its FASL dependencies (in-order-to); but | |
| 1263 | ;; it needn't be recompiled just because one of these dependencies | |
| 1264 | ;; hasn't yet been loaded in the current image (do-first). | |
| 1265 | ;; The names are crap, but they have been the official API since Dan Barlow's ASDF 1.52! | |
| 115a05e6 RT |
1266 | ;; LispWorks's defsystem has caused-by and requires for in-order-to and do-first respectively. |
| 1267 | ;; Maybe rename the slots in ASDF? But that's not very backwards compatible. | |
| 1ff353ac | 1268 | ;; See our ASDF 2 paper for more complete explanations. |
| 91fcdf9d | 1269 | (in-order-to :initform nil :initarg :in-order-to |
| 1270 | :accessor component-in-order-to) | |
| 36d9b3bc | 1271 | (do-first :initform nil :initarg :do-first |
| 1272 | :accessor component-do-first) | |
| 1273 | ;; methods defined using the "inline" style inside a defsystem form: | |
| 1274 | ;; need to store them somewhere so we can delete them when the system | |
| 1275 | ;; is re-evaluated | |
| 1276 | (inline-methods :accessor component-inline-methods :initform nil) | |
| 1277 | (parent :initarg :parent :initform nil :reader component-parent) | |
| 1278 | ;; no direct accessor for pathname, we do this as a method to allow | |
| 1279 | ;; it to default in funky ways if not supplied | |
| 1280 | (relative-pathname :initarg :pathname) | |
| 4c04d402 | 1281 | ;; the absolute-pathname is computed based on relative-pathname... |
| 36d9b3bc | 1282 | (absolute-pathname) |
| 1283 | (operation-times :initform (make-hash-table) | |
| 1284 | :accessor component-operation-times) | |
| 4c04d402 | 1285 | (around-compile :initarg :around-compile) |
| 115a05e6 | 1286 | (%encoding :accessor %component-encoding :initform nil :initarg :encoding) |
| 36d9b3bc | 1287 | ;; XXX we should provide some atomic interface for updating the |
| 1288 | ;; component properties | |
| 1289 | (properties :accessor component-properties :initarg :properties | |
| 1290 | :initform nil))) | |
| 1291 | ||
| 7fe7ec48 | 1292 | (defun* component-find-path (component) |
| 36d9b3bc | 1293 | (reverse |
| 1294 | (loop :for c = component :then (component-parent c) | |
| 1295 | :while c :collect (component-name c)))) | |
| 1296 | ||
| 1297 | (defmethod print-object ((c component) stream) | |
| 1298 | (print-unreadable-object (c stream :type t :identity nil) | |
| 1ff353ac | 1299 | (format stream "~{~S~^ ~}" (component-find-path c)))) |
| 36d9b3bc | 1300 | |
| 1301 | ||
| 1302 | ;;;; methods: conditions | |
| 1303 | ||
| 1304 | (defmethod print-object ((c missing-dependency) s) | |
| e5ee8946 | 1305 | (format s (compatfmt "~@<~A, required by ~A~@:>") |
| 36d9b3bc | 1306 | (call-next-method c nil) (missing-required-by c))) |
| 1307 | ||
| 7fe7ec48 | 1308 | (defun* sysdef-error (format &rest arguments) |
| 36d9b3bc | 1309 | (error 'formatted-system-definition-error :format-control |
| 1310 | format :format-arguments arguments)) | |
| 1311 | ||
| 1312 | ;;;; methods: components | |
| 1313 | ||
| 1314 | (defmethod print-object ((c missing-component) s) | |
| e5ee8946 | 1315 | (format s (compatfmt "~@<Component ~S not found~@[ in ~A~]~@:>") |
| 36d9b3bc | 1316 | (missing-requires c) |
| 1317 | (when (missing-parent c) | |
| 3871cbd7 | 1318 | (coerce-name (missing-parent c))))) |
| 36d9b3bc | 1319 | |
| 1320 | (defmethod print-object ((c missing-component-of-version) s) | |
| e5ee8946 | 1321 | (format s (compatfmt "~@<Component ~S does not match version ~A~@[ in ~A~]~@:>") |
| 7fe7ec48 | 1322 | (missing-requires c) |
| 1323 | (missing-version c) | |
| 1324 | (when (missing-parent c) | |
| c3e0c711 | 1325 | (coerce-name (missing-parent c))))) |
| 36d9b3bc | 1326 | |
| 1327 | (defmethod component-system ((component component)) | |
| 1328 | (aif (component-parent component) | |
| 1329 | (component-system it) | |
| 1330 | component)) | |
| 1331 | ||
| 1332 | (defvar *default-component-class* 'cl-source-file) | |
| 1333 | ||
| 7fe7ec48 | 1334 | (defun* compute-module-components-by-name (module) |
| b986cd91 | 1335 | (let ((hash (make-hash-table :test 'equal))) |
| 1336 | (setf (module-components-by-name module) hash) | |
| 36d9b3bc | 1337 | (loop :for c :in (module-components module) |
| 1338 | :for name = (component-name c) | |
| 1339 | :for previous = (gethash name (module-components-by-name module)) | |
| 1340 | :do | |
| 1341 | (when previous | |
| 1342 | (error 'duplicate-names :name name)) | |
| 1343 | :do (setf (gethash name (module-components-by-name module)) c)) | |
| 1344 | hash)) | |
| 1345 | ||
| 1346 | (defclass module (component) | |
| 1347 | ((components | |
| 1348 | :initform nil | |
| 1349 | :initarg :components | |
| 1350 | :accessor module-components) | |
| 1351 | (components-by-name | |
| 36d9b3bc | 1352 | :accessor module-components-by-name) |
| 1353 | ;; What to do if we can't satisfy a dependency of one of this module's | |
| 1354 | ;; components. This allows a limited form of conditional processing. | |
| 1355 | (if-component-dep-fails | |
| 1356 | :initform :fail | |
| 1357 | :initarg :if-component-dep-fails | |
| 1358 | :accessor module-if-component-dep-fails) | |
| 1359 | (default-component-class | |
| b0e85da9 | 1360 | :initform nil |
| 36d9b3bc | 1361 | :initarg :default-component-class |
| 1362 | :accessor module-default-component-class))) | |
| 1363 | ||
| 7fe7ec48 | 1364 | (defun* component-parent-pathname (component) |
| 36d9b3bc | 1365 | ;; No default anymore (in particular, no *default-pathname-defaults*). |
| 1366 | ;; If you force component to have a NULL pathname, you better arrange | |
| 1367 | ;; for any of its children to explicitly provide a proper absolute pathname | |
| 1368 | ;; wherever a pathname is actually wanted. | |
| 1369 | (let ((parent (component-parent component))) | |
| 1370 | (when parent | |
| 1371 | (component-pathname parent)))) | |
| 1372 | ||
| 1373 | (defmethod component-pathname ((component component)) | |
| 1374 | (if (slot-boundp component 'absolute-pathname) | |
| 1375 | (slot-value component 'absolute-pathname) | |
| 1376 | (let ((pathname | |
| 1377 | (merge-pathnames* | |
| d0c8d6d5 | 1378 | (component-relative-pathname component) |
| 1379 | (pathname-directory-pathname (component-parent-pathname component))))) | |
| 36d9b3bc | 1380 | (unless (or (null pathname) (absolute-pathname-p pathname)) |
| e5ee8946 | 1381 | (error (compatfmt "~@<Invalid relative pathname ~S for component ~S~@:>") |
| 7fe7ec48 | 1382 | pathname (component-find-path component))) |
| 36d9b3bc | 1383 | (setf (slot-value component 'absolute-pathname) pathname) |
| 1384 | pathname))) | |
| 1385 | ||
| 1386 | (defmethod component-property ((c component) property) | |
| 1387 | (cdr (assoc property (slot-value c 'properties) :test #'equal))) | |
| 1388 | ||
| 1389 | (defmethod (setf component-property) (new-value (c component) property) | |
| 1390 | (let ((a (assoc property (slot-value c 'properties) :test #'equal))) | |
| 1391 | (if a | |
| 1392 | (setf (cdr a) new-value) | |
| 1393 | (setf (slot-value c 'properties) | |
| 1394 | (acons property new-value (slot-value c 'properties))))) | |
| 1395 | new-value) | |
| 1396 | ||
| 115a05e6 RT |
1397 | (defvar *default-encoding* :default |
| 1398 | "Default encoding for source files. | |
| 1399 | The default value :default preserves the legacy behavior. | |
| 1400 | A future default might be :utf-8 or :autodetect | |
| 1401 | reading emacs-style -*- coding: utf-8 -*- specifications, | |
| 1402 | and falling back to utf-8 or latin1 if nothing is specified.") | |
| 1403 | ||
| 1404 | (defparameter *utf-8-external-format* | |
| 1405 | #+(and asdf-unicode (not clisp)) :utf-8 | |
| 1406 | #+(and asdf-unicode clisp) charset:utf-8 | |
| 1407 | #-asdf-unicode :default | |
| 1408 | "Default :external-format argument to pass to CL:OPEN and also | |
| 1409 | CL:LOAD or CL:COMPILE-FILE to best process a UTF-8 encoded file. | |
| 1410 | On modern implementations, this will decode UTF-8 code points as CL characters. | |
| 1411 | On legacy implementations, it may fall back on some 8-bit encoding, | |
| 1412 | with non-ASCII code points being read as several CL characters; | |
| 1413 | hopefully, if done consistently, that won't affect program behavior too much.") | |
| 1414 | ||
| 1415 | (defun* always-default-encoding (pathname) | |
| 1416 | (declare (ignore pathname)) | |
| 1417 | *default-encoding*) | |
| 1418 | ||
| 1419 | (defvar *encoding-detection-hook* #'always-default-encoding | |
| 1420 | "Hook for an extension to define a function to automatically detect a file's encoding") | |
| 1421 | ||
| 1422 | (defun* detect-encoding (pathname) | |
| 1423 | (funcall *encoding-detection-hook* pathname)) | |
| 1424 | ||
| 1425 | (defmethod component-encoding ((c component)) | |
| 1426 | (or (loop :for x = c :then (component-parent x) | |
| 1427 | :while x :thereis (%component-encoding x)) | |
| 1428 | (detect-encoding (component-pathname c)))) | |
| 1429 | ||
| 1430 | (defun* default-encoding-external-format (encoding) | |
| 1431 | (case encoding | |
| 1432 | (:default :default) ;; for backwards compatibility only. Explicit usage discouraged. | |
| 1433 | (:utf-8 *utf-8-external-format*) | |
| 1434 | (otherwise | |
| 1435 | (cerror "Continue using :external-format :default" (compatfmt "~@<Your ASDF component is using encoding ~S but it isn't recognized. Your system should :defsystem-depends-on (:asdf-encodings).~:>") encoding) | |
| 1436 | :default))) | |
| 1437 | ||
| 1438 | (defvar *encoding-external-format-hook* | |
| 1439 | #'default-encoding-external-format | |
| 1440 | "Hook for an extension to define a mapping between non-default encodings | |
| 1441 | and implementation-defined external-format's") | |
| 1442 | ||
| 1443 | (defun encoding-external-format (encoding) | |
| 1444 | (funcall *encoding-external-format-hook* encoding)) | |
| 1445 | ||
| 1446 | (defmethod component-external-format ((c component)) | |
| 1447 | (encoding-external-format (component-encoding c))) | |
| 1448 | ||
| 576ae2a5 RT |
1449 | (defclass proto-system () ; slots to keep when resetting a system |
| 1450 | ;; To preserve identity for all objects, we'd need keep the components slots | |
| 1451 | ;; but also to modify parse-component-form to reset the recycled objects. | |
| 1452 | ((name) #|(components) (components-by-names)|#)) | |
| 1453 | ||
| 1454 | (defclass system (module proto-system) | |
| 1ff353ac | 1455 | (;; description and long-description are now available for all component's, |
| 1456 | ;; but now also inherited from component, but we add the legacy accessor | |
| 1457 | (description :accessor system-description :initarg :description) | |
| 1458 | (long-description :accessor system-long-description :initarg :long-description) | |
| 36d9b3bc | 1459 | (author :accessor system-author :initarg :author) |
| 1460 | (maintainer :accessor system-maintainer :initarg :maintainer) | |
| 1461 | (licence :accessor system-licence :initarg :licence | |
| 1462 | :accessor system-license :initarg :license) | |
| 4c04d402 | 1463 | (source-file :reader %system-source-file :initarg :source-file ; for CLISP upgrade |
| 91fcdf9d | 1464 | :writer %set-system-source-file) |
| 1465 | (defsystem-depends-on :reader system-defsystem-depends-on :initarg :defsystem-depends-on))) | |
| 36d9b3bc | 1466 | |
| 1467 | ;;;; ------------------------------------------------------------------------- | |
| 1468 | ;;;; version-satisfies | |
| 1469 | ||
| 1470 | (defmethod version-satisfies ((c component) version) | |
| 1471 | (unless (and version (slot-boundp c 'version)) | |
| c3e0c711 | 1472 | (when version |
| 1473 | (warn "Requested version ~S but component ~S has no version" version c)) | |
| 36d9b3bc | 1474 | (return-from version-satisfies t)) |
| 1475 | (version-satisfies (component-version c) version)) | |
| 1476 | ||
| d0c8d6d5 | 1477 | (defun* asdf-version () |
| 1478 | "Exported interface to the version of ASDF currently installed. A string. | |
| 1479 | You can compare this string with e.g.: | |
| 1480 | (ASDF:VERSION-SATISFIES (ASDF:ASDF-VERSION) \"2.345.67\")." | |
| 1481 | *asdf-version*) | |
| 1482 | ||
| 1483 | (defun* parse-version (string &optional on-error) | |
| c3e0c711 | 1484 | "Parse a version string as a series of natural integers separated by dots. |
| 1485 | Return a (non-null) list of integers if the string is valid, NIL otherwise. | |
| 1486 | If on-error is error, warn, or designates a function of compatible signature, | |
| 1487 | the function is called with an explanation of what is wrong with the argument. | |
| 1488 | NB: ignores leading zeroes, and so doesn't distinguish between 2.003 and 2.3" | |
| 1489 | (and | |
| 1490 | (or (stringp string) | |
| 1491 | (when on-error | |
| 1492 | (funcall on-error "~S: ~S is not a string" | |
| 1493 | 'parse-version string)) nil) | |
| 1494 | (or (loop :for prev = nil :then c :for c :across string | |
| 1495 | :always (or (digit-char-p c) | |
| 1496 | (and (eql c #\.) prev (not (eql prev #\.)))) | |
| 1497 | :finally (return (and c (digit-char-p c)))) | |
| 1498 | (when on-error | |
| 1499 | (funcall on-error "~S: ~S doesn't follow asdf version numbering convention" | |
| 1500 | 'parse-version string)) nil) | |
| 1501 | (mapcar #'parse-integer (split-string string :separator ".")))) | |
| 1502 | ||
| 36d9b3bc | 1503 | (defmethod version-satisfies ((cver string) version) |
| c3e0c711 | 1504 | (let ((x (parse-version cver 'warn)) |
| 1505 | (y (parse-version version 'warn))) | |
| 36d9b3bc | 1506 | (labels ((bigger (x y) |
| 1507 | (cond ((not y) t) | |
| 1508 | ((not x) nil) | |
| 1509 | ((> (car x) (car y)) t) | |
| 1510 | ((= (car x) (car y)) | |
| 1511 | (bigger (cdr x) (cdr y)))))) | |
| c3e0c711 | 1512 | (and x y (= (car x) (car y)) |
| 36d9b3bc | 1513 | (or (not (cdr y)) (bigger (cdr x) (cdr y))))))) |
| 1514 | ||
| 4c04d402 RT |
1515 | ;;;; ----------------------------------------------------------------- |
| 1516 | ;;;; Windows shortcut support. Based on: | |
| 1517 | ;;;; | |
| 1518 | ;;;; Jesse Hager: The Windows Shortcut File Format. | |
| 1519 | ;;;; http://www.wotsit.org/list.asp?fc=13 | |
| 1520 | ||
| 1521 | #-(or clisp genera) ; CLISP doesn't need it, and READ-SEQUENCE annoys old Genera. | |
| 1522 | (progn | |
| 1523 | (defparameter *link-initial-dword* 76) | |
| 1524 | (defparameter *link-guid* #(1 20 2 0 0 0 0 0 192 0 0 0 0 0 0 70)) | |
| 1525 | ||
| 1526 | (defun* read-null-terminated-string (s) | |
| 1527 | (with-output-to-string (out) | |
| 1528 | (loop :for code = (read-byte s) | |
| 1529 | :until (zerop code) | |
| 1530 | :do (write-char (code-char code) out)))) | |
| 1531 | ||
| 1532 | (defun* read-little-endian (s &optional (bytes 4)) | |
| 1533 | (loop :for i :from 0 :below bytes | |
| 1534 | :sum (ash (read-byte s) (* 8 i)))) | |
| 1535 | ||
| 1536 | (defun* parse-file-location-info (s) | |
| 1537 | (let ((start (file-position s)) | |
| 1538 | (total-length (read-little-endian s)) | |
| 1539 | (end-of-header (read-little-endian s)) | |
| 1540 | (fli-flags (read-little-endian s)) | |
| 1541 | (local-volume-offset (read-little-endian s)) | |
| 1542 | (local-offset (read-little-endian s)) | |
| 1543 | (network-volume-offset (read-little-endian s)) | |
| 1544 | (remaining-offset (read-little-endian s))) | |
| 1545 | (declare (ignore total-length end-of-header local-volume-offset)) | |
| 1546 | (unless (zerop fli-flags) | |
| 1547 | (cond | |
| 1548 | ((logbitp 0 fli-flags) | |
| 1549 | (file-position s (+ start local-offset))) | |
| 1550 | ((logbitp 1 fli-flags) | |
| 1551 | (file-position s (+ start | |
| 1552 | network-volume-offset | |
| 1553 | #x14)))) | |
| 576ae2a5 RT |
1554 | (strcat (read-null-terminated-string s) |
| 1555 | (progn | |
| 1556 | (file-position s (+ start remaining-offset)) | |
| 1557 | (read-null-terminated-string s)))))) | |
| 4c04d402 RT |
1558 | |
| 1559 | (defun* parse-windows-shortcut (pathname) | |
| 1560 | (with-open-file (s pathname :element-type '(unsigned-byte 8)) | |
| 1561 | (handler-case | |
| 1562 | (when (and (= (read-little-endian s) *link-initial-dword*) | |
| 1563 | (let ((header (make-array (length *link-guid*)))) | |
| 1564 | (read-sequence header s) | |
| 1565 | (equalp header *link-guid*))) | |
| 1566 | (let ((flags (read-little-endian s))) | |
| 1567 | (file-position s 76) ;skip rest of header | |
| 1568 | (when (logbitp 0 flags) | |
| 1569 | ;; skip shell item id list | |
| 1570 | (let ((length (read-little-endian s 2))) | |
| 1571 | (file-position s (+ length (file-position s))))) | |
| 1572 | (cond | |
| 1573 | ((logbitp 1 flags) | |
| 1574 | (parse-file-location-info s)) | |
| 1575 | (t | |
| 1576 | (when (logbitp 2 flags) | |
| 1577 | ;; skip description string | |
| 1578 | (let ((length (read-little-endian s 2))) | |
| 1579 | (file-position s (+ length (file-position s))))) | |
| 1580 | (when (logbitp 3 flags) | |
| 1581 | ;; finally, our pathname | |
| 1582 | (let* ((length (read-little-endian s 2)) | |
| 1583 | (buffer (make-array length))) | |
| 1584 | (read-sequence buffer s) | |
| 1585 | (map 'string #'code-char buffer))))))) | |
| 1586 | (end-of-file () | |
| 1587 | nil))))) | |
| 1588 | ||
| 36d9b3bc | 1589 | ;;;; ------------------------------------------------------------------------- |
| 1590 | ;;;; Finding systems | |
| 1591 | ||
| 7fe7ec48 | 1592 | (defun* make-defined-systems-table () |
| 36d9b3bc | 1593 | (make-hash-table :test 'equal)) |
| 1594 | ||
| 1595 | (defvar *defined-systems* (make-defined-systems-table) | |
| 1596 | "This is a hash table whose keys are strings, being the | |
| 1597 | names of the systems, and whose values are pairs, the first | |
| 1598 | element of which is a universal-time indicating when the | |
| 1599 | system definition was last updated, and the second element | |
| 1600 | of which is a system object.") | |
| 1601 | ||
| 7fe7ec48 | 1602 | (defun* coerce-name (name) |
| 36d9b3bc | 1603 | (typecase name |
| 1604 | (component (component-name name)) | |
| 1605 | (symbol (string-downcase (symbol-name name))) | |
| 1606 | (string name) | |
| e5ee8946 | 1607 | (t (sysdef-error (compatfmt "~@<Invalid component designator: ~3i~_~A~@:>") name)))) |
| 36d9b3bc | 1608 | |
| 7fe7ec48 | 1609 | (defun* system-registered-p (name) |
| 36d9b3bc | 1610 | (gethash (coerce-name name) *defined-systems*)) |
| 1611 | ||
| 115a05e6 RT |
1612 | (defun* registered-systems () |
| 1613 | (loop :for (() . system) :being :the :hash-values :of *defined-systems* | |
| 1614 | :collect (coerce-name system))) | |
| 1615 | ||
| c3e0c711 | 1616 | (defun* register-system (system) |
| 1617 | (check-type system system) | |
| 1618 | (let ((name (component-name system))) | |
| 1619 | (check-type name string) | |
| 1620 | (asdf-message (compatfmt "~&~@<; ~@;Registering ~3i~_~A~@:>~%") system) | |
| 1621 | (unless (eq system (cdr (gethash name *defined-systems*))) | |
| 1622 | (setf (gethash name *defined-systems*) | |
| 1623 | (cons (get-universal-time) system))))) | |
| 1624 | ||
| 7fe7ec48 | 1625 | (defun* clear-system (name) |
| 3dfe0f91 | 1626 | "Clear the entry for a system in the database of systems previously loaded. |
| 1627 | Note that this does NOT in any way cause the code of the system to be unloaded." | |
| c3e0c711 | 1628 | ;; There is no "unload" operation in Common Lisp, and |
| 1629 | ;; a general such operation cannot be portably written, | |
| 1630 | ;; considering how much CL relies on side-effects to global data structures. | |
| 1ff353ac | 1631 | (remhash (coerce-name name) *defined-systems*)) |
| 3dfe0f91 | 1632 | |
| 7fe7ec48 | 1633 | (defun* map-systems (fn) |
| b986cd91 | 1634 | "Apply FN to each defined system. |
| 36d9b3bc | 1635 | |
| b986cd91 | 1636 | FN should be a function of one argument. It will be |
| 36d9b3bc | 1637 | called with an object of type asdf:system." |
| 1ff353ac | 1638 | (maphash #'(lambda (_ datum) |
| 36d9b3bc | 1639 | (declare (ignore _)) |
| 1ff353ac | 1640 | (destructuring-bind (_ . def) datum |
| 1641 | (declare (ignore _)) | |
| 1642 | (funcall fn def))) | |
| 36d9b3bc | 1643 | *defined-systems*)) |
| 1644 | ||
| 1645 | ;;; for the sake of keeping things reasonably neat, we adopt a | |
| 1646 | ;;; convention that functions in this list are prefixed SYSDEF- | |
| 1647 | ||
| 576ae2a5 RT |
1648 | (defvar *system-definition-search-functions* '()) |
| 1649 | ||
| 1650 | (setf *system-definition-search-functions* | |
| 1651 | (append | |
| 1652 | ;; Remove known-incompatible sysdef functions from ancient sbcl asdf. | |
| 1653 | (remove 'contrib-sysdef-search *system-definition-search-functions*) | |
| 1654 | ;; Tuck our defaults at the end of the list if they were absent. | |
| 1655 | ;; This is imperfect, in case they were removed on purpose, | |
| 1656 | ;; but then it will be the responsibility of whoever does that | |
| 1657 | ;; to upgrade asdf before he does such a thing rather than after. | |
| 1658 | (remove-if #'(lambda (x) (member x *system-definition-search-functions*)) | |
| 1659 | '(sysdef-central-registry-search | |
| 1660 | sysdef-source-registry-search | |
| 1661 | sysdef-find-asdf)))) | |
| b986cd91 | 1662 | |
| c3e0c711 | 1663 | (defun* search-for-system-definition (system) |
| 576ae2a5 RT |
1664 | (some (let ((name (coerce-name system))) #'(lambda (x) (funcall x name))) |
| 1665 | (cons 'find-system-if-being-defined | |
| 1666 | *system-definition-search-functions*))) | |
| 36d9b3bc | 1667 | |
| 1668 | (defvar *central-registry* nil | |
| 1669 | "A list of 'system directory designators' ASDF uses to find systems. | |
| 1670 | ||
| 1671 | A 'system directory designator' is a pathname or an expression | |
| 1672 | which evaluates to a pathname. For example: | |
| 1673 | ||
| 1674 | (setf asdf:*central-registry* | |
| 1675 | (list '*default-pathname-defaults* | |
| 1676 | #p\"/home/me/cl/systems/\" | |
| 1677 | #p\"/usr/share/common-lisp/systems/\")) | |
| 1678 | ||
| 1679 | This is for backward compatibilily. | |
| 1680 | Going forward, we recommend new users should be using the source-registry. | |
| 1681 | ") | |
| 1682 | ||
| 4c04d402 RT |
1683 | (defun* featurep (x &optional (features *features*)) |
| 1684 | (cond | |
| 1685 | ((atom x) | |
| 1686 | (and (member x features) t)) | |
| 1687 | ((eq :not (car x)) | |
| 1688 | (assert (null (cddr x))) | |
| 1689 | (not (featurep (cadr x) features))) | |
| 1690 | ((eq :or (car x)) | |
| 1691 | (some #'(lambda (x) (featurep x features)) (cdr x))) | |
| 1692 | ((eq :and (car x)) | |
| 1693 | (every #'(lambda (x) (featurep x features)) (cdr x))) | |
| 1694 | (t | |
| 1695 | (error "Malformed feature specification ~S" x)))) | |
| 1696 | ||
| 1697 | (defun* os-unix-p () | |
| 1698 | (featurep '(:or :unix :cygwin :darwin))) | |
| 1699 | ||
| 1700 | (defun* os-windows-p () | |
| 1701 | (and (not (os-unix-p)) (featurep '(:or :win32 :windows :mswindows :mingw32)))) | |
| 1702 | ||
| 7fe7ec48 | 1703 | (defun* probe-asd (name defaults) |
| b986cd91 | 1704 | (block nil |
| 1705 | (when (directory-pathname-p defaults) | |
| 115a05e6 RT |
1706 | (let* ((file (probe-file* (subpathname defaults (strcat name ".asd"))))) |
| 1707 | (when file | |
| b986cd91 | 1708 | (return file))) |
| 4c04d402 RT |
1709 | #-(or clisp genera) ; clisp doesn't need it, plain genera doesn't have read-sequence(!) |
| 1710 | (when (os-windows-p) | |
| 1711 | (let ((shortcut | |
| 1712 | (make-pathname | |
| 1713 | :defaults defaults :version :newest :case :local | |
| 576ae2a5 | 1714 | :name (strcat name ".asd") |
| 4c04d402 RT |
1715 | :type "lnk"))) |
| 1716 | (when (probe-file* shortcut) | |
| 1717 | (let ((target (parse-windows-shortcut shortcut))) | |
| 1718 | (when target | |
| 1719 | (return (pathname target)))))))))) | |
| b986cd91 | 1720 | |
| 7fe7ec48 | 1721 | (defun* sysdef-central-registry-search (system) |
| 36d9b3bc | 1722 | (let ((name (coerce-name system)) |
| 1723 | (to-remove nil) | |
| 1724 | (to-replace nil)) | |
| 1725 | (block nil | |
| 1726 | (unwind-protect | |
| 1727 | (dolist (dir *central-registry*) | |
| 1728 | (let ((defaults (eval dir))) | |
| 1729 | (when defaults | |
| 1730 | (cond ((directory-pathname-p defaults) | |
| 1731 | (let ((file (probe-asd name defaults))) | |
| 1732 | (when file | |
| 1733 | (return file)))) | |
| 1734 | (t | |
| 1735 | (restart-case | |
| 1736 | (let* ((*print-circle* nil) | |
| 1737 | (message | |
| e5ee8946 | 1738 | (format nil |
| 1739 | (compatfmt "~@<While searching for system ~S: ~3i~_~S evaluated to ~S which is not a directory.~@:>") | |
| 36d9b3bc | 1740 | system dir defaults))) |
| 1741 | (error message)) | |
| 1742 | (remove-entry-from-registry () | |
| 1743 | :report "Remove entry from *central-registry* and continue" | |
| 1744 | (push dir to-remove)) | |
| 1745 | (coerce-entry-to-directory () | |
| 1746 | :report (lambda (s) | |
| c3e0c711 | 1747 | (format s (compatfmt "~@<Coerce entry to ~a, replace ~a and continue.~@:>") |
| 1748 | (ensure-directory-pathname defaults) dir)) | |
| 36d9b3bc | 1749 | (push (cons dir (ensure-directory-pathname defaults)) to-replace)))))))) |
| 1750 | ;; cleanup | |
| 1751 | (dolist (dir to-remove) | |
| 1752 | (setf *central-registry* (remove dir *central-registry*))) | |
| 1753 | (dolist (pair to-replace) | |
| 1754 | (let* ((current (car pair)) | |
| 1755 | (new (cdr pair)) | |
| 1756 | (position (position current *central-registry*))) | |
| 1757 | (setf *central-registry* | |
| 1758 | (append (subseq *central-registry* 0 position) | |
| 1759 | (list new) | |
| 1760 | (subseq *central-registry* (1+ position)))))))))) | |
| 1761 | ||
| 7fe7ec48 | 1762 | (defun* make-temporary-package () |
| 36d9b3bc | 1763 | (flet ((try (counter) |
| 1764 | (ignore-errors | |
| 1765 | (make-package (format nil "~A~D" :asdf counter) | |
| 1766 | :use '(:cl :asdf))))) | |
| 1767 | (do* ((counter 0 (+ counter 1)) | |
| 1768 | (package (try counter) (try counter))) | |
| 1769 | (package package)))) | |
| 1770 | ||
| 7fe7ec48 | 1771 | (defun* safe-file-write-date (pathname) |
| 36d9b3bc | 1772 | ;; If FILE-WRITE-DATE returns NIL, it's possible that |
| 1773 | ;; the user or some other agent has deleted an input file. | |
| 1774 | ;; Also, generated files will not exist at the time planning is done | |
| 1775 | ;; and calls operation-done-p which calls safe-file-write-date. | |
| 1776 | ;; So it is very possible that we can't get a valid file-write-date, | |
| 1777 | ;; and we can survive and we will continue the planning | |
| 1778 | ;; as if the file were very old. | |
| 1779 | ;; (or should we treat the case in a different, special way?) | |
| c3e0c711 | 1780 | (or (and pathname (probe-file* pathname) (ignore-errors (file-write-date pathname))) |
| 36d9b3bc | 1781 | (progn |
| 7fe7ec48 | 1782 | (when (and pathname *asdf-verbose*) |
| e5ee8946 | 1783 | (warn (compatfmt "~@<Missing FILE-WRITE-DATE for ~S, treating it as zero.~@:>") |
| 36d9b3bc | 1784 | pathname)) |
| 1785 | 0))) | |
| 1786 | ||
| c3e0c711 | 1787 | (defmethod find-system ((name null) &optional (error-p t)) |
| 576ae2a5 | 1788 | (declare (ignorable name)) |
| c3e0c711 | 1789 | (when error-p |
| 1790 | (sysdef-error (compatfmt "~@<NIL is not a valid system name~@:>")))) | |
| 1791 | ||
| 7fe7ec48 | 1792 | (defmethod find-system (name &optional (error-p t)) |
| 1793 | (find-system (coerce-name name) error-p)) | |
| 1794 | ||
| c3e0c711 | 1795 | (defvar *systems-being-defined* nil |
| 1796 | "A hash-table of systems currently being defined keyed by name, or NIL") | |
| 1797 | ||
| 1798 | (defun* find-system-if-being-defined (name) | |
| 1799 | (when *systems-being-defined* | |
| 1800 | (gethash (coerce-name name) *systems-being-defined*))) | |
| 1801 | ||
| 1802 | (defun* call-with-system-definitions (thunk) | |
| 1803 | (if *systems-being-defined* | |
| 1804 | (funcall thunk) | |
| 1805 | (let ((*systems-being-defined* (make-hash-table :test 'equal))) | |
| 1806 | (funcall thunk)))) | |
| 1807 | ||
| 576ae2a5 | 1808 | (defmacro with-system-definitions ((&optional) &body body) |
| c3e0c711 | 1809 | `(call-with-system-definitions #'(lambda () ,@body))) |
| 1810 | ||
| 1811 | (defun* load-sysdef (name pathname) | |
| 1ff353ac | 1812 | ;; Tries to load system definition with canonical NAME from PATHNAME. |
| c3e0c711 | 1813 | (with-system-definitions () |
| 1814 | (let ((package (make-temporary-package))) | |
| 1815 | (unwind-protect | |
| 1816 | (handler-bind | |
| 1817 | ((error #'(lambda (condition) | |
| 1818 | (error 'load-system-definition-error | |
| 1819 | :name name :pathname pathname | |
| 1820 | :condition condition)))) | |
| 4c04d402 RT |
1821 | (let ((*package* package) |
| 1822 | (*default-pathname-defaults* | |
| 115a05e6 RT |
1823 | ;; resolve logical-pathnames so they won't wreak havoc in parsing namestrings. |
| 1824 | (pathname-directory-pathname (translate-logical-pathname pathname))) | |
| 1825 | (external-format (encoding-external-format (detect-encoding pathname)))) | |
| c3e0c711 | 1826 | (asdf-message (compatfmt "~&~@<; ~@;Loading system definition from ~A into ~A~@:>~%") |
| 1827 | pathname package) | |
| 115a05e6 | 1828 | (load pathname :external-format external-format))) |
| c3e0c711 | 1829 | (delete-package package))))) |
| 1ff353ac | 1830 | |
| 576ae2a5 RT |
1831 | (defun* locate-system (name) |
| 1832 | "Given a system NAME designator, try to locate where to load the system from. | |
| 115a05e6 RT |
1833 | Returns five values: FOUNDP FOUND-SYSTEM PATHNAME PREVIOUS PREVIOUS-TIME |
| 1834 | FOUNDP is true when a system was found, | |
| 1835 | either a new unregistered one or a previously registered one. | |
| 576ae2a5 | 1836 | FOUND-SYSTEM when not null is a SYSTEM object that may be REGISTER-SYSTEM'ed as is |
| 115a05e6 RT |
1837 | PATHNAME when not null is a path from where to load the system, |
| 1838 | either associated with FOUND-SYSTEM, or with the PREVIOUS system. | |
| 576ae2a5 RT |
1839 | PREVIOUS when not null is a previously loaded SYSTEM object of same name. |
| 1840 | PREVIOUS-TIME when not null is the time at which the PREVIOUS system was loaded." | |
| 1841 | (let* ((name (coerce-name name)) | |
| 1842 | (in-memory (system-registered-p name)) ; load from disk if absent or newer on disk | |
| 1843 | (previous (cdr in-memory)) | |
| 1844 | (previous (and (typep previous 'system) previous)) | |
| 1845 | (previous-time (car in-memory)) | |
| 115a05e6 | 1846 | (found (search-for-system-definition name)) |
| 576ae2a5 RT |
1847 | (found-system (and (typep found 'system) found)) |
| 1848 | (pathname (or (and (typep found '(or pathname string)) (pathname found)) | |
| 1849 | (and found-system (system-source-file found-system)) | |
| 1850 | (and previous (system-source-file previous)))) | |
| 1851 | (foundp (and (or found-system pathname previous) t))) | |
| 1852 | (check-type found (or null pathname system)) | |
| 1853 | (when foundp | |
| c3e0c711 | 1854 | (setf pathname (resolve-symlinks* pathname)) |
| 1855 | (when (and pathname (not (absolute-pathname-p pathname))) | |
| 1856 | (setf pathname (ensure-pathname-absolute pathname)) | |
| 1857 | (when found-system | |
| 1858 | (%set-system-source-file pathname found-system))) | |
| 1859 | (when (and previous (not (#-cormanlisp equal #+cormanlisp equalp | |
| 1860 | (system-source-file previous) pathname))) | |
| 1861 | (%set-system-source-file pathname previous) | |
| 1862 | (setf previous-time nil)) | |
| 576ae2a5 RT |
1863 | (values foundp found-system pathname previous previous-time)))) |
| 1864 | ||
| 1865 | (defmethod find-system ((name string) &optional (error-p t)) | |
| 1866 | (with-system-definitions () | |
| 1867 | (loop | |
| 1868 | (restart-case | |
| 1869 | (multiple-value-bind (foundp found-system pathname previous previous-time) | |
| 1870 | (locate-system name) | |
| 1871 | (declare (ignore foundp)) | |
| 1872 | (when (and found-system (not previous)) | |
| 1873 | (register-system found-system)) | |
| 1874 | (when (and pathname | |
| 1875 | (or (not previous-time) | |
| 1876 | ;; don't reload if it's already been loaded, | |
| 1877 | ;; or its filestamp is in the future which means some clock is skewed | |
| 1878 | ;; and trying to load might cause an infinite loop. | |
| 1879 | (< previous-time (safe-file-write-date pathname) (get-universal-time)))) | |
| 1880 | (load-sysdef name pathname)) | |
| 1881 | (let ((in-memory (system-registered-p name))) ; try again after loading from disk if needed | |
| 1882 | (return | |
| 1883 | (cond | |
| 1884 | (in-memory | |
| 1885 | (when pathname | |
| 1886 | (setf (car in-memory) (safe-file-write-date pathname))) | |
| 1887 | (cdr in-memory)) | |
| 1888 | (error-p | |
| 1889 | (error 'missing-component :requires name)))))) | |
| 1890 | (reinitialize-source-registry-and-retry () | |
| 1891 | :report (lambda (s) | |
| 115a05e6 | 1892 | (format s (compatfmt "~@<Retry finding system ~A after reinitializing the source-registry.~@:>") name)) |
| 576ae2a5 | 1893 | (initialize-source-registry)))))) |
| 7fe7ec48 | 1894 | |
| 91fcdf9d | 1895 | (defun* find-system-fallback (requested fallback &rest keys &key source-file &allow-other-keys) |
| 146e9122 | 1896 | (setf fallback (coerce-name fallback) |
| 146e9122 | 1897 | requested (coerce-name requested)) |
| 1898 | (when (equal requested fallback) | |
| c3e0c711 | 1899 | (let ((registered (cdr (gethash fallback *defined-systems*)))) |
| 1900 | (or registered | |
| 1901 | (apply 'make-instance 'system | |
| 1902 | :name fallback :source-file source-file keys))))) | |
| 146e9122 | 1903 | |
| 1904 | (defun* sysdef-find-asdf (name) | |
| 1ff353ac | 1905 | ;; Bug: :version *asdf-version* won't be updated when ASDF is updated. |
| 1906 | (find-system-fallback name "asdf" :version *asdf-version*)) | |
| 3dfe0f91 | 1907 | |
| 36d9b3bc | 1908 | |
| 1909 | ;;;; ------------------------------------------------------------------------- | |
| 1910 | ;;;; Finding components | |
| 1911 | ||
| 1912 | (defmethod find-component ((base string) path) | |
| 1913 | (let ((s (find-system base nil))) | |
| 1914 | (and s (find-component s path)))) | |
| 1915 | ||
| 1916 | (defmethod find-component ((base symbol) path) | |
| 1917 | (cond | |
| 1918 | (base (find-component (coerce-name base) path)) | |
| 1919 | (path (find-component path nil)) | |
| 1920 | (t nil))) | |
| 1921 | ||
| 1922 | (defmethod find-component ((base cons) path) | |
| 1923 | (find-component (car base) (cons (cdr base) path))) | |
| 1924 | ||
| 1925 | (defmethod find-component ((module module) (name string)) | |
| b986cd91 | 1926 | (unless (slot-boundp module 'components-by-name) ;; SBCL may miss the u-i-f-r-c method!!! |
| 1927 | (compute-module-components-by-name module)) | |
| 1928 | (values (gethash name (module-components-by-name module)))) | |
| 36d9b3bc | 1929 | |
| 1930 | (defmethod find-component ((component component) (name symbol)) | |
| 1931 | (if name | |
| 2220a102 | 1932 | (find-component component (coerce-name name)) |
| 36d9b3bc | 1933 | component)) |
| 1934 | ||
| 1935 | (defmethod find-component ((module module) (name cons)) | |
| 1936 | (find-component (find-component module (car name)) (cdr name))) | |
| 1937 | ||
| 1938 | ||
| 1939 | ;;; component subclasses | |
| 1940 | ||
| 1941 | (defclass source-file (component) | |
| 1942 | ((type :accessor source-file-explicit-type :initarg :type :initform nil))) | |
| 1943 | ||
| 1944 | (defclass cl-source-file (source-file) | |
| 1945 | ((type :initform "lisp"))) | |
| c3e0c711 | 1946 | (defclass cl-source-file.cl (cl-source-file) |
| 1947 | ((type :initform "cl"))) | |
| 1948 | (defclass cl-source-file.lsp (cl-source-file) | |
| 1949 | ((type :initform "lsp"))) | |
| 36d9b3bc | 1950 | (defclass c-source-file (source-file) |
| 1951 | ((type :initform "c"))) | |
| 1952 | (defclass java-source-file (source-file) | |
| 1953 | ((type :initform "java"))) | |
| 1954 | (defclass static-file (source-file) ()) | |
| 1955 | (defclass doc-file (static-file) ()) | |
| 1956 | (defclass html-file (doc-file) | |
| 1957 | ((type :initform "html"))) | |
| 1958 | ||
| 1959 | (defmethod source-file-type ((component module) (s module)) | |
| 1960 | (declare (ignorable component s)) | |
| 1961 | :directory) | |
| 1962 | (defmethod source-file-type ((component source-file) (s module)) | |
| 1963 | (declare (ignorable s)) | |
| 1964 | (source-file-explicit-type component)) | |
| 1965 | ||
| 36d9b3bc | 1966 | (defmethod component-relative-pathname ((component component)) |
| 1ff353ac | 1967 | (coerce-pathname |
| 36d9b3bc | 1968 | (or (slot-value component 'relative-pathname) |
| 1969 | (component-name component)) | |
| 1970 | :type (source-file-type component (component-system component)) | |
| 1971 | :defaults (component-parent-pathname component))) | |
| 1972 | ||
| 1973 | ;;;; ------------------------------------------------------------------------- | |
| 1974 | ;;;; Operations | |
| 1975 | ||
| 1976 | ;;; one of these is instantiated whenever #'operate is called | |
| 1977 | ||
| 1978 | (defclass operation () | |
| c3e0c711 | 1979 | (;; as of danb's 2003-03-16 commit e0d02781, :force can be: |
| 1980 | ;; T to force the inside of the specified system, | |
| 36d9b3bc | 1981 | ;; but not recurse to other systems we depend on. |
| 1982 | ;; :ALL (or any other atom) to force all systems | |
| 1983 | ;; including other systems we depend on. | |
| 1984 | ;; (SYSTEM1 SYSTEM2 ... SYSTEMN) | |
| 1985 | ;; to force systems named in a given list | |
| c3e0c711 | 1986 | ;; However, but this feature has only ever worked but starting with ASDF 2.014.5 |
| 36d9b3bc | 1987 | (forced :initform nil :initarg :force :accessor operation-forced) |
| 115a05e6 | 1988 | (forced-not :initform nil :initarg :force-not :accessor operation-forced-not) |
| 36d9b3bc | 1989 | (original-initargs :initform nil :initarg :original-initargs |
| 1990 | :accessor operation-original-initargs) | |
| 1991 | (visited-nodes :initform (make-hash-table :test 'equal) :accessor operation-visited-nodes) | |
| 1992 | (visiting-nodes :initform (make-hash-table :test 'equal) :accessor operation-visiting-nodes) | |
| 1993 | (parent :initform nil :initarg :parent :accessor operation-parent))) | |
| 1994 | ||
| 1995 | (defmethod print-object ((o operation) stream) | |
| 1996 | (print-unreadable-object (o stream :type t :identity t) | |
| 1997 | (ignore-errors | |
| 1998 | (prin1 (operation-original-initargs o) stream)))) | |
| 1999 | ||
| 2000 | (defmethod shared-initialize :after ((operation operation) slot-names | |
| 115a05e6 | 2001 | &key force force-not |
| 36d9b3bc | 2002 | &allow-other-keys) |
| 115a05e6 RT |
2003 | ;; the &allow-other-keys disables initarg validity checking |
| 2004 | (declare (ignorable operation slot-names force force-not)) | |
| 2005 | (macrolet ((frob (x) ;; normalize forced and forced-not slots | |
| 2006 | `(when (consp (,x operation)) | |
| 2007 | (setf (,x operation) | |
| 2008 | (mapcar #'coerce-name (,x operation)))))) | |
| 2009 | (frob operation-forced) (frob operation-forced-not)) | |
| 36d9b3bc | 2010 | (values)) |
| 2011 | ||
| 7fe7ec48 | 2012 | (defun* node-for (o c) |
| 36d9b3bc | 2013 | (cons (class-name (class-of o)) c)) |
| 2014 | ||
| 2015 | (defmethod operation-ancestor ((operation operation)) | |
| 2016 | (aif (operation-parent operation) | |
| 2017 | (operation-ancestor it) | |
| 2018 | operation)) | |
| 2019 | ||
| 2020 | ||
| 7fe7ec48 | 2021 | (defun* make-sub-operation (c o dep-c dep-o) |
| 36d9b3bc | 2022 | "C is a component, O is an operation, DEP-C is another |
| 2023 | component, and DEP-O, confusingly enough, is an operation | |
| 2024 | class specifier, not an operation." | |
| 2025 | (let* ((args (copy-list (operation-original-initargs o))) | |
| 2026 | (force-p (getf args :force))) | |
| 2027 | ;; note explicit comparison with T: any other non-NIL force value | |
| 2028 | ;; (e.g. :recursive) will pass through | |
| 2029 | (cond ((and (null (component-parent c)) | |
| 2030 | (null (component-parent dep-c)) | |
| 2031 | (not (eql c dep-c))) | |
| 2032 | (when (eql force-p t) | |
| 2033 | (setf (getf args :force) nil)) | |
| c3e0c711 | 2034 | (apply 'make-instance dep-o |
| 36d9b3bc | 2035 | :parent o |
| 2036 | :original-initargs args args)) | |
| 2037 | ((subtypep (type-of o) dep-o) | |
| 2038 | o) | |
| 2039 | (t | |
| c3e0c711 | 2040 | (apply 'make-instance dep-o |
| 36d9b3bc | 2041 | :parent o :original-initargs args args))))) |
| 2042 | ||
| 2043 | ||
| 2044 | (defmethod visit-component ((o operation) (c component) data) | |
| 2045 | (unless (component-visited-p o c) | |
| 2046 | (setf (gethash (node-for o c) | |
| 2047 | (operation-visited-nodes (operation-ancestor o))) | |
| 2048 | (cons t data)))) | |
| 2049 | ||
| 2050 | (defmethod component-visited-p ((o operation) (c component)) | |
| 2051 | (gethash (node-for o c) | |
| 2052 | (operation-visited-nodes (operation-ancestor o)))) | |
| 2053 | ||
| 2054 | (defmethod (setf visiting-component) (new-value operation component) | |
| 2055 | ;; MCL complains about unused lexical variables | |
| 2056 | (declare (ignorable operation component)) | |
| 2057 | new-value) | |
| 2058 | ||
| 2059 | (defmethod (setf visiting-component) (new-value (o operation) (c component)) | |
| 2060 | (let ((node (node-for o c)) | |
| 2061 | (a (operation-ancestor o))) | |
| 2062 | (if new-value | |
| 2063 | (setf (gethash node (operation-visiting-nodes a)) t) | |
| 2064 | (remhash node (operation-visiting-nodes a))) | |
| 2065 | new-value)) | |
| 2066 | ||
| 2067 | (defmethod component-visiting-p ((o operation) (c component)) | |
| 2068 | (let ((node (node-for o c))) | |
| 2069 | (gethash node (operation-visiting-nodes (operation-ancestor o))))) | |
| 2070 | ||
| 2071 | (defmethod component-depends-on ((op-spec symbol) (c component)) | |
| c3e0c711 | 2072 | ;; Note: we go from op-spec to operation via make-instance |
| 2073 | ;; to allow for specialization through defmethod's, even though | |
| 2074 | ;; it's a detour in the default case below. | |
| 36d9b3bc | 2075 | (component-depends-on (make-instance op-spec) c)) |
| 2076 | ||
| 2077 | (defmethod component-depends-on ((o operation) (c component)) | |
| c3e0c711 | 2078 | (cdr (assoc (type-of o) (component-in-order-to c)))) |
| 36d9b3bc | 2079 | |
| 2080 | (defmethod component-self-dependencies ((o operation) (c component)) | |
| 576ae2a5 RT |
2081 | (remove-if-not |
| 2082 | #'(lambda (x) (member (component-name c) (cdr x) :test #'string=)) | |
| 2083 | (component-depends-on o c))) | |
| 36d9b3bc | 2084 | |
| 2085 | (defmethod input-files ((operation operation) (c component)) | |
| 2086 | (let ((parent (component-parent c)) | |
| 2087 | (self-deps (component-self-dependencies operation c))) | |
| 2088 | (if self-deps | |
| 1ff353ac | 2089 | (mapcan #'(lambda (dep) |
| 2090 | (destructuring-bind (op name) dep | |
| 2091 | (output-files (make-instance op) | |
| 2092 | (find-component parent name)))) | |
| 36d9b3bc | 2093 | self-deps) |
| 2094 | ;; no previous operations needed? I guess we work with the | |
| 2095 | ;; original source file, then | |
| 2096 | (list (component-pathname c))))) | |
| 2097 | ||
| 2098 | (defmethod input-files ((operation operation) (c module)) | |
| 2099 | (declare (ignorable operation c)) | |
| 2100 | nil) | |
| 2101 | ||
| 2102 | (defmethod component-operation-time (o c) | |
| 2103 | (gethash (type-of o) (component-operation-times c))) | |
| 2104 | ||
| 2105 | (defmethod operation-done-p ((o operation) (c component)) | |
| 2106 | (let ((out-files (output-files o c)) | |
| 2107 | (in-files (input-files o c)) | |
| 2108 | (op-time (component-operation-time o c))) | |
| 2109 | (flet ((earliest-out () | |
| 2110 | (reduce #'min (mapcar #'safe-file-write-date out-files))) | |
| 2111 | (latest-in () | |
| 2112 | (reduce #'max (mapcar #'safe-file-write-date in-files)))) | |
| 2113 | (cond | |
| 2114 | ((and (not in-files) (not out-files)) | |
| 2115 | ;; arbitrary decision: an operation that uses nothing to | |
| 2116 | ;; produce nothing probably isn't doing much. | |
| 2117 | ;; e.g. operations on systems, modules that have no immediate action, | |
| 2118 | ;; but are only meaningful through traversed dependencies | |
| 2119 | t) | |
| 2120 | ((not out-files) | |
| 2121 | ;; an operation without output-files is probably meant | |
| 2122 | ;; for its side-effects in the current image, | |
| 2123 | ;; assumed to be idem-potent, | |
| 2124 | ;; e.g. LOAD-OP or LOAD-SOURCE-OP of some CL-SOURCE-FILE. | |
| 2125 | (and op-time (>= op-time (latest-in)))) | |
| 2126 | ((not in-files) | |
| 4c04d402 | 2127 | ;; an operation with output-files and no input-files |
| 36d9b3bc | 2128 | ;; is probably meant for its side-effects on the file-system, |
| 2129 | ;; assumed to have to be done everytime. | |
| 2130 | ;; (I don't think there is any such case in ASDF unless extended) | |
| 2131 | nil) | |
| 2132 | (t | |
| 2133 | ;; an operation with both input and output files is assumed | |
| 2134 | ;; as computing the latter from the former, | |
| 2135 | ;; assumed to have been done if the latter are all older | |
| 2136 | ;; than the former. | |
| 2137 | ;; e.g. COMPILE-OP of some CL-SOURCE-FILE. | |
| 2138 | ;; We use >= instead of > to play nice with generated files. | |
| 2139 | ;; This opens a race condition if an input file is changed | |
| 2140 | ;; after the output is created but within the same second | |
| 2141 | ;; of filesystem time; but the same race condition exists | |
| 2142 | ;; whenever the computation from input to output takes more | |
| 2143 | ;; than one second of filesystem time (or just crosses the | |
| 2144 | ;; second). So that's cool. | |
| 2145 | (and | |
| 1ff353ac | 2146 | (every #'probe-file* in-files) |
| 2147 | (every #'probe-file* out-files) | |
| 36d9b3bc | 2148 | (>= (earliest-out) (latest-in)))))))) |
| 2149 | ||
| 2150 | ||
| 2151 | ||
| 2152 | ;;; For 1.700 I've done my best to refactor TRAVERSE | |
| 2153 | ;;; by splitting it up in a bunch of functions, | |
| 2154 | ;;; so as to improve the collection and use-detection algorithm. --fare | |
| 2155 | ;;; The protocol is as follows: we pass around operation, dependency, | |
| 2156 | ;;; bunch of other stuff, and a force argument. Return a force flag. | |
| 2157 | ;;; The returned flag is T if anything has changed that requires a rebuild. | |
| 2158 | ;;; The force argument is a list of components that will require a rebuild | |
| 2159 | ;;; if the flag is T, at which point whoever returns the flag has to | |
| 2160 | ;;; mark them all as forced, and whoever recurses again can use a NIL list | |
| 2161 | ;;; as a further argument. | |
| 2162 | ||
| 2163 | (defvar *forcing* nil | |
| 2164 | "This dynamically-bound variable is used to force operations in | |
| 2165 | recursive calls to traverse.") | |
| 2166 | ||
| 7fe7ec48 | 2167 | (defgeneric* do-traverse (operation component collect)) |
| 36d9b3bc | 2168 | |
| 4c04d402 | 2169 | (defun* resolve-dependency-name (component name &optional version) |
| 36d9b3bc | 2170 | (loop |
| 2171 | (restart-case | |
| 4c04d402 RT |
2172 | (return |
| 2173 | (let ((comp (find-component (component-parent component) name))) | |
| 2174 | (unless comp | |
| 2175 | (error 'missing-dependency | |
| 2176 | :required-by component | |
| 2177 | :requires name)) | |
| 2178 | (when version | |
| 2179 | (unless (version-satisfies comp version) | |
| 2180 | (error 'missing-dependency-of-version | |
| 2181 | :required-by component | |
| 2182 | :version version | |
| 2183 | :requires name))) | |
| 2184 | comp)) | |
| 36d9b3bc | 2185 | (retry () |
| 2186 | :report (lambda (s) | |
| 115a05e6 | 2187 | (format s (compatfmt "~@<Retry loading ~3i~_~A.~@:>") name)) |
| 36d9b3bc | 2188 | :test |
| 2189 | (lambda (c) | |
| c3e0c711 | 2190 | (or (null c) |
| 2191 | (and (typep c 'missing-dependency) | |
| 4c04d402 RT |
2192 | (eq (missing-required-by c) component) |
| 2193 | (equal (missing-requires c) name)))))))) | |
| 2194 | ||
| 2195 | (defun* resolve-dependency-spec (component dep-spec) | |
| 2196 | (cond | |
| 2197 | ((atom dep-spec) | |
| 2198 | (resolve-dependency-name component dep-spec)) | |
| 2199 | ;; Structured dependencies --- this parses keywords. | |
| 2200 | ;; The keywords could conceivably be broken out and cleanly (extensibly) | |
| 2201 | ;; processed by EQL methods. But for now, here's what we've got. | |
| 2202 | ((eq :version (first dep-spec)) | |
| 2203 | ;; https://bugs.launchpad.net/asdf/+bug/527788 | |
| 2204 | (resolve-dependency-name component (second dep-spec) (third dep-spec))) | |
| 2205 | ((eq :feature (first dep-spec)) | |
| 2206 | ;; This particular subform is not documented and | |
| 2207 | ;; has always been broken in the past. | |
| 2208 | ;; Therefore no one uses it, and I'm cerroring it out, | |
| 2209 | ;; after fixing it | |
| 2210 | ;; See https://bugs.launchpad.net/asdf/+bug/518467 | |
| 2211 | (cerror "Continue nonetheless." | |
| 2212 | "Congratulations, you're the first ever user of FEATURE dependencies! Please contact the asdf-devel mailing-list.") | |
| 2213 | (when (find (second dep-spec) *features* :test 'string-equal) | |
| 2214 | (resolve-dependency-name component (third dep-spec)))) | |
| 2215 | (t | |
| 2216 | (error (compatfmt "~@<Bad dependency ~s. Dependencies must be (:version <name> <version>), (:feature <feature> <name>), or <name>.~@:>") dep-spec)))) | |
| 2217 | ||
| 2218 | (defun* do-one-dep (op c collect dep-op dep-c) | |
| 2219 | ;; Collects a partial plan for performing dep-op on dep-c | |
| 2220 | ;; as dependencies of a larger plan involving op and c. | |
| 2221 | ;; Returns t if this should force recompilation of those who depend on us. | |
| 2222 | ;; dep-op is an operation class name (not an operation object), | |
| 2223 | ;; whereas dep-c is a component object.n | |
| 2224 | (do-traverse (make-sub-operation c op dep-c dep-op) dep-c collect)) | |
| 2225 | ||
| 2226 | (defun* do-dep (op c collect dep-op-spec dep-c-specs) | |
| 2227 | ;; Collects a partial plan for performing dep-op-spec on each of dep-c-specs | |
| 2228 | ;; as dependencies of a larger plan involving op and c. | |
| 2229 | ;; Returns t if this should force recompilation of those who depend on us. | |
| 2230 | ;; dep-op-spec is either an operation class name (not an operation object), | |
| 2231 | ;; or the magic symbol asdf:feature. | |
| 2232 | ;; If dep-op-spec is asdf:feature, then the first dep-c-specs is a keyword, | |
| 2233 | ;; and the plan will succeed if that keyword is present in *feature*, | |
| 2234 | ;; or fail if it isn't | |
| 2235 | ;; (at which point c's :if-component-dep-fails will kick in). | |
| 2236 | ;; If dep-op-spec is an operation class name, | |
| 2237 | ;; then dep-c-specs specifies a list of sibling component of c, | |
| 2238 | ;; as per resolve-dependency-spec, such that operating op on c | |
| 2239 | ;; depends on operating dep-op-spec on each of them. | |
| 2240 | (cond ((eq dep-op-spec 'feature) | |
| 2241 | (if (member (car dep-c-specs) *features*) | |
| 36d9b3bc | 2242 | nil |
| 2243 | (error 'missing-dependency | |
| 2244 | :required-by c | |
| 4c04d402 | 2245 | :requires (list :feature (car dep-c-specs))))) |
| 36d9b3bc | 2246 | (t |
| 2247 | (let ((flag nil)) | |
| 4c04d402 RT |
2248 | (dolist (d dep-c-specs) |
| 2249 | (when (do-one-dep op c collect dep-op-spec | |
| 2250 | (resolve-dependency-spec c d)) | |
| 2251 | (setf flag t))) | |
| 36d9b3bc | 2252 | flag)))) |
| 2253 | ||
| 7fe7ec48 | 2254 | (defvar *visit-count* 0) ; counter that allows to sort nodes from operation-visited-nodes |
| 2255 | ||
| 2256 | (defun* do-collect (collect x) | |
| 36d9b3bc | 2257 | (funcall collect x)) |
| 2258 | ||
| 2259 | (defmethod do-traverse ((operation operation) (c component) collect) | |
| c3e0c711 | 2260 | (let ((*forcing* *forcing*) |
| 2261 | (flag nil)) ;; return value: must we rebuild this and its dependencies? | |
| 36d9b3bc | 2262 | (labels |
| 2263 | ((update-flag (x) | |
| c3e0c711 | 2264 | (orf flag x)) |
| 36d9b3bc | 2265 | (dep (op comp) |
| 2266 | (update-flag (do-dep operation c collect op comp)))) | |
| 2267 | ;; Have we been visited yet? If so, just process the result. | |
| 2268 | (aif (component-visited-p operation c) | |
| 2269 | (progn | |
| 2270 | (update-flag (cdr it)) | |
| 2271 | (return-from do-traverse flag))) | |
| 2272 | ;; dependencies | |
| 2273 | (when (component-visiting-p operation c) | |
| 2274 | (error 'circular-dependency :components (list c))) | |
| 2275 | (setf (visiting-component operation c) t) | |
| 2276 | (unwind-protect | |
| 115a05e6 RT |
2277 | (block nil |
| 2278 | (when (typep c 'system) ;; systems can be forced or forced-not | |
| 2279 | (let ((ancestor (operation-ancestor operation))) | |
| 2280 | (flet ((match? (f) | |
| 2281 | (and f (or (not (consp f)) ;; T or :ALL | |
| 2282 | (member (component-name c) f :test #'equal))))) | |
| 2283 | (cond | |
| 2284 | ((match? (operation-forced ancestor)) | |
| 2285 | (setf *forcing* t)) | |
| 2286 | ((match? (operation-forced-not ancestor)) | |
| 2287 | (return)))))) | |
| 36d9b3bc | 2288 | ;; first we check and do all the dependencies for the module. |
| 2289 | ;; Operations planned in this loop will show up | |
| 2290 | ;; in the results, and are consumed below. | |
| 2291 | (let ((*forcing* nil)) | |
| 2292 | ;; upstream dependencies are never forced to happen just because | |
| 2293 | ;; the things that depend on them are.... | |
| 2294 | (loop | |
| 2295 | :for (required-op . deps) :in (component-depends-on operation c) | |
| 2296 | :do (dep required-op deps))) | |
| 2297 | ;; constituent bits | |
| 2298 | (let ((module-ops | |
| 2299 | (when (typep c 'module) | |
| 2300 | (let ((at-least-one nil) | |
| 2301 | ;; This is set based on the results of the | |
| 2302 | ;; dependencies and whether we are in the | |
| 2303 | ;; context of a *forcing* call... | |
| 2304 | ;; inter-system dependencies do NOT trigger | |
| 2305 | ;; building components | |
| 2306 | (*forcing* | |
| 2307 | (or *forcing* | |
| 2308 | (and flag (not (typep c 'system))))) | |
| 2309 | (error nil)) | |
| 2310 | (while-collecting (internal-collect) | |
| 2311 | (dolist (kid (module-components c)) | |
| 2312 | (handler-case | |
| 2313 | (update-flag | |
| 2314 | (do-traverse operation kid #'internal-collect)) | |
| 4c04d402 | 2315 | #-genera |
| 36d9b3bc | 2316 | (missing-dependency (condition) |
| 2317 | (when (eq (module-if-component-dep-fails c) | |
| 2318 | :fail) | |
| 2319 | (error condition)) | |
| 2320 | (setf error condition)) | |
| 2321 | (:no-error (c) | |
| 2322 | (declare (ignore c)) | |
| 2323 | (setf at-least-one t)))) | |
| 2324 | (when (and (eq (module-if-component-dep-fails c) | |
| 2325 | :try-next) | |
| 2326 | (not at-least-one)) | |
| 2327 | (error error))))))) | |
| c3e0c711 | 2328 | (update-flag (or *forcing* (not (operation-done-p operation c)))) |
| 36d9b3bc | 2329 | ;; For sub-operations, check whether |
| 2330 | ;; the original ancestor operation was forced, | |
| 2331 | ;; or names us amongst an explicit list of things to force... | |
| 2332 | ;; except that this check doesn't distinguish | |
| 2333 | ;; between all the things with a given name. Sigh. | |
| 2334 | ;; BROKEN! | |
| 36d9b3bc | 2335 | (when flag |
| 2336 | (let ((do-first (cdr (assoc (class-name (class-of operation)) | |
| 2337 | (component-do-first c))))) | |
| 2338 | (loop :for (required-op . deps) :in do-first | |
| 2339 | :do (do-dep operation c collect required-op deps))) | |
| 2340 | (do-collect collect (vector module-ops)) | |
| 2341 | (do-collect collect (cons operation c))))) | |
| 115a05e6 RT |
2342 | (setf (visiting-component operation c) nil))) |
| 2343 | (visit-component operation c (when flag (incf *visit-count*))) | |
| 2344 | flag)) | |
| 36d9b3bc | 2345 | |
| 7fe7ec48 | 2346 | (defun* flatten-tree (l) |
| 36d9b3bc | 2347 | ;; You collected things into a list. |
| 2348 | ;; Most elements are just things to collect again. | |
| 2349 | ;; A (simple-vector 1) indicate that you should recurse into its contents. | |
| 2350 | ;; This way, in two passes (rather than N being the depth of the tree), | |
| 2351 | ;; you can collect things with marginally constant-time append, | |
| 2352 | ;; achieving linear time collection instead of quadratic time. | |
| 2353 | (while-collecting (c) | |
| 2354 | (labels ((r (x) | |
| 2355 | (if (typep x '(simple-vector 1)) | |
| 2356 | (r* (svref x 0)) | |
| 2357 | (c x))) | |
| 2358 | (r* (l) | |
| 2359 | (dolist (x l) (r x)))) | |
| 2360 | (r* l)))) | |
| 2361 | ||
| b986cd91 | 2362 | (defmethod traverse ((operation operation) (c component)) |
| b986cd91 | 2363 | (flatten-tree |
| 2364 | (while-collecting (collect) | |
| 7fe7ec48 | 2365 | (let ((*visit-count* 0)) |
| 2366 | (do-traverse operation c #'collect))))) | |
| b986cd91 | 2367 | |
| 36d9b3bc | 2368 | (defmethod perform ((operation operation) (c source-file)) |
| 2369 | (sysdef-error | |
| e5ee8946 | 2370 | (compatfmt "~@<Required method PERFORM not implemented for operation ~A, component ~A~@:>") |
| 36d9b3bc | 2371 | (class-of operation) (class-of c))) |
| 2372 | ||
| 2373 | (defmethod perform ((operation operation) (c module)) | |
| 2374 | (declare (ignorable operation c)) | |
| 2375 | nil) | |
| 2376 | ||
| 4c04d402 RT |
2377 | (defmethod mark-operation-done ((operation operation) (c component)) |
| 2378 | (setf (gethash (type-of operation) (component-operation-times c)) | |
| 2379 | (reduce #'max | |
| 2380 | (cons (get-universal-time) | |
| 2381 | (mapcar #'safe-file-write-date (input-files operation c)))))) | |
| 2382 | ||
| 2383 | (defmethod perform-with-restarts (operation component) | |
| 2384 | ;; TOO verbose, especially as the default. Add your own :before method | |
| 2385 | ;; to perform-with-restart or perform if you want that: | |
| 2386 | #|(when *asdf-verbose* (explain operation component))|# | |
| 2387 | (perform operation component)) | |
| 2388 | ||
| 2389 | (defmethod perform-with-restarts :around (operation component) | |
| 2390 | (loop | |
| 2391 | (restart-case | |
| 2392 | (return (call-next-method)) | |
| 2393 | (retry () | |
| 2394 | :report | |
| 2395 | (lambda (s) | |
| 2396 | (format s (compatfmt "~@<Retry ~A.~@:>") | |
| 2397 | (operation-description operation component)))) | |
| 2398 | (accept () | |
| 2399 | :report | |
| 2400 | (lambda (s) | |
| 2401 | (format s (compatfmt "~@<Continue, treating ~A as having been successful.~@:>") | |
| 2402 | (operation-description operation component))) | |
| 2403 | (mark-operation-done operation component) | |
| 2404 | (return))))) | |
| 2405 | ||
| 36d9b3bc | 2406 | (defmethod explain ((operation operation) (component component)) |
| c3e0c711 | 2407 | (asdf-message (compatfmt "~&~@<; ~@;~A~:>~%") |
| 2408 | (operation-description operation component))) | |
| 7fe7ec48 | 2409 | |
| 2410 | (defmethod operation-description (operation component) | |
| c3e0c711 | 2411 | (format nil (compatfmt "~@<~A on ~A~@:>") |
| 2412 | (class-of operation) component)) | |
| 36d9b3bc | 2413 | |
| 2414 | ;;;; ------------------------------------------------------------------------- | |
| 2415 | ;;;; compile-op | |
| 2416 | ||
| 2417 | (defclass compile-op (operation) | |
| 2418 | ((proclamations :initarg :proclamations :accessor compile-op-proclamations :initform nil) | |
| 2419 | (on-warnings :initarg :on-warnings :accessor operation-on-warnings | |
| 2420 | :initform *compile-file-warnings-behaviour*) | |
| 2421 | (on-failure :initarg :on-failure :accessor operation-on-failure | |
| 2422 | :initform *compile-file-failure-behaviour*) | |
| 2423 | (flags :initarg :flags :accessor compile-op-flags | |
| 1ff353ac | 2424 | :initform nil))) |
| 36d9b3bc | 2425 | |
| d0c8d6d5 | 2426 | (defun* output-file (operation component) |
| 7fe7ec48 | 2427 | "The unique output file of performing OPERATION on COMPONENT" |
| 2428 | (let ((files (output-files operation component))) | |
| 2429 | (assert (length=n-p files 1)) | |
| 2430 | (first files))) | |
| 2431 | ||
| 4c04d402 | 2432 | (defun* ensure-all-directories-exist (pathnames) |
| 115a05e6 RT |
2433 | (dolist (pathname pathnames) |
| 2434 | (ensure-directories-exist (translate-logical-pathname pathname)))) | |
| 36d9b3bc | 2435 | |
| 4c04d402 | 2436 | (defmethod perform :before ((operation compile-op) (c source-file)) |
| 115a05e6 | 2437 | (ensure-all-directories-exist (output-files operation c))) |
| 4c04d402 | 2438 | |
| 36d9b3bc | 2439 | (defmethod perform :after ((operation operation) (c component)) |
| 4c04d402 RT |
2440 | (mark-operation-done operation c)) |
| 2441 | ||
| 2442 | (defgeneric* around-compile-hook (component)) | |
| 2443 | (defgeneric* call-with-around-compile-hook (component thunk)) | |
| 2444 | ||
| 2445 | (defmethod around-compile-hook ((c component)) | |
| 2446 | (cond | |
| 2447 | ((slot-boundp c 'around-compile) | |
| 2448 | (slot-value c 'around-compile)) | |
| 2449 | ((component-parent c) | |
| 2450 | (around-compile-hook (component-parent c))))) | |
| 2451 | ||
| 576ae2a5 RT |
2452 | (defun ensure-function (fun &key (package :asdf)) |
| 2453 | (etypecase fun | |
| 2454 | ((or symbol function) fun) | |
| 2455 | (cons (eval `(function ,fun))) | |
| 2456 | (string (eval `(function ,(with-standard-io-syntax | |
| 2457 | (let ((*package* (find-package package))) | |
| 2458 | (read-from-string fun)))))))) | |
| 2459 | ||
| 4c04d402 RT |
2460 | (defmethod call-with-around-compile-hook ((c component) thunk) |
| 2461 | (let ((hook (around-compile-hook c))) | |
| 2462 | (if hook | |
| 576ae2a5 | 2463 | (funcall (ensure-function hook) thunk) |
| 4c04d402 | 2464 | (funcall thunk)))) |
| 36d9b3bc | 2465 | |
| 2466 | ;;; perform is required to check output-files to find out where to put | |
| 2467 | ;;; its answers, in case it has been overridden for site policy | |
| 2468 | (defmethod perform ((operation compile-op) (c cl-source-file)) | |
| 36d9b3bc | 2469 | (let ((source-file (component-pathname c)) |
| 7fe7ec48 | 2470 | ;; on some implementations, there are more than one output-file, |
| 2471 | ;; but the first one should always be the primary fasl that gets loaded. | |
| 2472 | (output-file (first (output-files operation c))) | |
| 3dfe0f91 | 2473 | (*compile-file-warnings-behaviour* (operation-on-warnings operation)) |
| 2474 | (*compile-file-failure-behaviour* (operation-on-failure operation))) | |
| 36d9b3bc | 2475 | (multiple-value-bind (output warnings-p failure-p) |
| 4c04d402 | 2476 | (call-with-around-compile-hook |
| 3db9313f | 2477 | c #'(lambda (&rest flags) |
| 4c04d402 | 2478 | (apply *compile-op-compile-file-function* source-file |
| 115a05e6 RT |
2479 | :output-file output-file |
| 2480 | :external-format (component-external-format c) | |
| 3db9313f | 2481 | (append flags (compile-op-flags operation))))) |
| c3e0c711 | 2482 | (unless output |
| 2483 | (error 'compile-error :component c :operation operation)) | |
| 36d9b3bc | 2484 | (when failure-p |
| 2485 | (case (operation-on-failure operation) | |
| 2486 | (:warn (warn | |
| e5ee8946 | 2487 | (compatfmt "~@<COMPILE-FILE failed while performing ~A on ~A.~@:>") |
| 36d9b3bc | 2488 | operation c)) |
| 2489 | (:error (error 'compile-failed :component c :operation operation)) | |
| 2490 | (:ignore nil))) | |
| c3e0c711 | 2491 | (when warnings-p |
| 2492 | (case (operation-on-warnings operation) | |
| 2493 | (:warn (warn | |
| 2494 | (compatfmt "~@<COMPILE-FILE warned while performing ~A on ~A.~@:>") | |
| 2495 | operation c)) | |
| 2496 | (:error (error 'compile-warned :component c :operation operation)) | |
| 2497 | (:ignore nil)))))) | |
| 36d9b3bc | 2498 | |
| 2499 | (defmethod output-files ((operation compile-op) (c cl-source-file)) | |
| 2500 | (declare (ignorable operation)) | |
| b6f29d0e RT |
2501 | (let* ((p (lispize-pathname (component-pathname c))) |
| 2502 | (f (compile-file-pathname ;; fasl | |
| 2503 | p #+mkcl :fasl-p #+mkcl t #+ecl :type #+ecl :fasl)) | |
| 2504 | #+mkcl (o (compile-file-pathname p :fasl-p nil))) ;; object file | |
| 2505 | #+ecl (if (use-ecl-byte-compiler-p) | |
| 2506 | (list f) | |
| 2507 | (list (compile-file-pathname p :type :object) f)) | |
| 2508 | #+mkcl (list o f) | |
| 2509 | #-(or ecl mkcl) (list f))) | |
| 36d9b3bc | 2510 | |
| 2511 | (defmethod perform ((operation compile-op) (c static-file)) | |
| 2512 | (declare (ignorable operation c)) | |
| 2513 | nil) | |
| 2514 | ||
| 2515 | (defmethod output-files ((operation compile-op) (c static-file)) | |
| 2516 | (declare (ignorable operation c)) | |
| 2517 | nil) | |
| 2518 | ||
| 2519 | (defmethod input-files ((operation compile-op) (c static-file)) | |
| 2520 | (declare (ignorable operation c)) | |
| 2521 | nil) | |
| 2522 | ||
| 7fe7ec48 | 2523 | (defmethod operation-description ((operation compile-op) component) |
| 2524 | (declare (ignorable operation)) | |
| c3e0c711 | 2525 | (format nil (compatfmt "~@<compiling ~3i~_~A~@:>") component)) |
| 2526 | ||
| 2527 | (defmethod operation-description ((operation compile-op) (component module)) | |
| 2528 | (declare (ignorable operation)) | |
| 2529 | (format nil (compatfmt "~@<compiled ~3i~_~A~@:>") component)) | |
| 2530 | ||
| 36d9b3bc | 2531 | |
| 2532 | ;;;; ------------------------------------------------------------------------- | |
| 2533 | ;;;; load-op | |
| 2534 | ||
| 2535 | (defclass basic-load-op (operation) ()) | |
| 2536 | ||
| 2537 | (defclass load-op (basic-load-op) ()) | |
| 2538 | ||
| 4c04d402 RT |
2539 | (defmethod perform-with-restarts ((o load-op) (c cl-source-file)) |
| 2540 | (loop | |
| 2541 | (restart-case | |
| 2542 | (return (call-next-method)) | |
| 2543 | (try-recompiling () | |
| 2544 | :report (lambda (s) | |
| 2545 | (format s "Recompile ~a and try loading it again" | |
| 2546 | (component-name c))) | |
| 2547 | (perform (make-sub-operation c o c 'compile-op) c))))) | |
| 2548 | ||
| 36d9b3bc | 2549 | (defmethod perform ((o load-op) (c cl-source-file)) |
| b6f29d0e RT |
2550 | (map () #'load |
| 2551 | #-(or ecl mkcl) | |
| 2552 | (input-files o c) | |
| 2553 | #+(or ecl mkcl) | |
| 2554 | (loop :for i :in (input-files o c) | |
| 2555 | :unless (string= (pathname-type i) "fas") | |
| 2556 | :collect (compile-file-pathname (lispize-pathname i))))) | |
| 36d9b3bc | 2557 | |
| 36d9b3bc | 2558 | (defmethod perform ((operation load-op) (c static-file)) |
| 2559 | (declare (ignorable operation c)) | |
| 2560 | nil) | |
| 2561 | ||
| 2562 | (defmethod operation-done-p ((operation load-op) (c static-file)) | |
| 2563 | (declare (ignorable operation c)) | |
| 2564 | t) | |
| 2565 | ||
| 2566 | (defmethod output-files ((operation operation) (c component)) | |
| 2567 | (declare (ignorable operation c)) | |
| 2568 | nil) | |
| 2569 | ||
| 2570 | (defmethod component-depends-on ((operation load-op) (c component)) | |
| 2571 | (declare (ignorable operation)) | |
| 2572 | (cons (list 'compile-op (component-name c)) | |
| 2573 | (call-next-method))) | |
| 2574 | ||
| 7fe7ec48 | 2575 | (defmethod operation-description ((operation load-op) component) |
| 2576 | (declare (ignorable operation)) | |
| c3e0c711 | 2577 | (format nil (compatfmt "~@<loading ~3i~_~A~@:>") |
| 2578 | component)) | |
| 2579 | ||
| 2580 | (defmethod operation-description ((operation load-op) (component cl-source-file)) | |
| 2581 | (declare (ignorable operation)) | |
| 2582 | (format nil (compatfmt "~@<loading FASL for ~3i~_~A~@:>") | |
| 2583 | component)) | |
| 7fe7ec48 | 2584 | |
| c3e0c711 | 2585 | (defmethod operation-description ((operation load-op) (component module)) |
| 2586 | (declare (ignorable operation)) | |
| 2587 | (format nil (compatfmt "~@<loaded ~3i~_~A~@:>") | |
| 2588 | component)) | |
| 7fe7ec48 | 2589 | |
| 36d9b3bc | 2590 | ;;;; ------------------------------------------------------------------------- |
| 2591 | ;;;; load-source-op | |
| 2592 | ||
| 2593 | (defclass load-source-op (basic-load-op) ()) | |
| 2594 | ||
| 2595 | (defmethod perform ((o load-source-op) (c cl-source-file)) | |
| 2596 | (declare (ignorable o)) | |
| 2597 | (let ((source (component-pathname c))) | |
| 2598 | (setf (component-property c 'last-loaded-as-source) | |
| 115a05e6 RT |
2599 | (and (call-with-around-compile-hook |
| 2600 | c #'(lambda () (load source :external-format (component-external-format c)))) | |
| 36d9b3bc | 2601 | (get-universal-time))))) |
| 2602 | ||
| 2603 | (defmethod perform ((operation load-source-op) (c static-file)) | |
| 2604 | (declare (ignorable operation c)) | |
| 2605 | nil) | |
| 2606 | ||
| 2607 | (defmethod output-files ((operation load-source-op) (c component)) | |
| 2608 | (declare (ignorable operation c)) | |
| 2609 | nil) | |
| 2610 | ||
| c3e0c711 | 2611 | ;;; FIXME: We simply copy load-op's dependencies. This is Just Not Right. |
| 36d9b3bc | 2612 | (defmethod component-depends-on ((o load-source-op) (c component)) |
| 2613 | (declare (ignorable o)) | |
| c3e0c711 | 2614 | (loop :with what-would-load-op-do = (component-depends-on 'load-op c) |
| 2615 | :for (op . co) :in what-would-load-op-do | |
| 2616 | :when (eq op 'load-op) :collect (cons 'load-source-op co))) | |
| 36d9b3bc | 2617 | |
| 2618 | (defmethod operation-done-p ((o load-source-op) (c source-file)) | |
| 2619 | (declare (ignorable o)) | |
| 2620 | (if (or (not (component-property c 'last-loaded-as-source)) | |
| 2621 | (> (safe-file-write-date (component-pathname c)) | |
| 2622 | (component-property c 'last-loaded-as-source))) | |
| 2623 | nil t)) | |
| 2624 | ||
| 7fe7ec48 | 2625 | (defmethod operation-description ((operation load-source-op) component) |
| 2626 | (declare (ignorable operation)) | |
| c3e0c711 | 2627 | (format nil (compatfmt "~@<Loading source of ~3i~_~A~@:>") |
| 2628 | component)) | |
| 2629 | ||
| 2630 | (defmethod operation-description ((operation load-source-op) (component module)) | |
| 2631 | (declare (ignorable operation)) | |
| 2632 | (format nil (compatfmt "~@<Loaded source of ~3i~_~A~@:>") component)) | |
| 7fe7ec48 | 2633 | |
| 36d9b3bc | 2634 | |
| 2635 | ;;;; ------------------------------------------------------------------------- | |
| 2636 | ;;;; test-op | |
| 2637 | ||
| 2638 | (defclass test-op (operation) ()) | |
| 2639 | ||
| 2640 | (defmethod perform ((operation test-op) (c component)) | |
| 2641 | (declare (ignorable operation c)) | |
| 2642 | nil) | |
| 2643 | ||
| 2644 | (defmethod operation-done-p ((operation test-op) (c system)) | |
| 2645 | "Testing a system is _never_ done." | |
| 2646 | (declare (ignorable operation c)) | |
| 2647 | nil) | |
| 2648 | ||
| 2649 | (defmethod component-depends-on :around ((o test-op) (c system)) | |
| 2650 | (declare (ignorable o)) | |
| 2651 | (cons `(load-op ,(component-name c)) (call-next-method))) | |
| 2652 | ||
| 2653 | ||
| 2654 | ;;;; ------------------------------------------------------------------------- | |
| 2655 | ;;;; Invoking Operations | |
| 2656 | ||
| 3dfe0f91 | 2657 | (defgeneric* operate (operation-class system &key &allow-other-keys)) |
| c3e0c711 | 2658 | (defgeneric* perform-plan (plan &key)) |
| 2659 | ||
| 576ae2a5 RT |
2660 | ;;;; Separating this into a different function makes it more forward-compatible |
| 2661 | (defun* cleanup-upgraded-asdf (old-version) | |
| 115a05e6 | 2662 | (let ((new-version (asdf-version))) |
| 576ae2a5 RT |
2663 | (unless (equal old-version new-version) |
| 2664 | (cond | |
| 2665 | ((version-satisfies new-version old-version) | |
| 2666 | (asdf-message (compatfmt "~&~@<; ~@;Upgraded ASDF from version ~A to version ~A~@:>~%") | |
| 2667 | old-version new-version)) | |
| 2668 | ((version-satisfies old-version new-version) | |
| 2669 | (warn (compatfmt "~&~@<; ~@;Downgraded ASDF from version ~A to version ~A~@:>~%") | |
| 2670 | old-version new-version)) | |
| 2671 | (t | |
| 2672 | (asdf-message (compatfmt "~&~@<; ~@;Changed ASDF from version ~A to incompatible version ~A~@:>~%") | |
| 2673 | old-version new-version))) | |
| 2674 | (let ((asdf (funcall (find-symbol* 'find-system :asdf) :asdf))) | |
| 2675 | ;; Invalidate all systems but ASDF itself. | |
| 2676 | (setf *defined-systems* (make-defined-systems-table)) | |
| 2677 | (register-system asdf) | |
| 2678 | ;; If we're in the middle of something, restart it. | |
| 2679 | (when *systems-being-defined* | |
| 2680 | (let ((l (loop :for name :being :the :hash-keys :of *systems-being-defined* :collect name))) | |
| 2681 | (clrhash *systems-being-defined*) | |
| 2682 | (dolist (s l) (find-system s nil)))) | |
| 2683 | t)))) | |
| 2684 | ||
| c3e0c711 | 2685 | ;;;; Try to upgrade of ASDF. If a different version was used, return T. |
| 2686 | ;;;; We need do that before we operate on anything that depends on ASDF. | |
| 2687 | (defun* upgrade-asdf () | |
| 115a05e6 | 2688 | (let ((version (asdf-version))) |
| c3e0c711 | 2689 | (handler-bind (((or style-warning warning) #'muffle-warning)) |
| 2690 | (operate 'load-op :asdf :verbose nil)) | |
| 576ae2a5 | 2691 | (cleanup-upgraded-asdf version))) |
| c3e0c711 | 2692 | |
| 2693 | (defmethod perform-plan ((steps list) &key) | |
| 2694 | (let ((*package* *package*) | |
| 2695 | (*readtable* *readtable*)) | |
| 2696 | (with-compilation-unit () | |
| 2697 | (loop :for (op . component) :in steps :do | |
| 4c04d402 | 2698 | (perform-with-restarts op component))))) |
| 36d9b3bc | 2699 | |
| 2700 | (defmethod operate (operation-class system &rest args | |
| 2701 | &key ((:verbose *asdf-verbose*) *asdf-verbose*) version force | |
| 2702 | &allow-other-keys) | |
| 2703 | (declare (ignore force)) | |
| c3e0c711 | 2704 | (with-system-definitions () |
| 2705 | (let* ((op (apply 'make-instance operation-class | |
| 2706 | :original-initargs args | |
| 2707 | args)) | |
| 2708 | (*verbose-out* (if *asdf-verbose* *standard-output* (make-broadcast-stream))) | |
| 2709 | (system (etypecase system | |
| 2710 | (system system) | |
| 2711 | ((or string symbol) (find-system system))))) | |
| 2712 | (unless (version-satisfies system version) | |
| 2713 | (error 'missing-component-of-version :requires system :version version)) | |
| 2714 | (let ((steps (traverse op system))) | |
| 2715 | (when (and (not (equal '("asdf") (component-find-path system))) | |
| 2716 | (find '("asdf") (mapcar 'cdr steps) | |
| 2717 | :test 'equal :key 'component-find-path) | |
| 2718 | (upgrade-asdf)) | |
| 2719 | ;; If we needed to upgrade ASDF to achieve our goal, | |
| 2720 | ;; then do it specially as the first thing, then | |
| 2721 | ;; invalidate all existing system | |
| 2722 | ;; retry the whole thing with the new OPERATE function, | |
| 2723 | ;; which on some implementations | |
| 2724 | ;; has a new symbol shadowing the current one. | |
| 2725 | (return-from operate | |
| 2726 | (apply (find-symbol* 'operate :asdf) operation-class system args))) | |
| 2727 | (perform-plan steps) | |
| 2728 | (values op steps))))) | |
| 36d9b3bc | 2729 | |
| 7fe7ec48 | 2730 | (defun* oos (operation-class system &rest args &key force verbose version |
| 36d9b3bc | 2731 | &allow-other-keys) |
| 2732 | (declare (ignore force verbose version)) | |
| c3e0c711 | 2733 | (apply 'operate operation-class system args)) |
| 36d9b3bc | 2734 | |
| 2735 | (let ((operate-docstring | |
| 2736 | "Operate does three things: | |
| 2737 | ||
| b986cd91 | 2738 | 1. It creates an instance of OPERATION-CLASS using any keyword parameters |
| 36d9b3bc | 2739 | as initargs. |
| b986cd91 | 2740 | 2. It finds the asdf-system specified by SYSTEM (possibly loading |
| 36d9b3bc | 2741 | it from disk). |
| b986cd91 | 2742 | 3. It then calls TRAVERSE with the operation and system as arguments |
| 36d9b3bc | 2743 | |
| b986cd91 | 2744 | The traverse operation is wrapped in WITH-COMPILATION-UNIT and error |
| 2745 | handling code. If a VERSION argument is supplied, then operate also | |
| 2746 | ensures that the system found satisfies it using the VERSION-SATISFIES | |
| 36d9b3bc | 2747 | method. |
| 2748 | ||
| 2749 | Note that dependencies may cause the operation to invoke other | |
| 2750 | operations on the system or its components: the new operations will be | |
| 2751 | created with the same initargs as the original one. | |
| 2752 | ")) | |
| 2753 | (setf (documentation 'oos 'function) | |
| 2754 | (format nil | |
| 576ae2a5 | 2755 | "Short for _operate on system_ and an alias for the OPERATE function.~%~%~a" |
| 36d9b3bc | 2756 | operate-docstring)) |
| 2757 | (setf (documentation 'operate 'function) | |
| 2758 | operate-docstring)) | |
| 2759 | ||
| b6f29d0e | 2760 | (defun* load-system (system &rest keys &key force verbose version &allow-other-keys) |
| c3e0c711 | 2761 | "Shorthand for `(operate 'asdf:load-op system)`. |
| 2762 | See OPERATE for details." | |
| 36d9b3bc | 2763 | (declare (ignore force verbose version)) |
| b6f29d0e | 2764 | (apply 'operate *load-system-operation* system keys) |
| 146e9122 | 2765 | t) |
| 36d9b3bc | 2766 | |
| 576ae2a5 RT |
2767 | (defun* load-systems (&rest systems) |
| 2768 | (map () 'load-system systems)) | |
| 2769 | ||
| 115a05e6 RT |
2770 | (defun component-loaded-p (c) |
| 2771 | (and (gethash 'load-op (component-operation-times (find-component c nil))) t)) | |
| 2772 | ||
| 2773 | (defun loaded-systems () | |
| 2774 | (remove-if-not 'component-loaded-p (registered-systems))) | |
| 2775 | ||
| b6f29d0e RT |
2776 | (defun require-system (s &rest keys &key &allow-other-keys) |
| 2777 | (apply 'load-system s :force-not (loaded-systems) keys)) | |
| 115a05e6 | 2778 | |
| 7fe7ec48 | 2779 | (defun* compile-system (system &rest args &key force verbose version |
| 36d9b3bc | 2780 | &allow-other-keys) |
| 115a05e6 | 2781 | "Shorthand for `(asdf:operate 'asdf:compile-op system)`. See OPERATE |
| 36d9b3bc | 2782 | for details." |
| 2783 | (declare (ignore force verbose version)) | |
| c3e0c711 | 2784 | (apply 'operate 'compile-op system args) |
| 146e9122 | 2785 | t) |
| 36d9b3bc | 2786 | |
| 7fe7ec48 | 2787 | (defun* test-system (system &rest args &key force verbose version |
| 36d9b3bc | 2788 | &allow-other-keys) |
| 115a05e6 | 2789 | "Shorthand for `(asdf:operate 'asdf:test-op system)`. See OPERATE for |
| 36d9b3bc | 2790 | details." |
| 2791 | (declare (ignore force verbose version)) | |
| c3e0c711 | 2792 | (apply 'operate 'test-op system args) |
| 146e9122 | 2793 | t) |
| 36d9b3bc | 2794 | |
| 2795 | ;;;; ------------------------------------------------------------------------- | |
| 2796 | ;;;; Defsystem | |
| 2797 | ||
| 7fe7ec48 | 2798 | (defun* load-pathname () |
| c3e0c711 | 2799 | (resolve-symlinks* (or *load-pathname* *compile-file-pathname*))) |
| 2220a102 | 2800 | |
| 4c04d402 | 2801 | (defun* determine-system-pathname (pathname) |
| 2220a102 | 2802 | ;; The defsystem macro calls us to determine |
| 2803 | ;; the pathname of a system as follows: | |
| 36d9b3bc | 2804 | ;; 1. the one supplied, |
| 2220a102 | 2805 | ;; 2. derived from *load-pathname* via load-pathname |
| b986cd91 | 2806 | ;; 3. taken from the *default-pathname-defaults* via default-directory |
| 2220a102 | 2807 | (let* ((file-pathname (load-pathname)) |
| 2808 | (directory-pathname (and file-pathname (pathname-directory-pathname file-pathname)))) | |
| 4c04d402 | 2809 | (or (and pathname (subpathname directory-pathname pathname :type :directory)) |
| 7fe7ec48 | 2810 | directory-pathname |
| b986cd91 | 2811 | (default-directory)))) |
| 36d9b3bc | 2812 | |
| b0e85da9 RT |
2813 | (defun* find-class* (x &optional (errorp t) environment) |
| 2814 | (etypecase x | |
| 2815 | ((or standard-class built-in-class) x) | |
| 2816 | (symbol (find-class x errorp environment)))) | |
| 2817 | ||
| 7fe7ec48 | 2818 | (defun* class-for-type (parent type) |
| 3dfe0f91 | 2819 | (or (loop :for symbol :in (list |
| 3871cbd7 | 2820 | type |
| 2821 | (find-symbol* type *package*) | |
| 2822 | (find-symbol* type :asdf)) | |
| 3dfe0f91 | 2823 | :for class = (and symbol (find-class symbol nil)) |
| c3e0c711 | 2824 | :when (and class |
| 2825 | (#-cormanlisp subtypep #+cormanlisp cl::subclassp | |
| 2826 | class (find-class 'component))) | |
| 3dfe0f91 | 2827 | :return class) |
| 2828 | (and (eq type :file) | |
| b0e85da9 RT |
2829 | (find-class* |
| 2830 | (or (loop :for module = parent :then (component-parent module) :while module | |
| 2831 | :thereis (module-default-component-class module)) | |
| 2832 | *default-component-class*) nil)) | |
| 1ff353ac | 2833 | (sysdef-error "don't recognize component type ~A" type))) |
| 36d9b3bc | 2834 | |
| 7fe7ec48 | 2835 | (defun* maybe-add-tree (tree op1 op2 c) |
| 36d9b3bc | 2836 | "Add the node C at /OP1/OP2 in TREE, unless it's there already. |
| 2837 | Returns the new tree (which probably shares structure with the old one)" | |
| 2838 | (let ((first-op-tree (assoc op1 tree))) | |
| 2839 | (if first-op-tree | |
| 2840 | (progn | |
| 2841 | (aif (assoc op2 (cdr first-op-tree)) | |
| 576ae2a5 | 2842 | (if (find c (cdr it) :test #'equal) |
| 36d9b3bc | 2843 | nil |
| 2844 | (setf (cdr it) (cons c (cdr it)))) | |
| 2845 | (setf (cdr first-op-tree) | |
| 2846 | (acons op2 (list c) (cdr first-op-tree)))) | |
| 2847 | tree) | |
| 2848 | (acons op1 (list (list op2 c)) tree)))) | |
| 2849 | ||
| 7fe7ec48 | 2850 | (defun* union-of-dependencies (&rest deps) |
| 36d9b3bc | 2851 | (let ((new-tree nil)) |
| 2852 | (dolist (dep deps) | |
| 2853 | (dolist (op-tree dep) | |
| 2854 | (dolist (op (cdr op-tree)) | |
| 2855 | (dolist (c (cdr op)) | |
| 2856 | (setf new-tree | |
| 2857 | (maybe-add-tree new-tree (car op-tree) (car op) c)))))) | |
| 2858 | new-tree)) | |
| 2859 | ||
| 2860 | ||
| 2861 | (defvar *serial-depends-on* nil) | |
| 2862 | ||
| 7fe7ec48 | 2863 | (defun* sysdef-error-component (msg type name value) |
| 576ae2a5 | 2864 | (sysdef-error (strcat msg (compatfmt "~&~@<The value specified for ~(~A~) ~A is ~S~@:>")) |
| 36d9b3bc | 2865 | type name value)) |
| 2866 | ||
| 7fe7ec48 | 2867 | (defun* check-component-input (type name weakly-depends-on |
| 36d9b3bc | 2868 | depends-on components in-order-to) |
| 2869 | "A partial test of the values of a component." | |
| 2870 | (unless (listp depends-on) | |
| 2871 | (sysdef-error-component ":depends-on must be a list." | |
| 2872 | type name depends-on)) | |
| 2873 | (unless (listp weakly-depends-on) | |
| 2874 | (sysdef-error-component ":weakly-depends-on must be a list." | |
| 2875 | type name weakly-depends-on)) | |
| 2876 | (unless (listp components) | |
| 2877 | (sysdef-error-component ":components must be NIL or a list of components." | |
| 2878 | type name components)) | |
| 2879 | (unless (and (listp in-order-to) (listp (car in-order-to))) | |
| 2880 | (sysdef-error-component ":in-order-to must be NIL or a list of components." | |
| 2881 | type name in-order-to))) | |
| 2882 | ||
| 7fe7ec48 | 2883 | (defun* %remove-component-inline-methods (component) |
| 36d9b3bc | 2884 | (dolist (name +asdf-methods+) |
| 2885 | (map () | |
| 2886 | ;; this is inefficient as most of the stored | |
| 2887 | ;; methods will not be for this particular gf | |
| 2888 | ;; But this is hardly performance-critical | |
| 1ff353ac | 2889 | #'(lambda (m) |
| 2890 | (remove-method (symbol-function name) m)) | |
| 36d9b3bc | 2891 | (component-inline-methods component))) |
| 2892 | ;; clear methods, then add the new ones | |
| 2893 | (setf (component-inline-methods component) nil)) | |
| 2894 | ||
| 7fe7ec48 | 2895 | (defun* %define-component-inline-methods (ret rest) |
| 36d9b3bc | 2896 | (dolist (name +asdf-methods+) |
| 2897 | (let ((keyword (intern (symbol-name name) :keyword))) | |
| 2898 | (loop :for data = rest :then (cddr data) | |
| 2899 | :for key = (first data) | |
| 2900 | :for value = (second data) | |
| 2901 | :while data | |
| 2902 | :when (eq key keyword) :do | |
| 2903 | (destructuring-bind (op qual (o c) &body body) value | |
| 2904 | (pushnew | |
| 2905 | (eval `(defmethod ,name ,qual ((,o ,op) (,c (eql ,ret))) | |
| 2906 | ,@body)) | |
| 2907 | (component-inline-methods ret))))))) | |
| 2908 | ||
| 7fe7ec48 | 2909 | (defun* %refresh-component-inline-methods (component rest) |
| 36d9b3bc | 2910 | (%remove-component-inline-methods component) |
| 2911 | (%define-component-inline-methods component rest)) | |
| 2912 | ||
| 7fe7ec48 | 2913 | (defun* parse-component-form (parent options) |
| 36d9b3bc | 2914 | (destructuring-bind |
| 2915 | (type name &rest rest &key | |
| 2916 | ;; the following list of keywords is reproduced below in the | |
| 2917 | ;; remove-keys form. important to keep them in sync | |
| b0e85da9 | 2918 | components pathname |
| 36d9b3bc | 2919 | perform explain output-files operation-done-p |
| 115a05e6 RT |
2920 | weakly-depends-on depends-on serial in-order-to |
| 2921 | do-first | |
| c3e0c711 | 2922 | (version nil versionp) |
| 36d9b3bc | 2923 | ;; list ends |
| 2924 | &allow-other-keys) options | |
| 2925 | (declare (ignorable perform explain output-files operation-done-p)) | |
| 2926 | (check-component-input type name weakly-depends-on depends-on components in-order-to) | |
| 2927 | ||
| 2928 | (when (and parent | |
| 2929 | (find-component parent name) | |
| 2930 | ;; ignore the same object when rereading the defsystem | |
| 2931 | (not | |
| 2932 | (typep (find-component parent name) | |
| 2933 | (class-for-type parent type)))) | |
| 2934 | (error 'duplicate-names :name name)) | |
| 2935 | ||
| c3e0c711 | 2936 | (when versionp |
| 2937 | (unless (parse-version version nil) | |
| 2938 | (warn (compatfmt "~@<Invalid version ~S for component ~S~@[ of ~S~]~@:>") | |
| 2939 | version name parent))) | |
| 2940 | ||
| 576ae2a5 RT |
2941 | (let* ((args (list* :name (coerce-name name) |
| 2942 | :pathname pathname | |
| 2943 | :parent parent | |
| 2944 | (remove-keys | |
| b0e85da9 | 2945 | '(components pathname |
| 576ae2a5 RT |
2946 | perform explain output-files operation-done-p |
| 2947 | weakly-depends-on depends-on serial in-order-to) | |
| 2948 | rest))) | |
| 4c04d402 | 2949 | (ret (find-component parent name))) |
| 36d9b3bc | 2950 | (when weakly-depends-on |
| 115a05e6 | 2951 | (appendf depends-on (remove-if (complement #'(lambda (x) (find-system x nil))) weakly-depends-on))) |
| 36d9b3bc | 2952 | (when *serial-depends-on* |
| 2953 | (push *serial-depends-on* depends-on)) | |
| 576ae2a5 RT |
2954 | (if ret ; preserve identity |
| 2955 | (apply 'reinitialize-instance ret args) | |
| 2956 | (setf ret (apply 'make-instance (class-for-type parent type) args))) | |
| 36d9b3bc | 2957 | (component-pathname ret) ; eagerly compute the absolute pathname |
| 2958 | (when (typep ret 'module) | |
| 36d9b3bc | 2959 | (let ((*serial-depends-on* nil)) |
| 2960 | (setf (module-components ret) | |
| 2961 | (loop | |
| 2962 | :for c-form :in components | |
| 2963 | :for c = (parse-component-form ret c-form) | |
| 2964 | :for name = (component-name c) | |
| 2965 | :collect c | |
| 2966 | :when serial :do (setf *serial-depends-on* name)))) | |
| 2967 | (compute-module-components-by-name ret)) | |
| 2968 | ||
| 2969 | (setf (component-load-dependencies ret) depends-on) ;; Used by POIU | |
| 2970 | ||
| 2971 | (setf (component-in-order-to ret) | |
| 2972 | (union-of-dependencies | |
| 2973 | in-order-to | |
| 2974 | `((compile-op (compile-op ,@depends-on)) | |
| 2975 | (load-op (load-op ,@depends-on))))) | |
| d0c8d6d5 | 2976 | (setf (component-do-first ret) |
| 2977 | (union-of-dependencies | |
| 2978 | do-first | |
| 4c04d402 | 2979 | `((compile-op (load-op ,@depends-on))))) |
| 36d9b3bc | 2980 | |
| 2981 | (%refresh-component-inline-methods ret rest) | |
| 2982 | ret))) | |
| 2983 | ||
| 576ae2a5 RT |
2984 | (defun* reset-system (system &rest keys &key &allow-other-keys) |
| 2985 | (change-class (change-class system 'proto-system) 'system) | |
| 2986 | (apply 'reinitialize-instance system keys)) | |
| 2987 | ||
| c3e0c711 | 2988 | (defun* do-defsystem (name &rest options |
| 4c04d402 | 2989 | &key pathname (class 'system) |
| c3e0c711 | 2990 | defsystem-depends-on &allow-other-keys) |
| 2991 | ;; The system must be registered before we parse the body, | |
| 2992 | ;; otherwise we recur when trying to find an existing system | |
| 2993 | ;; of the same name to reuse options (e.g. pathname) from. | |
| 2994 | ;; To avoid infinite recursion in cases where you defsystem a system | |
| 2995 | ;; that is registered to a different location to find-system, | |
| 2996 | ;; we also need to remember it in a special variable *systems-being-defined*. | |
| 2997 | (with-system-definitions () | |
| 2998 | (let* ((name (coerce-name name)) | |
| 2999 | (registered (system-registered-p name)) | |
| 576ae2a5 RT |
3000 | (registered! (if registered |
| 3001 | (rplaca registered (get-universal-time)) | |
| 3002 | (register-system (make-instance 'system :name name)))) | |
| 3003 | (system (reset-system (cdr registered!) | |
| 3004 | :name name :source-file (load-pathname))) | |
| c3e0c711 | 3005 | (component-options (remove-keys '(:class) options))) |
| c3e0c711 | 3006 | (setf (gethash name *systems-being-defined*) system) |
| 576ae2a5 | 3007 | (apply 'load-systems defsystem-depends-on) |
| c3e0c711 | 3008 | ;; We change-class (when necessary) AFTER we load the defsystem-dep's |
| 3009 | ;; since the class might not be defined as part of those. | |
| 3010 | (let ((class (class-for-type nil class))) | |
| 3011 | (unless (eq (type-of system) class) | |
| 3012 | (change-class system class))) | |
| 3013 | (parse-component-form | |
| 3014 | nil (list* | |
| 3015 | :module name | |
| 4c04d402 | 3016 | :pathname (determine-system-pathname pathname) |
| c3e0c711 | 3017 | component-options))))) |
| 3018 | ||
| 3019 | (defmacro defsystem (name &body options) | |
| 3020 | `(apply 'do-defsystem ',name ',options)) | |
| 3021 | ||
| 36d9b3bc | 3022 | ;;;; --------------------------------------------------------------------------- |
| 3023 | ;;;; run-shell-command | |
| 3024 | ;;;; | |
| 3025 | ;;;; run-shell-command functions for other lisp implementations will be | |
| 3026 | ;;;; gratefully accepted, if they do the same thing. | |
| 3027 | ;;;; If the docstring is ambiguous, send a bug report. | |
| 3028 | ;;;; | |
| 4c04d402 RT |
3029 | ;;;; WARNING! The function below is mostly dysfunctional. |
| 3030 | ;;;; For instance, it will probably run fine on most implementations on Unix, | |
| 3031 | ;;;; which will hopefully use the shell /bin/sh (which we force in some cases) | |
| 3032 | ;;;; which is hopefully reasonably compatible with a POSIX *or* Bourne shell. | |
| 3033 | ;;;; But behavior on Windows may vary wildly between implementations, | |
| 3034 | ;;;; either relying on your having installed a POSIX sh, or going through | |
| 3035 | ;;;; the CMD.EXE interpreter, for a totally different meaning, depending on | |
| 3036 | ;;;; what is easily expressible in said implementation. | |
| 3037 | ;;;; | |
| 36d9b3bc | 3038 | ;;;; We probably should move this functionality to its own system and deprecate |
| 3039 | ;;;; use of it from the asdf package. However, this would break unspecified | |
| 3040 | ;;;; existing software, so until a clear alternative exists, we can't deprecate | |
| 3041 | ;;;; it, and even after it's been deprecated, we will support it for a few | |
| 3042 | ;;;; years so everyone has time to migrate away from it. -- fare 2009-12-01 | |
| 4c04d402 RT |
3043 | ;;;; |
| 3044 | ;;;; As a suggested replacement which is portable to all ASDF-supported | |
| 3045 | ;;;; implementations and operating systems except Genera, I recommend | |
| 115a05e6 | 3046 | ;;;; xcvb-driver's xcvb-driver:run-program/ and its derivatives. |
| 36d9b3bc | 3047 | |
| 7fe7ec48 | 3048 | (defun* run-shell-command (control-string &rest args) |
| b986cd91 | 3049 | "Interpolate ARGS into CONTROL-STRING as if by FORMAT, and |
| 36d9b3bc | 3050 | synchronously execute the result using a Bourne-compatible shell, with |
| b986cd91 | 3051 | output to *VERBOSE-OUT*. Returns the shell's exit code." |
| c3e0c711 | 3052 | (let ((command (apply 'format nil control-string args))) |
| 36d9b3bc | 3053 | (asdf-message "; $ ~A~%" command) |
| 3054 | ||
| 3055 | #+abcl | |
| 3056 | (ext:run-shell-command command :output *verbose-out*) | |
| 3057 | ||
| 3058 | #+allegro | |
| 3059 | ;; will this fail if command has embedded quotes - it seems to work | |
| 3060 | (multiple-value-bind (stdout stderr exit-code) | |
| 3061 | (excl.osi:command-output | |
| 4c04d402 RT |
3062 | #-mswindows (vector "/bin/sh" "/bin/sh" "-c" command) |
| 3063 | #+mswindows command ; BEWARE! | |
| 36d9b3bc | 3064 | :input nil :whole nil |
| 3065 | #+mswindows :show-window #+mswindows :hide) | |
| 4c04d402 RT |
3066 | (asdf-message "~{~&~a~%~}~%" stderr) |
| 3067 | (asdf-message "~{~&~a~%~}~%" stdout) | |
| 36d9b3bc |