Update to asdf 2.24.
[projects/cmucl/cmucl.git] / src / contrib / asdf / asdf.lisp
CommitLineData
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 406Defaults 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?
411Valid 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)
416when compiling a file? Valid values are :error, :warn, and :ignore.
417Note 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.
426You may override it with e.g. ASDF:LOAD-FASL-OP from asdf-bundle,
427or 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
505defines a function that, when applied to an argument inside BODY, will
506add its argument to the corresponding collection. Returns multiple values,
507a 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\)\)\)\)
513Returns 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,
527and 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,
578and 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
588if the SPECIFIED pathname does not have an absolute directory,
589then the HOST and DEVICE both come from the DEFAULTS, whereas
590if the SPECIFIED pathname does have an absolute directory,
591then the HOST and DEVICE both come from the SPECIFIED.
36d9b3bc 592Also, 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,
625and 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
651any of the characters in the sequence SEPARATOR.
36d9b3bc 652If MAX is specified, then no more than max(1,MAX) components will be returned,
653starting 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:
684A flag that is either :absolute or :relative, indicating
685 how the rest of the values are to be interpreted.
686A directory path --- a list of strings, suitable for
687 use with MAKE-PATHNAME when prepended with the flag
688 value.
689A filename with type extension, possibly NIL in the
690 case of a directory pathname.
691FORCE-DIRECTORY forces S to be interpreted as a directory
692pathname \(third return value will be NIL, final component
693of S will be treated as part of the directory path.
694
695The intention of this function is to support structured component names,
696e.g., \(:file \"foo/bar\"\), which will be unpacked to relative
697pathnames."
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
764A directory-pathname is a pathname _without_ a filename. The three
b986cd91 765ways that the filename components can be missing are for it to be NIL,
766:UNSPECIFIC or the empty string.
36d9b3bc 767
b986cd91 768Note that this does _not_ check to see that PATHNAME points to an
36d9b3bc 769actually-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.
808When given a string, portably decompose it into a relative pathname:
809#\\/ separates subdirectories. The last #\\/-separated string is as follows:
810if TYPE is NIL, its last #\\. if any separates name and type from from type;
811if TYPE is a string, it is the type, and the whole string is the name;
812if TYPE is :DIRECTORY, the string is a directory component;
813if the string is empty, it's a directory.
814Any directory named .. is read as :BACK.
815Host, 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
888with 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
1037on this component, e.g. \"loading /a/b/c\".
1038You 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
1051interpreted relative to the pathname of that component's parent.
1052Despite the function's name, the return value may be an absolute
1053pathname, because an absolute pathname may be interpreted relative to
1054another 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;
1071if 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
1078the head of the tree"))
1079
1080(defgeneric* component-visited-p (operation component)
1081 (:documentation "Returns the value stored by a call to
1082VISIT-COMPONENT, if that has been called, otherwise NIL.
1083This value stored will be a cons cell, the first element
1084of which is a computed key, so not interesting. The
1085CDR wil be the DATA value stored by VISIT-COMPONENT; recover
1086it as (cdr (component-visited-p op c)).
1087 In the current form of ASDF, the DATA value retrieved is
1088effectively a boolean, indicating whether some operations are
1089to be performed in order to do OPERATION X COMPONENT. If the
1090data value is NIL, the combination had been explored, but no
1091operations needed to be performed."))
1092
1093(defgeneric* visit-component (operation component data)
1094 (:documentation "Record DATA as being associated with OPERATION
1095and COMPONENT. This is a side-effecting function: the association
1096will be recorded on the ROOT OPERATION \(OPERATION-ANCESTOR of the
1097OPERATION\).
1098 No evidence that DATA is ever interesting, beyond just being
1099non-NIL. Using the data field is probably very risky; if there is
1100already a record for OPERATION X COMPONENT, DATA will be quietly
1101discarded instead of recorded.
1102 Starting with 2.006, TRAVERSE will store an integer in data,
1103so 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
1133The plan returned is a list of dotted-pairs. Each pair is the CONS
1134of ASDF operation object and a COMPONENT object. The pairs will be
1135processed 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.
1399The default value :default preserves the legacy behavior.
1400A future default might be :utf-8 or :autodetect
1401reading emacs-style -*- coding: utf-8 -*- specifications,
1402and 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
1409CL:LOAD or CL:COMPILE-FILE to best process a UTF-8 encoded file.
1410On modern implementations, this will decode UTF-8 code points as CL characters.
1411On legacy implementations, it may fall back on some 8-bit encoding,
1412with non-ASCII code points being read as several CL characters;
1413hopefully, 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
1441and 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.
1479You 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.
1485Return a (non-null) list of integers if the string is valid, NIL otherwise.
1486If on-error is error, warn, or designates a function of compatible signature,
1487the function is called with an explanation of what is wrong with the argument.
1488NB: 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
1597names of the systems, and whose values are pairs, the first
1598element of which is a universal-time indicating when the
1599system definition was last updated, and the second element
1600of 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.
1627Note 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 1636FN should be a function of one argument. It will be
36d9b3bc 1637called 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
1671A 'system directory designator' is a pathname or an expression
1672which 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
1679This is for backward compatibilily.
1680Going 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
1833Returns five values: FOUNDP FOUND-SYSTEM PATHNAME PREVIOUS PREVIOUS-TIME
1834FOUNDP is true when a system was found,
1835either a new unregistered one or a previously registered one.
576ae2a5 1836FOUND-SYSTEM when not null is a SYSTEM object that may be REGISTER-SYSTEM'ed as is
115a05e6
RT
1837PATHNAME when not null is a path from where to load the system,
1838either associated with FOUND-SYSTEM, or with the PREVIOUS system.
576ae2a5
RT
1839PREVIOUS when not null is a previously loaded SYSTEM object of same name.
1840PREVIOUS-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
2023component, and DEP-O, confusingly enough, is an operation
2024class 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
2165recursive 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 27381. It creates an instance of OPERATION-CLASS using any keyword parameters
36d9b3bc 2739as initargs.
b986cd91 27402. It finds the asdf-system specified by SYSTEM (possibly loading
36d9b3bc 2741it from disk).
b986cd91 27423. It then calls TRAVERSE with the operation and system as arguments
36d9b3bc 2743
b986cd91 2744The traverse operation is wrapped in WITH-COMPILATION-UNIT and error
2745handling code. If a VERSION argument is supplied, then operate also
2746ensures that the system found satisfies it using the VERSION-SATISFIES
36d9b3bc 2747method.
2748
2749Note that dependencies may cause the operation to invoke other
2750operations on the system or its components: the new operations will be
2751created 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)`.
2762See 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 2782for 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 2790details."
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.
2837Returns 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 3050synchronously execute the result using a Bourne-compatible shell, with
b986cd91 3051output 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